commit b6326c4c849f2436036f71c09efb295516afd9cc Author: AntonioTorres-SrvUbuntuLocal Date: Sun Dec 13 20:58:06 2020 +0000 Creamos Proyecto y VirtualEnv diff --git a/.venv/bin/Activate.ps1 b/.venv/bin/Activate.ps1 new file mode 100644 index 00000000..2fb3852c --- /dev/null +++ b/.venv/bin/Activate.ps1 @@ -0,0 +1,241 @@ +<# +.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/.venv/bin/activate b/.venv/bin/activate new file mode 100644 index 00000000..1c97ebd5 --- /dev/null +++ b/.venv/bin/activate @@ -0,0 +1,76 @@ +# 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/antonio/bin/CursoPythonDavidBombal/.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/.venv/bin/activate.csh b/.venv/bin/activate.csh new file mode 100644 index 00000000..09ae5b60 --- /dev/null +++ b/.venv/bin/activate.csh @@ -0,0 +1,37 @@ +# 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/antonio/bin/CursoPythonDavidBombal/.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/.venv/bin/activate.fish b/.venv/bin/activate.fish new file mode 100644 index 00000000..a82e75cf --- /dev/null +++ b/.venv/bin/activate.fish @@ -0,0 +1,75 @@ +# 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/antonio/bin/CursoPythonDavidBombal/.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/.venv/bin/easy_install b/.venv/bin/easy_install new file mode 100755 index 00000000..19616dc6 --- /dev/null +++ b/.venv/bin/easy_install @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.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/.venv/bin/easy_install-3.8 b/.venv/bin/easy_install-3.8 new file mode 100755 index 00000000..19616dc6 --- /dev/null +++ b/.venv/bin/easy_install-3.8 @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.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/.venv/bin/epylint b/.venv/bin/epylint new file mode 100755 index 00000000..d2e22854 --- /dev/null +++ b/.venv/bin/epylint @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_epylint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_epylint()) diff --git a/.venv/bin/iptest b/.venv/bin/iptest new file mode 100755 index 00000000..ee04e805 --- /dev/null +++ b/.venv/bin/iptest @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from IPython.testing.iptestcontroller import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/iptest3 b/.venv/bin/iptest3 new file mode 100755 index 00000000..ee04e805 --- /dev/null +++ b/.venv/bin/iptest3 @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from IPython.testing.iptestcontroller import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/ipython b/.venv/bin/ipython new file mode 100755 index 00000000..ed3ccbf8 --- /dev/null +++ b/.venv/bin/ipython @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from IPython import start_ipython +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(start_ipython()) diff --git a/.venv/bin/ipython3 b/.venv/bin/ipython3 new file mode 100755 index 00000000..ed3ccbf8 --- /dev/null +++ b/.venv/bin/ipython3 @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from IPython import start_ipython +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(start_ipython()) diff --git a/.venv/bin/isort b/.venv/bin/isort new file mode 100755 index 00000000..31003f44 --- /dev/null +++ b/.venv/bin/isort @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from isort.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/jupyter b/.venv/bin/jupyter new file mode 100755 index 00000000..a764bc19 --- /dev/null +++ b/.venv/bin/jupyter @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from jupyter_core.command import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/jupyter-kernel b/.venv/bin/jupyter-kernel new file mode 100755 index 00000000..ef83da4e --- /dev/null +++ b/.venv/bin/jupyter-kernel @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from jupyter_client.kernelapp import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/jupyter-kernelspec b/.venv/bin/jupyter-kernelspec new file mode 100755 index 00000000..d1e4bdad --- /dev/null +++ b/.venv/bin/jupyter-kernelspec @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from jupyter_client.kernelspecapp import KernelSpecApp +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(KernelSpecApp.launch_instance()) diff --git a/.venv/bin/jupyter-migrate b/.venv/bin/jupyter-migrate new file mode 100755 index 00000000..b67e6f25 --- /dev/null +++ b/.venv/bin/jupyter-migrate @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from jupyter_core.migrate import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/jupyter-run b/.venv/bin/jupyter-run new file mode 100755 index 00000000..48b3df0a --- /dev/null +++ b/.venv/bin/jupyter-run @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from jupyter_client.runapp import RunApp +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(RunApp.launch_instance()) diff --git a/.venv/bin/jupyter-troubleshoot b/.venv/bin/jupyter-troubleshoot new file mode 100755 index 00000000..a9d59bce --- /dev/null +++ b/.venv/bin/jupyter-troubleshoot @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from jupyter_core.troubleshoot import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip b/.venv/bin/pip new file mode 100755 index 00000000..3a931e15 --- /dev/null +++ b/.venv/bin/pip @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3 b/.venv/bin/pip3 new file mode 100755 index 00000000..3a931e15 --- /dev/null +++ b/.venv/bin/pip3 @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pip3.8 b/.venv/bin/pip3.8 new file mode 100755 index 00000000..3a931e15 --- /dev/null +++ b/.venv/bin/pip3.8 @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys +from pip._internal.cli.main import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pygmentize b/.venv/bin/pygmentize new file mode 100755 index 00000000..c9efa388 --- /dev/null +++ b/.venv/bin/pygmentize @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pygments.cmdline import main +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/.venv/bin/pylint b/.venv/bin/pylint new file mode 100755 index 00000000..9620c70b --- /dev/null +++ b/.venv/bin/pylint @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_pylint +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_pylint()) diff --git a/.venv/bin/pyreverse b/.venv/bin/pyreverse new file mode 100755 index 00000000..3423935e --- /dev/null +++ b/.venv/bin/pyreverse @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_pyreverse +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_pyreverse()) diff --git a/.venv/bin/python b/.venv/bin/python new file mode 120000 index 00000000..b8a0adbb --- /dev/null +++ b/.venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/.venv/bin/python3 b/.venv/bin/python3 new file mode 120000 index 00000000..ae65fdaa --- /dev/null +++ b/.venv/bin/python3 @@ -0,0 +1 @@ +/usr/bin/python3 \ No newline at end of file diff --git a/.venv/bin/symilar b/.venv/bin/symilar new file mode 100755 index 00000000..3c920228 --- /dev/null +++ b/.venv/bin/symilar @@ -0,0 +1,8 @@ +#!/home/antonio/bin/CursoPythonDavidBombal/.venv/bin/python +# -*- coding: utf-8 -*- +import re +import sys +from pylint import run_symilar +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) + sys.exit(run_symilar()) diff --git a/.venv/lib/python3.8/site-packages/IPython/__init__.py b/.venv/lib/python3.8/site-packages/IPython/__init__.py new file mode 100644 index 00000000..4fb77107 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/__init__.py @@ -0,0 +1,152 @@ +# encoding: utf-8 +""" +IPython: tools for interactive and parallel computing in Python. + +https://ipython.org +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2008-2011, IPython Development Team. +# Copyright (c) 2001-2007, Fernando Perez +# Copyright (c) 2001, Janko Hauser +# Copyright (c) 2001, Nathaniel Gray +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os +import sys + +#----------------------------------------------------------------------------- +# Setup everything +#----------------------------------------------------------------------------- + +# Don't forget to also update setup.py when this changes! +if sys.version_info < (3, 6): + raise ImportError( +""" +IPython 7.10+ supports Python 3.6 and above. +When using Python 2.7, please install IPython 5.x LTS Long Term Support version. +Python 3.3 and 3.4 were supported up to IPython 6.x. +Python 3.5 was supported with IPython 7.0 to 7.9. + +See IPython `README.rst` file for more information: + + https://github.com/ipython/ipython/blob/master/README.rst + +""") + +# Make it easy to import extensions - they are always directly on pythonpath. +# Therefore, non-IPython modules can be added to extensions directory. +# This should probably be in ipapp.py. +sys.path.append(os.path.join(os.path.dirname(__file__), "extensions")) + +#----------------------------------------------------------------------------- +# Setup the top level names +#----------------------------------------------------------------------------- + +from .core.getipython import get_ipython +from .core import release +from .core.application import Application +from .terminal.embed import embed + +from .core.interactiveshell import InteractiveShell +from .testing import test +from .utils.sysinfo import sys_info +from .utils.frame import extract_module_locals + +# Release data +__author__ = '%s <%s>' % (release.author, release.author_email) +__license__ = release.license +__version__ = release.version +version_info = release.version_info + +def embed_kernel(module=None, local_ns=None, **kwargs): + """Embed and start an IPython kernel in a given scope. + + If you don't want the kernel to initialize the namespace + from the scope of the surrounding function, + and/or you want to load full IPython configuration, + you probably want `IPython.start_kernel()` instead. + + Parameters + ---------- + module : types.ModuleType, optional + The module to load into IPython globals (default: caller) + local_ns : dict, optional + The namespace to load into IPython user namespace (default: caller) + + kwargs : various, optional + Further keyword args are relayed to the IPKernelApp constructor, + allowing configuration of the Kernel. Will only have an effect + on the first embed_kernel call for a given process. + """ + + (caller_module, caller_locals) = extract_module_locals(1) + if module is None: + module = caller_module + if local_ns is None: + local_ns = caller_locals + + # Only import .zmq when we really need it + from ipykernel.embed import embed_kernel as real_embed_kernel + real_embed_kernel(module=module, local_ns=local_ns, **kwargs) + +def start_ipython(argv=None, **kwargs): + """Launch a normal IPython instance (as opposed to embedded) + + `IPython.embed()` puts a shell in a particular calling scope, + such as a function or method for debugging purposes, + which is often not desirable. + + `start_ipython()` does full, regular IPython initialization, + including loading startup files, configuration, etc. + much of which is skipped by `embed()`. + + This is a public API method, and will survive implementation changes. + + Parameters + ---------- + + argv : list or None, optional + If unspecified or None, IPython will parse command-line options from sys.argv. + To prevent any command-line parsing, pass an empty list: `argv=[]`. + user_ns : dict, optional + specify this dictionary to initialize the IPython user namespace with particular values. + kwargs : various, optional + Any other kwargs will be passed to the Application constructor, + such as `config`. + """ + from IPython.terminal.ipapp import launch_new_instance + return launch_new_instance(argv=argv, **kwargs) + +def start_kernel(argv=None, **kwargs): + """Launch a normal IPython kernel instance (as opposed to embedded) + + `IPython.embed_kernel()` puts a shell in a particular calling scope, + such as a function or method for debugging purposes, + which is often not desirable. + + `start_kernel()` does full, regular IPython initialization, + including loading startup files, configuration, etc. + much of which is skipped by `embed()`. + + Parameters + ---------- + + argv : list or None, optional + If unspecified or None, IPython will parse command-line options from sys.argv. + To prevent any command-line parsing, pass an empty list: `argv=[]`. + user_ns : dict, optional + specify this dictionary to initialize the IPython user namespace with particular values. + kwargs : various, optional + Any other kwargs will be passed to the Application constructor, + such as `config`. + """ + from IPython.kernel.zmq.kernelapp import launch_new_instance + return launch_new_instance(argv=argv, **kwargs) diff --git a/.venv/lib/python3.8/site-packages/IPython/__main__.py b/.venv/lib/python3.8/site-packages/IPython/__main__.py new file mode 100644 index 00000000..d5123f33 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/__main__.py @@ -0,0 +1,14 @@ +# encoding: utf-8 +"""Terminal-based IPython entry point. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012, IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +from IPython import start_ipython + +start_ipython() diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..b5732f06 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/__main__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/__main__.cpython-38.pyc new file mode 100644 index 00000000..e1f8147e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/__main__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/config.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/config.cpython-38.pyc new file mode 100644 index 00000000..85d5da4f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/config.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/conftest.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/conftest.cpython-38.pyc new file mode 100644 index 00000000..53881f03 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/conftest.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/consoleapp.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/consoleapp.cpython-38.pyc new file mode 100644 index 00000000..8d7b6284 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/consoleapp.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/display.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/display.cpython-38.pyc new file mode 100644 index 00000000..9bba0c1e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/display.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/frontend.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/frontend.cpython-38.pyc new file mode 100644 index 00000000..1e61e4a7 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/frontend.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/html.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/html.cpython-38.pyc new file mode 100644 index 00000000..be059895 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/html.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/nbconvert.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/nbconvert.cpython-38.pyc new file mode 100644 index 00000000..e773f60e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/nbconvert.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/nbformat.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/nbformat.cpython-38.pyc new file mode 100644 index 00000000..72a3d5da Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/nbformat.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/parallel.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/parallel.cpython-38.pyc new file mode 100644 index 00000000..c1bd9d7b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/parallel.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/paths.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/paths.cpython-38.pyc new file mode 100644 index 00000000..69057937 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/paths.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/__pycache__/qt.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/__pycache__/qt.cpython-38.pyc new file mode 100644 index 00000000..22783167 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/__pycache__/qt.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/config.py b/.venv/lib/python3.8/site-packages/IPython/config.py new file mode 100644 index 00000000..964f46f1 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/config.py @@ -0,0 +1,19 @@ +""" +Shim to maintain backwards compatibility with old IPython.config imports. +""" +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import sys +from warnings import warn + +from .utils.shimmodule import ShimModule, ShimWarning + +warn("The `IPython.config` package has been deprecated since IPython 4.0. " + "You should import from traitlets.config instead.", ShimWarning) + + +# Unconditionally insert the shim into sys.modules so that further import calls +# trigger the custom attribute access above + +sys.modules['IPython.config'] = ShimModule(src='IPython.config', mirror='traitlets.config') diff --git a/.venv/lib/python3.8/site-packages/IPython/conftest.py b/.venv/lib/python3.8/site-packages/IPython/conftest.py new file mode 100644 index 00000000..8b2af8c0 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/conftest.py @@ -0,0 +1,69 @@ +import types +import sys +import builtins +import os +import pytest +import pathlib +import shutil + +from .testing import tools + + +def get_ipython(): + from .terminal.interactiveshell import TerminalInteractiveShell + if TerminalInteractiveShell._instance: + return TerminalInteractiveShell.instance() + + config = tools.default_config() + config.TerminalInteractiveShell.simple_prompt = True + + # Create and initialize our test-friendly IPython instance. + shell = TerminalInteractiveShell.instance(config=config) + return shell + + +@pytest.fixture(scope='session', autouse=True) +def work_path(): + path = pathlib.Path("./tmp-ipython-pytest-profiledir") + os.environ["IPYTHONDIR"] = str(path.absolute()) + if path.exists(): + raise ValueError('IPython dir temporary path already exists ! Did previous test run exit successfully ?') + path.mkdir() + yield + shutil.rmtree(str(path.resolve())) + + +def nopage(strng, start=0, screen_lines=0, pager_cmd=None): + if isinstance(strng, dict): + strng = strng.get("text/plain", "") + print(strng) + + +def xsys(self, cmd): + """Replace the default system call with a capturing one for doctest. + """ + # We use getoutput, but we need to strip it because pexpect captures + # the trailing newline differently from commands.getoutput + print(self.getoutput(cmd, split=False, depth=1).rstrip(), end="", file=sys.stdout) + sys.stdout.flush() + + +# for things to work correctly we would need this as a session fixture; +# unfortunately this will fail on some test that get executed as _collection_ +# time (before the fixture run), in particular parametrized test that contain +# yields. so for now execute at import time. +#@pytest.fixture(autouse=True, scope='session') +def inject(): + + builtins.get_ipython = get_ipython + builtins._ip = get_ipython() + builtins.ip = get_ipython() + builtins.ip.system = types.MethodType(xsys, ip) + builtins.ip.builtin_trap.activate() + from .core import page + + page.pager_page = nopage + # yield + + +inject() diff --git a/.venv/lib/python3.8/site-packages/IPython/consoleapp.py b/.venv/lib/python3.8/site-packages/IPython/consoleapp.py new file mode 100644 index 00000000..c2bbe188 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/consoleapp.py @@ -0,0 +1,12 @@ +""" +Shim to maintain backwards compatibility with old IPython.consoleapp imports. +""" +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +from warnings import warn + +warn("The `IPython.consoleapp` package has been deprecated since IPython 4.0." + "You should import from jupyter_client.consoleapp instead.", stacklevel=2) + +from jupyter_client.consoleapp import * diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__init__.py b/.venv/lib/python3.8/site-packages/IPython/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..30e0fba9 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/alias.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/alias.cpython-38.pyc new file mode 100644 index 00000000..23f8bef3 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/alias.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/application.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/application.cpython-38.pyc new file mode 100644 index 00000000..c999338a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/application.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/async_helpers.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/async_helpers.cpython-38.pyc new file mode 100644 index 00000000..f9497c6f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/async_helpers.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/autocall.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/autocall.cpython-38.pyc new file mode 100644 index 00000000..ba2bcfe1 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/autocall.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/builtin_trap.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/builtin_trap.cpython-38.pyc new file mode 100644 index 00000000..78edd181 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/builtin_trap.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/compilerop.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/compilerop.cpython-38.pyc new file mode 100644 index 00000000..1f82bb00 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/compilerop.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/completer.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/completer.cpython-38.pyc new file mode 100644 index 00000000..f83bc43b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/completer.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/completerlib.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/completerlib.cpython-38.pyc new file mode 100644 index 00000000..2a0718de Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/completerlib.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/crashhandler.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/crashhandler.cpython-38.pyc new file mode 100644 index 00000000..d6dbb762 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/crashhandler.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/debugger.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/debugger.cpython-38.pyc new file mode 100644 index 00000000..0a638031 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/debugger.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/display.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/display.cpython-38.pyc new file mode 100644 index 00000000..0b159ba6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/display.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/display_trap.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/display_trap.cpython-38.pyc new file mode 100644 index 00000000..7104956c Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/display_trap.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/displayhook.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/displayhook.cpython-38.pyc new file mode 100644 index 00000000..b5594663 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/displayhook.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/displaypub.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/displaypub.cpython-38.pyc new file mode 100644 index 00000000..a7eea205 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/displaypub.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/error.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/error.cpython-38.pyc new file mode 100644 index 00000000..d12b6dcc Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/error.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/events.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/events.cpython-38.pyc new file mode 100644 index 00000000..9ec07970 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/events.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/excolors.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/excolors.cpython-38.pyc new file mode 100644 index 00000000..90558b94 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/excolors.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/extensions.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/extensions.cpython-38.pyc new file mode 100644 index 00000000..bf60b6f8 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/extensions.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/formatters.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/formatters.cpython-38.pyc new file mode 100644 index 00000000..423c850a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/formatters.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/getipython.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/getipython.cpython-38.pyc new file mode 100644 index 00000000..858f3520 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/getipython.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/history.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/history.cpython-38.pyc new file mode 100644 index 00000000..9c9eec6f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/history.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/historyapp.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/historyapp.cpython-38.pyc new file mode 100644 index 00000000..bb280849 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/historyapp.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/hooks.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/hooks.cpython-38.pyc new file mode 100644 index 00000000..1ab3a40f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/hooks.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputsplitter.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputsplitter.cpython-38.pyc new file mode 100644 index 00000000..e64d0bd3 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputsplitter.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputtransformer.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputtransformer.cpython-38.pyc new file mode 100644 index 00000000..99234c67 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputtransformer.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputtransformer2.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputtransformer2.cpython-38.pyc new file mode 100644 index 00000000..b1b0e91c Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/inputtransformer2.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/interactiveshell.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/interactiveshell.cpython-38.pyc new file mode 100644 index 00000000..02eb2b8e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/interactiveshell.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/latex_symbols.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/latex_symbols.cpython-38.pyc new file mode 100644 index 00000000..a1d0ab69 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/latex_symbols.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/logger.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/logger.cpython-38.pyc new file mode 100644 index 00000000..929470d3 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/logger.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/macro.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/macro.cpython-38.pyc new file mode 100644 index 00000000..46ab7203 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/macro.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/magic.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/magic.cpython-38.pyc new file mode 100644 index 00000000..95ba2e34 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/magic.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/magic_arguments.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/magic_arguments.cpython-38.pyc new file mode 100644 index 00000000..737ff472 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/magic_arguments.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/oinspect.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/oinspect.cpython-38.pyc new file mode 100644 index 00000000..4fa45a0c Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/oinspect.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/page.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/page.cpython-38.pyc new file mode 100644 index 00000000..40a39a6b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/page.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/payload.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/payload.cpython-38.pyc new file mode 100644 index 00000000..dd3fd1dd Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/payload.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/payloadpage.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/payloadpage.cpython-38.pyc new file mode 100644 index 00000000..56187b1b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/payloadpage.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/prefilter.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/prefilter.cpython-38.pyc new file mode 100644 index 00000000..57dc76f9 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/prefilter.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/profileapp.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/profileapp.cpython-38.pyc new file mode 100644 index 00000000..6d385775 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/profileapp.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/profiledir.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/profiledir.cpython-38.pyc new file mode 100644 index 00000000..e21eed7c Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/profiledir.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/prompts.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/prompts.cpython-38.pyc new file mode 100644 index 00000000..a3d59fd2 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/prompts.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/pylabtools.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/pylabtools.cpython-38.pyc new file mode 100644 index 00000000..4e6aed05 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/pylabtools.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/release.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/release.cpython-38.pyc new file mode 100644 index 00000000..92462cb3 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/release.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/shellapp.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/shellapp.cpython-38.pyc new file mode 100644 index 00000000..bfe351f5 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/shellapp.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/splitinput.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/splitinput.cpython-38.pyc new file mode 100644 index 00000000..c9a8978c Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/splitinput.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/ultratb.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/ultratb.cpython-38.pyc new file mode 100644 index 00000000..29685bf1 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/ultratb.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/usage.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/usage.cpython-38.pyc new file mode 100644 index 00000000..fc138beb Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/core/__pycache__/usage.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/core/alias.py b/.venv/lib/python3.8/site-packages/IPython/core/alias.py new file mode 100644 index 00000000..2ad99023 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/alias.py @@ -0,0 +1,258 @@ +# encoding: utf-8 +""" +System command aliases. + +Authors: + +* Fernando Perez +* Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os +import re +import sys + +from traitlets.config.configurable import Configurable +from .error import UsageError + +from traitlets import List, Instance +from logging import error + +#----------------------------------------------------------------------------- +# Utilities +#----------------------------------------------------------------------------- + +# This is used as the pattern for calls to split_user_input. +shell_line_split = re.compile(r'^(\s*)()(\S+)(.*$)') + +def default_aliases(): + """Return list of shell aliases to auto-define. + """ + # Note: the aliases defined here should be safe to use on a kernel + # regardless of what frontend it is attached to. Frontends that use a + # kernel in-process can define additional aliases that will only work in + # their case. For example, things like 'less' or 'clear' that manipulate + # the terminal should NOT be declared here, as they will only work if the + # kernel is running inside a true terminal, and not over the network. + + if os.name == 'posix': + default_aliases = [('mkdir', 'mkdir'), ('rmdir', 'rmdir'), + ('mv', 'mv'), ('rm', 'rm'), ('cp', 'cp'), + ('cat', 'cat'), + ] + # Useful set of ls aliases. The GNU and BSD options are a little + # different, so we make aliases that provide as similar as possible + # behavior in ipython, by passing the right flags for each platform + if sys.platform.startswith('linux'): + ls_aliases = [('ls', 'ls -F --color'), + # long ls + ('ll', 'ls -F -o --color'), + # ls normal files only + ('lf', 'ls -F -o --color %l | grep ^-'), + # ls symbolic links + ('lk', 'ls -F -o --color %l | grep ^l'), + # directories or links to directories, + ('ldir', 'ls -F -o --color %l | grep /$'), + # things which are executable + ('lx', 'ls -F -o --color %l | grep ^-..x'), + ] + elif sys.platform.startswith('openbsd') or sys.platform.startswith('netbsd'): + # OpenBSD, NetBSD. The ls implementation on these platforms do not support + # the -G switch and lack the ability to use colorized output. + ls_aliases = [('ls', 'ls -F'), + # long ls + ('ll', 'ls -F -l'), + # ls normal files only + ('lf', 'ls -F -l %l | grep ^-'), + # ls symbolic links + ('lk', 'ls -F -l %l | grep ^l'), + # directories or links to directories, + ('ldir', 'ls -F -l %l | grep /$'), + # things which are executable + ('lx', 'ls -F -l %l | grep ^-..x'), + ] + else: + # BSD, OSX, etc. + ls_aliases = [('ls', 'ls -F -G'), + # long ls + ('ll', 'ls -F -l -G'), + # ls normal files only + ('lf', 'ls -F -l -G %l | grep ^-'), + # ls symbolic links + ('lk', 'ls -F -l -G %l | grep ^l'), + # directories or links to directories, + ('ldir', 'ls -F -G -l %l | grep /$'), + # things which are executable + ('lx', 'ls -F -l -G %l | grep ^-..x'), + ] + default_aliases = default_aliases + ls_aliases + elif os.name in ['nt', 'dos']: + default_aliases = [('ls', 'dir /on'), + ('ddir', 'dir /ad /on'), ('ldir', 'dir /ad /on'), + ('mkdir', 'mkdir'), ('rmdir', 'rmdir'), + ('echo', 'echo'), ('ren', 'ren'), ('copy', 'copy'), + ] + else: + default_aliases = [] + + return default_aliases + + +class AliasError(Exception): + pass + + +class InvalidAliasError(AliasError): + pass + +class Alias(object): + """Callable object storing the details of one alias. + + Instances are registered as magic functions to allow use of aliases. + """ + + # Prepare blacklist + blacklist = {'cd','popd','pushd','dhist','alias','unalias'} + + def __init__(self, shell, name, cmd): + self.shell = shell + self.name = name + self.cmd = cmd + self.__doc__ = "Alias for `!{}`".format(cmd) + self.nargs = self.validate() + + def validate(self): + """Validate the alias, and return the number of arguments.""" + if self.name in self.blacklist: + raise InvalidAliasError("The name %s can't be aliased " + "because it is a keyword or builtin." % self.name) + try: + caller = self.shell.magics_manager.magics['line'][self.name] + except KeyError: + pass + else: + if not isinstance(caller, Alias): + raise InvalidAliasError("The name %s can't be aliased " + "because it is another magic command." % self.name) + + if not (isinstance(self.cmd, str)): + raise InvalidAliasError("An alias command must be a string, " + "got: %r" % self.cmd) + + nargs = self.cmd.count('%s') - self.cmd.count('%%s') + + if (nargs > 0) and (self.cmd.find('%l') >= 0): + raise InvalidAliasError('The %s and %l specifiers are mutually ' + 'exclusive in alias definitions.') + + return nargs + + def __repr__(self): + return "".format(self.name, self.cmd) + + def __call__(self, rest=''): + cmd = self.cmd + nargs = self.nargs + # Expand the %l special to be the user's input line + if cmd.find('%l') >= 0: + cmd = cmd.replace('%l', rest) + rest = '' + + if nargs==0: + if cmd.find('%%s') >= 1: + cmd = cmd.replace('%%s', '%s') + # Simple, argument-less aliases + cmd = '%s %s' % (cmd, rest) + else: + # Handle aliases with positional arguments + args = rest.split(None, nargs) + if len(args) < nargs: + raise UsageError('Alias <%s> requires %s arguments, %s given.' % + (self.name, nargs, len(args))) + cmd = '%s %s' % (cmd % tuple(args[:nargs]),' '.join(args[nargs:])) + + self.shell.system(cmd) + +#----------------------------------------------------------------------------- +# Main AliasManager class +#----------------------------------------------------------------------------- + +class AliasManager(Configurable): + + default_aliases = List(default_aliases()).tag(config=True) + user_aliases = List(default_value=[]).tag(config=True) + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True) + + def __init__(self, shell=None, **kwargs): + super(AliasManager, self).__init__(shell=shell, **kwargs) + # For convenient access + self.linemagics = self.shell.magics_manager.magics['line'] + self.init_aliases() + + def init_aliases(self): + # Load default & user aliases + for name, cmd in self.default_aliases + self.user_aliases: + if cmd.startswith('ls ') and self.shell.colors == 'NoColor': + cmd = cmd.replace(' --color', '') + self.soft_define_alias(name, cmd) + + @property + def aliases(self): + return [(n, func.cmd) for (n, func) in self.linemagics.items() + if isinstance(func, Alias)] + + def soft_define_alias(self, name, cmd): + """Define an alias, but don't raise on an AliasError.""" + try: + self.define_alias(name, cmd) + except AliasError as e: + error("Invalid alias: %s" % e) + + def define_alias(self, name, cmd): + """Define a new alias after validating it. + + This will raise an :exc:`AliasError` if there are validation + problems. + """ + caller = Alias(shell=self.shell, name=name, cmd=cmd) + self.shell.magics_manager.register_function(caller, magic_kind='line', + magic_name=name) + + def get_alias(self, name): + """Return an alias, or None if no alias by that name exists.""" + aname = self.linemagics.get(name, None) + return aname if isinstance(aname, Alias) else None + + def is_alias(self, name): + """Return whether or not a given name has been defined as an alias""" + return self.get_alias(name) is not None + + def undefine_alias(self, name): + if self.is_alias(name): + del self.linemagics[name] + else: + raise ValueError('%s is not an alias' % name) + + def clear_aliases(self): + for name, cmd in self.aliases: + self.undefine_alias(name) + + def retrieve_alias(self, name): + """Retrieve the command to which an alias expands.""" + caller = self.get_alias(name) + if caller: + return caller.cmd + else: + raise ValueError('%s is not an alias' % name) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/application.py b/.venv/lib/python3.8/site-packages/IPython/core/application.py new file mode 100644 index 00000000..93639d88 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/application.py @@ -0,0 +1,462 @@ +# encoding: utf-8 +""" +An application for IPython. + +All top-level applications should use the classes in this module for +handling configuration and creating configurables. + +The job of an :class:`Application` is to create the master configuration +object and then create the configurable objects, passing the config to them. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import atexit +from copy import deepcopy +import glob +import logging +import os +import shutil +import sys + +from traitlets.config.application import Application, catch_config_error +from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader +from IPython.core import release, crashhandler +from IPython.core.profiledir import ProfileDir, ProfileDirError +from IPython.paths import get_ipython_dir, get_ipython_package_dir +from IPython.utils.path import ensure_dir_exists +from traitlets import ( + List, Unicode, Type, Bool, Set, Instance, Undefined, + default, observe, +) + +if os.name == 'nt': + programdata = os.environ.get('PROGRAMDATA', None) + if programdata: + SYSTEM_CONFIG_DIRS = [os.path.join(programdata, 'ipython')] + else: # PROGRAMDATA is not defined by default on XP. + SYSTEM_CONFIG_DIRS = [] +else: + SYSTEM_CONFIG_DIRS = [ + "/usr/local/etc/ipython", + "/etc/ipython", + ] + + +ENV_CONFIG_DIRS = [] +_env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython') +if _env_config_dir not in SYSTEM_CONFIG_DIRS: + # only add ENV_CONFIG if sys.prefix is not already included + ENV_CONFIG_DIRS.append(_env_config_dir) + + +_envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS') +if _envvar in {None, ''}: + IPYTHON_SUPPRESS_CONFIG_ERRORS = None +else: + if _envvar.lower() in {'1','true'}: + IPYTHON_SUPPRESS_CONFIG_ERRORS = True + elif _envvar.lower() in {'0','false'} : + IPYTHON_SUPPRESS_CONFIG_ERRORS = False + else: + sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar ) + +# aliases and flags + +base_aliases = { + 'profile-dir' : 'ProfileDir.location', + 'profile' : 'BaseIPythonApplication.profile', + 'ipython-dir' : 'BaseIPythonApplication.ipython_dir', + 'log-level' : 'Application.log_level', + 'config' : 'BaseIPythonApplication.extra_config_file', +} + +base_flags = dict( + debug = ({'Application' : {'log_level' : logging.DEBUG}}, + "set log level to logging.DEBUG (maximize logging output)"), + quiet = ({'Application' : {'log_level' : logging.CRITICAL}}, + "set log level to logging.CRITICAL (minimize logging output)"), + init = ({'BaseIPythonApplication' : { + 'copy_config_files' : True, + 'auto_create' : True} + }, """Initialize profile with default config files. This is equivalent + to running `ipython profile create ` prior to startup. + """) +) + +class ProfileAwareConfigLoader(PyFileConfigLoader): + """A Python file config loader that is aware of IPython profiles.""" + def load_subconfig(self, fname, path=None, profile=None): + if profile is not None: + try: + profile_dir = ProfileDir.find_profile_dir_by_name( + get_ipython_dir(), + profile, + ) + except ProfileDirError: + return + path = profile_dir.location + return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path) + +class BaseIPythonApplication(Application): + + name = u'ipython' + description = Unicode(u'IPython: an enhanced interactive Python shell.') + version = Unicode(release.version) + + aliases = base_aliases + flags = base_flags + classes = List([ProfileDir]) + + # enable `load_subconfig('cfg.py', profile='name')` + python_config_loader_class = ProfileAwareConfigLoader + + # Track whether the config_file has changed, + # because some logic happens only if we aren't using the default. + config_file_specified = Set() + + config_file_name = Unicode() + @default('config_file_name') + def _config_file_name_default(self): + return self.name.replace('-','_') + u'_config.py' + @observe('config_file_name') + def _config_file_name_changed(self, change): + if change['new'] != change['old']: + self.config_file_specified.add(change['new']) + + # The directory that contains IPython's builtin profiles. + builtin_profile_dir = Unicode( + os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default') + ) + + config_file_paths = List(Unicode()) + @default('config_file_paths') + def _config_file_paths_default(self): + return [os.getcwd()] + + extra_config_file = Unicode( + help="""Path to an extra config file to load. + + If specified, load this config file in addition to any other IPython config. + """).tag(config=True) + @observe('extra_config_file') + def _extra_config_file_changed(self, change): + old = change['old'] + new = change['new'] + try: + self.config_files.remove(old) + except ValueError: + pass + self.config_file_specified.add(new) + self.config_files.append(new) + + profile = Unicode(u'default', + help="""The IPython profile to use.""" + ).tag(config=True) + + @observe('profile') + def _profile_changed(self, change): + self.builtin_profile_dir = os.path.join( + get_ipython_package_dir(), u'config', u'profile', change['new'] + ) + + ipython_dir = Unicode( + help=""" + The name of the IPython directory. This directory is used for logging + configuration (through profiles), history storage, etc. The default + is usually $HOME/.ipython. This option can also be specified through + the environment variable IPYTHONDIR. + """ + ).tag(config=True) + @default('ipython_dir') + def _ipython_dir_default(self): + d = get_ipython_dir() + self._ipython_dir_changed({ + 'name': 'ipython_dir', + 'old': d, + 'new': d, + }) + return d + + _in_init_profile_dir = False + profile_dir = Instance(ProfileDir, allow_none=True) + @default('profile_dir') + def _profile_dir_default(self): + # avoid recursion + if self._in_init_profile_dir: + return + # profile_dir requested early, force initialization + self.init_profile_dir() + return self.profile_dir + + overwrite = Bool(False, + help="""Whether to overwrite existing config files when copying""" + ).tag(config=True) + auto_create = Bool(False, + help="""Whether to create profile dir if it doesn't exist""" + ).tag(config=True) + + config_files = List(Unicode()) + @default('config_files') + def _config_files_default(self): + return [self.config_file_name] + + copy_config_files = Bool(False, + help="""Whether to install the default config files into the profile dir. + If a new profile is being created, and IPython contains config files for that + profile, then they will be staged into the new directory. Otherwise, + default config files will be automatically generated. + """).tag(config=True) + + verbose_crash = Bool(False, + help="""Create a massive crash report when IPython encounters what may be an + internal error. The default is to append a short message to the + usual traceback""").tag(config=True) + + # The class to use as the crash handler. + crash_handler_class = Type(crashhandler.CrashHandler) + + @catch_config_error + def __init__(self, **kwargs): + super(BaseIPythonApplication, self).__init__(**kwargs) + # ensure current working directory exists + try: + os.getcwd() + except: + # exit if cwd doesn't exist + self.log.error("Current working directory doesn't exist.") + self.exit(1) + + #------------------------------------------------------------------------- + # Various stages of Application creation + #------------------------------------------------------------------------- + + deprecated_subcommands = {} + + def initialize_subcommand(self, subc, argv=None): + if subc in self.deprecated_subcommands: + self.log.warning("Subcommand `ipython {sub}` is deprecated and will be removed " + "in future versions.".format(sub=subc)) + self.log.warning("You likely want to use `jupyter {sub}` in the " + "future".format(sub=subc)) + return super(BaseIPythonApplication, self).initialize_subcommand(subc, argv) + + def init_crash_handler(self): + """Create a crash handler, typically setting sys.excepthook to it.""" + self.crash_handler = self.crash_handler_class(self) + sys.excepthook = self.excepthook + def unset_crashhandler(): + sys.excepthook = sys.__excepthook__ + atexit.register(unset_crashhandler) + + def excepthook(self, etype, evalue, tb): + """this is sys.excepthook after init_crashhandler + + set self.verbose_crash=True to use our full crashhandler, instead of + a regular traceback with a short message (crash_handler_lite) + """ + + if self.verbose_crash: + return self.crash_handler(etype, evalue, tb) + else: + return crashhandler.crash_handler_lite(etype, evalue, tb) + + @observe('ipython_dir') + def _ipython_dir_changed(self, change): + old = change['old'] + new = change['new'] + if old is not Undefined: + str_old = os.path.abspath(old) + if str_old in sys.path: + sys.path.remove(str_old) + str_path = os.path.abspath(new) + sys.path.append(str_path) + ensure_dir_exists(new) + readme = os.path.join(new, 'README') + readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README') + if not os.path.exists(readme) and os.path.exists(readme_src): + shutil.copy(readme_src, readme) + for d in ('extensions', 'nbextensions'): + path = os.path.join(new, d) + try: + ensure_dir_exists(path) + except OSError as e: + # this will not be EEXIST + self.log.error("couldn't create path %s: %s", path, e) + self.log.debug("IPYTHONDIR set to: %s" % new) + + def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS): + """Load the config file. + + By default, errors in loading config are handled, and a warning + printed on screen. For testing, the suppress_errors option is set + to False, so errors will make tests fail. + + `suppress_errors` default value is to be `None` in which case the + behavior default to the one of `traitlets.Application`. + + The default value can be set : + - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive). + - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive). + - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset. + + Any other value are invalid, and will make IPython exit with a non-zero return code. + """ + + + self.log.debug("Searching path %s for config files", self.config_file_paths) + base_config = 'ipython_config.py' + self.log.debug("Attempting to load config file: %s" % + base_config) + try: + if suppress_errors is not None: + old_value = Application.raise_config_file_errors + Application.raise_config_file_errors = not suppress_errors; + Application.load_config_file( + self, + base_config, + path=self.config_file_paths + ) + except ConfigFileNotFound: + # ignore errors loading parent + self.log.debug("Config file %s not found", base_config) + pass + if suppress_errors is not None: + Application.raise_config_file_errors = old_value + + for config_file_name in self.config_files: + if not config_file_name or config_file_name == base_config: + continue + self.log.debug("Attempting to load config file: %s" % + self.config_file_name) + try: + Application.load_config_file( + self, + config_file_name, + path=self.config_file_paths + ) + except ConfigFileNotFound: + # Only warn if the default config file was NOT being used. + if config_file_name in self.config_file_specified: + msg = self.log.warning + else: + msg = self.log.debug + msg("Config file not found, skipping: %s", config_file_name) + except Exception: + # For testing purposes. + if not suppress_errors: + raise + self.log.warning("Error loading config file: %s" % + self.config_file_name, exc_info=True) + + def init_profile_dir(self): + """initialize the profile dir""" + self._in_init_profile_dir = True + if self.profile_dir is not None: + # already ran + return + if 'ProfileDir.location' not in self.config: + # location not specified, find by profile name + try: + p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config) + except ProfileDirError: + # not found, maybe create it (always create default profile) + if self.auto_create or self.profile == 'default': + try: + p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config) + except ProfileDirError: + self.log.fatal("Could not create profile: %r"%self.profile) + self.exit(1) + else: + self.log.info("Created profile dir: %r"%p.location) + else: + self.log.fatal("Profile %r not found."%self.profile) + self.exit(1) + else: + self.log.debug("Using existing profile dir: %r"%p.location) + else: + location = self.config.ProfileDir.location + # location is fully specified + try: + p = ProfileDir.find_profile_dir(location, self.config) + except ProfileDirError: + # not found, maybe create it + if self.auto_create: + try: + p = ProfileDir.create_profile_dir(location, self.config) + except ProfileDirError: + self.log.fatal("Could not create profile directory: %r"%location) + self.exit(1) + else: + self.log.debug("Creating new profile dir: %r"%location) + else: + self.log.fatal("Profile directory %r not found."%location) + self.exit(1) + else: + self.log.info("Using existing profile dir: %r"%location) + # if profile_dir is specified explicitly, set profile name + dir_name = os.path.basename(p.location) + if dir_name.startswith('profile_'): + self.profile = dir_name[8:] + + self.profile_dir = p + self.config_file_paths.append(p.location) + self._in_init_profile_dir = False + + def init_config_files(self): + """[optionally] copy default config files into profile dir.""" + self.config_file_paths.extend(ENV_CONFIG_DIRS) + self.config_file_paths.extend(SYSTEM_CONFIG_DIRS) + # copy config files + path = self.builtin_profile_dir + if self.copy_config_files: + src = self.profile + + cfg = self.config_file_name + if path and os.path.exists(os.path.join(path, cfg)): + self.log.warning("Staging %r from %s into %r [overwrite=%s]"%( + cfg, src, self.profile_dir.location, self.overwrite) + ) + self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite) + else: + self.stage_default_config_file() + else: + # Still stage *bundled* config files, but not generated ones + # This is necessary for `ipython profile=sympy` to load the profile + # on the first go + files = glob.glob(os.path.join(path, '*.py')) + for fullpath in files: + cfg = os.path.basename(fullpath) + if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False): + # file was copied + self.log.warning("Staging bundled %s from %s into %r"%( + cfg, self.profile, self.profile_dir.location) + ) + + + def stage_default_config_file(self): + """auto generate default config file, and stage it into the profile.""" + s = self.generate_config_file() + fname = os.path.join(self.profile_dir.location, self.config_file_name) + if self.overwrite or not os.path.exists(fname): + self.log.warning("Generating default config file: %r"%(fname)) + with open(fname, 'w') as f: + f.write(s) + + @catch_config_error + def initialize(self, argv=None): + # don't hook up crash handler before parsing command-line + self.parse_command_line(argv) + self.init_crash_handler() + if self.subapp is not None: + # stop here if subapp is taking over + return + # save a copy of CLI config to re-load after config files + # so that it has highest priority + cl_config = deepcopy(self.config) + self.init_profile_dir() + self.init_config_files() + self.load_config_file() + # enforce cl-opts override configfile opts: + self.update_config(cl_config) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/async_helpers.py b/.venv/lib/python3.8/site-packages/IPython/core/async_helpers.py new file mode 100644 index 00000000..fb4cc193 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/async_helpers.py @@ -0,0 +1,173 @@ +""" +Async helper function that are invalid syntax on Python 3.5 and below. + +This code is best effort, and may have edge cases not behaving as expected. In +particular it contain a number of heuristics to detect whether code is +effectively async and need to run in an event loop or not. + +Some constructs (like top-level `return`, or `yield`) are taken care of +explicitly to actually raise a SyntaxError and stay as close as possible to +Python semantics. +""" + + +import ast +import sys +import inspect +from textwrap import dedent, indent + + +class _AsyncIORunner: + + def __call__(self, coro): + """ + Handler for asyncio autoawait + """ + import asyncio + + return asyncio.get_event_loop().run_until_complete(coro) + + def __str__(self): + return 'asyncio' + +_asyncio_runner = _AsyncIORunner() + + +def _curio_runner(coroutine): + """ + handler for curio autoawait + """ + import curio + + return curio.run(coroutine) + + +def _trio_runner(async_fn): + import trio + + async def loc(coro): + """ + We need the dummy no-op async def to protect from + trio's internal. See https://github.com/python-trio/trio/issues/89 + """ + return await coro + + return trio.run(loc, async_fn) + + +def _pseudo_sync_runner(coro): + """ + A runner that does not really allow async execution, and just advance the coroutine. + + See discussion in https://github.com/python-trio/trio/issues/608, + + Credit to Nathaniel Smith + + """ + try: + coro.send(None) + except StopIteration as exc: + return exc.value + else: + # TODO: do not raise but return an execution result with the right info. + raise RuntimeError( + "{coro_name!r} needs a real async loop".format(coro_name=coro.__name__) + ) + + +def _asyncify(code: str) -> str: + """wrap code in async def definition. + + And setup a bit of context to run it later. + """ + res = dedent( + """ + async def __wrapper__(): + try: + {usercode} + finally: + locals() + """ + ).format(usercode=indent(code, " " * 8)) + return res + + +class _AsyncSyntaxErrorVisitor(ast.NodeVisitor): + """ + Find syntax errors that would be an error in an async repl, but because + the implementation involves wrapping the repl in an async function, it + is erroneously allowed (e.g. yield or return at the top level) + """ + def __init__(self): + if sys.version_info >= (3,8): + raise ValueError('DEPRECATED in Python 3.8+') + self.depth = 0 + super().__init__() + + def generic_visit(self, node): + func_types = (ast.FunctionDef, ast.AsyncFunctionDef) + invalid_types_by_depth = { + 0: (ast.Return, ast.Yield, ast.YieldFrom), + 1: (ast.Nonlocal,) + } + + should_traverse = self.depth < max(invalid_types_by_depth.keys()) + if isinstance(node, func_types) and should_traverse: + self.depth += 1 + super().generic_visit(node) + self.depth -= 1 + elif isinstance(node, invalid_types_by_depth[self.depth]): + raise SyntaxError() + else: + super().generic_visit(node) + + +def _async_parse_cell(cell: str) -> ast.AST: + """ + This is a compatibility shim for pre-3.7 when async outside of a function + is a syntax error at the parse stage. + + It will return an abstract syntax tree parsed as if async and await outside + of a function were not a syntax error. + """ + if sys.version_info < (3, 7): + # Prior to 3.7 you need to asyncify before parse + wrapped_parse_tree = ast.parse(_asyncify(cell)) + return wrapped_parse_tree.body[0].body[0] + else: + return ast.parse(cell) + + +def _should_be_async(cell: str) -> bool: + """Detect if a block of code need to be wrapped in an `async def` + + Attempt to parse the block of code, it it compile we're fine. + Otherwise we wrap if and try to compile. + + If it works, assume it should be async. Otherwise Return False. + + Not handled yet: If the block of code has a return statement as the top + level, it will be seen as async. This is a know limitation. + """ + if sys.version_info > (3, 8): + try: + code = compile(cell, "<>", "exec", flags=getattr(ast,'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0)) + return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE + except (SyntaxError, MemoryError): + return False + try: + # we can't limit ourself to ast.parse, as it __accepts__ to parse on + # 3.7+, but just does not _compile_ + code = compile(cell, "<>", "exec") + except (SyntaxError, MemoryError): + try: + parse_tree = _async_parse_cell(cell) + + # Raise a SyntaxError if there are top-level return or yields + v = _AsyncSyntaxErrorVisitor() + v.visit(parse_tree) + + except (SyntaxError, MemoryError): + return False + return True + return False diff --git a/.venv/lib/python3.8/site-packages/IPython/core/autocall.py b/.venv/lib/python3.8/site-packages/IPython/core/autocall.py new file mode 100644 index 00000000..bab7f859 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/autocall.py @@ -0,0 +1,70 @@ +# encoding: utf-8 +""" +Autocall capabilities for IPython.core. + +Authors: + +* Brian Granger +* Fernando Perez +* Thomas Kluyver + +Notes +----- +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +class IPyAutocall(object): + """ Instances of this class are always autocalled + + This happens regardless of 'autocall' variable state. Use this to + develop macro-like mechanisms. + """ + _ip = None + rewrite = True + def __init__(self, ip=None): + self._ip = ip + + def set_ip(self, ip): + """ Will be used to set _ip point to current ipython instance b/f call + + Override this method if you don't want this to happen. + + """ + self._ip = ip + + +class ExitAutocall(IPyAutocall): + """An autocallable object which will be added to the user namespace so that + exit, exit(), quit or quit() are all valid ways to close the shell.""" + rewrite = False + + def __call__(self): + self._ip.ask_exit() + +class ZMQExitAutocall(ExitAutocall): + """Exit IPython. Autocallable, so it needn't be explicitly called. + + Parameters + ---------- + keep_kernel : bool + If True, leave the kernel alive. Otherwise, tell the kernel to exit too + (default). + """ + def __call__(self, keep_kernel=False): + self._ip.keepkernel_on_exit = keep_kernel + self._ip.ask_exit() diff --git a/.venv/lib/python3.8/site-packages/IPython/core/builtin_trap.py b/.venv/lib/python3.8/site-packages/IPython/core/builtin_trap.py new file mode 100644 index 00000000..a8ea4abc --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/builtin_trap.py @@ -0,0 +1,86 @@ +""" +A context manager for managing things injected into :mod:`builtins`. +""" +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. +import builtins as builtin_mod + +from traitlets.config.configurable import Configurable + +from traitlets import Instance + + +class __BuiltinUndefined(object): pass +BuiltinUndefined = __BuiltinUndefined() + +class __HideBuiltin(object): pass +HideBuiltin = __HideBuiltin() + + +class BuiltinTrap(Configurable): + + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', + allow_none=True) + + def __init__(self, shell=None): + super(BuiltinTrap, self).__init__(shell=shell, config=None) + self._orig_builtins = {} + # We define this to track if a single BuiltinTrap is nested. + # Only turn off the trap when the outermost call to __exit__ is made. + self._nested_level = 0 + self.shell = shell + # builtins we always add - if set to HideBuiltin, they will just + # be removed instead of being replaced by something else + self.auto_builtins = {'exit': HideBuiltin, + 'quit': HideBuiltin, + 'get_ipython': self.shell.get_ipython, + } + + def __enter__(self): + if self._nested_level == 0: + self.activate() + self._nested_level += 1 + # I return self, so callers can use add_builtin in a with clause. + return self + + def __exit__(self, type, value, traceback): + if self._nested_level == 1: + self.deactivate() + self._nested_level -= 1 + # Returning False will cause exceptions to propagate + return False + + def add_builtin(self, key, value): + """Add a builtin and save the original.""" + bdict = builtin_mod.__dict__ + orig = bdict.get(key, BuiltinUndefined) + if value is HideBuiltin: + if orig is not BuiltinUndefined: #same as 'key in bdict' + self._orig_builtins[key] = orig + del bdict[key] + else: + self._orig_builtins[key] = orig + bdict[key] = value + + def remove_builtin(self, key, orig): + """Remove an added builtin and re-set the original.""" + if orig is BuiltinUndefined: + del builtin_mod.__dict__[key] + else: + builtin_mod.__dict__[key] = orig + + def activate(self): + """Store ipython references in the __builtin__ namespace.""" + + add_builtin = self.add_builtin + for name, func in self.auto_builtins.items(): + add_builtin(name, func) + + def deactivate(self): + """Remove any builtins which might have been added by add_builtins, or + restore overwritten ones to their previous values.""" + remove_builtin = self.remove_builtin + for key, val in self._orig_builtins.items(): + remove_builtin(key, val) + self._orig_builtins.clear() + self._builtins_added = False diff --git a/.venv/lib/python3.8/site-packages/IPython/core/compilerop.py b/.venv/lib/python3.8/site-packages/IPython/core/compilerop.py new file mode 100644 index 00000000..c4771af7 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/compilerop.py @@ -0,0 +1,160 @@ +"""Compiler tools with improved interactive support. + +Provides compilation machinery similar to codeop, but with caching support so +we can provide interactive tracebacks. + +Authors +------- +* Robert Kern +* Fernando Perez +* Thomas Kluyver +""" + +# Note: though it might be more natural to name this module 'compiler', that +# name is in the stdlib and name collisions with the stdlib tend to produce +# weird problems (often with third-party tools). + +#----------------------------------------------------------------------------- +# Copyright (C) 2010-2011 The IPython Development Team. +# +# Distributed under the terms of the BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib imports +import __future__ +from ast import PyCF_ONLY_AST +import codeop +import functools +import hashlib +import linecache +import operator +import time +from contextlib import contextmanager + +#----------------------------------------------------------------------------- +# Constants +#----------------------------------------------------------------------------- + +# Roughly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h, +# this is used as a bitmask to extract future-related code flags. +PyCF_MASK = functools.reduce(operator.or_, + (getattr(__future__, fname).compiler_flag + for fname in __future__.all_feature_names)) + +#----------------------------------------------------------------------------- +# Local utilities +#----------------------------------------------------------------------------- + +def code_name(code, number=0): + """ Compute a (probably) unique name for code for caching. + + This now expects code to be unicode. + """ + hash_digest = hashlib.sha1(code.encode("utf-8")).hexdigest() + # Include the number and 12 characters of the hash in the name. It's + # pretty much impossible that in a single session we'll have collisions + # even with truncated hashes, and the full one makes tracebacks too long + return ''.format(number, hash_digest[:12]) + +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- + +class CachingCompiler(codeop.Compile): + """A compiler that caches code compiled from interactive statements. + """ + + def __init__(self): + codeop.Compile.__init__(self) + + # This is ugly, but it must be done this way to allow multiple + # simultaneous ipython instances to coexist. Since Python itself + # directly accesses the data structures in the linecache module, and + # the cache therein is global, we must work with that data structure. + # We must hold a reference to the original checkcache routine and call + # that in our own check_cache() below, but the special IPython cache + # must also be shared by all IPython instances. If we were to hold + # separate caches (one in each CachingCompiler instance), any call made + # by Python itself to linecache.checkcache() would obliterate the + # cached data from the other IPython instances. + if not hasattr(linecache, '_ipython_cache'): + linecache._ipython_cache = {} + if not hasattr(linecache, '_checkcache_ori'): + linecache._checkcache_ori = linecache.checkcache + # Now, we must monkeypatch the linecache directly so that parts of the + # stdlib that call it outside our control go through our codepath + # (otherwise we'd lose our tracebacks). + linecache.checkcache = check_linecache_ipython + + + def ast_parse(self, source, filename='', symbol='exec'): + """Parse code to an AST with the current compiler flags active. + + Arguments are exactly the same as ast.parse (in the standard library), + and are passed to the built-in compile function.""" + return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1) + + def reset_compiler_flags(self): + """Reset compiler flags to default state.""" + # This value is copied from codeop.Compile.__init__, so if that ever + # changes, it will need to be updated. + self.flags = codeop.PyCF_DONT_IMPLY_DEDENT + + @property + def compiler_flags(self): + """Flags currently active in the compilation process. + """ + return self.flags + + def cache(self, code, number=0): + """Make a name for a block of code, and cache the code. + + Parameters + ---------- + code : str + The Python source code to cache. + number : int + A number which forms part of the code's name. Used for the execution + counter. + + Returns + ------- + The name of the cached code (as a string). Pass this as the filename + argument to compilation, so that tracebacks are correctly hooked up. + """ + name = code_name(code, number) + entry = (len(code), time.time(), + [line+'\n' for line in code.splitlines()], name) + linecache.cache[name] = entry + linecache._ipython_cache[name] = entry + return name + + @contextmanager + def extra_flags(self, flags): + ## bits that we'll set to 1 + turn_on_bits = ~self.flags & flags + + + self.flags = self.flags | flags + try: + yield + finally: + # turn off only the bits we turned on so that something like + # __future__ that set flags stays. + self.flags &= ~turn_on_bits + + +def check_linecache_ipython(*args): + """Call linecache.checkcache() safely protecting our cached values. + """ + # First call the original checkcache as intended + linecache._checkcache_ori(*args) + # Then, update back the cache with our data, so that tracebacks related + # to our compiled codes can be produced. + linecache.cache.update(linecache._ipython_cache) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/completer.py b/.venv/lib/python3.8/site-packages/IPython/core/completer.py new file mode 100644 index 00000000..bc114f0f --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/completer.py @@ -0,0 +1,2092 @@ +"""Completion for IPython. + +This module started as fork of the rlcompleter module in the Python standard +library. The original enhancements made to rlcompleter have been sent +upstream and were accepted as of Python 2.3, + +This module now support a wide variety of completion mechanism both available +for normal classic Python code, as well as completer for IPython specific +Syntax like magics. + +Latex and Unicode completion +============================ + +IPython and compatible frontends not only can complete your code, but can help +you to input a wide range of characters. In particular we allow you to insert +a unicode character using the tab completion mechanism. + +Forward latex/unicode completion +-------------------------------- + +Forward completion allows you to easily type a unicode character using its latex +name, or unicode long description. To do so type a backslash follow by the +relevant name and press tab: + + +Using latex completion: + +.. code:: + + \\alpha + α + +or using unicode completion: + + +.. code:: + + \\greek small letter alpha + α + + +Only valid Python identifiers will complete. Combining characters (like arrow or +dots) are also available, unlike latex they need to be put after the their +counterpart that is to say, `F\\\\vec` is correct, not `\\\\vecF`. + +Some browsers are known to display combining characters incorrectly. + +Backward latex completion +------------------------- + +It is sometime challenging to know how to type a character, if you are using +IPython, or any compatible frontend you can prepend backslash to the character +and press `` to expand it to its latex form. + +.. code:: + + \\α + \\alpha + + +Both forward and backward completions can be deactivated by setting the +``Completer.backslash_combining_completions`` option to ``False``. + + +Experimental +============ + +Starting with IPython 6.0, this module can make use of the Jedi library to +generate completions both using static analysis of the code, and dynamically +inspecting multiple namespaces. Jedi is an autocompletion and static analysis +for Python. The APIs attached to this new mechanism is unstable and will +raise unless use in an :any:`provisionalcompleter` context manager. + +You will find that the following are experimental: + + - :any:`provisionalcompleter` + - :any:`IPCompleter.completions` + - :any:`Completion` + - :any:`rectify_completions` + +.. note:: + + better name for :any:`rectify_completions` ? + +We welcome any feedback on these new API, and we also encourage you to try this +module in debug mode (start IPython with ``--Completer.debug=True``) in order +to have extra logging information if :any:`jedi` is crashing, or if current +IPython completer pending deprecations are returning results not yet handled +by :any:`jedi` + +Using Jedi for tab completion allow snippets like the following to work without +having to execute any code: + + >>> myvar = ['hello', 42] + ... myvar[1].bi + +Tab completion will be able to infer that ``myvar[1]`` is a real number without +executing any code unlike the previously available ``IPCompleter.greedy`` +option. + +Be sure to update :any:`jedi` to the latest stable version or to try the +current development version to get better completions. +""" + + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. +# +# Some of this code originated from rlcompleter in the Python standard library +# Copyright (C) 2001 Python Software Foundation, www.python.org + + +import __main__ +import builtins as builtin_mod +import glob +import time +import inspect +import itertools +import keyword +import os +import re +import sys +import unicodedata +import string +import warnings + +from contextlib import contextmanager +from importlib import import_module +from typing import Iterator, List, Tuple, Iterable +from types import SimpleNamespace + +from traitlets.config.configurable import Configurable +from IPython.core.error import TryNext +from IPython.core.inputtransformer2 import ESC_MAGIC +from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol +from IPython.core.oinspect import InspectColors +from IPython.utils import generics +from IPython.utils.dir2 import dir2, get_real_method +from IPython.utils.process import arg_split +from traitlets import Bool, Enum, observe, Int + +# skip module docstests +skip_doctest = True + +try: + import jedi + jedi.settings.case_insensitive_completion = False + import jedi.api.helpers + import jedi.api.classes + JEDI_INSTALLED = True +except ImportError: + JEDI_INSTALLED = False +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- + +# Public API +__all__ = ['Completer','IPCompleter'] + +if sys.platform == 'win32': + PROTECTABLES = ' ' +else: + PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&' + +# Protect against returning an enormous number of completions which the frontend +# may have trouble processing. +MATCHES_LIMIT = 500 + +_deprecation_readline_sentinel = object() + + +class ProvisionalCompleterWarning(FutureWarning): + """ + Exception raise by an experimental feature in this module. + + Wrap code in :any:`provisionalcompleter` context manager if you + are certain you want to use an unstable feature. + """ + pass + +warnings.filterwarnings('error', category=ProvisionalCompleterWarning) + +@contextmanager +def provisionalcompleter(action='ignore'): + """ + + + This context manager has to be used in any place where unstable completer + behavior and API may be called. + + >>> with provisionalcompleter(): + ... completer.do_experimental_things() # works + + >>> completer.do_experimental_things() # raises. + + .. note:: Unstable + + By using this context manager you agree that the API in use may change + without warning, and that you won't complain if they do so. + + You also understand that, if the API is not to your liking, you should report + a bug to explain your use case upstream. + + We'll be happy to get your feedback, feature requests, and improvements on + any of the unstable APIs! + """ + with warnings.catch_warnings(): + warnings.filterwarnings(action, category=ProvisionalCompleterWarning) + yield + + +def has_open_quotes(s): + """Return whether a string has open quotes. + + This simply counts whether the number of quote characters of either type in + the string is odd. + + Returns + ------- + If there is an open quote, the quote character is returned. Else, return + False. + """ + # We check " first, then ', so complex cases with nested quotes will get + # the " to take precedence. + if s.count('"') % 2: + return '"' + elif s.count("'") % 2: + return "'" + else: + return False + + +def protect_filename(s, protectables=PROTECTABLES): + """Escape a string to protect certain characters.""" + if set(s) & set(protectables): + if sys.platform == "win32": + return '"' + s + '"' + else: + return "".join(("\\" + c if c in protectables else c) for c in s) + else: + return s + + +def expand_user(path:str) -> Tuple[str, bool, str]: + """Expand ``~``-style usernames in strings. + + This is similar to :func:`os.path.expanduser`, but it computes and returns + extra information that will be useful if the input was being used in + computing completions, and you wish to return the completions with the + original '~' instead of its expanded value. + + Parameters + ---------- + path : str + String to be expanded. If no ~ is present, the output is the same as the + input. + + Returns + ------- + newpath : str + Result of ~ expansion in the input path. + tilde_expand : bool + Whether any expansion was performed or not. + tilde_val : str + The value that ~ was replaced with. + """ + # Default values + tilde_expand = False + tilde_val = '' + newpath = path + + if path.startswith('~'): + tilde_expand = True + rest = len(path)-1 + newpath = os.path.expanduser(path) + if rest: + tilde_val = newpath[:-rest] + else: + tilde_val = newpath + + return newpath, tilde_expand, tilde_val + + +def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str: + """Does the opposite of expand_user, with its outputs. + """ + if tilde_expand: + return path.replace(tilde_val, '~') + else: + return path + + +def completions_sorting_key(word): + """key for sorting completions + + This does several things: + + - Demote any completions starting with underscores to the end + - Insert any %magic and %%cellmagic completions in the alphabetical order + by their name + """ + prio1, prio2 = 0, 0 + + if word.startswith('__'): + prio1 = 2 + elif word.startswith('_'): + prio1 = 1 + + if word.endswith('='): + prio1 = -1 + + if word.startswith('%%'): + # If there's another % in there, this is something else, so leave it alone + if not "%" in word[2:]: + word = word[2:] + prio2 = 2 + elif word.startswith('%'): + if not "%" in word[1:]: + word = word[1:] + prio2 = 1 + + return prio1, word, prio2 + + +class _FakeJediCompletion: + """ + This is a workaround to communicate to the UI that Jedi has crashed and to + report a bug. Will be used only id :any:`IPCompleter.debug` is set to true. + + Added in IPython 6.0 so should likely be removed for 7.0 + + """ + + def __init__(self, name): + + self.name = name + self.complete = name + self.type = 'crashed' + self.name_with_symbols = name + self.signature = '' + self._origin = 'fake' + + def __repr__(self): + return '' + + +class Completion: + """ + Completion object used and return by IPython completers. + + .. warning:: Unstable + + This function is unstable, API may change without warning. + It will also raise unless use in proper context manager. + + This act as a middle ground :any:`Completion` object between the + :any:`jedi.api.classes.Completion` object and the Prompt Toolkit completion + object. While Jedi need a lot of information about evaluator and how the + code should be ran/inspected, PromptToolkit (and other frontend) mostly + need user facing information. + + - Which range should be replaced replaced by what. + - Some metadata (like completion type), or meta information to displayed to + the use user. + + For debugging purpose we can also store the origin of the completion (``jedi``, + ``IPython.python_matches``, ``IPython.magics_matches``...). + """ + + __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin'] + + def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='', signature='') -> None: + warnings.warn("``Completion`` is a provisional API (as of IPython 6.0). " + "It may change without warnings. " + "Use in corresponding context manager.", + category=ProvisionalCompleterWarning, stacklevel=2) + + self.start = start + self.end = end + self.text = text + self.type = type + self.signature = signature + self._origin = _origin + + def __repr__(self): + return '' % \ + (self.start, self.end, self.text, self.type or '?', self.signature or '?') + + def __eq__(self, other)->Bool: + """ + Equality and hash do not hash the type (as some completer may not be + able to infer the type), but are use to (partially) de-duplicate + completion. + + Completely de-duplicating completion is a bit tricker that just + comparing as it depends on surrounding text, which Completions are not + aware of. + """ + return self.start == other.start and \ + self.end == other.end and \ + self.text == other.text + + def __hash__(self): + return hash((self.start, self.end, self.text)) + + +_IC = Iterable[Completion] + + +def _deduplicate_completions(text: str, completions: _IC)-> _IC: + """ + Deduplicate a set of completions. + + .. warning:: Unstable + + This function is unstable, API may change without warning. + + Parameters + ---------- + text: str + text that should be completed. + completions: Iterator[Completion] + iterator over the completions to deduplicate + + Yields + ------ + `Completions` objects + + + Completions coming from multiple sources, may be different but end up having + the same effect when applied to ``text``. If this is the case, this will + consider completions as equal and only emit the first encountered. + + Not folded in `completions()` yet for debugging purpose, and to detect when + the IPython completer does return things that Jedi does not, but should be + at some point. + """ + completions = list(completions) + if not completions: + return + + new_start = min(c.start for c in completions) + new_end = max(c.end for c in completions) + + seen = set() + for c in completions: + new_text = text[new_start:c.start] + c.text + text[c.end:new_end] + if new_text not in seen: + yield c + seen.add(new_text) + + +def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC: + """ + Rectify a set of completions to all have the same ``start`` and ``end`` + + .. warning:: Unstable + + This function is unstable, API may change without warning. + It will also raise unless use in proper context manager. + + Parameters + ---------- + text: str + text that should be completed. + completions: Iterator[Completion] + iterator over the completions to rectify + + + :any:`jedi.api.classes.Completion` s returned by Jedi may not have the same start and end, though + the Jupyter Protocol requires them to behave like so. This will readjust + the completion to have the same ``start`` and ``end`` by padding both + extremities with surrounding text. + + During stabilisation should support a ``_debug`` option to log which + completion are return by the IPython completer and not found in Jedi in + order to make upstream bug report. + """ + warnings.warn("`rectify_completions` is a provisional API (as of IPython 6.0). " + "It may change without warnings. " + "Use in corresponding context manager.", + category=ProvisionalCompleterWarning, stacklevel=2) + + completions = list(completions) + if not completions: + return + starts = (c.start for c in completions) + ends = (c.end for c in completions) + + new_start = min(starts) + new_end = max(ends) + + seen_jedi = set() + seen_python_matches = set() + for c in completions: + new_text = text[new_start:c.start] + c.text + text[c.end:new_end] + if c._origin == 'jedi': + seen_jedi.add(new_text) + elif c._origin == 'IPCompleter.python_matches': + seen_python_matches.add(new_text) + yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature) + diff = seen_python_matches.difference(seen_jedi) + if diff and _debug: + print('IPython.python matches have extras:', diff) + + +if sys.platform == 'win32': + DELIMS = ' \t\n`!@#$^&*()=+[{]}|;\'",<>?' +else: + DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?' + +GREEDY_DELIMS = ' =\r\n' + + +class CompletionSplitter(object): + """An object to split an input line in a manner similar to readline. + + By having our own implementation, we can expose readline-like completion in + a uniform manner to all frontends. This object only needs to be given the + line of text to be split and the cursor position on said line, and it + returns the 'word' to be completed on at the cursor after splitting the + entire line. + + What characters are used as splitting delimiters can be controlled by + setting the ``delims`` attribute (this is a property that internally + automatically builds the necessary regular expression)""" + + # Private interface + + # A string of delimiter characters. The default value makes sense for + # IPython's most typical usage patterns. + _delims = DELIMS + + # The expression (a normal string) to be compiled into a regular expression + # for actual splitting. We store it as an attribute mostly for ease of + # debugging, since this type of code can be so tricky to debug. + _delim_expr = None + + # The regular expression that does the actual splitting + _delim_re = None + + def __init__(self, delims=None): + delims = CompletionSplitter._delims if delims is None else delims + self.delims = delims + + @property + def delims(self): + """Return the string of delimiter characters.""" + return self._delims + + @delims.setter + def delims(self, delims): + """Set the delimiters for line splitting.""" + expr = '[' + ''.join('\\'+ c for c in delims) + ']' + self._delim_re = re.compile(expr) + self._delims = delims + self._delim_expr = expr + + def split_line(self, line, cursor_pos=None): + """Split a line of text with a cursor at the given position. + """ + l = line if cursor_pos is None else line[:cursor_pos] + return self._delim_re.split(l)[-1] + + + +class Completer(Configurable): + + greedy = Bool(False, + help="""Activate greedy completion + PENDING DEPRECTION. this is now mostly taken care of with Jedi. + + This will enable completion on elements of lists, results of function calls, etc., + but can be unsafe because the code is actually evaluated on TAB. + """ + ).tag(config=True) + + use_jedi = Bool(default_value=JEDI_INSTALLED, + help="Experimental: Use Jedi to generate autocompletions. " + "Default to True if jedi is installed.").tag(config=True) + + jedi_compute_type_timeout = Int(default_value=400, + help="""Experimental: restrict time (in milliseconds) during which Jedi can compute types. + Set to 0 to stop computing types. Non-zero value lower than 100ms may hurt + performance by preventing jedi to build its cache. + """).tag(config=True) + + debug = Bool(default_value=False, + help='Enable debug for the Completer. Mostly print extra ' + 'information for experimental jedi integration.')\ + .tag(config=True) + + backslash_combining_completions = Bool(True, + help="Enable unicode completions, e.g. \\alpha . " + "Includes completion of latex commands, unicode names, and expanding " + "unicode characters back to latex commands.").tag(config=True) + + + + def __init__(self, namespace=None, global_namespace=None, **kwargs): + """Create a new completer for the command line. + + Completer(namespace=ns, global_namespace=ns2) -> completer instance. + + If unspecified, the default namespace where completions are performed + is __main__ (technically, __main__.__dict__). Namespaces should be + given as dictionaries. + + An optional second namespace can be given. This allows the completer + to handle cases where both the local and global scopes need to be + distinguished. + """ + + # Don't bind to namespace quite yet, but flag whether the user wants a + # specific namespace or to use __main__.__dict__. This will allow us + # to bind to __main__.__dict__ at completion time, not now. + if namespace is None: + self.use_main_ns = True + else: + self.use_main_ns = False + self.namespace = namespace + + # The global namespace, if given, can be bound directly + if global_namespace is None: + self.global_namespace = {} + else: + self.global_namespace = global_namespace + + self.custom_matchers = [] + + super(Completer, self).__init__(**kwargs) + + def complete(self, text, state): + """Return the next possible completion for 'text'. + + This is called successively with state == 0, 1, 2, ... until it + returns None. The completion should begin with 'text'. + + """ + if self.use_main_ns: + self.namespace = __main__.__dict__ + + if state == 0: + if "." in text: + self.matches = self.attr_matches(text) + else: + self.matches = self.global_matches(text) + try: + return self.matches[state] + except IndexError: + return None + + def global_matches(self, text): + """Compute matches when text is a simple name. + + Return a list of all keywords, built-in functions and names currently + defined in self.namespace or self.global_namespace that match. + + """ + matches = [] + match_append = matches.append + n = len(text) + for lst in [keyword.kwlist, + builtin_mod.__dict__.keys(), + self.namespace.keys(), + self.global_namespace.keys()]: + for word in lst: + if word[:n] == text and word != "__builtins__": + match_append(word) + + snake_case_re = re.compile(r"[^_]+(_[^_]+)+?\Z") + for lst in [self.namespace.keys(), + self.global_namespace.keys()]: + shortened = {"_".join([sub[0] for sub in word.split('_')]) : word + for word in lst if snake_case_re.match(word)} + for word in shortened.keys(): + if word[:n] == text and word != "__builtins__": + match_append(shortened[word]) + return matches + + def attr_matches(self, text): + """Compute matches when text contains a dot. + + Assuming the text is of the form NAME.NAME....[NAME], and is + evaluatable in self.namespace or self.global_namespace, it will be + evaluated and its attributes (as revealed by dir()) are used as + possible completions. (For class instances, class members are + also considered.) + + WARNING: this can still invoke arbitrary C code, if an object + with a __getattr__ hook is evaluated. + + """ + + # Another option, seems to work great. Catches things like ''. + m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text) + + if m: + expr, attr = m.group(1, 3) + elif self.greedy: + m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer) + if not m2: + return [] + expr, attr = m2.group(1,2) + else: + return [] + + try: + obj = eval(expr, self.namespace) + except: + try: + obj = eval(expr, self.global_namespace) + except: + return [] + + if self.limit_to__all__ and hasattr(obj, '__all__'): + words = get__all__entries(obj) + else: + words = dir2(obj) + + try: + words = generics.complete_object(obj, words) + except TryNext: + pass + except AssertionError: + raise + except Exception: + # Silence errors from completion function + #raise # dbg + pass + # Build match list to return + n = len(attr) + return [u"%s.%s" % (expr, w) for w in words if w[:n] == attr ] + + +def get__all__entries(obj): + """returns the strings in the __all__ attribute""" + try: + words = getattr(obj, '__all__') + except: + return [] + + return [w for w in words if isinstance(w, str)] + + +def match_dict_keys(keys: List[str], prefix: str, delims: str): + """Used by dict_key_matches, matching the prefix to a list of keys + + Parameters + ========== + keys: + list of keys in dictionary currently being completed. + prefix: + Part of the text already typed by the user. e.g. `mydict[b'fo` + delims: + String of delimiters to consider when finding the current key. + + Returns + ======= + + A tuple of three elements: ``quote``, ``token_start``, ``matched``, with + ``quote`` being the quote that need to be used to close current string. + ``token_start`` the position where the replacement should start occurring, + ``matches`` a list of replacement/completion + + """ + if not prefix: + return None, 0, [repr(k) for k in keys + if isinstance(k, (str, bytes))] + quote_match = re.search('["\']', prefix) + quote = quote_match.group() + try: + prefix_str = eval(prefix + quote, {}) + except Exception: + return None, 0, [] + + pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$' + token_match = re.search(pattern, prefix, re.UNICODE) + token_start = token_match.start() + token_prefix = token_match.group() + + matched = [] + for key in keys: + try: + if not key.startswith(prefix_str): + continue + except (AttributeError, TypeError, UnicodeError): + # Python 3+ TypeError on b'a'.startswith('a') or vice-versa + continue + + # reformat remainder of key to begin with prefix + rem = key[len(prefix_str):] + # force repr wrapped in ' + rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"') + if rem_repr.startswith('u') and prefix[0] not in 'uU': + # Found key is unicode, but prefix is Py2 string. + # Therefore attempt to interpret key as string. + try: + rem_repr = repr(rem.encode('ascii') + '"') + except UnicodeEncodeError: + continue + + rem_repr = rem_repr[1 + rem_repr.index("'"):-2] + if quote == '"': + # The entered prefix is quoted with ", + # but the match is quoted with '. + # A contained " hence needs escaping for comparison: + rem_repr = rem_repr.replace('"', '\\"') + + # then reinsert prefix from start of token + matched.append('%s%s' % (token_prefix, rem_repr)) + return quote, token_start, matched + + +def cursor_to_position(text:str, line:int, column:int)->int: + """ + + Convert the (line,column) position of the cursor in text to an offset in a + string. + + Parameters + ---------- + + text : str + The text in which to calculate the cursor offset + line : int + Line of the cursor; 0-indexed + column : int + Column of the cursor 0-indexed + + Return + ------ + Position of the cursor in ``text``, 0-indexed. + + See Also + -------- + position_to_cursor: reciprocal of this function + + """ + lines = text.split('\n') + assert line <= len(lines), '{} <= {}'.format(str(line), str(len(lines))) + + return sum(len(l) + 1 for l in lines[:line]) + column + +def position_to_cursor(text:str, offset:int)->Tuple[int, int]: + """ + Convert the position of the cursor in text (0 indexed) to a line + number(0-indexed) and a column number (0-indexed) pair + + Position should be a valid position in ``text``. + + Parameters + ---------- + + text : str + The text in which to calculate the cursor offset + offset : int + Position of the cursor in ``text``, 0-indexed. + + Return + ------ + (line, column) : (int, int) + Line of the cursor; 0-indexed, column of the cursor 0-indexed + + + See Also + -------- + cursor_to_position : reciprocal of this function + + + """ + + assert 0 <= offset <= len(text) , "0 <= %s <= %s" % (offset , len(text)) + + before = text[:offset] + blines = before.split('\n') # ! splitnes trim trailing \n + line = before.count('\n') + col = len(blines[-1]) + return line, col + + +def _safe_isinstance(obj, module, class_name): + """Checks if obj is an instance of module.class_name if loaded + """ + return (module in sys.modules and + isinstance(obj, getattr(import_module(module), class_name))) + + +def back_unicode_name_matches(text): + u"""Match unicode characters back to unicode name + + This does ``☃`` -> ``\\snowman`` + + Note that snowman is not a valid python3 combining character but will be expanded. + Though it will not recombine back to the snowman character by the completion machinery. + + This will not either back-complete standard sequences like \\n, \\b ... + + Used on Python 3 only. + """ + if len(text)<2: + return u'', () + maybe_slash = text[-2] + if maybe_slash != '\\': + return u'', () + + char = text[-1] + # no expand on quote for completion in strings. + # nor backcomplete standard ascii keys + if char in string.ascii_letters or char in ['"',"'"]: + return u'', () + try : + unic = unicodedata.name(char) + return '\\'+char,['\\'+unic] + except KeyError: + pass + return u'', () + +def back_latex_name_matches(text:str): + """Match latex characters back to unicode name + + This does ``\\ℵ`` -> ``\\aleph`` + + Used on Python 3 only. + """ + if len(text)<2: + return u'', () + maybe_slash = text[-2] + if maybe_slash != '\\': + return u'', () + + + char = text[-1] + # no expand on quote for completion in strings. + # nor backcomplete standard ascii keys + if char in string.ascii_letters or char in ['"',"'"]: + return u'', () + try : + latex = reverse_latex_symbol[char] + # '\\' replace the \ as well + return '\\'+char,[latex] + except KeyError: + pass + return u'', () + + +def _formatparamchildren(parameter) -> str: + """ + Get parameter name and value from Jedi Private API + + Jedi does not expose a simple way to get `param=value` from its API. + + Parameter + ========= + + parameter: + Jedi's function `Param` + + Returns + ======= + + A string like 'a', 'b=1', '*args', '**kwargs' + + + """ + description = parameter.description + if not description.startswith('param '): + raise ValueError('Jedi function parameter description have change format.' + 'Expected "param ...", found %r".' % description) + return description[6:] + +def _make_signature(completion)-> str: + """ + Make the signature from a jedi completion + + Parameter + ========= + + completion: jedi.Completion + object does not complete a function type + + Returns + ======= + + a string consisting of the function signature, with the parenthesis but + without the function name. example: + `(a, *args, b=1, **kwargs)` + + """ + + return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for p in completion.params) if f]) + +class IPCompleter(Completer): + """Extension of the completer class with IPython-specific features""" + + _names = None + + @observe('greedy') + def _greedy_changed(self, change): + """update the splitter and readline delims when greedy is changed""" + if change['new']: + self.splitter.delims = GREEDY_DELIMS + else: + self.splitter.delims = DELIMS + + dict_keys_only = Bool(False, + help="""Whether to show dict key matches only""") + + merge_completions = Bool(True, + help="""Whether to merge completion results into a single list + + If False, only the completion results from the first non-empty + completer will be returned. + """ + ).tag(config=True) + omit__names = Enum((0,1,2), default_value=2, + help="""Instruct the completer to omit private method names + + Specifically, when completing on ``object.``. + + When 2 [default]: all names that start with '_' will be excluded. + + When 1: all 'magic' names (``__foo__``) will be excluded. + + When 0: nothing will be excluded. + """ + ).tag(config=True) + limit_to__all__ = Bool(False, + help=""" + DEPRECATED as of version 5.0. + + Instruct the completer to use __all__ for the completion + + Specifically, when completing on ``object.``. + + When True: only those names in obj.__all__ will be included. + + When False [default]: the __all__ attribute is ignored + """, + ).tag(config=True) + + @observe('limit_to__all__') + def _limit_to_all_changed(self, change): + warnings.warn('`IPython.core.IPCompleter.limit_to__all__` configuration ' + 'value has been deprecated since IPython 5.0, will be made to have ' + 'no effects and then removed in future version of IPython.', + UserWarning) + + def __init__(self, shell=None, namespace=None, global_namespace=None, + use_readline=_deprecation_readline_sentinel, config=None, **kwargs): + """IPCompleter() -> completer + + Return a completer object. + + Parameters + ---------- + + shell + a pointer to the ipython shell itself. This is needed + because this completer knows about magic functions, and those can + only be accessed via the ipython instance. + + namespace : dict, optional + an optional dict where completions are performed. + + global_namespace : dict, optional + secondary optional dict for completions, to + handle cases (such as IPython embedded inside functions) where + both Python scopes are visible. + + use_readline : bool, optional + DEPRECATED, ignored since IPython 6.0, will have no effects + """ + + self.magic_escape = ESC_MAGIC + self.splitter = CompletionSplitter() + + if use_readline is not _deprecation_readline_sentinel: + warnings.warn('The `use_readline` parameter is deprecated and ignored since IPython 6.0.', + DeprecationWarning, stacklevel=2) + + # _greedy_changed() depends on splitter and readline being defined: + Completer.__init__(self, namespace=namespace, global_namespace=global_namespace, + config=config, **kwargs) + + # List where completion matches will be stored + self.matches = [] + self.shell = shell + # Regexp to split filenames with spaces in them + self.space_name_re = re.compile(r'([^\\] )') + # Hold a local ref. to glob.glob for speed + self.glob = glob.glob + + # Determine if we are running on 'dumb' terminals, like (X)Emacs + # buffers, to avoid completion problems. + term = os.environ.get('TERM','xterm') + self.dumb_terminal = term in ['dumb','emacs'] + + # Special handling of backslashes needed in win32 platforms + if sys.platform == "win32": + self.clean_glob = self._clean_glob_win32 + else: + self.clean_glob = self._clean_glob + + #regexp to parse docstring for function signature + self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*') + self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)') + #use this if positional argument name is also needed + #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)') + + self.magic_arg_matchers = [ + self.magic_config_matches, + self.magic_color_matches, + ] + + # This is set externally by InteractiveShell + self.custom_completers = None + + @property + def matchers(self): + """All active matcher routines for completion""" + if self.dict_keys_only: + return [self.dict_key_matches] + + if self.use_jedi: + return [ + *self.custom_matchers, + self.file_matches, + self.magic_matches, + self.dict_key_matches, + ] + else: + return [ + *self.custom_matchers, + self.python_matches, + self.file_matches, + self.magic_matches, + self.python_func_kw_matches, + self.dict_key_matches, + ] + + def all_completions(self, text) -> List[str]: + """ + Wrapper around the completion methods for the benefit of emacs. + """ + prefix = text.rpartition('.')[0] + with provisionalcompleter(): + return ['.'.join([prefix, c.text]) if prefix and self.use_jedi else c.text + for c in self.completions(text, len(text))] + + return self.complete(text)[1] + + def _clean_glob(self, text): + return self.glob("%s*" % text) + + def _clean_glob_win32(self,text): + return [f.replace("\\","/") + for f in self.glob("%s*" % text)] + + def file_matches(self, text): + """Match filenames, expanding ~USER type strings. + + Most of the seemingly convoluted logic in this completer is an + attempt to handle filenames with spaces in them. And yet it's not + quite perfect, because Python's readline doesn't expose all of the + GNU readline details needed for this to be done correctly. + + For a filename with a space in it, the printed completions will be + only the parts after what's already been typed (instead of the + full completions, as is normally done). I don't think with the + current (as of Python 2.3) Python readline it's possible to do + better.""" + + # chars that require escaping with backslash - i.e. chars + # that readline treats incorrectly as delimiters, but we + # don't want to treat as delimiters in filename matching + # when escaped with backslash + if text.startswith('!'): + text = text[1:] + text_prefix = u'!' + else: + text_prefix = u'' + + text_until_cursor = self.text_until_cursor + # track strings with open quotes + open_quotes = has_open_quotes(text_until_cursor) + + if '(' in text_until_cursor or '[' in text_until_cursor: + lsplit = text + else: + try: + # arg_split ~ shlex.split, but with unicode bugs fixed by us + lsplit = arg_split(text_until_cursor)[-1] + except ValueError: + # typically an unmatched ", or backslash without escaped char. + if open_quotes: + lsplit = text_until_cursor.split(open_quotes)[-1] + else: + return [] + except IndexError: + # tab pressed on empty line + lsplit = "" + + if not open_quotes and lsplit != protect_filename(lsplit): + # if protectables are found, do matching on the whole escaped name + has_protectables = True + text0,text = text,lsplit + else: + has_protectables = False + text = os.path.expanduser(text) + + if text == "": + return [text_prefix + protect_filename(f) for f in self.glob("*")] + + # Compute the matches from the filesystem + if sys.platform == 'win32': + m0 = self.clean_glob(text) + else: + m0 = self.clean_glob(text.replace('\\', '')) + + if has_protectables: + # If we had protectables, we need to revert our changes to the + # beginning of filename so that we don't double-write the part + # of the filename we have so far + len_lsplit = len(lsplit) + matches = [text_prefix + text0 + + protect_filename(f[len_lsplit:]) for f in m0] + else: + if open_quotes: + # if we have a string with an open quote, we don't need to + # protect the names beyond the quote (and we _shouldn't_, as + # it would cause bugs when the filesystem call is made). + matches = m0 if sys.platform == "win32" else\ + [protect_filename(f, open_quotes) for f in m0] + else: + matches = [text_prefix + + protect_filename(f) for f in m0] + + # Mark directories in input list by appending '/' to their names. + return [x+'/' if os.path.isdir(x) else x for x in matches] + + def magic_matches(self, text): + """Match magics""" + # Get all shell magics now rather than statically, so magics loaded at + # runtime show up too. + lsm = self.shell.magics_manager.lsmagic() + line_magics = lsm['line'] + cell_magics = lsm['cell'] + pre = self.magic_escape + pre2 = pre+pre + + explicit_magic = text.startswith(pre) + + # Completion logic: + # - user gives %%: only do cell magics + # - user gives %: do both line and cell magics + # - no prefix: do both + # In other words, line magics are skipped if the user gives %% explicitly + # + # We also exclude magics that match any currently visible names: + # https://github.com/ipython/ipython/issues/4877, unless the user has + # typed a %: + # https://github.com/ipython/ipython/issues/10754 + bare_text = text.lstrip(pre) + global_matches = self.global_matches(bare_text) + if not explicit_magic: + def matches(magic): + """ + Filter magics, in particular remove magics that match + a name present in global namespace. + """ + return ( magic.startswith(bare_text) and + magic not in global_matches ) + else: + def matches(magic): + return magic.startswith(bare_text) + + comp = [ pre2+m for m in cell_magics if matches(m)] + if not text.startswith(pre2): + comp += [ pre+m for m in line_magics if matches(m)] + + return comp + + def magic_config_matches(self, text:str) -> List[str]: + """ Match class names and attributes for %config magic """ + texts = text.strip().split() + + if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'): + # get all configuration classes + classes = sorted(set([ c for c in self.shell.configurables + if c.__class__.class_traits(config=True) + ]), key=lambda x: x.__class__.__name__) + classnames = [ c.__class__.__name__ for c in classes ] + + # return all classnames if config or %config is given + if len(texts) == 1: + return classnames + + # match classname + classname_texts = texts[1].split('.') + classname = classname_texts[0] + classname_matches = [ c for c in classnames + if c.startswith(classname) ] + + # return matched classes or the matched class with attributes + if texts[1].find('.') < 0: + return classname_matches + elif len(classname_matches) == 1 and \ + classname_matches[0] == classname: + cls = classes[classnames.index(classname)].__class__ + help = cls.class_get_help() + # strip leading '--' from cl-args: + help = re.sub(re.compile(r'^--', re.MULTILINE), '', help) + return [ attr.split('=')[0] + for attr in help.strip().splitlines() + if attr.startswith(texts[1]) ] + return [] + + def magic_color_matches(self, text:str) -> List[str] : + """ Match color schemes for %colors magic""" + texts = text.split() + if text.endswith(' '): + # .split() strips off the trailing whitespace. Add '' back + # so that: '%colors ' -> ['%colors', ''] + texts.append('') + + if len(texts) == 2 and (texts[0] == 'colors' or texts[0] == '%colors'): + prefix = texts[1] + return [ color for color in InspectColors.keys() + if color.startswith(prefix) ] + return [] + + def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str): + """ + + Return a list of :any:`jedi.api.Completions` object from a ``text`` and + cursor position. + + Parameters + ---------- + cursor_column : int + column position of the cursor in ``text``, 0-indexed. + cursor_line : int + line position of the cursor in ``text``, 0-indexed + text : str + text to complete + + Debugging + --------- + + If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion` + object containing a string with the Jedi debug information attached. + """ + namespaces = [self.namespace] + if self.global_namespace is not None: + namespaces.append(self.global_namespace) + + completion_filter = lambda x:x + offset = cursor_to_position(text, cursor_line, cursor_column) + # filter output if we are completing for object members + if offset: + pre = text[offset-1] + if pre == '.': + if self.omit__names == 2: + completion_filter = lambda c:not c.name.startswith('_') + elif self.omit__names == 1: + completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__')) + elif self.omit__names == 0: + completion_filter = lambda x:x + else: + raise ValueError("Don't understand self.omit__names == {}".format(self.omit__names)) + + interpreter = jedi.Interpreter( + text[:offset], namespaces, column=cursor_column, line=cursor_line + 1) + try_jedi = True + + try: + # find the first token in the current tree -- if it is a ' or " then we are in a string + completing_string = False + try: + first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value')) + except StopIteration: + pass + else: + # note the value may be ', ", or it may also be ''' or """, or + # in some cases, """what/you/typed..., but all of these are + # strings. + completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'} + + # if we are in a string jedi is likely not the right candidate for + # now. Skip it. + try_jedi = not completing_string + except Exception as e: + # many of things can go wrong, we are using private API just don't crash. + if self.debug: + print("Error detecting if completing a non-finished string :", e, '|') + + if not try_jedi: + return [] + try: + return filter(completion_filter, interpreter.completions()) + except Exception as e: + if self.debug: + return [_FakeJediCompletion('Oops Jedi has crashed, please report a bug with the following:\n"""\n%s\ns"""' % (e))] + else: + return [] + + def python_matches(self, text): + """Match attributes or global python names""" + if "." in text: + try: + matches = self.attr_matches(text) + if text.endswith('.') and self.omit__names: + if self.omit__names == 1: + # true if txt is _not_ a __ name, false otherwise: + no__name = (lambda txt: + re.match(r'.*\.__.*?__',txt) is None) + else: + # true if txt is _not_ a _ name, false otherwise: + no__name = (lambda txt: + re.match(r'\._.*?',txt[txt.rindex('.'):]) is None) + matches = filter(no__name, matches) + except NameError: + # catches . + matches = [] + else: + matches = self.global_matches(text) + return matches + + def _default_arguments_from_docstring(self, doc): + """Parse the first line of docstring for call signature. + + Docstring should be of the form 'min(iterable[, key=func])\n'. + It can also parse cython docstring of the form + 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'. + """ + if doc is None: + return [] + + #care only the firstline + line = doc.lstrip().splitlines()[0] + + #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*') + #'min(iterable[, key=func])\n' -> 'iterable[, key=func]' + sig = self.docstring_sig_re.search(line) + if sig is None: + return [] + # iterable[, key=func]' -> ['iterable[' ,' key=func]'] + sig = sig.groups()[0].split(',') + ret = [] + for s in sig: + #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)') + ret += self.docstring_kwd_re.findall(s) + return ret + + def _default_arguments(self, obj): + """Return the list of default arguments of obj if it is callable, + or empty list otherwise.""" + call_obj = obj + ret = [] + if inspect.isbuiltin(obj): + pass + elif not (inspect.isfunction(obj) or inspect.ismethod(obj)): + if inspect.isclass(obj): + #for cython embedsignature=True the constructor docstring + #belongs to the object itself not __init__ + ret += self._default_arguments_from_docstring( + getattr(obj, '__doc__', '')) + # for classes, check for __init__,__new__ + call_obj = (getattr(obj, '__init__', None) or + getattr(obj, '__new__', None)) + # for all others, check if they are __call__able + elif hasattr(obj, '__call__'): + call_obj = obj.__call__ + ret += self._default_arguments_from_docstring( + getattr(call_obj, '__doc__', '')) + + _keeps = (inspect.Parameter.KEYWORD_ONLY, + inspect.Parameter.POSITIONAL_OR_KEYWORD) + + try: + sig = inspect.signature(call_obj) + ret.extend(k for k, v in sig.parameters.items() if + v.kind in _keeps) + except ValueError: + pass + + return list(set(ret)) + + def python_func_kw_matches(self,text): + """Match named parameters (kwargs) of the last open function""" + + if "." in text: # a parameter cannot be dotted + return [] + try: regexp = self.__funcParamsRegex + except AttributeError: + regexp = self.__funcParamsRegex = re.compile(r''' + '.*?(?,a=1)", the candidate is "foo" + tokens = regexp.findall(self.text_until_cursor) + iterTokens = reversed(tokens); openPar = 0 + + for token in iterTokens: + if token == ')': + openPar -= 1 + elif token == '(': + openPar += 1 + if openPar > 0: + # found the last unclosed parenthesis + break + else: + return [] + # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" ) + ids = [] + isId = re.compile(r'\w+$').match + + while True: + try: + ids.append(next(iterTokens)) + if not isId(ids[-1]): + ids.pop(); break + if not next(iterTokens) == '.': + break + except StopIteration: + break + + # Find all named arguments already assigned to, as to avoid suggesting + # them again + usedNamedArgs = set() + par_level = -1 + for token, next_token in zip(tokens, tokens[1:]): + if token == '(': + par_level += 1 + elif token == ')': + par_level -= 1 + + if par_level != 0: + continue + + if next_token != '=': + continue + + usedNamedArgs.add(token) + + argMatches = [] + try: + callableObj = '.'.join(ids[::-1]) + namedArgs = self._default_arguments(eval(callableObj, + self.namespace)) + + # Remove used named arguments from the list, no need to show twice + for namedArg in set(namedArgs) - usedNamedArgs: + if namedArg.startswith(text): + argMatches.append(u"%s=" %namedArg) + except: + pass + + return argMatches + + def dict_key_matches(self, text): + "Match string keys in a dictionary, after e.g. 'foo[' " + def get_keys(obj): + # Objects can define their own completions by defining an + # _ipy_key_completions_() method. + method = get_real_method(obj, '_ipython_key_completions_') + if method is not None: + return method() + + # Special case some common in-memory dict-like types + if isinstance(obj, dict) or\ + _safe_isinstance(obj, 'pandas', 'DataFrame'): + try: + return list(obj.keys()) + except Exception: + return [] + elif _safe_isinstance(obj, 'numpy', 'ndarray') or\ + _safe_isinstance(obj, 'numpy', 'void'): + return obj.dtype.names or [] + return [] + + try: + regexps = self.__dict_key_regexps + except AttributeError: + dict_key_re_fmt = r'''(?x) + ( # match dict-referring expression wrt greedy setting + %s + ) + \[ # open bracket + \s* # and optional whitespace + ([uUbB]? # string prefix (r not handled) + (?: # unclosed string + '(?:[^']|(? key_start: + leading = '' + else: + leading = text[text_start:completion_start] + + # the index of the `[` character + bracket_idx = match.end(1) + + # append closing quote and bracket as appropriate + # this is *not* appropriate if the opening quote or bracket is outside + # the text given to this method + suf = '' + continuation = self.line_buffer[len(self.text_until_cursor):] + if key_start > text_start and closing_quote: + # quotes were opened inside text, maybe close them + if continuation.startswith(closing_quote): + continuation = continuation[len(closing_quote):] + else: + suf += closing_quote + if bracket_idx > text_start: + # brackets were opened inside text, maybe close them + if not continuation.startswith(']'): + suf += ']' + + return [leading + k + suf for k in matches] + + def unicode_name_matches(self, text): + u"""Match Latex-like syntax for unicode characters base + on the name of the character. + + This does ``\\GREEK SMALL LETTER ETA`` -> ``η`` + + Works only on valid python 3 identifier, or on combining characters that + will combine to form a valid identifier. + + Used on Python 3 only. + """ + slashpos = text.rfind('\\') + if slashpos > -1: + s = text[slashpos+1:] + try : + unic = unicodedata.lookup(s) + # allow combining chars + if ('a'+unic).isidentifier(): + return '\\'+s,[unic] + except KeyError: + pass + return u'', [] + + + def latex_matches(self, text): + u"""Match Latex syntax for unicode characters. + + This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α`` + """ + slashpos = text.rfind('\\') + if slashpos > -1: + s = text[slashpos:] + if s in latex_symbols: + # Try to complete a full latex symbol to unicode + # \\alpha -> α + return s, [latex_symbols[s]] + else: + # If a user has partially typed a latex symbol, give them + # a full list of options \al -> [\aleph, \alpha] + matches = [k for k in latex_symbols if k.startswith(s)] + if matches: + return s, matches + return u'', [] + + def dispatch_custom_completer(self, text): + if not self.custom_completers: + return + + line = self.line_buffer + if not line.strip(): + return None + + # Create a little structure to pass all the relevant information about + # the current completion to any custom completer. + event = SimpleNamespace() + event.line = line + event.symbol = text + cmd = line.split(None,1)[0] + event.command = cmd + event.text_until_cursor = self.text_until_cursor + + # for foo etc, try also to find completer for %foo + if not cmd.startswith(self.magic_escape): + try_magic = self.custom_completers.s_matches( + self.magic_escape + cmd) + else: + try_magic = [] + + for c in itertools.chain(self.custom_completers.s_matches(cmd), + try_magic, + self.custom_completers.flat_matches(self.text_until_cursor)): + try: + res = c(event) + if res: + # first, try case sensitive match + withcase = [r for r in res if r.startswith(text)] + if withcase: + return withcase + # if none, then case insensitive ones are ok too + text_low = text.lower() + return [r for r in res if r.lower().startswith(text_low)] + except TryNext: + pass + except KeyboardInterrupt: + """ + If custom completer take too long, + let keyboard interrupt abort and return nothing. + """ + break + + return None + + def completions(self, text: str, offset: int)->Iterator[Completion]: + """ + Returns an iterator over the possible completions + + .. warning:: Unstable + + This function is unstable, API may change without warning. + It will also raise unless use in proper context manager. + + Parameters + ---------- + + text:str + Full text of the current input, multi line string. + offset:int + Integer representing the position of the cursor in ``text``. Offset + is 0-based indexed. + + Yields + ------ + :any:`Completion` object + + + The cursor on a text can either be seen as being "in between" + characters or "On" a character depending on the interface visible to + the user. For consistency the cursor being on "in between" characters X + and Y is equivalent to the cursor being "on" character Y, that is to say + the character the cursor is on is considered as being after the cursor. + + Combining characters may span more that one position in the + text. + + + .. note:: + + If ``IPCompleter.debug`` is :any:`True` will yield a ``--jedi/ipython--`` + fake Completion token to distinguish completion returned by Jedi + and usual IPython completion. + + .. note:: + + Completions are not completely deduplicated yet. If identical + completions are coming from different sources this function does not + ensure that each completion object will only be present once. + """ + warnings.warn("_complete is a provisional API (as of IPython 6.0). " + "It may change without warnings. " + "Use in corresponding context manager.", + category=ProvisionalCompleterWarning, stacklevel=2) + + seen = set() + try: + for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000): + if c and (c in seen): + continue + yield c + seen.add(c) + except KeyboardInterrupt: + """if completions take too long and users send keyboard interrupt, + do not crash and return ASAP. """ + pass + + def _completions(self, full_text: str, offset: int, *, _timeout)->Iterator[Completion]: + """ + Core completion module.Same signature as :any:`completions`, with the + extra `timeout` parameter (in seconds). + + + Computing jedi's completion ``.type`` can be quite expensive (it is a + lazy property) and can require some warm-up, more warm up than just + computing the ``name`` of a completion. The warm-up can be : + + - Long warm-up the first time a module is encountered after + install/update: actually build parse/inference tree. + + - first time the module is encountered in a session: load tree from + disk. + + We don't want to block completions for tens of seconds so we give the + completer a "budget" of ``_timeout`` seconds per invocation to compute + completions types, the completions that have not yet been computed will + be marked as "unknown" an will have a chance to be computed next round + are things get cached. + + Keep in mind that Jedi is not the only thing treating the completion so + keep the timeout short-ish as if we take more than 0.3 second we still + have lots of processing to do. + + """ + deadline = time.monotonic() + _timeout + + + before = full_text[:offset] + cursor_line, cursor_column = position_to_cursor(full_text, offset) + + matched_text, matches, matches_origin, jedi_matches = self._complete( + full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column) + + iter_jm = iter(jedi_matches) + if _timeout: + for jm in iter_jm: + try: + type_ = jm.type + except Exception: + if self.debug: + print("Error in Jedi getting type of ", jm) + type_ = None + delta = len(jm.name_with_symbols) - len(jm.complete) + if type_ == 'function': + signature = _make_signature(jm) + else: + signature = '' + yield Completion(start=offset - delta, + end=offset, + text=jm.name_with_symbols, + type=type_, + signature=signature, + _origin='jedi') + + if time.monotonic() > deadline: + break + + for jm in iter_jm: + delta = len(jm.name_with_symbols) - len(jm.complete) + yield Completion(start=offset - delta, + end=offset, + text=jm.name_with_symbols, + type='', # don't compute type for speed + _origin='jedi', + signature='') + + + start_offset = before.rfind(matched_text) + + # TODO: + # Suppress this, right now just for debug. + if jedi_matches and matches and self.debug: + yield Completion(start=start_offset, end=offset, text='--jedi/ipython--', + _origin='debug', type='none', signature='') + + # I'm unsure if this is always true, so let's assert and see if it + # crash + assert before.endswith(matched_text) + for m, t in zip(matches, matches_origin): + yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='') + + + def complete(self, text=None, line_buffer=None, cursor_pos=None): + """Find completions for the given text and line context. + + Note that both the text and the line_buffer are optional, but at least + one of them must be given. + + Parameters + ---------- + text : string, optional + Text to perform the completion on. If not given, the line buffer + is split using the instance's CompletionSplitter object. + + line_buffer : string, optional + If not given, the completer attempts to obtain the current line + buffer via readline. This keyword allows clients which are + requesting for text completions in non-readline contexts to inform + the completer of the entire text. + + cursor_pos : int, optional + Index of the cursor in the full line buffer. Should be provided by + remote frontends where kernel has no access to frontend state. + + Returns + ------- + text : str + Text that was actually used in the completion. + + matches : list + A list of completion matches. + + + .. note:: + + This API is likely to be deprecated and replaced by + :any:`IPCompleter.completions` in the future. + + + """ + warnings.warn('`Completer.complete` is pending deprecation since ' + 'IPython 6.0 and will be replaced by `Completer.completions`.', + PendingDeprecationWarning) + # potential todo, FOLD the 3rd throw away argument of _complete + # into the first 2 one. + return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2] + + def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None, + full_text=None) -> Tuple[str, List[str], List[str], Iterable[_FakeJediCompletion]]: + """ + + Like complete but can also returns raw jedi completions as well as the + origin of the completion text. This could (and should) be made much + cleaner but that will be simpler once we drop the old (and stateful) + :any:`complete` API. + + + With current provisional API, cursor_pos act both (depending on the + caller) as the offset in the ``text`` or ``line_buffer``, or as the + ``column`` when passing multiline strings this could/should be renamed + but would add extra noise. + """ + + # if the cursor position isn't given, the only sane assumption we can + # make is that it's at the end of the line (the common case) + if cursor_pos is None: + cursor_pos = len(line_buffer) if text is None else len(text) + + if self.use_main_ns: + self.namespace = __main__.__dict__ + + # if text is either None or an empty string, rely on the line buffer + if (not line_buffer) and full_text: + line_buffer = full_text.split('\n')[cursor_line] + if not text: # issue #11508: check line_buffer before calling split_line + text = self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else '' + + if self.backslash_combining_completions: + # allow deactivation of these on windows. + base_text = text if not line_buffer else line_buffer[:cursor_pos] + latex_text, latex_matches = self.latex_matches(base_text) + if latex_matches: + return latex_text, latex_matches, ['latex_matches']*len(latex_matches), () + name_text = '' + name_matches = [] + # need to add self.fwd_unicode_match() function here when done + for meth in (self.unicode_name_matches, back_latex_name_matches, back_unicode_name_matches, self.fwd_unicode_match): + name_text, name_matches = meth(base_text) + if name_text: + return name_text, name_matches[:MATCHES_LIMIT], \ + [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), () + + + # If no line buffer is given, assume the input text is all there was + if line_buffer is None: + line_buffer = text + + self.line_buffer = line_buffer + self.text_until_cursor = self.line_buffer[:cursor_pos] + + # Do magic arg matches + for matcher in self.magic_arg_matchers: + matches = list(matcher(line_buffer))[:MATCHES_LIMIT] + if matches: + origins = [matcher.__qualname__] * len(matches) + return text, matches, origins, () + + # Start with a clean slate of completions + matches = [] + + # FIXME: we should extend our api to return a dict with completions for + # different types of objects. The rlcomplete() method could then + # simply collapse the dict into a list for readline, but we'd have + # richer completion semantics in other environments. + completions = () + if self.use_jedi: + if not full_text: + full_text = line_buffer + completions = self._jedi_matches( + cursor_pos, cursor_line, full_text) + + if self.merge_completions: + matches = [] + for matcher in self.matchers: + try: + matches.extend([(m, matcher.__qualname__) + for m in matcher(text)]) + except: + # Show the ugly traceback if the matcher causes an + # exception, but do NOT crash the kernel! + sys.excepthook(*sys.exc_info()) + else: + for matcher in self.matchers: + matches = [(m, matcher.__qualname__) + for m in matcher(text)] + if matches: + break + + seen = set() + filtered_matches = set() + for m in matches: + t, c = m + if t not in seen: + filtered_matches.add(m) + seen.add(t) + + _filtered_matches = sorted(filtered_matches, key=lambda x: completions_sorting_key(x[0])) + + custom_res = [(m, 'custom') for m in self.dispatch_custom_completer(text) or []] + + _filtered_matches = custom_res or _filtered_matches + + _filtered_matches = _filtered_matches[:MATCHES_LIMIT] + _matches = [m[0] for m in _filtered_matches] + origins = [m[1] for m in _filtered_matches] + + self.matches = _matches + + return text, _matches, origins, completions + + def fwd_unicode_match(self, text:str) -> Tuple[str, list]: + if self._names is None: + self._names = [] + for c in range(0,0x10FFFF + 1): + try: + self._names.append(unicodedata.name(chr(c))) + except ValueError: + pass + + slashpos = text.rfind('\\') + # if text starts with slash + if slashpos > -1: + s = text[slashpos+1:] + candidates = [x for x in self._names if x.startswith(s)] + if candidates: + return s, candidates + else: + return '', () + + # if text does not start with slash + else: + return u'', () diff --git a/.venv/lib/python3.8/site-packages/IPython/core/completerlib.py b/.venv/lib/python3.8/site-packages/IPython/core/completerlib.py new file mode 100644 index 00000000..7860cb67 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/completerlib.py @@ -0,0 +1,354 @@ +# encoding: utf-8 +"""Implementations for various useful completers. + +These are all loaded by default by IPython. +""" +#----------------------------------------------------------------------------- +# Copyright (C) 2010-2011 The IPython Development Team. +# +# Distributed under the terms of the BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib imports +import glob +import inspect +import os +import re +import sys +from importlib import import_module +from importlib.machinery import all_suffixes + + +# Third-party imports +from time import time +from zipimport import zipimporter + +# Our own imports +from .completer import expand_user, compress_user +from .error import TryNext +from ..utils._process_common import arg_split + +# FIXME: this should be pulled in with the right call via the component system +from IPython import get_ipython + +from typing import List + +#----------------------------------------------------------------------------- +# Globals and constants +#----------------------------------------------------------------------------- +_suffixes = all_suffixes() + +# Time in seconds after which the rootmodules will be stored permanently in the +# ipython ip.db database (kept in the user's .ipython dir). +TIMEOUT_STORAGE = 2 + +# Time in seconds after which we give up +TIMEOUT_GIVEUP = 20 + +# Regular expression for the python import statement +import_re = re.compile(r'(?P[^\W\d]\w*?)' + r'(?P[/\\]__init__)?' + r'(?P%s)$' % + r'|'.join(re.escape(s) for s in _suffixes)) + +# RE for the ipython %run command (python + ipython scripts) +magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$') + +#----------------------------------------------------------------------------- +# Local utilities +#----------------------------------------------------------------------------- + +def module_list(path): + """ + Return the list containing the names of the modules available in the given + folder. + """ + # sys.path has the cwd as an empty string, but isdir/listdir need it as '.' + if path == '': + path = '.' + + # A few local constants to be used in loops below + pjoin = os.path.join + + if os.path.isdir(path): + # Build a list of all files in the directory and all files + # in its subdirectories. For performance reasons, do not + # recurse more than one level into subdirectories. + files = [] + for root, dirs, nondirs in os.walk(path, followlinks=True): + subdir = root[len(path)+1:] + if subdir: + files.extend(pjoin(subdir, f) for f in nondirs) + dirs[:] = [] # Do not recurse into additional subdirectories. + else: + files.extend(nondirs) + + else: + try: + files = list(zipimporter(path)._files.keys()) + except: + files = [] + + # Build a list of modules which match the import_re regex. + modules = [] + for f in files: + m = import_re.match(f) + if m: + modules.append(m.group('name')) + return list(set(modules)) + + +def get_root_modules(): + """ + Returns a list containing the names of all the modules available in the + folders of the pythonpath. + + ip.db['rootmodules_cache'] maps sys.path entries to list of modules. + """ + ip = get_ipython() + if ip is None: + # No global shell instance to store cached list of modules. + # Don't try to scan for modules every time. + return list(sys.builtin_module_names) + + rootmodules_cache = ip.db.get('rootmodules_cache', {}) + rootmodules = list(sys.builtin_module_names) + start_time = time() + store = False + for path in sys.path: + try: + modules = rootmodules_cache[path] + except KeyError: + modules = module_list(path) + try: + modules.remove('__init__') + except ValueError: + pass + if path not in ('', '.'): # cwd modules should not be cached + rootmodules_cache[path] = modules + if time() - start_time > TIMEOUT_STORAGE and not store: + store = True + print("\nCaching the list of root modules, please wait!") + print("(This will only be done once - type '%rehashx' to " + "reset cache!)\n") + sys.stdout.flush() + if time() - start_time > TIMEOUT_GIVEUP: + print("This is taking too long, we give up.\n") + return [] + rootmodules.extend(modules) + if store: + ip.db['rootmodules_cache'] = rootmodules_cache + rootmodules = list(set(rootmodules)) + return rootmodules + + +def is_importable(module, attr, only_modules): + if only_modules: + return inspect.ismodule(getattr(module, attr)) + else: + return not(attr[:2] == '__' and attr[-2:] == '__') + + +def try_import(mod: str, only_modules=False) -> List[str]: + """ + Try to import given module and return list of potential completions. + """ + mod = mod.rstrip('.') + try: + m = import_module(mod) + except: + return [] + + m_is_init = '__init__' in (getattr(m, '__file__', '') or '') + + completions = [] + if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init: + completions.extend( [attr for attr in dir(m) if + is_importable(m, attr, only_modules)]) + + completions.extend(getattr(m, '__all__', [])) + if m_is_init: + completions.extend(module_list(os.path.dirname(m.__file__))) + completions_set = {c for c in completions if isinstance(c, str)} + completions_set.discard('__init__') + return list(completions_set) + + +#----------------------------------------------------------------------------- +# Completion-related functions. +#----------------------------------------------------------------------------- + +def quick_completer(cmd, completions): + r""" Easily create a trivial completer for a command. + + Takes either a list of completions, or all completions in string (that will + be split on whitespace). + + Example:: + + [d:\ipython]|1> import ipy_completers + [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz']) + [d:\ipython]|3> foo b + bar baz + [d:\ipython]|3> foo ba + """ + + if isinstance(completions, str): + completions = completions.split() + + def do_complete(self, event): + return completions + + get_ipython().set_hook('complete_command',do_complete, str_key = cmd) + +def module_completion(line): + """ + Returns a list containing the completion possibilities for an import line. + + The line looks like this : + 'import xml.d' + 'from xml.dom import' + """ + + words = line.split(' ') + nwords = len(words) + + # from whatever -> 'import ' + if nwords == 3 and words[0] == 'from': + return ['import '] + + # 'from xy' or 'import xy' + if nwords < 3 and (words[0] in {'%aimport', 'import', 'from'}) : + if nwords == 1: + return get_root_modules() + mod = words[1].split('.') + if len(mod) < 2: + return get_root_modules() + completion_list = try_import('.'.join(mod[:-1]), True) + return ['.'.join(mod[:-1] + [el]) for el in completion_list] + + # 'from xyz import abc' + if nwords >= 3 and words[0] == 'from': + mod = words[1] + return try_import(mod) + +#----------------------------------------------------------------------------- +# Completers +#----------------------------------------------------------------------------- +# These all have the func(self, event) signature to be used as custom +# completers + +def module_completer(self,event): + """Give completions after user has typed 'import ...' or 'from ...'""" + + # This works in all versions of python. While 2.5 has + # pkgutil.walk_packages(), that particular routine is fairly dangerous, + # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full + # of possibly problematic side effects. + # This search the folders in the sys.path for available modules. + + return module_completion(event.line) + +# FIXME: there's a lot of logic common to the run, cd and builtin file +# completers, that is currently reimplemented in each. + +def magic_run_completer(self, event): + """Complete files that end in .py or .ipy or .ipynb for the %run command. + """ + comps = arg_split(event.line, strict=False) + # relpath should be the current token that we need to complete. + if (len(comps) > 1) and (not event.line.endswith(' ')): + relpath = comps[-1].strip("'\"") + else: + relpath = '' + + #print("\nev=", event) # dbg + #print("rp=", relpath) # dbg + #print('comps=', comps) # dbg + + lglob = glob.glob + isdir = os.path.isdir + relpath, tilde_expand, tilde_val = expand_user(relpath) + + # Find if the user has already typed the first filename, after which we + # should complete on all files, since after the first one other files may + # be arguments to the input script. + + if any(magic_run_re.match(c) for c in comps): + matches = [f.replace('\\','/') + ('/' if isdir(f) else '') + for f in lglob(relpath+'*')] + else: + dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)] + pys = [f.replace('\\','/') + for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') + + lglob(relpath+'*.ipynb') + lglob(relpath + '*.pyw')] + + matches = dirs + pys + + #print('run comp:', dirs+pys) # dbg + return [compress_user(p, tilde_expand, tilde_val) for p in matches] + + +def cd_completer(self, event): + """Completer function for cd, which only returns directories.""" + ip = get_ipython() + relpath = event.symbol + + #print(event) # dbg + if event.line.endswith('-b') or ' -b ' in event.line: + # return only bookmark completions + bkms = self.db.get('bookmarks', None) + if bkms: + return bkms.keys() + else: + return [] + + if event.symbol == '-': + width_dh = str(len(str(len(ip.user_ns['_dh']) + 1))) + # jump in directory history by number + fmt = '-%0' + width_dh +'d [%s]' + ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])] + if len(ents) > 1: + return ents + return [] + + if event.symbol.startswith('--'): + return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']] + + # Expand ~ in path and normalize directory separators. + relpath, tilde_expand, tilde_val = expand_user(relpath) + relpath = relpath.replace('\\','/') + + found = [] + for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*') + if os.path.isdir(f)]: + if ' ' in d: + # we don't want to deal with any of that, complex code + # for this is elsewhere + raise TryNext + + found.append(d) + + if not found: + if os.path.isdir(relpath): + return [compress_user(relpath, tilde_expand, tilde_val)] + + # if no completions so far, try bookmarks + bks = self.db.get('bookmarks',{}) + bkmatches = [s for s in bks if s.startswith(event.symbol)] + if bkmatches: + return bkmatches + + raise TryNext + + return [compress_user(p, tilde_expand, tilde_val) for p in found] + +def reset_completer(self, event): + "A completer for %reset magic" + return '-f -s in out array dhist'.split() diff --git a/.venv/lib/python3.8/site-packages/IPython/core/crashhandler.py b/.venv/lib/python3.8/site-packages/IPython/core/crashhandler.py new file mode 100644 index 00000000..1e0b429d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/crashhandler.py @@ -0,0 +1,228 @@ +# encoding: utf-8 +"""sys.excepthook for IPython itself, leaves a detailed report on disk. + +Authors: + +* Fernando Perez +* Brian E. Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2001-2007 Fernando Perez. +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os +import sys +import traceback +from pprint import pformat + +from IPython.core import ultratb +from IPython.core.release import author_email +from IPython.utils.sysinfo import sys_info +from IPython.utils.py3compat import input + +from IPython.core.release import __version__ as version + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +# Template for the user message. +_default_message_template = """\ +Oops, {app_name} crashed. We do our best to make it stable, but... + +A crash report was automatically generated with the following information: + - A verbatim copy of the crash traceback. + - A copy of your input history during this session. + - Data on your current {app_name} configuration. + +It was left in the file named: +\t'{crash_report_fname}' +If you can email this file to the developers, the information in it will help +them in understanding and correcting the problem. + +You can mail it to: {contact_name} at {contact_email} +with the subject '{app_name} Crash Report'. + +If you want to do it now, the following command will work (under Unix): +mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname} + +In your email, please also include information about: +- The operating system under which the crash happened: Linux, macOS, Windows, + other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2, + Windows 10 Pro), and whether it is 32-bit or 64-bit; +- How {app_name} was installed: using pip or conda, from GitHub, as part of + a Docker container, or other, providing more detail if possible; +- How to reproduce the crash: what exact sequence of instructions can one + input to get the same crash? Ideally, find a minimal yet complete sequence + of instructions that yields the crash. + +To ensure accurate tracking of this issue, please file a report about it at: +{bug_tracker} +""" + +_lite_message_template = """ +If you suspect this is an IPython {version} bug, please report it at: + https://github.com/ipython/ipython/issues +or send an email to the mailing list at {email} + +You can print a more detailed traceback right now with "%tb", or use "%debug" +to interactively debug it. + +Extra-detailed tracebacks for bug-reporting purposes can be enabled via: + {config}Application.verbose_crash=True +""" + + +class CrashHandler(object): + """Customizable crash handlers for IPython applications. + + Instances of this class provide a :meth:`__call__` method which can be + used as a ``sys.excepthook``. The :meth:`__call__` signature is:: + + def __call__(self, etype, evalue, etb) + """ + + message_template = _default_message_template + section_sep = '\n\n'+'*'*75+'\n\n' + + def __init__(self, app, contact_name=None, contact_email=None, + bug_tracker=None, show_crash_traceback=True, call_pdb=False): + """Create a new crash handler + + Parameters + ---------- + app : Application + A running :class:`Application` instance, which will be queried at + crash time for internal information. + + contact_name : str + A string with the name of the person to contact. + + contact_email : str + A string with the email address of the contact. + + bug_tracker : str + A string with the URL for your project's bug tracker. + + show_crash_traceback : bool + If false, don't print the crash traceback on stderr, only generate + the on-disk report + + Non-argument instance attributes: + + These instances contain some non-argument attributes which allow for + further customization of the crash handler's behavior. Please see the + source for further details. + """ + self.crash_report_fname = "Crash_report_%s.txt" % app.name + self.app = app + self.call_pdb = call_pdb + #self.call_pdb = True # dbg + self.show_crash_traceback = show_crash_traceback + self.info = dict(app_name = app.name, + contact_name = contact_name, + contact_email = contact_email, + bug_tracker = bug_tracker, + crash_report_fname = self.crash_report_fname) + + + def __call__(self, etype, evalue, etb): + """Handle an exception, call for compatible with sys.excepthook""" + + # do not allow the crash handler to be called twice without reinstalling it + # this prevents unlikely errors in the crash handling from entering an + # infinite loop. + sys.excepthook = sys.__excepthook__ + + # Report tracebacks shouldn't use color in general (safer for users) + color_scheme = 'NoColor' + + # Use this ONLY for developer debugging (keep commented out for release) + #color_scheme = 'Linux' # dbg + try: + rptdir = self.app.ipython_dir + except: + rptdir = os.getcwd() + if rptdir is None or not os.path.isdir(rptdir): + rptdir = os.getcwd() + report_name = os.path.join(rptdir,self.crash_report_fname) + # write the report filename into the instance dict so it can get + # properly expanded out in the user message template + self.crash_report_fname = report_name + self.info['crash_report_fname'] = report_name + TBhandler = ultratb.VerboseTB( + color_scheme=color_scheme, + long_header=1, + call_pdb=self.call_pdb, + ) + if self.call_pdb: + TBhandler(etype,evalue,etb) + return + else: + traceback = TBhandler.text(etype,evalue,etb,context=31) + + # print traceback to screen + if self.show_crash_traceback: + print(traceback, file=sys.stderr) + + # and generate a complete report on disk + try: + report = open(report_name,'w') + except: + print('Could not create crash report on disk.', file=sys.stderr) + return + + with report: + # Inform user on stderr of what happened + print('\n'+'*'*70+'\n', file=sys.stderr) + print(self.message_template.format(**self.info), file=sys.stderr) + + # Construct report on disk + report.write(self.make_report(traceback)) + + input("Hit to quit (your terminal may close):") + + def make_report(self,traceback): + """Return a string containing a crash report.""" + + sec_sep = self.section_sep + + report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n'] + rpt_add = report.append + rpt_add(sys_info()) + + try: + config = pformat(self.app.config) + rpt_add(sec_sep) + rpt_add('Application name: %s\n\n' % self.app_name) + rpt_add('Current user configuration structure:\n\n') + rpt_add(config) + except: + pass + rpt_add(sec_sep+'Crash traceback:\n\n' + traceback) + + return ''.join(report) + + +def crash_handler_lite(etype, evalue, tb): + """a light excepthook, adding a small message to the usual traceback""" + traceback.print_exception(etype, evalue, tb) + + from IPython.core.interactiveshell import InteractiveShell + if InteractiveShell.initialized(): + # we are in a Shell environment, give %magic example + config = "%config " + else: + # we are not in a shell, show generic config + config = "c." + print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr) + diff --git a/.venv/lib/python3.8/site-packages/IPython/core/debugger.py b/.venv/lib/python3.8/site-packages/IPython/core/debugger.py new file mode 100644 index 00000000..ff838562 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/debugger.py @@ -0,0 +1,833 @@ +# -*- coding: utf-8 -*- +""" +Pdb debugger class. + +Modified from the standard pdb.Pdb class to avoid including readline, so that +the command line completion of other programs which include this isn't +damaged. + +In the future, this class will be expanded with improvements over the standard +pdb. + +The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor +changes. Licensing should therefore be under the standard Python terms. For +details on the PSF (Python Software Foundation) standard license, see: + +https://docs.python.org/2/license.html +""" + +#***************************************************************************** +# +# This file is licensed under the PSF license. +# +# Copyright (C) 2001 Python Software Foundation, www.python.org +# Copyright (C) 2005-2006 Fernando Perez. +# +# +#***************************************************************************** + +import bdb +import functools +import inspect +import linecache +import sys +import warnings +import re + +from IPython import get_ipython +from IPython.utils import PyColorize +from IPython.utils import coloransi, py3compat +from IPython.core.excolors import exception_colors +from IPython.testing.skipdoctest import skip_doctest + + +prompt = 'ipdb> ' + +#We have to check this directly from sys.argv, config struct not yet available +from pdb import Pdb as OldPdb + +# Allow the set_trace code to operate outside of an ipython instance, even if +# it does so with some limitations. The rest of this support is implemented in +# the Tracer constructor. + +def make_arrow(pad): + """generate the leading arrow in front of traceback or debugger""" + if pad >= 2: + return '-'*(pad-2) + '> ' + elif pad == 1: + return '>' + return '' + + +def BdbQuit_excepthook(et, ev, tb, excepthook=None): + """Exception hook which handles `BdbQuit` exceptions. + + All other exceptions are processed using the `excepthook` + parameter. + """ + warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1", + DeprecationWarning, stacklevel=2) + if et==bdb.BdbQuit: + print('Exiting Debugger.') + elif excepthook is not None: + excepthook(et, ev, tb) + else: + # Backwards compatibility. Raise deprecation warning? + BdbQuit_excepthook.excepthook_ori(et,ev,tb) + + +def BdbQuit_IPython_excepthook(self,et,ev,tb,tb_offset=None): + warnings.warn( + "`BdbQuit_IPython_excepthook` is deprecated since version 5.1", + DeprecationWarning, stacklevel=2) + print('Exiting Debugger.') + + +class Tracer(object): + """ + DEPRECATED + + Class for local debugging, similar to pdb.set_trace. + + Instances of this class, when called, behave like pdb.set_trace, but + providing IPython's enhanced capabilities. + + This is implemented as a class which must be initialized in your own code + and not as a standalone function because we need to detect at runtime + whether IPython is already active or not. That detection is done in the + constructor, ensuring that this code plays nicely with a running IPython, + while functioning acceptably (though with limitations) if outside of it. + """ + + @skip_doctest + def __init__(self, colors=None): + """ + DEPRECATED + + Create a local debugger instance. + + Parameters + ---------- + + colors : str, optional + The name of the color scheme to use, it must be one of IPython's + valid color schemes. If not given, the function will default to + the current IPython scheme when running inside IPython, and to + 'NoColor' otherwise. + + Examples + -------- + :: + + from IPython.core.debugger import Tracer; debug_here = Tracer() + + Later in your code:: + + debug_here() # -> will open up the debugger at that point. + + Once the debugger activates, you can use all of its regular commands to + step through code, set breakpoints, etc. See the pdb documentation + from the Python standard library for usage details. + """ + warnings.warn("`Tracer` is deprecated since version 5.1, directly use " + "`IPython.core.debugger.Pdb.set_trace()`", + DeprecationWarning, stacklevel=2) + + ip = get_ipython() + if ip is None: + # Outside of ipython, we set our own exception hook manually + sys.excepthook = functools.partial(BdbQuit_excepthook, + excepthook=sys.excepthook) + def_colors = 'NoColor' + else: + # In ipython, we use its custom exception handler mechanism + def_colors = ip.colors + ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook) + + if colors is None: + colors = def_colors + + # The stdlib debugger internally uses a modified repr from the `repr` + # module, that limits the length of printed strings to a hardcoded + # limit of 30 characters. That much trimming is too aggressive, let's + # at least raise that limit to 80 chars, which should be enough for + # most interactive uses. + try: + from reprlib import aRepr + aRepr.maxstring = 80 + except: + # This is only a user-facing convenience, so any error we encounter + # here can be warned about but can be otherwise ignored. These + # printouts will tell us about problems if this API changes + import traceback + traceback.print_exc() + + self.debugger = Pdb(colors) + + def __call__(self): + """Starts an interactive debugger at the point where called. + + This is similar to the pdb.set_trace() function from the std lib, but + using IPython's enhanced debugger.""" + + self.debugger.set_trace(sys._getframe().f_back) + + +RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+') + + +def strip_indentation(multiline_string): + return RGX_EXTRA_INDENT.sub('', multiline_string) + + +def decorate_fn_with_doc(new_fn, old_fn, additional_text=""): + """Make new_fn have old_fn's doc string. This is particularly useful + for the ``do_...`` commands that hook into the help system. + Adapted from from a comp.lang.python posting + by Duncan Booth.""" + def wrapper(*args, **kw): + return new_fn(*args, **kw) + if old_fn.__doc__: + wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text + return wrapper + + +class Pdb(OldPdb): + """Modified Pdb class, does not load readline. + + for a standalone version that uses prompt_toolkit, see + `IPython.terminal.debugger.TerminalPdb` and + `IPython.terminal.debugger.set_trace()` + """ + + def __init__(self, color_scheme=None, completekey=None, + stdin=None, stdout=None, context=5, **kwargs): + """Create a new IPython debugger. + + :param color_scheme: Deprecated, do not use. + :param completekey: Passed to pdb.Pdb. + :param stdin: Passed to pdb.Pdb. + :param stdout: Passed to pdb.Pdb. + :param context: Number of lines of source code context to show when + displaying stacktrace information. + :param kwargs: Passed to pdb.Pdb. + The possibilities are python version dependent, see the python + docs for more info. + """ + + # Parent constructor: + try: + self.context = int(context) + if self.context <= 0: + raise ValueError("Context must be a positive integer") + except (TypeError, ValueError): + raise ValueError("Context must be a positive integer") + + # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`. + OldPdb.__init__(self, completekey, stdin, stdout, **kwargs) + + # IPython changes... + self.shell = get_ipython() + + if self.shell is None: + save_main = sys.modules['__main__'] + # No IPython instance running, we must create one + from IPython.terminal.interactiveshell import \ + TerminalInteractiveShell + self.shell = TerminalInteractiveShell.instance() + # needed by any code which calls __import__("__main__") after + # the debugger was entered. See also #9941. + sys.modules['__main__'] = save_main + + if color_scheme is not None: + warnings.warn( + "The `color_scheme` argument is deprecated since version 5.1", + DeprecationWarning, stacklevel=2) + else: + color_scheme = self.shell.colors + + self.aliases = {} + + # Create color table: we copy the default one from the traceback + # module and add a few attributes needed for debugging + self.color_scheme_table = exception_colors() + + # shorthands + C = coloransi.TermColors + cst = self.color_scheme_table + + cst['NoColor'].colors.prompt = C.NoColor + cst['NoColor'].colors.breakpoint_enabled = C.NoColor + cst['NoColor'].colors.breakpoint_disabled = C.NoColor + + cst['Linux'].colors.prompt = C.Green + cst['Linux'].colors.breakpoint_enabled = C.LightRed + cst['Linux'].colors.breakpoint_disabled = C.Red + + cst['LightBG'].colors.prompt = C.Blue + cst['LightBG'].colors.breakpoint_enabled = C.LightRed + cst['LightBG'].colors.breakpoint_disabled = C.Red + + cst['Neutral'].colors.prompt = C.Blue + cst['Neutral'].colors.breakpoint_enabled = C.LightRed + cst['Neutral'].colors.breakpoint_disabled = C.Red + + + # Add a python parser so we can syntax highlight source while + # debugging. + self.parser = PyColorize.Parser(style=color_scheme) + self.set_colors(color_scheme) + + # Set the prompt - the default prompt is '(Pdb)' + self.prompt = prompt + self.skip_hidden = True + + def set_colors(self, scheme): + """Shorthand access to the color table scheme selector method.""" + self.color_scheme_table.set_active_scheme(scheme) + self.parser.style = scheme + + def set_trace(self, frame=None): + if frame is None: + frame = sys._getframe().f_back + self.initial_frame = frame + return super().set_trace(frame) + + def hidden_frames(self, stack): + """ + Given an index in the stack return wether it should be skipped. + + This is used in up/down and where to skip frames. + """ + # The f_locals dictionary is updated from the actual frame + # locals whenever the .f_locals accessor is called, so we + # avoid calling it here to preserve self.curframe_locals. + # Futhermore, there is no good reason to hide the current frame. + ip_hide = [ + False + if s[0] in (self.curframe, getattr(self, "initial_frame", None)) + else s[0].f_locals.get("__tracebackhide__", False) + for s in stack + ] + ip_start = [i for i, s in enumerate(ip_hide) if s == "__ipython_bottom__"] + if ip_start: + ip_hide = [h if i > ip_start[0] else True for (i, h) in enumerate(ip_hide)] + return ip_hide + + def interaction(self, frame, traceback): + try: + OldPdb.interaction(self, frame, traceback) + except KeyboardInterrupt: + self.stdout.write("\n" + self.shell.get_exception_only()) + + def new_do_frame(self, arg): + OldPdb.do_frame(self, arg) + + def new_do_quit(self, arg): + + if hasattr(self, 'old_all_completions'): + self.shell.Completer.all_completions=self.old_all_completions + + return OldPdb.do_quit(self, arg) + + do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit) + + def new_do_restart(self, arg): + """Restart command. In the context of ipython this is exactly the same + thing as 'quit'.""" + self.msg("Restart doesn't make sense here. Using 'quit' instead.") + return self.do_quit(arg) + + def print_stack_trace(self, context=None): + Colors = self.color_scheme_table.active_colors + ColorsNormal = Colors.Normal + if context is None: + context = self.context + try: + context=int(context) + if context <= 0: + raise ValueError("Context must be a positive integer") + except (TypeError, ValueError): + raise ValueError("Context must be a positive integer") + try: + skipped = 0 + for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack): + if hidden and self.skip_hidden: + skipped += 1 + continue + if skipped: + print( + f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n" + ) + skipped = 0 + self.print_stack_entry(frame_lineno, context=context) + if skipped: + print( + f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n" + ) + except KeyboardInterrupt: + pass + + def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ', + context=None): + if context is None: + context = self.context + try: + context=int(context) + if context <= 0: + raise ValueError("Context must be a positive integer") + except (TypeError, ValueError): + raise ValueError("Context must be a positive integer") + print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout) + + # vds: >> + frame, lineno = frame_lineno + filename = frame.f_code.co_filename + self.shell.hooks.synchronize_with_editor(filename, lineno, 0) + # vds: << + + def format_stack_entry(self, frame_lineno, lprefix=': ', context=None): + if context is None: + context = self.context + try: + context=int(context) + if context <= 0: + print("Context must be a positive integer", file=self.stdout) + except (TypeError, ValueError): + print("Context must be a positive integer", file=self.stdout) + try: + import reprlib # Py 3 + except ImportError: + import repr as reprlib # Py 2 + + ret = [] + + Colors = self.color_scheme_table.active_colors + ColorsNormal = Colors.Normal + tpl_link = u'%s%%s%s' % (Colors.filenameEm, ColorsNormal) + tpl_call = u'%s%%s%s%%s%s' % (Colors.vName, Colors.valEm, ColorsNormal) + tpl_line = u'%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) + tpl_line_em = u'%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, + ColorsNormal) + + frame, lineno = frame_lineno + + return_value = '' + if '__return__' in frame.f_locals: + rv = frame.f_locals['__return__'] + #return_value += '->' + return_value += reprlib.repr(rv) + '\n' + ret.append(return_value) + + #s = filename + '(' + `lineno` + ')' + filename = self.canonic(frame.f_code.co_filename) + link = tpl_link % py3compat.cast_unicode(filename) + + if frame.f_code.co_name: + func = frame.f_code.co_name + else: + func = "" + + call = '' + if func != '?': + if '__args__' in frame.f_locals: + args = reprlib.repr(frame.f_locals['__args__']) + else: + args = '()' + call = tpl_call % (func, args) + + # The level info should be generated in the same format pdb uses, to + # avoid breaking the pdbtrack functionality of python-mode in *emacs. + if frame is self.curframe: + ret.append('> ') + else: + ret.append(' ') + ret.append(u'%s(%s)%s\n' % (link,lineno,call)) + + start = lineno - 1 - context//2 + lines = linecache.getlines(filename) + start = min(start, len(lines) - context) + start = max(start, 0) + lines = lines[start : start + context] + + for i,line in enumerate(lines): + show_arrow = (start + 1 + i == lineno) + linetpl = (frame is self.curframe or show_arrow) \ + and tpl_line_em \ + or tpl_line + ret.append(self.__format_line(linetpl, filename, + start + 1 + i, line, + arrow = show_arrow) ) + return ''.join(ret) + + def __format_line(self, tpl_line, filename, lineno, line, arrow = False): + bp_mark = "" + bp_mark_color = "" + + new_line, err = self.parser.format2(line, 'str') + if not err: + line = new_line + + bp = None + if lineno in self.get_file_breaks(filename): + bps = self.get_breaks(filename, lineno) + bp = bps[-1] + + if bp: + Colors = self.color_scheme_table.active_colors + bp_mark = str(bp.number) + bp_mark_color = Colors.breakpoint_enabled + if not bp.enabled: + bp_mark_color = Colors.breakpoint_disabled + + numbers_width = 7 + if arrow: + # This is the line with the error + pad = numbers_width - len(str(lineno)) - len(bp_mark) + num = '%s%s' % (make_arrow(pad), str(lineno)) + else: + num = '%*s' % (numbers_width - len(bp_mark), str(lineno)) + + return tpl_line % (bp_mark_color + bp_mark, num, line) + + + def print_list_lines(self, filename, first, last): + """The printing (as opposed to the parsing part of a 'list' + command.""" + try: + Colors = self.color_scheme_table.active_colors + ColorsNormal = Colors.Normal + tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal) + tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal) + src = [] + if filename == "" and hasattr(self, "_exec_filename"): + filename = self._exec_filename + + for lineno in range(first, last+1): + line = linecache.getline(filename, lineno) + if not line: + break + + if lineno == self.curframe.f_lineno: + line = self.__format_line(tpl_line_em, filename, lineno, line, arrow = True) + else: + line = self.__format_line(tpl_line, filename, lineno, line, arrow = False) + + src.append(line) + self.lineno = lineno + + print(''.join(src), file=self.stdout) + + except KeyboardInterrupt: + pass + + def do_skip_hidden(self, arg): + """ + Change whether or not we should skip frames with the + __tracebackhide__ attribute. + """ + if arg.strip().lower() in ("true", "yes"): + self.skip_hidden = True + elif arg.strip().lower() in ("false", "no"): + self.skip_hidden = False + + def do_list(self, arg): + """Print lines of code from the current stack frame + """ + self.lastcmd = 'list' + last = None + if arg: + try: + x = eval(arg, {}, {}) + if type(x) == type(()): + first, last = x + first = int(first) + last = int(last) + if last < first: + # Assume it's a count + last = first + last + else: + first = max(1, int(x) - 5) + except: + print('*** Error in argument:', repr(arg), file=self.stdout) + return + elif self.lineno is None: + first = max(1, self.curframe.f_lineno - 5) + else: + first = self.lineno + 1 + if last is None: + last = first + 10 + self.print_list_lines(self.curframe.f_code.co_filename, first, last) + + # vds: >> + lineno = first + filename = self.curframe.f_code.co_filename + self.shell.hooks.synchronize_with_editor(filename, lineno, 0) + # vds: << + + do_l = do_list + + def getsourcelines(self, obj): + lines, lineno = inspect.findsource(obj) + if inspect.isframe(obj) and obj.f_globals is obj.f_locals: + # must be a module frame: do not try to cut a block out of it + return lines, 1 + elif inspect.ismodule(obj): + return lines, 1 + return inspect.getblock(lines[lineno:]), lineno+1 + + def do_longlist(self, arg): + """Print lines of code from the current stack frame. + + Shows more lines than 'list' does. + """ + self.lastcmd = 'longlist' + try: + lines, lineno = self.getsourcelines(self.curframe) + except OSError as err: + self.error(err) + return + last = lineno + len(lines) + self.print_list_lines(self.curframe.f_code.co_filename, lineno, last) + do_ll = do_longlist + + def do_debug(self, arg): + """debug code + Enter a recursive debugger that steps through the code + argument (which is an arbitrary expression or statement to be + executed in the current environment). + """ + trace_function = sys.gettrace() + sys.settrace(None) + globals = self.curframe.f_globals + locals = self.curframe_locals + p = self.__class__(completekey=self.completekey, + stdin=self.stdin, stdout=self.stdout) + p.use_rawinput = self.use_rawinput + p.prompt = "(%s) " % self.prompt.strip() + self.message("ENTERING RECURSIVE DEBUGGER") + sys.call_tracing(p.run, (arg, globals, locals)) + self.message("LEAVING RECURSIVE DEBUGGER") + sys.settrace(trace_function) + self.lastcmd = p.lastcmd + + def do_pdef(self, arg): + """Print the call signature for any callable object. + + The debugger interface to %pdef""" + namespaces = [ + ("Locals", self.curframe_locals), + ("Globals", self.curframe.f_globals), + ] + self.shell.find_line_magic("pdef")(arg, namespaces=namespaces) + + def do_pdoc(self, arg): + """Print the docstring for an object. + + The debugger interface to %pdoc.""" + namespaces = [ + ("Locals", self.curframe_locals), + ("Globals", self.curframe.f_globals), + ] + self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces) + + def do_pfile(self, arg): + """Print (or run through pager) the file where an object is defined. + + The debugger interface to %pfile. + """ + namespaces = [ + ("Locals", self.curframe_locals), + ("Globals", self.curframe.f_globals), + ] + self.shell.find_line_magic("pfile")(arg, namespaces=namespaces) + + def do_pinfo(self, arg): + """Provide detailed information about an object. + + The debugger interface to %pinfo, i.e., obj?.""" + namespaces = [ + ("Locals", self.curframe_locals), + ("Globals", self.curframe.f_globals), + ] + self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces) + + def do_pinfo2(self, arg): + """Provide extra detailed information about an object. + + The debugger interface to %pinfo2, i.e., obj??.""" + namespaces = [ + ("Locals", self.curframe_locals), + ("Globals", self.curframe.f_globals), + ] + self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces) + + def do_psource(self, arg): + """Print (or run through pager) the source code for an object.""" + namespaces = [ + ("Locals", self.curframe_locals), + ("Globals", self.curframe.f_globals), + ] + self.shell.find_line_magic("psource")(arg, namespaces=namespaces) + + def do_where(self, arg): + """w(here) + Print a stack trace, with the most recent frame at the bottom. + An arrow indicates the "current frame", which determines the + context of most commands. 'bt' is an alias for this command. + + Take a number as argument as an (optional) number of context line to + print""" + if arg: + try: + context = int(arg) + except ValueError as err: + self.error(err) + return + self.print_stack_trace(context) + else: + self.print_stack_trace() + + do_w = do_where + + def stop_here(self, frame): + """Check if pdb should stop here""" + if not super().stop_here(frame): + return False + if self.skip_hidden and frame.f_locals.get("__tracebackhide__", False): + if self._wait_for_mainpyfile: + return False + Colors = self.color_scheme_table.active_colors + ColorsNormal = Colors.Normal + print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n") + return False + return True + + def do_up(self, arg): + """u(p) [count] + Move the current frame count (default one) levels up in the + stack trace (to an older frame). + + Will skip hidden frames. + """ + ## modified version of upstream that skips + # frames with __tracebackide__ + if self.curindex == 0: + self.error("Oldest frame") + return + try: + count = int(arg or 1) + except ValueError: + self.error("Invalid frame count (%s)" % arg) + return + skipped = 0 + if count < 0: + _newframe = 0 + else: + _newindex = self.curindex + counter = 0 + hidden_frames = self.hidden_frames(self.stack) + for i in range(self.curindex - 1, -1, -1): + frame = self.stack[i][0] + if hidden_frames[i] and self.skip_hidden: + skipped += 1 + continue + counter += 1 + if counter >= count: + break + else: + # if no break occured. + self.error("all frames above hidden") + return + + Colors = self.color_scheme_table.active_colors + ColorsNormal = Colors.Normal + _newframe = i + self._select_frame(_newframe) + if skipped: + print( + f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n" + ) + + def do_down(self, arg): + """d(own) [count] + Move the current frame count (default one) levels down in the + stack trace (to a newer frame). + + Will skip hidden frames. + """ + if self.curindex + 1 == len(self.stack): + self.error("Newest frame") + return + try: + count = int(arg or 1) + except ValueError: + self.error("Invalid frame count (%s)" % arg) + return + if count < 0: + _newframe = len(self.stack) - 1 + else: + _newindex = self.curindex + counter = 0 + skipped = 0 + hidden_frames = self.hidden_frames(self.stack) + for i in range(self.curindex + 1, len(self.stack)): + frame = self.stack[i][0] + if hidden_frames[i] and self.skip_hidden: + skipped += 1 + continue + counter += 1 + if counter >= count: + break + else: + self.error("all frames bellow hidden") + return + + Colors = self.color_scheme_table.active_colors + ColorsNormal = Colors.Normal + if skipped: + print( + f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n" + ) + _newframe = i + + self._select_frame(_newframe) + + do_d = do_down + do_u = do_up + +class InterruptiblePdb(Pdb): + """Version of debugger where KeyboardInterrupt exits the debugger altogether.""" + + def cmdloop(self): + """Wrap cmdloop() such that KeyboardInterrupt stops the debugger.""" + try: + return OldPdb.cmdloop(self) + except KeyboardInterrupt: + self.stop_here = lambda frame: False + self.do_quit("") + sys.settrace(None) + self.quitting = False + raise + + def _cmdloop(self): + while True: + try: + # keyboard interrupts allow for an easy way to cancel + # the current command, so allow them during interactive input + self.allow_kbdint = True + self.cmdloop() + self.allow_kbdint = False + break + except KeyboardInterrupt: + self.message('--KeyboardInterrupt--') + raise + + +def set_trace(frame=None): + """ + Start debugging from `frame`. + + If frame is not specified, debugging starts from caller's frame. + """ + Pdb().set_trace(frame or sys._getframe().f_back) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/display.py b/.venv/lib/python3.8/site-packages/IPython/core/display.py new file mode 100644 index 00000000..424414a6 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/display.py @@ -0,0 +1,1527 @@ +# -*- coding: utf-8 -*- +"""Top-level display functions for displaying object in different formats.""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + + +from binascii import b2a_hex, b2a_base64, hexlify +import json +import mimetypes +import os +import struct +import sys +import warnings +from copy import deepcopy +from os.path import splitext +from pathlib import Path, PurePath + +from IPython.utils.py3compat import cast_unicode +from IPython.testing.skipdoctest import skip_doctest + +__all__ = ['display', 'display_pretty', 'display_html', 'display_markdown', +'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json', +'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject', +'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON', +'GeoJSON', 'Javascript', 'Image', 'clear_output', 'set_matplotlib_formats', +'set_matplotlib_close', 'publish_display_data', 'update_display', 'DisplayHandle', +'Video'] + +#----------------------------------------------------------------------------- +# utility functions +#----------------------------------------------------------------------------- + +def _safe_exists(path): + """Check path, but don't let exceptions raise""" + try: + return os.path.exists(path) + except Exception: + return False + +def _merge(d1, d2): + """Like update, but merges sub-dicts instead of clobbering at the top level. + + Updates d1 in-place + """ + + if not isinstance(d2, dict) or not isinstance(d1, dict): + return d2 + for key, value in d2.items(): + d1[key] = _merge(d1.get(key), value) + return d1 + +def _display_mimetype(mimetype, objs, raw=False, metadata=None): + """internal implementation of all display_foo methods + + Parameters + ---------- + mimetype : str + The mimetype to be published (e.g. 'image/png') + objs : tuple of objects + The Python objects to display, or if raw=True raw text data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + if metadata: + metadata = {mimetype: metadata} + if raw: + # turn list of pngdata into list of { 'image/png': pngdata } + objs = [ {mimetype: obj} for obj in objs ] + display(*objs, raw=raw, metadata=metadata, include=[mimetype]) + +#----------------------------------------------------------------------------- +# Main functions +#----------------------------------------------------------------------------- + +# use * to indicate transient is keyword-only +def publish_display_data(data, metadata=None, source=None, *, transient=None, **kwargs): + """Publish data and metadata to all frontends. + + See the ``display_data`` message in the messaging documentation for + more details about this message type. + + Keys of data and metadata can be any mime-type. + + Parameters + ---------- + data : dict + A dictionary having keys that are valid MIME types (like + 'text/plain' or 'image/svg+xml') and values that are the data for + that MIME type. The data itself must be a JSON'able data + structure. Minimally all data should have the 'text/plain' data, + which can be displayed by all frontends. If more than the plain + text is given, it is up to the frontend to decide which + representation to use. + metadata : dict + A dictionary for metadata related to the data. This can contain + arbitrary key, value pairs that frontends can use to interpret + the data. mime-type keys matching those in data can be used + to specify metadata about particular representations. + source : str, deprecated + Unused. + transient : dict, keyword-only + A dictionary of transient data, such as display_id. + """ + from IPython.core.interactiveshell import InteractiveShell + + display_pub = InteractiveShell.instance().display_pub + + # only pass transient if supplied, + # to avoid errors with older ipykernel. + # TODO: We could check for ipykernel version and provide a detailed upgrade message. + if transient: + kwargs['transient'] = transient + + display_pub.publish( + data=data, + metadata=metadata, + **kwargs + ) + + +def _new_id(): + """Generate a new random text id with urandom""" + return b2a_hex(os.urandom(16)).decode('ascii') + + +def display(*objs, include=None, exclude=None, metadata=None, transient=None, display_id=None, **kwargs): + """Display a Python object in all frontends. + + By default all representations will be computed and sent to the frontends. + Frontends can decide which representation is used and how. + + In terminal IPython this will be similar to using :func:`print`, for use in richer + frontends see Jupyter notebook examples with rich display logic. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display. + raw : bool, optional + Are the objects to be displayed already mimetype-keyed dicts of raw display data, + or Python objects that need to be formatted before display? [default: False] + include : list, tuple or set, optional + A list of format type strings (MIME types) to include in the + format data dict. If this is set *only* the format types included + in this list will be computed. + exclude : list, tuple or set, optional + A list of format type strings (MIME types) to exclude in the format + data dict. If this is set all format types will be computed, + except for those included in this argument. + metadata : dict, optional + A dictionary of metadata to associate with the output. + mime-type keys in this dictionary will be associated with the individual + representation formats, if they exist. + transient : dict, optional + A dictionary of transient data to associate with the output. + Data in this dict should not be persisted to files (e.g. notebooks). + display_id : str, bool optional + Set an id for the display. + This id can be used for updating this display area later via update_display. + If given as `True`, generate a new `display_id` + kwargs: additional keyword-args, optional + Additional keyword-arguments are passed through to the display publisher. + + Returns + ------- + + handle: DisplayHandle + Returns a handle on updatable displays for use with :func:`update_display`, + if `display_id` is given. Returns :any:`None` if no `display_id` is given + (default). + + Examples + -------- + + >>> class Json(object): + ... def __init__(self, json): + ... self.json = json + ... def _repr_pretty_(self, pp, cycle): + ... import json + ... pp.text(json.dumps(self.json, indent=2)) + ... def __repr__(self): + ... return str(self.json) + ... + + >>> d = Json({1:2, 3: {4:5}}) + + >>> print(d) + {1: 2, 3: {4: 5}} + + >>> display(d) + { + "1": 2, + "3": { + "4": 5 + } + } + + >>> def int_formatter(integer, pp, cycle): + ... pp.text('I'*integer) + + >>> plain = get_ipython().display_formatter.formatters['text/plain'] + >>> plain.for_type(int, int_formatter) + + >>> display(7-5) + II + + >>> del plain.type_printers[int] + >>> display(7-5) + 2 + + See Also + -------- + + :func:`update_display` + + Notes + ----- + + In Python, objects can declare their textual representation using the + `__repr__` method. IPython expands on this idea and allows objects to declare + other, rich representations including: + + - HTML + - JSON + - PNG + - JPEG + - SVG + - LaTeX + + A single object can declare some or all of these representations; all are + handled by IPython's display system. + + The main idea of the first approach is that you have to implement special + display methods when you define your class, one for each representation you + want to use. Here is a list of the names of the special methods and the + values they must return: + + - `_repr_html_`: return raw HTML as a string, or a tuple (see below). + - `_repr_json_`: return a JSONable dict, or a tuple (see below). + - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below). + - `_repr_png_`: return raw PNG data, or a tuple (see below). + - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below). + - `_repr_latex_`: return LaTeX commands in a string surrounded by "$", + or a tuple (see below). + - `_repr_mimebundle_`: return a full mimebundle containing the mapping + from all mimetypes to data. + Use this for any mime-type not listed above. + + The above functions may also return the object's metadata alonside the + data. If the metadata is available, the functions will return a tuple + containing the data and metadata, in that order. If there is no metadata + available, then the functions will return the data only. + + When you are directly writing your own classes, you can adapt them for + display in IPython by following the above approach. But in practice, you + often need to work with existing classes that you can't easily modify. + + You can refer to the documentation on integrating with the display system in + order to register custom formatters for already existing types + (:ref:`integrating_rich_display`). + + .. versionadded:: 5.4 display available without import + .. versionadded:: 6.1 display available without import + + Since IPython 5.4 and 6.1 :func:`display` is automatically made available to + the user without import. If you are using display in a document that might + be used in a pure python context or with older version of IPython, use the + following import at the top of your file:: + + from IPython.display import display + + """ + from IPython.core.interactiveshell import InteractiveShell + + if not InteractiveShell.initialized(): + # Directly print objects. + print(*objs) + return + + raw = kwargs.pop('raw', False) + if transient is None: + transient = {} + if metadata is None: + metadata={} + if display_id: + if display_id is True: + display_id = _new_id() + transient['display_id'] = display_id + if kwargs.get('update') and 'display_id' not in transient: + raise TypeError('display_id required for update_display') + if transient: + kwargs['transient'] = transient + + if not objs and display_id: + # if given no objects, but still a request for a display_id, + # we assume the user wants to insert an empty output that + # can be updated later + objs = [{}] + raw = True + + if not raw: + format = InteractiveShell.instance().display_formatter.format + + for obj in objs: + if raw: + publish_display_data(data=obj, metadata=metadata, **kwargs) + else: + format_dict, md_dict = format(obj, include=include, exclude=exclude) + if not format_dict: + # nothing to display (e.g. _ipython_display_ took over) + continue + if metadata: + # kwarg-specified metadata gets precedence + _merge(md_dict, metadata) + publish_display_data(data=format_dict, metadata=md_dict, **kwargs) + if display_id: + return DisplayHandle(display_id) + + +# use * for keyword-only display_id arg +def update_display(obj, *, display_id, **kwargs): + """Update an existing display by id + + Parameters + ---------- + + obj: + The object with which to update the display + display_id: keyword-only + The id of the display to update + + See Also + -------- + + :func:`display` + """ + kwargs['update'] = True + display(obj, display_id=display_id, **kwargs) + + +class DisplayHandle(object): + """A handle on an updatable display + + Call `.update(obj)` to display a new object. + + Call `.display(obj`) to add a new instance of this display, + and update existing instances. + + See Also + -------- + + :func:`display`, :func:`update_display` + + """ + + def __init__(self, display_id=None): + if display_id is None: + display_id = _new_id() + self.display_id = display_id + + def __repr__(self): + return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id) + + def display(self, obj, **kwargs): + """Make a new display with my id, updating existing instances. + + Parameters + ---------- + + obj: + object to display + **kwargs: + additional keyword arguments passed to display + """ + display(obj, display_id=self.display_id, **kwargs) + + def update(self, obj, **kwargs): + """Update existing displays with my id + + Parameters + ---------- + + obj: + object to display + **kwargs: + additional keyword arguments passed to update_display + """ + update_display(obj, display_id=self.display_id, **kwargs) + + +def display_pretty(*objs, **kwargs): + """Display the pretty (default) representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw text data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('text/plain', objs, **kwargs) + + +def display_html(*objs, **kwargs): + """Display the HTML representation of an object. + + Note: If raw=False and the object does not have a HTML + representation, no HTML will be shown. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw HTML data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('text/html', objs, **kwargs) + + +def display_markdown(*objs, **kwargs): + """Displays the Markdown representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw markdown data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + + _display_mimetype('text/markdown', objs, **kwargs) + + +def display_svg(*objs, **kwargs): + """Display the SVG representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw svg data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('image/svg+xml', objs, **kwargs) + + +def display_png(*objs, **kwargs): + """Display the PNG representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw png data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('image/png', objs, **kwargs) + + +def display_jpeg(*objs, **kwargs): + """Display the JPEG representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw JPEG data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('image/jpeg', objs, **kwargs) + + +def display_latex(*objs, **kwargs): + """Display the LaTeX representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw latex data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('text/latex', objs, **kwargs) + + +def display_json(*objs, **kwargs): + """Display the JSON representation of an object. + + Note that not many frontends support displaying JSON. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw json data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('application/json', objs, **kwargs) + + +def display_javascript(*objs, **kwargs): + """Display the Javascript representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw javascript data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('application/javascript', objs, **kwargs) + + +def display_pdf(*objs, **kwargs): + """Display the PDF representation of an object. + + Parameters + ---------- + objs : tuple of objects + The Python objects to display, or if raw=True raw javascript data to + display. + raw : bool + Are the data objects raw data or Python objects that need to be + formatted before display? [default: False] + metadata : dict (optional) + Metadata to be associated with the specific mimetype output. + """ + _display_mimetype('application/pdf', objs, **kwargs) + + +#----------------------------------------------------------------------------- +# Smart classes +#----------------------------------------------------------------------------- + + +class DisplayObject(object): + """An object that wraps data to be displayed.""" + + _read_flags = 'r' + _show_mem_addr = False + metadata = None + + def __init__(self, data=None, url=None, filename=None, metadata=None): + """Create a display object given raw data. + + When this object is returned by an expression or passed to the + display function, it will result in the data being displayed + in the frontend. The MIME type of the data should match the + subclasses used, so the Png subclass should be used for 'image/png' + data. If the data is a URL, the data will first be downloaded + and then displayed. If + + Parameters + ---------- + data : unicode, str or bytes + The raw data or a URL or file to load the data from + url : unicode + A URL to download the data from. + filename : unicode + Path to a local file to load the data from. + metadata : dict + Dict of metadata associated to be the object when displayed + """ + if isinstance(data, (Path, PurePath)): + data = str(data) + + if data is not None and isinstance(data, str): + if data.startswith('http') and url is None: + url = data + filename = None + data = None + elif _safe_exists(data) and filename is None: + url = None + filename = data + data = None + + self.url = url + self.filename = filename + # because of @data.setter methods in + # subclasses ensure url and filename are set + # before assigning to self.data + self.data = data + + if metadata is not None: + self.metadata = metadata + elif self.metadata is None: + self.metadata = {} + + self.reload() + self._check_data() + + def __repr__(self): + if not self._show_mem_addr: + cls = self.__class__ + r = "<%s.%s object>" % (cls.__module__, cls.__name__) + else: + r = super(DisplayObject, self).__repr__() + return r + + def _check_data(self): + """Override in subclasses if there's something to check.""" + pass + + def _data_and_metadata(self): + """shortcut for returning metadata with shape information, if defined""" + if self.metadata: + return self.data, deepcopy(self.metadata) + else: + return self.data + + def reload(self): + """Reload the raw data from file or URL.""" + if self.filename is not None: + with open(self.filename, self._read_flags) as f: + self.data = f.read() + elif self.url is not None: + # Deferred import + from urllib.request import urlopen + response = urlopen(self.url) + data = response.read() + # extract encoding from header, if there is one: + encoding = None + if 'content-type' in response.headers: + for sub in response.headers['content-type'].split(';'): + sub = sub.strip() + if sub.startswith('charset'): + encoding = sub.split('=')[-1].strip() + break + if 'content-encoding' in response.headers: + # TODO: do deflate? + if 'gzip' in response.headers['content-encoding']: + import gzip + from io import BytesIO + with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp: + encoding = None + data = fp.read() + + # decode data, if an encoding was specified + # We only touch self.data once since + # subclasses such as SVG have @data.setter methods + # that transform self.data into ... well svg. + if encoding: + self.data = data.decode(encoding, 'replace') + else: + self.data = data + + +class TextDisplayObject(DisplayObject): + """Validate that display data is text""" + def _check_data(self): + if self.data is not None and not isinstance(self.data, str): + raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data)) + +class Pretty(TextDisplayObject): + + def _repr_pretty_(self, pp, cycle): + return pp.text(self.data) + + +class HTML(TextDisplayObject): + + def __init__(self, data=None, url=None, filename=None, metadata=None): + def warn(): + if not data: + return False + + # + # Avoid calling lower() on the entire data, because it could be a + # long string and we're only interested in its beginning and end. + # + prefix = data[:10].lower() + suffix = data[-10:].lower() + return prefix.startswith("') + m_warn.assert_not_called() + + display.HTML('') + m_warn.assert_called_with('Consider using IPython.display.IFrame instead') + + m_warn.reset_mock() + display.HTML('') + m_warn.assert_called_with('Consider using IPython.display.IFrame instead') + +def test_progress(): + p = display.ProgressBar(10) + nt.assert_in('0/10',repr(p)) + p.html_width = '100%' + p.progress = 5 + nt.assert_equal(p._repr_html_(), "") + +def test_progress_iter(): + with capture_output(display=False) as captured: + for i in display.ProgressBar(5): + out = captured.stdout + nt.assert_in('{0}/5'.format(i), out) + out = captured.stdout + nt.assert_in('5/5', out) + +def test_json(): + d = {'a': 5} + lis = [d] + metadata = [ + {'expanded': False, 'root': 'root'}, + {'expanded': True, 'root': 'root'}, + {'expanded': False, 'root': 'custom'}, + {'expanded': True, 'root': 'custom'}, + ] + json_objs = [ + display.JSON(d), + display.JSON(d, expanded=True), + display.JSON(d, root='custom'), + display.JSON(d, expanded=True, root='custom'), + ] + for j, md in zip(json_objs, metadata): + nt.assert_equal(j._repr_json_(), (d, md)) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + j = display.JSON(json.dumps(d)) + nt.assert_equal(len(w), 1) + nt.assert_equal(j._repr_json_(), (d, metadata[0])) + + json_objs = [ + display.JSON(lis), + display.JSON(lis, expanded=True), + display.JSON(lis, root='custom'), + display.JSON(lis, expanded=True, root='custom'), + ] + for j, md in zip(json_objs, metadata): + nt.assert_equal(j._repr_json_(), (lis, md)) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + j = display.JSON(json.dumps(lis)) + nt.assert_equal(len(w), 1) + nt.assert_equal(j._repr_json_(), (lis, metadata[0])) + +def test_video_embedding(): + """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash""" + v = display.Video("http://ignored") + assert not v.embed + html = v._repr_html_() + nt.assert_not_in('src="data:', html) + nt.assert_in('src="http://ignored"', html) + + with nt.assert_raises(ValueError): + v = display.Video(b'abc') + + with NamedFileInTemporaryDirectory('test.mp4') as f: + f.write(b'abc') + f.close() + + v = display.Video(f.name) + assert not v.embed + html = v._repr_html_() + nt.assert_not_in('src="data:', html) + + v = display.Video(f.name, embed=True) + html = v._repr_html_() + nt.assert_in('src="data:video/mp4;base64,YWJj"',html) + + v = display.Video(f.name, embed=True, mimetype='video/other') + html = v._repr_html_() + nt.assert_in('src="data:video/other;base64,YWJj"',html) + + v = display.Video(b'abc', embed=True, mimetype='video/mp4') + html = v._repr_html_() + nt.assert_in('src="data:video/mp4;base64,YWJj"',html) + + v = display.Video(u'YWJj', embed=True, mimetype='video/xyz') + html = v._repr_html_() + nt.assert_in('src="data:video/xyz;base64,YWJj"',html) + +def test_html_metadata(): + s = "

Test

" + h = display.HTML(s, metadata={"isolated": True}) + nt.assert_equal(h._repr_html_(), (s, {"isolated": True})) + +def test_display_id(): + ip = get_ipython() + with mock.patch.object(ip.display_pub, 'publish') as pub: + handle = display.display('x') + nt.assert_is(handle, None) + handle = display.display('y', display_id='secret') + nt.assert_is_instance(handle, display.DisplayHandle) + handle2 = display.display('z', display_id=True) + nt.assert_is_instance(handle2, display.DisplayHandle) + nt.assert_not_equal(handle.display_id, handle2.display_id) + + nt.assert_equal(pub.call_count, 3) + args, kwargs = pub.call_args_list[0] + nt.assert_equal(args, ()) + nt.assert_equal(kwargs, { + 'data': { + 'text/plain': repr('x') + }, + 'metadata': {}, + }) + args, kwargs = pub.call_args_list[1] + nt.assert_equal(args, ()) + nt.assert_equal(kwargs, { + 'data': { + 'text/plain': repr('y') + }, + 'metadata': {}, + 'transient': { + 'display_id': handle.display_id, + }, + }) + args, kwargs = pub.call_args_list[2] + nt.assert_equal(args, ()) + nt.assert_equal(kwargs, { + 'data': { + 'text/plain': repr('z') + }, + 'metadata': {}, + 'transient': { + 'display_id': handle2.display_id, + }, + }) + + +def test_update_display(): + ip = get_ipython() + with mock.patch.object(ip.display_pub, 'publish') as pub: + with nt.assert_raises(TypeError): + display.update_display('x') + display.update_display('x', display_id='1') + display.update_display('y', display_id='2') + args, kwargs = pub.call_args_list[0] + nt.assert_equal(args, ()) + nt.assert_equal(kwargs, { + 'data': { + 'text/plain': repr('x') + }, + 'metadata': {}, + 'transient': { + 'display_id': '1', + }, + 'update': True, + }) + args, kwargs = pub.call_args_list[1] + nt.assert_equal(args, ()) + nt.assert_equal(kwargs, { + 'data': { + 'text/plain': repr('y') + }, + 'metadata': {}, + 'transient': { + 'display_id': '2', + }, + 'update': True, + }) + + +def test_display_handle(): + ip = get_ipython() + handle = display.DisplayHandle() + nt.assert_is_instance(handle.display_id, str) + handle = display.DisplayHandle('my-id') + nt.assert_equal(handle.display_id, 'my-id') + with mock.patch.object(ip.display_pub, 'publish') as pub: + handle.display('x') + handle.update('y') + + args, kwargs = pub.call_args_list[0] + nt.assert_equal(args, ()) + nt.assert_equal(kwargs, { + 'data': { + 'text/plain': repr('x') + }, + 'metadata': {}, + 'transient': { + 'display_id': handle.display_id, + } + }) + args, kwargs = pub.call_args_list[1] + nt.assert_equal(args, ()) + nt.assert_equal(kwargs, { + 'data': { + 'text/plain': repr('y') + }, + 'metadata': {}, + 'transient': { + 'display_id': handle.display_id, + }, + 'update': True, + }) + diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_displayhook.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_displayhook.py new file mode 100644 index 00000000..6ad89793 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_displayhook.py @@ -0,0 +1,112 @@ +import sys +from IPython.testing.tools import AssertPrints, AssertNotPrints +from IPython.core.displayhook import CapturingDisplayHook +from IPython.utils.capture import CapturedIO + +def test_output_displayed(): + """Checking to make sure that output is displayed""" + + with AssertPrints('2'): + ip.run_cell('1+1', store_history=True) + + with AssertPrints('2'): + ip.run_cell('1+1 # comment with a semicolon;', store_history=True) + + with AssertPrints('2'): + ip.run_cell('1+1\n#commented_out_function();', store_history=True) + + +def test_output_quiet(): + """Checking to make sure that output is quiet""" + + with AssertNotPrints('2'): + ip.run_cell('1+1;', store_history=True) + + with AssertNotPrints('2'): + ip.run_cell('1+1; # comment with a semicolon', store_history=True) + + with AssertNotPrints('2'): + ip.run_cell('1+1;\n#commented_out_function()', store_history=True) + +def test_underscore_no_overrite_user(): + ip.run_cell('_ = 42', store_history=True) + ip.run_cell('1+1', store_history=True) + + with AssertPrints('42'): + ip.run_cell('print(_)', store_history=True) + + ip.run_cell('del _', store_history=True) + ip.run_cell('6+6', store_history=True) + with AssertPrints('12'): + ip.run_cell('_', store_history=True) + + +def test_underscore_no_overrite_builtins(): + ip.run_cell("import gettext ; gettext.install('foo')", store_history=True) + ip.run_cell('3+3', store_history=True) + + with AssertPrints('gettext'): + ip.run_cell('print(_)', store_history=True) + + ip.run_cell('_ = "userset"', store_history=True) + + with AssertPrints('userset'): + ip.run_cell('print(_)', store_history=True) + ip.run_cell('import builtins; del builtins._') + + +def test_interactivehooks_ast_modes(): + """ + Test that ast nodes can be triggered with different modes + """ + saved_mode = ip.ast_node_interactivity + ip.ast_node_interactivity = 'last_expr_or_assign' + + try: + with AssertPrints('2'): + ip.run_cell('a = 1+1', store_history=True) + + with AssertPrints('9'): + ip.run_cell('b = 1+8 # comment with a semicolon;', store_history=False) + + with AssertPrints('7'): + ip.run_cell('c = 1+6\n#commented_out_function();', store_history=True) + + ip.run_cell('d = 11', store_history=True) + with AssertPrints('12'): + ip.run_cell('d += 1', store_history=True) + + with AssertNotPrints('42'): + ip.run_cell('(u,v) = (41+1, 43-1)') + + finally: + ip.ast_node_interactivity = saved_mode + +def test_interactivehooks_ast_modes_semi_suppress(): + """ + Test that ast nodes can be triggered with different modes and suppressed + by semicolon + """ + saved_mode = ip.ast_node_interactivity + ip.ast_node_interactivity = 'last_expr_or_assign' + + try: + with AssertNotPrints('2'): + ip.run_cell('x = 1+1;', store_history=True) + + with AssertNotPrints('7'): + ip.run_cell('y = 1+6; # comment with a semicolon', store_history=True) + + with AssertNotPrints('9'): + ip.run_cell('z = 1+8;\n#commented_out_function()', store_history=True) + + finally: + ip.ast_node_interactivity = saved_mode + +def test_capture_display_hook_format(): + """Tests that the capture display hook conforms to the CapturedIO output format""" + hook = CapturingDisplayHook(ip) + hook({"foo": "bar"}) + captured = CapturedIO(sys.stdout, sys.stderr, hook.outputs) + # Should not raise with RichOutput transformation error + captured.outputs diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_events.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_events.py new file mode 100644 index 00000000..a4211ece --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_events.py @@ -0,0 +1,92 @@ +import unittest +from unittest.mock import Mock +import nose.tools as nt + +from IPython.core import events +import IPython.testing.tools as tt + + +@events._define_event +def ping_received(): + pass + + +@events._define_event +def event_with_argument(argument): + pass + + +class CallbackTests(unittest.TestCase): + def setUp(self): + self.em = events.EventManager(get_ipython(), + {'ping_received': ping_received, + 'event_with_argument': event_with_argument}) + + def test_register_unregister(self): + cb = Mock() + + self.em.register('ping_received', cb) + self.em.trigger('ping_received') + self.assertEqual(cb.call_count, 1) + + self.em.unregister('ping_received', cb) + self.em.trigger('ping_received') + self.assertEqual(cb.call_count, 1) + + def test_bare_function_missed_unregister(self): + def cb1(): + ... + + def cb2(): + ... + + self.em.register('ping_received', cb1) + nt.assert_raises(ValueError, self.em.unregister, 'ping_received', cb2) + self.em.unregister('ping_received', cb1) + + def test_cb_error(self): + cb = Mock(side_effect=ValueError) + self.em.register('ping_received', cb) + with tt.AssertPrints("Error in callback"): + self.em.trigger('ping_received') + + def test_cb_keyboard_interrupt(self): + cb = Mock(side_effect=KeyboardInterrupt) + self.em.register('ping_received', cb) + with tt.AssertPrints("Error in callback"): + self.em.trigger('ping_received') + + def test_unregister_during_callback(self): + invoked = [False] * 3 + + def func1(*_): + invoked[0] = True + self.em.unregister('ping_received', func1) + self.em.register('ping_received', func3) + + def func2(*_): + invoked[1] = True + self.em.unregister('ping_received', func2) + + def func3(*_): + invoked[2] = True + + self.em.register('ping_received', func1) + self.em.register('ping_received', func2) + + self.em.trigger('ping_received') + self.assertEqual([True, True, False], invoked) + self.assertEqual([func3], self.em.callbacks['ping_received']) + + def test_ignore_event_arguments_if_no_argument_required(self): + call_count = [0] + def event_with_no_argument(): + call_count[0] += 1 + + self.em.register('event_with_argument', event_with_no_argument) + self.em.trigger('event_with_argument', 'the argument') + self.assertEqual(call_count[0], 1) + + self.em.unregister('event_with_argument', event_with_no_argument) + self.em.trigger('ping_received') + self.assertEqual(call_count[0], 1) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_extension.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_extension.py new file mode 100644 index 00000000..b18e6848 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_extension.py @@ -0,0 +1,96 @@ +import os.path + +import nose.tools as nt + +import IPython.testing.tools as tt +from IPython.utils.syspathcontext import prepended_to_syspath +from IPython.utils.tempdir import TemporaryDirectory + +ext1_content = """ +def load_ipython_extension(ip): + print("Running ext1 load") + +def unload_ipython_extension(ip): + print("Running ext1 unload") +""" + +ext2_content = """ +def load_ipython_extension(ip): + print("Running ext2 load") +""" + +ext3_content = """ +def load_ipython_extension(ip): + ip2 = get_ipython() + print(ip is ip2) +""" + +def test_extension_loading(): + em = get_ipython().extension_manager + with TemporaryDirectory() as td: + ext1 = os.path.join(td, 'ext1.py') + with open(ext1, 'w') as f: + f.write(ext1_content) + + ext2 = os.path.join(td, 'ext2.py') + with open(ext2, 'w') as f: + f.write(ext2_content) + + with prepended_to_syspath(td): + assert 'ext1' not in em.loaded + assert 'ext2' not in em.loaded + + # Load extension + with tt.AssertPrints("Running ext1 load"): + assert em.load_extension('ext1') is None + assert 'ext1' in em.loaded + + # Should refuse to load it again + with tt.AssertNotPrints("Running ext1 load"): + assert em.load_extension('ext1') == 'already loaded' + + # Reload + with tt.AssertPrints("Running ext1 unload"): + with tt.AssertPrints("Running ext1 load", suppress=False): + em.reload_extension('ext1') + + # Unload + with tt.AssertPrints("Running ext1 unload"): + assert em.unload_extension('ext1') is None + + # Can't unload again + with tt.AssertNotPrints("Running ext1 unload"): + assert em.unload_extension('ext1') == 'not loaded' + assert em.unload_extension('ext2') == 'not loaded' + + # Load extension 2 + with tt.AssertPrints("Running ext2 load"): + assert em.load_extension('ext2') is None + + # Can't unload this + assert em.unload_extension('ext2') == 'no unload function' + + # But can reload it + with tt.AssertPrints("Running ext2 load"): + em.reload_extension('ext2') + + +def test_extension_builtins(): + em = get_ipython().extension_manager + with TemporaryDirectory() as td: + ext3 = os.path.join(td, 'ext3.py') + with open(ext3, 'w') as f: + f.write(ext3_content) + + assert 'ext3' not in em.loaded + + with prepended_to_syspath(td): + # Load extension + with tt.AssertPrints("True"): + assert em.load_extension('ext3') is None + assert 'ext3' in em.loaded + + +def test_non_extension(): + em = get_ipython().extension_manager + nt.assert_equal(em.load_extension('sys'), "no load function") diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_formatters.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_formatters.py new file mode 100644 index 00000000..cde43c94 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_formatters.py @@ -0,0 +1,533 @@ +"""Tests for the Formatters.""" + +import warnings +from math import pi + +try: + import numpy +except: + numpy = None +import nose.tools as nt + +from IPython import get_ipython +from traitlets.config import Config +from IPython.core.formatters import ( + PlainTextFormatter, HTMLFormatter, PDFFormatter, _mod_name_key, + DisplayFormatter, JSONFormatter, +) +from IPython.utils.io import capture_output + +class A(object): + def __repr__(self): + return 'A()' + +class B(A): + def __repr__(self): + return 'B()' + +class C: + pass + +class BadRepr(object): + def __repr__(self): + raise ValueError("bad repr") + +class BadPretty(object): + _repr_pretty_ = None + +class GoodPretty(object): + def _repr_pretty_(self, pp, cycle): + pp.text('foo') + + def __repr__(self): + return 'GoodPretty()' + +def foo_printer(obj, pp, cycle): + pp.text('foo') + +def test_pretty(): + f = PlainTextFormatter() + f.for_type(A, foo_printer) + nt.assert_equal(f(A()), 'foo') + nt.assert_equal(f(B()), 'B()') + nt.assert_equal(f(GoodPretty()), 'foo') + # Just don't raise an exception for the following: + f(BadPretty()) + + f.pprint = False + nt.assert_equal(f(A()), 'A()') + nt.assert_equal(f(B()), 'B()') + nt.assert_equal(f(GoodPretty()), 'GoodPretty()') + + +def test_deferred(): + f = PlainTextFormatter() + +def test_precision(): + """test various values for float_precision.""" + f = PlainTextFormatter() + nt.assert_equal(f(pi), repr(pi)) + f.float_precision = 0 + if numpy: + po = numpy.get_printoptions() + nt.assert_equal(po['precision'], 0) + nt.assert_equal(f(pi), '3') + f.float_precision = 2 + if numpy: + po = numpy.get_printoptions() + nt.assert_equal(po['precision'], 2) + nt.assert_equal(f(pi), '3.14') + f.float_precision = '%g' + if numpy: + po = numpy.get_printoptions() + nt.assert_equal(po['precision'], 2) + nt.assert_equal(f(pi), '3.14159') + f.float_precision = '%e' + nt.assert_equal(f(pi), '3.141593e+00') + f.float_precision = '' + if numpy: + po = numpy.get_printoptions() + nt.assert_equal(po['precision'], 8) + nt.assert_equal(f(pi), repr(pi)) + +def test_bad_precision(): + """test various invalid values for float_precision.""" + f = PlainTextFormatter() + def set_fp(p): + f.float_precision=p + nt.assert_raises(ValueError, set_fp, '%') + nt.assert_raises(ValueError, set_fp, '%.3f%i') + nt.assert_raises(ValueError, set_fp, 'foo') + nt.assert_raises(ValueError, set_fp, -1) + +def test_for_type(): + f = PlainTextFormatter() + + # initial return, None + nt.assert_is(f.for_type(C, foo_printer), None) + # no func queries + nt.assert_is(f.for_type(C), foo_printer) + # shouldn't change anything + nt.assert_is(f.for_type(C), foo_printer) + # None should do the same + nt.assert_is(f.for_type(C, None), foo_printer) + nt.assert_is(f.for_type(C, None), foo_printer) + +def test_for_type_string(): + f = PlainTextFormatter() + + type_str = '%s.%s' % (C.__module__, 'C') + + # initial return, None + nt.assert_is(f.for_type(type_str, foo_printer), None) + # no func queries + nt.assert_is(f.for_type(type_str), foo_printer) + nt.assert_in(_mod_name_key(C), f.deferred_printers) + nt.assert_is(f.for_type(C), foo_printer) + nt.assert_not_in(_mod_name_key(C), f.deferred_printers) + nt.assert_in(C, f.type_printers) + +def test_for_type_by_name(): + f = PlainTextFormatter() + + mod = C.__module__ + + # initial return, None + nt.assert_is(f.for_type_by_name(mod, 'C', foo_printer), None) + # no func queries + nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer) + # shouldn't change anything + nt.assert_is(f.for_type_by_name(mod, 'C'), foo_printer) + # None should do the same + nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer) + nt.assert_is(f.for_type_by_name(mod, 'C', None), foo_printer) + +def test_lookup(): + f = PlainTextFormatter() + + f.for_type(C, foo_printer) + nt.assert_is(f.lookup(C()), foo_printer) + with nt.assert_raises(KeyError): + f.lookup(A()) + +def test_lookup_string(): + f = PlainTextFormatter() + type_str = '%s.%s' % (C.__module__, 'C') + + f.for_type(type_str, foo_printer) + nt.assert_is(f.lookup(C()), foo_printer) + # should move from deferred to imported dict + nt.assert_not_in(_mod_name_key(C), f.deferred_printers) + nt.assert_in(C, f.type_printers) + +def test_lookup_by_type(): + f = PlainTextFormatter() + f.for_type(C, foo_printer) + nt.assert_is(f.lookup_by_type(C), foo_printer) + with nt.assert_raises(KeyError): + f.lookup_by_type(A) + +def test_lookup_by_type_string(): + f = PlainTextFormatter() + type_str = '%s.%s' % (C.__module__, 'C') + f.for_type(type_str, foo_printer) + + # verify insertion + nt.assert_in(_mod_name_key(C), f.deferred_printers) + nt.assert_not_in(C, f.type_printers) + + nt.assert_is(f.lookup_by_type(type_str), foo_printer) + # lookup by string doesn't cause import + nt.assert_in(_mod_name_key(C), f.deferred_printers) + nt.assert_not_in(C, f.type_printers) + + nt.assert_is(f.lookup_by_type(C), foo_printer) + # should move from deferred to imported dict + nt.assert_not_in(_mod_name_key(C), f.deferred_printers) + nt.assert_in(C, f.type_printers) + +def test_in_formatter(): + f = PlainTextFormatter() + f.for_type(C, foo_printer) + type_str = '%s.%s' % (C.__module__, 'C') + nt.assert_in(C, f) + nt.assert_in(type_str, f) + +def test_string_in_formatter(): + f = PlainTextFormatter() + type_str = '%s.%s' % (C.__module__, 'C') + f.for_type(type_str, foo_printer) + nt.assert_in(type_str, f) + nt.assert_in(C, f) + +def test_pop(): + f = PlainTextFormatter() + f.for_type(C, foo_printer) + nt.assert_is(f.lookup_by_type(C), foo_printer) + nt.assert_is(f.pop(C, None), foo_printer) + f.for_type(C, foo_printer) + nt.assert_is(f.pop(C), foo_printer) + with nt.assert_raises(KeyError): + f.lookup_by_type(C) + with nt.assert_raises(KeyError): + f.pop(C) + with nt.assert_raises(KeyError): + f.pop(A) + nt.assert_is(f.pop(A, None), None) + +def test_pop_string(): + f = PlainTextFormatter() + type_str = '%s.%s' % (C.__module__, 'C') + + with nt.assert_raises(KeyError): + f.pop(type_str) + + f.for_type(type_str, foo_printer) + f.pop(type_str) + with nt.assert_raises(KeyError): + f.lookup_by_type(C) + with nt.assert_raises(KeyError): + f.pop(type_str) + + f.for_type(C, foo_printer) + nt.assert_is(f.pop(type_str, None), foo_printer) + with nt.assert_raises(KeyError): + f.lookup_by_type(C) + with nt.assert_raises(KeyError): + f.pop(type_str) + nt.assert_is(f.pop(type_str, None), None) + + +def test_error_method(): + f = HTMLFormatter() + class BadHTML(object): + def _repr_html_(self): + raise ValueError("Bad HTML") + bad = BadHTML() + with capture_output() as captured: + result = f(bad) + nt.assert_is(result, None) + nt.assert_in("Traceback", captured.stdout) + nt.assert_in("Bad HTML", captured.stdout) + nt.assert_in("_repr_html_", captured.stdout) + +def test_nowarn_notimplemented(): + f = HTMLFormatter() + class HTMLNotImplemented(object): + def _repr_html_(self): + raise NotImplementedError + h = HTMLNotImplemented() + with capture_output() as captured: + result = f(h) + nt.assert_is(result, None) + nt.assert_equal("", captured.stderr) + nt.assert_equal("", captured.stdout) + +def test_warn_error_for_type(): + f = HTMLFormatter() + f.for_type(int, lambda i: name_error) + with capture_output() as captured: + result = f(5) + nt.assert_is(result, None) + nt.assert_in("Traceback", captured.stdout) + nt.assert_in("NameError", captured.stdout) + nt.assert_in("name_error", captured.stdout) + +def test_error_pretty_method(): + f = PlainTextFormatter() + class BadPretty(object): + def _repr_pretty_(self): + return "hello" + bad = BadPretty() + with capture_output() as captured: + result = f(bad) + nt.assert_is(result, None) + nt.assert_in("Traceback", captured.stdout) + nt.assert_in("_repr_pretty_", captured.stdout) + nt.assert_in("given", captured.stdout) + nt.assert_in("argument", captured.stdout) + + +def test_bad_repr_traceback(): + f = PlainTextFormatter() + bad = BadRepr() + with capture_output() as captured: + result = f(bad) + # catches error, returns None + nt.assert_is(result, None) + nt.assert_in("Traceback", captured.stdout) + nt.assert_in("__repr__", captured.stdout) + nt.assert_in("ValueError", captured.stdout) + + +class MakePDF(object): + def _repr_pdf_(self): + return 'PDF' + +def test_pdf_formatter(): + pdf = MakePDF() + f = PDFFormatter() + nt.assert_equal(f(pdf), 'PDF') + +def test_print_method_bound(): + f = HTMLFormatter() + class MyHTML(object): + def _repr_html_(self): + return "hello" + with capture_output() as captured: + result = f(MyHTML) + nt.assert_is(result, None) + nt.assert_not_in("FormatterWarning", captured.stderr) + + with capture_output() as captured: + result = f(MyHTML()) + nt.assert_equal(result, "hello") + nt.assert_equal(captured.stderr, "") + +def test_print_method_weird(): + + class TextMagicHat(object): + def __getattr__(self, key): + return key + + f = HTMLFormatter() + + text_hat = TextMagicHat() + nt.assert_equal(text_hat._repr_html_, '_repr_html_') + with capture_output() as captured: + result = f(text_hat) + + nt.assert_is(result, None) + nt.assert_not_in("FormatterWarning", captured.stderr) + + class CallableMagicHat(object): + def __getattr__(self, key): + return lambda : key + + call_hat = CallableMagicHat() + with capture_output() as captured: + result = f(call_hat) + + nt.assert_equal(result, None) + + class BadReprArgs(object): + def _repr_html_(self, extra, args): + return "html" + + bad = BadReprArgs() + with capture_output() as captured: + result = f(bad) + + nt.assert_is(result, None) + nt.assert_not_in("FormatterWarning", captured.stderr) + + +def test_format_config(): + """config objects don't pretend to support fancy reprs with lazy attrs""" + f = HTMLFormatter() + cfg = Config() + with capture_output() as captured: + result = f(cfg) + nt.assert_is(result, None) + nt.assert_equal(captured.stderr, "") + + with capture_output() as captured: + result = f(Config) + nt.assert_is(result, None) + nt.assert_equal(captured.stderr, "") + +def test_pretty_max_seq_length(): + f = PlainTextFormatter(max_seq_length=1) + lis = list(range(3)) + text = f(lis) + nt.assert_equal(text, '[0, ...]') + f.max_seq_length = 0 + text = f(lis) + nt.assert_equal(text, '[0, 1, 2]') + text = f(list(range(1024))) + lines = text.splitlines() + nt.assert_equal(len(lines), 1024) + + +def test_ipython_display_formatter(): + """Objects with _ipython_display_ defined bypass other formatters""" + f = get_ipython().display_formatter + catcher = [] + class SelfDisplaying(object): + def _ipython_display_(self): + catcher.append(self) + + class NotSelfDisplaying(object): + def __repr__(self): + return "NotSelfDisplaying" + + def _ipython_display_(self): + raise NotImplementedError + + save_enabled = f.ipython_display_formatter.enabled + f.ipython_display_formatter.enabled = True + + yes = SelfDisplaying() + no = NotSelfDisplaying() + + d, md = f.format(no) + nt.assert_equal(d, {'text/plain': repr(no)}) + nt.assert_equal(md, {}) + nt.assert_equal(catcher, []) + + d, md = f.format(yes) + nt.assert_equal(d, {}) + nt.assert_equal(md, {}) + nt.assert_equal(catcher, [yes]) + + f.ipython_display_formatter.enabled = save_enabled + + +def test_json_as_string_deprecated(): + class JSONString(object): + def _repr_json_(self): + return '{}' + + f = JSONFormatter() + with warnings.catch_warnings(record=True) as w: + d = f(JSONString()) + nt.assert_equal(d, {}) + nt.assert_equal(len(w), 1) + + +def test_repr_mime(): + class HasReprMime(object): + def _repr_mimebundle_(self, include=None, exclude=None): + return { + 'application/json+test.v2': { + 'x': 'y' + }, + 'plain/text' : '', + 'image/png' : 'i-overwrite' + } + + def _repr_png_(self): + return 'should-be-overwritten' + def _repr_html_(self): + return 'hi!' + + f = get_ipython().display_formatter + html_f = f.formatters['text/html'] + save_enabled = html_f.enabled + html_f.enabled = True + obj = HasReprMime() + d, md = f.format(obj) + html_f.enabled = save_enabled + + nt.assert_equal(sorted(d), ['application/json+test.v2', + 'image/png', + 'plain/text', + 'text/html', + 'text/plain']) + nt.assert_equal(md, {}) + + d, md = f.format(obj, include={'image/png'}) + nt.assert_equal(list(d.keys()), ['image/png'], + 'Include should filter out even things from repr_mimebundle') + nt.assert_equal(d['image/png'], 'i-overwrite', '_repr_mimebundle_ take precedence') + + + +def test_pass_correct_include_exclude(): + class Tester(object): + + def __init__(self, include=None, exclude=None): + self.include = include + self.exclude = exclude + + def _repr_mimebundle_(self, include, exclude, **kwargs): + if include and (include != self.include): + raise ValueError('include got modified: display() may be broken.') + if exclude and (exclude != self.exclude): + raise ValueError('exclude got modified: display() may be broken.') + + return None + + include = {'a', 'b', 'c'} + exclude = {'c', 'e' , 'f'} + + f = get_ipython().display_formatter + f.format(Tester(include=include, exclude=exclude), include=include, exclude=exclude) + f.format(Tester(exclude=exclude), exclude=exclude) + f.format(Tester(include=include), include=include) + + +def test_repr_mime_meta(): + class HasReprMimeMeta(object): + def _repr_mimebundle_(self, include=None, exclude=None): + data = { + 'image/png': 'base64-image-data', + } + metadata = { + 'image/png': { + 'width': 5, + 'height': 10, + } + } + return (data, metadata) + + f = get_ipython().display_formatter + obj = HasReprMimeMeta() + d, md = f.format(obj) + nt.assert_equal(sorted(d), ['image/png', 'text/plain']) + nt.assert_equal(md, { + 'image/png': { + 'width': 5, + 'height': 10, + } + }) + +def test_repr_mime_failure(): + class BadReprMime(object): + def _repr_mimebundle_(self, include=None, exclude=None): + raise RuntimeError + + f = get_ipython().display_formatter + obj = BadReprMime() + d, md = f.format(obj) + nt.assert_in('text/plain', d) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_handlers.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_handlers.py new file mode 100644 index 00000000..19248177 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_handlers.py @@ -0,0 +1,94 @@ +"""Tests for input handlers. +""" +#----------------------------------------------------------------------------- +# Module imports +#----------------------------------------------------------------------------- + +# third party +import nose.tools as nt + +# our own packages +from IPython.core import autocall +from IPython.testing import tools as tt + +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- + +# Get the public instance of IPython + +failures = [] +num_tests = 0 + +#----------------------------------------------------------------------------- +# Test functions +#----------------------------------------------------------------------------- + +class CallableIndexable(object): + def __getitem__(self, idx): return True + def __call__(self, *args, **kws): return True + + +class Autocallable(autocall.IPyAutocall): + def __call__(self): + return "called" + + +def run(tests): + """Loop through a list of (pre, post) inputs, where pre is the string + handed to ipython, and post is how that string looks after it's been + transformed (i.e. ipython's notion of _i)""" + tt.check_pairs(ip.prefilter_manager.prefilter_lines, tests) + + +def test_handlers(): + call_idx = CallableIndexable() + ip.user_ns['call_idx'] = call_idx + + # For many of the below, we're also checking that leading whitespace + # turns off the esc char, which it should unless there is a continuation + # line. + run( + [('"no change"', '"no change"'), # normal + (u"lsmagic", "get_ipython().run_line_magic('lsmagic', '')"), # magic + #("a = b # PYTHON-MODE", '_i'), # emacs -- avoids _in cache + ]) + + # Objects which are instances of IPyAutocall are *always* autocalled + autocallable = Autocallable() + ip.user_ns['autocallable'] = autocallable + + # auto + ip.magic('autocall 0') + # Only explicit escapes or instances of IPyAutocallable should get + # expanded + run([ + ('len "abc"', 'len "abc"'), + ('autocallable', 'autocallable()'), + # Don't add extra brackets (gh-1117) + ('autocallable()', 'autocallable()'), + ]) + ip.magic('autocall 1') + run([ + ('len "abc"', 'len("abc")'), + ('len "abc";', 'len("abc");'), # ; is special -- moves out of parens + # Autocall is turned off if first arg is [] and the object + # is both callable and indexable. Like so: + ('len [1,2]', 'len([1,2])'), # len doesn't support __getitem__... + ('call_idx [1]', 'call_idx [1]'), # call_idx *does*.. + ('call_idx 1', 'call_idx(1)'), + ('len', 'len'), # only at 2 does it auto-call on single args + ]) + ip.magic('autocall 2') + run([ + ('len "abc"', 'len("abc")'), + ('len "abc";', 'len("abc");'), + ('len [1,2]', 'len([1,2])'), + ('call_idx [1]', 'call_idx [1]'), + ('call_idx 1', 'call_idx(1)'), + # This is what's different: + ('len', 'len()'), # only at 2 does it auto-call on single args + ]) + ip.magic('autocall 1') + + nt.assert_equal(failures, []) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_history.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_history.py new file mode 100644 index 00000000..f4f080dc --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_history.py @@ -0,0 +1,214 @@ +# coding: utf-8 +"""Tests for the IPython tab-completion machinery. +""" +#----------------------------------------------------------------------------- +# Module imports +#----------------------------------------------------------------------------- + +# stdlib +import io +import os +import sys +import tempfile +from datetime import datetime +import sqlite3 + +# third party +import nose.tools as nt + +# our own packages +from traitlets.config.loader import Config +from IPython.utils.tempdir import TemporaryDirectory +from IPython.core.history import HistoryManager, extract_hist_ranges +from IPython.testing.decorators import skipif + +def test_proper_default_encoding(): + nt.assert_equal(sys.getdefaultencoding(), "utf-8") + +@skipif(sqlite3.sqlite_version_info > (3,24,0)) +def test_history(): + ip = get_ipython() + with TemporaryDirectory() as tmpdir: + hist_manager_ori = ip.history_manager + hist_file = os.path.join(tmpdir, 'history.sqlite') + try: + ip.history_manager = HistoryManager(shell=ip, hist_file=hist_file) + hist = [u'a=1', u'def f():\n test = 1\n return test', u"b='€Æ¾÷ß'"] + for i, h in enumerate(hist, start=1): + ip.history_manager.store_inputs(i, h) + + ip.history_manager.db_log_output = True + # Doesn't match the input, but we'll just check it's stored. + ip.history_manager.output_hist_reprs[3] = "spam" + ip.history_manager.store_output(3) + + nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist) + + # Detailed tests for _get_range_session + grs = ip.history_manager._get_range_session + nt.assert_equal(list(grs(start=2,stop=-1)), list(zip([0], [2], hist[1:-1]))) + nt.assert_equal(list(grs(start=-2)), list(zip([0,0], [2,3], hist[-2:]))) + nt.assert_equal(list(grs(output=True)), list(zip([0,0,0], [1,2,3], zip(hist, [None,None,'spam'])))) + + # Check whether specifying a range beyond the end of the current + # session results in an error (gh-804) + ip.magic('%hist 2-500') + + # Check that we can write non-ascii characters to a file + ip.magic("%%hist -f %s" % os.path.join(tmpdir, "test1")) + ip.magic("%%hist -pf %s" % os.path.join(tmpdir, "test2")) + ip.magic("%%hist -nf %s" % os.path.join(tmpdir, "test3")) + ip.magic("%%save %s 1-10" % os.path.join(tmpdir, "test4")) + + # New session + ip.history_manager.reset() + newcmds = [u"z=5", + u"class X(object):\n pass", + u"k='p'", + u"z=5"] + for i, cmd in enumerate(newcmds, start=1): + ip.history_manager.store_inputs(i, cmd) + gothist = ip.history_manager.get_range(start=1, stop=4) + nt.assert_equal(list(gothist), list(zip([0,0,0],[1,2,3], newcmds))) + # Previous session: + gothist = ip.history_manager.get_range(-1, 1, 4) + nt.assert_equal(list(gothist), list(zip([1,1,1],[1,2,3], hist))) + + newhist = [(2, i, c) for (i, c) in enumerate(newcmds, 1)] + + # Check get_hist_tail + gothist = ip.history_manager.get_tail(5, output=True, + include_latest=True) + expected = [(1, 3, (hist[-1], "spam"))] \ + + [(s, n, (c, None)) for (s, n, c) in newhist] + nt.assert_equal(list(gothist), expected) + + gothist = ip.history_manager.get_tail(2) + expected = newhist[-3:-1] + nt.assert_equal(list(gothist), expected) + + # Check get_hist_search + + gothist = ip.history_manager.search("*test*") + nt.assert_equal(list(gothist), [(1,2,hist[1])] ) + + gothist = ip.history_manager.search("*=*") + nt.assert_equal(list(gothist), + [(1, 1, hist[0]), + (1, 2, hist[1]), + (1, 3, hist[2]), + newhist[0], + newhist[2], + newhist[3]]) + + gothist = ip.history_manager.search("*=*", n=4) + nt.assert_equal(list(gothist), + [(1, 3, hist[2]), + newhist[0], + newhist[2], + newhist[3]]) + + gothist = ip.history_manager.search("*=*", unique=True) + nt.assert_equal(list(gothist), + [(1, 1, hist[0]), + (1, 2, hist[1]), + (1, 3, hist[2]), + newhist[2], + newhist[3]]) + + gothist = ip.history_manager.search("*=*", unique=True, n=3) + nt.assert_equal(list(gothist), + [(1, 3, hist[2]), + newhist[2], + newhist[3]]) + + gothist = ip.history_manager.search("b*", output=True) + nt.assert_equal(list(gothist), [(1,3,(hist[2],"spam"))] ) + + # Cross testing: check that magic %save can get previous session. + testfilename = os.path.realpath(os.path.join(tmpdir, "test.py")) + ip.magic("save " + testfilename + " ~1/1-3") + with io.open(testfilename, encoding='utf-8') as testfile: + nt.assert_equal(testfile.read(), + u"# coding: utf-8\n" + u"\n".join(hist)+u"\n") + + # Duplicate line numbers - check that it doesn't crash, and + # gets a new session + ip.history_manager.store_inputs(1, "rogue") + ip.history_manager.writeout_cache() + nt.assert_equal(ip.history_manager.session_number, 3) + finally: + # Ensure saving thread is shut down before we try to clean up the files + ip.history_manager.save_thread.stop() + # Forcibly close database rather than relying on garbage collection + ip.history_manager.db.close() + # Restore history manager + ip.history_manager = hist_manager_ori + + +def test_extract_hist_ranges(): + instr = "1 2/3 ~4/5-6 ~4/7-~4/9 ~9/2-~7/5 ~10/" + expected = [(0, 1, 2), # 0 == current session + (2, 3, 4), + (-4, 5, 7), + (-4, 7, 10), + (-9, 2, None), # None == to end + (-8, 1, None), + (-7, 1, 6), + (-10, 1, None)] + actual = list(extract_hist_ranges(instr)) + nt.assert_equal(actual, expected) + +def test_magic_rerun(): + """Simple test for %rerun (no args -> rerun last line)""" + ip = get_ipython() + ip.run_cell("a = 10", store_history=True) + ip.run_cell("a += 1", store_history=True) + nt.assert_equal(ip.user_ns["a"], 11) + ip.run_cell("%rerun", store_history=True) + nt.assert_equal(ip.user_ns["a"], 12) + +def test_timestamp_type(): + ip = get_ipython() + info = ip.history_manager.get_session_info() + nt.assert_true(isinstance(info[1], datetime)) + +def test_hist_file_config(): + cfg = Config() + tfile = tempfile.NamedTemporaryFile(delete=False) + cfg.HistoryManager.hist_file = tfile.name + try: + hm = HistoryManager(shell=get_ipython(), config=cfg) + nt.assert_equal(hm.hist_file, cfg.HistoryManager.hist_file) + finally: + try: + os.remove(tfile.name) + except OSError: + # same catch as in testing.tools.TempFileMixin + # On Windows, even though we close the file, we still can't + # delete it. I have no clue why + pass + +def test_histmanager_disabled(): + """Ensure that disabling the history manager doesn't create a database.""" + cfg = Config() + cfg.HistoryAccessor.enabled = False + + ip = get_ipython() + with TemporaryDirectory() as tmpdir: + hist_manager_ori = ip.history_manager + hist_file = os.path.join(tmpdir, 'history.sqlite') + cfg.HistoryManager.hist_file = hist_file + try: + ip.history_manager = HistoryManager(shell=ip, config=cfg) + hist = [u'a=1', u'def f():\n test = 1\n return test', u"b='€Æ¾÷ß'"] + for i, h in enumerate(hist, start=1): + ip.history_manager.store_inputs(i, h) + nt.assert_equal(ip.history_manager.input_hist_raw, [''] + hist) + ip.history_manager.reset() + ip.history_manager.end_session() + finally: + ip.history_manager = hist_manager_ori + + # hist_file should not be created + nt.assert_false(os.path.exists(hist_file)) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_hooks.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_hooks.py new file mode 100644 index 00000000..35d3f315 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_hooks.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +"""Tests for CommandChainDispatcher.""" + + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import nose.tools as nt +from IPython.core.error import TryNext +from IPython.core.hooks import CommandChainDispatcher + +#----------------------------------------------------------------------------- +# Local utilities +#----------------------------------------------------------------------------- + +# Define two classes, one which succeeds and one which raises TryNext. Each +# sets the attribute `called` to True when it is called. +class Okay(object): + def __init__(self, message): + self.message = message + self.called = False + def __call__(self): + self.called = True + return self.message + +class Fail(object): + def __init__(self, message): + self.message = message + self.called = False + def __call__(self): + self.called = True + raise TryNext(self.message) + +#----------------------------------------------------------------------------- +# Test functions +#----------------------------------------------------------------------------- + +def test_command_chain_dispatcher_ff(): + """Test two failing hooks""" + fail1 = Fail(u'fail1') + fail2 = Fail(u'fail2') + dp = CommandChainDispatcher([(0, fail1), + (10, fail2)]) + + try: + dp() + except TryNext as e: + nt.assert_equal(str(e), u'fail2') + else: + assert False, "Expected exception was not raised." + + nt.assert_true(fail1.called) + nt.assert_true(fail2.called) + +def test_command_chain_dispatcher_fofo(): + """Test a mixture of failing and succeeding hooks.""" + fail1 = Fail(u'fail1') + fail2 = Fail(u'fail2') + okay1 = Okay(u'okay1') + okay2 = Okay(u'okay2') + + dp = CommandChainDispatcher([(0, fail1), + # (5, okay1), # add this later + (10, fail2), + (15, okay2)]) + dp.add(okay1, 5) + + nt.assert_equal(dp(), u'okay1') + + nt.assert_true(fail1.called) + nt.assert_true(okay1.called) + nt.assert_false(fail2.called) + nt.assert_false(okay2.called) + +def test_command_chain_dispatcher_eq_priority(): + okay1 = Okay(u'okay1') + okay2 = Okay(u'okay2') + dp = CommandChainDispatcher([(1, okay1)]) + dp.add(okay2, 1) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_imports.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_imports.py new file mode 100644 index 00000000..7aa278fb --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_imports.py @@ -0,0 +1,52 @@ +# encoding: utf-8 + +def test_import_completer(): + from IPython.core import completer + +def test_import_crashhandler(): + from IPython.core import crashhandler + +def test_import_debugger(): + from IPython.core import debugger + +def test_import_excolors(): + from IPython.core import excolors + +def test_import_history(): + from IPython.core import history + +def test_import_hooks(): + from IPython.core import hooks + +def test_import_getipython(): + from IPython.core import getipython + +def test_import_interactiveshell(): + from IPython.core import interactiveshell + +def test_import_logger(): + from IPython.core import logger + +def test_import_macro(): + from IPython.core import macro + +def test_import_magic(): + from IPython.core import magic + +def test_import_oinspect(): + from IPython.core import oinspect + +def test_import_prefilter(): + from IPython.core import prefilter + +def test_import_prompts(): + from IPython.core import prompts + +def test_import_release(): + from IPython.core import release + +def test_import_ultratb(): + from IPython.core import ultratb + +def test_import_usage(): + from IPython.core import usage diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputsplitter.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputsplitter.py new file mode 100644 index 00000000..a39943ae --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputsplitter.py @@ -0,0 +1,639 @@ +# -*- coding: utf-8 -*- +"""Tests for the inputsplitter module.""" + + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import unittest +import sys + +import nose.tools as nt + +from IPython.core import inputsplitter as isp +from IPython.core.inputtransformer import InputTransformer +from IPython.core.tests.test_inputtransformer import syntax, syntax_ml +from IPython.testing import tools as tt + +#----------------------------------------------------------------------------- +# Semi-complete examples (also used as tests) +#----------------------------------------------------------------------------- + +# Note: at the bottom, there's a slightly more complete version of this that +# can be useful during development of code here. + +def mini_interactive_loop(input_func): + """Minimal example of the logic of an interactive interpreter loop. + + This serves as an example, and it is used by the test system with a fake + raw_input that simulates interactive input.""" + + from IPython.core.inputsplitter import InputSplitter + + isp = InputSplitter() + # In practice, this input loop would be wrapped in an outside loop to read + # input indefinitely, until some exit/quit command was issued. Here we + # only illustrate the basic inner loop. + while isp.push_accepts_more(): + indent = ' '*isp.get_indent_spaces() + prompt = '>>> ' + indent + line = indent + input_func(prompt) + isp.push(line) + + # Here we just return input so we can use it in a test suite, but a real + # interpreter would instead send it for execution somewhere. + src = isp.source_reset() + #print 'Input source was:\n', src # dbg + return src + +#----------------------------------------------------------------------------- +# Test utilities, just for local use +#----------------------------------------------------------------------------- + +def assemble(block): + """Assemble a block into multi-line sub-blocks.""" + return ['\n'.join(sub_block)+'\n' for sub_block in block] + + +def pseudo_input(lines): + """Return a function that acts like raw_input but feeds the input list.""" + ilines = iter(lines) + def raw_in(prompt): + try: + return next(ilines) + except StopIteration: + return '' + return raw_in + +#----------------------------------------------------------------------------- +# Tests +#----------------------------------------------------------------------------- +def test_spaces(): + tests = [('', 0), + (' ', 1), + ('\n', 0), + (' \n', 1), + ('x', 0), + (' x', 1), + (' x',2), + (' x',4), + # Note: tabs are counted as a single whitespace! + ('\tx', 1), + ('\t x', 2), + ] + tt.check_pairs(isp.num_ini_spaces, tests) + + +def test_remove_comments(): + tests = [('text', 'text'), + ('text # comment', 'text '), + ('text # comment\n', 'text \n'), + ('text # comment \n', 'text \n'), + ('line # c \nline\n','line \nline\n'), + ('line # c \nline#c2 \nline\nline #c\n\n', + 'line \nline\nline\nline \n\n'), + ] + tt.check_pairs(isp.remove_comments, tests) + + +def test_get_input_encoding(): + encoding = isp.get_input_encoding() + nt.assert_true(isinstance(encoding, str)) + # simple-minded check that at least encoding a simple string works with the + # encoding we got. + nt.assert_equal(u'test'.encode(encoding), b'test') + + +class NoInputEncodingTestCase(unittest.TestCase): + def setUp(self): + self.old_stdin = sys.stdin + class X: pass + fake_stdin = X() + sys.stdin = fake_stdin + + def test(self): + # Verify that if sys.stdin has no 'encoding' attribute we do the right + # thing + enc = isp.get_input_encoding() + self.assertEqual(enc, 'ascii') + + def tearDown(self): + sys.stdin = self.old_stdin + + +class InputSplitterTestCase(unittest.TestCase): + def setUp(self): + self.isp = isp.InputSplitter() + + def test_reset(self): + isp = self.isp + isp.push('x=1') + isp.reset() + self.assertEqual(isp._buffer, []) + self.assertEqual(isp.get_indent_spaces(), 0) + self.assertEqual(isp.source, '') + self.assertEqual(isp.code, None) + self.assertEqual(isp._is_complete, False) + + def test_source(self): + self.isp._store('1') + self.isp._store('2') + self.assertEqual(self.isp.source, '1\n2\n') + self.assertEqual(len(self.isp._buffer)>0, True) + self.assertEqual(self.isp.source_reset(), '1\n2\n') + self.assertEqual(self.isp._buffer, []) + self.assertEqual(self.isp.source, '') + + def test_indent(self): + isp = self.isp # shorthand + isp.push('x=1') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('if 1:\n x=1') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push('y=2\n') + self.assertEqual(isp.get_indent_spaces(), 0) + + def test_indent2(self): + isp = self.isp + isp.push('if 1:') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push(' x=1') + self.assertEqual(isp.get_indent_spaces(), 4) + # Blank lines shouldn't change the indent level + isp.push(' '*2) + self.assertEqual(isp.get_indent_spaces(), 4) + + def test_indent3(self): + isp = self.isp + # When a multiline statement contains parens or multiline strings, we + # shouldn't get confused. + isp.push("if 1:") + isp.push(" x = (1+\n 2)") + self.assertEqual(isp.get_indent_spaces(), 4) + + def test_indent4(self): + isp = self.isp + # whitespace after ':' should not screw up indent level + isp.push('if 1: \n x=1') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push('y=2\n') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('if 1:\t\n x=1') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push('y=2\n') + self.assertEqual(isp.get_indent_spaces(), 0) + + def test_dedent_pass(self): + isp = self.isp # shorthand + # should NOT cause dedent + isp.push('if 1:\n passes = 5') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push('if 1:\n pass') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('if 1:\n pass ') + self.assertEqual(isp.get_indent_spaces(), 0) + + def test_dedent_break(self): + isp = self.isp # shorthand + # should NOT cause dedent + isp.push('while 1:\n breaks = 5') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push('while 1:\n break') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('while 1:\n break ') + self.assertEqual(isp.get_indent_spaces(), 0) + + def test_dedent_continue(self): + isp = self.isp # shorthand + # should NOT cause dedent + isp.push('while 1:\n continues = 5') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push('while 1:\n continue') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('while 1:\n continue ') + self.assertEqual(isp.get_indent_spaces(), 0) + + def test_dedent_raise(self): + isp = self.isp # shorthand + # should NOT cause dedent + isp.push('if 1:\n raised = 4') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push('if 1:\n raise TypeError()') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('if 1:\n raise') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('if 1:\n raise ') + self.assertEqual(isp.get_indent_spaces(), 0) + + def test_dedent_return(self): + isp = self.isp # shorthand + # should NOT cause dedent + isp.push('if 1:\n returning = 4') + self.assertEqual(isp.get_indent_spaces(), 4) + isp.push('if 1:\n return 5 + 493') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('if 1:\n return') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('if 1:\n return ') + self.assertEqual(isp.get_indent_spaces(), 0) + isp.push('if 1:\n return(0)') + self.assertEqual(isp.get_indent_spaces(), 0) + + def test_push(self): + isp = self.isp + self.assertEqual(isp.push('x=1'), True) + + def test_push2(self): + isp = self.isp + self.assertEqual(isp.push('if 1:'), False) + for line in [' x=1', '# a comment', ' y=2']: + print(line) + self.assertEqual(isp.push(line), True) + + def test_push3(self): + isp = self.isp + isp.push('if True:') + isp.push(' a = 1') + self.assertEqual(isp.push('b = [1,'), False) + + def test_push_accepts_more(self): + isp = self.isp + isp.push('x=1') + self.assertEqual(isp.push_accepts_more(), False) + + def test_push_accepts_more2(self): + isp = self.isp + isp.push('if 1:') + self.assertEqual(isp.push_accepts_more(), True) + isp.push(' x=1') + self.assertEqual(isp.push_accepts_more(), True) + isp.push('') + self.assertEqual(isp.push_accepts_more(), False) + + def test_push_accepts_more3(self): + isp = self.isp + isp.push("x = (2+\n3)") + self.assertEqual(isp.push_accepts_more(), False) + + def test_push_accepts_more4(self): + isp = self.isp + # When a multiline statement contains parens or multiline strings, we + # shouldn't get confused. + # FIXME: we should be able to better handle de-dents in statements like + # multiline strings and multiline expressions (continued with \ or + # parens). Right now we aren't handling the indentation tracking quite + # correctly with this, though in practice it may not be too much of a + # problem. We'll need to see. + isp.push("if 1:") + isp.push(" x = (2+") + isp.push(" 3)") + self.assertEqual(isp.push_accepts_more(), True) + isp.push(" y = 3") + self.assertEqual(isp.push_accepts_more(), True) + isp.push('') + self.assertEqual(isp.push_accepts_more(), False) + + def test_push_accepts_more5(self): + isp = self.isp + isp.push('try:') + isp.push(' a = 5') + isp.push('except:') + isp.push(' raise') + # We want to be able to add an else: block at this point, so it should + # wait for a blank line. + self.assertEqual(isp.push_accepts_more(), True) + + def test_continuation(self): + isp = self.isp + isp.push("import os, \\") + self.assertEqual(isp.push_accepts_more(), True) + isp.push("sys") + self.assertEqual(isp.push_accepts_more(), False) + + def test_syntax_error(self): + isp = self.isp + # Syntax errors immediately produce a 'ready' block, so the invalid + # Python can be sent to the kernel for evaluation with possible ipython + # special-syntax conversion. + isp.push('run foo') + self.assertEqual(isp.push_accepts_more(), False) + + def test_unicode(self): + self.isp.push(u"Pérez") + self.isp.push(u'\xc3\xa9') + self.isp.push(u"u'\xc3\xa9'") + + def test_line_continuation(self): + """ Test issue #2108.""" + isp = self.isp + # A blank line after a line continuation should not accept more + isp.push("1 \\\n\n") + self.assertEqual(isp.push_accepts_more(), False) + # Whitespace after a \ is a SyntaxError. The only way to test that + # here is to test that push doesn't accept more (as with + # test_syntax_error() above). + isp.push(r"1 \ ") + self.assertEqual(isp.push_accepts_more(), False) + # Even if the line is continuable (c.f. the regular Python + # interpreter) + isp.push(r"(1 \ ") + self.assertEqual(isp.push_accepts_more(), False) + + def test_check_complete(self): + isp = self.isp + self.assertEqual(isp.check_complete("a = 1"), ('complete', None)) + self.assertEqual(isp.check_complete("for a in range(5):"), ('incomplete', 4)) + self.assertEqual(isp.check_complete("raise = 2"), ('invalid', None)) + self.assertEqual(isp.check_complete("a = [1,\n2,"), ('incomplete', 0)) + self.assertEqual(isp.check_complete("def a():\n x=1\n global x"), ('invalid', None)) + +class InteractiveLoopTestCase(unittest.TestCase): + """Tests for an interactive loop like a python shell. + """ + def check_ns(self, lines, ns): + """Validate that the given input lines produce the resulting namespace. + + Note: the input lines are given exactly as they would be typed in an + auto-indenting environment, as mini_interactive_loop above already does + auto-indenting and prepends spaces to the input. + """ + src = mini_interactive_loop(pseudo_input(lines)) + test_ns = {} + exec(src, test_ns) + # We can't check that the provided ns is identical to the test_ns, + # because Python fills test_ns with extra keys (copyright, etc). But + # we can check that the given dict is *contained* in test_ns + for k,v in ns.items(): + self.assertEqual(test_ns[k], v) + + def test_simple(self): + self.check_ns(['x=1'], dict(x=1)) + + def test_simple2(self): + self.check_ns(['if 1:', 'x=2'], dict(x=2)) + + def test_xy(self): + self.check_ns(['x=1; y=2'], dict(x=1, y=2)) + + def test_abc(self): + self.check_ns(['if 1:','a=1','b=2','c=3'], dict(a=1, b=2, c=3)) + + def test_multi(self): + self.check_ns(['x =(1+','1+','2)'], dict(x=4)) + + +class IPythonInputTestCase(InputSplitterTestCase): + """By just creating a new class whose .isp is a different instance, we + re-run the same test battery on the new input splitter. + + In addition, this runs the tests over the syntax and syntax_ml dicts that + were tested by individual functions, as part of the OO interface. + + It also makes some checks on the raw buffer storage. + """ + + def setUp(self): + self.isp = isp.IPythonInputSplitter() + + def test_syntax(self): + """Call all single-line syntax tests from the main object""" + isp = self.isp + for example in syntax.values(): + for raw, out_t in example: + if raw.startswith(' '): + continue + + isp.push(raw+'\n') + out_raw = isp.source_raw + out = isp.source_reset() + self.assertEqual(out.rstrip(), out_t, + tt.pair_fail_msg.format("inputsplitter",raw, out_t, out)) + self.assertEqual(out_raw.rstrip(), raw.rstrip()) + + def test_syntax_multiline(self): + isp = self.isp + for example in syntax_ml.values(): + for line_pairs in example: + out_t_parts = [] + raw_parts = [] + for lraw, out_t_part in line_pairs: + if out_t_part is not None: + out_t_parts.append(out_t_part) + + if lraw is not None: + isp.push(lraw) + raw_parts.append(lraw) + + out_raw = isp.source_raw + out = isp.source_reset() + out_t = '\n'.join(out_t_parts).rstrip() + raw = '\n'.join(raw_parts).rstrip() + self.assertEqual(out.rstrip(), out_t) + self.assertEqual(out_raw.rstrip(), raw) + + def test_syntax_multiline_cell(self): + isp = self.isp + for example in syntax_ml.values(): + + out_t_parts = [] + for line_pairs in example: + raw = '\n'.join(r for r, _ in line_pairs if r is not None) + out_t = '\n'.join(t for _,t in line_pairs if t is not None) + out = isp.transform_cell(raw) + # Match ignoring trailing whitespace + self.assertEqual(out.rstrip(), out_t.rstrip()) + + def test_cellmagic_preempt(self): + isp = self.isp + for raw, name, line, cell in [ + ("%%cellm a\nIn[1]:", u'cellm', u'a', u'In[1]:'), + ("%%cellm \nline\n>>> hi", u'cellm', u'', u'line\n>>> hi'), + (">>> %%cellm \nline\n>>> hi", u'cellm', u'', u'line\nhi'), + ("%%cellm \n>>> hi", u'cellm', u'', u'>>> hi'), + ("%%cellm \nline1\nline2", u'cellm', u'', u'line1\nline2'), + ("%%cellm \nline1\\\\\nline2", u'cellm', u'', u'line1\\\\\nline2'), + ]: + expected = "get_ipython().run_cell_magic(%r, %r, %r)" % ( + name, line, cell + ) + out = isp.transform_cell(raw) + self.assertEqual(out.rstrip(), expected.rstrip()) + + def test_multiline_passthrough(self): + isp = self.isp + class CommentTransformer(InputTransformer): + def __init__(self): + self._lines = [] + + def push(self, line): + self._lines.append(line + '#') + + def reset(self): + text = '\n'.join(self._lines) + self._lines = [] + return text + + isp.physical_line_transforms.insert(0, CommentTransformer()) + + for raw, expected in [ + ("a=5", "a=5#"), + ("%ls foo", "get_ipython().run_line_magic(%r, %r)" % (u'ls', u'foo#')), + ("!ls foo\n%ls bar", "get_ipython().system(%r)\nget_ipython().run_line_magic(%r, %r)" % ( + u'ls foo#', u'ls', u'bar#' + )), + ("1\n2\n3\n%ls foo\n4\n5", "1#\n2#\n3#\nget_ipython().run_line_magic(%r, %r)\n4#\n5#" % (u'ls', u'foo#')), + ]: + out = isp.transform_cell(raw) + self.assertEqual(out.rstrip(), expected.rstrip()) + +#----------------------------------------------------------------------------- +# Main - use as a script, mostly for developer experiments +#----------------------------------------------------------------------------- + +if __name__ == '__main__': + # A simple demo for interactive experimentation. This code will not get + # picked up by any test suite. + from IPython.core.inputsplitter import IPythonInputSplitter + + # configure here the syntax to use, prompt and whether to autoindent + #isp, start_prompt = InputSplitter(), '>>> ' + isp, start_prompt = IPythonInputSplitter(), 'In> ' + + autoindent = True + #autoindent = False + + try: + while True: + prompt = start_prompt + while isp.push_accepts_more(): + indent = ' '*isp.get_indent_spaces() + if autoindent: + line = indent + input(prompt+indent) + else: + line = input(prompt) + isp.push(line) + prompt = '... ' + + # Here we just return input so we can use it in a test suite, but a + # real interpreter would instead send it for execution somewhere. + #src = isp.source; raise EOFError # dbg + raw = isp.source_raw + src = isp.source_reset() + print('Input source was:\n', src) + print('Raw source was:\n', raw) + except EOFError: + print('Bye') + +# Tests for cell magics support + +def test_last_blank(): + nt.assert_false(isp.last_blank('')) + nt.assert_false(isp.last_blank('abc')) + nt.assert_false(isp.last_blank('abc\n')) + nt.assert_false(isp.last_blank('abc\na')) + + nt.assert_true(isp.last_blank('\n')) + nt.assert_true(isp.last_blank('\n ')) + nt.assert_true(isp.last_blank('abc\n ')) + nt.assert_true(isp.last_blank('abc\n\n')) + nt.assert_true(isp.last_blank('abc\nd\n\n')) + nt.assert_true(isp.last_blank('abc\nd\ne\n\n')) + nt.assert_true(isp.last_blank('abc \n \n \n\n')) + + +def test_last_two_blanks(): + nt.assert_false(isp.last_two_blanks('')) + nt.assert_false(isp.last_two_blanks('abc')) + nt.assert_false(isp.last_two_blanks('abc\n')) + nt.assert_false(isp.last_two_blanks('abc\n\na')) + nt.assert_false(isp.last_two_blanks('abc\n \n')) + nt.assert_false(isp.last_two_blanks('abc\n\n')) + + nt.assert_true(isp.last_two_blanks('\n\n')) + nt.assert_true(isp.last_two_blanks('\n\n ')) + nt.assert_true(isp.last_two_blanks('\n \n')) + nt.assert_true(isp.last_two_blanks('abc\n\n ')) + nt.assert_true(isp.last_two_blanks('abc\n\n\n')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n ')) + nt.assert_true(isp.last_two_blanks('abc\n\n \n \n')) + nt.assert_true(isp.last_two_blanks('abc\nd\n\n\n')) + nt.assert_true(isp.last_two_blanks('abc\nd\ne\nf\n\n\n')) + + +class CellMagicsCommon(object): + + def test_whole_cell(self): + src = "%%cellm line\nbody\n" + out = self.sp.transform_cell(src) + ref = "get_ipython().run_cell_magic('cellm', 'line', 'body')\n" + nt.assert_equal(out, ref) + + def test_cellmagic_help(self): + self.sp.push('%%cellm?') + nt.assert_false(self.sp.push_accepts_more()) + + def tearDown(self): + self.sp.reset() + + +class CellModeCellMagics(CellMagicsCommon, unittest.TestCase): + sp = isp.IPythonInputSplitter(line_input_checker=False) + + def test_incremental(self): + sp = self.sp + sp.push('%%cellm firstline\n') + nt.assert_true(sp.push_accepts_more()) #1 + sp.push('line2\n') + nt.assert_true(sp.push_accepts_more()) #2 + sp.push('\n') + # This should accept a blank line and carry on until the cell is reset + nt.assert_true(sp.push_accepts_more()) #3 + + def test_no_strip_coding(self): + src = '\n'.join([ + '%%writefile foo.py', + '# coding: utf-8', + 'print(u"üñîçø∂é")', + ]) + out = self.sp.transform_cell(src) + nt.assert_in('# coding: utf-8', out) + + +class LineModeCellMagics(CellMagicsCommon, unittest.TestCase): + sp = isp.IPythonInputSplitter(line_input_checker=True) + + def test_incremental(self): + sp = self.sp + sp.push('%%cellm line2\n') + nt.assert_true(sp.push_accepts_more()) #1 + sp.push('\n') + # In this case, a blank line should end the cell magic + nt.assert_false(sp.push_accepts_more()) #2 + +indentation_samples = [ + ('a = 1', 0), + ('for a in b:', 4), + ('def f():', 4), + ('def f(): #comment', 4), + ('a = ":#not a comment"', 0), + ('def f():\n a = 1', 4), + ('def f():\n return 1', 0), + ('for a in b:\n' + ' if a < 0:' + ' continue', 3), + ('a = {', 4), + ('a = {\n' + ' 1,', 5), + ('b = """123', 0), + ('', 0), + ('def f():\n pass', 0), + ('class Bar:\n def f():\n pass', 4), + ('class Bar:\n def f():\n raise', 4), +] + +def test_find_next_indent(): + for code, exp in indentation_samples: + res = isp.find_next_indent(code) + msg = "{!r} != {!r} (expected)\n Code: {!r}".format(res, exp, code) + assert res == exp, msg diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer.py new file mode 100644 index 00000000..0d97fd4d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer.py @@ -0,0 +1,495 @@ +import tokenize +import nose.tools as nt + +from IPython.testing import tools as tt +from IPython.utils import py3compat +u_fmt = py3compat.u_format + +from IPython.core import inputtransformer as ipt + +def transform_and_reset(transformer): + transformer = transformer() + def transform(inp): + try: + return transformer.push(inp) + finally: + transformer.reset() + + return transform + +# Transformer tests +def transform_checker(tests, transformer, **kwargs): + """Utility to loop over test inputs""" + transformer = transformer(**kwargs) + try: + for inp, tr in tests: + if inp is None: + out = transformer.reset() + else: + out = transformer.push(inp) + nt.assert_equal(out, tr) + finally: + transformer.reset() + +# Data for all the syntax tests in the form of lists of pairs of +# raw/transformed input. We store it here as a global dict so that we can use +# it both within single-function tests and also to validate the behavior of the +# larger objects + +syntax = \ + dict(assign_system = + [(i,py3compat.u_format(o)) for i,o in \ + [(u'a =! ls', "a = get_ipython().getoutput('ls')"), + (u'b = !ls', "b = get_ipython().getoutput('ls')"), + (u'c= !ls', "c = get_ipython().getoutput('ls')"), + (u'd == !ls', u'd == !ls'), # Invalid syntax, but we leave == alone. + ('x=1', 'x=1'), # normal input is unmodified + (' ',' '), # blank lines are kept intact + # Tuple unpacking + (u"a, b = !echo 'a\\nb'", u"a, b = get_ipython().getoutput(\"echo 'a\\\\nb'\")"), + (u"a,= !echo 'a'", u"a, = get_ipython().getoutput(\"echo 'a'\")"), + (u"a, *bc = !echo 'a\\nb\\nc'", u"a, *bc = get_ipython().getoutput(\"echo 'a\\\\nb\\\\nc'\")"), + # Tuple unpacking with regular Python expressions, not our syntax. + (u"a, b = range(2)", u"a, b = range(2)"), + (u"a, = range(1)", u"a, = range(1)"), + (u"a, *bc = range(3)", u"a, *bc = range(3)"), + ]], + + assign_magic = + [(i,py3compat.u_format(o)) for i,o in \ + [(u'a =% who', "a = get_ipython().run_line_magic('who', '')"), + (u'b = %who', "b = get_ipython().run_line_magic('who', '')"), + (u'c= %ls', "c = get_ipython().run_line_magic('ls', '')"), + (u'd == %ls', u'd == %ls'), # Invalid syntax, but we leave == alone. + ('x=1', 'x=1'), # normal input is unmodified + (' ',' '), # blank lines are kept intact + (u"a, b = %foo", u"a, b = get_ipython().run_line_magic('foo', '')"), + ]], + + classic_prompt = + [('>>> x=1', 'x=1'), + ('x=1', 'x=1'), # normal input is unmodified + (' ', ' '), # blank lines are kept intact + ], + + ipy_prompt = + [('In [1]: x=1', 'x=1'), + ('x=1', 'x=1'), # normal input is unmodified + (' ',' '), # blank lines are kept intact + ], + + # Tests for the escape transformer to leave normal code alone + escaped_noesc = + [ (' ', ' '), + ('x=1', 'x=1'), + ], + + # System calls + escaped_shell = + [(i,py3compat.u_format(o)) for i,o in \ + [ (u'!ls', "get_ipython().system('ls')"), + # Double-escape shell, this means to capture the output of the + # subprocess and return it + (u'!!ls', "get_ipython().getoutput('ls')"), + ]], + + # Help/object info + escaped_help = + [(i,py3compat.u_format(o)) for i,o in \ + [ (u'?', 'get_ipython().show_usage()'), + (u'?x1', "get_ipython().run_line_magic('pinfo', 'x1')"), + (u'??x2', "get_ipython().run_line_magic('pinfo2', 'x2')"), + (u'?a.*s', "get_ipython().run_line_magic('psearch', 'a.*s')"), + (u'?%hist1', "get_ipython().run_line_magic('pinfo', '%hist1')"), + (u'?%%hist2', "get_ipython().run_line_magic('pinfo', '%%hist2')"), + (u'?abc = qwe', "get_ipython().run_line_magic('pinfo', 'abc')"), + ]], + + end_help = + [(i,py3compat.u_format(o)) for i,o in \ + [ (u'x3?', "get_ipython().run_line_magic('pinfo', 'x3')"), + (u'x4??', "get_ipython().run_line_magic('pinfo2', 'x4')"), + (u'%hist1?', "get_ipython().run_line_magic('pinfo', '%hist1')"), + (u'%hist2??', "get_ipython().run_line_magic('pinfo2', '%hist2')"), + (u'%%hist3?', "get_ipython().run_line_magic('pinfo', '%%hist3')"), + (u'%%hist4??', "get_ipython().run_line_magic('pinfo2', '%%hist4')"), + (u'π.foo?', "get_ipython().run_line_magic('pinfo', 'π.foo')"), + (u'f*?', "get_ipython().run_line_magic('psearch', 'f*')"), + (u'ax.*aspe*?', "get_ipython().run_line_magic('psearch', 'ax.*aspe*')"), + (u'a = abc?', "get_ipython().set_next_input('a = abc');" + "get_ipython().run_line_magic('pinfo', 'abc')"), + (u'a = abc.qe??', "get_ipython().set_next_input('a = abc.qe');" + "get_ipython().run_line_magic('pinfo2', 'abc.qe')"), + (u'a = *.items?', "get_ipython().set_next_input('a = *.items');" + "get_ipython().run_line_magic('psearch', '*.items')"), + (u'plot(a?', "get_ipython().set_next_input('plot(a');" + "get_ipython().run_line_magic('pinfo', 'a')"), + (u'a*2 #comment?', 'a*2 #comment?'), + ]], + + # Explicit magic calls + escaped_magic = + [(i,py3compat.u_format(o)) for i,o in \ + [ (u'%cd', "get_ipython().run_line_magic('cd', '')"), + (u'%cd /home', "get_ipython().run_line_magic('cd', '/home')"), + # Backslashes need to be escaped. + (u'%cd C:\\User', "get_ipython().run_line_magic('cd', 'C:\\\\User')"), + (u' %magic', " get_ipython().run_line_magic('magic', '')"), + ]], + + # Quoting with separate arguments + escaped_quote = + [ (',f', 'f("")'), + (',f x', 'f("x")'), + (' ,f y', ' f("y")'), + (',f a b', 'f("a", "b")'), + ], + + # Quoting with single argument + escaped_quote2 = + [ (';f', 'f("")'), + (';f x', 'f("x")'), + (' ;f y', ' f("y")'), + (';f a b', 'f("a b")'), + ], + + # Simply apply parens + escaped_paren = + [ ('/f', 'f()'), + ('/f x', 'f(x)'), + (' /f y', ' f(y)'), + ('/f a b', 'f(a, b)'), + ], + + # Check that we transform prompts before other transforms + mixed = + [(i,py3compat.u_format(o)) for i,o in \ + [ (u'In [1]: %lsmagic', "get_ipython().run_line_magic('lsmagic', '')"), + (u'>>> %lsmagic', "get_ipython().run_line_magic('lsmagic', '')"), + (u'In [2]: !ls', "get_ipython().system('ls')"), + (u'In [3]: abs?', "get_ipython().run_line_magic('pinfo', 'abs')"), + (u'In [4]: b = %who', "b = get_ipython().run_line_magic('who', '')"), + ]], + ) + +# multiline syntax examples. Each of these should be a list of lists, with +# each entry itself having pairs of raw/transformed input. The union (with +# '\n'.join() of the transformed inputs is what the splitter should produce +# when fed the raw lines one at a time via push. +syntax_ml = \ + dict(classic_prompt = + [ [('>>> for i in range(10):','for i in range(10):'), + ('... print i',' print i'), + ('... ', ''), + ], + [('>>> a="""','a="""'), + ('... 123"""','123"""'), + ], + [('a="""','a="""'), + ('... 123','123'), + ('... 456"""','456"""'), + ], + [('a="""','a="""'), + ('>>> 123','123'), + ('... 456"""','456"""'), + ], + [('a="""','a="""'), + ('123','123'), + ('... 456"""','... 456"""'), + ], + [('....__class__','....__class__'), + ], + [('a=5', 'a=5'), + ('...', ''), + ], + [('>>> def f(x):', 'def f(x):'), + ('...', ''), + ('... return x', ' return x'), + ], + [('board = """....', 'board = """....'), + ('....', '....'), + ('...."""', '...."""'), + ], + ], + + ipy_prompt = + [ [('In [24]: for i in range(10):','for i in range(10):'), + (' ....: print i',' print i'), + (' ....: ', ''), + ], + [('In [24]: for i in range(10):','for i in range(10):'), + # Qt console prompts expand with spaces, not dots + (' ...: print i',' print i'), + (' ...: ', ''), + ], + [('In [24]: for i in range(10):','for i in range(10):'), + # Sometimes whitespace preceding '...' has been removed + ('...: print i',' print i'), + ('...: ', ''), + ], + [('In [24]: for i in range(10):','for i in range(10):'), + # Space after last continuation prompt has been removed (issue #6674) + ('...: print i',' print i'), + ('...:', ''), + ], + [('In [2]: a="""','a="""'), + (' ...: 123"""','123"""'), + ], + [('a="""','a="""'), + (' ...: 123','123'), + (' ...: 456"""','456"""'), + ], + [('a="""','a="""'), + ('In [1]: 123','123'), + (' ...: 456"""','456"""'), + ], + [('a="""','a="""'), + ('123','123'), + (' ...: 456"""',' ...: 456"""'), + ], + ], + + multiline_datastructure_prompt = + [ [('>>> a = [1,','a = [1,'), + ('... 2]','2]'), + ], + ], + + multiline_datastructure = + [ [('b = ("%s"', None), + ('# comment', None), + ('%foo )', 'b = ("%s"\n# comment\n%foo )'), + ], + ], + + multiline_string = + [ [("'''foo?", None), + ("bar'''", "'''foo?\nbar'''"), + ], + ], + + leading_indent = + [ [(' print "hi"','print "hi"'), + ], + [(' for a in range(5):','for a in range(5):'), + (' a*2',' a*2'), + ], + [(' a="""','a="""'), + (' 123"""','123"""'), + ], + [('a="""','a="""'), + (' 123"""',' 123"""'), + ], + ], + + cellmagic = + [ [(u'%%foo a', None), + (None, u_fmt("get_ipython().run_cell_magic('foo', 'a', '')")), + ], + [(u'%%bar 123', None), + (u'hello', None), + (None , u_fmt("get_ipython().run_cell_magic('bar', '123', 'hello')")), + ], + [(u'a=5', 'a=5'), + (u'%%cellmagic', '%%cellmagic'), + ], + ], + + escaped = + [ [('%abc def \\', None), + ('ghi', u_fmt("get_ipython().run_line_magic('abc', 'def ghi')")), + ], + [('%abc def \\', None), + ('ghi\\', None), + (None, u_fmt("get_ipython().run_line_magic('abc', 'def ghi')")), + ], + ], + + assign_magic = + [ [(u'a = %bc de \\', None), + (u'fg', u_fmt("a = get_ipython().run_line_magic('bc', 'de fg')")), + ], + [(u'a = %bc de \\', None), + (u'fg\\', None), + (None, u_fmt("a = get_ipython().run_line_magic('bc', 'de fg')")), + ], + ], + + assign_system = + [ [(u'a = !bc de \\', None), + (u'fg', u_fmt("a = get_ipython().getoutput('bc de fg')")), + ], + [(u'a = !bc de \\', None), + (u'fg\\', None), + (None, u_fmt("a = get_ipython().getoutput('bc de fg')")), + ], + ], + ) + + +def test_assign_system(): + tt.check_pairs(transform_and_reset(ipt.assign_from_system), syntax['assign_system']) + +def test_assign_magic(): + tt.check_pairs(transform_and_reset(ipt.assign_from_magic), syntax['assign_magic']) + +def test_classic_prompt(): + tt.check_pairs(transform_and_reset(ipt.classic_prompt), syntax['classic_prompt']) + for example in syntax_ml['classic_prompt']: + transform_checker(example, ipt.classic_prompt) + for example in syntax_ml['multiline_datastructure_prompt']: + transform_checker(example, ipt.classic_prompt) + + # Check that we don't transform the second line if the first is obviously + # IPython syntax + transform_checker([ + (u'%foo', '%foo'), + (u'>>> bar', '>>> bar'), + ], ipt.classic_prompt) + + +def test_ipy_prompt(): + tt.check_pairs(transform_and_reset(ipt.ipy_prompt), syntax['ipy_prompt']) + for example in syntax_ml['ipy_prompt']: + transform_checker(example, ipt.ipy_prompt) + + # Check that we don't transform the second line if we're inside a cell magic + transform_checker([ + (u'%%foo', '%%foo'), + (u'In [1]: bar', 'In [1]: bar'), + ], ipt.ipy_prompt) + +def test_assemble_logical_lines(): + tests = \ + [ [(u"a = \\", None), + (u"123", u"a = 123"), + ], + [(u"a = \\", None), # Test resetting when within a multi-line string + (u"12 *\\", None), + (None, u"a = 12 *"), + ], + [(u"# foo\\", u"# foo\\"), # Comments can't be continued like this + ], + ] + for example in tests: + transform_checker(example, ipt.assemble_logical_lines) + +def test_assemble_python_lines(): + tests = \ + [ [(u"a = '''", None), + (u"abc'''", u"a = '''\nabc'''"), + ], + [(u"a = '''", None), # Test resetting when within a multi-line string + (u"def", None), + (None, u"a = '''\ndef"), + ], + [(u"a = [1,", None), + (u"2]", u"a = [1,\n2]"), + ], + [(u"a = [1,", None), # Test resetting when within a multi-line string + (u"2,", None), + (None, u"a = [1,\n2,"), + ], + [(u"a = '''", None), # Test line continuation within a multi-line string + (u"abc\\", None), + (u"def", None), + (u"'''", u"a = '''\nabc\\\ndef\n'''"), + ], + ] + syntax_ml['multiline_datastructure'] + for example in tests: + transform_checker(example, ipt.assemble_python_lines) + + +def test_help_end(): + tt.check_pairs(transform_and_reset(ipt.help_end), syntax['end_help']) + +def test_escaped_noesc(): + tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_noesc']) + + +def test_escaped_shell(): + tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_shell']) + + +def test_escaped_help(): + tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_help']) + + +def test_escaped_magic(): + tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_magic']) + + +def test_escaped_quote(): + tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_quote']) + + +def test_escaped_quote2(): + tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_quote2']) + + +def test_escaped_paren(): + tt.check_pairs(transform_and_reset(ipt.escaped_commands), syntax['escaped_paren']) + + +def test_cellmagic(): + for example in syntax_ml['cellmagic']: + transform_checker(example, ipt.cellmagic) + + line_example = [(u'%%bar 123', None), + (u'hello', None), + (u'' , u_fmt("get_ipython().run_cell_magic('bar', '123', 'hello')")), + ] + transform_checker(line_example, ipt.cellmagic, end_on_blank_line=True) + +def test_has_comment(): + tests = [('text', False), + ('text #comment', True), + ('text #comment\n', True), + ('#comment', True), + ('#comment\n', True), + ('a = "#string"', False), + ('a = "#string" # comment', True), + ('a #comment not "string"', True), + ] + tt.check_pairs(ipt.has_comment, tests) + +@ipt.TokenInputTransformer.wrap +def decistmt(tokens): + """Substitute Decimals for floats in a string of statements. + + Based on an example from the tokenize module docs. + """ + result = [] + for toknum, tokval, _, _, _ in tokens: + if toknum == tokenize.NUMBER and '.' in tokval: # replace NUMBER tokens + for newtok in [ + (tokenize.NAME, 'Decimal'), + (tokenize.OP, '('), + (tokenize.STRING, repr(tokval)), + (tokenize.OP, ')') + ]: + yield newtok + else: + yield (toknum, tokval) + + + +def test_token_input_transformer(): + tests = [(u'1.2', u_fmt(u"Decimal ('1.2')")), + (u'"1.2"', u'"1.2"'), + ] + tt.check_pairs(transform_and_reset(decistmt), tests) + ml_tests = \ + [ [(u"a = 1.2; b = '''x", None), + (u"y'''", u_fmt(u"a =Decimal ('1.2');b ='''x\ny'''")), + ], + [(u"a = [1.2,", None), + (u"3]", u_fmt(u"a =[Decimal ('1.2'),\n3 ]")), + ], + [(u"a = '''foo", None), # Test resetting when within a multi-line string + (u"bar", None), + (None, u"a = '''foo\nbar"), + ], + ] + for example in ml_tests: + transform_checker(example, decistmt) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer2.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer2.py new file mode 100644 index 00000000..61f183cb --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer2.py @@ -0,0 +1,337 @@ +"""Tests for the token-based transformers in IPython.core.inputtransformer2 + +Line-based transformers are the simpler ones; token-based transformers are +more complex. See test_inputtransformer2_line for tests for line-based +transformations. +""" +import nose.tools as nt +import string + +from IPython.core import inputtransformer2 as ipt2 +from IPython.core.inputtransformer2 import make_tokens_by_line, _find_assign_op + +from textwrap import dedent + +MULTILINE_MAGIC = ("""\ +a = f() +%foo \\ +bar +g() +""".splitlines(keepends=True), (2, 0), """\ +a = f() +get_ipython().run_line_magic('foo', ' bar') +g() +""".splitlines(keepends=True)) + +INDENTED_MAGIC = ("""\ +for a in range(5): + %ls +""".splitlines(keepends=True), (2, 4), """\ +for a in range(5): + get_ipython().run_line_magic('ls', '') +""".splitlines(keepends=True)) + +CRLF_MAGIC = ([ + "a = f()\n", + "%ls\r\n", + "g()\n" +], (2, 0), [ + "a = f()\n", + "get_ipython().run_line_magic('ls', '')\n", + "g()\n" +]) + +MULTILINE_MAGIC_ASSIGN = ("""\ +a = f() +b = %foo \\ + bar +g() +""".splitlines(keepends=True), (2, 4), """\ +a = f() +b = get_ipython().run_line_magic('foo', ' bar') +g() +""".splitlines(keepends=True)) + +MULTILINE_SYSTEM_ASSIGN = ("""\ +a = f() +b = !foo \\ + bar +g() +""".splitlines(keepends=True), (2, 4), """\ +a = f() +b = get_ipython().getoutput('foo bar') +g() +""".splitlines(keepends=True)) + +##### + +MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT = ("""\ +def test(): + for i in range(1): + print(i) + res =! ls +""".splitlines(keepends=True), (4, 7), '''\ +def test(): + for i in range(1): + print(i) + res =get_ipython().getoutput(\' ls\') +'''.splitlines(keepends=True)) + +###### + +AUTOCALL_QUOTE = ( + [",f 1 2 3\n"], (1, 0), + ['f("1", "2", "3")\n'] +) + +AUTOCALL_QUOTE2 = ( + [";f 1 2 3\n"], (1, 0), + ['f("1 2 3")\n'] +) + +AUTOCALL_PAREN = ( + ["/f 1 2 3\n"], (1, 0), + ['f(1, 2, 3)\n'] +) + +SIMPLE_HELP = ( + ["foo?\n"], (1, 0), + ["get_ipython().run_line_magic('pinfo', 'foo')\n"] +) + +DETAILED_HELP = ( + ["foo??\n"], (1, 0), + ["get_ipython().run_line_magic('pinfo2', 'foo')\n"] +) + +MAGIC_HELP = ( + ["%foo?\n"], (1, 0), + ["get_ipython().run_line_magic('pinfo', '%foo')\n"] +) + +HELP_IN_EXPR = ( + ["a = b + c?\n"], (1, 0), + ["get_ipython().set_next_input('a = b + c');" + "get_ipython().run_line_magic('pinfo', 'c')\n"] +) + +HELP_CONTINUED_LINE = ("""\ +a = \\ +zip? +""".splitlines(keepends=True), (1, 0), +[r"get_ipython().set_next_input('a = \\\nzip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"] +) + +HELP_MULTILINE = ("""\ +(a, +b) = zip? +""".splitlines(keepends=True), (1, 0), +[r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"] +) + +HELP_UNICODE = ( + ["π.foo?\n"], (1, 0), + ["get_ipython().run_line_magic('pinfo', 'π.foo')\n"] +) + + +def null_cleanup_transformer(lines): + """ + A cleanup transform that returns an empty list. + """ + return [] + +def check_make_token_by_line_never_ends_empty(): + """ + Check that not sequence of single or double characters ends up leading to en empty list of tokens + """ + from string import printable + for c in printable: + nt.assert_not_equal(make_tokens_by_line(c)[-1], []) + for k in printable: + nt.assert_not_equal(make_tokens_by_line(c+k)[-1], []) + +def check_find(transformer, case, match=True): + sample, expected_start, _ = case + tbl = make_tokens_by_line(sample) + res = transformer.find(tbl) + if match: + # start_line is stored 0-indexed, expected values are 1-indexed + nt.assert_equal((res.start_line+1, res.start_col), expected_start) + return res + else: + nt.assert_is(res, None) + +def check_transform(transformer_cls, case): + lines, start, expected = case + transformer = transformer_cls(start) + nt.assert_equal(transformer.transform(lines), expected) + +def test_continued_line(): + lines = MULTILINE_MAGIC_ASSIGN[0] + nt.assert_equal(ipt2.find_end_of_continued_line(lines, 1), 2) + + nt.assert_equal(ipt2.assemble_continued_line(lines, (1, 5), 2), "foo bar") + +def test_find_assign_magic(): + check_find(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN) + check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN, match=False) + check_find(ipt2.MagicAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT, match=False) + +def test_transform_assign_magic(): + check_transform(ipt2.MagicAssign, MULTILINE_MAGIC_ASSIGN) + +def test_find_assign_system(): + check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN) + check_find(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT) + check_find(ipt2.SystemAssign, (["a = !ls\n"], (1, 5), None)) + check_find(ipt2.SystemAssign, (["a=!ls\n"], (1, 2), None)) + check_find(ipt2.SystemAssign, MULTILINE_MAGIC_ASSIGN, match=False) + +def test_transform_assign_system(): + check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN) + check_transform(ipt2.SystemAssign, MULTILINE_SYSTEM_ASSIGN_AFTER_DEDENT) + +def test_find_magic_escape(): + check_find(ipt2.EscapedCommand, MULTILINE_MAGIC) + check_find(ipt2.EscapedCommand, INDENTED_MAGIC) + check_find(ipt2.EscapedCommand, MULTILINE_MAGIC_ASSIGN, match=False) + +def test_transform_magic_escape(): + check_transform(ipt2.EscapedCommand, MULTILINE_MAGIC) + check_transform(ipt2.EscapedCommand, INDENTED_MAGIC) + check_transform(ipt2.EscapedCommand, CRLF_MAGIC) + +def test_find_autocalls(): + for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]: + print("Testing %r" % case[0]) + check_find(ipt2.EscapedCommand, case) + +def test_transform_autocall(): + for case in [AUTOCALL_QUOTE, AUTOCALL_QUOTE2, AUTOCALL_PAREN]: + print("Testing %r" % case[0]) + check_transform(ipt2.EscapedCommand, case) + +def test_find_help(): + for case in [SIMPLE_HELP, DETAILED_HELP, MAGIC_HELP, HELP_IN_EXPR]: + check_find(ipt2.HelpEnd, case) + + tf = check_find(ipt2.HelpEnd, HELP_CONTINUED_LINE) + nt.assert_equal(tf.q_line, 1) + nt.assert_equal(tf.q_col, 3) + + tf = check_find(ipt2.HelpEnd, HELP_MULTILINE) + nt.assert_equal(tf.q_line, 1) + nt.assert_equal(tf.q_col, 8) + + # ? in a comment does not trigger help + check_find(ipt2.HelpEnd, (["foo # bar?\n"], None, None), match=False) + # Nor in a string + check_find(ipt2.HelpEnd, (["foo = '''bar?\n"], None, None), match=False) + +def test_transform_help(): + tf = ipt2.HelpEnd((1, 0), (1, 9)) + nt.assert_equal(tf.transform(HELP_IN_EXPR[0]), HELP_IN_EXPR[2]) + + tf = ipt2.HelpEnd((1, 0), (2, 3)) + nt.assert_equal(tf.transform(HELP_CONTINUED_LINE[0]), HELP_CONTINUED_LINE[2]) + + tf = ipt2.HelpEnd((1, 0), (2, 8)) + nt.assert_equal(tf.transform(HELP_MULTILINE[0]), HELP_MULTILINE[2]) + + tf = ipt2.HelpEnd((1, 0), (1, 0)) + nt.assert_equal(tf.transform(HELP_UNICODE[0]), HELP_UNICODE[2]) + +def test_find_assign_op_dedent(): + """ + be careful that empty token like dedent are not counted as parens + """ + class Tk: + def __init__(self, s): + self.string = s + + nt.assert_equal(_find_assign_op([Tk(s) for s in ('','a','=','b')]), 2) + nt.assert_equal(_find_assign_op([Tk(s) for s in ('','(', 'a','=','b', ')', '=' ,'5')]), 6) + +def test_check_complete(): + cc = ipt2.TransformerManager().check_complete + nt.assert_equal(cc("a = 1"), ('complete', None)) + nt.assert_equal(cc("for a in range(5):"), ('incomplete', 4)) + nt.assert_equal(cc("for a in range(5):\n if a > 0:"), ('incomplete', 8)) + nt.assert_equal(cc("raise = 2"), ('invalid', None)) + nt.assert_equal(cc("a = [1,\n2,"), ('incomplete', 0)) + nt.assert_equal(cc(")"), ('incomplete', 0)) + nt.assert_equal(cc("\\\r\n"), ('incomplete', 0)) + nt.assert_equal(cc("a = '''\n hi"), ('incomplete', 3)) + nt.assert_equal(cc("def a():\n x=1\n global x"), ('invalid', None)) + nt.assert_equal(cc("a \\ "), ('invalid', None)) # Nothing allowed after backslash + nt.assert_equal(cc("1\\\n+2"), ('complete', None)) + nt.assert_equal(cc("exit"), ('complete', None)) + + example = dedent(""" + if True: + a=1""" ) + + nt.assert_equal(cc(example), ('incomplete', 4)) + nt.assert_equal(cc(example+'\n'), ('complete', None)) + nt.assert_equal(cc(example+'\n '), ('complete', None)) + + # no need to loop on all the letters/numbers. + short = '12abAB'+string.printable[62:] + for c in short: + # test does not raise: + cc(c) + for k in short: + cc(c+k) + + nt.assert_equal(cc("def f():\n x=0\n \\\n "), ('incomplete', 2)) + +def test_check_complete_II(): + """ + Test that multiple line strings are properly handled. + + Separate test function for convenience + + """ + cc = ipt2.TransformerManager().check_complete + nt.assert_equal(cc('''def foo():\n """'''), ('incomplete', 4)) + + +def test_null_cleanup_transformer(): + manager = ipt2.TransformerManager() + manager.cleanup_transforms.insert(0, null_cleanup_transformer) + assert manager.transform_cell("") == "" + + + + +def test_side_effects_I(): + count = 0 + def counter(lines): + nonlocal count + count += 1 + return lines + + counter.has_side_effects = True + + manager = ipt2.TransformerManager() + manager.cleanup_transforms.insert(0, counter) + assert manager.check_complete("a=1\n") == ('complete', None) + assert count == 0 + + + + +def test_side_effects_II(): + count = 0 + def counter(lines): + nonlocal count + count += 1 + return lines + + counter.has_side_effects = True + + manager = ipt2.TransformerManager() + manager.line_transforms.insert(0, counter) + assert manager.check_complete("b=1\n") == ('complete', None) + assert count == 0 diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer2_line.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer2_line.py new file mode 100644 index 00000000..263bbd9e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_inputtransformer2_line.py @@ -0,0 +1,126 @@ +"""Tests for the line-based transformers in IPython.core.inputtransformer2 + +Line-based transformers are the simpler ones; token-based transformers are +more complex. See test_inputtransformer2 for tests for token-based transformers. +""" +import nose.tools as nt + +from IPython.core import inputtransformer2 as ipt2 + +CELL_MAGIC = ("""\ +%%foo arg +body 1 +body 2 +""", """\ +get_ipython().run_cell_magic('foo', 'arg', 'body 1\\nbody 2\\n') +""") + +def test_cell_magic(): + for sample, expected in [CELL_MAGIC]: + nt.assert_equal(ipt2.cell_magic(sample.splitlines(keepends=True)), + expected.splitlines(keepends=True)) + +CLASSIC_PROMPT = ("""\ +>>> for a in range(5): +... print(a) +""", """\ +for a in range(5): + print(a) +""") + +CLASSIC_PROMPT_L2 = ("""\ +for a in range(5): +... print(a) +... print(a ** 2) +""", """\ +for a in range(5): + print(a) + print(a ** 2) +""") + +def test_classic_prompt(): + for sample, expected in [CLASSIC_PROMPT, CLASSIC_PROMPT_L2]: + nt.assert_equal(ipt2.classic_prompt(sample.splitlines(keepends=True)), + expected.splitlines(keepends=True)) + +IPYTHON_PROMPT = ("""\ +In [1]: for a in range(5): + ...: print(a) +""", """\ +for a in range(5): + print(a) +""") + +IPYTHON_PROMPT_L2 = ("""\ +for a in range(5): + ...: print(a) + ...: print(a ** 2) +""", """\ +for a in range(5): + print(a) + print(a ** 2) +""") + +def test_ipython_prompt(): + for sample, expected in [IPYTHON_PROMPT, IPYTHON_PROMPT_L2]: + nt.assert_equal(ipt2.ipython_prompt(sample.splitlines(keepends=True)), + expected.splitlines(keepends=True)) + +INDENT_SPACES = ("""\ + if True: + a = 3 +""", """\ +if True: + a = 3 +""") + +INDENT_TABS = ("""\ +\tif True: +\t\tb = 4 +""", """\ +if True: +\tb = 4 +""") + +def test_leading_indent(): + for sample, expected in [INDENT_SPACES, INDENT_TABS]: + nt.assert_equal(ipt2.leading_indent(sample.splitlines(keepends=True)), + expected.splitlines(keepends=True)) + +LEADING_EMPTY_LINES = ("""\ + \t + +if True: + a = 3 + +b = 4 +""", """\ +if True: + a = 3 + +b = 4 +""") + +ONLY_EMPTY_LINES = ("""\ + \t + +""", """\ + \t + +""") + +def test_leading_empty_lines(): + for sample, expected in [LEADING_EMPTY_LINES, ONLY_EMPTY_LINES]: + nt.assert_equal( + ipt2.leading_empty_lines(sample.splitlines(keepends=True)), + expected.splitlines(keepends=True)) + +CRLF_MAGIC = ([ + "%%ls\r\n" +], [ + "get_ipython().run_cell_magic('ls', '', '')\n" +]) + +def test_crlf_magic(): + for sample, expected in [CRLF_MAGIC]: + nt.assert_equal(ipt2.cell_magic(sample), expected) \ No newline at end of file diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_interactiveshell.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_interactiveshell.py new file mode 100644 index 00000000..951b843e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_interactiveshell.py @@ -0,0 +1,1042 @@ +# -*- coding: utf-8 -*- +"""Tests for the key interactiveshell module. + +Historically the main classes in interactiveshell have been under-tested. This +module should grow as many single-method tests as possible to trap many of the +recurring bugs we seem to encounter with high-level interaction. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import asyncio +import ast +import os +import signal +import shutil +import sys +import tempfile +import unittest +from unittest import mock + +from os.path import join + +import nose.tools as nt + +from IPython.core.error import InputRejected +from IPython.core.inputtransformer import InputTransformer +from IPython.core import interactiveshell +from IPython.testing.decorators import ( + skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist, +) +from IPython.testing import tools as tt +from IPython.utils.process import find_cmd + +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- +# This is used by every single test, no point repeating it ad nauseam + +#----------------------------------------------------------------------------- +# Tests +#----------------------------------------------------------------------------- + +class DerivedInterrupt(KeyboardInterrupt): + pass + +class InteractiveShellTestCase(unittest.TestCase): + def test_naked_string_cells(self): + """Test that cells with only naked strings are fully executed""" + # First, single-line inputs + ip.run_cell('"a"\n') + self.assertEqual(ip.user_ns['_'], 'a') + # And also multi-line cells + ip.run_cell('"""a\nb"""\n') + self.assertEqual(ip.user_ns['_'], 'a\nb') + + def test_run_empty_cell(self): + """Just make sure we don't get a horrible error with a blank + cell of input. Yes, I did overlook that.""" + old_xc = ip.execution_count + res = ip.run_cell('') + self.assertEqual(ip.execution_count, old_xc) + self.assertEqual(res.execution_count, None) + + def test_run_cell_multiline(self): + """Multi-block, multi-line cells must execute correctly. + """ + src = '\n'.join(["x=1", + "y=2", + "if 1:", + " x += 1", + " y += 1",]) + res = ip.run_cell(src) + self.assertEqual(ip.user_ns['x'], 2) + self.assertEqual(ip.user_ns['y'], 3) + self.assertEqual(res.success, True) + self.assertEqual(res.result, None) + + def test_multiline_string_cells(self): + "Code sprinkled with multiline strings should execute (GH-306)" + ip.run_cell('tmp=0') + self.assertEqual(ip.user_ns['tmp'], 0) + res = ip.run_cell('tmp=1;"""a\nb"""\n') + self.assertEqual(ip.user_ns['tmp'], 1) + self.assertEqual(res.success, True) + self.assertEqual(res.result, "a\nb") + + def test_dont_cache_with_semicolon(self): + "Ending a line with semicolon should not cache the returned object (GH-307)" + oldlen = len(ip.user_ns['Out']) + for cell in ['1;', '1;1;']: + res = ip.run_cell(cell, store_history=True) + newlen = len(ip.user_ns['Out']) + self.assertEqual(oldlen, newlen) + self.assertIsNone(res.result) + i = 0 + #also test the default caching behavior + for cell in ['1', '1;1']: + ip.run_cell(cell, store_history=True) + newlen = len(ip.user_ns['Out']) + i += 1 + self.assertEqual(oldlen+i, newlen) + + def test_syntax_error(self): + res = ip.run_cell("raise = 3") + self.assertIsInstance(res.error_before_exec, SyntaxError) + + def test_In_variable(self): + "Verify that In variable grows with user input (GH-284)" + oldlen = len(ip.user_ns['In']) + ip.run_cell('1;', store_history=True) + newlen = len(ip.user_ns['In']) + self.assertEqual(oldlen+1, newlen) + self.assertEqual(ip.user_ns['In'][-1],'1;') + + def test_magic_names_in_string(self): + ip.run_cell('a = """\n%exit\n"""') + self.assertEqual(ip.user_ns['a'], '\n%exit\n') + + def test_trailing_newline(self): + """test that running !(command) does not raise a SyntaxError""" + ip.run_cell('!(true)\n', False) + ip.run_cell('!(true)\n\n\n', False) + + def test_gh_597(self): + """Pretty-printing lists of objects with non-ascii reprs may cause + problems.""" + class Spam(object): + def __repr__(self): + return "\xe9"*50 + import IPython.core.formatters + f = IPython.core.formatters.PlainTextFormatter() + f([Spam(),Spam()]) + + + def test_future_flags(self): + """Check that future flags are used for parsing code (gh-777)""" + ip.run_cell('from __future__ import barry_as_FLUFL') + try: + ip.run_cell('prfunc_return_val = 1 <> 2') + assert 'prfunc_return_val' in ip.user_ns + finally: + # Reset compiler flags so we don't mess up other tests. + ip.compile.reset_compiler_flags() + + def test_can_pickle(self): + "Can we pickle objects defined interactively (GH-29)" + ip = get_ipython() + ip.reset() + ip.run_cell(("class Mylist(list):\n" + " def __init__(self,x=[]):\n" + " list.__init__(self,x)")) + ip.run_cell("w=Mylist([1,2,3])") + + from pickle import dumps + + # We need to swap in our main module - this is only necessary + # inside the test framework, because IPython puts the interactive module + # in place (but the test framework undoes this). + _main = sys.modules['__main__'] + sys.modules['__main__'] = ip.user_module + try: + res = dumps(ip.user_ns["w"]) + finally: + sys.modules['__main__'] = _main + self.assertTrue(isinstance(res, bytes)) + + def test_global_ns(self): + "Code in functions must be able to access variables outside them." + ip = get_ipython() + ip.run_cell("a = 10") + ip.run_cell(("def f(x):\n" + " return x + a")) + ip.run_cell("b = f(12)") + self.assertEqual(ip.user_ns["b"], 22) + + def test_bad_custom_tb(self): + """Check that InteractiveShell is protected from bad custom exception handlers""" + ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0) + self.assertEqual(ip.custom_exceptions, (IOError,)) + with tt.AssertPrints("Custom TB Handler failed", channel='stderr'): + ip.run_cell(u'raise IOError("foo")') + self.assertEqual(ip.custom_exceptions, ()) + + def test_bad_custom_tb_return(self): + """Check that InteractiveShell is protected from bad return types in custom exception handlers""" + ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1) + self.assertEqual(ip.custom_exceptions, (NameError,)) + with tt.AssertPrints("Custom TB Handler failed", channel='stderr'): + ip.run_cell(u'a=abracadabra') + self.assertEqual(ip.custom_exceptions, ()) + + def test_drop_by_id(self): + myvars = {"a":object(), "b":object(), "c": object()} + ip.push(myvars, interactive=False) + for name in myvars: + assert name in ip.user_ns, name + assert name in ip.user_ns_hidden, name + ip.user_ns['b'] = 12 + ip.drop_by_id(myvars) + for name in ["a", "c"]: + assert name not in ip.user_ns, name + assert name not in ip.user_ns_hidden, name + assert ip.user_ns['b'] == 12 + ip.reset() + + def test_var_expand(self): + ip.user_ns['f'] = u'Ca\xf1o' + self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o') + self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o') + self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1') + self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2') + + self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'") + + ip.user_ns['f'] = b'Ca\xc3\xb1o' + # This should not raise any exception: + ip.var_expand(u'echo $f') + + def test_var_expand_local(self): + """Test local variable expansion in !system and %magic calls""" + # !system + ip.run_cell('def test():\n' + ' lvar = "ttt"\n' + ' ret = !echo {lvar}\n' + ' return ret[0]\n') + res = ip.user_ns['test']() + nt.assert_in('ttt', res) + + # %magic + ip.run_cell('def makemacro():\n' + ' macroname = "macro_var_expand_locals"\n' + ' %macro {macroname} codestr\n') + ip.user_ns['codestr'] = "str(12)" + ip.run_cell('makemacro()') + nt.assert_in('macro_var_expand_locals', ip.user_ns) + + def test_var_expand_self(self): + """Test variable expansion with the name 'self', which was failing. + + See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218 + """ + ip.run_cell('class cTest:\n' + ' classvar="see me"\n' + ' def test(self):\n' + ' res = !echo Variable: {self.classvar}\n' + ' return res[0]\n') + nt.assert_in('see me', ip.user_ns['cTest']().test()) + + def test_bad_var_expand(self): + """var_expand on invalid formats shouldn't raise""" + # SyntaxError + self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}") + # NameError + self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}") + # ZeroDivisionError + self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}") + + def test_silent_postexec(self): + """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks""" + pre_explicit = mock.Mock() + pre_always = mock.Mock() + post_explicit = mock.Mock() + post_always = mock.Mock() + all_mocks = [pre_explicit, pre_always, post_explicit, post_always] + + ip.events.register('pre_run_cell', pre_explicit) + ip.events.register('pre_execute', pre_always) + ip.events.register('post_run_cell', post_explicit) + ip.events.register('post_execute', post_always) + + try: + ip.run_cell("1", silent=True) + assert pre_always.called + assert not pre_explicit.called + assert post_always.called + assert not post_explicit.called + # double-check that non-silent exec did what we expected + # silent to avoid + ip.run_cell("1") + assert pre_explicit.called + assert post_explicit.called + info, = pre_explicit.call_args[0] + result, = post_explicit.call_args[0] + self.assertEqual(info, result.info) + # check that post hooks are always called + [m.reset_mock() for m in all_mocks] + ip.run_cell("syntax error") + assert pre_always.called + assert pre_explicit.called + assert post_always.called + assert post_explicit.called + info, = pre_explicit.call_args[0] + result, = post_explicit.call_args[0] + self.assertEqual(info, result.info) + finally: + # remove post-exec + ip.events.unregister('pre_run_cell', pre_explicit) + ip.events.unregister('pre_execute', pre_always) + ip.events.unregister('post_run_cell', post_explicit) + ip.events.unregister('post_execute', post_always) + + def test_silent_noadvance(self): + """run_cell(silent=True) doesn't advance execution_count""" + ec = ip.execution_count + # silent should force store_history=False + ip.run_cell("1", store_history=True, silent=True) + + self.assertEqual(ec, ip.execution_count) + # double-check that non-silent exec did what we expected + # silent to avoid + ip.run_cell("1", store_history=True) + self.assertEqual(ec+1, ip.execution_count) + + def test_silent_nodisplayhook(self): + """run_cell(silent=True) doesn't trigger displayhook""" + d = dict(called=False) + + trap = ip.display_trap + save_hook = trap.hook + + def failing_hook(*args, **kwargs): + d['called'] = True + + try: + trap.hook = failing_hook + res = ip.run_cell("1", silent=True) + self.assertFalse(d['called']) + self.assertIsNone(res.result) + # double-check that non-silent exec did what we expected + # silent to avoid + ip.run_cell("1") + self.assertTrue(d['called']) + finally: + trap.hook = save_hook + + def test_ofind_line_magic(self): + from IPython.core.magic import register_line_magic + + @register_line_magic + def lmagic(line): + "A line magic" + + # Get info on line magic + lfind = ip._ofind('lmagic') + info = dict(found=True, isalias=False, ismagic=True, + namespace = 'IPython internal', obj= lmagic.__wrapped__, + parent = None) + nt.assert_equal(lfind, info) + + def test_ofind_cell_magic(self): + from IPython.core.magic import register_cell_magic + + @register_cell_magic + def cmagic(line, cell): + "A cell magic" + + # Get info on cell magic + find = ip._ofind('cmagic') + info = dict(found=True, isalias=False, ismagic=True, + namespace = 'IPython internal', obj= cmagic.__wrapped__, + parent = None) + nt.assert_equal(find, info) + + def test_ofind_property_with_error(self): + class A(object): + @property + def foo(self): + raise NotImplementedError() + a = A() + + found = ip._ofind('a.foo', [('locals', locals())]) + info = dict(found=True, isalias=False, ismagic=False, + namespace='locals', obj=A.foo, parent=a) + nt.assert_equal(found, info) + + def test_ofind_multiple_attribute_lookups(self): + class A(object): + @property + def foo(self): + raise NotImplementedError() + + a = A() + a.a = A() + a.a.a = A() + + found = ip._ofind('a.a.a.foo', [('locals', locals())]) + info = dict(found=True, isalias=False, ismagic=False, + namespace='locals', obj=A.foo, parent=a.a.a) + nt.assert_equal(found, info) + + def test_ofind_slotted_attributes(self): + class A(object): + __slots__ = ['foo'] + def __init__(self): + self.foo = 'bar' + + a = A() + found = ip._ofind('a.foo', [('locals', locals())]) + info = dict(found=True, isalias=False, ismagic=False, + namespace='locals', obj=a.foo, parent=a) + nt.assert_equal(found, info) + + found = ip._ofind('a.bar', [('locals', locals())]) + info = dict(found=False, isalias=False, ismagic=False, + namespace=None, obj=None, parent=a) + nt.assert_equal(found, info) + + def test_ofind_prefers_property_to_instance_level_attribute(self): + class A(object): + @property + def foo(self): + return 'bar' + a = A() + a.__dict__['foo'] = 'baz' + nt.assert_equal(a.foo, 'bar') + found = ip._ofind('a.foo', [('locals', locals())]) + nt.assert_is(found['obj'], A.foo) + + def test_custom_syntaxerror_exception(self): + called = [] + def my_handler(shell, etype, value, tb, tb_offset=None): + called.append(etype) + shell.showtraceback((etype, value, tb), tb_offset=tb_offset) + + ip.set_custom_exc((SyntaxError,), my_handler) + try: + ip.run_cell("1f") + # Check that this was called, and only once. + self.assertEqual(called, [SyntaxError]) + finally: + # Reset the custom exception hook + ip.set_custom_exc((), None) + + def test_custom_exception(self): + called = [] + def my_handler(shell, etype, value, tb, tb_offset=None): + called.append(etype) + shell.showtraceback((etype, value, tb), tb_offset=tb_offset) + + ip.set_custom_exc((ValueError,), my_handler) + try: + res = ip.run_cell("raise ValueError('test')") + # Check that this was called, and only once. + self.assertEqual(called, [ValueError]) + # Check that the error is on the result object + self.assertIsInstance(res.error_in_exec, ValueError) + finally: + # Reset the custom exception hook + ip.set_custom_exc((), None) + + def test_mktempfile(self): + filename = ip.mktempfile() + # Check that we can open the file again on Windows + with open(filename, 'w') as f: + f.write('abc') + + filename = ip.mktempfile(data='blah') + with open(filename, 'r') as f: + self.assertEqual(f.read(), 'blah') + + def test_new_main_mod(self): + # Smoketest to check that this accepts a unicode module name + name = u'jiefmw' + mod = ip.new_main_mod(u'%s.py' % name, name) + self.assertEqual(mod.__name__, name) + + def test_get_exception_only(self): + try: + raise KeyboardInterrupt + except KeyboardInterrupt: + msg = ip.get_exception_only() + self.assertEqual(msg, 'KeyboardInterrupt\n') + + try: + raise DerivedInterrupt("foo") + except KeyboardInterrupt: + msg = ip.get_exception_only() + self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n') + + def test_inspect_text(self): + ip.run_cell('a = 5') + text = ip.object_inspect_text('a') + self.assertIsInstance(text, str) + + def test_last_execution_result(self): + """ Check that last execution result gets set correctly (GH-10702) """ + result = ip.run_cell('a = 5; a') + self.assertTrue(ip.last_execution_succeeded) + self.assertEqual(ip.last_execution_result.result, 5) + + result = ip.run_cell('a = x_invalid_id_x') + self.assertFalse(ip.last_execution_succeeded) + self.assertFalse(ip.last_execution_result.success) + self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError) + + def test_reset_aliasing(self): + """ Check that standard posix aliases work after %reset. """ + if os.name != 'posix': + return + + ip.reset() + for cmd in ('clear', 'more', 'less', 'man'): + res = ip.run_cell('%' + cmd) + self.assertEqual(res.success, True) + + +class TestSafeExecfileNonAsciiPath(unittest.TestCase): + + @onlyif_unicode_paths + def setUp(self): + self.BASETESTDIR = tempfile.mkdtemp() + self.TESTDIR = join(self.BASETESTDIR, u"åäö") + os.mkdir(self.TESTDIR) + with open(join(self.TESTDIR, u"åäötestscript.py"), "w") as sfile: + sfile.write("pass\n") + self.oldpath = os.getcwd() + os.chdir(self.TESTDIR) + self.fname = u"åäötestscript.py" + + def tearDown(self): + os.chdir(self.oldpath) + shutil.rmtree(self.BASETESTDIR) + + @onlyif_unicode_paths + def test_1(self): + """Test safe_execfile with non-ascii path + """ + ip.safe_execfile(self.fname, {}, raise_exceptions=True) + +class ExitCodeChecks(tt.TempFileMixin): + + def setUp(self): + self.system = ip.system_raw + + def test_exit_code_ok(self): + self.system('exit 0') + self.assertEqual(ip.user_ns['_exit_code'], 0) + + def test_exit_code_error(self): + self.system('exit 1') + self.assertEqual(ip.user_ns['_exit_code'], 1) + + @skipif(not hasattr(signal, 'SIGALRM')) + def test_exit_code_signal(self): + self.mktmp("import signal, time\n" + "signal.setitimer(signal.ITIMER_REAL, 0.1)\n" + "time.sleep(1)\n") + self.system("%s %s" % (sys.executable, self.fname)) + self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM) + + @onlyif_cmds_exist("csh") + def test_exit_code_signal_csh(self): + SHELL = os.environ.get('SHELL', None) + os.environ['SHELL'] = find_cmd("csh") + try: + self.test_exit_code_signal() + finally: + if SHELL is not None: + os.environ['SHELL'] = SHELL + else: + del os.environ['SHELL'] + + +class TestSystemRaw(ExitCodeChecks): + + def setUp(self): + super().setUp() + self.system = ip.system_raw + + @onlyif_unicode_paths + def test_1(self): + """Test system_raw with non-ascii cmd + """ + cmd = u'''python -c "'åäö'" ''' + ip.system_raw(cmd) + + @mock.patch('subprocess.call', side_effect=KeyboardInterrupt) + @mock.patch('os.system', side_effect=KeyboardInterrupt) + def test_control_c(self, *mocks): + try: + self.system("sleep 1 # wont happen") + except KeyboardInterrupt: + self.fail("system call should intercept " + "keyboard interrupt from subprocess.call") + self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGINT) + +# TODO: Exit codes are currently ignored on Windows. +class TestSystemPipedExitCode(ExitCodeChecks): + + def setUp(self): + super().setUp() + self.system = ip.system_piped + + @skip_win32 + def test_exit_code_ok(self): + ExitCodeChecks.test_exit_code_ok(self) + + @skip_win32 + def test_exit_code_error(self): + ExitCodeChecks.test_exit_code_error(self) + + @skip_win32 + def test_exit_code_signal(self): + ExitCodeChecks.test_exit_code_signal(self) + +class TestModules(tt.TempFileMixin): + def test_extraneous_loads(self): + """Test we're not loading modules on startup that we shouldn't. + """ + self.mktmp("import sys\n" + "print('numpy' in sys.modules)\n" + "print('ipyparallel' in sys.modules)\n" + "print('ipykernel' in sys.modules)\n" + ) + out = "False\nFalse\nFalse\n" + tt.ipexec_validate(self.fname, out) + +class Negator(ast.NodeTransformer): + """Negates all number literals in an AST.""" + + # for python 3.7 and earlier + def visit_Num(self, node): + node.n = -node.n + return node + + # for python 3.8+ + def visit_Constant(self, node): + if isinstance(node.value, int): + return self.visit_Num(node) + return node + +class TestAstTransform(unittest.TestCase): + def setUp(self): + self.negator = Negator() + ip.ast_transformers.append(self.negator) + + def tearDown(self): + ip.ast_transformers.remove(self.negator) + + def test_run_cell(self): + with tt.AssertPrints('-34'): + ip.run_cell('print (12 + 22)') + + # A named reference to a number shouldn't be transformed. + ip.user_ns['n'] = 55 + with tt.AssertNotPrints('-55'): + ip.run_cell('print (n)') + + def test_timeit(self): + called = set() + def f(x): + called.add(x) + ip.push({'f':f}) + + with tt.AssertPrints("std. dev. of"): + ip.run_line_magic("timeit", "-n1 f(1)") + self.assertEqual(called, {-1}) + called.clear() + + with tt.AssertPrints("std. dev. of"): + ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)") + self.assertEqual(called, {-2, -3}) + + def test_time(self): + called = [] + def f(x): + called.append(x) + ip.push({'f':f}) + + # Test with an expression + with tt.AssertPrints("Wall time: "): + ip.run_line_magic("time", "f(5+9)") + self.assertEqual(called, [-14]) + called[:] = [] + + # Test with a statement (different code path) + with tt.AssertPrints("Wall time: "): + ip.run_line_magic("time", "a = f(-3 + -2)") + self.assertEqual(called, [5]) + + def test_macro(self): + ip.push({'a':10}) + # The AST transformation makes this do a+=-1 + ip.define_macro("amacro", "a+=1\nprint(a)") + + with tt.AssertPrints("9"): + ip.run_cell("amacro") + with tt.AssertPrints("8"): + ip.run_cell("amacro") + +class TestMiscTransform(unittest.TestCase): + + + def test_transform_only_once(self): + cleanup = 0 + line_t = 0 + def count_cleanup(lines): + nonlocal cleanup + cleanup += 1 + return lines + + def count_line_t(lines): + nonlocal line_t + line_t += 1 + return lines + + ip.input_transformer_manager.cleanup_transforms.append(count_cleanup) + ip.input_transformer_manager.line_transforms.append(count_line_t) + + ip.run_cell('1') + + assert cleanup == 1 + assert line_t == 1 + +class IntegerWrapper(ast.NodeTransformer): + """Wraps all integers in a call to Integer()""" + + # for Python 3.7 and earlier + + # for Python 3.7 and earlier + def visit_Num(self, node): + if isinstance(node.n, int): + return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()), + args=[node], keywords=[]) + return node + + # For Python 3.8+ + def visit_Constant(self, node): + if isinstance(node.value, int): + return self.visit_Num(node) + return node + + +class TestAstTransform2(unittest.TestCase): + def setUp(self): + self.intwrapper = IntegerWrapper() + ip.ast_transformers.append(self.intwrapper) + + self.calls = [] + def Integer(*args): + self.calls.append(args) + return args + ip.push({"Integer": Integer}) + + def tearDown(self): + ip.ast_transformers.remove(self.intwrapper) + del ip.user_ns['Integer'] + + def test_run_cell(self): + ip.run_cell("n = 2") + self.assertEqual(self.calls, [(2,)]) + + # This shouldn't throw an error + ip.run_cell("o = 2.0") + self.assertEqual(ip.user_ns['o'], 2.0) + + def test_timeit(self): + called = set() + def f(x): + called.add(x) + ip.push({'f':f}) + + with tt.AssertPrints("std. dev. of"): + ip.run_line_magic("timeit", "-n1 f(1)") + self.assertEqual(called, {(1,)}) + called.clear() + + with tt.AssertPrints("std. dev. of"): + ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)") + self.assertEqual(called, {(2,), (3,)}) + +class ErrorTransformer(ast.NodeTransformer): + """Throws an error when it sees a number.""" + + # for Python 3.7 and earlier + def visit_Num(self, node): + raise ValueError("test") + + # for Python 3.8+ + def visit_Constant(self, node): + if isinstance(node.value, int): + return self.visit_Num(node) + return node + + +class TestAstTransformError(unittest.TestCase): + def test_unregistering(self): + err_transformer = ErrorTransformer() + ip.ast_transformers.append(err_transformer) + + with self.assertWarnsRegex(UserWarning, "It will be unregistered"): + ip.run_cell("1 + 2") + + # This should have been removed. + nt.assert_not_in(err_transformer, ip.ast_transformers) + + +class StringRejector(ast.NodeTransformer): + """Throws an InputRejected when it sees a string literal. + + Used to verify that NodeTransformers can signal that a piece of code should + not be executed by throwing an InputRejected. + """ + + #for python 3.7 and earlier + def visit_Str(self, node): + raise InputRejected("test") + + # 3.8 only + def visit_Constant(self, node): + if isinstance(node.value, str): + raise InputRejected("test") + return node + + +class TestAstTransformInputRejection(unittest.TestCase): + + def setUp(self): + self.transformer = StringRejector() + ip.ast_transformers.append(self.transformer) + + def tearDown(self): + ip.ast_transformers.remove(self.transformer) + + def test_input_rejection(self): + """Check that NodeTransformers can reject input.""" + + expect_exception_tb = tt.AssertPrints("InputRejected: test") + expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False) + + # Run the same check twice to verify that the transformer is not + # disabled after raising. + with expect_exception_tb, expect_no_cell_output: + ip.run_cell("'unsafe'") + + with expect_exception_tb, expect_no_cell_output: + res = ip.run_cell("'unsafe'") + + self.assertIsInstance(res.error_before_exec, InputRejected) + +def test__IPYTHON__(): + # This shouldn't raise a NameError, that's all + __IPYTHON__ + + +class DummyRepr(object): + def __repr__(self): + return "DummyRepr" + + def _repr_html_(self): + return "dummy" + + def _repr_javascript_(self): + return "console.log('hi');", {'key': 'value'} + + +def test_user_variables(): + # enable all formatters + ip.display_formatter.active_types = ip.display_formatter.format_types + + ip.user_ns['dummy'] = d = DummyRepr() + keys = {'dummy', 'doesnotexist'} + r = ip.user_expressions({ key:key for key in keys}) + + nt.assert_equal(keys, set(r.keys())) + dummy = r['dummy'] + nt.assert_equal({'status', 'data', 'metadata'}, set(dummy.keys())) + nt.assert_equal(dummy['status'], 'ok') + data = dummy['data'] + metadata = dummy['metadata'] + nt.assert_equal(data.get('text/html'), d._repr_html_()) + js, jsmd = d._repr_javascript_() + nt.assert_equal(data.get('application/javascript'), js) + nt.assert_equal(metadata.get('application/javascript'), jsmd) + + dne = r['doesnotexist'] + nt.assert_equal(dne['status'], 'error') + nt.assert_equal(dne['ename'], 'NameError') + + # back to text only + ip.display_formatter.active_types = ['text/plain'] + +def test_user_expression(): + # enable all formatters + ip.display_formatter.active_types = ip.display_formatter.format_types + query = { + 'a' : '1 + 2', + 'b' : '1/0', + } + r = ip.user_expressions(query) + import pprint + pprint.pprint(r) + nt.assert_equal(set(r.keys()), set(query.keys())) + a = r['a'] + nt.assert_equal({'status', 'data', 'metadata'}, set(a.keys())) + nt.assert_equal(a['status'], 'ok') + data = a['data'] + metadata = a['metadata'] + nt.assert_equal(data.get('text/plain'), '3') + + b = r['b'] + nt.assert_equal(b['status'], 'error') + nt.assert_equal(b['ename'], 'ZeroDivisionError') + + # back to text only + ip.display_formatter.active_types = ['text/plain'] + + +class TestSyntaxErrorTransformer(unittest.TestCase): + """Check that SyntaxError raised by an input transformer is handled by run_cell()""" + + @staticmethod + def transformer(lines): + for line in lines: + pos = line.find('syntaxerror') + if pos >= 0: + e = SyntaxError('input contains "syntaxerror"') + e.text = line + e.offset = pos + 1 + raise e + return lines + + def setUp(self): + ip.input_transformers_post.append(self.transformer) + + def tearDown(self): + ip.input_transformers_post.remove(self.transformer) + + def test_syntaxerror_input_transformer(self): + with tt.AssertPrints('1234'): + ip.run_cell('1234') + with tt.AssertPrints('SyntaxError: invalid syntax'): + ip.run_cell('1 2 3') # plain python syntax error + with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'): + ip.run_cell('2345 # syntaxerror') # input transformer syntax error + with tt.AssertPrints('3456'): + ip.run_cell('3456') + + +class TestWarningSuppression(unittest.TestCase): + def test_warning_suppression(self): + ip.run_cell("import warnings") + try: + with self.assertWarnsRegex(UserWarning, "asdf"): + ip.run_cell("warnings.warn('asdf')") + # Here's the real test -- if we run that again, we should get the + # warning again. Traditionally, each warning was only issued once per + # IPython session (approximately), even if the user typed in new and + # different code that should have also triggered the warning, leading + # to much confusion. + with self.assertWarnsRegex(UserWarning, "asdf"): + ip.run_cell("warnings.warn('asdf')") + finally: + ip.run_cell("del warnings") + + + def test_deprecation_warning(self): + ip.run_cell(""" +import warnings +def wrn(): + warnings.warn( + "I AM A WARNING", + DeprecationWarning + ) + """) + try: + with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"): + ip.run_cell("wrn()") + finally: + ip.run_cell("del warnings") + ip.run_cell("del wrn") + + +class TestImportNoDeprecate(tt.TempFileMixin): + + def setUp(self): + """Make a valid python temp file.""" + self.mktmp(""" +import warnings +def wrn(): + warnings.warn( + "I AM A WARNING", + DeprecationWarning + ) +""") + super().setUp() + + def test_no_dep(self): + """ + No deprecation warning should be raised from imported functions + """ + ip.run_cell("from {} import wrn".format(self.fname)) + + with tt.AssertNotPrints("I AM A WARNING"): + ip.run_cell("wrn()") + ip.run_cell("del wrn") + + +def test_custom_exc_count(): + hook = mock.Mock(return_value=None) + ip.set_custom_exc((SyntaxError,), hook) + before = ip.execution_count + ip.run_cell("def foo()", store_history=True) + # restore default excepthook + ip.set_custom_exc((), None) + nt.assert_equal(hook.call_count, 1) + nt.assert_equal(ip.execution_count, before + 1) + + +def test_run_cell_async(): + loop = asyncio.get_event_loop() + ip.run_cell("import asyncio") + coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5") + assert asyncio.iscoroutine(coro) + result = loop.run_until_complete(coro) + assert isinstance(result, interactiveshell.ExecutionResult) + assert result.result == 5 + + +def test_should_run_async(): + assert not ip.should_run_async("a = 5") + assert ip.should_run_async("await x") + assert ip.should_run_async("import asyncio; await asyncio.sleep(1)") + + +def test_set_custom_completer(): + num_completers = len(ip.Completer.matchers) + + def foo(*args, **kwargs): + return "I'm a completer!" + + ip.set_custom_completer(foo, 0) + + # check that we've really added a new completer + assert len(ip.Completer.matchers) == num_completers + 1 + + # check that the first completer is the function we defined + assert ip.Completer.matchers[0]() == "I'm a completer!" + + # clean up + ip.Completer.custom_matchers.pop() diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_iplib.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_iplib.py new file mode 100644 index 00000000..adadae56 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_iplib.py @@ -0,0 +1,246 @@ +"""Tests for the key interactiveshell module, where the main ipython class is defined. +""" +#----------------------------------------------------------------------------- +# Module imports +#----------------------------------------------------------------------------- + +# third party +import nose.tools as nt + +# our own packages + +#----------------------------------------------------------------------------- +# Test functions +#----------------------------------------------------------------------------- + +def test_reset(): + """reset must clear most namespaces.""" + + # Check that reset runs without error + ip.reset() + + # Once we've reset it (to clear of any junk that might have been there from + # other tests, we can count how many variables are in the user's namespace + nvars_user_ns = len(ip.user_ns) + nvars_hidden = len(ip.user_ns_hidden) + + # Now add a few variables to user_ns, and check that reset clears them + ip.user_ns['x'] = 1 + ip.user_ns['y'] = 1 + ip.reset() + + # Finally, check that all namespaces have only as many variables as we + # expect to find in them: + nt.assert_equal(len(ip.user_ns), nvars_user_ns) + nt.assert_equal(len(ip.user_ns_hidden), nvars_hidden) + + +# Tests for reporting of exceptions in various modes, handling of SystemExit, +# and %tb functionality. This is really a mix of testing ultraTB and interactiveshell. + +def doctest_tb_plain(): + """ +In [18]: xmode plain +Exception reporting mode: Plain + +In [19]: run simpleerr.py +Traceback (most recent call last): + ...line 32, in + bar(mode) + ...line 16, in bar + div0() + ...line 8, in div0 + x/y +ZeroDivisionError: ... + """ + + +def doctest_tb_context(): + """ +In [3]: xmode context +Exception reporting mode: Context + +In [4]: run simpleerr.py +--------------------------------------------------------------------------- +ZeroDivisionError Traceback (most recent call last) + +... in + 30 mode = 'div' + 31 +---> 32 bar(mode) + +... in bar(mode) + 14 "bar" + 15 if mode=='div': +---> 16 div0() + 17 elif mode=='exit': + 18 try: + +... in div0() + 6 x = 1 + 7 y = 0 +----> 8 x/y + 9 + 10 def sysexit(stat, mode): + +ZeroDivisionError: ... +""" + + +def doctest_tb_verbose(): + """ +In [5]: xmode verbose +Exception reporting mode: Verbose + +In [6]: run simpleerr.py +--------------------------------------------------------------------------- +ZeroDivisionError Traceback (most recent call last) + +... in + 30 mode = 'div' + 31 +---> 32 bar(mode) + global bar = + global mode = 'div' + +... in bar(mode='div') + 14 "bar" + 15 if mode=='div': +---> 16 div0() + global div0 = + 17 elif mode=='exit': + 18 try: + +... in div0() + 6 x = 1 + 7 y = 0 +----> 8 x/y + x = 1 + y = 0 + 9 + 10 def sysexit(stat, mode): + +ZeroDivisionError: ... + """ + +def doctest_tb_sysexit(): + """ +In [17]: %xmode plain +Exception reporting mode: Plain + +In [18]: %run simpleerr.py exit +An exception has occurred, use %tb to see the full traceback. +SystemExit: (1, 'Mode = exit') + +In [19]: %run simpleerr.py exit 2 +An exception has occurred, use %tb to see the full traceback. +SystemExit: (2, 'Mode = exit') + +In [20]: %tb +Traceback (most recent call last): + File ... in + bar(mode) + File ... line 22, in bar + sysexit(stat, mode) + File ... line 11, in sysexit + raise SystemExit(stat, 'Mode = %s' % mode) +SystemExit: (2, 'Mode = exit') + +In [21]: %xmode context +Exception reporting mode: Context + +In [22]: %tb +--------------------------------------------------------------------------- +SystemExit Traceback (most recent call last) + +... + 30 mode = 'div' + 31 +---> 32 bar(mode) + +...bar(mode) + 20 except: + 21 stat = 1 +---> 22 sysexit(stat, mode) + 23 else: + 24 raise ValueError('Unknown mode') + +...sysexit(stat, mode) + 9 + 10 def sysexit(stat, mode): +---> 11 raise SystemExit(stat, 'Mode = %s' % mode) + 12 + 13 def bar(mode): + +SystemExit: (2, 'Mode = exit') + +In [23]: %xmode verbose +Exception reporting mode: Verbose + +In [24]: %tb +--------------------------------------------------------------------------- +SystemExit Traceback (most recent call last) + +... in + 30 mode = 'div' + 31 +---> 32 bar(mode) + global bar = + global mode = 'exit' + +... in bar(mode='exit') + 20 except: + 21 stat = 1 +---> 22 sysexit(stat, mode) + global sysexit = + stat = 2 + mode = 'exit' + 23 else: + 24 raise ValueError('Unknown mode') + +... in sysexit(stat=2, mode='exit') + 9 + 10 def sysexit(stat, mode): +---> 11 raise SystemExit(stat, 'Mode = %s' % mode) + global SystemExit = undefined + stat = 2 + mode = 'exit' + 12 + 13 def bar(mode): + +SystemExit: (2, 'Mode = exit') + """ + + +def test_run_cell(): + import textwrap + ip.run_cell('a = 10\na+=1') + ip.run_cell('assert a == 11\nassert 1') + + nt.assert_equal(ip.user_ns['a'], 11) + complex = textwrap.dedent(""" + if 1: + print "hello" + if 1: + print "world" + + if 2: + print "foo" + + if 3: + print "bar" + + if 4: + print "bar" + + """) + # Simply verifies that this kind of input is run + ip.run_cell(complex) + + +def test_db(): + """Test the internal database used for variable persistence.""" + ip.db['__unittest_'] = 12 + nt.assert_equal(ip.db['__unittest_'], 12) + del ip.db['__unittest_'] + assert '__unittest_' not in ip.db diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_logger.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_logger.py new file mode 100644 index 00000000..ebebac16 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_logger.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +"""Test IPython.core.logger""" + +import os.path + +import nose.tools as nt +from IPython.utils.tempdir import TemporaryDirectory + +def test_logstart_inaccessible_file(): + try: + _ip.logger.logstart(logfname="/") # Opening that filename will fail. + except IOError: + pass + else: + nt.assert_true(False) # The try block should never pass. + + try: + _ip.run_cell("a=1") # Check it doesn't try to log this + finally: + _ip.logger.log_active = False # If this fails, don't let later tests fail + +def test_logstart_unicode(): + with TemporaryDirectory() as tdir: + logfname = os.path.join(tdir, "test_unicode.log") + _ip.run_cell("'abc€'") + try: + _ip.magic("logstart -to %s" % logfname) + _ip.run_cell("'abc€'") + finally: + _ip.logger.logstop() diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic.py new file mode 100644 index 00000000..d6e5d2f4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic.py @@ -0,0 +1,1238 @@ +# -*- coding: utf-8 -*- +"""Tests for various magic functions. + +Needs to be run by nose (to make ipython session available). +""" + +import io +import os +import re +import sys +import warnings +from textwrap import dedent +from unittest import TestCase +from unittest import mock +from importlib import invalidate_caches +from io import StringIO + +import nose.tools as nt + +import shlex + +from IPython import get_ipython +from IPython.core import magic +from IPython.core.error import UsageError +from IPython.core.magic import (Magics, magics_class, line_magic, + cell_magic, + register_line_magic, register_cell_magic) +from IPython.core.magics import execution, script, code, logging, osm +from IPython.testing import decorators as dec +from IPython.testing import tools as tt +from IPython.utils.io import capture_output +from IPython.utils.tempdir import (TemporaryDirectory, + TemporaryWorkingDirectory) +from IPython.utils.process import find_cmd +from .test_debugger import PdbTestInput + + +@magic.magics_class +class DummyMagics(magic.Magics): pass + +def test_extract_code_ranges(): + instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :" + expected = [(0, 1), + (2, 3), + (4, 6), + (6, 9), + (9, 14), + (16, None), + (None, 9), + (9, None), + (None, 13), + (None, None)] + actual = list(code.extract_code_ranges(instr)) + nt.assert_equal(actual, expected) + +def test_extract_symbols(): + source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n""" + symbols_args = ["a", "b", "A", "A,b", "A,a", "z"] + expected = [([], ['a']), + (["def b():\n return 42\n"], []), + (["class A: pass\n"], []), + (["class A: pass\n", "def b():\n return 42\n"], []), + (["class A: pass\n"], ['a']), + ([], ['z'])] + for symbols, exp in zip(symbols_args, expected): + nt.assert_equal(code.extract_symbols(source, symbols), exp) + + +def test_extract_symbols_raises_exception_with_non_python_code(): + source = ("=begin A Ruby program :)=end\n" + "def hello\n" + "puts 'Hello world'\n" + "end") + with nt.assert_raises(SyntaxError): + code.extract_symbols(source, "hello") + + +def test_magic_not_found(): + # magic not found raises UsageError + with nt.assert_raises(UsageError): + _ip.magic('doesntexist') + + # ensure result isn't success when a magic isn't found + result = _ip.run_cell('%doesntexist') + assert isinstance(result.error_in_exec, UsageError) + + +def test_cell_magic_not_found(): + # magic not found raises UsageError + with nt.assert_raises(UsageError): + _ip.run_cell_magic('doesntexist', 'line', 'cell') + + # ensure result isn't success when a magic isn't found + result = _ip.run_cell('%%doesntexist') + assert isinstance(result.error_in_exec, UsageError) + + +def test_magic_error_status(): + def fail(shell): + 1/0 + _ip.register_magic_function(fail) + result = _ip.run_cell('%fail') + assert isinstance(result.error_in_exec, ZeroDivisionError) + + +def test_config(): + """ test that config magic does not raise + can happen if Configurable init is moved too early into + Magics.__init__ as then a Config object will be registered as a + magic. + """ + ## should not raise. + _ip.magic('config') + +def test_config_available_configs(): + """ test that config magic prints available configs in unique and + sorted order. """ + with capture_output() as captured: + _ip.magic('config') + + stdout = captured.stdout + config_classes = stdout.strip().split('\n')[1:] + nt.assert_list_equal(config_classes, sorted(set(config_classes))) + +def test_config_print_class(): + """ test that config with a classname prints the class's options. """ + with capture_output() as captured: + _ip.magic('config TerminalInteractiveShell') + + stdout = captured.stdout + if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]): + print(stdout) + raise AssertionError("1st line of stdout not like " + "'TerminalInteractiveShell.* options'") + +def test_rehashx(): + # clear up everything + _ip.alias_manager.clear_aliases() + del _ip.db['syscmdlist'] + + _ip.magic('rehashx') + # Practically ALL ipython development systems will have more than 10 aliases + + nt.assert_true(len(_ip.alias_manager.aliases) > 10) + for name, cmd in _ip.alias_manager.aliases: + # we must strip dots from alias names + nt.assert_not_in('.', name) + + # rehashx must fill up syscmdlist + scoms = _ip.db['syscmdlist'] + nt.assert_true(len(scoms) > 10) + + + +def test_magic_parse_options(): + """Test that we don't mangle paths when parsing magic options.""" + ip = get_ipython() + path = 'c:\\x' + m = DummyMagics(ip) + opts = m.parse_options('-f %s' % path,'f:')[0] + # argv splitting is os-dependent + if os.name == 'posix': + expected = 'c:x' + else: + expected = path + nt.assert_equal(opts['f'], expected) + +def test_magic_parse_long_options(): + """Magic.parse_options can handle --foo=bar long options""" + ip = get_ipython() + m = DummyMagics(ip) + opts, _ = m.parse_options('--foo --bar=bubble', 'a', 'foo', 'bar=') + nt.assert_in('foo', opts) + nt.assert_in('bar', opts) + nt.assert_equal(opts['bar'], "bubble") + + +@dec.skip_without('sqlite3') +def doctest_hist_f(): + """Test %hist -f with temporary filename. + + In [9]: import tempfile + + In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-') + + In [11]: %hist -nl -f $tfile 3 + + In [13]: import os; os.unlink(tfile) + """ + + +@dec.skip_without('sqlite3') +def doctest_hist_r(): + """Test %hist -r + + XXX - This test is not recording the output correctly. For some reason, in + testing mode the raw history isn't getting populated. No idea why. + Disabling the output checking for now, though at least we do run it. + + In [1]: 'hist' in _ip.lsmagic() + Out[1]: True + + In [2]: x=1 + + In [3]: %hist -rl 2 + x=1 # random + %hist -r 2 + """ + + +@dec.skip_without('sqlite3') +def doctest_hist_op(): + """Test %hist -op + + In [1]: class b(float): + ...: pass + ...: + + In [2]: class s(object): + ...: def __str__(self): + ...: return 's' + ...: + + In [3]: + + In [4]: class r(b): + ...: def __repr__(self): + ...: return 'r' + ...: + + In [5]: class sr(s,r): pass + ...: + + In [6]: + + In [7]: bb=b() + + In [8]: ss=s() + + In [9]: rr=r() + + In [10]: ssrr=sr() + + In [11]: 4.5 + Out[11]: 4.5 + + In [12]: str(ss) + Out[12]: 's' + + In [13]: + + In [14]: %hist -op + >>> class b: + ... pass + ... + >>> class s(b): + ... def __str__(self): + ... return 's' + ... + >>> + >>> class r(b): + ... def __repr__(self): + ... return 'r' + ... + >>> class sr(s,r): pass + >>> + >>> bb=b() + >>> ss=s() + >>> rr=r() + >>> ssrr=sr() + >>> 4.5 + 4.5 + >>> str(ss) + 's' + >>> + """ + +def test_hist_pof(): + ip = get_ipython() + ip.run_cell(u"1+2", store_history=True) + #raise Exception(ip.history_manager.session_number) + #raise Exception(list(ip.history_manager._get_range_session())) + with TemporaryDirectory() as td: + tf = os.path.join(td, 'hist.py') + ip.run_line_magic('history', '-pof %s' % tf) + assert os.path.isfile(tf) + + +@dec.skip_without('sqlite3') +def test_macro(): + ip = get_ipython() + ip.history_manager.reset() # Clear any existing history. + cmds = ["a=1", "def b():\n return a**2", "print(a,b())"] + for i, cmd in enumerate(cmds, start=1): + ip.history_manager.store_inputs(i, cmd) + ip.magic("macro test 1-3") + nt.assert_equal(ip.user_ns["test"].value, "\n".join(cmds)+"\n") + + # List macros + nt.assert_in("test", ip.magic("macro")) + + +@dec.skip_without('sqlite3') +def test_macro_run(): + """Test that we can run a multi-line macro successfully.""" + ip = get_ipython() + ip.history_manager.reset() + cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"] + for cmd in cmds: + ip.run_cell(cmd, store_history=True) + nt.assert_equal(ip.user_ns["test"].value, "a+=1\nprint(a)\n") + with tt.AssertPrints("12"): + ip.run_cell("test") + with tt.AssertPrints("13"): + ip.run_cell("test") + + +def test_magic_magic(): + """Test %magic""" + ip = get_ipython() + with capture_output() as captured: + ip.magic("magic") + + stdout = captured.stdout + nt.assert_in('%magic', stdout) + nt.assert_in('IPython', stdout) + nt.assert_in('Available', stdout) + + +@dec.skipif_not_numpy +def test_numpy_reset_array_undec(): + "Test '%reset array' functionality" + _ip.ex('import numpy as np') + _ip.ex('a = np.empty(2)') + nt.assert_in('a', _ip.user_ns) + _ip.magic('reset -f array') + nt.assert_not_in('a', _ip.user_ns) + +def test_reset_out(): + "Test '%reset out' magic" + _ip.run_cell("parrot = 'dead'", store_history=True) + # test '%reset -f out', make an Out prompt + _ip.run_cell("parrot", store_history=True) + nt.assert_true('dead' in [_ip.user_ns[x] for x in ('_','__','___')]) + _ip.magic('reset -f out') + nt.assert_false('dead' in [_ip.user_ns[x] for x in ('_','__','___')]) + nt.assert_equal(len(_ip.user_ns['Out']), 0) + +def test_reset_in(): + "Test '%reset in' magic" + # test '%reset -f in' + _ip.run_cell("parrot", store_history=True) + nt.assert_true('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')]) + _ip.magic('%reset -f in') + nt.assert_false('parrot' in [_ip.user_ns[x] for x in ('_i','_ii','_iii')]) + nt.assert_equal(len(set(_ip.user_ns['In'])), 1) + +def test_reset_dhist(): + "Test '%reset dhist' magic" + _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing + _ip.magic('cd ' + os.path.dirname(nt.__file__)) + _ip.magic('cd -') + nt.assert_true(len(_ip.user_ns['_dh']) > 0) + _ip.magic('reset -f dhist') + nt.assert_equal(len(_ip.user_ns['_dh']), 0) + _ip.run_cell("_dh = [d for d in tmp]") #restore + +def test_reset_in_length(): + "Test that '%reset in' preserves In[] length" + _ip.run_cell("print 'foo'") + _ip.run_cell("reset -f in") + nt.assert_equal(len(_ip.user_ns['In']), _ip.displayhook.prompt_count+1) + +class TestResetErrors(TestCase): + + def test_reset_redefine(self): + + @magics_class + class KernelMagics(Magics): + @line_magic + def less(self, shell): pass + + _ip.register_magics(KernelMagics) + + with self.assertLogs() as cm: + # hack, we want to just capture logs, but assertLogs fails if not + # logs get produce. + # so log one things we ignore. + import logging as log_mod + log = log_mod.getLogger() + log.info('Nothing') + # end hack. + _ip.run_cell("reset -f") + + assert len(cm.output) == 1 + for out in cm.output: + assert "Invalid alias" not in out + +def test_tb_syntaxerror(): + """test %tb after a SyntaxError""" + ip = get_ipython() + ip.run_cell("for") + + # trap and validate stdout + save_stdout = sys.stdout + try: + sys.stdout = StringIO() + ip.run_cell("%tb") + out = sys.stdout.getvalue() + finally: + sys.stdout = save_stdout + # trim output, and only check the last line + last_line = out.rstrip().splitlines()[-1].strip() + nt.assert_equal(last_line, "SyntaxError: invalid syntax") + + +def test_time(): + ip = get_ipython() + + with tt.AssertPrints("Wall time: "): + ip.run_cell("%time None") + + ip.run_cell("def f(kmjy):\n" + " %time print (2*kmjy)") + + with tt.AssertPrints("Wall time: "): + with tt.AssertPrints("hihi", suppress=False): + ip.run_cell("f('hi')") + +def test_time_last_not_expression(): + ip.run_cell("%%time\n" + "var_1 = 1\n" + "var_2 = 2\n") + assert ip.user_ns['var_1'] == 1 + del ip.user_ns['var_1'] + assert ip.user_ns['var_2'] == 2 + del ip.user_ns['var_2'] + + +@dec.skip_win32 +def test_time2(): + ip = get_ipython() + + with tt.AssertPrints("CPU times: user "): + ip.run_cell("%time None") + +def test_time3(): + """Erroneous magic function calls, issue gh-3334""" + ip = get_ipython() + ip.user_ns.pop('run', None) + + with tt.AssertNotPrints("not found", channel='stderr'): + ip.run_cell("%%time\n" + "run = 0\n" + "run += 1") + +def test_multiline_time(): + """Make sure last statement from time return a value.""" + ip = get_ipython() + ip.user_ns.pop('run', None) + + ip.run_cell(dedent("""\ + %%time + a = "ho" + b = "hey" + a+b + """)) + nt.assert_equal(ip.user_ns_hidden['_'], 'hohey') + +def test_time_local_ns(): + """ + Test that local_ns is actually global_ns when running a cell magic + """ + ip = get_ipython() + ip.run_cell("%%time\n" + "myvar = 1") + nt.assert_equal(ip.user_ns['myvar'], 1) + del ip.user_ns['myvar'] + +def test_doctest_mode(): + "Toggle doctest_mode twice, it should be a no-op and run without error" + _ip.magic('doctest_mode') + _ip.magic('doctest_mode') + + +def test_parse_options(): + """Tests for basic options parsing in magics.""" + # These are only the most minimal of tests, more should be added later. At + # the very least we check that basic text/unicode calls work OK. + m = DummyMagics(_ip) + nt.assert_equal(m.parse_options('foo', '')[1], 'foo') + nt.assert_equal(m.parse_options(u'foo', '')[1], u'foo') + + +def test_dirops(): + """Test various directory handling operations.""" + # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/') + curpath = os.getcwd + startdir = os.getcwd() + ipdir = os.path.realpath(_ip.ipython_dir) + try: + _ip.magic('cd "%s"' % ipdir) + nt.assert_equal(curpath(), ipdir) + _ip.magic('cd -') + nt.assert_equal(curpath(), startdir) + _ip.magic('pushd "%s"' % ipdir) + nt.assert_equal(curpath(), ipdir) + _ip.magic('popd') + nt.assert_equal(curpath(), startdir) + finally: + os.chdir(startdir) + + +def test_cd_force_quiet(): + """Test OSMagics.cd_force_quiet option""" + _ip.config.OSMagics.cd_force_quiet = True + osmagics = osm.OSMagics(shell=_ip) + + startdir = os.getcwd() + ipdir = os.path.realpath(_ip.ipython_dir) + + try: + with tt.AssertNotPrints(ipdir): + osmagics.cd('"%s"' % ipdir) + with tt.AssertNotPrints(startdir): + osmagics.cd('-') + finally: + os.chdir(startdir) + + +def test_xmode(): + # Calling xmode three times should be a no-op + xmode = _ip.InteractiveTB.mode + for i in range(4): + _ip.magic("xmode") + nt.assert_equal(_ip.InteractiveTB.mode, xmode) + +def test_reset_hard(): + monitor = [] + class A(object): + def __del__(self): + monitor.append(1) + def __repr__(self): + return "" + + _ip.user_ns["a"] = A() + _ip.run_cell("a") + + nt.assert_equal(monitor, []) + _ip.magic("reset -f") + nt.assert_equal(monitor, [1]) + +class TestXdel(tt.TempFileMixin): + def test_xdel(self): + """Test that references from %run are cleared by xdel.""" + src = ("class A(object):\n" + " monitor = []\n" + " def __del__(self):\n" + " self.monitor.append(1)\n" + "a = A()\n") + self.mktmp(src) + # %run creates some hidden references... + _ip.magic("run %s" % self.fname) + # ... as does the displayhook. + _ip.run_cell("a") + + monitor = _ip.user_ns["A"].monitor + nt.assert_equal(monitor, []) + + _ip.magic("xdel a") + + # Check that a's __del__ method has been called. + nt.assert_equal(monitor, [1]) + +def doctest_who(): + """doctest for %who + + In [1]: %reset -f + + In [2]: alpha = 123 + + In [3]: beta = 'beta' + + In [4]: %who int + alpha + + In [5]: %who str + beta + + In [6]: %whos + Variable Type Data/Info + ---------------------------- + alpha int 123 + beta str beta + + In [7]: %who_ls + Out[7]: ['alpha', 'beta'] + """ + +def test_whos(): + """Check that whos is protected against objects where repr() fails.""" + class A(object): + def __repr__(self): + raise Exception() + _ip.user_ns['a'] = A() + _ip.magic("whos") + +def doctest_precision(): + """doctest for %precision + + In [1]: f = get_ipython().display_formatter.formatters['text/plain'] + + In [2]: %precision 5 + Out[2]: '%.5f' + + In [3]: f.float_format + Out[3]: '%.5f' + + In [4]: %precision %e + Out[4]: '%e' + + In [5]: f(3.1415927) + Out[5]: '3.141593e+00' + """ + +def test_debug_magic(): + """Test debugging a small code with %debug + + In [1]: with PdbTestInput(['c']): + ...: %debug print("a b") #doctest: +ELLIPSIS + ...: + ... + ipdb> c + a b + In [2]: + """ + +def test_psearch(): + with tt.AssertPrints("dict.fromkeys"): + _ip.run_cell("dict.fr*?") + with tt.AssertPrints("π.is_integer"): + _ip.run_cell("π = 3.14;\nπ.is_integ*?") + +def test_timeit_shlex(): + """test shlex issues with timeit (#1109)""" + _ip.ex("def f(*a,**kw): pass") + _ip.magic('timeit -n1 "this is a bug".count(" ")') + _ip.magic('timeit -r1 -n1 f(" ", 1)') + _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")') + _ip.magic('timeit -r1 -n1 ("a " + "b")') + _ip.magic('timeit -r1 -n1 f("a " + "b")') + _ip.magic('timeit -r1 -n1 f("a " + "b ")') + + +def test_timeit_special_syntax(): + "Test %%timeit with IPython special syntax" + @register_line_magic + def lmagic(line): + ip = get_ipython() + ip.user_ns['lmagic_out'] = line + + # line mode test + _ip.run_line_magic('timeit', '-n1 -r1 %lmagic my line') + nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line') + # cell mode test + _ip.run_cell_magic('timeit', '-n1 -r1', '%lmagic my line2') + nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2') + +def test_timeit_return(): + """ + test whether timeit -o return object + """ + + res = _ip.run_line_magic('timeit','-n10 -r10 -o 1') + assert(res is not None) + +def test_timeit_quiet(): + """ + test quiet option of timeit magic + """ + with tt.AssertNotPrints("loops"): + _ip.run_cell("%timeit -n1 -r1 -q 1") + +def test_timeit_return_quiet(): + with tt.AssertNotPrints("loops"): + res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1') + assert (res is not None) + +def test_timeit_invalid_return(): + with nt.assert_raises_regex(SyntaxError, "outside function"): + _ip.run_line_magic('timeit', 'return') + +@dec.skipif(execution.profile is None) +def test_prun_special_syntax(): + "Test %%prun with IPython special syntax" + @register_line_magic + def lmagic(line): + ip = get_ipython() + ip.user_ns['lmagic_out'] = line + + # line mode test + _ip.run_line_magic('prun', '-q %lmagic my line') + nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line') + # cell mode test + _ip.run_cell_magic('prun', '-q', '%lmagic my line2') + nt.assert_equal(_ip.user_ns['lmagic_out'], 'my line2') + +@dec.skipif(execution.profile is None) +def test_prun_quotes(): + "Test that prun does not clobber string escapes (GH #1302)" + _ip.magic(r"prun -q x = '\t'") + nt.assert_equal(_ip.user_ns['x'], '\t') + +def test_extension(): + # Debugging information for failures of this test + print('sys.path:') + for p in sys.path: + print(' ', p) + print('CWD', os.getcwd()) + + nt.assert_raises(ImportError, _ip.magic, "load_ext daft_extension") + daft_path = os.path.join(os.path.dirname(__file__), "daft_extension") + sys.path.insert(0, daft_path) + try: + _ip.user_ns.pop('arq', None) + invalidate_caches() # Clear import caches + _ip.magic("load_ext daft_extension") + nt.assert_equal(_ip.user_ns['arq'], 185) + _ip.magic("unload_ext daft_extension") + assert 'arq' not in _ip.user_ns + finally: + sys.path.remove(daft_path) + + +def test_notebook_export_json(): + _ip = get_ipython() + _ip.history_manager.reset() # Clear any existing history. + cmds = [u"a=1", u"def b():\n return a**2", u"print('noël, été', b())"] + for i, cmd in enumerate(cmds, start=1): + _ip.history_manager.store_inputs(i, cmd) + with TemporaryDirectory() as td: + outfile = os.path.join(td, "nb.ipynb") + _ip.magic("notebook -e %s" % outfile) + + +class TestEnv(TestCase): + + def test_env(self): + env = _ip.magic("env") + self.assertTrue(isinstance(env, dict)) + + def test_env_secret(self): + env = _ip.magic("env") + hidden = "" + with mock.patch.dict( + os.environ, + { + "API_KEY": "abc123", + "SECRET_THING": "ssshhh", + "JUPYTER_TOKEN": "", + "VAR": "abc" + } + ): + env = _ip.magic("env") + assert env["API_KEY"] == hidden + assert env["SECRET_THING"] == hidden + assert env["JUPYTER_TOKEN"] == hidden + assert env["VAR"] == "abc" + + def test_env_get_set_simple(self): + env = _ip.magic("env var val1") + self.assertEqual(env, None) + self.assertEqual(os.environ['var'], 'val1') + self.assertEqual(_ip.magic("env var"), 'val1') + env = _ip.magic("env var=val2") + self.assertEqual(env, None) + self.assertEqual(os.environ['var'], 'val2') + + def test_env_get_set_complex(self): + env = _ip.magic("env var 'val1 '' 'val2") + self.assertEqual(env, None) + self.assertEqual(os.environ['var'], "'val1 '' 'val2") + self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2") + env = _ip.magic('env var=val2 val3="val4') + self.assertEqual(env, None) + self.assertEqual(os.environ['var'], 'val2 val3="val4') + + def test_env_set_bad_input(self): + self.assertRaises(UsageError, lambda: _ip.magic("set_env var")) + + def test_env_set_whitespace(self): + self.assertRaises(UsageError, lambda: _ip.magic("env var A=B")) + + +class CellMagicTestCase(TestCase): + + def check_ident(self, magic): + # Manually called, we get the result + out = _ip.run_cell_magic(magic, 'a', 'b') + nt.assert_equal(out, ('a','b')) + # Via run_cell, it goes into the user's namespace via displayhook + _ip.run_cell('%%' + magic +' c\nd\n') + nt.assert_equal(_ip.user_ns['_'], ('c','d\n')) + + def test_cell_magic_func_deco(self): + "Cell magic using simple decorator" + @register_cell_magic + def cellm(line, cell): + return line, cell + + self.check_ident('cellm') + + def test_cell_magic_reg(self): + "Cell magic manually registered" + def cellm(line, cell): + return line, cell + + _ip.register_magic_function(cellm, 'cell', 'cellm2') + self.check_ident('cellm2') + + def test_cell_magic_class(self): + "Cell magics declared via a class" + @magics_class + class MyMagics(Magics): + + @cell_magic + def cellm3(self, line, cell): + return line, cell + + _ip.register_magics(MyMagics) + self.check_ident('cellm3') + + def test_cell_magic_class2(self): + "Cell magics declared via a class, #2" + @magics_class + class MyMagics2(Magics): + + @cell_magic('cellm4') + def cellm33(self, line, cell): + return line, cell + + _ip.register_magics(MyMagics2) + self.check_ident('cellm4') + # Check that nothing is registered as 'cellm33' + c33 = _ip.find_cell_magic('cellm33') + nt.assert_equal(c33, None) + +def test_file(): + """Basic %%writefile""" + ip = get_ipython() + with TemporaryDirectory() as td: + fname = os.path.join(td, 'file1') + ip.run_cell_magic("writefile", fname, u'\n'.join([ + 'line1', + 'line2', + ])) + with open(fname) as f: + s = f.read() + nt.assert_in('line1\n', s) + nt.assert_in('line2', s) + +@dec.skip_win32 +def test_file_single_quote(): + """Basic %%writefile with embedded single quotes""" + ip = get_ipython() + with TemporaryDirectory() as td: + fname = os.path.join(td, '\'file1\'') + ip.run_cell_magic("writefile", fname, u'\n'.join([ + 'line1', + 'line2', + ])) + with open(fname) as f: + s = f.read() + nt.assert_in('line1\n', s) + nt.assert_in('line2', s) + +@dec.skip_win32 +def test_file_double_quote(): + """Basic %%writefile with embedded double quotes""" + ip = get_ipython() + with TemporaryDirectory() as td: + fname = os.path.join(td, '"file1"') + ip.run_cell_magic("writefile", fname, u'\n'.join([ + 'line1', + 'line2', + ])) + with open(fname) as f: + s = f.read() + nt.assert_in('line1\n', s) + nt.assert_in('line2', s) + +def test_file_var_expand(): + """%%writefile $filename""" + ip = get_ipython() + with TemporaryDirectory() as td: + fname = os.path.join(td, 'file1') + ip.user_ns['filename'] = fname + ip.run_cell_magic("writefile", '$filename', u'\n'.join([ + 'line1', + 'line2', + ])) + with open(fname) as f: + s = f.read() + nt.assert_in('line1\n', s) + nt.assert_in('line2', s) + +def test_file_unicode(): + """%%writefile with unicode cell""" + ip = get_ipython() + with TemporaryDirectory() as td: + fname = os.path.join(td, 'file1') + ip.run_cell_magic("writefile", fname, u'\n'.join([ + u'liné1', + u'liné2', + ])) + with io.open(fname, encoding='utf-8') as f: + s = f.read() + nt.assert_in(u'liné1\n', s) + nt.assert_in(u'liné2', s) + +def test_file_amend(): + """%%writefile -a amends files""" + ip = get_ipython() + with TemporaryDirectory() as td: + fname = os.path.join(td, 'file2') + ip.run_cell_magic("writefile", fname, u'\n'.join([ + 'line1', + 'line2', + ])) + ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([ + 'line3', + 'line4', + ])) + with open(fname) as f: + s = f.read() + nt.assert_in('line1\n', s) + nt.assert_in('line3\n', s) + +def test_file_spaces(): + """%%file with spaces in filename""" + ip = get_ipython() + with TemporaryWorkingDirectory() as td: + fname = "file name" + ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([ + 'line1', + 'line2', + ])) + with open(fname) as f: + s = f.read() + nt.assert_in('line1\n', s) + nt.assert_in('line2', s) + +def test_script_config(): + ip = get_ipython() + ip.config.ScriptMagics.script_magics = ['whoda'] + sm = script.ScriptMagics(shell=ip) + nt.assert_in('whoda', sm.magics['cell']) + +@dec.skip_win32 +def test_script_out(): + ip = get_ipython() + ip.run_cell_magic("script", "--out output sh", "echo 'hi'") + nt.assert_equal(ip.user_ns['output'], 'hi\n') + +@dec.skip_win32 +def test_script_err(): + ip = get_ipython() + ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2") + nt.assert_equal(ip.user_ns['error'], 'hello\n') + +@dec.skip_win32 +def test_script_out_err(): + ip = get_ipython() + ip.run_cell_magic("script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2") + nt.assert_equal(ip.user_ns['output'], 'hi\n') + nt.assert_equal(ip.user_ns['error'], 'hello\n') + +@dec.skip_win32 +def test_script_bg_out(): + ip = get_ipython() + ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'") + + nt.assert_equal(ip.user_ns['output'].read(), b'hi\n') + ip.user_ns['output'].close() + +@dec.skip_win32 +def test_script_bg_err(): + ip = get_ipython() + ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2") + nt.assert_equal(ip.user_ns['error'].read(), b'hello\n') + ip.user_ns['error'].close() + +@dec.skip_win32 +def test_script_bg_out_err(): + ip = get_ipython() + ip.run_cell_magic("script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2") + nt.assert_equal(ip.user_ns['output'].read(), b'hi\n') + nt.assert_equal(ip.user_ns['error'].read(), b'hello\n') + ip.user_ns['output'].close() + ip.user_ns['error'].close() + +def test_script_defaults(): + ip = get_ipython() + for cmd in ['sh', 'bash', 'perl', 'ruby']: + try: + find_cmd(cmd) + except Exception: + pass + else: + nt.assert_in(cmd, ip.magics_manager.magics['cell']) + + +@magics_class +class FooFoo(Magics): + """class with both %foo and %%foo magics""" + @line_magic('foo') + def line_foo(self, line): + "I am line foo" + pass + + @cell_magic("foo") + def cell_foo(self, line, cell): + "I am cell foo, not line foo" + pass + +def test_line_cell_info(): + """%%foo and %foo magics are distinguishable to inspect""" + ip = get_ipython() + ip.magics_manager.register(FooFoo) + oinfo = ip.object_inspect('foo') + nt.assert_true(oinfo['found']) + nt.assert_true(oinfo['ismagic']) + + oinfo = ip.object_inspect('%%foo') + nt.assert_true(oinfo['found']) + nt.assert_true(oinfo['ismagic']) + nt.assert_equal(oinfo['docstring'], FooFoo.cell_foo.__doc__) + + oinfo = ip.object_inspect('%foo') + nt.assert_true(oinfo['found']) + nt.assert_true(oinfo['ismagic']) + nt.assert_equal(oinfo['docstring'], FooFoo.line_foo.__doc__) + +def test_multiple_magics(): + ip = get_ipython() + foo1 = FooFoo(ip) + foo2 = FooFoo(ip) + mm = ip.magics_manager + mm.register(foo1) + nt.assert_true(mm.magics['line']['foo'].__self__ is foo1) + mm.register(foo2) + nt.assert_true(mm.magics['line']['foo'].__self__ is foo2) + +def test_alias_magic(): + """Test %alias_magic.""" + ip = get_ipython() + mm = ip.magics_manager + + # Basic operation: both cell and line magics are created, if possible. + ip.run_line_magic('alias_magic', 'timeit_alias timeit') + nt.assert_in('timeit_alias', mm.magics['line']) + nt.assert_in('timeit_alias', mm.magics['cell']) + + # --cell is specified, line magic not created. + ip.run_line_magic('alias_magic', '--cell timeit_cell_alias timeit') + nt.assert_not_in('timeit_cell_alias', mm.magics['line']) + nt.assert_in('timeit_cell_alias', mm.magics['cell']) + + # Test that line alias is created successfully. + ip.run_line_magic('alias_magic', '--line env_alias env') + nt.assert_equal(ip.run_line_magic('env', ''), + ip.run_line_magic('env_alias', '')) + + # Test that line alias with parameters passed in is created successfully. + ip.run_line_magic('alias_magic', '--line history_alias history --params ' + shlex.quote('3')) + nt.assert_in('history_alias', mm.magics['line']) + + +def test_save(): + """Test %save.""" + ip = get_ipython() + ip.history_manager.reset() # Clear any existing history. + cmds = [u"a=1", u"def b():\n return a**2", u"print(a, b())"] + for i, cmd in enumerate(cmds, start=1): + ip.history_manager.store_inputs(i, cmd) + with TemporaryDirectory() as tmpdir: + file = os.path.join(tmpdir, "testsave.py") + ip.run_line_magic("save", "%s 1-10" % file) + with open(file) as f: + content = f.read() + nt.assert_equal(content.count(cmds[0]), 1) + nt.assert_in('coding: utf-8', content) + ip.run_line_magic("save", "-a %s 1-10" % file) + with open(file) as f: + content = f.read() + nt.assert_equal(content.count(cmds[0]), 2) + nt.assert_in('coding: utf-8', content) + + +def test_store(): + """Test %store.""" + ip = get_ipython() + ip.run_line_magic('load_ext', 'storemagic') + + # make sure the storage is empty + ip.run_line_magic('store', '-z') + ip.user_ns['var'] = 42 + ip.run_line_magic('store', 'var') + ip.user_ns['var'] = 39 + ip.run_line_magic('store', '-r') + nt.assert_equal(ip.user_ns['var'], 42) + + ip.run_line_magic('store', '-d var') + ip.user_ns['var'] = 39 + ip.run_line_magic('store' , '-r') + nt.assert_equal(ip.user_ns['var'], 39) + + +def _run_edit_test(arg_s, exp_filename=None, + exp_lineno=-1, + exp_contents=None, + exp_is_temp=None): + ip = get_ipython() + M = code.CodeMagics(ip) + last_call = ['',''] + opts,args = M.parse_options(arg_s,'prxn:') + filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call) + + if exp_filename is not None: + nt.assert_equal(exp_filename, filename) + if exp_contents is not None: + with io.open(filename, 'r', encoding='utf-8') as f: + contents = f.read() + nt.assert_equal(exp_contents, contents) + if exp_lineno != -1: + nt.assert_equal(exp_lineno, lineno) + if exp_is_temp is not None: + nt.assert_equal(exp_is_temp, is_temp) + + +def test_edit_interactive(): + """%edit on interactively defined objects""" + ip = get_ipython() + n = ip.execution_count + ip.run_cell(u"def foo(): return 1", store_history=True) + + try: + _run_edit_test("foo") + except code.InteractivelyDefined as e: + nt.assert_equal(e.index, n) + else: + raise AssertionError("Should have raised InteractivelyDefined") + + +def test_edit_cell(): + """%edit [cell id]""" + ip = get_ipython() + + ip.run_cell(u"def foo(): return 1", store_history=True) + + # test + _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True) + +def test_bookmark(): + ip = get_ipython() + ip.run_line_magic('bookmark', 'bmname') + with tt.AssertPrints('bmname'): + ip.run_line_magic('bookmark', '-l') + ip.run_line_magic('bookmark', '-d bmname') + +def test_ls_magic(): + ip = get_ipython() + json_formatter = ip.display_formatter.formatters['application/json'] + json_formatter.enabled = True + lsmagic = ip.magic('lsmagic') + with warnings.catch_warnings(record=True) as w: + j = json_formatter(lsmagic) + nt.assert_equal(sorted(j), ['cell', 'line']) + nt.assert_equal(w, []) # no warnings + +def test_strip_initial_indent(): + def sii(s): + lines = s.splitlines() + return '\n'.join(code.strip_initial_indent(lines)) + + nt.assert_equal(sii(" a = 1\nb = 2"), "a = 1\nb = 2") + nt.assert_equal(sii(" a\n b\nc"), "a\n b\nc") + nt.assert_equal(sii("a\n b"), "a\n b") + +def test_logging_magic_quiet_from_arg(): + _ip.config.LoggingMagics.quiet = False + lm = logging.LoggingMagics(shell=_ip) + with TemporaryDirectory() as td: + try: + with tt.AssertNotPrints(re.compile("Activating.*")): + lm.logstart('-q {}'.format( + os.path.join(td, "quiet_from_arg.log"))) + finally: + _ip.logger.logstop() + +def test_logging_magic_quiet_from_config(): + _ip.config.LoggingMagics.quiet = True + lm = logging.LoggingMagics(shell=_ip) + with TemporaryDirectory() as td: + try: + with tt.AssertNotPrints(re.compile("Activating.*")): + lm.logstart(os.path.join(td, "quiet_from_config.log")) + finally: + _ip.logger.logstop() + + +def test_logging_magic_not_quiet(): + _ip.config.LoggingMagics.quiet = False + lm = logging.LoggingMagics(shell=_ip) + with TemporaryDirectory() as td: + try: + with tt.AssertPrints(re.compile("Activating.*")): + lm.logstart(os.path.join(td, "not_quiet.log")) + finally: + _ip.logger.logstop() + + +def test_time_no_var_expand(): + _ip.user_ns['a'] = 5 + _ip.user_ns['b'] = [] + _ip.magic('time b.append("{a}")') + assert _ip.user_ns['b'] == ['{a}'] + + +# this is slow, put at the end for local testing. +def test_timeit_arguments(): + "Test valid timeit arguments, should not cause SyntaxError (GH #1269)" + if sys.version_info < (3,7): + _ip.magic("timeit -n1 -r1 ('#')") + else: + # 3.7 optimize no-op statement like above out, and complain there is + # nothing in the for loop. + _ip.magic("timeit -n1 -r1 a=('#')") diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic_arguments.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic_arguments.py new file mode 100644 index 00000000..5dea32dd --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic_arguments.py @@ -0,0 +1,118 @@ +#----------------------------------------------------------------------------- +# Copyright (C) 2010-2011, IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +import argparse +from nose.tools import assert_equal + +from IPython.core.magic_arguments import (argument, argument_group, kwds, + magic_arguments, parse_argstring, real_name) + + +@magic_arguments() +@argument('-f', '--foo', help="an argument") +def magic_foo1(self, args): + """ A docstring. + """ + return parse_argstring(magic_foo1, args) + + +@magic_arguments() +def magic_foo2(self, args): + """ A docstring. + """ + return parse_argstring(magic_foo2, args) + + +@magic_arguments() +@argument('-f', '--foo', help="an argument") +@argument_group('Group') +@argument('-b', '--bar', help="a grouped argument") +@argument_group('Second Group') +@argument('-z', '--baz', help="another grouped argument") +def magic_foo3(self, args): + """ A docstring. + """ + return parse_argstring(magic_foo3, args) + + +@magic_arguments() +@kwds(argument_default=argparse.SUPPRESS) +@argument('-f', '--foo', help="an argument") +def magic_foo4(self, args): + """ A docstring. + """ + return parse_argstring(magic_foo4, args) + + +@magic_arguments('frobnicate') +@argument('-f', '--foo', help="an argument") +def magic_foo5(self, args): + """ A docstring. + """ + return parse_argstring(magic_foo5, args) + + +@magic_arguments() +@argument('-f', '--foo', help="an argument") +def magic_magic_foo(self, args): + """ A docstring. + """ + return parse_argstring(magic_magic_foo, args) + + +@magic_arguments() +@argument('-f', '--foo', help="an argument") +def foo(self, args): + """ A docstring. + """ + return parse_argstring(foo, args) + + +def test_magic_arguments(): + assert_equal(magic_foo1.__doc__, '::\n\n %foo1 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') + assert_equal(getattr(magic_foo1, 'argcmd_name', None), None) + assert_equal(real_name(magic_foo1), 'foo1') + assert_equal(magic_foo1(None, ''), argparse.Namespace(foo=None)) + assert hasattr(magic_foo1, 'has_arguments') + + assert_equal(magic_foo2.__doc__, '::\n\n %foo2\n\n A docstring.\n') + assert_equal(getattr(magic_foo2, 'argcmd_name', None), None) + assert_equal(real_name(magic_foo2), 'foo2') + assert_equal(magic_foo2(None, ''), argparse.Namespace()) + assert hasattr(magic_foo2, 'has_arguments') + + assert_equal(magic_foo3.__doc__, '::\n\n %foo3 [-f FOO] [-b BAR] [-z BAZ]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n\nGroup:\n -b BAR, --bar BAR a grouped argument\n\nSecond Group:\n -z BAZ, --baz BAZ another grouped argument\n') + assert_equal(getattr(magic_foo3, 'argcmd_name', None), None) + assert_equal(real_name(magic_foo3), 'foo3') + assert_equal(magic_foo3(None, ''), + argparse.Namespace(bar=None, baz=None, foo=None)) + assert hasattr(magic_foo3, 'has_arguments') + + assert_equal(magic_foo4.__doc__, '::\n\n %foo4 [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') + assert_equal(getattr(magic_foo4, 'argcmd_name', None), None) + assert_equal(real_name(magic_foo4), 'foo4') + assert_equal(magic_foo4(None, ''), argparse.Namespace()) + assert hasattr(magic_foo4, 'has_arguments') + + assert_equal(magic_foo5.__doc__, '::\n\n %frobnicate [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') + assert_equal(getattr(magic_foo5, 'argcmd_name', None), 'frobnicate') + assert_equal(real_name(magic_foo5), 'frobnicate') + assert_equal(magic_foo5(None, ''), argparse.Namespace(foo=None)) + assert hasattr(magic_foo5, 'has_arguments') + + assert_equal(magic_magic_foo.__doc__, '::\n\n %magic_foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') + assert_equal(getattr(magic_magic_foo, 'argcmd_name', None), None) + assert_equal(real_name(magic_magic_foo), 'magic_foo') + assert_equal(magic_magic_foo(None, ''), argparse.Namespace(foo=None)) + assert hasattr(magic_magic_foo, 'has_arguments') + + assert_equal(foo.__doc__, '::\n\n %foo [-f FOO]\n\n A docstring.\n\noptional arguments:\n -f FOO, --foo FOO an argument\n') + assert_equal(getattr(foo, 'argcmd_name', None), None) + assert_equal(real_name(foo), 'foo') + assert_equal(foo(None, ''), argparse.Namespace(foo=None)) + assert hasattr(foo, 'has_arguments') diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic_terminal.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic_terminal.py new file mode 100644 index 00000000..79e2d3ed --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_magic_terminal.py @@ -0,0 +1,192 @@ +"""Tests for various magic functions specific to the terminal frontend. + +Needs to be run by nose (to make ipython session available). +""" + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import sys +from io import StringIO +from unittest import TestCase + +import nose.tools as nt + +from IPython.testing import tools as tt + +#----------------------------------------------------------------------------- +# Test functions begin +#----------------------------------------------------------------------------- + +def check_cpaste(code, should_fail=False): + """Execute code via 'cpaste' and ensure it was executed, unless + should_fail is set. + """ + ip.user_ns['code_ran'] = False + + src = StringIO() + if not hasattr(src, 'encoding'): + # IPython expects stdin to have an encoding attribute + src.encoding = None + src.write(code) + src.write('\n--\n') + src.seek(0) + + stdin_save = sys.stdin + sys.stdin = src + + try: + context = tt.AssertPrints if should_fail else tt.AssertNotPrints + with context("Traceback (most recent call last)"): + ip.magic('cpaste') + + if not should_fail: + assert ip.user_ns['code_ran'], "%r failed" % code + finally: + sys.stdin = stdin_save + +def test_cpaste(): + """Test cpaste magic""" + + def runf(): + """Marker function: sets a flag when executed. + """ + ip.user_ns['code_ran'] = True + return 'runf' # return string so '+ runf()' doesn't result in success + + tests = {'pass': ["runf()", + "In [1]: runf()", + "In [1]: if 1:\n ...: runf()", + "> > > runf()", + ">>> runf()", + " >>> runf()", + ], + + 'fail': ["1 + runf()", + "++ runf()", + ]} + + ip.user_ns['runf'] = runf + + for code in tests['pass']: + check_cpaste(code) + + for code in tests['fail']: + check_cpaste(code, should_fail=True) + + +class PasteTestCase(TestCase): + """Multiple tests for clipboard pasting""" + + def paste(self, txt, flags='-q'): + """Paste input text, by default in quiet mode""" + ip.hooks.clipboard_get = lambda : txt + ip.magic('paste '+flags) + + def setUp(self): + # Inject fake clipboard hook but save original so we can restore it later + self.original_clip = ip.hooks.clipboard_get + + def tearDown(self): + # Restore original hook + ip.hooks.clipboard_get = self.original_clip + + def test_paste(self): + ip.user_ns.pop('x', None) + self.paste('x = 1') + nt.assert_equal(ip.user_ns['x'], 1) + ip.user_ns.pop('x') + + def test_paste_pyprompt(self): + ip.user_ns.pop('x', None) + self.paste('>>> x=2') + nt.assert_equal(ip.user_ns['x'], 2) + ip.user_ns.pop('x') + + def test_paste_py_multi(self): + self.paste(""" + >>> x = [1,2,3] + >>> y = [] + >>> for i in x: + ... y.append(i**2) + ... + """) + nt.assert_equal(ip.user_ns['x'], [1,2,3]) + nt.assert_equal(ip.user_ns['y'], [1,4,9]) + + def test_paste_py_multi_r(self): + "Now, test that self.paste -r works" + self.test_paste_py_multi() + nt.assert_equal(ip.user_ns.pop('x'), [1,2,3]) + nt.assert_equal(ip.user_ns.pop('y'), [1,4,9]) + nt.assert_false('x' in ip.user_ns) + ip.magic('paste -r') + nt.assert_equal(ip.user_ns['x'], [1,2,3]) + nt.assert_equal(ip.user_ns['y'], [1,4,9]) + + def test_paste_email(self): + "Test pasting of email-quoted contents" + self.paste("""\ + >> def foo(x): + >> return x + 1 + >> xx = foo(1.1)""") + nt.assert_equal(ip.user_ns['xx'], 2.1) + + def test_paste_email2(self): + "Email again; some programs add a space also at each quoting level" + self.paste("""\ + > > def foo(x): + > > return x + 1 + > > yy = foo(2.1) """) + nt.assert_equal(ip.user_ns['yy'], 3.1) + + def test_paste_email_py(self): + "Email quoting of interactive input" + self.paste("""\ + >> >>> def f(x): + >> ... return x+1 + >> ... + >> >>> zz = f(2.5) """) + nt.assert_equal(ip.user_ns['zz'], 3.5) + + def test_paste_echo(self): + "Also test self.paste echoing, by temporarily faking the writer" + w = StringIO() + writer = ip.write + ip.write = w.write + code = """ + a = 100 + b = 200""" + try: + self.paste(code,'') + out = w.getvalue() + finally: + ip.write = writer + nt.assert_equal(ip.user_ns['a'], 100) + nt.assert_equal(ip.user_ns['b'], 200) + assert out == code+"\n## -- End pasted text --\n" + + def test_paste_leading_commas(self): + "Test multiline strings with leading commas" + tm = ip.magics_manager.registry['TerminalMagics'] + s = '''\ +a = """ +,1,2,3 +"""''' + ip.user_ns.pop('foo', None) + tm.store_or_execute(s, 'foo') + nt.assert_in('foo', ip.user_ns) + + + def test_paste_trailing_question(self): + "Test pasting sources with trailing question marks" + tm = ip.magics_manager.registry['TerminalMagics'] + s = '''\ +def funcfoo(): + if True: #am i true? + return 'fooresult' +''' + ip.user_ns.pop('funcfoo', None) + self.paste(s) + nt.assert_equal(ip.user_ns['funcfoo'](), 'fooresult') diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_oinspect.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_oinspect.py new file mode 100644 index 00000000..19c6db7c --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_oinspect.py @@ -0,0 +1,447 @@ +"""Tests for the object inspection functionality. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + + +from inspect import signature, Signature, Parameter +import os +import re + +import nose.tools as nt + +from .. import oinspect + +from decorator import decorator + +from IPython.testing.tools import AssertPrints, AssertNotPrints +from IPython.utils.path import compress_user + + +#----------------------------------------------------------------------------- +# Globals and constants +#----------------------------------------------------------------------------- + +inspector = None + +def setup_module(): + global inspector + inspector = oinspect.Inspector() + + +#----------------------------------------------------------------------------- +# Local utilities +#----------------------------------------------------------------------------- + +# WARNING: since this test checks the line number where a function is +# defined, if any code is inserted above, the following line will need to be +# updated. Do NOT insert any whitespace between the next line and the function +# definition below. +THIS_LINE_NUMBER = 41 # Put here the actual number of this line + +from unittest import TestCase + +class Test(TestCase): + + def test_find_source_lines(self): + self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines), + THIS_LINE_NUMBER+6) + + +# A couple of utilities to ensure these tests work the same from a source or a +# binary install +def pyfile(fname): + return os.path.normcase(re.sub('.py[co]$', '.py', fname)) + + +def match_pyfiles(f1, f2): + nt.assert_equal(pyfile(f1), pyfile(f2)) + + +def test_find_file(): + match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__)) + + +def test_find_file_decorated1(): + + @decorator + def noop1(f): + def wrapper(*a, **kw): + return f(*a, **kw) + return wrapper + + @noop1 + def f(x): + "My docstring" + + match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__)) + nt.assert_equal(f.__doc__, "My docstring") + + +def test_find_file_decorated2(): + + @decorator + def noop2(f, *a, **kw): + return f(*a, **kw) + + @noop2 + @noop2 + @noop2 + def f(x): + "My docstring 2" + + match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__)) + nt.assert_equal(f.__doc__, "My docstring 2") + + +def test_find_file_magic(): + run = ip.find_line_magic('run') + nt.assert_not_equal(oinspect.find_file(run), None) + + +# A few generic objects we can then inspect in the tests below + +class Call(object): + """This is the class docstring.""" + + def __init__(self, x, y=1): + """This is the constructor docstring.""" + + def __call__(self, *a, **kw): + """This is the call docstring.""" + + def method(self, x, z=2): + """Some method's docstring""" + +class HasSignature(object): + """This is the class docstring.""" + __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)]) + + def __init__(self, *args): + """This is the init docstring""" + + +class SimpleClass(object): + def method(self, x, z=2): + """Some method's docstring""" + + +class Awkward(object): + def __getattr__(self, name): + raise Exception(name) + +class NoBoolCall: + """ + callable with `__bool__` raising should still be inspect-able. + """ + + def __call__(self): + """does nothing""" + pass + + def __bool__(self): + """just raise NotImplemented""" + raise NotImplementedError('Must be implemented') + + +class SerialLiar(object): + """Attribute accesses always get another copy of the same class. + + unittest.mock.call does something similar, but it's not ideal for testing + as the failure mode is to eat all your RAM. This gives up after 10k levels. + """ + def __init__(self, max_fibbing_twig, lies_told=0): + if lies_told > 10000: + raise RuntimeError('Nose too long, honesty is the best policy') + self.max_fibbing_twig = max_fibbing_twig + self.lies_told = lies_told + max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told) + + def __getattr__(self, item): + return SerialLiar(self.max_fibbing_twig, self.lies_told + 1) + +#----------------------------------------------------------------------------- +# Tests +#----------------------------------------------------------------------------- + +def test_info(): + "Check that Inspector.info fills out various fields as expected." + i = inspector.info(Call, oname='Call') + nt.assert_equal(i['type_name'], 'type') + expted_class = str(type(type)) # (Python 3) or + nt.assert_equal(i['base_class'], expted_class) + nt.assert_regex(i['string_form'], "") + fname = __file__ + if fname.endswith(".pyc"): + fname = fname[:-1] + # case-insensitive comparison needed on some filesystems + # e.g. Windows: + nt.assert_equal(i['file'].lower(), compress_user(fname).lower()) + nt.assert_equal(i['definition'], None) + nt.assert_equal(i['docstring'], Call.__doc__) + nt.assert_equal(i['source'], None) + nt.assert_true(i['isclass']) + nt.assert_equal(i['init_definition'], "Call(x, y=1)") + nt.assert_equal(i['init_docstring'], Call.__init__.__doc__) + + i = inspector.info(Call, detail_level=1) + nt.assert_not_equal(i['source'], None) + nt.assert_equal(i['docstring'], None) + + c = Call(1) + c.__doc__ = "Modified instance docstring" + i = inspector.info(c) + nt.assert_equal(i['type_name'], 'Call') + nt.assert_equal(i['docstring'], "Modified instance docstring") + nt.assert_equal(i['class_docstring'], Call.__doc__) + nt.assert_equal(i['init_docstring'], Call.__init__.__doc__) + nt.assert_equal(i['call_docstring'], Call.__call__.__doc__) + +def test_class_signature(): + info = inspector.info(HasSignature, 'HasSignature') + nt.assert_equal(info['init_definition'], "HasSignature(test)") + nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__) + +def test_info_awkward(): + # Just test that this doesn't throw an error. + inspector.info(Awkward()) + +def test_bool_raise(): + inspector.info(NoBoolCall()) + +def test_info_serialliar(): + fib_tracker = [0] + inspector.info(SerialLiar(fib_tracker)) + + # Nested attribute access should be cut off at 100 levels deep to avoid + # infinite loops: https://github.com/ipython/ipython/issues/9122 + nt.assert_less(fib_tracker[0], 9000) + +def support_function_one(x, y=2, *a, **kw): + """A simple function.""" + +def test_calldef_none(): + # We should ignore __call__ for all of these. + for obj in [support_function_one, SimpleClass().method, any, str.upper]: + i = inspector.info(obj) + nt.assert_is(i['call_def'], None) + +def f_kwarg(pos, *, kwonly): + pass + +def test_definition_kwonlyargs(): + i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore + nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)") + +def test_getdoc(): + class A(object): + """standard docstring""" + pass + + class B(object): + """standard docstring""" + def getdoc(self): + return "custom docstring" + + class C(object): + """standard docstring""" + def getdoc(self): + return None + + a = A() + b = B() + c = C() + + nt.assert_equal(oinspect.getdoc(a), "standard docstring") + nt.assert_equal(oinspect.getdoc(b), "custom docstring") + nt.assert_equal(oinspect.getdoc(c), "standard docstring") + + +def test_empty_property_has_no_source(): + i = inspector.info(property(), detail_level=1) + nt.assert_is(i['source'], None) + + +def test_property_sources(): + import posixpath + # A simple adder whose source and signature stays + # the same across Python distributions + def simple_add(a, b): + "Adds two numbers" + return a + b + + class A(object): + @property + def foo(self): + return 'bar' + + foo = foo.setter(lambda self, v: setattr(self, 'bar', v)) + + dname = property(posixpath.dirname) + adder = property(simple_add) + + i = inspector.info(A.foo, detail_level=1) + nt.assert_in('def foo(self):', i['source']) + nt.assert_in('lambda self, v:', i['source']) + + i = inspector.info(A.dname, detail_level=1) + nt.assert_in('def dirname(p)', i['source']) + + i = inspector.info(A.adder, detail_level=1) + nt.assert_in('def simple_add(a, b)', i['source']) + + +def test_property_docstring_is_in_info_for_detail_level_0(): + class A(object): + @property + def foobar(self): + """This is `foobar` property.""" + pass + + ip.user_ns['a_obj'] = A() + nt.assert_equal( + 'This is `foobar` property.', + ip.object_inspect('a_obj.foobar', detail_level=0)['docstring']) + + ip.user_ns['a_cls'] = A + nt.assert_equal( + 'This is `foobar` property.', + ip.object_inspect('a_cls.foobar', detail_level=0)['docstring']) + + +def test_pdef(): + # See gh-1914 + def foo(): pass + inspector.pdef(foo, 'foo') + + +def test_pinfo_nonascii(): + # See gh-1177 + from . import nonascii2 + ip.user_ns['nonascii2'] = nonascii2 + ip._inspect('pinfo', 'nonascii2', detail_level=1) + +def test_pinfo_type(): + """ + type can fail in various edge case, for example `type.__subclass__()` + """ + ip._inspect('pinfo', 'type') + + +def test_pinfo_docstring_no_source(): + """Docstring should be included with detail_level=1 if there is no source""" + with AssertPrints('Docstring:'): + ip._inspect('pinfo', 'str.format', detail_level=0) + with AssertPrints('Docstring:'): + ip._inspect('pinfo', 'str.format', detail_level=1) + + +def test_pinfo_no_docstring_if_source(): + """Docstring should not be included with detail_level=1 if source is found""" + def foo(): + """foo has a docstring""" + + ip.user_ns['foo'] = foo + + with AssertPrints('Docstring:'): + ip._inspect('pinfo', 'foo', detail_level=0) + with AssertPrints('Source:'): + ip._inspect('pinfo', 'foo', detail_level=1) + with AssertNotPrints('Docstring:'): + ip._inspect('pinfo', 'foo', detail_level=1) + + +def test_pinfo_docstring_if_detail_and_no_source(): + """ Docstring should be displayed if source info not available """ + obj_def = '''class Foo(object): + """ This is a docstring for Foo """ + def bar(self): + """ This is a docstring for Foo.bar """ + pass + ''' + + ip.run_cell(obj_def) + ip.run_cell('foo = Foo()') + + with AssertNotPrints("Source:"): + with AssertPrints('Docstring:'): + ip._inspect('pinfo', 'foo', detail_level=0) + with AssertPrints('Docstring:'): + ip._inspect('pinfo', 'foo', detail_level=1) + with AssertPrints('Docstring:'): + ip._inspect('pinfo', 'foo.bar', detail_level=0) + + with AssertNotPrints('Docstring:'): + with AssertPrints('Source:'): + ip._inspect('pinfo', 'foo.bar', detail_level=1) + + +def test_pinfo_magic(): + with AssertPrints('Docstring:'): + ip._inspect('pinfo', 'lsmagic', detail_level=0) + + with AssertPrints('Source:'): + ip._inspect('pinfo', 'lsmagic', detail_level=1) + + +def test_init_colors(): + # ensure colors are not present in signature info + info = inspector.info(HasSignature) + init_def = info['init_definition'] + nt.assert_not_in('[0m', init_def) + + +def test_builtin_init(): + info = inspector.info(list) + init_def = info['init_definition'] + nt.assert_is_not_none(init_def) + + +def test_render_signature_short(): + def short_fun(a=1): pass + sig = oinspect._render_signature( + signature(short_fun), + short_fun.__name__, + ) + nt.assert_equal(sig, 'short_fun(a=1)') + + +def test_render_signature_long(): + from typing import Optional + + def long_function( + a_really_long_parameter: int, + and_another_long_one: bool = False, + let_us_make_sure_this_is_looong: Optional[str] = None, + ) -> bool: pass + + sig = oinspect._render_signature( + signature(long_function), + long_function.__name__, + ) + nt.assert_in(sig, [ + # Python >=3.9 + '''\ +long_function( + a_really_long_parameter: int, + and_another_long_one: bool = False, + let_us_make_sure_this_is_looong: Optional[str] = None, +) -> bool\ +''', + # Python >=3.7 + '''\ +long_function( + a_really_long_parameter: int, + and_another_long_one: bool = False, + let_us_make_sure_this_is_looong: Union[str, NoneType] = None, +) -> bool\ +''', # Python <=3.6 + '''\ +long_function( + a_really_long_parameter:int, + and_another_long_one:bool=False, + let_us_make_sure_this_is_looong:Union[str, NoneType]=None, +) -> bool\ +''', + ]) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_page.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_page.py new file mode 100644 index 00000000..9f6a3742 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_page.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (C) 2010-2011 The IPython Development Team. +# +# Distributed under the terms of the BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- +import io + +# N.B. For the test suite, page.page is overridden (see IPython.testing.globalipapp) +from IPython.core import page + +def test_detect_screen_size(): + """Simple smoketest for page._detect_screen_size.""" + try: + page._detect_screen_size(True, 25) + except (TypeError, io.UnsupportedOperation): + # This can happen in the test suite, because stdout may not have a + # fileno. + pass diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_paths.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_paths.py new file mode 100644 index 00000000..ab1c4132 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_paths.py @@ -0,0 +1,200 @@ +import errno +import os +import shutil +import sys +import tempfile +import warnings +from unittest.mock import patch + +import nose.tools as nt +from testpath import modified_env, assert_isdir, assert_isfile + +from IPython import paths +from IPython.testing.decorators import skip_win32 +from IPython.utils.tempdir import TemporaryDirectory + +TMP_TEST_DIR = os.path.realpath(tempfile.mkdtemp()) +HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir") +XDG_TEST_DIR = os.path.join(HOME_TEST_DIR, "xdg_test_dir") +XDG_CACHE_DIR = os.path.join(HOME_TEST_DIR, "xdg_cache_dir") +IP_TEST_DIR = os.path.join(HOME_TEST_DIR,'.ipython') + +def setup_module(): + """Setup testenvironment for the module: + + - Adds dummy home dir tree + """ + # Do not mask exceptions here. In particular, catching WindowsError is a + # problem because that exception is only defined on Windows... + os.makedirs(IP_TEST_DIR) + os.makedirs(os.path.join(XDG_TEST_DIR, 'ipython')) + os.makedirs(os.path.join(XDG_CACHE_DIR, 'ipython')) + + +def teardown_module(): + """Teardown testenvironment for the module: + + - Remove dummy home dir tree + """ + # Note: we remove the parent test dir, which is the root of all test + # subdirs we may have created. Use shutil instead of os.removedirs, so + # that non-empty directories are all recursively removed. + shutil.rmtree(TMP_TEST_DIR) + +def patch_get_home_dir(dirpath): + return patch.object(paths, 'get_home_dir', return_value=dirpath) + + +def test_get_ipython_dir_1(): + """test_get_ipython_dir_1, Testcase to see if we can call get_ipython_dir without Exceptions.""" + env_ipdir = os.path.join("someplace", ".ipython") + with patch.object(paths, '_writable_dir', return_value=True), \ + modified_env({'IPYTHONDIR': env_ipdir}): + ipdir = paths.get_ipython_dir() + + nt.assert_equal(ipdir, env_ipdir) + +def test_get_ipython_dir_2(): + """test_get_ipython_dir_2, Testcase to see if we can call get_ipython_dir without Exceptions.""" + with patch_get_home_dir('someplace'), \ + patch.object(paths, 'get_xdg_dir', return_value=None), \ + patch.object(paths, '_writable_dir', return_value=True), \ + patch('os.name', "posix"), \ + modified_env({'IPYTHON_DIR': None, + 'IPYTHONDIR': None, + 'XDG_CONFIG_HOME': None + }): + ipdir = paths.get_ipython_dir() + + nt.assert_equal(ipdir, os.path.join("someplace", ".ipython")) + +def test_get_ipython_dir_3(): + """test_get_ipython_dir_3, move XDG if defined, and .ipython doesn't exist.""" + tmphome = TemporaryDirectory() + try: + with patch_get_home_dir(tmphome.name), \ + patch('os.name', 'posix'), \ + modified_env({ + 'IPYTHON_DIR': None, + 'IPYTHONDIR': None, + 'XDG_CONFIG_HOME': XDG_TEST_DIR, + }), warnings.catch_warnings(record=True) as w: + ipdir = paths.get_ipython_dir() + + nt.assert_equal(ipdir, os.path.join(tmphome.name, ".ipython")) + if sys.platform != 'darwin': + nt.assert_equal(len(w), 1) + nt.assert_in('Moving', str(w[0])) + finally: + tmphome.cleanup() + +def test_get_ipython_dir_4(): + """test_get_ipython_dir_4, warn if XDG and home both exist.""" + with patch_get_home_dir(HOME_TEST_DIR), \ + patch('os.name', 'posix'): + try: + os.mkdir(os.path.join(XDG_TEST_DIR, 'ipython')) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + + with modified_env({ + 'IPYTHON_DIR': None, + 'IPYTHONDIR': None, + 'XDG_CONFIG_HOME': XDG_TEST_DIR, + }), warnings.catch_warnings(record=True) as w: + ipdir = paths.get_ipython_dir() + + nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, ".ipython")) + if sys.platform != 'darwin': + nt.assert_equal(len(w), 1) + nt.assert_in('Ignoring', str(w[0])) + +def test_get_ipython_dir_5(): + """test_get_ipython_dir_5, use .ipython if exists and XDG defined, but doesn't exist.""" + with patch_get_home_dir(HOME_TEST_DIR), \ + patch('os.name', 'posix'): + try: + os.rmdir(os.path.join(XDG_TEST_DIR, 'ipython')) + except OSError as e: + if e.errno != errno.ENOENT: + raise + + with modified_env({ + 'IPYTHON_DIR': None, + 'IPYTHONDIR': None, + 'XDG_CONFIG_HOME': XDG_TEST_DIR, + }): + ipdir = paths.get_ipython_dir() + + nt.assert_equal(ipdir, IP_TEST_DIR) + +def test_get_ipython_dir_6(): + """test_get_ipython_dir_6, use home over XDG if defined and neither exist.""" + xdg = os.path.join(HOME_TEST_DIR, 'somexdg') + os.mkdir(xdg) + shutil.rmtree(os.path.join(HOME_TEST_DIR, '.ipython')) + print(paths._writable_dir) + with patch_get_home_dir(HOME_TEST_DIR), \ + patch.object(paths, 'get_xdg_dir', return_value=xdg), \ + patch('os.name', 'posix'), \ + modified_env({ + 'IPYTHON_DIR': None, + 'IPYTHONDIR': None, + 'XDG_CONFIG_HOME': None, + }), warnings.catch_warnings(record=True) as w: + ipdir = paths.get_ipython_dir() + + nt.assert_equal(ipdir, os.path.join(HOME_TEST_DIR, '.ipython')) + nt.assert_equal(len(w), 0) + +def test_get_ipython_dir_7(): + """test_get_ipython_dir_7, test home directory expansion on IPYTHONDIR""" + home_dir = os.path.normpath(os.path.expanduser('~')) + with modified_env({'IPYTHONDIR': os.path.join('~', 'somewhere')}), \ + patch.object(paths, '_writable_dir', return_value=True): + ipdir = paths.get_ipython_dir() + nt.assert_equal(ipdir, os.path.join(home_dir, 'somewhere')) + +@skip_win32 +def test_get_ipython_dir_8(): + """test_get_ipython_dir_8, test / home directory""" + with patch.object(paths, '_writable_dir', lambda path: bool(path)), \ + patch.object(paths, 'get_xdg_dir', return_value=None), \ + modified_env({ + 'IPYTHON_DIR': None, + 'IPYTHONDIR': None, + 'HOME': '/', + }): + nt.assert_equal(paths.get_ipython_dir(), '/.ipython') + + +def test_get_ipython_cache_dir(): + with modified_env({'HOME': HOME_TEST_DIR}): + if os.name == 'posix' and sys.platform != 'darwin': + # test default + os.makedirs(os.path.join(HOME_TEST_DIR, ".cache")) + with modified_env({'XDG_CACHE_HOME': None}): + ipdir = paths.get_ipython_cache_dir() + nt.assert_equal(os.path.join(HOME_TEST_DIR, ".cache", "ipython"), + ipdir) + assert_isdir(ipdir) + + # test env override + with modified_env({"XDG_CACHE_HOME": XDG_CACHE_DIR}): + ipdir = paths.get_ipython_cache_dir() + assert_isdir(ipdir) + nt.assert_equal(ipdir, os.path.join(XDG_CACHE_DIR, "ipython")) + else: + nt.assert_equal(paths.get_ipython_cache_dir(), + paths.get_ipython_dir()) + +def test_get_ipython_package_dir(): + ipdir = paths.get_ipython_package_dir() + assert_isdir(ipdir) + + +def test_get_ipython_module_path(): + ipapp_path = paths.get_ipython_module_path('IPython.terminal.ipapp') + assert_isfile(ipapp_path) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_prefilter.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_prefilter.py new file mode 100644 index 00000000..ca447b3d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_prefilter.py @@ -0,0 +1,127 @@ +"""Tests for input manipulation machinery.""" + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +import nose.tools as nt + +from IPython.core.prefilter import AutocallChecker + +#----------------------------------------------------------------------------- +# Tests +#----------------------------------------------------------------------------- + +def test_prefilter(): + """Test user input conversions""" + + # pairs of (raw, expected correct) input + pairs = [ ('2+2','2+2'), + ] + + for raw, correct in pairs: + nt.assert_equal(ip.prefilter(raw), correct) + +def test_prefilter_shadowed(): + def dummy_magic(line): pass + + prev_automagic_state = ip.automagic + ip.automagic = True + ip.autocall = 0 + + try: + # These should not be transformed - they are shadowed by other names + for name in ['if', 'zip', 'get_ipython']: # keyword, builtin, global + ip.register_magic_function(dummy_magic, magic_name=name) + res = ip.prefilter(name+' foo') + nt.assert_equal(res, name+' foo') + del ip.magics_manager.magics['line'][name] + + # These should be transformed + for name in ['fi', 'piz', 'nohtypi_teg']: + ip.register_magic_function(dummy_magic, magic_name=name) + res = ip.prefilter(name+' foo') + nt.assert_not_equal(res, name+' foo') + del ip.magics_manager.magics['line'][name] + + finally: + ip.automagic = prev_automagic_state + +def test_autocall_binops(): + """See https://github.com/ipython/ipython/issues/81""" + ip.magic('autocall 2') + f = lambda x: x + ip.user_ns['f'] = f + try: + nt.assert_equal(ip.prefilter('f 1'),'f(1)') + for t in ['f +1', 'f -1']: + nt.assert_equal(ip.prefilter(t), t) + + # Run tests again with a more permissive exclude_regexp, which will + # allow transformation of binary operations ('f -1' -> 'f(-1)'). + pm = ip.prefilter_manager + ac = AutocallChecker(shell=pm.shell, prefilter_manager=pm, + config=pm.config) + try: + ac.priority = 1 + ac.exclude_regexp = r'^[,&^\|\*/]|^is |^not |^in |^and |^or ' + pm.sort_checkers() + + nt.assert_equal(ip.prefilter('f -1'), 'f(-1)') + nt.assert_equal(ip.prefilter('f +1'), 'f(+1)') + finally: + pm.unregister_checker(ac) + finally: + ip.magic('autocall 0') + del ip.user_ns['f'] + + +def test_issue_114(): + """Check that multiline string literals don't expand as magic + see http://github.com/ipython/ipython/issues/114""" + + template = '"""\n%s\n"""' + # Store the current value of multi_line_specials and turn it off before + # running test, since it could be true (case in which the test doesn't make + # sense, as multiline string literals *will* expand as magic in that case). + msp = ip.prefilter_manager.multi_line_specials + ip.prefilter_manager.multi_line_specials = False + try: + for mgk in ip.magics_manager.lsmagic()['line']: + raw = template % mgk + nt.assert_equal(ip.prefilter(raw), raw) + finally: + ip.prefilter_manager.multi_line_specials = msp + + +def test_prefilter_attribute_errors(): + """Capture exceptions thrown by user objects on attribute access. + + See http://github.com/ipython/ipython/issues/988.""" + + class X(object): + def __getattr__(self, k): + raise ValueError('broken object') + def __call__(self, x): + return x + + # Create a callable broken object + ip.user_ns['x'] = X() + ip.magic('autocall 2') + try: + # Even if x throws an attribute error when looking at its rewrite + # attribute, we should not crash. So the test here is simply making + # the prefilter call and not having an exception. + ip.prefilter('x 1') + finally: + del ip.user_ns['x'] + ip.magic('autocall 0') + + +def test_autocall_should_support_unicode(): + ip.magic('autocall 2') + ip.user_ns['π'] = lambda x: x + try: + nt.assert_equal(ip.prefilter('π 3'),'π(3)') + finally: + ip.magic('autocall 0') + del ip.user_ns['π'] diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_profile.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_profile.py new file mode 100644 index 00000000..e63fb3ef --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_profile.py @@ -0,0 +1,161 @@ +# coding: utf-8 +"""Tests for profile-related functions. + +Currently only the startup-dir functionality is tested, but more tests should +be added for: + + * ipython profile create + * ipython profile list + * ipython profile create --parallel + * security dir permissions + +Authors +------- + +* MinRK + +""" + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os +import shutil +import sys +import tempfile + +from unittest import TestCase + +import nose.tools as nt + +from IPython.core.profileapp import list_profiles_in, list_bundled_profiles +from IPython.core.profiledir import ProfileDir + +from IPython.testing import decorators as dec +from IPython.testing import tools as tt +from IPython.utils.process import getoutput +from IPython.utils.tempdir import TemporaryDirectory + +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- +TMP_TEST_DIR = tempfile.mkdtemp() +HOME_TEST_DIR = os.path.join(TMP_TEST_DIR, "home_test_dir") +IP_TEST_DIR = os.path.join(HOME_TEST_DIR,'.ipython') + +# +# Setup/teardown functions/decorators +# + +def setup_module(): + """Setup test environment for the module: + + - Adds dummy home dir tree + """ + # Do not mask exceptions here. In particular, catching WindowsError is a + # problem because that exception is only defined on Windows... + os.makedirs(IP_TEST_DIR) + + +def teardown_module(): + """Teardown test environment for the module: + + - Remove dummy home dir tree + """ + # Note: we remove the parent test dir, which is the root of all test + # subdirs we may have created. Use shutil instead of os.removedirs, so + # that non-empty directories are all recursively removed. + shutil.rmtree(TMP_TEST_DIR) + + +#----------------------------------------------------------------------------- +# Test functions +#----------------------------------------------------------------------------- +def win32_without_pywin32(): + if sys.platform == 'win32': + try: + import pywin32 + except ImportError: + return True + return False + + +class ProfileStartupTest(TestCase): + def setUp(self): + # create profile dir + self.pd = ProfileDir.create_profile_dir_by_name(IP_TEST_DIR, 'test') + self.options = ['--ipython-dir', IP_TEST_DIR, '--profile', 'test'] + self.fname = os.path.join(TMP_TEST_DIR, 'test.py') + + def tearDown(self): + # We must remove this profile right away so its presence doesn't + # confuse other tests. + shutil.rmtree(self.pd.location) + + def init(self, startup_file, startup, test): + # write startup python file + with open(os.path.join(self.pd.startup_dir, startup_file), 'w') as f: + f.write(startup) + # write simple test file, to check that the startup file was run + with open(self.fname, 'w') as f: + f.write(test) + + def validate(self, output): + tt.ipexec_validate(self.fname, output, '', options=self.options) + + @dec.skipif(win32_without_pywin32(), "Test requires pywin32 on Windows") + def test_startup_py(self): + self.init('00-start.py', 'zzz=123\n', 'print(zzz)\n') + self.validate('123') + + @dec.skipif(win32_without_pywin32(), "Test requires pywin32 on Windows") + def test_startup_ipy(self): + self.init('00-start.ipy', '%xmode plain\n', '') + self.validate('Exception reporting mode: Plain') + + +def test_list_profiles_in(): + # No need to remove these directories and files, as they will get nuked in + # the module-level teardown. + td = tempfile.mkdtemp(dir=TMP_TEST_DIR) + for name in ('profile_foo', 'profile_hello', 'not_a_profile'): + os.mkdir(os.path.join(td, name)) + if dec.unicode_paths: + os.mkdir(os.path.join(td, u'profile_ünicode')) + + with open(os.path.join(td, 'profile_file'), 'w') as f: + f.write("I am not a profile directory") + profiles = list_profiles_in(td) + + # unicode normalization can turn u'ünicode' into u'u\0308nicode', + # so only check for *nicode, and that creating a ProfileDir from the + # name remains valid + found_unicode = False + for p in list(profiles): + if p.endswith('nicode'): + pd = ProfileDir.find_profile_dir_by_name(td, p) + profiles.remove(p) + found_unicode = True + break + if dec.unicode_paths: + nt.assert_true(found_unicode) + nt.assert_equal(set(profiles), {'foo', 'hello'}) + + +def test_list_bundled_profiles(): + # This variable will need to be updated when a new profile gets bundled + bundled = sorted(list_bundled_profiles()) + nt.assert_equal(bundled, []) + + +def test_profile_create_ipython_dir(): + """ipython profile create respects --ipython-dir""" + with TemporaryDirectory() as td: + getoutput([sys.executable, '-m', 'IPython', 'profile', 'create', + 'foo', '--ipython-dir=%s' % td]) + profile_dir = os.path.join(td, 'profile_foo') + assert os.path.exists(profile_dir) + ipython_config = os.path.join(profile_dir, 'ipython_config.py') + assert os.path.exists(ipython_config) + diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_prompts.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_prompts.py new file mode 100644 index 00000000..95e6163b --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_prompts.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 +"""Tests for prompt generation.""" + +import unittest + +from IPython.core.prompts import LazyEvaluate + +class PromptTests(unittest.TestCase): + def test_lazy_eval_unicode(self): + u = u'ünicødé' + lz = LazyEvaluate(lambda : u) + self.assertEqual(str(lz), u) + self.assertEqual(format(lz), u) + + def test_lazy_eval_nonascii_bytes(self): + u = u'ünicødé' + b = u.encode('utf8') + lz = LazyEvaluate(lambda : b) + # unicode(lz) would fail + self.assertEqual(str(lz), str(b)) + self.assertEqual(format(lz), str(b)) + + def test_lazy_eval_float(self): + f = 0.503 + lz = LazyEvaluate(lambda : f) + + self.assertEqual(str(lz), str(f)) + self.assertEqual(format(lz), str(f)) + self.assertEqual(format(lz, '.1'), '0.5') + diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_pylabtools.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_pylabtools.py new file mode 100644 index 00000000..7b64aab1 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_pylabtools.py @@ -0,0 +1,256 @@ +"""Tests for pylab tools module. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + + +from io import UnsupportedOperation, BytesIO + +import matplotlib +matplotlib.use('Agg') +from matplotlib.figure import Figure + +from nose import SkipTest +import nose.tools as nt + +from matplotlib import pyplot as plt +import numpy as np + +from IPython.core.getipython import get_ipython +from IPython.core.interactiveshell import InteractiveShell +from IPython.core.display import _PNG, _JPEG +from .. import pylabtools as pt + +from IPython.testing import decorators as dec + + +def test_figure_to_svg(): + # simple empty-figure test + fig = plt.figure() + nt.assert_equal(pt.print_figure(fig, 'svg'), None) + + plt.close('all') + + # simple check for at least svg-looking output + fig = plt.figure() + ax = fig.add_subplot(1,1,1) + ax.plot([1,2,3]) + plt.draw() + svg = pt.print_figure(fig, 'svg')[:100].lower() + nt.assert_in(u'doctype svg', svg) + +def _check_pil_jpeg_bytes(): + """Skip if PIL can't write JPEGs to BytesIO objects""" + # PIL's JPEG plugin can't write to BytesIO objects + # Pillow fixes this + from PIL import Image + buf = BytesIO() + img = Image.new("RGB", (4,4)) + try: + img.save(buf, 'jpeg') + except Exception as e: + ename = e.__class__.__name__ + raise SkipTest("PIL can't write JPEG to BytesIO: %s: %s" % (ename, e)) + +@dec.skip_without("PIL.Image") +def test_figure_to_jpeg(): + _check_pil_jpeg_bytes() + # simple check for at least jpeg-looking output + fig = plt.figure() + ax = fig.add_subplot(1,1,1) + ax.plot([1,2,3]) + plt.draw() + jpeg = pt.print_figure(fig, 'jpeg', pil_kwargs={'optimize': 50})[:100].lower() + assert jpeg.startswith(_JPEG) + +def test_retina_figure(): + # simple empty-figure test + fig = plt.figure() + nt.assert_equal(pt.retina_figure(fig), None) + plt.close('all') + + fig = plt.figure() + ax = fig.add_subplot(1,1,1) + ax.plot([1,2,3]) + plt.draw() + png, md = pt.retina_figure(fig) + assert png.startswith(_PNG) + nt.assert_in('width', md) + nt.assert_in('height', md) + +_fmt_mime_map = { + 'png': 'image/png', + 'jpeg': 'image/jpeg', + 'pdf': 'application/pdf', + 'retina': 'image/png', + 'svg': 'image/svg+xml', +} + +def test_select_figure_formats_str(): + ip = get_ipython() + for fmt, active_mime in _fmt_mime_map.items(): + pt.select_figure_formats(ip, fmt) + for mime, f in ip.display_formatter.formatters.items(): + if mime == active_mime: + nt.assert_in(Figure, f) + else: + nt.assert_not_in(Figure, f) + +def test_select_figure_formats_kwargs(): + ip = get_ipython() + kwargs = dict(quality=10, bbox_inches='tight') + pt.select_figure_formats(ip, 'png', **kwargs) + formatter = ip.display_formatter.formatters['image/png'] + f = formatter.lookup_by_type(Figure) + cell = f.__closure__[0].cell_contents + nt.assert_equal(cell, kwargs) + + # check that the formatter doesn't raise + fig = plt.figure() + ax = fig.add_subplot(1,1,1) + ax.plot([1,2,3]) + plt.draw() + formatter.enabled = True + png = formatter(fig) + assert png.startswith(_PNG) + +def test_select_figure_formats_set(): + ip = get_ipython() + for fmts in [ + {'png', 'svg'}, + ['png'], + ('jpeg', 'pdf', 'retina'), + {'svg'}, + ]: + active_mimes = {_fmt_mime_map[fmt] for fmt in fmts} + pt.select_figure_formats(ip, fmts) + for mime, f in ip.display_formatter.formatters.items(): + if mime in active_mimes: + nt.assert_in(Figure, f) + else: + nt.assert_not_in(Figure, f) + +def test_select_figure_formats_bad(): + ip = get_ipython() + with nt.assert_raises(ValueError): + pt.select_figure_formats(ip, 'foo') + with nt.assert_raises(ValueError): + pt.select_figure_formats(ip, {'png', 'foo'}) + with nt.assert_raises(ValueError): + pt.select_figure_formats(ip, ['retina', 'pdf', 'bar', 'bad']) + +def test_import_pylab(): + ns = {} + pt.import_pylab(ns, import_all=False) + nt.assert_true('plt' in ns) + nt.assert_equal(ns['np'], np) + +class TestPylabSwitch(object): + class Shell(InteractiveShell): + def enable_gui(self, gui): + pass + + def setup(self): + import matplotlib + def act_mpl(backend): + matplotlib.rcParams['backend'] = backend + + # Save rcParams since they get modified + self._saved_rcParams = matplotlib.rcParams + self._saved_rcParamsOrig = matplotlib.rcParamsOrig + matplotlib.rcParams = dict(backend='Qt4Agg') + matplotlib.rcParamsOrig = dict(backend='Qt4Agg') + + # Mock out functions + self._save_am = pt.activate_matplotlib + pt.activate_matplotlib = act_mpl + self._save_ip = pt.import_pylab + pt.import_pylab = lambda *a,**kw:None + self._save_cis = pt.configure_inline_support + pt.configure_inline_support = lambda *a,**kw:None + + def teardown(self): + pt.activate_matplotlib = self._save_am + pt.import_pylab = self._save_ip + pt.configure_inline_support = self._save_cis + import matplotlib + matplotlib.rcParams = self._saved_rcParams + matplotlib.rcParamsOrig = self._saved_rcParamsOrig + + def test_qt(self): + s = self.Shell() + gui, backend = s.enable_matplotlib(None) + nt.assert_equal(gui, 'qt') + nt.assert_equal(s.pylab_gui_select, 'qt') + + gui, backend = s.enable_matplotlib('inline') + nt.assert_equal(gui, 'inline') + nt.assert_equal(s.pylab_gui_select, 'qt') + + gui, backend = s.enable_matplotlib('qt') + nt.assert_equal(gui, 'qt') + nt.assert_equal(s.pylab_gui_select, 'qt') + + gui, backend = s.enable_matplotlib('inline') + nt.assert_equal(gui, 'inline') + nt.assert_equal(s.pylab_gui_select, 'qt') + + gui, backend = s.enable_matplotlib() + nt.assert_equal(gui, 'qt') + nt.assert_equal(s.pylab_gui_select, 'qt') + + def test_inline(self): + s = self.Shell() + gui, backend = s.enable_matplotlib('inline') + nt.assert_equal(gui, 'inline') + nt.assert_equal(s.pylab_gui_select, None) + + gui, backend = s.enable_matplotlib('inline') + nt.assert_equal(gui, 'inline') + nt.assert_equal(s.pylab_gui_select, None) + + gui, backend = s.enable_matplotlib('qt') + nt.assert_equal(gui, 'qt') + nt.assert_equal(s.pylab_gui_select, 'qt') + + def test_inline_twice(self): + "Using '%matplotlib inline' twice should not reset formatters" + + ip = self.Shell() + gui, backend = ip.enable_matplotlib('inline') + nt.assert_equal(gui, 'inline') + + fmts = {'png'} + active_mimes = {_fmt_mime_map[fmt] for fmt in fmts} + pt.select_figure_formats(ip, fmts) + + gui, backend = ip.enable_matplotlib('inline') + nt.assert_equal(gui, 'inline') + + for mime, f in ip.display_formatter.formatters.items(): + if mime in active_mimes: + nt.assert_in(Figure, f) + else: + nt.assert_not_in(Figure, f) + + def test_qt_gtk(self): + s = self.Shell() + gui, backend = s.enable_matplotlib('qt') + nt.assert_equal(gui, 'qt') + nt.assert_equal(s.pylab_gui_select, 'qt') + + gui, backend = s.enable_matplotlib('gtk') + nt.assert_equal(gui, 'qt') + nt.assert_equal(s.pylab_gui_select, 'qt') + + +def test_no_gui_backends(): + for k in ['agg', 'svg', 'pdf', 'ps']: + assert k not in pt.backend2gui + + +def test_figure_no_canvas(): + fig = Figure() + fig.canvas = None + pt.print_figure(fig) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_run.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_run.py new file mode 100644 index 00000000..eff832b3 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_run.py @@ -0,0 +1,607 @@ +# encoding: utf-8 +"""Tests for code execution (%run and related), which is particularly tricky. + +Because of how %run manages namespaces, and the fact that we are trying here to +verify subtle object deletion and reference counting issues, the %run tests +will be kept in this separate file. This makes it easier to aggregate in one +place the tricks needed to handle it; most other magics are much easier to test +and we do so in a common test_magic file. + +Note that any test using `run -i` should make sure to do a `reset` afterwards, +as otherwise it may influence later tests. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + + + +import functools +import os +from os.path import join as pjoin +import random +import string +import sys +import textwrap +import unittest +from unittest.mock import patch + +import nose.tools as nt +from nose import SkipTest + +from IPython.testing import decorators as dec +from IPython.testing import tools as tt +from IPython.utils.io import capture_output +from IPython.utils.tempdir import TemporaryDirectory +from IPython.core import debugger + +def doctest_refbug(): + """Very nasty problem with references held by multiple runs of a script. + See: https://github.com/ipython/ipython/issues/141 + + In [1]: _ip.clear_main_mod_cache() + # random + + In [2]: %run refbug + + In [3]: call_f() + lowercased: hello + + In [4]: %run refbug + + In [5]: call_f() + lowercased: hello + lowercased: hello + """ + + +def doctest_run_builtins(): + r"""Check that %run doesn't damage __builtins__. + + In [1]: import tempfile + + In [2]: bid1 = id(__builtins__) + + In [3]: fname = tempfile.mkstemp('.py')[1] + + In [3]: f = open(fname,'w') + + In [4]: dummy= f.write('pass\n') + + In [5]: f.flush() + + In [6]: t1 = type(__builtins__) + + In [7]: %run $fname + + In [7]: f.close() + + In [8]: bid2 = id(__builtins__) + + In [9]: t2 = type(__builtins__) + + In [10]: t1 == t2 + Out[10]: True + + In [10]: bid1 == bid2 + Out[10]: True + + In [12]: try: + ....: os.unlink(fname) + ....: except: + ....: pass + ....: + """ + + +def doctest_run_option_parser(): + r"""Test option parser in %run. + + In [1]: %run print_argv.py + [] + + In [2]: %run print_argv.py print*.py + ['print_argv.py'] + + In [3]: %run -G print_argv.py print*.py + ['print*.py'] + + """ + + +@dec.skip_win32 +def doctest_run_option_parser_for_posix(): + r"""Test option parser in %run (Linux/OSX specific). + + You need double quote to escape glob in POSIX systems: + + In [1]: %run print_argv.py print\\*.py + ['print*.py'] + + You can't use quote to escape glob in POSIX systems: + + In [2]: %run print_argv.py 'print*.py' + ['print_argv.py'] + + """ + + +@dec.skip_if_not_win32 +def doctest_run_option_parser_for_windows(): + r"""Test option parser in %run (Windows specific). + + In Windows, you can't escape ``*` `by backslash: + + In [1]: %run print_argv.py print\\*.py + ['print\\*.py'] + + You can use quote to escape glob: + + In [2]: %run print_argv.py 'print*.py' + ['print*.py'] + + """ + + +def doctest_reset_del(): + """Test that resetting doesn't cause errors in __del__ methods. + + In [2]: class A(object): + ...: def __del__(self): + ...: print(str("Hi")) + ...: + + In [3]: a = A() + + In [4]: get_ipython().reset() + Hi + + In [5]: 1+1 + Out[5]: 2 + """ + +# For some tests, it will be handy to organize them in a class with a common +# setup that makes a temp file + +class TestMagicRunPass(tt.TempFileMixin): + + def setUp(self): + content = "a = [1,2,3]\nb = 1" + self.mktmp(content) + + def run_tmpfile(self): + _ip = get_ipython() + # This fails on Windows if self.tmpfile.name has spaces or "~" in it. + # See below and ticket https://bugs.launchpad.net/bugs/366353 + _ip.magic('run %s' % self.fname) + + def run_tmpfile_p(self): + _ip = get_ipython() + # This fails on Windows if self.tmpfile.name has spaces or "~" in it. + # See below and ticket https://bugs.launchpad.net/bugs/366353 + _ip.magic('run -p %s' % self.fname) + + def test_builtins_id(self): + """Check that %run doesn't damage __builtins__ """ + _ip = get_ipython() + # Test that the id of __builtins__ is not modified by %run + bid1 = id(_ip.user_ns['__builtins__']) + self.run_tmpfile() + bid2 = id(_ip.user_ns['__builtins__']) + nt.assert_equal(bid1, bid2) + + def test_builtins_type(self): + """Check that the type of __builtins__ doesn't change with %run. + + However, the above could pass if __builtins__ was already modified to + be a dict (it should be a module) by a previous use of %run. So we + also check explicitly that it really is a module: + """ + _ip = get_ipython() + self.run_tmpfile() + nt.assert_equal(type(_ip.user_ns['__builtins__']),type(sys)) + + def test_run_profile( self ): + """Test that the option -p, which invokes the profiler, do not + crash by invoking execfile""" + self.run_tmpfile_p() + + def test_run_debug_twice(self): + # https://github.com/ipython/ipython/issues/10028 + _ip = get_ipython() + with tt.fake_input(['c']): + _ip.magic('run -d %s' % self.fname) + with tt.fake_input(['c']): + _ip.magic('run -d %s' % self.fname) + + def test_run_debug_twice_with_breakpoint(self): + """Make a valid python temp file.""" + _ip = get_ipython() + with tt.fake_input(['b 2', 'c', 'c']): + _ip.magic('run -d %s' % self.fname) + + with tt.fake_input(['c']): + with tt.AssertNotPrints('KeyError'): + _ip.magic('run -d %s' % self.fname) + + +class TestMagicRunSimple(tt.TempFileMixin): + + def test_simpledef(self): + """Test that simple class definitions work.""" + src = ("class foo: pass\n" + "def f(): return foo()") + self.mktmp(src) + _ip.magic('run %s' % self.fname) + _ip.run_cell('t = isinstance(f(), foo)') + nt.assert_true(_ip.user_ns['t']) + + def test_obj_del(self): + """Test that object's __del__ methods are called on exit.""" + if sys.platform == 'win32': + try: + import win32api + except ImportError: + raise SkipTest("Test requires pywin32") + src = ("class A(object):\n" + " def __del__(self):\n" + " print('object A deleted')\n" + "a = A()\n") + self.mktmp(src) + if dec.module_not_available('sqlite3'): + err = 'WARNING: IPython History requires SQLite, your history will not be saved\n' + else: + err = None + tt.ipexec_validate(self.fname, 'object A deleted', err) + + def test_aggressive_namespace_cleanup(self): + """Test that namespace cleanup is not too aggressive GH-238 + + Returning from another run magic deletes the namespace""" + # see ticket https://github.com/ipython/ipython/issues/238 + + with tt.TempFileMixin() as empty: + empty.mktmp('') + # On Windows, the filename will have \users in it, so we need to use the + # repr so that the \u becomes \\u. + src = ("ip = get_ipython()\n" + "for i in range(5):\n" + " try:\n" + " ip.magic(%r)\n" + " except NameError as e:\n" + " print(i)\n" + " break\n" % ('run ' + empty.fname)) + self.mktmp(src) + _ip.magic('run %s' % self.fname) + _ip.run_cell('ip == get_ipython()') + nt.assert_equal(_ip.user_ns['i'], 4) + + def test_run_second(self): + """Test that running a second file doesn't clobber the first, gh-3547 + """ + self.mktmp("avar = 1\n" + "def afunc():\n" + " return avar\n") + + with tt.TempFileMixin() as empty: + empty.mktmp("") + + _ip.magic('run %s' % self.fname) + _ip.magic('run %s' % empty.fname) + nt.assert_equal(_ip.user_ns['afunc'](), 1) + + @dec.skip_win32 + def test_tclass(self): + mydir = os.path.dirname(__file__) + tc = os.path.join(mydir, 'tclass') + src = ("%%run '%s' C-first\n" + "%%run '%s' C-second\n" + "%%run '%s' C-third\n") % (tc, tc, tc) + self.mktmp(src, '.ipy') + out = """\ +ARGV 1-: ['C-first'] +ARGV 1-: ['C-second'] +tclass.py: deleting object: C-first +ARGV 1-: ['C-third'] +tclass.py: deleting object: C-second +tclass.py: deleting object: C-third +""" + if dec.module_not_available('sqlite3'): + err = 'WARNING: IPython History requires SQLite, your history will not be saved\n' + else: + err = None + tt.ipexec_validate(self.fname, out, err) + + def test_run_i_after_reset(self): + """Check that %run -i still works after %reset (gh-693)""" + src = "yy = zz\n" + self.mktmp(src) + _ip.run_cell("zz = 23") + try: + _ip.magic('run -i %s' % self.fname) + nt.assert_equal(_ip.user_ns['yy'], 23) + finally: + _ip.magic('reset -f') + + _ip.run_cell("zz = 23") + try: + _ip.magic('run -i %s' % self.fname) + nt.assert_equal(_ip.user_ns['yy'], 23) + finally: + _ip.magic('reset -f') + + def test_unicode(self): + """Check that files in odd encodings are accepted.""" + mydir = os.path.dirname(__file__) + na = os.path.join(mydir, 'nonascii.py') + _ip.magic('run "%s"' % na) + nt.assert_equal(_ip.user_ns['u'], u'Ўт№Ф') + + def test_run_py_file_attribute(self): + """Test handling of `__file__` attribute in `%run .py`.""" + src = "t = __file__\n" + self.mktmp(src) + _missing = object() + file1 = _ip.user_ns.get('__file__', _missing) + _ip.magic('run %s' % self.fname) + file2 = _ip.user_ns.get('__file__', _missing) + + # Check that __file__ was equal to the filename in the script's + # namespace. + nt.assert_equal(_ip.user_ns['t'], self.fname) + + # Check that __file__ was not leaked back into user_ns. + nt.assert_equal(file1, file2) + + def test_run_ipy_file_attribute(self): + """Test handling of `__file__` attribute in `%run `.""" + src = "t = __file__\n" + self.mktmp(src, ext='.ipy') + _missing = object() + file1 = _ip.user_ns.get('__file__', _missing) + _ip.magic('run %s' % self.fname) + file2 = _ip.user_ns.get('__file__', _missing) + + # Check that __file__ was equal to the filename in the script's + # namespace. + nt.assert_equal(_ip.user_ns['t'], self.fname) + + # Check that __file__ was not leaked back into user_ns. + nt.assert_equal(file1, file2) + + def test_run_formatting(self): + """ Test that %run -t -N does not raise a TypeError for N > 1.""" + src = "pass" + self.mktmp(src) + _ip.magic('run -t -N 1 %s' % self.fname) + _ip.magic('run -t -N 10 %s' % self.fname) + + def test_ignore_sys_exit(self): + """Test the -e option to ignore sys.exit()""" + src = "import sys; sys.exit(1)" + self.mktmp(src) + with tt.AssertPrints('SystemExit'): + _ip.magic('run %s' % self.fname) + + with tt.AssertNotPrints('SystemExit'): + _ip.magic('run -e %s' % self.fname) + + def test_run_nb(self): + """Test %run notebook.ipynb""" + from nbformat import v4, writes + nb = v4.new_notebook( + cells=[ + v4.new_markdown_cell("The Ultimate Question of Everything"), + v4.new_code_cell("answer=42") + ] + ) + src = writes(nb, version=4) + self.mktmp(src, ext='.ipynb') + + _ip.magic("run %s" % self.fname) + + nt.assert_equal(_ip.user_ns['answer'], 42) + + def test_run_nb_error(self): + """Test %run notebook.ipynb error""" + from nbformat import v4, writes + # %run when a file name isn't provided + nt.assert_raises(Exception, _ip.magic, "run") + + # %run when a file doesn't exist + nt.assert_raises(Exception, _ip.magic, "run foobar.ipynb") + + # %run on a notebook with an error + nb = v4.new_notebook( + cells=[ + v4.new_code_cell("0/0") + ] + ) + src = writes(nb, version=4) + self.mktmp(src, ext='.ipynb') + nt.assert_raises(Exception, _ip.magic, "run %s" % self.fname) + + def test_file_options(self): + src = ('import sys\n' + 'a = " ".join(sys.argv[1:])\n') + self.mktmp(src) + test_opts = '-x 3 --verbose' + _ip.run_line_magic("run", '{0} {1}'.format(self.fname, test_opts)) + nt.assert_equal(_ip.user_ns['a'], test_opts) + + +class TestMagicRunWithPackage(unittest.TestCase): + + def writefile(self, name, content): + path = os.path.join(self.tempdir.name, name) + d = os.path.dirname(path) + if not os.path.isdir(d): + os.makedirs(d) + with open(path, 'w') as f: + f.write(textwrap.dedent(content)) + + def setUp(self): + self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)])) + """Temporary (probably) valid python package name.""" + + self.value = int(random.random() * 10000) + + self.tempdir = TemporaryDirectory() + self.__orig_cwd = os.getcwd() + sys.path.insert(0, self.tempdir.name) + + self.writefile(os.path.join(package, '__init__.py'), '') + self.writefile(os.path.join(package, 'sub.py'), """ + x = {0!r} + """.format(self.value)) + self.writefile(os.path.join(package, 'relative.py'), """ + from .sub import x + """) + self.writefile(os.path.join(package, 'absolute.py'), """ + from {0}.sub import x + """.format(package)) + self.writefile(os.path.join(package, 'args.py'), """ + import sys + a = " ".join(sys.argv[1:]) + """.format(package)) + + def tearDown(self): + os.chdir(self.__orig_cwd) + sys.path[:] = [p for p in sys.path if p != self.tempdir.name] + self.tempdir.cleanup() + + def check_run_submodule(self, submodule, opts=''): + _ip.user_ns.pop('x', None) + _ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts)) + self.assertEqual(_ip.user_ns['x'], self.value, + 'Variable `x` is not loaded from module `{0}`.' + .format(submodule)) + + def test_run_submodule_with_absolute_import(self): + self.check_run_submodule('absolute') + + def test_run_submodule_with_relative_import(self): + """Run submodule that has a relative import statement (#2727).""" + self.check_run_submodule('relative') + + def test_prun_submodule_with_absolute_import(self): + self.check_run_submodule('absolute', '-p') + + def test_prun_submodule_with_relative_import(self): + self.check_run_submodule('relative', '-p') + + def with_fake_debugger(func): + @functools.wraps(func) + def wrapper(*args, **kwds): + with patch.object(debugger.Pdb, 'run', staticmethod(eval)): + return func(*args, **kwds) + return wrapper + + @with_fake_debugger + def test_debug_run_submodule_with_absolute_import(self): + self.check_run_submodule('absolute', '-d') + + @with_fake_debugger + def test_debug_run_submodule_with_relative_import(self): + self.check_run_submodule('relative', '-d') + + def test_module_options(self): + _ip.user_ns.pop('a', None) + test_opts = '-x abc -m test' + _ip.run_line_magic('run', '-m {0}.args {1}'.format(self.package, test_opts)) + nt.assert_equal(_ip.user_ns['a'], test_opts) + + def test_module_options_with_separator(self): + _ip.user_ns.pop('a', None) + test_opts = '-x abc -m test' + _ip.run_line_magic('run', '-m {0}.args -- {1}'.format(self.package, test_opts)) + nt.assert_equal(_ip.user_ns['a'], test_opts) + +def test_run__name__(): + with TemporaryDirectory() as td: + path = pjoin(td, 'foo.py') + with open(path, 'w') as f: + f.write("q = __name__") + + _ip.user_ns.pop('q', None) + _ip.magic('run {}'.format(path)) + nt.assert_equal(_ip.user_ns.pop('q'), '__main__') + + _ip.magic('run -n {}'.format(path)) + nt.assert_equal(_ip.user_ns.pop('q'), 'foo') + + try: + _ip.magic('run -i -n {}'.format(path)) + nt.assert_equal(_ip.user_ns.pop('q'), 'foo') + finally: + _ip.magic('reset -f') + + +def test_run_tb(): + """Test traceback offset in %run""" + with TemporaryDirectory() as td: + path = pjoin(td, 'foo.py') + with open(path, 'w') as f: + f.write('\n'.join([ + "def foo():", + " return bar()", + "def bar():", + " raise RuntimeError('hello!')", + "foo()", + ])) + with capture_output() as io: + _ip.magic('run {}'.format(path)) + out = io.stdout + nt.assert_not_in("execfile", out) + nt.assert_in("RuntimeError", out) + nt.assert_equal(out.count("---->"), 3) + del ip.user_ns['bar'] + del ip.user_ns['foo'] + + +def test_multiprocessing_run(): + """Set we can run mutiprocesgin without messing up up main namespace + + Note that import `nose.tools as nt` mdify the value s + sys.module['__mp_main__'] so wee need to temporarily set it to None to test + the issue. + """ + with TemporaryDirectory() as td: + mpm = sys.modules.get('__mp_main__') + assert mpm is not None + sys.modules['__mp_main__'] = None + try: + path = pjoin(td, 'test.py') + with open(path, 'w') as f: + f.write("import multiprocessing\nprint('hoy')") + with capture_output() as io: + _ip.run_line_magic('run', path) + _ip.run_cell("i_m_undefined") + out = io.stdout + nt.assert_in("hoy", out) + nt.assert_not_in("AttributeError", out) + nt.assert_in("NameError", out) + nt.assert_equal(out.count("---->"), 1) + except: + raise + finally: + sys.modules['__mp_main__'] = mpm + +@dec.knownfailureif(sys.platform == 'win32', "writes to io.stdout aren't captured on Windows") +def test_script_tb(): + """Test traceback offset in `ipython script.py`""" + with TemporaryDirectory() as td: + path = pjoin(td, 'foo.py') + with open(path, 'w') as f: + f.write('\n'.join([ + "def foo():", + " return bar()", + "def bar():", + " raise RuntimeError('hello!')", + "foo()", + ])) + out, err = tt.ipexec(path) + nt.assert_not_in("execfile", out) + nt.assert_in("RuntimeError", out) + nt.assert_equal(out.count("---->"), 3) + diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_shellapp.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_shellapp.py new file mode 100644 index 00000000..6808114b --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_shellapp.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +"""Tests for shellapp module. + +Authors +------- +* Bradley Froehle +""" +#----------------------------------------------------------------------------- +# Copyright (C) 2012 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +import unittest + +from IPython.testing import decorators as dec +from IPython.testing import tools as tt + +sqlite_err_maybe = dec.module_not_available('sqlite3') +SQLITE_NOT_AVAILABLE_ERROR = ('WARNING: IPython History requires SQLite,' + ' your history will not be saved\n') + +class TestFileToRun(tt.TempFileMixin, unittest.TestCase): + """Test the behavior of the file_to_run parameter.""" + + def test_py_script_file_attribute(self): + """Test that `__file__` is set when running `ipython file.py`""" + src = "print(__file__)\n" + self.mktmp(src) + + err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None + tt.ipexec_validate(self.fname, self.fname, err) + + def test_ipy_script_file_attribute(self): + """Test that `__file__` is set when running `ipython file.ipy`""" + src = "print(__file__)\n" + self.mktmp(src, ext='.ipy') + + err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None + tt.ipexec_validate(self.fname, self.fname, err) + + # The commands option to ipexec_validate doesn't work on Windows, and it + # doesn't seem worth fixing + @dec.skip_win32 + def test_py_script_file_attribute_interactively(self): + """Test that `__file__` is not set after `ipython -i file.py`""" + src = "True\n" + self.mktmp(src) + + out, err = tt.ipexec(self.fname, options=['-i'], + commands=['"__file__" in globals()', 'print(123)', 'exit()']) + if 'False' not in out: + print("Subprocess stderr:") + print(err) + print('-----') + raise AssertionError("'False' not found in %r" % out) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_splitinput.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_splitinput.py new file mode 100644 index 00000000..98b4189e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_splitinput.py @@ -0,0 +1,38 @@ +# coding: utf-8 +import nose.tools as nt + +from IPython.core.splitinput import split_user_input, LineInfo +from IPython.testing import tools as tt + +tests = [ + ('x=1', ('', '', 'x', '=1')), + ('?', ('', '?', '', '')), + ('??', ('', '??', '', '')), + (' ?', (' ', '?', '', '')), + (' ??', (' ', '??', '', '')), + ('??x', ('', '??', 'x', '')), + ('?x=1', ('', '?', 'x', '=1')), + ('!ls', ('', '!', 'ls', '')), + (' !ls', (' ', '!', 'ls', '')), + ('!!ls', ('', '!!', 'ls', '')), + (' !!ls', (' ', '!!', 'ls', '')), + (',ls', ('', ',', 'ls', '')), + (';ls', ('', ';', 'ls', '')), + (' ;ls', (' ', ';', 'ls', '')), + ('f.g(x)', ('', '', 'f.g', '(x)')), + ('f.g (x)', ('', '', 'f.g', '(x)')), + ('?%hist1', ('', '?', '%hist1', '')), + ('?%%hist2', ('', '?', '%%hist2', '')), + ('??%hist3', ('', '??', '%hist3', '')), + ('??%%hist4', ('', '??', '%%hist4', '')), + ('?x*', ('', '?', 'x*', '')), + ] +tests.append((u"Pérez Fernando", (u'', u'', u'Pérez', u'Fernando'))) + +def test_split_user_input(): + return tt.check_pairs(split_user_input, tests) + +def test_LineInfo(): + """Simple test for LineInfo construction and str()""" + linfo = LineInfo(' %cd /home') + nt.assert_equal(str(linfo), 'LineInfo [ |%|cd|/home]') diff --git a/.venv/lib/python3.8/site-packages/IPython/core/tests/test_ultratb.py b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_ultratb.py new file mode 100644 index 00000000..3751117b --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/tests/test_ultratb.py @@ -0,0 +1,470 @@ +# encoding: utf-8 +"""Tests for IPython.core.ultratb +""" +import io +import logging +import sys +import os.path +from textwrap import dedent +import traceback +import unittest +from unittest import mock + +import IPython.core.ultratb as ultratb +from IPython.core.ultratb import ColorTB, VerboseTB, find_recursion + + +from IPython.testing import tools as tt +from IPython.testing.decorators import onlyif_unicode_paths +from IPython.utils.syspathcontext import prepended_to_syspath +from IPython.utils.tempdir import TemporaryDirectory + +file_1 = """1 +2 +3 +def f(): + 1/0 +""" + +file_2 = """def f(): + 1/0 +""" + + +def recursionlimit(frames): + """ + decorator to set the recursion limit temporarily + """ + + def inner(test_function): + def wrapper(*args, **kwargs): + _orig_rec_limit = ultratb._FRAME_RECURSION_LIMIT + ultratb._FRAME_RECURSION_LIMIT = 50 + + rl = sys.getrecursionlimit() + sys.setrecursionlimit(frames) + try: + return test_function(*args, **kwargs) + finally: + sys.setrecursionlimit(rl) + ultratb._FRAME_RECURSION_LIMIT = _orig_rec_limit + + return wrapper + + return inner + + +class ChangedPyFileTest(unittest.TestCase): + def test_changing_py_file(self): + """Traceback produced if the line where the error occurred is missing? + + https://github.com/ipython/ipython/issues/1456 + """ + with TemporaryDirectory() as td: + fname = os.path.join(td, "foo.py") + with open(fname, "w") as f: + f.write(file_1) + + with prepended_to_syspath(td): + ip.run_cell("import foo") + + with tt.AssertPrints("ZeroDivisionError"): + ip.run_cell("foo.f()") + + # Make the file shorter, so the line of the error is missing. + with open(fname, "w") as f: + f.write(file_2) + + # For some reason, this was failing on the *second* call after + # changing the file, so we call f() twice. + with tt.AssertNotPrints("Internal Python error", channel='stderr'): + with tt.AssertPrints("ZeroDivisionError"): + ip.run_cell("foo.f()") + with tt.AssertPrints("ZeroDivisionError"): + ip.run_cell("foo.f()") + +iso_8859_5_file = u'''# coding: iso-8859-5 + +def fail(): + """дбИЖ""" + 1/0 # дбИЖ +''' + +class NonAsciiTest(unittest.TestCase): + @onlyif_unicode_paths + def test_nonascii_path(self): + # Non-ascii directory name as well. + with TemporaryDirectory(suffix=u'é') as td: + fname = os.path.join(td, u"fooé.py") + with open(fname, "w") as f: + f.write(file_1) + + with prepended_to_syspath(td): + ip.run_cell("import foo") + + with tt.AssertPrints("ZeroDivisionError"): + ip.run_cell("foo.f()") + + def test_iso8859_5(self): + with TemporaryDirectory() as td: + fname = os.path.join(td, 'dfghjkl.py') + + with io.open(fname, 'w', encoding='iso-8859-5') as f: + f.write(iso_8859_5_file) + + with prepended_to_syspath(td): + ip.run_cell("from dfghjkl import fail") + + with tt.AssertPrints("ZeroDivisionError"): + with tt.AssertPrints(u'дбИЖ', suppress=False): + ip.run_cell('fail()') + + def test_nonascii_msg(self): + cell = u"raise Exception('é')" + expected = u"Exception('é')" + ip.run_cell("%xmode plain") + with tt.AssertPrints(expected): + ip.run_cell(cell) + + ip.run_cell("%xmode verbose") + with tt.AssertPrints(expected): + ip.run_cell(cell) + + ip.run_cell("%xmode context") + with tt.AssertPrints(expected): + ip.run_cell(cell) + + ip.run_cell("%xmode minimal") + with tt.AssertPrints(u"Exception: é"): + ip.run_cell(cell) + + # Put this back into Context mode for later tests. + ip.run_cell("%xmode context") + +class NestedGenExprTestCase(unittest.TestCase): + """ + Regression test for the following issues: + https://github.com/ipython/ipython/issues/8293 + https://github.com/ipython/ipython/issues/8205 + """ + def test_nested_genexpr(self): + code = dedent( + """\ + class SpecificException(Exception): + pass + + def foo(x): + raise SpecificException("Success!") + + sum(sum(foo(x) for _ in [0]) for x in [0]) + """ + ) + with tt.AssertPrints('SpecificException: Success!', suppress=False): + ip.run_cell(code) + + +indentationerror_file = """if True: +zoon() +""" + +class IndentationErrorTest(unittest.TestCase): + def test_indentationerror_shows_line(self): + # See issue gh-2398 + with tt.AssertPrints("IndentationError"): + with tt.AssertPrints("zoon()", suppress=False): + ip.run_cell(indentationerror_file) + + with TemporaryDirectory() as td: + fname = os.path.join(td, "foo.py") + with open(fname, "w") as f: + f.write(indentationerror_file) + + with tt.AssertPrints("IndentationError"): + with tt.AssertPrints("zoon()", suppress=False): + ip.magic('run %s' % fname) + +se_file_1 = """1 +2 +7/ +""" + +se_file_2 = """7/ +""" + +class SyntaxErrorTest(unittest.TestCase): + def test_syntaxerror_without_lineno(self): + with tt.AssertNotPrints("TypeError"): + with tt.AssertPrints("line unknown"): + ip.run_cell("raise SyntaxError()") + + def test_syntaxerror_no_stacktrace_at_compile_time(self): + syntax_error_at_compile_time = """ +def foo(): + .. +""" + with tt.AssertPrints("SyntaxError"): + ip.run_cell(syntax_error_at_compile_time) + + with tt.AssertNotPrints("foo()"): + ip.run_cell(syntax_error_at_compile_time) + + def test_syntaxerror_stacktrace_when_running_compiled_code(self): + syntax_error_at_runtime = """ +def foo(): + eval("..") + +def bar(): + foo() + +bar() +""" + with tt.AssertPrints("SyntaxError"): + ip.run_cell(syntax_error_at_runtime) + # Assert syntax error during runtime generate stacktrace + with tt.AssertPrints(["foo()", "bar()"]): + ip.run_cell(syntax_error_at_runtime) + del ip.user_ns['bar'] + del ip.user_ns['foo'] + + def test_changing_py_file(self): + with TemporaryDirectory() as td: + fname = os.path.join(td, "foo.py") + with open(fname, 'w') as f: + f.write(se_file_1) + + with tt.AssertPrints(["7/", "SyntaxError"]): + ip.magic("run " + fname) + + # Modify the file + with open(fname, 'w') as f: + f.write(se_file_2) + + # The SyntaxError should point to the correct line + with tt.AssertPrints(["7/", "SyntaxError"]): + ip.magic("run " + fname) + + def test_non_syntaxerror(self): + # SyntaxTB may be called with an error other than a SyntaxError + # See e.g. gh-4361 + try: + raise ValueError('QWERTY') + except ValueError: + with tt.AssertPrints('QWERTY'): + ip.showsyntaxerror() + +import sys +if sys.version_info < (3,9): + """ + New 3.9 Pgen Parser does not raise Memory error, except on failed malloc. + """ + class MemoryErrorTest(unittest.TestCase): + def test_memoryerror(self): + memoryerror_code = "(" * 200 + ")" * 200 + with tt.AssertPrints("MemoryError"): + ip.run_cell(memoryerror_code) + + +class Python3ChainedExceptionsTest(unittest.TestCase): + DIRECT_CAUSE_ERROR_CODE = """ +try: + x = 1 + 2 + print(not_defined_here) +except Exception as e: + x += 55 + x - 1 + y = {} + raise KeyError('uh') from e + """ + + EXCEPTION_DURING_HANDLING_CODE = """ +try: + x = 1 + 2 + print(not_defined_here) +except Exception as e: + x += 55 + x - 1 + y = {} + raise KeyError('uh') + """ + + SUPPRESS_CHAINING_CODE = """ +try: + 1/0 +except Exception: + raise ValueError("Yikes") from None + """ + + def test_direct_cause_error(self): + with tt.AssertPrints(["KeyError", "NameError", "direct cause"]): + ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE) + + def test_exception_during_handling_error(self): + with tt.AssertPrints(["KeyError", "NameError", "During handling"]): + ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE) + + def test_suppress_exception_chaining(self): + with tt.AssertNotPrints("ZeroDivisionError"), \ + tt.AssertPrints("ValueError", suppress=False): + ip.run_cell(self.SUPPRESS_CHAINING_CODE) + + def test_plain_direct_cause_error(self): + with tt.AssertPrints(["KeyError", "NameError", "direct cause"]): + ip.run_cell("%xmode Plain") + ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE) + ip.run_cell("%xmode Verbose") + + def test_plain_exception_during_handling_error(self): + with tt.AssertPrints(["KeyError", "NameError", "During handling"]): + ip.run_cell("%xmode Plain") + ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE) + ip.run_cell("%xmode Verbose") + + def test_plain_suppress_exception_chaining(self): + with tt.AssertNotPrints("ZeroDivisionError"), \ + tt.AssertPrints("ValueError", suppress=False): + ip.run_cell("%xmode Plain") + ip.run_cell(self.SUPPRESS_CHAINING_CODE) + ip.run_cell("%xmode Verbose") + + +class RecursionTest(unittest.TestCase): + DEFINITIONS = """ +def non_recurs(): + 1/0 + +def r1(): + r1() + +def r3a(): + r3b() + +def r3b(): + r3c() + +def r3c(): + r3a() + +def r3o1(): + r3a() + +def r3o2(): + r3o1() +""" + def setUp(self): + ip.run_cell(self.DEFINITIONS) + + def test_no_recursion(self): + with tt.AssertNotPrints("frames repeated"): + ip.run_cell("non_recurs()") + + @recursionlimit(150) + def test_recursion_one_frame(self): + with tt.AssertPrints("1 frames repeated"): + ip.run_cell("r1()") + + @recursionlimit(150) + def test_recursion_three_frames(self): + with tt.AssertPrints("3 frames repeated"): + ip.run_cell("r3o2()") + + @recursionlimit(150) + def test_find_recursion(self): + captured = [] + def capture_exc(*args, **kwargs): + captured.append(sys.exc_info()) + with mock.patch.object(ip, 'showtraceback', capture_exc): + ip.run_cell("r3o2()") + + self.assertEqual(len(captured), 1) + etype, evalue, tb = captured[0] + self.assertIn("recursion", str(evalue)) + + records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset) + for r in records[:10]: + print(r[1:4]) + + # The outermost frames should be: + # 0: the 'cell' that was running when the exception came up + # 1: r3o2() + # 2: r3o1() + # 3: r3a() + # Then repeating r3b, r3c, r3a + last_unique, repeat_length = find_recursion(etype, evalue, records) + self.assertEqual(last_unique, 2) + self.assertEqual(repeat_length, 3) + + +#---------------------------------------------------------------------------- + +# module testing (minimal) +def test_handlers(): + def spam(c, d_e): + (d, e) = d_e + x = c + d + y = c * d + foo(x, y) + + def foo(a, b, bar=1): + eggs(a, b + bar) + + def eggs(f, g, z=globals()): + h = f + g + i = f - g + return h / i + + buff = io.StringIO() + + buff.write('') + buff.write('*** Before ***') + try: + buff.write(spam(1, (2, 3))) + except: + traceback.print_exc(file=buff) + + handler = ColorTB(ostream=buff) + buff.write('*** ColorTB ***') + try: + buff.write(spam(1, (2, 3))) + except: + handler(*sys.exc_info()) + buff.write('') + + handler = VerboseTB(ostream=buff) + buff.write('*** VerboseTB ***') + try: + buff.write(spam(1, (2, 3))) + except: + handler(*sys.exc_info()) + buff.write('') + +from IPython.testing.decorators import skipif + +class TokenizeFailureTest(unittest.TestCase): + """Tests related to https://github.com/ipython/ipython/issues/6864.""" + + # that appear to test that we are handling an exception that can be thrown + # by the tokenizer due to a bug that seem to have been fixed in 3.8, though + # I'm unsure if other sequences can make it raise this error. Let's just + # skip in 3.8 for now + @skipif(sys.version_info > (3,8)) + def testLogging(self): + message = "An unexpected error occurred while tokenizing input" + cell = 'raise ValueError("""a\nb""")' + + stream = io.StringIO() + handler = logging.StreamHandler(stream) + logger = logging.getLogger() + loglevel = logger.level + logger.addHandler(handler) + self.addCleanup(lambda: logger.removeHandler(handler)) + self.addCleanup(lambda: logger.setLevel(loglevel)) + + logger.setLevel(logging.INFO) + with tt.AssertNotPrints(message): + ip.run_cell(cell) + self.assertNotIn(message, stream.getvalue()) + + logger.setLevel(logging.DEBUG) + with tt.AssertNotPrints(message): + ip.run_cell(cell) + self.assertIn(message, stream.getvalue()) diff --git a/.venv/lib/python3.8/site-packages/IPython/core/ultratb.py b/.venv/lib/python3.8/site-packages/IPython/core/ultratb.py new file mode 100644 index 00000000..4358ea5b --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/ultratb.py @@ -0,0 +1,1525 @@ +# -*- coding: utf-8 -*- +""" +Verbose and colourful traceback formatting. + +**ColorTB** + +I've always found it a bit hard to visually parse tracebacks in Python. The +ColorTB class is a solution to that problem. It colors the different parts of a +traceback in a manner similar to what you would expect from a syntax-highlighting +text editor. + +Installation instructions for ColorTB:: + + import sys,ultratb + sys.excepthook = ultratb.ColorTB() + +**VerboseTB** + +I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds +of useful info when a traceback occurs. Ping originally had it spit out HTML +and intended it for CGI programmers, but why should they have all the fun? I +altered it to spit out colored text to the terminal. It's a bit overwhelming, +but kind of neat, and maybe useful for long-running programs that you believe +are bug-free. If a crash *does* occur in that type of program you want details. +Give it a shot--you'll love it or you'll hate it. + +.. note:: + + The Verbose mode prints the variables currently visible where the exception + happened (shortening their strings if too long). This can potentially be + very slow, if you happen to have a huge data structure whose string + representation is complex to compute. Your computer may appear to freeze for + a while with cpu usage at 100%. If this occurs, you can cancel the traceback + with Ctrl-C (maybe hitting it more than once). + + If you encounter this kind of situation often, you may want to use the + Verbose_novars mode instead of the regular Verbose, which avoids formatting + variables (but otherwise includes the information and context given by + Verbose). + +.. note:: + + The verbose mode print all variables in the stack, which means it can + potentially leak sensitive information like access keys, or unencrypted + password. + +Installation instructions for VerboseTB:: + + import sys,ultratb + sys.excepthook = ultratb.VerboseTB() + +Note: Much of the code in this module was lifted verbatim from the standard +library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'. + +Color schemes +------------- + +The colors are defined in the class TBTools through the use of the +ColorSchemeTable class. Currently the following exist: + + - NoColor: allows all of this module to be used in any terminal (the color + escapes are just dummy blank strings). + + - Linux: is meant to look good in a terminal like the Linux console (black + or very dark background). + + - LightBG: similar to Linux but swaps dark/light colors to be more readable + in light background terminals. + + - Neutral: a neutral color scheme that should be readable on both light and + dark background + +You can implement other color schemes easily, the syntax is fairly +self-explanatory. Please send back new schemes you develop to the author for +possible inclusion in future releases. + +Inheritance diagram: + +.. inheritance-diagram:: IPython.core.ultratb + :parts: 3 +""" + +#***************************************************************************** +# Copyright (C) 2001 Nathaniel Gray +# Copyright (C) 2001-2004 Fernando Perez +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + + +import dis +import inspect +import keyword +import linecache +import os +import pydoc +import re +import sys +import time +import tokenize +import traceback + +from tokenize import generate_tokens + +# For purposes of monkeypatching inspect to fix a bug in it. +from inspect import getsourcefile, getfile, getmodule, \ + ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode + +# IPython's own modules +from IPython import get_ipython +from IPython.core import debugger +from IPython.core.display_trap import DisplayTrap +from IPython.core.excolors import exception_colors +from IPython.utils import PyColorize +from IPython.utils import path as util_path +from IPython.utils import py3compat +from IPython.utils.data import uniq_stable +from IPython.utils.terminal import get_terminal_size + +from logging import info, error, debug + +from importlib.util import source_from_cache + +import IPython.utils.colorable as colorable + +# Globals +# amount of space to put line numbers before verbose tracebacks +INDENT_SIZE = 8 + +# Default color scheme. This is used, for example, by the traceback +# formatter. When running in an actual IPython instance, the user's rc.colors +# value is used, but having a module global makes this functionality available +# to users of ultratb who are NOT running inside ipython. +DEFAULT_SCHEME = 'NoColor' + + +# Number of frame above which we are likely to have a recursion and will +# **attempt** to detect it. Made modifiable mostly to speedup test suite +# as detecting recursion is one of our slowest test +_FRAME_RECURSION_LIMIT = 500 + +# --------------------------------------------------------------------------- +# Code begins + +# Utility functions +def inspect_error(): + """Print a message about internal inspect errors. + + These are unfortunately quite common.""" + + error('Internal Python error in the inspect module.\n' + 'Below is the traceback from this internal error.\n') + + +# This function is a monkeypatch we apply to the Python inspect module. We have +# now found when it's needed (see discussion on issue gh-1456), and we have a +# test case (IPython.core.tests.test_ultratb.ChangedPyFileTest) that fails if +# the monkeypatch is not applied. TK, Aug 2012. +def findsource(object): + """Return the entire source file and starting line number for an object. + + The argument may be a module, class, method, function, traceback, frame, + or code object. The source code is returned as a list of all the lines + in the file and the line number indexes a line in that list. An IOError + is raised if the source code cannot be retrieved. + + FIXED version with which we monkeypatch the stdlib to work around a bug.""" + + file = getsourcefile(object) or getfile(object) + # If the object is a frame, then trying to get the globals dict from its + # module won't work. Instead, the frame object itself has the globals + # dictionary. + globals_dict = None + if inspect.isframe(object): + # XXX: can this ever be false? + globals_dict = object.f_globals + else: + module = getmodule(object, file) + if module: + globals_dict = module.__dict__ + lines = linecache.getlines(file, globals_dict) + if not lines: + raise IOError('could not get source code') + + if ismodule(object): + return lines, 0 + + if isclass(object): + name = object.__name__ + pat = re.compile(r'^(\s*)class\s*' + name + r'\b') + # make some effort to find the best matching class definition: + # use the one with the least indentation, which is the one + # that's most probably not inside a function definition. + candidates = [] + for i, line in enumerate(lines): + match = pat.match(line) + if match: + # if it's at toplevel, it's already the best one + if line[0] == 'c': + return lines, i + # else add whitespace to candidate list + candidates.append((match.group(1), i)) + if candidates: + # this will sort by whitespace, and by line number, + # less whitespace first + candidates.sort() + return lines, candidates[0][1] + else: + raise IOError('could not find class definition') + + if ismethod(object): + object = object.__func__ + if isfunction(object): + object = object.__code__ + if istraceback(object): + object = object.tb_frame + if isframe(object): + object = object.f_code + if iscode(object): + if not hasattr(object, 'co_firstlineno'): + raise IOError('could not find function definition') + pat = re.compile(r'^(\s*def\s)|(.*(? 0: + if pmatch(lines[lnum]): + break + lnum -= 1 + + return lines, lnum + raise IOError('could not find code object') + + +# This is a patched version of inspect.getargs that applies the (unmerged) +# patch for http://bugs.python.org/issue14611 by Stefano Taschini. This fixes +# https://github.com/ipython/ipython/issues/8205 and +# https://github.com/ipython/ipython/issues/8293 +def getargs(co): + """Get information about the arguments accepted by a code object. + + Three things are returned: (args, varargs, varkw), where 'args' is + a list of argument names (possibly containing nested lists), and + 'varargs' and 'varkw' are the names of the * and ** arguments or None.""" + if not iscode(co): + raise TypeError('{!r} is not a code object'.format(co)) + + nargs = co.co_argcount + names = co.co_varnames + args = list(names[:nargs]) + step = 0 + + # The following acrobatics are for anonymous (tuple) arguments. + for i in range(nargs): + if args[i][:1] in ('', '.'): + stack, remain, count = [], [], [] + while step < len(co.co_code): + op = ord(co.co_code[step]) + step = step + 1 + if op >= dis.HAVE_ARGUMENT: + opname = dis.opname[op] + value = ord(co.co_code[step]) + ord(co.co_code[step+1])*256 + step = step + 2 + if opname in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'): + remain.append(value) + count.append(value) + elif opname in ('STORE_FAST', 'STORE_DEREF'): + if op in dis.haslocal: + stack.append(co.co_varnames[value]) + elif op in dis.hasfree: + stack.append((co.co_cellvars + co.co_freevars)[value]) + # Special case for sublists of length 1: def foo((bar)) + # doesn't generate the UNPACK_TUPLE bytecode, so if + # `remain` is empty here, we have such a sublist. + if not remain: + stack[0] = [stack[0]] + break + else: + remain[-1] = remain[-1] - 1 + while remain[-1] == 0: + remain.pop() + size = count.pop() + stack[-size:] = [stack[-size:]] + if not remain: + break + remain[-1] = remain[-1] - 1 + if not remain: + break + args[i] = stack[0] + + varargs = None + if co.co_flags & inspect.CO_VARARGS: + varargs = co.co_varnames[nargs] + nargs = nargs + 1 + varkw = None + if co.co_flags & inspect.CO_VARKEYWORDS: + varkw = co.co_varnames[nargs] + return inspect.Arguments(args, varargs, varkw) + + +# Monkeypatch inspect to apply our bugfix. +def with_patch_inspect(f): + """ + Deprecated since IPython 6.0 + decorator for monkeypatching inspect.findsource + """ + + def wrapped(*args, **kwargs): + save_findsource = inspect.findsource + save_getargs = inspect.getargs + inspect.findsource = findsource + inspect.getargs = getargs + try: + return f(*args, **kwargs) + finally: + inspect.findsource = save_findsource + inspect.getargs = save_getargs + + return wrapped + + +def fix_frame_records_filenames(records): + """Try to fix the filenames in each record from inspect.getinnerframes(). + + Particularly, modules loaded from within zip files have useless filenames + attached to their code object, and inspect.getinnerframes() just uses it. + """ + fixed_records = [] + for frame, filename, line_no, func_name, lines, index in records: + # Look inside the frame's globals dictionary for __file__, + # which should be better. However, keep Cython filenames since + # we prefer the source filenames over the compiled .so file. + if not filename.endswith(('.pyx', '.pxd', '.pxi')): + better_fn = frame.f_globals.get('__file__', None) + if isinstance(better_fn, str): + # Check the type just in case someone did something weird with + # __file__. It might also be None if the error occurred during + # import. + filename = better_fn + fixed_records.append((frame, filename, line_no, func_name, lines, index)) + return fixed_records + + +@with_patch_inspect +def _fixed_getinnerframes(etb, context=1, tb_offset=0): + LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5 + + records = fix_frame_records_filenames(inspect.getinnerframes(etb, context)) + # If the error is at the console, don't build any context, since it would + # otherwise produce 5 blank lines printed out (there is no file at the + # console) + rec_check = records[tb_offset:] + try: + rname = rec_check[0][1] + if rname == '' or rname.endswith(''): + return rec_check + except IndexError: + pass + + aux = traceback.extract_tb(etb) + assert len(records) == len(aux) + for i, (file, lnum, _, _) in enumerate(aux): + maybeStart = lnum - 1 - context // 2 + start = max(maybeStart, 0) + end = start + context + lines = linecache.getlines(file)[start:end] + buf = list(records[i]) + buf[LNUM_POS] = lnum + buf[INDEX_POS] = lnum - 1 - start + buf[LINES_POS] = lines + records[i] = tuple(buf) + return records[tb_offset:] + +# Helper function -- largely belongs to VerboseTB, but we need the same +# functionality to produce a pseudo verbose TB for SyntaxErrors, so that they +# can be recognized properly by ipython.el's py-traceback-line-re +# (SyntaxErrors have to be treated specially because they have no traceback) + + +def _format_traceback_lines(lnum, index, lines, Colors, lvals, _line_format): + """ + Format tracebacks lines with pointing arrow, leading numbers... + + Parameters + ========== + + lnum: int + index: int + lines: list[string] + Colors: + ColorScheme used. + lvals: bytes + Values of local variables, already colored, to inject just after the error line. + _line_format: f (str) -> (str, bool) + return (colorized version of str, failure to do so) + """ + numbers_width = INDENT_SIZE - 1 + res = [] + + for i,line in enumerate(lines, lnum-index): + line = py3compat.cast_unicode(line) + + new_line, err = _line_format(line, 'str') + if not err: + line = new_line + + if i == lnum: + # This is the line with the error + pad = numbers_width - len(str(i)) + num = '%s%s' % (debugger.make_arrow(pad), str(lnum)) + line = '%s%s%s %s%s' % (Colors.linenoEm, num, + Colors.line, line, Colors.Normal) + else: + num = '%*s' % (numbers_width, i) + line = '%s%s%s %s' % (Colors.lineno, num, + Colors.Normal, line) + + res.append(line) + if lvals and i == lnum: + res.append(lvals + '\n') + return res + +def is_recursion_error(etype, value, records): + try: + # RecursionError is new in Python 3.5 + recursion_error_type = RecursionError + except NameError: + recursion_error_type = RuntimeError + + # The default recursion limit is 1000, but some of that will be taken up + # by stack frames in IPython itself. >500 frames probably indicates + # a recursion error. + return (etype is recursion_error_type) \ + and "recursion" in str(value).lower() \ + and len(records) > _FRAME_RECURSION_LIMIT + +def find_recursion(etype, value, records): + """Identify the repeating stack frames from a RecursionError traceback + + 'records' is a list as returned by VerboseTB.get_records() + + Returns (last_unique, repeat_length) + """ + # This involves a bit of guesswork - we want to show enough of the traceback + # to indicate where the recursion is occurring. We guess that the innermost + # quarter of the traceback (250 frames by default) is repeats, and find the + # first frame (from in to out) that looks different. + if not is_recursion_error(etype, value, records): + return len(records), 0 + + # Select filename, lineno, func_name to track frames with + records = [r[1:4] for r in records] + inner_frames = records[-(len(records)//4):] + frames_repeated = set(inner_frames) + + last_seen_at = {} + longest_repeat = 0 + i = len(records) + for frame in reversed(records): + i -= 1 + if frame not in frames_repeated: + last_unique = i + break + + if frame in last_seen_at: + distance = last_seen_at[frame] - i + longest_repeat = max(longest_repeat, distance) + + last_seen_at[frame] = i + else: + last_unique = 0 # The whole traceback was recursion + + return last_unique, longest_repeat + +#--------------------------------------------------------------------------- +# Module classes +class TBTools(colorable.Colorable): + """Basic tools used by all traceback printer classes.""" + + # Number of frames to skip when reporting tracebacks + tb_offset = 0 + + def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None): + # Whether to call the interactive pdb debugger after printing + # tracebacks or not + super(TBTools, self).__init__(parent=parent, config=config) + self.call_pdb = call_pdb + + # Output stream to write to. Note that we store the original value in + # a private attribute and then make the public ostream a property, so + # that we can delay accessing sys.stdout until runtime. The way + # things are written now, the sys.stdout object is dynamically managed + # so a reference to it should NEVER be stored statically. This + # property approach confines this detail to a single location, and all + # subclasses can simply access self.ostream for writing. + self._ostream = ostream + + # Create color table + self.color_scheme_table = exception_colors() + + self.set_colors(color_scheme) + self.old_scheme = color_scheme # save initial value for toggles + + if call_pdb: + self.pdb = debugger.Pdb() + else: + self.pdb = None + + def _get_ostream(self): + """Output stream that exceptions are written to. + + Valid values are: + + - None: the default, which means that IPython will dynamically resolve + to sys.stdout. This ensures compatibility with most tools, including + Windows (where plain stdout doesn't recognize ANSI escapes). + + - Any object with 'write' and 'flush' attributes. + """ + return sys.stdout if self._ostream is None else self._ostream + + def _set_ostream(self, val): + assert val is None or (hasattr(val, 'write') and hasattr(val, 'flush')) + self._ostream = val + + ostream = property(_get_ostream, _set_ostream) + + def get_parts_of_chained_exception(self, evalue): + def get_chained_exception(exception_value): + cause = getattr(exception_value, '__cause__', None) + if cause: + return cause + if getattr(exception_value, '__suppress_context__', False): + return None + return getattr(exception_value, '__context__', None) + + chained_evalue = get_chained_exception(evalue) + + if chained_evalue: + return chained_evalue.__class__, chained_evalue, chained_evalue.__traceback__ + + def prepare_chained_exception_message(self, cause): + direct_cause = "\nThe above exception was the direct cause of the following exception:\n" + exception_during_handling = "\nDuring handling of the above exception, another exception occurred:\n" + + if cause: + message = [[direct_cause]] + else: + message = [[exception_during_handling]] + return message + + def set_colors(self, *args, **kw): + """Shorthand access to the color table scheme selector method.""" + + # Set own color table + self.color_scheme_table.set_active_scheme(*args, **kw) + # for convenience, set Colors to the active scheme + self.Colors = self.color_scheme_table.active_colors + # Also set colors of debugger + if hasattr(self, 'pdb') and self.pdb is not None: + self.pdb.set_colors(*args, **kw) + + def color_toggle(self): + """Toggle between the currently active color scheme and NoColor.""" + + if self.color_scheme_table.active_scheme_name == 'NoColor': + self.color_scheme_table.set_active_scheme(self.old_scheme) + self.Colors = self.color_scheme_table.active_colors + else: + self.old_scheme = self.color_scheme_table.active_scheme_name + self.color_scheme_table.set_active_scheme('NoColor') + self.Colors = self.color_scheme_table.active_colors + + def stb2text(self, stb): + """Convert a structured traceback (a list) to a string.""" + return '\n'.join(stb) + + def text(self, etype, value, tb, tb_offset=None, context=5): + """Return formatted traceback. + + Subclasses may override this if they add extra arguments. + """ + tb_list = self.structured_traceback(etype, value, tb, + tb_offset, context) + return self.stb2text(tb_list) + + def structured_traceback(self, etype, evalue, tb, tb_offset=None, + context=5, mode=None): + """Return a list of traceback frames. + + Must be implemented by each class. + """ + raise NotImplementedError() + + +#--------------------------------------------------------------------------- +class ListTB(TBTools): + """Print traceback information from a traceback list, with optional color. + + Calling requires 3 arguments: (etype, evalue, elist) + as would be obtained by:: + + etype, evalue, tb = sys.exc_info() + if tb: + elist = traceback.extract_tb(tb) + else: + elist = None + + It can thus be used by programs which need to process the traceback before + printing (such as console replacements based on the code module from the + standard library). + + Because they are meant to be called without a full traceback (only a + list), instances of this class can't call the interactive pdb debugger.""" + + def __init__(self, color_scheme='NoColor', call_pdb=False, ostream=None, parent=None, config=None): + TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, + ostream=ostream, parent=parent,config=config) + + def __call__(self, etype, value, elist): + self.ostream.flush() + self.ostream.write(self.text(etype, value, elist)) + self.ostream.write('\n') + + def _extract_tb(self, tb): + if tb: + return traceback.extract_tb(tb) + else: + return None + + def structured_traceback(self, etype, evalue, etb=None, tb_offset=None, + context=5): + """Return a color formatted string with the traceback info. + + Parameters + ---------- + etype : exception type + Type of the exception raised. + + evalue : object + Data stored in the exception + + etb : object + If list: List of frames, see class docstring for details. + If Traceback: Traceback of the exception. + + tb_offset : int, optional + Number of frames in the traceback to skip. If not given, the + instance evalue is used (set in constructor). + + context : int, optional + Number of lines of context information to print. + + Returns + ------- + String with formatted exception. + """ + # This is a workaround to get chained_exc_ids in recursive calls + # etb should not be a tuple if structured_traceback is not recursive + if isinstance(etb, tuple): + etb, chained_exc_ids = etb + else: + chained_exc_ids = set() + + if isinstance(etb, list): + elist = etb + elif etb is not None: + elist = self._extract_tb(etb) + else: + elist = [] + tb_offset = self.tb_offset if tb_offset is None else tb_offset + Colors = self.Colors + out_list = [] + if elist: + + if tb_offset and len(elist) > tb_offset: + elist = elist[tb_offset:] + + out_list.append('Traceback %s(most recent call last)%s:' % + (Colors.normalEm, Colors.Normal) + '\n') + out_list.extend(self._format_list(elist)) + # The exception info should be a single entry in the list. + lines = ''.join(self._format_exception_only(etype, evalue)) + out_list.append(lines) + + exception = self.get_parts_of_chained_exception(evalue) + + if exception and not id(exception[1]) in chained_exc_ids: + chained_exception_message = self.prepare_chained_exception_message( + evalue.__cause__)[0] + etype, evalue, etb = exception + # Trace exception to avoid infinite 'cause' loop + chained_exc_ids.add(id(exception[1])) + chained_exceptions_tb_offset = 0 + out_list = ( + self.structured_traceback( + etype, evalue, (etb, chained_exc_ids), + chained_exceptions_tb_offset, context) + + chained_exception_message + + out_list) + + return out_list + + def _format_list(self, extracted_list): + """Format a list of traceback entry tuples for printing. + + Given a list of tuples as returned by extract_tb() or + extract_stack(), return a list of strings ready for printing. + Each string in the resulting list corresponds to the item with the + same index in the argument list. Each string ends in a newline; + the strings may contain internal newlines as well, for those items + whose source text line is not None. + + Lifted almost verbatim from traceback.py + """ + + Colors = self.Colors + list = [] + for filename, lineno, name, line in extracted_list[:-1]: + item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \ + (Colors.filename, filename, Colors.Normal, + Colors.lineno, lineno, Colors.Normal, + Colors.name, name, Colors.Normal) + if line: + item += ' %s\n' % line.strip() + list.append(item) + # Emphasize the last entry + filename, lineno, name, line = extracted_list[-1] + item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \ + (Colors.normalEm, + Colors.filenameEm, filename, Colors.normalEm, + Colors.linenoEm, lineno, Colors.normalEm, + Colors.nameEm, name, Colors.normalEm, + Colors.Normal) + if line: + item += '%s %s%s\n' % (Colors.line, line.strip(), + Colors.Normal) + list.append(item) + return list + + def _format_exception_only(self, etype, value): + """Format the exception part of a traceback. + + The arguments are the exception type and value such as given by + sys.exc_info()[:2]. The return value is a list of strings, each ending + in a newline. Normally, the list contains a single string; however, + for SyntaxError exceptions, it contains several lines that (when + printed) display detailed information about where the syntax error + occurred. The message indicating which exception occurred is the + always last string in the list. + + Also lifted nearly verbatim from traceback.py + """ + have_filedata = False + Colors = self.Colors + list = [] + stype = py3compat.cast_unicode(Colors.excName + etype.__name__ + Colors.Normal) + if value is None: + # Not sure if this can still happen in Python 2.6 and above + list.append(stype + '\n') + else: + if issubclass(etype, SyntaxError): + have_filedata = True + if not value.filename: value.filename = "" + if value.lineno: + lineno = value.lineno + textline = linecache.getline(value.filename, value.lineno) + else: + lineno = 'unknown' + textline = '' + list.append('%s File %s"%s"%s, line %s%s%s\n' % \ + (Colors.normalEm, + Colors.filenameEm, py3compat.cast_unicode(value.filename), Colors.normalEm, + Colors.linenoEm, lineno, Colors.Normal )) + if textline == '': + textline = py3compat.cast_unicode(value.text, "utf-8") + + if textline is not None: + i = 0 + while i < len(textline) and textline[i].isspace(): + i += 1 + list.append('%s %s%s\n' % (Colors.line, + textline.strip(), + Colors.Normal)) + if value.offset is not None: + s = ' ' + for c in textline[i:value.offset - 1]: + if c.isspace(): + s += c + else: + s += ' ' + list.append('%s%s^%s\n' % (Colors.caret, s, + Colors.Normal)) + + try: + s = value.msg + except Exception: + s = self._some_str(value) + if s: + list.append('%s%s:%s %s\n' % (stype, Colors.excName, + Colors.Normal, s)) + else: + list.append('%s\n' % stype) + + # sync with user hooks + if have_filedata: + ipinst = get_ipython() + if ipinst is not None: + ipinst.hooks.synchronize_with_editor(value.filename, value.lineno, 0) + + return list + + def get_exception_only(self, etype, value): + """Only print the exception type and message, without a traceback. + + Parameters + ---------- + etype : exception type + value : exception value + """ + return ListTB.structured_traceback(self, etype, value) + + def show_exception_only(self, etype, evalue): + """Only print the exception type and message, without a traceback. + + Parameters + ---------- + etype : exception type + value : exception value + """ + # This method needs to use __call__ from *this* class, not the one from + # a subclass whose signature or behavior may be different + ostream = self.ostream + ostream.flush() + ostream.write('\n'.join(self.get_exception_only(etype, evalue))) + ostream.flush() + + def _some_str(self, value): + # Lifted from traceback.py + try: + return py3compat.cast_unicode(str(value)) + except: + return u'' % type(value).__name__ + + +#---------------------------------------------------------------------------- +class VerboseTB(TBTools): + """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead + of HTML. Requires inspect and pydoc. Crazy, man. + + Modified version which optionally strips the topmost entries from the + traceback, to be used with alternate interpreters (because their own code + would appear in the traceback).""" + + def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None, + tb_offset=0, long_header=False, include_vars=True, + check_cache=None, debugger_cls = None, + parent=None, config=None): + """Specify traceback offset, headers and color scheme. + + Define how many frames to drop from the tracebacks. Calling it with + tb_offset=1 allows use of this handler in interpreters which will have + their own code at the top of the traceback (VerboseTB will first + remove that frame before printing the traceback info).""" + TBTools.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, + ostream=ostream, parent=parent, config=config) + self.tb_offset = tb_offset + self.long_header = long_header + self.include_vars = include_vars + # By default we use linecache.checkcache, but the user can provide a + # different check_cache implementation. This is used by the IPython + # kernel to provide tracebacks for interactive code that is cached, + # by a compiler instance that flushes the linecache but preserves its + # own code cache. + if check_cache is None: + check_cache = linecache.checkcache + self.check_cache = check_cache + + self.debugger_cls = debugger_cls or debugger.Pdb + self.skip_hidden = True + + def format_records(self, records, last_unique, recursion_repeat): + """Format the stack frames of the traceback""" + frames = [] + + skipped = 0 + lastrecord = len(records) - 1 + for i, r in enumerate(records[: last_unique + recursion_repeat + 1]): + if self.skip_hidden: + if r[0].f_locals.get("__tracebackhide__", 0) and i != lastrecord: + skipped += 1 + continue + if skipped: + Colors = self.Colors # just a shorthand + quicker name lookup + ColorsNormal = Colors.Normal # used a lot + frames.append( + " %s[... skipping hidden %s frame]%s\n" + % (Colors.excName, skipped, ColorsNormal) + ) + skipped = 0 + + frames.append(self.format_record(*r)) + + if skipped: + Colors = self.Colors # just a shorthand + quicker name lookup + ColorsNormal = Colors.Normal # used a lot + frames.append( + " %s[... skipping hidden %s frame]%s\n" + % (Colors.excName, skipped, ColorsNormal) + ) + + if recursion_repeat: + frames.append('... last %d frames repeated, from the frame below ...\n' % recursion_repeat) + frames.append(self.format_record(*records[last_unique+recursion_repeat+1])) + + return frames + + def format_record(self, frame, file, lnum, func, lines, index): + """Format a single stack frame""" + Colors = self.Colors # just a shorthand + quicker name lookup + ColorsNormal = Colors.Normal # used a lot + col_scheme = self.color_scheme_table.active_scheme_name + indent = ' ' * INDENT_SIZE + em_normal = '%s\n%s%s' % (Colors.valEm, indent, ColorsNormal) + undefined = '%sundefined%s' % (Colors.em, ColorsNormal) + tpl_link = '%s%%s%s' % (Colors.filenameEm, ColorsNormal) + tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm, + ColorsNormal) + tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \ + (Colors.vName, Colors.valEm, ColorsNormal) + tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal) + tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal, + Colors.vName, ColorsNormal) + tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal) + + if not file: + file = '?' + elif file.startswith(str("<")) and file.endswith(str(">")): + # Not a real filename, no problem... + pass + elif not os.path.isabs(file): + # Try to make the filename absolute by trying all + # sys.path entries (which is also what linecache does) + for dirname in sys.path: + try: + fullname = os.path.join(dirname, file) + if os.path.isfile(fullname): + file = os.path.abspath(fullname) + break + except Exception: + # Just in case that sys.path contains very + # strange entries... + pass + + file = py3compat.cast_unicode(file, util_path.fs_encoding) + link = tpl_link % util_path.compress_user(file) + args, varargs, varkw, locals_ = inspect.getargvalues(frame) + + if func == '?': + call = '' + elif func == '': + call = tpl_call % (func, '') + else: + # Decide whether to include variable details or not + var_repr = eqrepr if self.include_vars else nullrepr + try: + call = tpl_call % (func, inspect.formatargvalues(args, + varargs, varkw, + locals_, formatvalue=var_repr)) + except KeyError: + # This happens in situations like errors inside generator + # expressions, where local variables are listed in the + # line, but can't be extracted from the frame. I'm not + # 100% sure this isn't actually a bug in inspect itself, + # but since there's no info for us to compute with, the + # best we can do is report the failure and move on. Here + # we must *not* call any traceback construction again, + # because that would mess up use of %debug later on. So we + # simply report the failure and move on. The only + # limitation will be that this frame won't have locals + # listed in the call signature. Quite subtle problem... + # I can't think of a good way to validate this in a unit + # test, but running a script consisting of: + # dict( (k,v.strip()) for (k,v) in range(10) ) + # will illustrate the error, if this exception catch is + # disabled. + call = tpl_call_fail % func + + # Don't attempt to tokenize binary files. + if file.endswith(('.so', '.pyd', '.dll')): + return '%s %s\n' % (link, call) + + elif file.endswith(('.pyc', '.pyo')): + # Look up the corresponding source file. + try: + file = source_from_cache(file) + except ValueError: + # Failed to get the source file for some reason + # E.g. https://github.com/ipython/ipython/issues/9486 + return '%s %s\n' % (link, call) + + def linereader(file=file, lnum=[lnum], getline=linecache.getline): + line = getline(file, lnum[0]) + lnum[0] += 1 + return line + + # Build the list of names on this line of code where the exception + # occurred. + try: + names = [] + name_cont = False + + for token_type, token, start, end, line in generate_tokens(linereader): + # build composite names + if token_type == tokenize.NAME and token not in keyword.kwlist: + if name_cont: + # Continuation of a dotted name + try: + names[-1].append(token) + except IndexError: + names.append([token]) + name_cont = False + else: + # Regular new names. We append everything, the caller + # will be responsible for pruning the list later. It's + # very tricky to try to prune as we go, b/c composite + # names can fool us. The pruning at the end is easy + # to do (or the caller can print a list with repeated + # names if so desired. + names.append([token]) + elif token == '.': + name_cont = True + elif token_type == tokenize.NEWLINE: + break + + except (IndexError, UnicodeDecodeError, SyntaxError): + # signals exit of tokenizer + # SyntaxError can occur if the file is not actually Python + # - see gh-6300 + pass + except tokenize.TokenError as msg: + # Tokenizing may fail for various reasons, many of which are + # harmless. (A good example is when the line in question is the + # close of a triple-quoted string, cf gh-6864). We don't want to + # show this to users, but want make it available for debugging + # purposes. + _m = ("An unexpected error occurred while tokenizing input\n" + "The following traceback may be corrupted or invalid\n" + "The error message is: %s\n" % msg) + debug(_m) + + # Join composite names (e.g. "dict.fromkeys") + names = ['.'.join(n) for n in names] + # prune names list of duplicates, but keep the right order + unique_names = uniq_stable(names) + + # Start loop over vars + lvals = '' + lvals_list = [] + if self.include_vars: + for name_full in unique_names: + name_base = name_full.split('.', 1)[0] + if name_base in frame.f_code.co_varnames: + if name_base in locals_: + try: + value = repr(eval(name_full, locals_)) + except: + value = undefined + else: + value = undefined + name = tpl_local_var % name_full + else: + if name_base in frame.f_globals: + try: + value = repr(eval(name_full, frame.f_globals)) + except: + value = undefined + else: + value = undefined + name = tpl_global_var % name_full + lvals_list.append(tpl_name_val % (name, value)) + if lvals_list: + lvals = '%s%s' % (indent, em_normal.join(lvals_list)) + + level = '%s %s\n' % (link, call) + + if index is None: + return level + else: + _line_format = PyColorize.Parser(style=col_scheme, parent=self).format2 + return '%s%s' % (level, ''.join( + _format_traceback_lines(lnum, index, lines, Colors, lvals, + _line_format))) + + def prepare_header(self, etype, long_version=False): + colors = self.Colors # just a shorthand + quicker name lookup + colorsnormal = colors.Normal # used a lot + exc = '%s%s%s' % (colors.excName, etype, colorsnormal) + width = min(75, get_terminal_size()[0]) + if long_version: + # Header with the exception type, python version, and date + pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable + date = time.ctime(time.time()) + + head = '%s%s%s\n%s%s%s\n%s' % (colors.topline, '-' * width, colorsnormal, + exc, ' ' * (width - len(str(etype)) - len(pyver)), + pyver, date.rjust(width) ) + head += "\nA problem occurred executing Python code. Here is the sequence of function" \ + "\ncalls leading up to the error, with the most recent (innermost) call last." + else: + # Simplified header + head = '%s%s' % (exc, 'Traceback (most recent call last)'. \ + rjust(width - len(str(etype))) ) + + return head + + def format_exception(self, etype, evalue): + colors = self.Colors # just a shorthand + quicker name lookup + colorsnormal = colors.Normal # used a lot + # Get (safely) a string form of the exception info + try: + etype_str, evalue_str = map(str, (etype, evalue)) + except: + # User exception is improperly defined. + etype, evalue = str, sys.exc_info()[:2] + etype_str, evalue_str = map(str, (etype, evalue)) + # ... and format it + return ['%s%s%s: %s' % (colors.excName, etype_str, + colorsnormal, py3compat.cast_unicode(evalue_str))] + + def format_exception_as_a_whole(self, etype, evalue, etb, number_of_lines_of_context, tb_offset): + """Formats the header, traceback and exception message for a single exception. + + This may be called multiple times by Python 3 exception chaining + (PEP 3134). + """ + # some locals + orig_etype = etype + try: + etype = etype.__name__ + except AttributeError: + pass + + tb_offset = self.tb_offset if tb_offset is None else tb_offset + head = self.prepare_header(etype, self.long_header) + records = self.get_records(etb, number_of_lines_of_context, tb_offset) + + + last_unique, recursion_repeat = find_recursion(orig_etype, evalue, records) + + frames = self.format_records(records, last_unique, recursion_repeat) + + formatted_exception = self.format_exception(etype, evalue) + if records: + filepath, lnum = records[-1][1:3] + filepath = os.path.abspath(filepath) + ipinst = get_ipython() + if ipinst is not None: + ipinst.hooks.synchronize_with_editor(filepath, lnum, 0) + + return [[head] + frames + [''.join(formatted_exception[0])]] + + def get_records(self, etb, number_of_lines_of_context, tb_offset): + try: + # Try the default getinnerframes and Alex's: Alex's fixes some + # problems, but it generates empty tracebacks for console errors + # (5 blanks lines) where none should be returned. + return _fixed_getinnerframes(etb, number_of_lines_of_context, tb_offset) + except UnicodeDecodeError: + # This can occur if a file's encoding magic comment is wrong. + # I can't see a way to recover without duplicating a bunch of code + # from the stdlib traceback module. --TK + error('\nUnicodeDecodeError while processing traceback.\n') + return None + except: + # FIXME: I've been getting many crash reports from python 2.3 + # users, traceable to inspect.py. If I can find a small test-case + # to reproduce this, I should either write a better workaround or + # file a bug report against inspect (if that's the real problem). + # So far, I haven't been able to find an isolated example to + # reproduce the problem. + inspect_error() + traceback.print_exc(file=self.ostream) + info('\nUnfortunately, your original traceback can not be constructed.\n') + return None + + def structured_traceback(self, etype, evalue, etb, tb_offset=None, + number_of_lines_of_context=5): + """Return a nice text document describing the traceback.""" + + formatted_exception = self.format_exception_as_a_whole(etype, evalue, etb, number_of_lines_of_context, + tb_offset) + + colors = self.Colors # just a shorthand + quicker name lookup + colorsnormal = colors.Normal # used a lot + head = '%s%s%s' % (colors.topline, '-' * min(75, get_terminal_size()[0]), colorsnormal) + structured_traceback_parts = [head] + chained_exceptions_tb_offset = 0 + lines_of_context = 3 + formatted_exceptions = formatted_exception + exception = self.get_parts_of_chained_exception(evalue) + if exception: + formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__) + etype, evalue, etb = exception + else: + evalue = None + chained_exc_ids = set() + while evalue: + formatted_exceptions += self.format_exception_as_a_whole(etype, evalue, etb, lines_of_context, + chained_exceptions_tb_offset) + exception = self.get_parts_of_chained_exception(evalue) + + if exception and not id(exception[1]) in chained_exc_ids: + chained_exc_ids.add(id(exception[1])) # trace exception to avoid infinite 'cause' loop + formatted_exceptions += self.prepare_chained_exception_message(evalue.__cause__) + etype, evalue, etb = exception + else: + evalue = None + + # we want to see exceptions in a reversed order: + # the first exception should be on top + for formatted_exception in reversed(formatted_exceptions): + structured_traceback_parts += formatted_exception + + return structured_traceback_parts + + def debugger(self, force=False): + """Call up the pdb debugger if desired, always clean up the tb + reference. + + Keywords: + + - force(False): by default, this routine checks the instance call_pdb + flag and does not actually invoke the debugger if the flag is false. + The 'force' option forces the debugger to activate even if the flag + is false. + + If the call_pdb flag is set, the pdb interactive debugger is + invoked. In all cases, the self.tb reference to the current traceback + is deleted to prevent lingering references which hamper memory + management. + + Note that each call to pdb() does an 'import readline', so if your app + requires a special setup for the readline completers, you'll have to + fix that by hand after invoking the exception handler.""" + + if force or self.call_pdb: + if self.pdb is None: + self.pdb = self.debugger_cls() + # the system displayhook may have changed, restore the original + # for pdb + display_trap = DisplayTrap(hook=sys.__displayhook__) + with display_trap: + self.pdb.reset() + # Find the right frame so we don't pop up inside ipython itself + if hasattr(self, 'tb') and self.tb is not None: + etb = self.tb + else: + etb = self.tb = sys.last_traceback + while self.tb is not None and self.tb.tb_next is not None: + self.tb = self.tb.tb_next + if etb and etb.tb_next: + etb = etb.tb_next + self.pdb.botframe = etb.tb_frame + self.pdb.interaction(None, etb) + + if hasattr(self, 'tb'): + del self.tb + + def handler(self, info=None): + (etype, evalue, etb) = info or sys.exc_info() + self.tb = etb + ostream = self.ostream + ostream.flush() + ostream.write(self.text(etype, evalue, etb)) + ostream.write('\n') + ostream.flush() + + # Changed so an instance can just be called as VerboseTB_inst() and print + # out the right info on its own. + def __call__(self, etype=None, evalue=None, etb=None): + """This hook can replace sys.excepthook (for Python 2.1 or higher).""" + if etb is None: + self.handler() + else: + self.handler((etype, evalue, etb)) + try: + self.debugger() + except KeyboardInterrupt: + print("\nKeyboardInterrupt") + + +#---------------------------------------------------------------------------- +class FormattedTB(VerboseTB, ListTB): + """Subclass ListTB but allow calling with a traceback. + + It can thus be used as a sys.excepthook for Python > 2.1. + + Also adds 'Context' and 'Verbose' modes, not available in ListTB. + + Allows a tb_offset to be specified. This is useful for situations where + one needs to remove a number of topmost frames from the traceback (such as + occurs with python programs that themselves execute other python code, + like Python shells). """ + + def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False, + ostream=None, + tb_offset=0, long_header=False, include_vars=False, + check_cache=None, debugger_cls=None, + parent=None, config=None): + + # NEVER change the order of this list. Put new modes at the end: + self.valid_modes = ['Plain', 'Context', 'Verbose', 'Minimal'] + self.verbose_modes = self.valid_modes[1:3] + + VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, + ostream=ostream, tb_offset=tb_offset, + long_header=long_header, include_vars=include_vars, + check_cache=check_cache, debugger_cls=debugger_cls, + parent=parent, config=config) + + # Different types of tracebacks are joined with different separators to + # form a single string. They are taken from this dict + self._join_chars = dict(Plain='', Context='\n', Verbose='\n', + Minimal='') + # set_mode also sets the tb_join_char attribute + self.set_mode(mode) + + def structured_traceback(self, etype, value, tb, tb_offset=None, number_of_lines_of_context=5): + tb_offset = self.tb_offset if tb_offset is None else tb_offset + mode = self.mode + if mode in self.verbose_modes: + # Verbose modes need a full traceback + return VerboseTB.structured_traceback( + self, etype, value, tb, tb_offset, number_of_lines_of_context + ) + elif mode == 'Minimal': + return ListTB.get_exception_only(self, etype, value) + else: + # We must check the source cache because otherwise we can print + # out-of-date source code. + self.check_cache() + # Now we can extract and format the exception + return ListTB.structured_traceback( + self, etype, value, tb, tb_offset, number_of_lines_of_context + ) + + def stb2text(self, stb): + """Convert a structured traceback (a list) to a string.""" + return self.tb_join_char.join(stb) + + + def set_mode(self, mode=None): + """Switch to the desired mode. + + If mode is not specified, cycles through the available modes.""" + + if not mode: + new_idx = (self.valid_modes.index(self.mode) + 1 ) % \ + len(self.valid_modes) + self.mode = self.valid_modes[new_idx] + elif mode not in self.valid_modes: + raise ValueError('Unrecognized mode in FormattedTB: <' + mode + '>\n' + 'Valid modes: ' + str(self.valid_modes)) + else: + self.mode = mode + # include variable details only in 'Verbose' mode + self.include_vars = (self.mode == self.valid_modes[2]) + # Set the join character for generating text tracebacks + self.tb_join_char = self._join_chars[self.mode] + + # some convenient shortcuts + def plain(self): + self.set_mode(self.valid_modes[0]) + + def context(self): + self.set_mode(self.valid_modes[1]) + + def verbose(self): + self.set_mode(self.valid_modes[2]) + + def minimal(self): + self.set_mode(self.valid_modes[3]) + + +#---------------------------------------------------------------------------- +class AutoFormattedTB(FormattedTB): + """A traceback printer which can be called on the fly. + + It will find out about exceptions by itself. + + A brief example:: + + AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux') + try: + ... + except: + AutoTB() # or AutoTB(out=logfile) where logfile is an open file object + """ + + def __call__(self, etype=None, evalue=None, etb=None, + out=None, tb_offset=None): + """Print out a formatted exception traceback. + + Optional arguments: + - out: an open file-like object to direct output to. + + - tb_offset: the number of frames to skip over in the stack, on a + per-call basis (this overrides temporarily the instance's tb_offset + given at initialization time. """ + + if out is None: + out = self.ostream + out.flush() + out.write(self.text(etype, evalue, etb, tb_offset)) + out.write('\n') + out.flush() + # FIXME: we should remove the auto pdb behavior from here and leave + # that to the clients. + try: + self.debugger() + except KeyboardInterrupt: + print("\nKeyboardInterrupt") + + def structured_traceback(self, etype=None, value=None, tb=None, + tb_offset=None, number_of_lines_of_context=5): + if etype is None: + etype, value, tb = sys.exc_info() + if isinstance(tb, tuple): + # tb is a tuple if this is a chained exception. + self.tb = tb[0] + else: + self.tb = tb + return FormattedTB.structured_traceback( + self, etype, value, tb, tb_offset, number_of_lines_of_context) + + +#--------------------------------------------------------------------------- + +# A simple class to preserve Nathan's original functionality. +class ColorTB(FormattedTB): + """Shorthand to initialize a FormattedTB in Linux colors mode.""" + + def __init__(self, color_scheme='Linux', call_pdb=0, **kwargs): + FormattedTB.__init__(self, color_scheme=color_scheme, + call_pdb=call_pdb, **kwargs) + + +class SyntaxTB(ListTB): + """Extension which holds some state: the last exception value""" + + def __init__(self, color_scheme='NoColor', parent=None, config=None): + ListTB.__init__(self, color_scheme, parent=parent, config=config) + self.last_syntax_error = None + + def __call__(self, etype, value, elist): + self.last_syntax_error = value + + ListTB.__call__(self, etype, value, elist) + + def structured_traceback(self, etype, value, elist, tb_offset=None, + context=5): + # If the source file has been edited, the line in the syntax error can + # be wrong (retrieved from an outdated cache). This replaces it with + # the current value. + if isinstance(value, SyntaxError) \ + and isinstance(value.filename, str) \ + and isinstance(value.lineno, int): + linecache.checkcache(value.filename) + newtext = linecache.getline(value.filename, value.lineno) + if newtext: + value.text = newtext + self.last_syntax_error = value + return super(SyntaxTB, self).structured_traceback(etype, value, elist, + tb_offset=tb_offset, context=context) + + def clear_err_state(self): + """Return the current error state and clear it""" + e = self.last_syntax_error + self.last_syntax_error = None + return e + + def stb2text(self, stb): + """Convert a structured traceback (a list) to a string.""" + return ''.join(stb) + + +# some internal-use functions +def text_repr(value): + """Hopefully pretty robust repr equivalent.""" + # this is pretty horrible but should always return *something* + try: + return pydoc.text.repr(value) + except KeyboardInterrupt: + raise + except: + try: + return repr(value) + except KeyboardInterrupt: + raise + except: + try: + # all still in an except block so we catch + # getattr raising + name = getattr(value, '__name__', None) + if name: + # ick, recursion + return text_repr(name) + klass = getattr(value, '__class__', None) + if klass: + return '%s instance' % text_repr(klass) + except KeyboardInterrupt: + raise + except: + return 'UNRECOVERABLE REPR FAILURE' + + +def eqrepr(value, repr=text_repr): + return '=%s' % repr(value) + + +def nullrepr(value, repr=text_repr): + return '' diff --git a/.venv/lib/python3.8/site-packages/IPython/core/usage.py b/.venv/lib/python3.8/site-packages/IPython/core/usage.py new file mode 100644 index 00000000..37024c44 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/core/usage.py @@ -0,0 +1,341 @@ +# -*- coding: utf-8 -*- +"""Usage information for the main IPython applications. +""" +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# Copyright (C) 2001-2007 Fernando Perez. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +import sys +from IPython.core import release + +cl_usage = """\ +========= + IPython +========= + +Tools for Interactive Computing in Python +========================================= + + A Python shell with automatic history (input and output), dynamic object + introspection, easier configuration, command completion, access to the + system shell and more. IPython can also be embedded in running programs. + + +Usage + + ipython [subcommand] [options] [-c cmd | -m mod | file] [--] [arg] ... + + If invoked with no options, it executes the file and exits, passing the + remaining arguments to the script, just as if you had specified the same + command with python. You may need to specify `--` before args to be passed + to the script, to prevent IPython from attempting to parse them. If you + specify the option `-i` before the filename, it will enter an interactive + IPython session after running the script, rather than exiting. Files ending + in .py will be treated as normal Python, but files ending in .ipy can + contain special IPython syntax (magic commands, shell expansions, etc.). + + Almost all configuration in IPython is available via the command-line. Do + `ipython --help-all` to see all available options. For persistent + configuration, look into your `ipython_config.py` configuration file for + details. + + This file is typically installed in the `IPYTHONDIR` directory, and there + is a separate configuration directory for each profile. The default profile + directory will be located in $IPYTHONDIR/profile_default. IPYTHONDIR + defaults to to `$HOME/.ipython`. For Windows users, $HOME resolves to + C:\\Users\\YourUserName in most instances. + + To initialize a profile with the default configuration file, do:: + + $> ipython profile create + + and start editing `IPYTHONDIR/profile_default/ipython_config.py` + + In IPython's documentation, we will refer to this directory as + `IPYTHONDIR`, you can change its default location by creating an + environment variable with this name and setting it to the desired path. + + For more information, see the manual available in HTML and PDF in your + installation, or online at https://ipython.org/documentation.html. +""" + +interactive_usage = """ +IPython -- An enhanced Interactive Python +========================================= + +IPython offers a fully compatible replacement for the standard Python +interpreter, with convenient shell features, special commands, command +history mechanism and output results caching. + +At your system command line, type 'ipython -h' to see the command line +options available. This document only describes interactive features. + +GETTING HELP +------------ + +Within IPython you have various way to access help: + + ? -> Introduction and overview of IPython's features (this screen). + object? -> Details about 'object'. + object?? -> More detailed, verbose information about 'object'. + %quickref -> Quick reference of all IPython specific syntax and magics. + help -> Access Python's own help system. + +If you are in terminal IPython you can quit this screen by pressing `q`. + + +MAIN FEATURES +------------- + +* Access to the standard Python help with object docstrings and the Python + manuals. Simply type 'help' (no quotes) to invoke it. + +* Magic commands: type %magic for information on the magic subsystem. + +* System command aliases, via the %alias command or the configuration file(s). + +* Dynamic object information: + + Typing ?word or word? prints detailed information about an object. Certain + long strings (code, etc.) get snipped in the center for brevity. + + Typing ??word or word?? gives access to the full information without + snipping long strings. Strings that are longer than the screen are printed + through the less pager. + + The ?/?? system gives access to the full source code for any object (if + available), shows function prototypes and other useful information. + + If you just want to see an object's docstring, type '%pdoc object' (without + quotes, and without % if you have automagic on). + +* Tab completion in the local namespace: + + At any time, hitting tab will complete any available python commands or + variable names, and show you a list of the possible completions if there's + no unambiguous one. It will also complete filenames in the current directory. + +* Search previous command history in multiple ways: + + - Start typing, and then use arrow keys up/down or (Ctrl-p/Ctrl-n) to search + through the history items that match what you've typed so far. + + - Hit Ctrl-r: opens a search prompt. Begin typing and the system searches + your history for lines that match what you've typed so far, completing as + much as it can. + + - %hist: search history by index. + +* Persistent command history across sessions. + +* Logging of input with the ability to save and restore a working session. + +* System shell with !. Typing !ls will run 'ls' in the current directory. + +* The reload command does a 'deep' reload of a module: changes made to the + module since you imported will actually be available without having to exit. + +* Verbose and colored exception traceback printouts. See the magic xmode and + xcolor functions for details (just type %magic). + +* Input caching system: + + IPython offers numbered prompts (In/Out) with input and output caching. All + input is saved and can be retrieved as variables (besides the usual arrow + key recall). + + The following GLOBAL variables always exist (so don't overwrite them!): + _i: stores previous input. + _ii: next previous. + _iii: next-next previous. + _ih : a list of all input _ih[n] is the input from line n. + + Additionally, global variables named _i are dynamically created ( + being the prompt counter), such that _i == _ih[] + + For example, what you typed at prompt 14 is available as _i14 and _ih[14]. + + You can create macros which contain multiple input lines from this history, + for later re-execution, with the %macro function. + + The history function %hist allows you to see any part of your input history + by printing a range of the _i variables. Note that inputs which contain + magic functions (%) appear in the history with a prepended comment. This is + because they aren't really valid Python code, so you can't exec them. + +* Output caching system: + + For output that is returned from actions, a system similar to the input + cache exists but using _ instead of _i. Only actions that produce a result + (NOT assignments, for example) are cached. If you are familiar with + Mathematica, IPython's _ variables behave exactly like Mathematica's % + variables. + + The following GLOBAL variables always exist (so don't overwrite them!): + _ (one underscore): previous output. + __ (two underscores): next previous. + ___ (three underscores): next-next previous. + + Global variables named _ are dynamically created ( being the prompt + counter), such that the result of output is always available as _. + + Finally, a global dictionary named _oh exists with entries for all lines + which generated output. + +* Directory history: + + Your history of visited directories is kept in the global list _dh, and the + magic %cd command can be used to go to any entry in that list. + +* Auto-parentheses and auto-quotes (adapted from Nathan Gray's LazyPython) + + 1. Auto-parentheses + + Callable objects (i.e. functions, methods, etc) can be invoked like + this (notice the commas between the arguments):: + + In [1]: callable_ob arg1, arg2, arg3 + + and the input will be translated to this:: + + callable_ob(arg1, arg2, arg3) + + This feature is off by default (in rare cases it can produce + undesirable side-effects), but you can activate it at the command-line + by starting IPython with `--autocall 1`, set it permanently in your + configuration file, or turn on at runtime with `%autocall 1`. + + You can force auto-parentheses by using '/' as the first character + of a line. For example:: + + In [1]: /globals # becomes 'globals()' + + Note that the '/' MUST be the first character on the line! This + won't work:: + + In [2]: print /globals # syntax error + + In most cases the automatic algorithm should work, so you should + rarely need to explicitly invoke /. One notable exception is if you + are trying to call a function with a list of tuples as arguments (the + parenthesis will confuse IPython):: + + In [1]: zip (1,2,3),(4,5,6) # won't work + + but this will work:: + + In [2]: /zip (1,2,3),(4,5,6) + ------> zip ((1,2,3),(4,5,6)) + Out[2]= [(1, 4), (2, 5), (3, 6)] + + IPython tells you that it has altered your command line by + displaying the new command line preceded by -->. e.g.:: + + In [18]: callable list + -------> callable (list) + + 2. Auto-Quoting + + You can force auto-quoting of a function's arguments by using ',' as + the first character of a line. For example:: + + In [1]: ,my_function /home/me # becomes my_function("/home/me") + + If you use ';' instead, the whole argument is quoted as a single + string (while ',' splits on whitespace):: + + In [2]: ,my_function a b c # becomes my_function("a","b","c") + In [3]: ;my_function a b c # becomes my_function("a b c") + + Note that the ',' MUST be the first character on the line! This + won't work:: + + In [4]: x = ,my_function /home/me # syntax error +""" + +interactive_usage_min = """\ +An enhanced console for Python. +Some of its features are: +- Tab completion in the local namespace. +- Logging of input, see command-line options. +- System shell escape via ! , eg !ls. +- Magic commands, starting with a % (like %ls, %pwd, %cd, etc.) +- Keeps track of locally defined variables via %who, %whos. +- Show object information with a ? eg ?x or x? (use ?? for more info). +""" + +quick_reference = r""" +IPython -- An enhanced Interactive Python - Quick Reference Card +================================================================ + +obj?, obj?? : Get help, or more help for object (also works as + ?obj, ??obj). +?foo.*abc* : List names in 'foo' containing 'abc' in them. +%magic : Information about IPython's 'magic' % functions. + +Magic functions are prefixed by % or %%, and typically take their arguments +without parentheses, quotes or even commas for convenience. Line magics take a +single % and cell magics are prefixed with two %%. + +Example magic function calls: + +%alias d ls -F : 'd' is now an alias for 'ls -F' +alias d ls -F : Works if 'alias' not a python name +alist = %alias : Get list of aliases to 'alist' +cd /usr/share : Obvious. cd - to choose from visited dirs. +%cd?? : See help AND source for magic %cd +%timeit x=10 : time the 'x=10' statement with high precision. +%%timeit x=2**100 +x**100 : time 'x**100' with a setup of 'x=2**100'; setup code is not + counted. This is an example of a cell magic. + +System commands: + +!cp a.txt b/ : System command escape, calls os.system() +cp a.txt b/ : after %rehashx, most system commands work without ! +cp ${f}.txt $bar : Variable expansion in magics and system commands +files = !ls /usr : Capture system command output +files.s, files.l, files.n: "a b c", ['a','b','c'], 'a\nb\nc' + +History: + +_i, _ii, _iii : Previous, next previous, next next previous input +_i4, _ih[2:5] : Input history line 4, lines 2-4 +exec _i81 : Execute input history line #81 again +%rep 81 : Edit input history line #81 +_, __, ___ : previous, next previous, next next previous output +_dh : Directory history +_oh : Output history +%hist : Command history of current session. +%hist -g foo : Search command history of (almost) all sessions for 'foo'. +%hist -g : Command history of (almost) all sessions. +%hist 1/2-8 : Command history containing lines 2-8 of session 1. +%hist 1/ ~2/ : Command history of session 1 and 2 sessions before current. +%hist ~8/1-~6/5 : Command history from line 1 of 8 sessions ago to + line 5 of 6 sessions ago. +%edit 0/ : Open editor to execute code with history of current session. + +Autocall: + +f 1,2 : f(1,2) # Off by default, enable with %autocall magic. +/f 1,2 : f(1,2) (forced autoparen) +,f 1 2 : f("1","2") +;f 1 2 : f("1 2") + +Remember: TAB completion works in many contexts, not just file names +or python names. + +The following magic functions are currently available: + +""" + +default_banner_parts = ["Python %s\n"%sys.version.split("\n")[0], + "Type 'copyright', 'credits' or 'license' for more information\n" , + "IPython {version} -- An enhanced Interactive Python. Type '?' for help.\n".format(version=release.version), +] + +default_banner = ''.join(default_banner_parts) diff --git a/.venv/lib/python3.8/site-packages/IPython/display.py b/.venv/lib/python3.8/site-packages/IPython/display.py new file mode 100644 index 00000000..7d248ba0 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/display.py @@ -0,0 +1,16 @@ +"""Public API for display tools in IPython. +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2012 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +from IPython.core.display import * +from IPython.lib.display import * diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/__init__.py b/.venv/lib/python3.8/site-packages/IPython/extensions/__init__.py new file mode 100644 index 00000000..db7f79fc --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/extensions/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +"""This directory is meant for IPython extensions.""" diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..e46b82af Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/autoreload.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/autoreload.cpython-38.pyc new file mode 100644 index 00000000..51f0327e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/autoreload.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/cythonmagic.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/cythonmagic.cpython-38.pyc new file mode 100644 index 00000000..9b593a39 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/cythonmagic.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/rmagic.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/rmagic.cpython-38.pyc new file mode 100644 index 00000000..45171b89 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/rmagic.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/storemagic.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/storemagic.cpython-38.pyc new file mode 100644 index 00000000..28a95367 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/storemagic.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/sympyprinting.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/sympyprinting.cpython-38.pyc new file mode 100644 index 00000000..7c0cfe62 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/__pycache__/sympyprinting.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/autoreload.py b/.venv/lib/python3.8/site-packages/IPython/extensions/autoreload.py new file mode 100644 index 00000000..ada680fc --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/extensions/autoreload.py @@ -0,0 +1,550 @@ +"""IPython extension to reload modules before executing user code. + +``autoreload`` reloads modules automatically before entering the execution of +code typed at the IPython prompt. + +This makes for example the following workflow possible: + +.. sourcecode:: ipython + + In [1]: %load_ext autoreload + + In [2]: %autoreload 2 + + In [3]: from foo import some_function + + In [4]: some_function() + Out[4]: 42 + + In [5]: # open foo.py in an editor and change some_function to return 43 + + In [6]: some_function() + Out[6]: 43 + +The module was reloaded without reloading it explicitly, and the object +imported with ``from foo import ...`` was also updated. + +Usage +===== + +The following magic commands are provided: + +``%autoreload`` + + Reload all modules (except those excluded by ``%aimport``) + automatically now. + +``%autoreload 0`` + + Disable automatic reloading. + +``%autoreload 1`` + + Reload all modules imported with ``%aimport`` every time before + executing the Python code typed. + +``%autoreload 2`` + + Reload all modules (except those excluded by ``%aimport``) every + time before executing the Python code typed. + +``%aimport`` + + List modules which are to be automatically imported or not to be imported. + +``%aimport foo`` + + Import module 'foo' and mark it to be autoreloaded for ``%autoreload 1`` + +``%aimport foo, bar`` + + Import modules 'foo', 'bar' and mark them to be autoreloaded for ``%autoreload 1`` + +``%aimport -foo`` + + Mark module 'foo' to not be autoreloaded. + +Caveats +======= + +Reloading Python modules in a reliable way is in general difficult, +and unexpected things may occur. ``%autoreload`` tries to work around +common pitfalls by replacing function code objects and parts of +classes previously in the module with new versions. This makes the +following things to work: + +- Functions and classes imported via 'from xxx import foo' are upgraded + to new versions when 'xxx' is reloaded. + +- Methods and properties of classes are upgraded on reload, so that + calling 'c.foo()' on an object 'c' created before the reload causes + the new code for 'foo' to be executed. + +Some of the known remaining caveats are: + +- Replacing code objects does not always succeed: changing a @property + in a class to an ordinary method or a method to a member variable + can cause problems (but in old objects only). + +- Functions that are removed (eg. via monkey-patching) from a module + before it is reloaded are not upgraded. + +- C extension modules cannot be reloaded, and so cannot be autoreloaded. +""" + +skip_doctest = True + +#----------------------------------------------------------------------------- +# Copyright (C) 2000 Thomas Heller +# Copyright (C) 2008 Pauli Virtanen +# Copyright (C) 2012 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- +# +# This IPython module is written by Pauli Virtanen, based on the autoreload +# code by Thomas Heller. + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os +import sys +import traceback +import types +import weakref +import gc +from importlib import import_module +from importlib.util import source_from_cache +from imp import reload + +#------------------------------------------------------------------------------ +# Autoreload functionality +#------------------------------------------------------------------------------ + +class ModuleReloader(object): + enabled = False + """Whether this reloader is enabled""" + + check_all = True + """Autoreload all modules, not just those listed in 'modules'""" + + def __init__(self): + # Modules that failed to reload: {module: mtime-on-failed-reload, ...} + self.failed = {} + # Modules specially marked as autoreloadable. + self.modules = {} + # Modules specially marked as not autoreloadable. + self.skip_modules = {} + # (module-name, name) -> weakref, for replacing old code objects + self.old_objects = {} + # Module modification timestamps + self.modules_mtimes = {} + + # Cache module modification times + self.check(check_all=True, do_reload=False) + + def mark_module_skipped(self, module_name): + """Skip reloading the named module in the future""" + try: + del self.modules[module_name] + except KeyError: + pass + self.skip_modules[module_name] = True + + def mark_module_reloadable(self, module_name): + """Reload the named module in the future (if it is imported)""" + try: + del self.skip_modules[module_name] + except KeyError: + pass + self.modules[module_name] = True + + def aimport_module(self, module_name): + """Import a module, and mark it reloadable + + Returns + ------- + top_module : module + The imported module if it is top-level, or the top-level + top_name : module + Name of top_module + + """ + self.mark_module_reloadable(module_name) + + import_module(module_name) + top_name = module_name.split('.')[0] + top_module = sys.modules[top_name] + return top_module, top_name + + def filename_and_mtime(self, module): + if not hasattr(module, '__file__') or module.__file__ is None: + return None, None + + if getattr(module, '__name__', None) in [None, '__mp_main__', '__main__']: + # we cannot reload(__main__) or reload(__mp_main__) + return None, None + + filename = module.__file__ + path, ext = os.path.splitext(filename) + + if ext.lower() == '.py': + py_filename = filename + else: + try: + py_filename = source_from_cache(filename) + except ValueError: + return None, None + + try: + pymtime = os.stat(py_filename).st_mtime + except OSError: + return None, None + + return py_filename, pymtime + + def check(self, check_all=False, do_reload=True): + """Check whether some modules need to be reloaded.""" + + if not self.enabled and not check_all: + return + + if check_all or self.check_all: + modules = list(sys.modules.keys()) + else: + modules = list(self.modules.keys()) + + for modname in modules: + m = sys.modules.get(modname, None) + + if modname in self.skip_modules: + continue + + py_filename, pymtime = self.filename_and_mtime(m) + if py_filename is None: + continue + + try: + if pymtime <= self.modules_mtimes[modname]: + continue + except KeyError: + self.modules_mtimes[modname] = pymtime + continue + else: + if self.failed.get(py_filename, None) == pymtime: + continue + + self.modules_mtimes[modname] = pymtime + + # If we've reached this point, we should try to reload the module + if do_reload: + try: + superreload(m, reload, self.old_objects) + if py_filename in self.failed: + del self.failed[py_filename] + except: + print("[autoreload of %s failed: %s]" % ( + modname, traceback.format_exc(10)), file=sys.stderr) + self.failed[py_filename] = pymtime + +#------------------------------------------------------------------------------ +# superreload +#------------------------------------------------------------------------------ + + +func_attrs = ['__code__', '__defaults__', '__doc__', + '__closure__', '__globals__', '__dict__'] + + +def update_function(old, new): + """Upgrade the code object of a function""" + for name in func_attrs: + try: + setattr(old, name, getattr(new, name)) + except (AttributeError, TypeError): + pass + + +def update_instances(old, new): + """Use garbage collector to find all instances that refer to the old + class definition and update their __class__ to point to the new class + definition""" + + refs = gc.get_referrers(old) + + for ref in refs: + if type(ref) is old: + ref.__class__ = new + + +def update_class(old, new): + """Replace stuff in the __dict__ of a class, and upgrade + method code objects, and add new methods, if any""" + for key in list(old.__dict__.keys()): + old_obj = getattr(old, key) + try: + new_obj = getattr(new, key) + # explicitly checking that comparison returns True to handle + # cases where `==` doesn't return a boolean. + if (old_obj == new_obj) is True: + continue + except AttributeError: + # obsolete attribute: remove it + try: + delattr(old, key) + except (AttributeError, TypeError): + pass + continue + + if update_generic(old_obj, new_obj): continue + + try: + setattr(old, key, getattr(new, key)) + except (AttributeError, TypeError): + pass # skip non-writable attributes + + for key in list(new.__dict__.keys()): + if key not in list(old.__dict__.keys()): + try: + setattr(old, key, getattr(new, key)) + except (AttributeError, TypeError): + pass # skip non-writable attributes + + # update all instances of class + update_instances(old, new) + + +def update_property(old, new): + """Replace get/set/del functions of a property""" + update_generic(old.fdel, new.fdel) + update_generic(old.fget, new.fget) + update_generic(old.fset, new.fset) + + +def isinstance2(a, b, typ): + return isinstance(a, typ) and isinstance(b, typ) + + +UPDATE_RULES = [ + (lambda a, b: isinstance2(a, b, type), + update_class), + (lambda a, b: isinstance2(a, b, types.FunctionType), + update_function), + (lambda a, b: isinstance2(a, b, property), + update_property), +] +UPDATE_RULES.extend([(lambda a, b: isinstance2(a, b, types.MethodType), + lambda a, b: update_function(a.__func__, b.__func__)), +]) + + +def update_generic(a, b): + for type_check, update in UPDATE_RULES: + if type_check(a, b): + update(a, b) + return True + return False + + +class StrongRef(object): + def __init__(self, obj): + self.obj = obj + def __call__(self): + return self.obj + + +def superreload(module, reload=reload, old_objects=None): + """Enhanced version of the builtin reload function. + + superreload remembers objects previously in the module, and + + - upgrades the class dictionary of every old class in the module + - upgrades the code object of every old function and method + - clears the module's namespace before reloading + + """ + if old_objects is None: + old_objects = {} + + # collect old objects in the module + for name, obj in list(module.__dict__.items()): + if not hasattr(obj, '__module__') or obj.__module__ != module.__name__: + continue + key = (module.__name__, name) + try: + old_objects.setdefault(key, []).append(weakref.ref(obj)) + except TypeError: + pass + + # reload module + try: + # clear namespace first from old cruft + old_dict = module.__dict__.copy() + old_name = module.__name__ + module.__dict__.clear() + module.__dict__['__name__'] = old_name + module.__dict__['__loader__'] = old_dict['__loader__'] + except (TypeError, AttributeError, KeyError): + pass + + try: + module = reload(module) + except: + # restore module dictionary on failed reload + module.__dict__.update(old_dict) + raise + + # iterate over all objects and update functions & classes + for name, new_obj in list(module.__dict__.items()): + key = (module.__name__, name) + if key not in old_objects: continue + + new_refs = [] + for old_ref in old_objects[key]: + old_obj = old_ref() + if old_obj is None: continue + new_refs.append(old_ref) + update_generic(old_obj, new_obj) + + if new_refs: + old_objects[key] = new_refs + else: + del old_objects[key] + + return module + +#------------------------------------------------------------------------------ +# IPython connectivity +#------------------------------------------------------------------------------ + +from IPython.core.magic import Magics, magics_class, line_magic + +@magics_class +class AutoreloadMagics(Magics): + def __init__(self, *a, **kw): + super(AutoreloadMagics, self).__init__(*a, **kw) + self._reloader = ModuleReloader() + self._reloader.check_all = False + self.loaded_modules = set(sys.modules) + + @line_magic + def autoreload(self, parameter_s=''): + r"""%autoreload => Reload modules automatically + + %autoreload + Reload all modules (except those excluded by %aimport) automatically + now. + + %autoreload 0 + Disable automatic reloading. + + %autoreload 1 + Reload all modules imported with %aimport every time before executing + the Python code typed. + + %autoreload 2 + Reload all modules (except those excluded by %aimport) every time + before executing the Python code typed. + + Reloading Python modules in a reliable way is in general + difficult, and unexpected things may occur. %autoreload tries to + work around common pitfalls by replacing function code objects and + parts of classes previously in the module with new versions. This + makes the following things to work: + + - Functions and classes imported via 'from xxx import foo' are upgraded + to new versions when 'xxx' is reloaded. + + - Methods and properties of classes are upgraded on reload, so that + calling 'c.foo()' on an object 'c' created before the reload causes + the new code for 'foo' to be executed. + + Some of the known remaining caveats are: + + - Replacing code objects does not always succeed: changing a @property + in a class to an ordinary method or a method to a member variable + can cause problems (but in old objects only). + + - Functions that are removed (eg. via monkey-patching) from a module + before it is reloaded are not upgraded. + + - C extension modules cannot be reloaded, and so cannot be + autoreloaded. + + """ + if parameter_s == '': + self._reloader.check(True) + elif parameter_s == '0': + self._reloader.enabled = False + elif parameter_s == '1': + self._reloader.check_all = False + self._reloader.enabled = True + elif parameter_s == '2': + self._reloader.check_all = True + self._reloader.enabled = True + + @line_magic + def aimport(self, parameter_s='', stream=None): + """%aimport => Import modules for automatic reloading. + + %aimport + List modules to automatically import and not to import. + + %aimport foo + Import module 'foo' and mark it to be autoreloaded for %autoreload 1 + + %aimport foo, bar + Import modules 'foo', 'bar' and mark them to be autoreloaded for %autoreload 1 + + %aimport -foo + Mark module 'foo' to not be autoreloaded for %autoreload 1 + """ + modname = parameter_s + if not modname: + to_reload = sorted(self._reloader.modules.keys()) + to_skip = sorted(self._reloader.skip_modules.keys()) + if stream is None: + stream = sys.stdout + if self._reloader.check_all: + stream.write("Modules to reload:\nall-except-skipped\n") + else: + stream.write("Modules to reload:\n%s\n" % ' '.join(to_reload)) + stream.write("\nModules to skip:\n%s\n" % ' '.join(to_skip)) + elif modname.startswith('-'): + modname = modname[1:] + self._reloader.mark_module_skipped(modname) + else: + for _module in ([_.strip() for _ in modname.split(',')]): + top_module, top_name = self._reloader.aimport_module(_module) + + # Inject module to user namespace + self.shell.push({top_name: top_module}) + + def pre_run_cell(self): + if self._reloader.enabled: + try: + self._reloader.check() + except: + pass + + def post_execute_hook(self): + """Cache the modification times of any modules imported in this execution + """ + newly_loaded_modules = set(sys.modules) - self.loaded_modules + for modname in newly_loaded_modules: + _, pymtime = self._reloader.filename_and_mtime(sys.modules[modname]) + if pymtime is not None: + self._reloader.modules_mtimes[modname] = pymtime + + self.loaded_modules.update(newly_loaded_modules) + + +def load_ipython_extension(ip): + """Load the extension in IPython.""" + auto_reload = AutoreloadMagics(ip) + ip.register_magics(auto_reload) + ip.events.register('pre_run_cell', auto_reload.pre_run_cell) + ip.events.register('post_execute', auto_reload.post_execute_hook) diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/cythonmagic.py b/.venv/lib/python3.8/site-packages/IPython/extensions/cythonmagic.py new file mode 100644 index 00000000..3c88e7c2 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/extensions/cythonmagic.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +""" +**DEPRECATED** + +The cython magic has been integrated into Cython itself, +which is now released in version 0.21. + +cf github `Cython` organisation, `Cython` repo, under the +file `Cython/Build/IpythonMagic.py` +""" +#----------------------------------------------------------------------------- +# Copyright (C) 2010-2011, IPython Development Team. +#----------------------------------------------------------------------------- + +import warnings + +## still load the magic in IPython 3.x, remove completely in future versions. +def load_ipython_extension(ip): + """Load the extension in IPython.""" + + warnings.warn("""The Cython magic has been moved to the Cython package""") diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/rmagic.py b/.venv/lib/python3.8/site-packages/IPython/extensions/rmagic.py new file mode 100644 index 00000000..ec576397 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/extensions/rmagic.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +#----------------------------------------------------------------------------- +# Copyright (C) 2012 The IPython Development Team +#----------------------------------------------------------------------------- + +import warnings + +def load_ipython_extension(ip): + """Load the extension in IPython.""" + warnings.warn("The rmagic extension in IPython has moved to " + "`rpy2.ipython`, please see `rpy2` documentation.") diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/storemagic.py b/.venv/lib/python3.8/site-packages/IPython/extensions/storemagic.py new file mode 100644 index 00000000..51b79ad3 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/extensions/storemagic.py @@ -0,0 +1,233 @@ +# -*- coding: utf-8 -*- +""" +%store magic for lightweight persistence. + +Stores variables, aliases and macros in IPython's database. + +To automatically restore stored variables at startup, add this to your +:file:`ipython_config.py` file:: + + c.StoreMagics.autorestore = True +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import inspect, os, sys, textwrap + +from IPython.core.error import UsageError +from IPython.core.magic import Magics, magics_class, line_magic +from traitlets import Bool + + +def restore_aliases(ip, alias=None): + staliases = ip.db.get('stored_aliases', {}) + if alias is None: + for k,v in staliases.items(): + #print "restore alias",k,v # dbg + #self.alias_table[k] = v + ip.alias_manager.define_alias(k,v) + else: + ip.alias_manager.define_alias(alias, staliases[alias]) + + +def refresh_variables(ip): + db = ip.db + for key in db.keys('autorestore/*'): + # strip autorestore + justkey = os.path.basename(key) + try: + obj = db[key] + except KeyError: + print("Unable to restore variable '%s', ignoring (use %%store -d to forget!)" % justkey) + print("The error was:", sys.exc_info()[0]) + else: + #print "restored",justkey,"=",obj #dbg + ip.user_ns[justkey] = obj + + +def restore_dhist(ip): + ip.user_ns['_dh'] = ip.db.get('dhist',[]) + + +def restore_data(ip): + refresh_variables(ip) + restore_aliases(ip) + restore_dhist(ip) + + +@magics_class +class StoreMagics(Magics): + """Lightweight persistence for python variables. + + Provides the %store magic.""" + + autorestore = Bool(False, help= + """If True, any %store-d variables will be automatically restored + when IPython starts. + """ + ).tag(config=True) + + def __init__(self, shell): + super(StoreMagics, self).__init__(shell=shell) + self.shell.configurables.append(self) + if self.autorestore: + restore_data(self.shell) + + @line_magic + def store(self, parameter_s=''): + """Lightweight persistence for python variables. + + Example:: + + In [1]: l = ['hello',10,'world'] + In [2]: %store l + In [3]: exit + + (IPython session is closed and started again...) + + ville@badger:~$ ipython + In [1]: l + NameError: name 'l' is not defined + In [2]: %store -r + In [3]: l + Out[3]: ['hello', 10, 'world'] + + Usage: + + * ``%store`` - Show list of all variables and their current + values + * ``%store spam bar`` - Store the *current* value of the variables spam + and bar to disk + * ``%store -d spam`` - Remove the variable and its value from storage + * ``%store -z`` - Remove all variables from storage + * ``%store -r`` - Refresh all variables, aliases and directory history + from store (overwrite current vals) + * ``%store -r spam bar`` - Refresh specified variables and aliases from store + (delete current val) + * ``%store foo >a.txt`` - Store value of foo to new file a.txt + * ``%store foo >>a.txt`` - Append value of foo to file a.txt + + It should be noted that if you change the value of a variable, you + need to %store it again if you want to persist the new value. + + Note also that the variables will need to be pickleable; most basic + python types can be safely %store'd. + + Also aliases can be %store'd across sessions. + To remove an alias from the storage, use the %unalias magic. + """ + + opts,argsl = self.parse_options(parameter_s,'drz',mode='string') + args = argsl.split() + ip = self.shell + db = ip.db + # delete + if 'd' in opts: + try: + todel = args[0] + except IndexError: + raise UsageError('You must provide the variable to forget') + else: + try: + del db['autorestore/' + todel] + except: + raise UsageError("Can't delete variable '%s'" % todel) + # reset + elif 'z' in opts: + for k in db.keys('autorestore/*'): + del db[k] + + elif 'r' in opts: + if args: + for arg in args: + try: + obj = db['autorestore/' + arg] + except KeyError: + try: + restore_aliases(ip, alias=arg) + except KeyError: + print("no stored variable or alias %s" % arg) + else: + ip.user_ns[arg] = obj + else: + restore_data(ip) + + # run without arguments -> list variables & values + elif not args: + vars = db.keys('autorestore/*') + vars.sort() + if vars: + size = max(map(len, vars)) + else: + size = 0 + + print('Stored variables and their in-db values:') + fmt = '%-'+str(size)+'s -> %s' + get = db.get + for var in vars: + justkey = os.path.basename(var) + # print 30 first characters from every var + print(fmt % (justkey, repr(get(var, ''))[:50])) + + # default action - store the variable + else: + # %store foo >file.txt or >>file.txt + if len(args) > 1 and args[1].startswith('>'): + fnam = os.path.expanduser(args[1].lstrip('>').lstrip()) + if args[1].startswith('>>'): + fil = open(fnam, 'a') + else: + fil = open(fnam, 'w') + with fil: + obj = ip.ev(args[0]) + print("Writing '%s' (%s) to file '%s'." % (args[0], + obj.__class__.__name__, fnam)) + + if not isinstance (obj, str): + from pprint import pprint + pprint(obj, fil) + else: + fil.write(obj) + if not obj.endswith('\n'): + fil.write('\n') + + return + + # %store foo + for arg in args: + try: + obj = ip.user_ns[arg] + except KeyError: + # it might be an alias + name = arg + try: + cmd = ip.alias_manager.retrieve_alias(name) + except ValueError: + raise UsageError("Unknown variable '%s'" % name) + + staliases = db.get('stored_aliases',{}) + staliases[name] = cmd + db['stored_aliases'] = staliases + print("Alias stored: %s (%s)" % (name, cmd)) + return + + else: + modname = getattr(inspect.getmodule(obj), '__name__', '') + if modname == '__main__': + print(textwrap.dedent("""\ + Warning:%s is %s + Proper storage of interactively declared classes (or instances + of those classes) is not possible! Only instances + of classes in real modules on file system can be %%store'd. + """ % (arg, obj) )) + return + #pickled = pickle.dumps(obj) + db[ 'autorestore/' + arg ] = obj + print("Stored '%s' (%s)" % (arg, obj.__class__.__name__)) + + +def load_ipython_extension(ip): + """Load the extension in IPython.""" + ip.register_magics(StoreMagics) + diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/sympyprinting.py b/.venv/lib/python3.8/site-packages/IPython/extensions/sympyprinting.py new file mode 100644 index 00000000..e6a83cd3 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/extensions/sympyprinting.py @@ -0,0 +1,32 @@ +""" +**DEPRECATED** + +A print function that pretty prints sympy Basic objects. + +:moduleauthor: Brian Granger + +Usage +===== + +Once the extension is loaded, Sympy Basic objects are automatically +pretty-printed. + +As of SymPy 0.7.2, maintenance of this extension has moved to SymPy under +sympy.interactive.ipythonprinting, any modifications to account for changes to +SymPy should be submitted to SymPy rather than changed here. This module is +maintained here for backwards compatibility with old SymPy versions. + +""" +#----------------------------------------------------------------------------- +# Copyright (C) 2008 The IPython Development Team +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import warnings + +def load_ipython_extension(ip): + warnings.warn("The sympyprinting extension has moved to `sympy`, " + "use `from sympy import init_printing; init_printing()`") diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__init__.py b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..dd5a56bb Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/test_autoreload.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/test_autoreload.cpython-38.pyc new file mode 100644 index 00000000..56bfd341 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/test_autoreload.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/test_storemagic.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/test_storemagic.cpython-38.pyc new file mode 100644 index 00000000..5a744ade Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/__pycache__/test_storemagic.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/tests/test_autoreload.py b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/test_autoreload.py new file mode 100644 index 00000000..e81bf221 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/test_autoreload.py @@ -0,0 +1,447 @@ +"""Tests for autoreload extension. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012 IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os +import sys +import tempfile +import textwrap +import shutil +import random +import time +from io import StringIO + +import nose.tools as nt +import IPython.testing.tools as tt + +from unittest import TestCase + +from IPython.extensions.autoreload import AutoreloadMagics +from IPython.core.events import EventManager, pre_run_cell + +#----------------------------------------------------------------------------- +# Test fixture +#----------------------------------------------------------------------------- + +noop = lambda *a, **kw: None + +class FakeShell: + + def __init__(self): + self.ns = {} + self.user_ns = self.ns + self.user_ns_hidden = {} + self.events = EventManager(self, {'pre_run_cell', pre_run_cell}) + self.auto_magics = AutoreloadMagics(shell=self) + self.events.register('pre_run_cell', self.auto_magics.pre_run_cell) + + register_magics = set_hook = noop + + def run_code(self, code): + self.events.trigger('pre_run_cell') + exec(code, self.user_ns) + self.auto_magics.post_execute_hook() + + def push(self, items): + self.ns.update(items) + + def magic_autoreload(self, parameter): + self.auto_magics.autoreload(parameter) + + def magic_aimport(self, parameter, stream=None): + self.auto_magics.aimport(parameter, stream=stream) + self.auto_magics.post_execute_hook() + + +class Fixture(TestCase): + """Fixture for creating test module files""" + + test_dir = None + old_sys_path = None + filename_chars = "abcdefghijklmopqrstuvwxyz0123456789" + + def setUp(self): + self.test_dir = tempfile.mkdtemp() + self.old_sys_path = list(sys.path) + sys.path.insert(0, self.test_dir) + self.shell = FakeShell() + + def tearDown(self): + shutil.rmtree(self.test_dir) + sys.path = self.old_sys_path + + self.test_dir = None + self.old_sys_path = None + self.shell = None + + def get_module(self): + module_name = "tmpmod_" + "".join(random.sample(self.filename_chars,20)) + if module_name in sys.modules: + del sys.modules[module_name] + file_name = os.path.join(self.test_dir, module_name + ".py") + return module_name, file_name + + def write_file(self, filename, content): + """ + Write a file, and force a timestamp difference of at least one second + + Notes + ----- + Python's .pyc files record the timestamp of their compilation + with a time resolution of one second. + + Therefore, we need to force a timestamp difference between .py + and .pyc, without having the .py file be timestamped in the + future, and without changing the timestamp of the .pyc file + (because that is stored in the file). The only reliable way + to achieve this seems to be to sleep. + """ + content = textwrap.dedent(content) + # Sleep one second + eps + time.sleep(1.05) + + # Write + with open(filename, 'w') as f: + f.write(content) + + def new_module(self, code): + code = textwrap.dedent(code) + mod_name, mod_fn = self.get_module() + with open(mod_fn, 'w') as f: + f.write(code) + return mod_name, mod_fn + +#----------------------------------------------------------------------------- +# Test automatic reloading +#----------------------------------------------------------------------------- + +def pickle_get_current_class(obj): + """ + Original issue comes from pickle; hence the name. + """ + name = obj.__class__.__name__ + module_name = getattr(obj, "__module__", None) + obj2 = sys.modules[module_name] + for subpath in name.split("."): + obj2 = getattr(obj2, subpath) + return obj2 + +class TestAutoreload(Fixture): + + def test_reload_enums(self): + mod_name, mod_fn = self.new_module(textwrap.dedent(""" + from enum import Enum + class MyEnum(Enum): + A = 'A' + B = 'B' + """)) + self.shell.magic_autoreload("2") + self.shell.magic_aimport(mod_name) + self.write_file(mod_fn, textwrap.dedent(""" + from enum import Enum + class MyEnum(Enum): + A = 'A' + B = 'B' + C = 'C' + """)) + with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'): + self.shell.run_code("pass") # trigger another reload + + def test_reload_class_type(self): + self.shell.magic_autoreload("2") + mod_name, mod_fn = self.new_module( + """ + class Test(): + def meth(self): + return "old" + """ + ) + assert "test" not in self.shell.ns + assert "result" not in self.shell.ns + + self.shell.run_code("from %s import Test" % mod_name) + self.shell.run_code("test = Test()") + + self.write_file( + mod_fn, + """ + class Test(): + def meth(self): + return "new" + """, + ) + + test_object = self.shell.ns["test"] + + # important to trigger autoreload logic ! + self.shell.run_code("pass") + + test_class = pickle_get_current_class(test_object) + assert isinstance(test_object, test_class) + + # extra check. + self.shell.run_code("import pickle") + self.shell.run_code("p = pickle.dumps(test)") + + def test_reload_class_attributes(self): + self.shell.magic_autoreload("2") + mod_name, mod_fn = self.new_module(textwrap.dedent(""" + class MyClass: + + def __init__(self, a=10): + self.a = a + self.b = 22 + # self.toto = 33 + + def square(self): + print('compute square') + return self.a*self.a + """ + ) + ) + self.shell.run_code("from %s import MyClass" % mod_name) + self.shell.run_code("first = MyClass(5)") + self.shell.run_code("first.square()") + with nt.assert_raises(AttributeError): + self.shell.run_code("first.cube()") + with nt.assert_raises(AttributeError): + self.shell.run_code("first.power(5)") + self.shell.run_code("first.b") + with nt.assert_raises(AttributeError): + self.shell.run_code("first.toto") + + # remove square, add power + + self.write_file( + mod_fn, + textwrap.dedent( + """ + class MyClass: + + def __init__(self, a=10): + self.a = a + self.b = 11 + + def power(self, p): + print('compute power '+str(p)) + return self.a**p + """ + ), + ) + + self.shell.run_code("second = MyClass(5)") + + for object_name in {'first', 'second'}: + self.shell.run_code("{object_name}.power(5)".format(object_name=object_name)) + with nt.assert_raises(AttributeError): + self.shell.run_code("{object_name}.cube()".format(object_name=object_name)) + with nt.assert_raises(AttributeError): + self.shell.run_code("{object_name}.square()".format(object_name=object_name)) + self.shell.run_code("{object_name}.b".format(object_name=object_name)) + self.shell.run_code("{object_name}.a".format(object_name=object_name)) + with nt.assert_raises(AttributeError): + self.shell.run_code("{object_name}.toto".format(object_name=object_name)) + + def _check_smoketest(self, use_aimport=True): + """ + Functional test for the automatic reloader using either + '%autoreload 1' or '%autoreload 2' + """ + + mod_name, mod_fn = self.new_module(""" +x = 9 + +z = 123 # this item will be deleted + +def foo(y): + return y + 3 + +class Baz(object): + def __init__(self, x): + self.x = x + def bar(self, y): + return self.x + y + @property + def quux(self): + return 42 + def zzz(self): + '''This method will be deleted below''' + return 99 + +class Bar: # old-style class: weakref doesn't work for it on Python < 2.7 + def foo(self): + return 1 +""") + + # + # Import module, and mark for reloading + # + if use_aimport: + self.shell.magic_autoreload("1") + self.shell.magic_aimport(mod_name) + stream = StringIO() + self.shell.magic_aimport("", stream=stream) + nt.assert_in(("Modules to reload:\n%s" % mod_name), stream.getvalue()) + + with nt.assert_raises(ImportError): + self.shell.magic_aimport("tmpmod_as318989e89ds") + else: + self.shell.magic_autoreload("2") + self.shell.run_code("import %s" % mod_name) + stream = StringIO() + self.shell.magic_aimport("", stream=stream) + nt.assert_true("Modules to reload:\nall-except-skipped" in + stream.getvalue()) + nt.assert_in(mod_name, self.shell.ns) + + mod = sys.modules[mod_name] + + # + # Test module contents + # + old_foo = mod.foo + old_obj = mod.Baz(9) + old_obj2 = mod.Bar() + + def check_module_contents(): + nt.assert_equal(mod.x, 9) + nt.assert_equal(mod.z, 123) + + nt.assert_equal(old_foo(0), 3) + nt.assert_equal(mod.foo(0), 3) + + obj = mod.Baz(9) + nt.assert_equal(old_obj.bar(1), 10) + nt.assert_equal(obj.bar(1), 10) + nt.assert_equal(obj.quux, 42) + nt.assert_equal(obj.zzz(), 99) + + obj2 = mod.Bar() + nt.assert_equal(old_obj2.foo(), 1) + nt.assert_equal(obj2.foo(), 1) + + check_module_contents() + + # + # Simulate a failed reload: no reload should occur and exactly + # one error message should be printed + # + self.write_file(mod_fn, """ +a syntax error +""") + + with tt.AssertPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'): + self.shell.run_code("pass") # trigger reload + with tt.AssertNotPrints(('[autoreload of %s failed:' % mod_name), channel='stderr'): + self.shell.run_code("pass") # trigger another reload + check_module_contents() + + # + # Rewrite module (this time reload should succeed) + # + self.write_file(mod_fn, """ +x = 10 + +def foo(y): + return y + 4 + +class Baz(object): + def __init__(self, x): + self.x = x + def bar(self, y): + return self.x + y + 1 + @property + def quux(self): + return 43 + +class Bar: # old-style class + def foo(self): + return 2 +""") + + def check_module_contents(): + nt.assert_equal(mod.x, 10) + nt.assert_false(hasattr(mod, 'z')) + + nt.assert_equal(old_foo(0), 4) # superreload magic! + nt.assert_equal(mod.foo(0), 4) + + obj = mod.Baz(9) + nt.assert_equal(old_obj.bar(1), 11) # superreload magic! + nt.assert_equal(obj.bar(1), 11) + + nt.assert_equal(old_obj.quux, 43) + nt.assert_equal(obj.quux, 43) + + nt.assert_false(hasattr(old_obj, 'zzz')) + nt.assert_false(hasattr(obj, 'zzz')) + + obj2 = mod.Bar() + nt.assert_equal(old_obj2.foo(), 2) + nt.assert_equal(obj2.foo(), 2) + + self.shell.run_code("pass") # trigger reload + check_module_contents() + + # + # Another failure case: deleted file (shouldn't reload) + # + os.unlink(mod_fn) + + self.shell.run_code("pass") # trigger reload + check_module_contents() + + # + # Disable autoreload and rewrite module: no reload should occur + # + if use_aimport: + self.shell.magic_aimport("-" + mod_name) + stream = StringIO() + self.shell.magic_aimport("", stream=stream) + nt.assert_true(("Modules to skip:\n%s" % mod_name) in + stream.getvalue()) + + # This should succeed, although no such module exists + self.shell.magic_aimport("-tmpmod_as318989e89ds") + else: + self.shell.magic_autoreload("0") + + self.write_file(mod_fn, """ +x = -99 +""") + + self.shell.run_code("pass") # trigger reload + self.shell.run_code("pass") + check_module_contents() + + # + # Re-enable autoreload: reload should now occur + # + if use_aimport: + self.shell.magic_aimport(mod_name) + else: + self.shell.magic_autoreload("") + + self.shell.run_code("pass") # trigger reload + nt.assert_equal(mod.x, -99) + + def test_smoketest_aimport(self): + self._check_smoketest(use_aimport=True) + + def test_smoketest_autoreload(self): + self._check_smoketest(use_aimport=False) + + + + + diff --git a/.venv/lib/python3.8/site-packages/IPython/extensions/tests/test_storemagic.py b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/test_storemagic.py new file mode 100644 index 00000000..6f8371d3 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/extensions/tests/test_storemagic.py @@ -0,0 +1,66 @@ +import tempfile, os + +from traitlets.config.loader import Config +import nose.tools as nt + + +def setup_module(): + ip.magic('load_ext storemagic') + +def test_store_restore(): + assert 'bar' not in ip.user_ns, "Error: some other test leaked `bar` in user_ns" + assert 'foo' not in ip.user_ns, "Error: some other test leaked `foo` in user_ns" + assert 'foobar' not in ip.user_ns, "Error: some other test leaked `foobar` in user_ns" + assert 'foobaz' not in ip.user_ns, "Error: some other test leaked `foobaz` in user_ns" + ip.user_ns['foo'] = 78 + ip.magic('alias bar echo "hello"') + ip.user_ns['foobar'] = 79 + ip.user_ns['foobaz'] = '80' + tmpd = tempfile.mkdtemp() + ip.magic('cd ' + tmpd) + ip.magic('store foo') + ip.magic('store bar') + ip.magic('store foobar foobaz') + + # Check storing + nt.assert_equal(ip.db['autorestore/foo'], 78) + nt.assert_in('bar', ip.db['stored_aliases']) + nt.assert_equal(ip.db['autorestore/foobar'], 79) + nt.assert_equal(ip.db['autorestore/foobaz'], '80') + + # Remove those items + ip.user_ns.pop('foo', None) + ip.user_ns.pop('foobar', None) + ip.user_ns.pop('foobaz', None) + ip.alias_manager.undefine_alias('bar') + ip.magic('cd -') + ip.user_ns['_dh'][:] = [] + + # Check restoring + ip.magic('store -r foo bar foobar foobaz') + nt.assert_equal(ip.user_ns['foo'], 78) + assert ip.alias_manager.is_alias('bar') + nt.assert_equal(ip.user_ns['foobar'], 79) + nt.assert_equal(ip.user_ns['foobaz'], '80') + + ip.magic('store -r') # restores _dh too + nt.assert_in(os.path.realpath(tmpd), ip.user_ns['_dh']) + + os.rmdir(tmpd) + +def test_autorestore(): + ip.user_ns['foo'] = 95 + ip.magic('store foo') + del ip.user_ns['foo'] + c = Config() + c.StoreMagics.autorestore = False + orig_config = ip.config + try: + ip.config = c + ip.extension_manager.reload_extension('storemagic') + nt.assert_not_in('foo', ip.user_ns) + c.StoreMagics.autorestore = True + ip.extension_manager.reload_extension('storemagic') + nt.assert_equal(ip.user_ns['foo'], 95) + finally: + ip.config = orig_config diff --git a/.venv/lib/python3.8/site-packages/IPython/external/__init__.py b/.venv/lib/python3.8/site-packages/IPython/external/__init__.py new file mode 100644 index 00000000..1c8c546f --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/external/__init__.py @@ -0,0 +1,5 @@ +""" +This package contains all third-party modules bundled with IPython. +""" + +__all__ = [] diff --git a/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..885fa65b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/mathjax.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/mathjax.cpython-38.pyc new file mode 100644 index 00000000..78c846e2 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/mathjax.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/qt_for_kernel.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/qt_for_kernel.cpython-38.pyc new file mode 100644 index 00000000..f482b7af Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/qt_for_kernel.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/qt_loaders.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/qt_loaders.cpython-38.pyc new file mode 100644 index 00000000..2f0ea435 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/external/__pycache__/qt_loaders.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/external/decorators/__init__.py b/.venv/lib/python3.8/site-packages/IPython/external/decorators/__init__.py new file mode 100644 index 00000000..1db80edd --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/external/decorators/__init__.py @@ -0,0 +1,8 @@ +try: + from numpy.testing import KnownFailure, knownfailureif +except ImportError: + from ._decorators import knownfailureif + try: + from ._numpy_testing_noseclasses import KnownFailure + except ImportError: + pass diff --git a/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..6abf3148 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/_decorators.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/_decorators.cpython-38.pyc new file mode 100644 index 00000000..467cb60c Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/_decorators.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/_numpy_testing_noseclasses.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/_numpy_testing_noseclasses.cpython-38.pyc new file mode 100644 index 00000000..9c15ae4c Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/external/decorators/__pycache__/_numpy_testing_noseclasses.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/external/decorators/_decorators.py b/.venv/lib/python3.8/site-packages/IPython/external/decorators/_decorators.py new file mode 100644 index 00000000..18f847ad --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/external/decorators/_decorators.py @@ -0,0 +1,143 @@ +""" +Decorators for labeling and modifying behavior of test objects. + +Decorators that merely return a modified version of the original +function object are straightforward. Decorators that return a new +function object need to use +:: + + nose.tools.make_decorator(original_function)(decorator) + +in returning the decorator, in order to preserve meta-data such as +function name, setup and teardown functions and so on - see +``nose.tools`` for more information. + +""" + +# IPython changes: make this work if numpy not available +# Original code: +try: + from ._numpy_testing_noseclasses import KnownFailureTest +except: + pass + +# End IPython changes + + +def skipif(skip_condition, msg=None): + """ + Make function raise SkipTest exception if a given condition is true. + + If the condition is a callable, it is used at runtime to dynamically + make the decision. This is useful for tests that may require costly + imports, to delay the cost until the test suite is actually executed. + + Parameters + ---------- + skip_condition : bool or callable + Flag to determine whether to skip the decorated test. + msg : str, optional + Message to give on raising a SkipTest exception. Default is None. + + Returns + ------- + decorator : function + Decorator which, when applied to a function, causes SkipTest + to be raised when `skip_condition` is True, and the function + to be called normally otherwise. + + Notes + ----- + The decorator itself is decorated with the ``nose.tools.make_decorator`` + function in order to transmit function name, and various other metadata. + + """ + + def skip_decorator(f): + # Local import to avoid a hard nose dependency and only incur the + # import time overhead at actual test-time. + import nose + + # Allow for both boolean or callable skip conditions. + if callable(skip_condition): + skip_val = lambda : skip_condition() + else: + skip_val = lambda : skip_condition + + def get_msg(func,msg=None): + """Skip message with information about function being skipped.""" + if msg is None: + out = 'Test skipped due to test condition' + else: + out = '\n'+msg + + return "Skipping test: %s%s" % (func.__name__,out) + + # We need to define *two* skippers because Python doesn't allow both + # return with value and yield inside the same function. + def skipper_func(*args, **kwargs): + """Skipper for normal test functions.""" + if skip_val(): + raise nose.SkipTest(get_msg(f,msg)) + else: + return f(*args, **kwargs) + + def skipper_gen(*args, **kwargs): + """Skipper for test generators.""" + if skip_val(): + raise nose.SkipTest(get_msg(f,msg)) + else: + for x in f(*args, **kwargs): + yield x + + # Choose the right skipper to use when building the actual decorator. + if nose.util.isgenerator(f): + skipper = skipper_gen + else: + skipper = skipper_func + + return nose.tools.make_decorator(f)(skipper) + + return skip_decorator + +def knownfailureif(fail_condition, msg=None): + """ + Make function raise KnownFailureTest exception if given condition is true. + + Parameters + ---------- + fail_condition : bool + Flag to determine whether to mark the decorated test as a known + failure (if True) or not (if False). + msg : str, optional + Message to give on raising a KnownFailureTest exception. + Default is None. + + Returns + ------- + decorator : function + Decorator, which, when applied to a function, causes KnownFailureTest to + be raised when `fail_condition` is True and the test fails. + + Notes + ----- + The decorator itself is decorated with the ``nose.tools.make_decorator`` + function in order to transmit function name, and various other metadata. + + """ + if msg is None: + msg = 'Test skipped due to known failure' + + def knownfail_decorator(f): + # Local import to avoid a hard nose dependency and only incur the + # import time overhead at actual test-time. + import nose + + def knownfailer(*args, **kwargs): + if fail_condition: + raise KnownFailureTest(msg) + else: + return f(*args, **kwargs) + return nose.tools.make_decorator(f)(knownfailer) + + return knownfail_decorator diff --git a/.venv/lib/python3.8/site-packages/IPython/external/decorators/_numpy_testing_noseclasses.py b/.venv/lib/python3.8/site-packages/IPython/external/decorators/_numpy_testing_noseclasses.py new file mode 100644 index 00000000..ca6ccd87 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/external/decorators/_numpy_testing_noseclasses.py @@ -0,0 +1,41 @@ +# IPython: modified copy of numpy.testing.noseclasses, so +# IPython.external._decorators works without numpy being installed. + +# These classes implement a "known failure" error class. + +import os + +from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin + +class KnownFailureTest(Exception): + '''Raise this exception to mark a test as a known failing test.''' + pass + + +class KnownFailure(ErrorClassPlugin): + '''Plugin that installs a KNOWNFAIL error class for the + KnownFailureClass exception. When KnownFailureTest is raised, + the exception will be logged in the knownfail attribute of the + result, 'K' or 'KNOWNFAIL' (verbose) will be output, and the + exception will not be counted as an error or failure.''' + enabled = True + knownfail = ErrorClass(KnownFailureTest, + label='KNOWNFAIL', + isfailure=False) + + def options(self, parser, env=os.environ): + env_opt = 'NOSE_WITHOUT_KNOWNFAIL' + parser.add_option('--no-knownfail', action='store_true', + dest='noKnownFail', default=env.get(env_opt, False), + help='Disable special handling of KnownFailureTest ' + 'exceptions') + + def configure(self, options, conf): + if not self.can_configure: + return + self.conf = conf + disable = getattr(options, 'noKnownFail', False) + if disable: + self.enabled = False + + diff --git a/.venv/lib/python3.8/site-packages/IPython/external/mathjax.py b/.venv/lib/python3.8/site-packages/IPython/external/mathjax.py new file mode 100644 index 00000000..1b9b8090 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/external/mathjax.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +""" +`IPython.external.mathjax` is deprecated with IPython 4.0+ + +mathjax is now install by default with the notebook package + +""" + +import sys + +if __name__ == '__main__' : + sys.exit("IPython.external.mathjax is deprecated, Mathjax is now installed by default with the notebook package") + diff --git a/.venv/lib/python3.8/site-packages/IPython/external/qt_for_kernel.py b/.venv/lib/python3.8/site-packages/IPython/external/qt_for_kernel.py new file mode 100644 index 00000000..1a94e7e0 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/external/qt_for_kernel.py @@ -0,0 +1,95 @@ +""" Import Qt in a manner suitable for an IPython kernel. + +This is the import used for the `gui=qt` or `matplotlib=qt` initialization. + +Import Priority: + +if Qt has been imported anywhere else: + use that + +if matplotlib has been imported and doesn't support v2 (<= 1.0.1): + use PyQt4 @v1 + +Next, ask QT_API env variable + +if QT_API not set: + ask matplotlib what it's using. If Qt4Agg or Qt5Agg, then use the + version matplotlib is configured with + + else: (matplotlib said nothing) + # this is the default path - nobody told us anything + try in this order: + PyQt default version, PySide, PyQt5 +else: + use what QT_API says + +""" +# NOTE: This is no longer an external, third-party module, and should be +# considered part of IPython. For compatibility however, it is being kept in +# IPython/external. + +import os +import sys + +from IPython.utils.version import check_version +from IPython.external.qt_loaders import (load_qt, loaded_api, QT_API_PYSIDE, + QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, + QT_API_PYQTv1, QT_API_PYQT_DEFAULT) + +_qt_apis = (QT_API_PYSIDE, QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, QT_API_PYQTv1, + QT_API_PYQT_DEFAULT) + +#Constraints placed on an imported matplotlib +def matplotlib_options(mpl): + if mpl is None: + return + backend = mpl.rcParams.get('backend', None) + if backend == 'Qt4Agg': + mpqt = mpl.rcParams.get('backend.qt4', None) + if mpqt is None: + return None + if mpqt.lower() == 'pyside': + return [QT_API_PYSIDE] + elif mpqt.lower() == 'pyqt4': + return [QT_API_PYQT_DEFAULT] + elif mpqt.lower() == 'pyqt4v2': + return [QT_API_PYQT] + raise ImportError("unhandled value for backend.qt4 from matplotlib: %r" % + mpqt) + elif backend == 'Qt5Agg': + mpqt = mpl.rcParams.get('backend.qt5', None) + if mpqt is None: + return None + if mpqt.lower() == 'pyqt5': + return [QT_API_PYQT5] + raise ImportError("unhandled value for backend.qt5 from matplotlib: %r" % + mpqt) + +def get_options(): + """Return a list of acceptable QT APIs, in decreasing order of + preference + """ + #already imported Qt somewhere. Use that + loaded = loaded_api() + if loaded is not None: + return [loaded] + + mpl = sys.modules.get('matplotlib', None) + + if mpl is not None and not check_version(mpl.__version__, '1.0.2'): + #1.0.1 only supports PyQt4 v1 + return [QT_API_PYQT_DEFAULT] + + qt_api = os.environ.get('QT_API', None) + if qt_api is None: + #no ETS variable. Ask mpl, then use default fallback path + return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE, + QT_API_PYQT5, QT_API_PYSIDE2] + elif qt_api not in _qt_apis: + raise RuntimeError("Invalid Qt API %r, valid values are: %r" % + (qt_api, ', '.join(_qt_apis))) + else: + return [qt_api] + +api_opts = get_options() +QtCore, QtGui, QtSvg, QT_API = load_qt(api_opts) diff --git a/.venv/lib/python3.8/site-packages/IPython/external/qt_loaders.py b/.venv/lib/python3.8/site-packages/IPython/external/qt_loaders.py new file mode 100644 index 00000000..46cd9c35 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/external/qt_loaders.py @@ -0,0 +1,329 @@ +""" +This module contains factory functions that attempt +to return Qt submodules from the various python Qt bindings. + +It also protects against double-importing Qt with different +bindings, which is unstable and likely to crash + +This is used primarily by qt and qt_for_kernel, and shouldn't +be accessed directly from the outside +""" +import sys +import types +from functools import partial +from importlib import import_module + +from IPython.utils.version import check_version + +# Available APIs. +QT_API_PYQT = 'pyqt' # Force version 2 +QT_API_PYQT5 = 'pyqt5' +QT_API_PYQTv1 = 'pyqtv1' # Force version 2 +QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2 +QT_API_PYSIDE = 'pyside' +QT_API_PYSIDE2 = 'pyside2' + +api_to_module = {QT_API_PYSIDE2: 'PySide2', + QT_API_PYSIDE: 'PySide', + QT_API_PYQT: 'PyQt4', + QT_API_PYQTv1: 'PyQt4', + QT_API_PYQT5: 'PyQt5', + QT_API_PYQT_DEFAULT: 'PyQt4', + } + + +class ImportDenier(object): + """Import Hook that will guard against bad Qt imports + once IPython commits to a specific binding + """ + + def __init__(self): + self.__forbidden = set() + + def forbid(self, module_name): + sys.modules.pop(module_name, None) + self.__forbidden.add(module_name) + + def find_module(self, fullname, path=None): + if path: + return + if fullname in self.__forbidden: + return self + + def load_module(self, fullname): + raise ImportError(""" + Importing %s disabled by IPython, which has + already imported an Incompatible QT Binding: %s + """ % (fullname, loaded_api())) + +ID = ImportDenier() +sys.meta_path.insert(0, ID) + + +def commit_api(api): + """Commit to a particular API, and trigger ImportErrors on subsequent + dangerous imports""" + + if api == QT_API_PYSIDE2: + ID.forbid('PySide') + ID.forbid('PyQt4') + ID.forbid('PyQt5') + elif api == QT_API_PYSIDE: + ID.forbid('PySide2') + ID.forbid('PyQt4') + ID.forbid('PyQt5') + elif api == QT_API_PYQT5: + ID.forbid('PySide2') + ID.forbid('PySide') + ID.forbid('PyQt4') + else: # There are three other possibilities, all representing PyQt4 + ID.forbid('PyQt5') + ID.forbid('PySide2') + ID.forbid('PySide') + + +def loaded_api(): + """Return which API is loaded, if any + + If this returns anything besides None, + importing any other Qt binding is unsafe. + + Returns + ------- + None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1' + """ + if 'PyQt4.QtCore' in sys.modules: + if qtapi_version() == 2: + return QT_API_PYQT + else: + return QT_API_PYQTv1 + elif 'PySide.QtCore' in sys.modules: + return QT_API_PYSIDE + elif 'PySide2.QtCore' in sys.modules: + return QT_API_PYSIDE2 + elif 'PyQt5.QtCore' in sys.modules: + return QT_API_PYQT5 + return None + + +def has_binding(api): + """Safely check for PyQt4/5, PySide or PySide2, without importing submodules + + Parameters + ---------- + api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault'] + Which module to check for + + Returns + ------- + True if the relevant module appears to be importable + """ + module_name = api_to_module[api] + from importlib.util import find_spec + + required = ['QtCore', 'QtGui', 'QtSvg'] + if api in (QT_API_PYQT5, QT_API_PYSIDE2): + # QT5 requires QtWidgets too + required.append('QtWidgets') + + for submod in required: + try: + spec = find_spec('%s.%s' % (module_name, submod)) + except ImportError: + # Package (e.g. PyQt5) not found + return False + else: + if spec is None: + # Submodule (e.g. PyQt5.QtCore) not found + return False + + if api == QT_API_PYSIDE: + # We can also safely check PySide version + import PySide + return check_version(PySide.__version__, '1.0.3') + + return True + + +def qtapi_version(): + """Return which QString API has been set, if any + + Returns + ------- + The QString API version (1 or 2), or None if not set + """ + try: + import sip + except ImportError: + return + try: + return sip.getapi('QString') + except ValueError: + return + + +def can_import(api): + """Safely query whether an API is importable, without importing it""" + if not has_binding(api): + return False + + current = loaded_api() + if api == QT_API_PYQT_DEFAULT: + return current in [QT_API_PYQT, QT_API_PYQTv1, None] + else: + return current in [api, None] + + +def import_pyqt4(version=2): + """ + Import PyQt4 + + Parameters + ---------- + version : 1, 2, or None + Which QString/QVariant API to use. Set to None to use the system + default + + ImportErrors rasied within this function are non-recoverable + """ + # The new-style string API (version=2) automatically + # converts QStrings to Unicode Python strings. Also, automatically unpacks + # QVariants to their underlying objects. + import sip + + if version is not None: + sip.setapi('QString', version) + sip.setapi('QVariant', version) + + from PyQt4 import QtGui, QtCore, QtSvg + + if not check_version(QtCore.PYQT_VERSION_STR, '4.7'): + raise ImportError("IPython requires PyQt4 >= 4.7, found %s" % + QtCore.PYQT_VERSION_STR) + + # Alias PyQt-specific functions for PySide compatibility. + QtCore.Signal = QtCore.pyqtSignal + QtCore.Slot = QtCore.pyqtSlot + + # query for the API version (in case version == None) + version = sip.getapi('QString') + api = QT_API_PYQTv1 if version == 1 else QT_API_PYQT + return QtCore, QtGui, QtSvg, api + + +def import_pyqt5(): + """ + Import PyQt5 + + ImportErrors rasied within this function are non-recoverable + """ + + from PyQt5 import QtCore, QtSvg, QtWidgets, QtGui + + # Alias PyQt-specific functions for PySide compatibility. + QtCore.Signal = QtCore.pyqtSignal + QtCore.Slot = QtCore.pyqtSlot + + # Join QtGui and QtWidgets for Qt4 compatibility. + QtGuiCompat = types.ModuleType('QtGuiCompat') + QtGuiCompat.__dict__.update(QtGui.__dict__) + QtGuiCompat.__dict__.update(QtWidgets.__dict__) + + api = QT_API_PYQT5 + return QtCore, QtGuiCompat, QtSvg, api + + +def import_pyside(): + """ + Import PySide + + ImportErrors raised within this function are non-recoverable + """ + from PySide import QtGui, QtCore, QtSvg + return QtCore, QtGui, QtSvg, QT_API_PYSIDE + +def import_pyside2(): + """ + Import PySide2 + + ImportErrors raised within this function are non-recoverable + """ + from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport + + # Join QtGui and QtWidgets for Qt4 compatibility. + QtGuiCompat = types.ModuleType('QtGuiCompat') + QtGuiCompat.__dict__.update(QtGui.__dict__) + QtGuiCompat.__dict__.update(QtWidgets.__dict__) + QtGuiCompat.__dict__.update(QtPrintSupport.__dict__) + + return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2 + + +def load_qt(api_options): + """ + Attempt to import Qt, given a preference list + of permissible bindings + + It is safe to call this function multiple times. + + Parameters + ---------- + api_options: List of strings + The order of APIs to try. Valid items are 'pyside', 'pyside2', + 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault' + + Returns + ------- + + A tuple of QtCore, QtGui, QtSvg, QT_API + The first three are the Qt modules. The last is the + string indicating which module was loaded. + + Raises + ------ + ImportError, if it isn't possible to import any requested + bindings (either because they aren't installed, or because + an incompatible library has already been installed) + """ + loaders = { + QT_API_PYSIDE2: import_pyside2, + QT_API_PYSIDE: import_pyside, + QT_API_PYQT: import_pyqt4, + QT_API_PYQT5: import_pyqt5, + QT_API_PYQTv1: partial(import_pyqt4, version=1), + QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None) + } + + for api in api_options: + + if api not in loaders: + raise RuntimeError( + "Invalid Qt API %r, valid values are: %s" % + (api, ", ".join(["%r" % k for k in loaders.keys()]))) + + if not can_import(api): + continue + + #cannot safely recover from an ImportError during this + result = loaders[api]() + api = result[-1] # changed if api = QT_API_PYQT_DEFAULT + commit_api(api) + return result + else: + raise ImportError(""" + Could not load requested Qt binding. Please ensure that + PyQt4 >= 4.7, PyQt5, PySide >= 1.0.3 or PySide2 is available, + and only one is imported per session. + + Currently-imported Qt library: %r + PyQt4 available (requires QtCore, QtGui, QtSvg): %s + PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s + PySide >= 1.0.3 installed: %s + PySide2 installed: %s + Tried to load: %r + """ % (loaded_api(), + has_binding(QT_API_PYQT), + has_binding(QT_API_PYQT5), + has_binding(QT_API_PYSIDE), + has_binding(QT_API_PYSIDE2), + api_options)) diff --git a/.venv/lib/python3.8/site-packages/IPython/frontend.py b/.venv/lib/python3.8/site-packages/IPython/frontend.py new file mode 100644 index 00000000..9cc3eaff --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/frontend.py @@ -0,0 +1,29 @@ +""" +Shim to maintain backwards compatibility with old frontend imports. + +We have moved all contents of the old `frontend` subpackage into top-level +subpackages (`html`, `qt` and `terminal`), and flattened the notebook into +just `IPython.html`, formerly `IPython.frontend.html.notebook`. + +This will let code that was making `from IPython.frontend...` calls continue +working, though a warning will be printed. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import sys +from warnings import warn + +from IPython.utils.shimmodule import ShimModule, ShimWarning + +warn("The top-level `frontend` package has been deprecated since IPython 1.0. " + "All its subpackages have been moved to the top `IPython` level.", ShimWarning) + +# Unconditionally insert the shim into sys.modules so that further import calls +# trigger the custom attribute access above + +sys.modules['IPython.frontend.html.notebook'] = ShimModule( + src='IPython.frontend.html.notebook', mirror='IPython.html') +sys.modules['IPython.frontend'] = ShimModule( + src='IPython.frontend', mirror='IPython') diff --git a/.venv/lib/python3.8/site-packages/IPython/html.py b/.venv/lib/python3.8/site-packages/IPython/html.py new file mode 100644 index 00000000..050be5c5 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/html.py @@ -0,0 +1,28 @@ +""" +Shim to maintain backwards compatibility with old IPython.html imports. +""" +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import sys +from warnings import warn + +from IPython.utils.shimmodule import ShimModule, ShimWarning + +warn("The `IPython.html` package has been deprecated since IPython 4.0. " + "You should import from `notebook` instead. " + "`IPython.html.widgets` has moved to `ipywidgets`.", ShimWarning) + +_widgets = sys.modules['IPython.html.widgets'] = ShimModule( + src='IPython.html.widgets', mirror='ipywidgets') + +_html = ShimModule( + src='IPython.html', mirror='notebook') + +# hook up widgets +_html.widgets = _widgets +sys.modules['IPython.html'] = _html + +if __name__ == '__main__': + from notebook import notebookapp as app + app.launch_new_instance() diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__init__.py b/.venv/lib/python3.8/site-packages/IPython/kernel/__init__.py new file mode 100644 index 00000000..70a05ed4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/__init__.py @@ -0,0 +1,35 @@ +""" +Shim to maintain backwards compatibility with old IPython.kernel imports. +""" +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import sys +from warnings import warn + +from IPython.utils.shimmodule import ShimModule, ShimWarning + +warn("The `IPython.kernel` package has been deprecated since IPython 4.0." + "You should import from ipykernel or jupyter_client instead.", ShimWarning) + + +# zmq subdir is gone +sys.modules['IPython.kernel.zmq.session'] = ShimModule( + src='IPython.kernel.zmq.session', mirror='jupyter_client.session') +sys.modules['IPython.kernel.zmq'] = ShimModule( + src='IPython.kernel.zmq', mirror='ipykernel') + +for pkg in ('comm', 'inprocess'): + src = 'IPython.kernel.%s' % pkg + sys.modules[src] = ShimModule(src=src, mirror='ipykernel.%s' % pkg) + +for pkg in ('ioloop', 'blocking'): + src = 'IPython.kernel.%s' % pkg + sys.modules[src] = ShimModule(src=src, mirror='jupyter_client.%s' % pkg) + +# required for `from IPython.kernel import PKG` +from ipykernel import comm, inprocess +from jupyter_client import ioloop, blocking +# public API +from ipykernel.connect import * +from jupyter_client import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__main__.py b/.venv/lib/python3.8/site-packages/IPython/kernel/__main__.py new file mode 100644 index 00000000..d1f0cf53 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/__main__.py @@ -0,0 +1,3 @@ +if __name__ == '__main__': + from ipykernel import kernelapp as app + app.launch_new_instance() diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..bb783c89 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/__main__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/__main__.cpython-38.pyc new file mode 100644 index 00000000..5256ff19 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/__main__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/adapter.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/adapter.cpython-38.pyc new file mode 100644 index 00000000..af00b18b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/adapter.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/channels.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/channels.cpython-38.pyc new file mode 100644 index 00000000..ed60973a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/channels.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/channelsabc.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/channelsabc.cpython-38.pyc new file mode 100644 index 00000000..80a43e16 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/channelsabc.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/client.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/client.cpython-38.pyc new file mode 100644 index 00000000..5bdc132e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/client.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/clientabc.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/clientabc.cpython-38.pyc new file mode 100644 index 00000000..295e511b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/clientabc.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/connect.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/connect.cpython-38.pyc new file mode 100644 index 00000000..7e2b550f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/connect.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/kernelspec.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/kernelspec.cpython-38.pyc new file mode 100644 index 00000000..a444458b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/kernelspec.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/kernelspecapp.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/kernelspecapp.cpython-38.pyc new file mode 100644 index 00000000..5a7ecede Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/kernelspecapp.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/launcher.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/launcher.cpython-38.pyc new file mode 100644 index 00000000..1b0e4f49 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/launcher.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/manager.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/manager.cpython-38.pyc new file mode 100644 index 00000000..d23344db Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/manager.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/managerabc.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/managerabc.cpython-38.pyc new file mode 100644 index 00000000..ea5f7976 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/managerabc.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/multikernelmanager.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/multikernelmanager.cpython-38.pyc new file mode 100644 index 00000000..98c1f381 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/multikernelmanager.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/restarter.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/restarter.cpython-38.pyc new file mode 100644 index 00000000..04822eb8 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/restarter.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/threaded.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/threaded.cpython-38.pyc new file mode 100644 index 00000000..1825aac1 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/kernel/__pycache__/threaded.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/adapter.py b/.venv/lib/python3.8/site-packages/IPython/kernel/adapter.py new file mode 100644 index 00000000..3b8c046b --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/adapter.py @@ -0,0 +1 @@ +from jupyter_client.adapter import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/channels.py b/.venv/lib/python3.8/site-packages/IPython/kernel/channels.py new file mode 100644 index 00000000..8c7fe2a0 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/channels.py @@ -0,0 +1 @@ +from jupyter_client.channels import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/channelsabc.py b/.venv/lib/python3.8/site-packages/IPython/kernel/channelsabc.py new file mode 100644 index 00000000..88944012 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/channelsabc.py @@ -0,0 +1 @@ +from jupyter_client.channelsabc import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/client.py b/.venv/lib/python3.8/site-packages/IPython/kernel/client.py new file mode 100644 index 00000000..a98690b7 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/client.py @@ -0,0 +1 @@ +from jupyter_client.client import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/clientabc.py b/.venv/lib/python3.8/site-packages/IPython/kernel/clientabc.py new file mode 100644 index 00000000..e0cf06c9 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/clientabc.py @@ -0,0 +1 @@ +from jupyter_client.clientabc import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/connect.py b/.venv/lib/python3.8/site-packages/IPython/kernel/connect.py new file mode 100644 index 00000000..5b6d40a5 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/connect.py @@ -0,0 +1,2 @@ +from ipykernel.connect import * +from jupyter_client.connect import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/kernelspec.py b/.venv/lib/python3.8/site-packages/IPython/kernel/kernelspec.py new file mode 100644 index 00000000..123419b2 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/kernelspec.py @@ -0,0 +1 @@ +from jupyter_client.kernelspec import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/kernelspecapp.py b/.venv/lib/python3.8/site-packages/IPython/kernel/kernelspecapp.py new file mode 100644 index 00000000..28cd33ab --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/kernelspecapp.py @@ -0,0 +1 @@ +from jupyter_client.kernelspecapp import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/launcher.py b/.venv/lib/python3.8/site-packages/IPython/kernel/launcher.py new file mode 100644 index 00000000..1953bc48 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/launcher.py @@ -0,0 +1 @@ +from jupyter_client.launcher import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/manager.py b/.venv/lib/python3.8/site-packages/IPython/kernel/manager.py new file mode 100644 index 00000000..c88097cf --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/manager.py @@ -0,0 +1 @@ +from jupyter_client.manager import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/managerabc.py b/.venv/lib/python3.8/site-packages/IPython/kernel/managerabc.py new file mode 100644 index 00000000..6b40827f --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/managerabc.py @@ -0,0 +1 @@ +from jupyter_client.managerabc import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/multikernelmanager.py b/.venv/lib/python3.8/site-packages/IPython/kernel/multikernelmanager.py new file mode 100644 index 00000000..ce576e27 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/multikernelmanager.py @@ -0,0 +1 @@ +from jupyter_client.multikernelmanager import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/restarter.py b/.venv/lib/python3.8/site-packages/IPython/kernel/restarter.py new file mode 100644 index 00000000..dc24117c --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/restarter.py @@ -0,0 +1 @@ +from jupyter_client.restarter import * diff --git a/.venv/lib/python3.8/site-packages/IPython/kernel/threaded.py b/.venv/lib/python3.8/site-packages/IPython/kernel/threaded.py new file mode 100644 index 00000000..4a1072f7 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/kernel/threaded.py @@ -0,0 +1 @@ +from jupyter_client.threaded import * diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__init__.py b/.venv/lib/python3.8/site-packages/IPython/lib/__init__.py new file mode 100644 index 00000000..8eb89012 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/__init__.py @@ -0,0 +1,21 @@ +# encoding: utf-8 +""" +Extra capabilities for IPython +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +from IPython.lib.security import passwd + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..21690f29 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/backgroundjobs.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/backgroundjobs.cpython-38.pyc new file mode 100644 index 00000000..e1823af8 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/backgroundjobs.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/clipboard.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/clipboard.cpython-38.pyc new file mode 100644 index 00000000..618ac70d Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/clipboard.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/deepreload.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/deepreload.cpython-38.pyc new file mode 100644 index 00000000..57dab7b6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/deepreload.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/demo.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/demo.cpython-38.pyc new file mode 100644 index 00000000..5c0bf65a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/demo.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/display.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/display.cpython-38.pyc new file mode 100644 index 00000000..60599e2e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/display.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/editorhooks.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/editorhooks.cpython-38.pyc new file mode 100644 index 00000000..c3d08663 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/editorhooks.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/guisupport.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/guisupport.cpython-38.pyc new file mode 100644 index 00000000..f3f62ec2 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/guisupport.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhook.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhook.cpython-38.pyc new file mode 100644 index 00000000..91ae601a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhook.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookglut.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookglut.cpython-38.pyc new file mode 100644 index 00000000..b6f2543d Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookglut.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookgtk.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookgtk.cpython-38.pyc new file mode 100644 index 00000000..9555a123 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookgtk.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookgtk3.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookgtk3.cpython-38.pyc new file mode 100644 index 00000000..aecc7566 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookgtk3.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookpyglet.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookpyglet.cpython-38.pyc new file mode 100644 index 00000000..f9a676fc Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookpyglet.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookqt4.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookqt4.cpython-38.pyc new file mode 100644 index 00000000..eac3ea63 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookqt4.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookwx.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookwx.cpython-38.pyc new file mode 100644 index 00000000..5a6e2c6d Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/inputhookwx.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/kernel.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/kernel.cpython-38.pyc new file mode 100644 index 00000000..118239f6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/kernel.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/latextools.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/latextools.cpython-38.pyc new file mode 100644 index 00000000..71a85611 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/latextools.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/lexers.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/lexers.cpython-38.pyc new file mode 100644 index 00000000..f6dfc6cc Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/lexers.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/pretty.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/pretty.cpython-38.pyc new file mode 100644 index 00000000..2bf8bd80 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/pretty.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/security.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/security.cpython-38.pyc new file mode 100644 index 00000000..75cb37cb Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/__pycache__/security.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/backgroundjobs.py b/.venv/lib/python3.8/site-packages/IPython/lib/backgroundjobs.py new file mode 100644 index 00000000..31997e13 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/backgroundjobs.py @@ -0,0 +1,491 @@ +# -*- coding: utf-8 -*- +"""Manage background (threaded) jobs conveniently from an interactive shell. + +This module provides a BackgroundJobManager class. This is the main class +meant for public usage, it implements an object which can create and manage +new background jobs. + +It also provides the actual job classes managed by these BackgroundJobManager +objects, see their docstrings below. + + +This system was inspired by discussions with B. Granger and the +BackgroundCommand class described in the book Python Scripting for +Computational Science, by H. P. Langtangen: + +http://folk.uio.no/hpl/scripting + +(although ultimately no code from this text was used, as IPython's system is a +separate implementation). + +An example notebook is provided in our documentation illustrating interactive +use of the system. +""" + +#***************************************************************************** +# Copyright (C) 2005-2006 Fernando Perez +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +# Code begins +import sys +import threading + +from IPython import get_ipython +from IPython.core.ultratb import AutoFormattedTB +from logging import error, debug + + +class BackgroundJobManager(object): + """Class to manage a pool of backgrounded threaded jobs. + + Below, we assume that 'jobs' is a BackgroundJobManager instance. + + Usage summary (see the method docstrings for details): + + jobs.new(...) -> start a new job + + jobs() or jobs.status() -> print status summary of all jobs + + jobs[N] -> returns job number N. + + foo = jobs[N].result -> assign to variable foo the result of job N + + jobs[N].traceback() -> print the traceback of dead job N + + jobs.remove(N) -> remove (finished) job N + + jobs.flush() -> remove all finished jobs + + As a convenience feature, BackgroundJobManager instances provide the + utility result and traceback methods which retrieve the corresponding + information from the jobs list: + + jobs.result(N) <--> jobs[N].result + jobs.traceback(N) <--> jobs[N].traceback() + + While this appears minor, it allows you to use tab completion + interactively on the job manager instance. + """ + + def __init__(self): + # Lists for job management, accessed via a property to ensure they're + # up to date.x + self._running = [] + self._completed = [] + self._dead = [] + # A dict of all jobs, so users can easily access any of them + self.all = {} + # For reporting + self._comp_report = [] + self._dead_report = [] + # Store status codes locally for fast lookups + self._s_created = BackgroundJobBase.stat_created_c + self._s_running = BackgroundJobBase.stat_running_c + self._s_completed = BackgroundJobBase.stat_completed_c + self._s_dead = BackgroundJobBase.stat_dead_c + self._current_job_id = 0 + + @property + def running(self): + self._update_status() + return self._running + + @property + def dead(self): + self._update_status() + return self._dead + + @property + def completed(self): + self._update_status() + return self._completed + + def new(self, func_or_exp, *args, **kwargs): + """Add a new background job and start it in a separate thread. + + There are two types of jobs which can be created: + + 1. Jobs based on expressions which can be passed to an eval() call. + The expression must be given as a string. For example: + + job_manager.new('myfunc(x,y,z=1)'[,glob[,loc]]) + + The given expression is passed to eval(), along with the optional + global/local dicts provided. If no dicts are given, they are + extracted automatically from the caller's frame. + + A Python statement is NOT a valid eval() expression. Basically, you + can only use as an eval() argument something which can go on the right + of an '=' sign and be assigned to a variable. + + For example,"print 'hello'" is not valid, but '2+3' is. + + 2. Jobs given a function object, optionally passing additional + positional arguments: + + job_manager.new(myfunc, x, y) + + The function is called with the given arguments. + + If you need to pass keyword arguments to your function, you must + supply them as a dict named kw: + + job_manager.new(myfunc, x, y, kw=dict(z=1)) + + The reason for this assymmetry is that the new() method needs to + maintain access to its own keywords, and this prevents name collisions + between arguments to new() and arguments to your own functions. + + In both cases, the result is stored in the job.result field of the + background job object. + + You can set `daemon` attribute of the thread by giving the keyword + argument `daemon`. + + Notes and caveats: + + 1. All threads running share the same standard output. Thus, if your + background jobs generate output, it will come out on top of whatever + you are currently writing. For this reason, background jobs are best + used with silent functions which simply return their output. + + 2. Threads also all work within the same global namespace, and this + system does not lock interactive variables. So if you send job to the + background which operates on a mutable object for a long time, and + start modifying that same mutable object interactively (or in another + backgrounded job), all sorts of bizarre behaviour will occur. + + 3. If a background job is spending a lot of time inside a C extension + module which does not release the Python Global Interpreter Lock + (GIL), this will block the IPython prompt. This is simply because the + Python interpreter can only switch between threads at Python + bytecodes. While the execution is inside C code, the interpreter must + simply wait unless the extension module releases the GIL. + + 4. There is no way, due to limitations in the Python threads library, + to kill a thread once it has started.""" + + if callable(func_or_exp): + kw = kwargs.get('kw',{}) + job = BackgroundJobFunc(func_or_exp,*args,**kw) + elif isinstance(func_or_exp, str): + if not args: + frame = sys._getframe(1) + glob, loc = frame.f_globals, frame.f_locals + elif len(args)==1: + glob = loc = args[0] + elif len(args)==2: + glob,loc = args + else: + raise ValueError( + 'Expression jobs take at most 2 args (globals,locals)') + job = BackgroundJobExpr(func_or_exp, glob, loc) + else: + raise TypeError('invalid args for new job') + + if kwargs.get('daemon', False): + job.daemon = True + job.num = self._current_job_id + self._current_job_id += 1 + self.running.append(job) + self.all[job.num] = job + debug('Starting job # %s in a separate thread.' % job.num) + job.start() + return job + + def __getitem__(self, job_key): + num = job_key if isinstance(job_key, int) else job_key.num + return self.all[num] + + def __call__(self): + """An alias to self.status(), + + This allows you to simply call a job manager instance much like the + Unix `jobs` shell command.""" + + return self.status() + + def _update_status(self): + """Update the status of the job lists. + + This method moves finished jobs to one of two lists: + - self.completed: jobs which completed successfully + - self.dead: jobs which finished but died. + + It also copies those jobs to corresponding _report lists. These lists + are used to report jobs completed/dead since the last update, and are + then cleared by the reporting function after each call.""" + + # Status codes + srun, scomp, sdead = self._s_running, self._s_completed, self._s_dead + # State lists, use the actual lists b/c the public names are properties + # that call this very function on access + running, completed, dead = self._running, self._completed, self._dead + + # Now, update all state lists + for num, job in enumerate(running): + stat = job.stat_code + if stat == srun: + continue + elif stat == scomp: + completed.append(job) + self._comp_report.append(job) + running[num] = False + elif stat == sdead: + dead.append(job) + self._dead_report.append(job) + running[num] = False + # Remove dead/completed jobs from running list + running[:] = filter(None, running) + + def _group_report(self,group,name): + """Report summary for a given job group. + + Return True if the group had any elements.""" + + if group: + print('%s jobs:' % name) + for job in group: + print('%s : %s' % (job.num,job)) + print() + return True + + def _group_flush(self,group,name): + """Flush a given job group + + Return True if the group had any elements.""" + + njobs = len(group) + if njobs: + plural = {1:''}.setdefault(njobs,'s') + print('Flushing %s %s job%s.' % (njobs,name,plural)) + group[:] = [] + return True + + def _status_new(self): + """Print the status of newly finished jobs. + + Return True if any new jobs are reported. + + This call resets its own state every time, so it only reports jobs + which have finished since the last time it was called.""" + + self._update_status() + new_comp = self._group_report(self._comp_report, 'Completed') + new_dead = self._group_report(self._dead_report, + 'Dead, call jobs.traceback() for details') + self._comp_report[:] = [] + self._dead_report[:] = [] + return new_comp or new_dead + + def status(self,verbose=0): + """Print a status of all jobs currently being managed.""" + + self._update_status() + self._group_report(self.running,'Running') + self._group_report(self.completed,'Completed') + self._group_report(self.dead,'Dead') + # Also flush the report queues + self._comp_report[:] = [] + self._dead_report[:] = [] + + def remove(self,num): + """Remove a finished (completed or dead) job.""" + + try: + job = self.all[num] + except KeyError: + error('Job #%s not found' % num) + else: + stat_code = job.stat_code + if stat_code == self._s_running: + error('Job #%s is still running, it can not be removed.' % num) + return + elif stat_code == self._s_completed: + self.completed.remove(job) + elif stat_code == self._s_dead: + self.dead.remove(job) + + def flush(self): + """Flush all finished jobs (completed and dead) from lists. + + Running jobs are never flushed. + + It first calls _status_new(), to update info. If any jobs have + completed since the last _status_new() call, the flush operation + aborts.""" + + # Remove the finished jobs from the master dict + alljobs = self.all + for job in self.completed+self.dead: + del(alljobs[job.num]) + + # Now flush these lists completely + fl_comp = self._group_flush(self.completed, 'Completed') + fl_dead = self._group_flush(self.dead, 'Dead') + if not (fl_comp or fl_dead): + print('No jobs to flush.') + + def result(self,num): + """result(N) -> return the result of job N.""" + try: + return self.all[num].result + except KeyError: + error('Job #%s not found' % num) + + def _traceback(self, job): + num = job if isinstance(job, int) else job.num + try: + self.all[num].traceback() + except KeyError: + error('Job #%s not found' % num) + + def traceback(self, job=None): + if job is None: + self._update_status() + for deadjob in self.dead: + print("Traceback for: %r" % deadjob) + self._traceback(deadjob) + print() + else: + self._traceback(job) + + +class BackgroundJobBase(threading.Thread): + """Base class to build BackgroundJob classes. + + The derived classes must implement: + + - Their own __init__, since the one here raises NotImplementedError. The + derived constructor must call self._init() at the end, to provide common + initialization. + + - A strform attribute used in calls to __str__. + + - A call() method, which will make the actual execution call and must + return a value to be held in the 'result' field of the job object. + """ + + # Class constants for status, in string and as numerical codes (when + # updating jobs lists, we don't want to do string comparisons). This will + # be done at every user prompt, so it has to be as fast as possible + stat_created = 'Created'; stat_created_c = 0 + stat_running = 'Running'; stat_running_c = 1 + stat_completed = 'Completed'; stat_completed_c = 2 + stat_dead = 'Dead (Exception), call jobs.traceback() for details' + stat_dead_c = -1 + + def __init__(self): + """Must be implemented in subclasses. + + Subclasses must call :meth:`_init` for standard initialisation. + """ + raise NotImplementedError("This class can not be instantiated directly.") + + def _init(self): + """Common initialization for all BackgroundJob objects""" + + for attr in ['call','strform']: + assert hasattr(self,attr), "Missing attribute <%s>" % attr + + # The num tag can be set by an external job manager + self.num = None + + self.status = BackgroundJobBase.stat_created + self.stat_code = BackgroundJobBase.stat_created_c + self.finished = False + self.result = '' + + # reuse the ipython traceback handler if we can get to it, otherwise + # make a new one + try: + make_tb = get_ipython().InteractiveTB.text + except: + make_tb = AutoFormattedTB(mode = 'Context', + color_scheme='NoColor', + tb_offset = 1).text + # Note that the actual API for text() requires the three args to be + # passed in, so we wrap it in a simple lambda. + self._make_tb = lambda : make_tb(None, None, None) + + # Hold a formatted traceback if one is generated. + self._tb = None + + threading.Thread.__init__(self) + + def __str__(self): + return self.strform + + def __repr__(self): + return '' % (self.num, self.strform) + + def traceback(self): + print(self._tb) + + def run(self): + try: + self.status = BackgroundJobBase.stat_running + self.stat_code = BackgroundJobBase.stat_running_c + self.result = self.call() + except: + self.status = BackgroundJobBase.stat_dead + self.stat_code = BackgroundJobBase.stat_dead_c + self.finished = None + self.result = ('') + self._tb = self._make_tb() + else: + self.status = BackgroundJobBase.stat_completed + self.stat_code = BackgroundJobBase.stat_completed_c + self.finished = True + + +class BackgroundJobExpr(BackgroundJobBase): + """Evaluate an expression as a background job (uses a separate thread).""" + + def __init__(self, expression, glob=None, loc=None): + """Create a new job from a string which can be fed to eval(). + + global/locals dicts can be provided, which will be passed to the eval + call.""" + + # fail immediately if the given expression can't be compiled + self.code = compile(expression,'','eval') + + glob = {} if glob is None else glob + loc = {} if loc is None else loc + self.expression = self.strform = expression + self.glob = glob + self.loc = loc + self._init() + + def call(self): + return eval(self.code,self.glob,self.loc) + + +class BackgroundJobFunc(BackgroundJobBase): + """Run a function call as a background job (uses a separate thread).""" + + def __init__(self, func, *args, **kwargs): + """Create a new job from a callable object. + + Any positional arguments and keyword args given to this constructor + after the initial callable are passed directly to it.""" + + if not callable(func): + raise TypeError( + 'first argument to BackgroundJobFunc must be callable') + + self.func = func + self.args = args + self.kwargs = kwargs + # The string form will only include the function passed, because + # generating string representations of the arguments is a potentially + # _very_ expensive operation (e.g. with large arrays). + self.strform = str(func) + self._init() + + def call(self): + return self.func(*self.args, **self.kwargs) diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/clipboard.py b/.venv/lib/python3.8/site-packages/IPython/lib/clipboard.py new file mode 100644 index 00000000..316a8ab1 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/clipboard.py @@ -0,0 +1,69 @@ +""" Utilities for accessing the platform's clipboard. +""" + +import subprocess + +from IPython.core.error import TryNext +import IPython.utils.py3compat as py3compat + +class ClipboardEmpty(ValueError): + pass + +def win32_clipboard_get(): + """ Get the current clipboard's text on Windows. + + Requires Mark Hammond's pywin32 extensions. + """ + try: + import win32clipboard + except ImportError: + raise TryNext("Getting text from the clipboard requires the pywin32 " + "extensions: http://sourceforge.net/projects/pywin32/") + win32clipboard.OpenClipboard() + try: + text = win32clipboard.GetClipboardData(win32clipboard.CF_UNICODETEXT) + except (TypeError, win32clipboard.error): + try: + text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT) + text = py3compat.cast_unicode(text, py3compat.DEFAULT_ENCODING) + except (TypeError, win32clipboard.error): + raise ClipboardEmpty + finally: + win32clipboard.CloseClipboard() + return text + +def osx_clipboard_get() -> str: + """ Get the clipboard's text on OS X. + """ + p = subprocess.Popen(['pbpaste', '-Prefer', 'ascii'], + stdout=subprocess.PIPE) + bytes_, stderr = p.communicate() + # Text comes in with old Mac \r line endings. Change them to \n. + bytes_ = bytes_.replace(b'\r', b'\n') + text = py3compat.decode(bytes_) + return text + +def tkinter_clipboard_get(): + """ Get the clipboard's text using Tkinter. + + This is the default on systems that are not Windows or OS X. It may + interfere with other UI toolkits and should be replaced with an + implementation that uses that toolkit. + """ + try: + from tkinter import Tk, TclError + except ImportError: + raise TryNext("Getting text from the clipboard on this platform requires tkinter.") + + root = Tk() + root.withdraw() + try: + text = root.clipboard_get() + except TclError: + raise ClipboardEmpty + finally: + root.destroy() + text = py3compat.cast_unicode(text, py3compat.DEFAULT_ENCODING) + return text + + diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/deepreload.py b/.venv/lib/python3.8/site-packages/IPython/lib/deepreload.py new file mode 100644 index 00000000..bd8c01b2 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/deepreload.py @@ -0,0 +1,341 @@ +# -*- coding: utf-8 -*- +""" +Provides a reload() function that acts recursively. + +Python's normal :func:`python:reload` function only reloads the module that it's +passed. The :func:`reload` function in this module also reloads everything +imported from that module, which is useful when you're changing files deep +inside a package. + +To use this as your default reload function, type this:: + + import builtins + from IPython.lib import deepreload + builtins.reload = deepreload.reload + +A reference to the original :func:`python:reload` is stored in this module as +:data:`original_reload`, so you can restore it later. + +This code is almost entirely based on knee.py, which is a Python +re-implementation of hierarchical module import. +""" +#***************************************************************************** +# Copyright (C) 2001 Nathaniel Gray +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +import builtins as builtin_mod +from contextlib import contextmanager +import imp +import sys + +from types import ModuleType +from warnings import warn +import types + +original_import = builtin_mod.__import__ + +@contextmanager +def replace_import_hook(new_import): + saved_import = builtin_mod.__import__ + builtin_mod.__import__ = new_import + try: + yield + finally: + builtin_mod.__import__ = saved_import + +def get_parent(globals, level): + """ + parent, name = get_parent(globals, level) + + Return the package that an import is being performed in. If globals comes + from the module foo.bar.bat (not itself a package), this returns the + sys.modules entry for foo.bar. If globals is from a package's __init__.py, + the package's entry in sys.modules is returned. + + If globals doesn't come from a package or a module in a package, or a + corresponding entry is not found in sys.modules, None is returned. + """ + orig_level = level + + if not level or not isinstance(globals, dict): + return None, '' + + pkgname = globals.get('__package__', None) + + if pkgname is not None: + # __package__ is set, so use it + if not hasattr(pkgname, 'rindex'): + raise ValueError('__package__ set to non-string') + if len(pkgname) == 0: + if level > 0: + raise ValueError('Attempted relative import in non-package') + return None, '' + name = pkgname + else: + # __package__ not set, so figure it out and set it + if '__name__' not in globals: + return None, '' + modname = globals['__name__'] + + if '__path__' in globals: + # __path__ is set, so modname is already the package name + globals['__package__'] = name = modname + else: + # Normal module, so work out the package name if any + lastdot = modname.rfind('.') + if lastdot < 0 < level: + raise ValueError("Attempted relative import in non-package") + if lastdot < 0: + globals['__package__'] = None + return None, '' + globals['__package__'] = name = modname[:lastdot] + + dot = len(name) + for x in range(level, 1, -1): + try: + dot = name.rindex('.', 0, dot) + except ValueError: + raise ValueError("attempted relative import beyond top-level " + "package") + name = name[:dot] + + try: + parent = sys.modules[name] + except: + if orig_level < 1: + warn("Parent module '%.200s' not found while handling absolute " + "import" % name) + parent = None + else: + raise SystemError("Parent module '%.200s' not loaded, cannot " + "perform relative import" % name) + + # We expect, but can't guarantee, if parent != None, that: + # - parent.__name__ == name + # - parent.__dict__ is globals + # If this is violated... Who cares? + return parent, name + +def load_next(mod, altmod, name, buf): + """ + mod, name, buf = load_next(mod, altmod, name, buf) + + altmod is either None or same as mod + """ + + if len(name) == 0: + # completely empty module name should only happen in + # 'from . import' (or '__import__("")') + return mod, None, buf + + dot = name.find('.') + if dot == 0: + raise ValueError('Empty module name') + + if dot < 0: + subname = name + next = None + else: + subname = name[:dot] + next = name[dot+1:] + + if buf != '': + buf += '.' + buf += subname + + result = import_submodule(mod, subname, buf) + if result is None and mod != altmod: + result = import_submodule(altmod, subname, subname) + if result is not None: + buf = subname + + if result is None: + raise ImportError("No module named %.200s" % name) + + return result, next, buf + + +# Need to keep track of what we've already reloaded to prevent cyclic evil +found_now = {} + +def import_submodule(mod, subname, fullname): + """m = import_submodule(mod, subname, fullname)""" + # Require: + # if mod == None: subname == fullname + # else: mod.__name__ + "." + subname == fullname + + global found_now + if fullname in found_now and fullname in sys.modules: + m = sys.modules[fullname] + else: + print('Reloading', fullname) + found_now[fullname] = 1 + oldm = sys.modules.get(fullname, None) + + if mod is None: + path = None + elif hasattr(mod, '__path__'): + path = mod.__path__ + else: + return None + + try: + # This appears to be necessary on Python 3, because imp.find_module() + # tries to import standard libraries (like io) itself, and we don't + # want them to be processed by our deep_import_hook. + with replace_import_hook(original_import): + fp, filename, stuff = imp.find_module(subname, path) + except ImportError: + return None + + try: + m = imp.load_module(fullname, fp, filename, stuff) + except: + # load_module probably removed name from modules because of + # the error. Put back the original module object. + if oldm: + sys.modules[fullname] = oldm + raise + finally: + if fp: fp.close() + + add_submodule(mod, m, fullname, subname) + + return m + +def add_submodule(mod, submod, fullname, subname): + """mod.{subname} = submod""" + if mod is None: + return #Nothing to do here. + + if submod is None: + submod = sys.modules[fullname] + + setattr(mod, subname, submod) + + return + +def ensure_fromlist(mod, fromlist, buf, recursive): + """Handle 'from module import a, b, c' imports.""" + if not hasattr(mod, '__path__'): + return + for item in fromlist: + if not hasattr(item, 'rindex'): + raise TypeError("Item in ``from list'' not a string") + if item == '*': + if recursive: + continue # avoid endless recursion + try: + all = mod.__all__ + except AttributeError: + pass + else: + ret = ensure_fromlist(mod, all, buf, 1) + if not ret: + return 0 + elif not hasattr(mod, item): + import_submodule(mod, item, buf + '.' + item) + +def deep_import_hook(name, globals=None, locals=None, fromlist=None, level=-1): + """Replacement for __import__()""" + parent, buf = get_parent(globals, level) + + head, name, buf = load_next(parent, None if level < 0 else parent, name, buf) + + tail = head + while name: + tail, name, buf = load_next(tail, tail, name, buf) + + # If tail is None, both get_parent and load_next found + # an empty module name: someone called __import__("") or + # doctored faulty bytecode + if tail is None: + raise ValueError('Empty module name') + + if not fromlist: + return head + + ensure_fromlist(tail, fromlist, buf, 0) + return tail + +modules_reloading = {} + +def deep_reload_hook(m): + """Replacement for reload().""" + # Hardcode this one as it would raise a NotImplementedError from the + # bowels of Python and screw up the import machinery after. + # unlike other imports the `exclude` list already in place is not enough. + + if m is types: + return m + if not isinstance(m, ModuleType): + raise TypeError("reload() argument must be module") + + name = m.__name__ + + if name not in sys.modules: + raise ImportError("reload(): module %.200s not in sys.modules" % name) + + global modules_reloading + try: + return modules_reloading[name] + except: + modules_reloading[name] = m + + dot = name.rfind('.') + if dot < 0: + subname = name + path = None + else: + try: + parent = sys.modules[name[:dot]] + except KeyError: + modules_reloading.clear() + raise ImportError("reload(): parent %.200s not in sys.modules" % name[:dot]) + subname = name[dot+1:] + path = getattr(parent, "__path__", None) + + try: + # This appears to be necessary on Python 3, because imp.find_module() + # tries to import standard libraries (like io) itself, and we don't + # want them to be processed by our deep_import_hook. + with replace_import_hook(original_import): + fp, filename, stuff = imp.find_module(subname, path) + finally: + modules_reloading.clear() + + try: + newm = imp.load_module(name, fp, filename, stuff) + except: + # load_module probably removed name from modules because of + # the error. Put back the original module object. + sys.modules[name] = m + raise + finally: + if fp: fp.close() + + modules_reloading.clear() + return newm + +# Save the original hooks +original_reload = imp.reload + +# Replacement for reload() +def reload(module, exclude=('sys', 'os.path', 'builtins', '__main__', + 'numpy', 'numpy._globals')): + """Recursively reload all modules used in the given module. Optionally + takes a list of modules to exclude from reloading. The default exclude + list contains sys, __main__, and __builtin__, to prevent, e.g., resetting + display, exception, and io hooks. + """ + global found_now + for i in exclude: + found_now[i] = 1 + try: + with replace_import_hook(deep_import_hook): + return deep_reload_hook(module) + finally: + found_now = {} diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/demo.py b/.venv/lib/python3.8/site-packages/IPython/lib/demo.py new file mode 100644 index 00000000..0b19c413 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/demo.py @@ -0,0 +1,671 @@ +"""Module for interactive demos using IPython. + +This module implements a few classes for running Python scripts interactively +in IPython for demonstrations. With very simple markup (a few tags in +comments), you can control points where the script stops executing and returns +control to IPython. + + +Provided classes +---------------- + +The classes are (see their docstrings for further details): + + - Demo: pure python demos + + - IPythonDemo: demos with input to be processed by IPython as if it had been + typed interactively (so magics work, as well as any other special syntax you + may have added via input prefilters). + + - LineDemo: single-line version of the Demo class. These demos are executed + one line at a time, and require no markup. + + - IPythonLineDemo: IPython version of the LineDemo class (the demo is + executed a line at a time, but processed via IPython). + + - ClearMixin: mixin to make Demo classes with less visual clutter. It + declares an empty marquee and a pre_cmd that clears the screen before each + block (see Subclassing below). + + - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo + classes. + +Inheritance diagram: + +.. inheritance-diagram:: IPython.lib.demo + :parts: 3 + +Subclassing +----------- + +The classes here all include a few methods meant to make customization by +subclassing more convenient. Their docstrings below have some more details: + + - highlight(): format every block and optionally highlight comments and + docstring content. + + - marquee(): generates a marquee to provide visible on-screen markers at each + block start and end. + + - pre_cmd(): run right before the execution of each block. + + - post_cmd(): run right after the execution of each block. If the block + raises an exception, this is NOT called. + + +Operation +--------- + +The file is run in its own empty namespace (though you can pass it a string of +arguments as if in a command line environment, and it will see those as +sys.argv). But at each stop, the global IPython namespace is updated with the +current internal demo namespace, so you can work interactively with the data +accumulated so far. + +By default, each block of code is printed (with syntax highlighting) before +executing it and you have to confirm execution. This is intended to show the +code to an audience first so you can discuss it, and only proceed with +execution once you agree. There are a few tags which allow you to modify this +behavior. + +The supported tags are: + +# stop + + Defines block boundaries, the points where IPython stops execution of the + file and returns to the interactive prompt. + + You can optionally mark the stop tag with extra dashes before and after the + word 'stop', to help visually distinguish the blocks in a text editor: + + # --- stop --- + + +# silent + + Make a block execute silently (and hence automatically). Typically used in + cases where you have some boilerplate or initialization code which you need + executed but do not want to be seen in the demo. + +# auto + + Make a block execute automatically, but still being printed. Useful for + simple code which does not warrant discussion, since it avoids the extra + manual confirmation. + +# auto_all + + This tag can _only_ be in the first block, and if given it overrides the + individual auto tags to make the whole demo fully automatic (no block asks + for confirmation). It can also be given at creation time (or the attribute + set later) to override what's in the file. + +While _any_ python file can be run as a Demo instance, if there are no stop +tags the whole file will run in a single block (no different that calling +first %pycat and then %run). The minimal markup to make this useful is to +place a set of stop tags; the other tags are only there to let you fine-tune +the execution. + +This is probably best explained with the simple example file below. You can +copy this into a file named ex_demo.py, and try running it via:: + + from IPython.lib.demo import Demo + d = Demo('ex_demo.py') + d() + +Each time you call the demo object, it runs the next block. The demo object +has a few useful methods for navigation, like again(), edit(), jump(), seek() +and back(). It can be reset for a new run via reset() or reloaded from disk +(in case you've edited the source) via reload(). See their docstrings below. + +Note: To make this simpler to explore, a file called "demo-exercizer.py" has +been added to the "docs/examples/core" directory. Just cd to this directory in +an IPython session, and type:: + + %run demo-exercizer.py + +and then follow the directions. + +Example +------- + +The following is a very simple example of a valid demo file. + +:: + + #################### EXAMPLE DEMO ############################### + '''A simple interactive demo to illustrate the use of IPython's Demo class.''' + + print 'Hello, welcome to an interactive IPython demo.' + + # The mark below defines a block boundary, which is a point where IPython will + # stop execution and return to the interactive prompt. The dashes are actually + # optional and used only as a visual aid to clearly separate blocks while + # editing the demo code. + # stop + + x = 1 + y = 2 + + # stop + + # the mark below makes this block as silent + # silent + + print 'This is a silent block, which gets executed but not printed.' + + # stop + # auto + print 'This is an automatic block.' + print 'It is executed without asking for confirmation, but printed.' + z = x+y + + print 'z=',x + + # stop + # This is just another normal block. + print 'z is now:', z + + print 'bye!' + ################### END EXAMPLE DEMO ############################ +""" + + +#***************************************************************************** +# Copyright (C) 2005-2006 Fernando Perez. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +# +#***************************************************************************** + +import os +import re +import shlex +import sys +import pygments + +from IPython.utils.text import marquee +from IPython.utils import openpy +from IPython.utils import py3compat +__all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] + +class DemoError(Exception): pass + +def re_mark(mark): + return re.compile(r'^\s*#\s+\s+%s\s*$' % mark,re.MULTILINE) + +class Demo(object): + + re_stop = re_mark(r'-*\s?stop\s?-*') + re_silent = re_mark('silent') + re_auto = re_mark('auto') + re_auto_all = re_mark('auto_all') + + def __init__(self,src,title='',arg_str='',auto_all=None, format_rst=False, + formatter='terminal', style='default'): + """Make a new demo object. To run the demo, simply call the object. + + See the module docstring for full details and an example (you can use + IPython.Demo? in IPython to see it). + + Inputs: + + - src is either a file, or file-like object, or a + string that can be resolved to a filename. + + Optional inputs: + + - title: a string to use as the demo name. Of most use when the demo + you are making comes from an object that has no filename, or if you + want an alternate denotation distinct from the filename. + + - arg_str(''): a string of arguments, internally converted to a list + just like sys.argv, so the demo script can see a similar + environment. + + - auto_all(None): global flag to run all blocks automatically without + confirmation. This attribute overrides the block-level tags and + applies to the whole demo. It is an attribute of the object, and + can be changed at runtime simply by reassigning it to a boolean + value. + + - format_rst(False): a bool to enable comments and doc strings + formatting with pygments rst lexer + + - formatter('terminal'): a string of pygments formatter name to be + used. Useful values for terminals: terminal, terminal256, + terminal16m + + - style('default'): a string of pygments style name to be used. + """ + if hasattr(src, "read"): + # It seems to be a file or a file-like object + self.fname = "from a file-like object" + if title == '': + self.title = "from a file-like object" + else: + self.title = title + else: + # Assume it's a string or something that can be converted to one + self.fname = src + if title == '': + (filepath, filename) = os.path.split(src) + self.title = filename + else: + self.title = title + self.sys_argv = [src] + shlex.split(arg_str) + self.auto_all = auto_all + self.src = src + + try: + ip = get_ipython() # this is in builtins whenever IPython is running + self.inside_ipython = True + except NameError: + self.inside_ipython = False + + if self.inside_ipython: + # get a few things from ipython. While it's a bit ugly design-wise, + # it ensures that things like color scheme and the like are always in + # sync with the ipython mode being used. This class is only meant to + # be used inside ipython anyways, so it's OK. + self.ip_ns = ip.user_ns + self.ip_colorize = ip.pycolorize + self.ip_showtb = ip.showtraceback + self.ip_run_cell = ip.run_cell + self.shell = ip + + self.formatter = pygments.formatters.get_formatter_by_name(formatter, + style=style) + self.python_lexer = pygments.lexers.get_lexer_by_name("py3") + self.format_rst = format_rst + if format_rst: + self.rst_lexer = pygments.lexers.get_lexer_by_name("rst") + + # load user data and initialize data structures + self.reload() + + def fload(self): + """Load file object.""" + # read data and parse into blocks + if hasattr(self, 'fobj') and self.fobj is not None: + self.fobj.close() + if hasattr(self.src, "read"): + # It seems to be a file or a file-like object + self.fobj = self.src + else: + # Assume it's a string or something that can be converted to one + self.fobj = openpy.open(self.fname) + + def reload(self): + """Reload source from disk and initialize state.""" + self.fload() + + self.src = "".join(openpy.strip_encoding_cookie(self.fobj)) + src_b = [b.strip() for b in self.re_stop.split(self.src) if b] + self._silent = [bool(self.re_silent.findall(b)) for b in src_b] + self._auto = [bool(self.re_auto.findall(b)) for b in src_b] + + # if auto_all is not given (def. None), we read it from the file + if self.auto_all is None: + self.auto_all = bool(self.re_auto_all.findall(src_b[0])) + else: + self.auto_all = bool(self.auto_all) + + # Clean the sources from all markup so it doesn't get displayed when + # running the demo + src_blocks = [] + auto_strip = lambda s: self.re_auto.sub('',s) + for i,b in enumerate(src_b): + if self._auto[i]: + src_blocks.append(auto_strip(b)) + else: + src_blocks.append(b) + # remove the auto_all marker + src_blocks[0] = self.re_auto_all.sub('',src_blocks[0]) + + self.nblocks = len(src_blocks) + self.src_blocks = src_blocks + + # also build syntax-highlighted source + self.src_blocks_colored = list(map(self.highlight,self.src_blocks)) + + # ensure clean namespace and seek offset + self.reset() + + def reset(self): + """Reset the namespace and seek pointer to restart the demo""" + self.user_ns = {} + self.finished = False + self.block_index = 0 + + def _validate_index(self,index): + if index<0 or index>=self.nblocks: + raise ValueError('invalid block index %s' % index) + + def _get_index(self,index): + """Get the current block index, validating and checking status. + + Returns None if the demo is finished""" + + if index is None: + if self.finished: + print('Demo finished. Use .reset() if you want to rerun it.') + return None + index = self.block_index + else: + self._validate_index(index) + return index + + def seek(self,index): + """Move the current seek pointer to the given block. + + You can use negative indices to seek from the end, with identical + semantics to those of Python lists.""" + if index<0: + index = self.nblocks + index + self._validate_index(index) + self.block_index = index + self.finished = False + + def back(self,num=1): + """Move the seek pointer back num blocks (default is 1).""" + self.seek(self.block_index-num) + + def jump(self,num=1): + """Jump a given number of blocks relative to the current one. + + The offset can be positive or negative, defaults to 1.""" + self.seek(self.block_index+num) + + def again(self): + """Move the seek pointer back one block and re-execute.""" + self.back(1) + self() + + def edit(self,index=None): + """Edit a block. + + If no number is given, use the last block executed. + + This edits the in-memory copy of the demo, it does NOT modify the + original source file. If you want to do that, simply open the file in + an editor and use reload() when you make changes to the file. This + method is meant to let you change a block during a demonstration for + explanatory purposes, without damaging your original script.""" + + index = self._get_index(index) + if index is None: + return + # decrease the index by one (unless we're at the very beginning), so + # that the default demo.edit() call opens up the sblock we've last run + if index>0: + index -= 1 + + filename = self.shell.mktempfile(self.src_blocks[index]) + self.shell.hooks.editor(filename,1) + with open(filename, 'r') as f: + new_block = f.read() + # update the source and colored block + self.src_blocks[index] = new_block + self.src_blocks_colored[index] = self.highlight(new_block) + self.block_index = index + # call to run with the newly edited index + self() + + def show(self,index=None): + """Show a single block on screen""" + + index = self._get_index(index) + if index is None: + return + + print(self.marquee('<%s> block # %s (%s remaining)' % + (self.title,index,self.nblocks-index-1))) + print(self.src_blocks_colored[index]) + sys.stdout.flush() + + def show_all(self): + """Show entire demo on screen, block by block""" + + fname = self.title + title = self.title + nblocks = self.nblocks + silent = self._silent + marquee = self.marquee + for index,block in enumerate(self.src_blocks_colored): + if silent[index]: + print(marquee('<%s> SILENT block # %s (%s remaining)' % + (title,index,nblocks-index-1))) + else: + print(marquee('<%s> block # %s (%s remaining)' % + (title,index,nblocks-index-1))) + print(block, end=' ') + sys.stdout.flush() + + def run_cell(self,source): + """Execute a string with one or more lines of code""" + + exec(source, self.user_ns) + + def __call__(self,index=None): + """run a block of the demo. + + If index is given, it should be an integer >=1 and <= nblocks. This + means that the calling convention is one off from typical Python + lists. The reason for the inconsistency is that the demo always + prints 'Block n/N, and N is the total, so it would be very odd to use + zero-indexing here.""" + + index = self._get_index(index) + if index is None: + return + try: + marquee = self.marquee + next_block = self.src_blocks[index] + self.block_index += 1 + if self._silent[index]: + print(marquee('Executing silent block # %s (%s remaining)' % + (index,self.nblocks-index-1))) + else: + self.pre_cmd() + self.show(index) + if self.auto_all or self._auto[index]: + print(marquee('output:')) + else: + print(marquee('Press to quit, to execute...'), end=' ') + ans = py3compat.input().strip() + if ans: + print(marquee('Block NOT executed')) + return + try: + save_argv = sys.argv + sys.argv = self.sys_argv + self.run_cell(next_block) + self.post_cmd() + finally: + sys.argv = save_argv + + except: + if self.inside_ipython: + self.ip_showtb(filename=self.fname) + else: + if self.inside_ipython: + self.ip_ns.update(self.user_ns) + + if self.block_index == self.nblocks: + mq1 = self.marquee('END OF DEMO') + if mq1: + # avoid spurious print if empty marquees are used + print() + print(mq1) + print(self.marquee('Use .reset() if you want to rerun it.')) + self.finished = True + + # These methods are meant to be overridden by subclasses who may wish to + # customize the behavior of of their demos. + def marquee(self,txt='',width=78,mark='*'): + """Return the input string centered in a 'marquee'.""" + return marquee(txt,width,mark) + + def pre_cmd(self): + """Method called before executing each block.""" + pass + + def post_cmd(self): + """Method called after executing each block.""" + pass + + def highlight(self, block): + """Method called on each block to highlight it content""" + tokens = pygments.lex(block, self.python_lexer) + if self.format_rst: + from pygments.token import Token + toks = [] + for token in tokens: + if token[0] == Token.String.Doc and len(token[1]) > 6: + toks += pygments.lex(token[1][:3], self.python_lexer) + # parse doc string content by rst lexer + toks += pygments.lex(token[1][3:-3], self.rst_lexer) + toks += pygments.lex(token[1][-3:], self.python_lexer) + elif token[0] == Token.Comment.Single: + toks.append((Token.Comment.Single, token[1][0])) + # parse comment content by rst lexer + # remove the extrat newline added by rst lexer + toks += list(pygments.lex(token[1][1:], self.rst_lexer))[:-1] + else: + toks.append(token) + tokens = toks + return pygments.format(tokens, self.formatter) + + +class IPythonDemo(Demo): + """Class for interactive demos with IPython's input processing applied. + + This subclasses Demo, but instead of executing each block by the Python + interpreter (via exec), it actually calls IPython on it, so that any input + filters which may be in place are applied to the input block. + + If you have an interactive environment which exposes special input + processing, you can use this class instead to write demo scripts which + operate exactly as if you had typed them interactively. The default Demo + class requires the input to be valid, pure Python code. + """ + + def run_cell(self,source): + """Execute a string with one or more lines of code""" + + self.shell.run_cell(source) + +class LineDemo(Demo): + """Demo where each line is executed as a separate block. + + The input script should be valid Python code. + + This class doesn't require any markup at all, and it's meant for simple + scripts (with no nesting or any kind of indentation) which consist of + multiple lines of input to be executed, one at a time, as if they had been + typed in the interactive prompt. + + Note: the input can not have *any* indentation, which means that only + single-lines of input are accepted, not even function definitions are + valid.""" + + def reload(self): + """Reload source from disk and initialize state.""" + # read data and parse into blocks + self.fload() + lines = self.fobj.readlines() + src_b = [l for l in lines if l.strip()] + nblocks = len(src_b) + self.src = ''.join(lines) + self._silent = [False]*nblocks + self._auto = [True]*nblocks + self.auto_all = True + self.nblocks = nblocks + self.src_blocks = src_b + + # also build syntax-highlighted source + self.src_blocks_colored = list(map(self.highlight,self.src_blocks)) + + # ensure clean namespace and seek offset + self.reset() + + +class IPythonLineDemo(IPythonDemo,LineDemo): + """Variant of the LineDemo class whose input is processed by IPython.""" + pass + + +class ClearMixin(object): + """Use this mixin to make Demo classes with less visual clutter. + + Demos using this mixin will clear the screen before every block and use + blank marquees. + + Note that in order for the methods defined here to actually override those + of the classes it's mixed with, it must go /first/ in the inheritance + tree. For example: + + class ClearIPDemo(ClearMixin,IPythonDemo): pass + + will provide an IPythonDemo class with the mixin's features. + """ + + def marquee(self,txt='',width=78,mark='*'): + """Blank marquee that returns '' no matter what the input.""" + return '' + + def pre_cmd(self): + """Method called before executing each block. + + This one simply clears the screen.""" + from IPython.utils.terminal import _term_clear + _term_clear() + +class ClearDemo(ClearMixin,Demo): + pass + + +class ClearIPDemo(ClearMixin,IPythonDemo): + pass + + +def slide(file_path, noclear=False, format_rst=True, formatter="terminal", + style="native", auto_all=False, delimiter='...'): + if noclear: + demo_class = Demo + else: + demo_class = ClearDemo + demo = demo_class(file_path, format_rst=format_rst, formatter=formatter, + style=style, auto_all=auto_all) + while not demo.finished: + demo() + try: + py3compat.input('\n' + delimiter) + except KeyboardInterrupt: + exit(1) + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser(description='Run python demos') + parser.add_argument('--noclear', '-C', action='store_true', + help='Do not clear terminal on each slide') + parser.add_argument('--rst', '-r', action='store_true', + help='Highlight comments and dostrings as rst') + parser.add_argument('--formatter', '-f', default='terminal', + help='pygments formatter name could be: terminal, ' + 'terminal256, terminal16m') + parser.add_argument('--style', '-s', default='default', + help='pygments style name') + parser.add_argument('--auto', '-a', action='store_true', + help='Run all blocks automatically without' + 'confirmation') + parser.add_argument('--delimiter', '-d', default='...', + help='slides delimiter added after each slide run') + parser.add_argument('file', nargs=1, + help='python demo file') + args = parser.parse_args() + slide(args.file[0], noclear=args.noclear, format_rst=args.rst, + formatter=args.formatter, style=args.style, auto_all=args.auto, + delimiter=args.delimiter) diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/display.py b/.venv/lib/python3.8/site-packages/IPython/lib/display.py new file mode 100644 index 00000000..de31788a --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/display.py @@ -0,0 +1,654 @@ +"""Various display related classes. + +Authors : MinRK, gregcaporaso, dannystaple +""" +from html import escape as html_escape +from os.path import exists, isfile, splitext, abspath, join, isdir +from os import walk, sep, fsdecode + +from IPython.core.display import DisplayObject, TextDisplayObject + +__all__ = ['Audio', 'IFrame', 'YouTubeVideo', 'VimeoVideo', 'ScribdDocument', + 'FileLink', 'FileLinks', 'Code'] + + +class Audio(DisplayObject): + """Create an audio object. + + When this object is returned by an input cell or passed to the + display function, it will result in Audio controls being displayed + in the frontend (only works in the notebook). + + Parameters + ---------- + data : numpy array, list, unicode, str or bytes + Can be one of + + * Numpy 1d array containing the desired waveform (mono) + * Numpy 2d array containing waveforms for each channel. + Shape=(NCHAN, NSAMPLES). For the standard channel order, see + http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx + * List of float or integer representing the waveform (mono) + * String containing the filename + * Bytestring containing raw PCM data or + * URL pointing to a file on the web. + + If the array option is used, the waveform will be normalized. + + If a filename or url is used, the format support will be browser + dependent. + url : unicode + A URL to download the data from. + filename : unicode + Path to a local file to load the data from. + embed : boolean + Should the audio data be embedded using a data URI (True) or should + the original source be referenced. Set this to True if you want the + audio to playable later with no internet connection in the notebook. + + Default is `True`, unless the keyword argument `url` is set, then + default value is `False`. + rate : integer + The sampling rate of the raw data. + Only required when data parameter is being used as an array + autoplay : bool + Set to True if the audio should immediately start playing. + Default is `False`. + normalize : bool + Whether audio should be normalized (rescaled) to the maximum possible + range. Default is `True`. When set to `False`, `data` must be between + -1 and 1 (inclusive), otherwise an error is raised. + Applies only when `data` is a list or array of samples; other types of + audio are never normalized. + + Examples + -------- + :: + + # Generate a sound + import numpy as np + framerate = 44100 + t = np.linspace(0,5,framerate*5) + data = np.sin(2*np.pi*220*t) + np.sin(2*np.pi*224*t) + Audio(data,rate=framerate) + + # Can also do stereo or more channels + dataleft = np.sin(2*np.pi*220*t) + dataright = np.sin(2*np.pi*224*t) + Audio([dataleft, dataright],rate=framerate) + + Audio("http://www.nch.com.au/acm/8k16bitpcm.wav") # From URL + Audio(url="http://www.w3schools.com/html/horse.ogg") + + Audio('/path/to/sound.wav') # From file + Audio(filename='/path/to/sound.ogg') + + Audio(b'RAW_WAV_DATA..) # From bytes + Audio(data=b'RAW_WAV_DATA..) + + See Also + -------- + + See also the ``Audio`` widgets form the ``ipywidget`` package for more flexibility and options. + + """ + _read_flags = 'rb' + + def __init__(self, data=None, filename=None, url=None, embed=None, rate=None, autoplay=False, normalize=True, *, + element_id=None): + if filename is None and url is None and data is None: + raise ValueError("No audio data found. Expecting filename, url, or data.") + if embed is False and url is None: + raise ValueError("No url found. Expecting url when embed=False") + + if url is not None and embed is not True: + self.embed = False + else: + self.embed = True + self.autoplay = autoplay + self.element_id = element_id + super(Audio, self).__init__(data=data, url=url, filename=filename) + + if self.data is not None and not isinstance(self.data, bytes): + if rate is None: + raise ValueError("rate must be specified when data is a numpy array or list of audio samples.") + self.data = Audio._make_wav(data, rate, normalize) + + def reload(self): + """Reload the raw data from file or URL.""" + import mimetypes + if self.embed: + super(Audio, self).reload() + + if self.filename is not None: + self.mimetype = mimetypes.guess_type(self.filename)[0] + elif self.url is not None: + self.mimetype = mimetypes.guess_type(self.url)[0] + else: + self.mimetype = "audio/wav" + + @staticmethod + def _make_wav(data, rate, normalize): + """ Transform a numpy array to a PCM bytestring """ + from io import BytesIO + import wave + + try: + scaled, nchan = Audio._validate_and_normalize_with_numpy(data, normalize) + except ImportError: + scaled, nchan = Audio._validate_and_normalize_without_numpy(data, normalize) + + fp = BytesIO() + waveobj = wave.open(fp,mode='wb') + waveobj.setnchannels(nchan) + waveobj.setframerate(rate) + waveobj.setsampwidth(2) + waveobj.setcomptype('NONE','NONE') + waveobj.writeframes(scaled) + val = fp.getvalue() + waveobj.close() + + return val + + @staticmethod + def _validate_and_normalize_with_numpy(data, normalize): + import numpy as np + + data = np.array(data, dtype=float) + if len(data.shape) == 1: + nchan = 1 + elif len(data.shape) == 2: + # In wave files,channels are interleaved. E.g., + # "L1R1L2R2..." for stereo. See + # http://msdn.microsoft.com/en-us/library/windows/hardware/dn653308(v=vs.85).aspx + # for channel ordering + nchan = data.shape[0] + data = data.T.ravel() + else: + raise ValueError('Array audio input must be a 1D or 2D array') + + max_abs_value = np.max(np.abs(data)) + normalization_factor = Audio._get_normalization_factor(max_abs_value, normalize) + scaled = data / normalization_factor * 32767 + return scaled.astype(' 1: + raise ValueError('Audio data must be between -1 and 1 when normalize=False.') + return max_abs_value if normalize else 1 + + def _data_and_metadata(self): + """shortcut for returning metadata with url information, if defined""" + md = {} + if self.url: + md['url'] = self.url + if md: + return self.data, md + else: + return self.data + + def _repr_html_(self): + src = """ + + """ + return src.format(src=self.src_attr(), type=self.mimetype, autoplay=self.autoplay_attr(), + element_id=self.element_id_attr()) + + def src_attr(self): + import base64 + if self.embed and (self.data is not None): + data = base64=base64.b64encode(self.data).decode('ascii') + return """data:{type};base64,{base64}""".format(type=self.mimetype, + base64=data) + elif self.url is not None: + return self.url + else: + return "" + + def autoplay_attr(self): + if(self.autoplay): + return 'autoplay="autoplay"' + else: + return '' + + def element_id_attr(self): + if (self.element_id): + return 'id="{element_id}"'.format(element_id=self.element_id) + else: + return '' + +class IFrame(object): + """ + Generic class to embed an iframe in an IPython notebook + """ + + iframe = """ + + """ + + def __init__(self, src, width, height, **kwargs): + self.src = src + self.width = width + self.height = height + self.params = kwargs + + def _repr_html_(self): + """return the embed iframe""" + if self.params: + try: + from urllib.parse import urlencode # Py 3 + except ImportError: + from urllib import urlencode + params = "?" + urlencode(self.params) + else: + params = "" + return self.iframe.format(src=self.src, + width=self.width, + height=self.height, + params=params) + +class YouTubeVideo(IFrame): + """Class for embedding a YouTube Video in an IPython session, based on its video id. + + e.g. to embed the video from https://www.youtube.com/watch?v=foo , you would + do:: + + vid = YouTubeVideo("foo") + display(vid) + + To start from 30 seconds:: + + vid = YouTubeVideo("abc", start=30) + display(vid) + + To calculate seconds from time as hours, minutes, seconds use + :class:`datetime.timedelta`:: + + start=int(timedelta(hours=1, minutes=46, seconds=40).total_seconds()) + + Other parameters can be provided as documented at + https://developers.google.com/youtube/player_parameters#Parameters + + When converting the notebook using nbconvert, a jpeg representation of the video + will be inserted in the document. + """ + + def __init__(self, id, width=400, height=300, **kwargs): + self.id=id + src = "https://www.youtube.com/embed/{0}".format(id) + super(YouTubeVideo, self).__init__(src, width, height, **kwargs) + + def _repr_jpeg_(self): + # Deferred import + from urllib.request import urlopen + + try: + return urlopen("https://img.youtube.com/vi/{id}/hqdefault.jpg".format(id=self.id)).read() + except IOError: + return None + +class VimeoVideo(IFrame): + """ + Class for embedding a Vimeo video in an IPython session, based on its video id. + """ + + def __init__(self, id, width=400, height=300, **kwargs): + src="https://player.vimeo.com/video/{0}".format(id) + super(VimeoVideo, self).__init__(src, width, height, **kwargs) + +class ScribdDocument(IFrame): + """ + Class for embedding a Scribd document in an IPython session + + Use the start_page params to specify a starting point in the document + Use the view_mode params to specify display type one off scroll | slideshow | book + + e.g to Display Wes' foundational paper about PANDAS in book mode from page 3 + + ScribdDocument(71048089, width=800, height=400, start_page=3, view_mode="book") + """ + + def __init__(self, id, width=400, height=300, **kwargs): + src="https://www.scribd.com/embeds/{0}/content".format(id) + super(ScribdDocument, self).__init__(src, width, height, **kwargs) + +class FileLink(object): + """Class for embedding a local file link in an IPython session, based on path + + e.g. to embed a link that was generated in the IPython notebook as my/data.txt + + you would do:: + + local_file = FileLink("my/data.txt") + display(local_file) + + or in the HTML notebook, just:: + + FileLink("my/data.txt") + """ + + html_link_str = "%s" + + def __init__(self, + path, + url_prefix='', + result_html_prefix='', + result_html_suffix='
'): + """ + Parameters + ---------- + path : str + path to the file or directory that should be formatted + url_prefix : str + prefix to be prepended to all files to form a working link [default: + ''] + result_html_prefix : str + text to append to beginning to link [default: ''] + result_html_suffix : str + text to append at the end of link [default: '
'] + """ + if isdir(path): + raise ValueError("Cannot display a directory using FileLink. " + "Use FileLinks to display '%s'." % path) + self.path = fsdecode(path) + self.url_prefix = url_prefix + self.result_html_prefix = result_html_prefix + self.result_html_suffix = result_html_suffix + + def _format_path(self): + fp = ''.join([self.url_prefix, html_escape(self.path)]) + return ''.join([self.result_html_prefix, + self.html_link_str % \ + (fp, html_escape(self.path, quote=False)), + self.result_html_suffix]) + + def _repr_html_(self): + """return html link to file + """ + if not exists(self.path): + return ("Path (%s) doesn't exist. " + "It may still be in the process of " + "being generated, or you may have the " + "incorrect path." % self.path) + + return self._format_path() + + def __repr__(self): + """return absolute path to file + """ + return abspath(self.path) + +class FileLinks(FileLink): + """Class for embedding local file links in an IPython session, based on path + + e.g. to embed links to files that were generated in the IPython notebook + under ``my/data``, you would do:: + + local_files = FileLinks("my/data") + display(local_files) + + or in the HTML notebook, just:: + + FileLinks("my/data") + """ + def __init__(self, + path, + url_prefix='', + included_suffixes=None, + result_html_prefix='', + result_html_suffix='
', + notebook_display_formatter=None, + terminal_display_formatter=None, + recursive=True): + """ + See :class:`FileLink` for the ``path``, ``url_prefix``, + ``result_html_prefix`` and ``result_html_suffix`` parameters. + + included_suffixes : list + Filename suffixes to include when formatting output [default: include + all files] + + notebook_display_formatter : function + Used to format links for display in the notebook. See discussion of + formatter functions below. + + terminal_display_formatter : function + Used to format links for display in the terminal. See discussion of + formatter functions below. + + Formatter functions must be of the form:: + + f(dirname, fnames, included_suffixes) + + dirname : str + The name of a directory + fnames : list + The files in that directory + included_suffixes : list + The file suffixes that should be included in the output (passing None + meansto include all suffixes in the output in the built-in formatters) + recursive : boolean + Whether to recurse into subdirectories. Default is True. + + The function should return a list of lines that will be printed in the + notebook (if passing notebook_display_formatter) or the terminal (if + passing terminal_display_formatter). This function is iterated over for + each directory in self.path. Default formatters are in place, can be + passed here to support alternative formatting. + + """ + if isfile(path): + raise ValueError("Cannot display a file using FileLinks. " + "Use FileLink to display '%s'." % path) + self.included_suffixes = included_suffixes + # remove trailing slashes for more consistent output formatting + path = path.rstrip('/') + + self.path = path + self.url_prefix = url_prefix + self.result_html_prefix = result_html_prefix + self.result_html_suffix = result_html_suffix + + self.notebook_display_formatter = \ + notebook_display_formatter or self._get_notebook_display_formatter() + self.terminal_display_formatter = \ + terminal_display_formatter or self._get_terminal_display_formatter() + + self.recursive = recursive + + def _get_display_formatter(self, + dirname_output_format, + fname_output_format, + fp_format, + fp_cleaner=None): + """ generate built-in formatter function + + this is used to define both the notebook and terminal built-in + formatters as they only differ by some wrapper text for each entry + + dirname_output_format: string to use for formatting directory + names, dirname will be substituted for a single "%s" which + must appear in this string + fname_output_format: string to use for formatting file names, + if a single "%s" appears in the string, fname will be substituted + if two "%s" appear in the string, the path to fname will be + substituted for the first and fname will be substituted for the + second + fp_format: string to use for formatting filepaths, must contain + exactly two "%s" and the dirname will be substituted for the first + and fname will be substituted for the second + """ + def f(dirname, fnames, included_suffixes=None): + result = [] + # begin by figuring out which filenames, if any, + # are going to be displayed + display_fnames = [] + for fname in fnames: + if (isfile(join(dirname,fname)) and + (included_suffixes is None or + splitext(fname)[1] in included_suffixes)): + display_fnames.append(fname) + + if len(display_fnames) == 0: + # if there are no filenames to display, don't print anything + # (not even the directory name) + pass + else: + # otherwise print the formatted directory name followed by + # the formatted filenames + dirname_output_line = dirname_output_format % dirname + result.append(dirname_output_line) + for fname in display_fnames: + fp = fp_format % (dirname,fname) + if fp_cleaner is not None: + fp = fp_cleaner(fp) + try: + # output can include both a filepath and a filename... + fname_output_line = fname_output_format % (fp, fname) + except TypeError: + # ... or just a single filepath + fname_output_line = fname_output_format % fname + result.append(fname_output_line) + return result + return f + + def _get_notebook_display_formatter(self, + spacer="  "): + """ generate function to use for notebook formatting + """ + dirname_output_format = \ + self.result_html_prefix + "%s/" + self.result_html_suffix + fname_output_format = \ + self.result_html_prefix + spacer + self.html_link_str + self.result_html_suffix + fp_format = self.url_prefix + '%s/%s' + if sep == "\\": + # Working on a platform where the path separator is "\", so + # must convert these to "/" for generating a URI + def fp_cleaner(fp): + # Replace all occurrences of backslash ("\") with a forward + # slash ("/") - this is necessary on windows when a path is + # provided as input, but we must link to a URI + return fp.replace('\\','/') + else: + fp_cleaner = None + + return self._get_display_formatter(dirname_output_format, + fname_output_format, + fp_format, + fp_cleaner) + + def _get_terminal_display_formatter(self, + spacer=" "): + """ generate function to use for terminal formatting + """ + dirname_output_format = "%s/" + fname_output_format = spacer + "%s" + fp_format = '%s/%s' + + return self._get_display_formatter(dirname_output_format, + fname_output_format, + fp_format) + + def _format_path(self): + result_lines = [] + if self.recursive: + walked_dir = list(walk(self.path)) + else: + walked_dir = [next(walk(self.path))] + walked_dir.sort() + for dirname, subdirs, fnames in walked_dir: + result_lines += self.notebook_display_formatter(dirname, fnames, self.included_suffixes) + return '\n'.join(result_lines) + + def __repr__(self): + """return newline-separated absolute paths + """ + result_lines = [] + if self.recursive: + walked_dir = list(walk(self.path)) + else: + walked_dir = [next(walk(self.path))] + walked_dir.sort() + for dirname, subdirs, fnames in walked_dir: + result_lines += self.terminal_display_formatter(dirname, fnames, self.included_suffixes) + return '\n'.join(result_lines) + + +class Code(TextDisplayObject): + """Display syntax-highlighted source code. + + This uses Pygments to highlight the code for HTML and Latex output. + + Parameters + ---------- + data : str + The code as a string + url : str + A URL to fetch the code from + filename : str + A local filename to load the code from + language : str + The short name of a Pygments lexer to use for highlighting. + If not specified, it will guess the lexer based on the filename + or the code. Available lexers: http://pygments.org/docs/lexers/ + """ + def __init__(self, data=None, url=None, filename=None, language=None): + self.language = language + super().__init__(data=data, url=url, filename=filename) + + def _get_lexer(self): + if self.language: + from pygments.lexers import get_lexer_by_name + return get_lexer_by_name(self.language) + elif self.filename: + from pygments.lexers import get_lexer_for_filename + return get_lexer_for_filename(self.filename) + else: + from pygments.lexers import guess_lexer + return guess_lexer(self.data) + + def __repr__(self): + return self.data + + def _repr_html_(self): + from pygments import highlight + from pygments.formatters import HtmlFormatter + fmt = HtmlFormatter() + style = ''.format(fmt.get_style_defs('.output_html')) + return style + highlight(self.data, self._get_lexer(), fmt) + + def _repr_latex_(self): + from pygments import highlight + from pygments.formatters import LatexFormatter + return highlight(self.data, self._get_lexer(), LatexFormatter()) diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/editorhooks.py b/.venv/lib/python3.8/site-packages/IPython/lib/editorhooks.py new file mode 100644 index 00000000..7ce0577a --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/editorhooks.py @@ -0,0 +1,128 @@ +""" 'editor' hooks for common editors that work well with ipython + +They should honor the line number argument, at least. + +Contributions are *very* welcome. +""" + +import os +import pipes +import shlex +import subprocess +import sys + +from IPython import get_ipython +from IPython.core.error import TryNext +from IPython.utils import py3compat + + +def install_editor(template, wait=False): + """Installs the editor that is called by IPython for the %edit magic. + + This overrides the default editor, which is generally set by your EDITOR + environment variable or is notepad (windows) or vi (linux). By supplying a + template string `run_template`, you can control how the editor is invoked + by IPython -- (e.g. the format in which it accepts command line options) + + Parameters + ---------- + template : basestring + run_template acts as a template for how your editor is invoked by + the shell. It should contain '{filename}', which will be replaced on + invocation with the file name, and '{line}', $line by line number + (or 0) to invoke the file with. + wait : bool + If `wait` is true, wait until the user presses enter before returning, + to facilitate non-blocking editors that exit immediately after + the call. + """ + + # not all editors support $line, so we'll leave out this check + # for substitution in ['$file', '$line']: + # if not substitution in run_template: + # raise ValueError(('run_template should contain %s' + # ' for string substitution. You supplied "%s"' % (substitution, + # run_template))) + + def call_editor(self, filename, line=0): + if line is None: + line = 0 + cmd = template.format(filename=pipes.quote(filename), line=line) + print(">", cmd) + # pipes.quote doesn't work right on Windows, but it does after splitting + if sys.platform.startswith('win'): + cmd = shlex.split(cmd) + proc = subprocess.Popen(cmd, shell=True) + if proc.wait() != 0: + raise TryNext() + if wait: + py3compat.input("Press Enter when done editing:") + + get_ipython().set_hook('editor', call_editor) + get_ipython().editor = template + + +# in these, exe is always the path/name of the executable. Useful +# if you don't have the editor directory in your path +def komodo(exe=u'komodo'): + """ Activestate Komodo [Edit] """ + install_editor(exe + u' -l {line} {filename}', wait=True) + + +def scite(exe=u"scite"): + """ SciTE or Sc1 """ + install_editor(exe + u' {filename} -goto:{line}') + + +def notepadplusplus(exe=u'notepad++'): + """ Notepad++ http://notepad-plus.sourceforge.net """ + install_editor(exe + u' -n{line} {filename}') + + +def jed(exe=u'jed'): + """ JED, the lightweight emacsish editor """ + install_editor(exe + u' +{line} {filename}') + + +def idle(exe=u'idle'): + """ Idle, the editor bundled with python + + Parameters + ---------- + exe : str, None + If none, should be pretty smart about finding the executable. + """ + if exe is None: + import idlelib + p = os.path.dirname(idlelib.__filename__) + # i'm not sure if this actually works. Is this idle.py script + # guaranteed to be executable? + exe = os.path.join(p, 'idle.py') + install_editor(exe + u' {filename}') + + +def mate(exe=u'mate'): + """ TextMate, the missing editor""" + # wait=True is not required since we're using the -w flag to mate + install_editor(exe + u' -w -l {line} {filename}') + + +# ########################################## +# these are untested, report any problems +# ########################################## + + +def emacs(exe=u'emacs'): + install_editor(exe + u' +{line} {filename}') + + +def gnuclient(exe=u'gnuclient'): + install_editor(exe + u' -nw +{line} {filename}') + + +def crimson_editor(exe=u'cedt.exe'): + install_editor(exe + u' /L:{line} {filename}') + + +def kate(exe=u'kate'): + install_editor(exe + u' -u -l {line} {filename}') diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/guisupport.py b/.venv/lib/python3.8/site-packages/IPython/lib/guisupport.py new file mode 100644 index 00000000..cfd325e9 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/guisupport.py @@ -0,0 +1,155 @@ +# coding: utf-8 +""" +Support for creating GUI apps and starting event loops. + +IPython's GUI integration allows interactive plotting and GUI usage in IPython +session. IPython has two different types of GUI integration: + +1. The terminal based IPython supports GUI event loops through Python's + PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically + whenever raw_input is waiting for a user to type code. We implement GUI + support in the terminal by setting PyOS_InputHook to a function that + iterates the event loop for a short while. It is important to note that + in this situation, the real GUI event loop is NOT run in the normal + manner, so you can't use the normal means to detect that it is running. +2. In the two process IPython kernel/frontend, the GUI event loop is run in + the kernel. In this case, the event loop is run in the normal manner by + calling the function or method of the GUI toolkit that starts the event + loop. + +In addition to starting the GUI event loops in one of these two ways, IPython +will *always* create an appropriate GUI application object when GUi +integration is enabled. + +If you want your GUI apps to run in IPython you need to do two things: + +1. Test to see if there is already an existing main application object. If + there is, you should use it. If there is not an existing application object + you should create one. +2. Test to see if the GUI event loop is running. If it is, you should not + start it. If the event loop is not running you may start it. + +This module contains functions for each toolkit that perform these things +in a consistent manner. Because of how PyOS_InputHook runs the event loop +you cannot detect if the event loop is running using the traditional calls +(such as ``wx.GetApp.IsMainLoopRunning()`` in wxPython). If PyOS_InputHook is +set These methods will return a false negative. That is, they will say the +event loop is not running, when is actually is. To work around this limitation +we proposed the following informal protocol: + +* Whenever someone starts the event loop, they *must* set the ``_in_event_loop`` + attribute of the main application object to ``True``. This should be done + regardless of how the event loop is actually run. +* Whenever someone stops the event loop, they *must* set the ``_in_event_loop`` + attribute of the main application object to ``False``. +* If you want to see if the event loop is running, you *must* use ``hasattr`` + to see if ``_in_event_loop`` attribute has been set. If it is set, you + *must* use its value. If it has not been set, you can query the toolkit + in the normal manner. +* If you want GUI support and no one else has created an application or + started the event loop you *must* do this. We don't want projects to + attempt to defer these things to someone else if they themselves need it. + +The functions below implement this logic for each GUI toolkit. If you need +to create custom application subclasses, you will likely have to modify this +code for your own purposes. This code can be copied into your own project +so you don't have to depend on IPython. + +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +from IPython.core.getipython import get_ipython + +#----------------------------------------------------------------------------- +# wx +#----------------------------------------------------------------------------- + +def get_app_wx(*args, **kwargs): + """Create a new wx app or return an exiting one.""" + import wx + app = wx.GetApp() + if app is None: + if 'redirect' not in kwargs: + kwargs['redirect'] = False + app = wx.PySimpleApp(*args, **kwargs) + return app + +def is_event_loop_running_wx(app=None): + """Is the wx event loop running.""" + # New way: check attribute on shell instance + ip = get_ipython() + if ip is not None: + if ip.active_eventloop and ip.active_eventloop == 'wx': + return True + # Fall through to checking the application, because Wx has a native way + # to check if the event loop is running, unlike Qt. + + # Old way: check Wx application + if app is None: + app = get_app_wx() + if hasattr(app, '_in_event_loop'): + return app._in_event_loop + else: + return app.IsMainLoopRunning() + +def start_event_loop_wx(app=None): + """Start the wx event loop in a consistent manner.""" + if app is None: + app = get_app_wx() + if not is_event_loop_running_wx(app): + app._in_event_loop = True + app.MainLoop() + app._in_event_loop = False + else: + app._in_event_loop = True + +#----------------------------------------------------------------------------- +# qt4 +#----------------------------------------------------------------------------- + +def get_app_qt4(*args, **kwargs): + """Create a new qt4 app or return an existing one.""" + from IPython.external.qt_for_kernel import QtGui + app = QtGui.QApplication.instance() + if app is None: + if not args: + args = ([''],) + app = QtGui.QApplication(*args, **kwargs) + return app + +def is_event_loop_running_qt4(app=None): + """Is the qt4 event loop running.""" + # New way: check attribute on shell instance + ip = get_ipython() + if ip is not None: + return ip.active_eventloop and ip.active_eventloop.startswith('qt') + + # Old way: check attribute on QApplication singleton + if app is None: + app = get_app_qt4(['']) + if hasattr(app, '_in_event_loop'): + return app._in_event_loop + else: + # Does qt4 provide a other way to detect this? + return False + +def start_event_loop_qt4(app=None): + """Start the qt4 event loop in a consistent manner.""" + if app is None: + app = get_app_qt4(['']) + if not is_event_loop_running_qt4(app): + app._in_event_loop = True + app.exec_() + app._in_event_loop = False + else: + app._in_event_loop = True + +#----------------------------------------------------------------------------- +# Tk +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# gtk +#----------------------------------------------------------------------------- diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/inputhook.py b/.venv/lib/python3.8/site-packages/IPython/lib/inputhook.py new file mode 100644 index 00000000..e6e8f2db --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/inputhook.py @@ -0,0 +1,666 @@ +# coding: utf-8 +""" +Deprecated since IPython 5.0 + +Inputhook management for GUI event loop integration. +""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +try: + import ctypes +except ImportError: + ctypes = None +except SystemError: # IronPython issue, 2/8/2014 + ctypes = None +import os +import platform +import sys +from distutils.version import LooseVersion as V + +from warnings import warn + + +warn("`IPython.lib.inputhook` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + + +#----------------------------------------------------------------------------- +# Constants +#----------------------------------------------------------------------------- + +# Constants for identifying the GUI toolkits. +GUI_WX = 'wx' +GUI_QT = 'qt' +GUI_QT4 = 'qt4' +GUI_GTK = 'gtk' +GUI_TK = 'tk' +GUI_OSX = 'osx' +GUI_GLUT = 'glut' +GUI_PYGLET = 'pyglet' +GUI_GTK3 = 'gtk3' +GUI_NONE = 'none' # i.e. disable + +#----------------------------------------------------------------------------- +# Utilities +#----------------------------------------------------------------------------- + +def _stdin_ready_posix(): + """Return True if there's something to read on stdin (posix version).""" + infds, outfds, erfds = select.select([sys.stdin],[],[],0) + return bool(infds) + +def _stdin_ready_nt(): + """Return True if there's something to read on stdin (nt version).""" + return msvcrt.kbhit() + +def _stdin_ready_other(): + """Return True, assuming there's something to read on stdin.""" + return True + +def _use_appnope(): + """Should we use appnope for dealing with OS X app nap? + + Checks if we are on OS X 10.9 or greater. + """ + return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9') + +def _ignore_CTRL_C_posix(): + """Ignore CTRL+C (SIGINT).""" + signal.signal(signal.SIGINT, signal.SIG_IGN) + +def _allow_CTRL_C_posix(): + """Take CTRL+C into account (SIGINT).""" + signal.signal(signal.SIGINT, signal.default_int_handler) + +def _ignore_CTRL_C_other(): + """Ignore CTRL+C (not implemented).""" + pass + +def _allow_CTRL_C_other(): + """Take CTRL+C into account (not implemented).""" + pass + +if os.name == 'posix': + import select + import signal + stdin_ready = _stdin_ready_posix + ignore_CTRL_C = _ignore_CTRL_C_posix + allow_CTRL_C = _allow_CTRL_C_posix +elif os.name == 'nt': + import msvcrt + stdin_ready = _stdin_ready_nt + ignore_CTRL_C = _ignore_CTRL_C_other + allow_CTRL_C = _allow_CTRL_C_other +else: + stdin_ready = _stdin_ready_other + ignore_CTRL_C = _ignore_CTRL_C_other + allow_CTRL_C = _allow_CTRL_C_other + + +#----------------------------------------------------------------------------- +# Main InputHookManager class +#----------------------------------------------------------------------------- + + +class InputHookManager(object): + """DEPRECATED since IPython 5.0 + + Manage PyOS_InputHook for different GUI toolkits. + + This class installs various hooks under ``PyOSInputHook`` to handle + GUI event loop integration. + """ + + def __init__(self): + if ctypes is None: + warn("IPython GUI event loop requires ctypes, %gui will not be available") + else: + self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int) + self.guihooks = {} + self.aliases = {} + self.apps = {} + self._reset() + + def _reset(self): + self._callback_pyfunctype = None + self._callback = None + self._installed = False + self._current_gui = None + + def get_pyos_inputhook(self): + """DEPRECATED since IPython 5.0 + + Return the current PyOS_InputHook as a ctypes.c_void_p.""" + warn("`get_pyos_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook") + + def get_pyos_inputhook_as_func(self): + """DEPRECATED since IPython 5.0 + + Return the current PyOS_InputHook as a ctypes.PYFUNCYPE.""" + warn("`get_pyos_inputhook_as_func` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook") + + def set_inputhook(self, callback): + """DEPRECATED since IPython 5.0 + + Set PyOS_InputHook to callback and return the previous one.""" + # On platforms with 'readline' support, it's all too likely to + # have a KeyboardInterrupt signal delivered *even before* an + # initial ``try:`` clause in the callback can be executed, so + # we need to disable CTRL+C in this situation. + ignore_CTRL_C() + self._callback = callback + self._callback_pyfunctype = self.PYFUNC(callback) + pyos_inputhook_ptr = self.get_pyos_inputhook() + original = self.get_pyos_inputhook_as_func() + pyos_inputhook_ptr.value = \ + ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value + self._installed = True + return original + + def clear_inputhook(self, app=None): + """DEPRECATED since IPython 5.0 + + Set PyOS_InputHook to NULL and return the previous one. + + Parameters + ---------- + app : optional, ignored + This parameter is allowed only so that clear_inputhook() can be + called with a similar interface as all the ``enable_*`` methods. But + the actual value of the parameter is ignored. This uniform interface + makes it easier to have user-level entry points in the main IPython + app like :meth:`enable_gui`.""" + warn("`clear_inputhook` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + pyos_inputhook_ptr = self.get_pyos_inputhook() + original = self.get_pyos_inputhook_as_func() + pyos_inputhook_ptr.value = ctypes.c_void_p(None).value + allow_CTRL_C() + self._reset() + return original + + def clear_app_refs(self, gui=None): + """DEPRECATED since IPython 5.0 + + Clear IPython's internal reference to an application instance. + + Whenever we create an app for a user on qt4 or wx, we hold a + reference to the app. This is needed because in some cases bad things + can happen if a user doesn't hold a reference themselves. This + method is provided to clear the references we are holding. + + Parameters + ---------- + gui : None or str + If None, clear all app references. If ('wx', 'qt4') clear + the app for that toolkit. References are not held for gtk or tk + as those toolkits don't have the notion of an app. + """ + warn("`clear_app_refs` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + if gui is None: + self.apps = {} + elif gui in self.apps: + del self.apps[gui] + + def register(self, toolkitname, *aliases): + """DEPRECATED since IPython 5.0 + + Register a class to provide the event loop for a given GUI. + + This is intended to be used as a class decorator. It should be passed + the names with which to register this GUI integration. The classes + themselves should subclass :class:`InputHookBase`. + + :: + + @inputhook_manager.register('qt') + class QtInputHook(InputHookBase): + def enable(self, app=None): + ... + """ + warn("`register` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + def decorator(cls): + if ctypes is not None: + inst = cls(self) + self.guihooks[toolkitname] = inst + for a in aliases: + self.aliases[a] = toolkitname + return cls + return decorator + + def current_gui(self): + """DEPRECATED since IPython 5.0 + + Return a string indicating the currently active GUI or None.""" + warn("`current_gui` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + return self._current_gui + + def enable_gui(self, gui=None, app=None): + """DEPRECATED since IPython 5.0 + + Switch amongst GUI input hooks by name. + + This is a higher level method than :meth:`set_inputhook` - it uses the + GUI name to look up a registered object which enables the input hook + for that GUI. + + Parameters + ---------- + gui : optional, string or None + If None (or 'none'), clears input hook, otherwise it must be one + of the recognized GUI names (see ``GUI_*`` constants in module). + + app : optional, existing application object. + For toolkits that have the concept of a global app, you can supply an + existing one. If not given, the toolkit will be probed for one, and if + none is found, a new one will be created. Note that GTK does not have + this concept, and passing an app if ``gui=="GTK"`` will raise an error. + + Returns + ------- + The output of the underlying gui switch routine, typically the actual + PyOS_InputHook wrapper object or the GUI toolkit app created, if there was + one. + """ + warn("`enable_gui` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + if gui in (None, GUI_NONE): + return self.disable_gui() + + if gui in self.aliases: + return self.enable_gui(self.aliases[gui], app) + + try: + gui_hook = self.guihooks[gui] + except KeyError: + e = "Invalid GUI request {!r}, valid ones are: {}" + raise ValueError(e.format(gui, ', '.join(self.guihooks))) + self._current_gui = gui + + app = gui_hook.enable(app) + if app is not None: + app._in_event_loop = True + self.apps[gui] = app + return app + + def disable_gui(self): + """DEPRECATED since IPython 5.0 + + Disable GUI event loop integration. + + If an application was registered, this sets its ``_in_event_loop`` + attribute to False. It then calls :meth:`clear_inputhook`. + """ + warn("`disable_gui` is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + gui = self._current_gui + if gui in self.apps: + self.apps[gui]._in_event_loop = False + return self.clear_inputhook() + +class InputHookBase(object): + """DEPRECATED since IPython 5.0 + + Base class for input hooks for specific toolkits. + + Subclasses should define an :meth:`enable` method with one argument, ``app``, + which will either be an instance of the toolkit's application class, or None. + They may also define a :meth:`disable` method with no arguments. + """ + def __init__(self, manager): + self.manager = manager + + def disable(self): + pass + +inputhook_manager = InputHookManager() + +@inputhook_manager.register('osx') +class NullInputHook(InputHookBase): + """DEPRECATED since IPython 5.0 + + A null inputhook that doesn't need to do anything""" + def enable(self, app=None): + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + +@inputhook_manager.register('wx') +class WxInputHook(InputHookBase): + def enable(self, app=None): + """DEPRECATED since IPython 5.0 + + Enable event loop integration with wxPython. + + Parameters + ---------- + app : WX Application, optional. + Running application to use. If not given, we probe WX for an + existing application object, and create a new one if none is found. + + Notes + ----- + This methods sets the ``PyOS_InputHook`` for wxPython, which allows + the wxPython to integrate with terminal based applications like + IPython. + + If ``app`` is not given we probe for an existing one, and return it if + found. If no existing app is found, we create an :class:`wx.App` as + follows:: + + import wx + app = wx.App(redirect=False, clearSigInt=False) + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + import wx + + wx_version = V(wx.__version__).version + + if wx_version < [2, 8]: + raise ValueError("requires wxPython >= 2.8, but you have %s" % wx.__version__) + + from IPython.lib.inputhookwx import inputhook_wx + self.manager.set_inputhook(inputhook_wx) + if _use_appnope(): + from appnope import nope + nope() + + import wx + if app is None: + app = wx.GetApp() + if app is None: + app = wx.App(redirect=False, clearSigInt=False) + + return app + + def disable(self): + """DEPRECATED since IPython 5.0 + + Disable event loop integration with wxPython. + + This restores appnapp on OS X + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + if _use_appnope(): + from appnope import nap + nap() + +@inputhook_manager.register('qt', 'qt4') +class Qt4InputHook(InputHookBase): + def enable(self, app=None): + """DEPRECATED since IPython 5.0 + + Enable event loop integration with PyQt4. + + Parameters + ---------- + app : Qt Application, optional. + Running application to use. If not given, we probe Qt for an + existing application object, and create a new one if none is found. + + Notes + ----- + This methods sets the PyOS_InputHook for PyQt4, which allows + the PyQt4 to integrate with terminal based applications like + IPython. + + If ``app`` is not given we probe for an existing one, and return it if + found. If no existing app is found, we create an :class:`QApplication` + as follows:: + + from PyQt4 import QtCore + app = QtGui.QApplication(sys.argv) + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + from IPython.lib.inputhookqt4 import create_inputhook_qt4 + app, inputhook_qt4 = create_inputhook_qt4(self.manager, app) + self.manager.set_inputhook(inputhook_qt4) + if _use_appnope(): + from appnope import nope + nope() + + return app + + def disable_qt4(self): + """DEPRECATED since IPython 5.0 + + Disable event loop integration with PyQt4. + + This restores appnapp on OS X + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + if _use_appnope(): + from appnope import nap + nap() + + +@inputhook_manager.register('qt5') +class Qt5InputHook(Qt4InputHook): + def enable(self, app=None): + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + os.environ['QT_API'] = 'pyqt5' + return Qt4InputHook.enable(self, app) + + +@inputhook_manager.register('gtk') +class GtkInputHook(InputHookBase): + def enable(self, app=None): + """DEPRECATED since IPython 5.0 + + Enable event loop integration with PyGTK. + + Parameters + ---------- + app : ignored + Ignored, it's only a placeholder to keep the call signature of all + gui activation methods consistent, which simplifies the logic of + supporting magics. + + Notes + ----- + This methods sets the PyOS_InputHook for PyGTK, which allows + the PyGTK to integrate with terminal based applications like + IPython. + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + import gtk + try: + gtk.set_interactive(True) + except AttributeError: + # For older versions of gtk, use our own ctypes version + from IPython.lib.inputhookgtk import inputhook_gtk + self.manager.set_inputhook(inputhook_gtk) + + +@inputhook_manager.register('tk') +class TkInputHook(InputHookBase): + def enable(self, app=None): + """DEPRECATED since IPython 5.0 + + Enable event loop integration with Tk. + + Parameters + ---------- + app : toplevel :class:`Tkinter.Tk` widget, optional. + Running toplevel widget to use. If not given, we probe Tk for an + existing one, and create a new one if none is found. + + Notes + ----- + If you have already created a :class:`Tkinter.Tk` object, the only + thing done by this method is to register with the + :class:`InputHookManager`, since creating that object automatically + sets ``PyOS_InputHook``. + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + if app is None: + try: + from tkinter import Tk # Py 3 + except ImportError: + from Tkinter import Tk # Py 2 + app = Tk() + app.withdraw() + self.manager.apps[GUI_TK] = app + return app + + +@inputhook_manager.register('glut') +class GlutInputHook(InputHookBase): + def enable(self, app=None): + """DEPRECATED since IPython 5.0 + + Enable event loop integration with GLUT. + + Parameters + ---------- + + app : ignored + Ignored, it's only a placeholder to keep the call signature of all + gui activation methods consistent, which simplifies the logic of + supporting magics. + + Notes + ----- + + This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to + integrate with terminal based applications like IPython. Due to GLUT + limitations, it is currently not possible to start the event loop + without first creating a window. You should thus not create another + window but use instead the created one. See 'gui-glut.py' in the + docs/examples/lib directory. + + The default screen mode is set to: + glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + + import OpenGL.GLUT as glut + from IPython.lib.inputhookglut import glut_display_mode, \ + glut_close, glut_display, \ + glut_idle, inputhook_glut + + if GUI_GLUT not in self.manager.apps: + glut.glutInit( sys.argv ) + glut.glutInitDisplayMode( glut_display_mode ) + # This is specific to freeglut + if bool(glut.glutSetOption): + glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE, + glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS ) + glut.glutCreateWindow( sys.argv[0] ) + glut.glutReshapeWindow( 1, 1 ) + glut.glutHideWindow( ) + glut.glutWMCloseFunc( glut_close ) + glut.glutDisplayFunc( glut_display ) + glut.glutIdleFunc( glut_idle ) + else: + glut.glutWMCloseFunc( glut_close ) + glut.glutDisplayFunc( glut_display ) + glut.glutIdleFunc( glut_idle) + self.manager.set_inputhook( inputhook_glut ) + + + def disable(self): + """DEPRECATED since IPython 5.0 + + Disable event loop integration with glut. + + This sets PyOS_InputHook to NULL and set the display function to a + dummy one and set the timer to a dummy timer that will be triggered + very far in the future. + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + import OpenGL.GLUT as glut + from glut_support import glutMainLoopEvent + + glut.glutHideWindow() # This is an event to be processed below + glutMainLoopEvent() + super(GlutInputHook, self).disable() + +@inputhook_manager.register('pyglet') +class PygletInputHook(InputHookBase): + def enable(self, app=None): + """DEPRECATED since IPython 5.0 + + Enable event loop integration with pyglet. + + Parameters + ---------- + app : ignored + Ignored, it's only a placeholder to keep the call signature of all + gui activation methods consistent, which simplifies the logic of + supporting magics. + + Notes + ----- + This methods sets the ``PyOS_InputHook`` for pyglet, which allows + pyglet to integrate with terminal based applications like + IPython. + + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + from IPython.lib.inputhookpyglet import inputhook_pyglet + self.manager.set_inputhook(inputhook_pyglet) + return app + + +@inputhook_manager.register('gtk3') +class Gtk3InputHook(InputHookBase): + def enable(self, app=None): + """DEPRECATED since IPython 5.0 + + Enable event loop integration with Gtk3 (gir bindings). + + Parameters + ---------- + app : ignored + Ignored, it's only a placeholder to keep the call signature of all + gui activation methods consistent, which simplifies the logic of + supporting magics. + + Notes + ----- + This methods sets the PyOS_InputHook for Gtk3, which allows + the Gtk3 to integrate with terminal based applications like + IPython. + """ + warn("This function is deprecated since IPython 5.0 and will be removed in future versions.", + DeprecationWarning, stacklevel=2) + from IPython.lib.inputhookgtk3 import inputhook_gtk3 + self.manager.set_inputhook(inputhook_gtk3) + + +clear_inputhook = inputhook_manager.clear_inputhook +set_inputhook = inputhook_manager.set_inputhook +current_gui = inputhook_manager.current_gui +clear_app_refs = inputhook_manager.clear_app_refs +enable_gui = inputhook_manager.enable_gui +disable_gui = inputhook_manager.disable_gui +register = inputhook_manager.register +guis = inputhook_manager.guihooks + + +def _deprecated_disable(): + warn("This function is deprecated since IPython 4.0 use disable_gui() instead", + DeprecationWarning, stacklevel=2) + inputhook_manager.disable_gui() + +disable_wx = disable_qt4 = disable_gtk = disable_gtk3 = disable_glut = \ + disable_pyglet = disable_osx = _deprecated_disable diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/inputhookglut.py b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookglut.py new file mode 100644 index 00000000..e6f7f125 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookglut.py @@ -0,0 +1,172 @@ +# coding: utf-8 +""" +GLUT Inputhook support functions +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +# GLUT is quite an old library and it is difficult to ensure proper +# integration within IPython since original GLUT does not allow to handle +# events one by one. Instead, it requires for the mainloop to be entered +# and never returned (there is not even a function to exit he +# mainloop). Fortunately, there are alternatives such as freeglut +# (available for linux and windows) and the OSX implementation gives +# access to a glutCheckLoop() function that blocks itself until a new +# event is received. This means we have to setup the idle callback to +# ensure we got at least one event that will unblock the function. +# +# Furthermore, it is not possible to install these handlers without a window +# being first created. We choose to make this window invisible. This means that +# display mode options are set at this level and user won't be able to change +# them later without modifying the code. This should probably be made available +# via IPython options system. + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +import os +import sys +import time +import signal +import OpenGL.GLUT as glut +import OpenGL.platform as platform +from timeit import default_timer as clock + +#----------------------------------------------------------------------------- +# Constants +#----------------------------------------------------------------------------- + +# Frame per second : 60 +# Should probably be an IPython option +glut_fps = 60 + + +# Display mode : double buffeed + rgba + depth +# Should probably be an IPython option +glut_display_mode = (glut.GLUT_DOUBLE | + glut.GLUT_RGBA | + glut.GLUT_DEPTH) + +glutMainLoopEvent = None +if sys.platform == 'darwin': + try: + glutCheckLoop = platform.createBaseFunction( + 'glutCheckLoop', dll=platform.GLUT, resultType=None, + argTypes=[], + doc='glutCheckLoop( ) -> None', + argNames=(), + ) + except AttributeError: + raise RuntimeError( + '''Your glut implementation does not allow interactive sessions''' + '''Consider installing freeglut.''') + glutMainLoopEvent = glutCheckLoop +elif glut.HAVE_FREEGLUT: + glutMainLoopEvent = glut.glutMainLoopEvent +else: + raise RuntimeError( + '''Your glut implementation does not allow interactive sessions. ''' + '''Consider installing freeglut.''') + + +#----------------------------------------------------------------------------- +# Platform-dependent imports and functions +#----------------------------------------------------------------------------- + +if os.name == 'posix': + import select + + def stdin_ready(): + infds, outfds, erfds = select.select([sys.stdin],[],[],0) + if infds: + return True + else: + return False + +elif sys.platform == 'win32': + import msvcrt + + def stdin_ready(): + return msvcrt.kbhit() + +#----------------------------------------------------------------------------- +# Callback functions +#----------------------------------------------------------------------------- + +def glut_display(): + # Dummy display function + pass + +def glut_idle(): + # Dummy idle function + pass + +def glut_close(): + # Close function only hides the current window + glut.glutHideWindow() + glutMainLoopEvent() + +def glut_int_handler(signum, frame): + # Catch sigint and print the default message + signal.signal(signal.SIGINT, signal.default_int_handler) + print('\nKeyboardInterrupt') + # Need to reprint the prompt at this stage + + + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- +def inputhook_glut(): + """Run the pyglet event loop by processing pending events only. + + This keeps processing pending events until stdin is ready. After + processing all pending events, a call to time.sleep is inserted. This is + needed, otherwise, CPU usage is at 100%. This sleep time should be tuned + though for best performance. + """ + # We need to protect against a user pressing Control-C when IPython is + # idle and this is running. We trap KeyboardInterrupt and pass. + + signal.signal(signal.SIGINT, glut_int_handler) + + try: + t = clock() + + # Make sure the default window is set after a window has been closed + if glut.glutGetWindow() == 0: + glut.glutSetWindow( 1 ) + glutMainLoopEvent() + return 0 + + while not stdin_ready(): + glutMainLoopEvent() + # We need to sleep at this point to keep the idle CPU load + # low. However, if sleep to long, GUI response is poor. As + # a compromise, we watch how often GUI events are being processed + # and switch between a short and long sleep time. Here are some + # stats useful in helping to tune this. + # time CPU load + # 0.001 13% + # 0.005 3% + # 0.01 1.5% + # 0.05 0.5% + used_time = clock() - t + if used_time > 10.0: + # print 'Sleep for 1 s' # dbg + time.sleep(1.0) + elif used_time > 0.1: + # Few GUI events coming in, so we can sleep longer + # print 'Sleep for 0.05 s' # dbg + time.sleep(0.05) + else: + # Many GUI events coming in, so sleep only very little + time.sleep(0.001) + except KeyboardInterrupt: + pass + return 0 diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/inputhookgtk.py b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookgtk.py new file mode 100644 index 00000000..98569f54 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookgtk.py @@ -0,0 +1,35 @@ +# encoding: utf-8 +""" +Enable pygtk to be used interactively by setting PyOS_InputHook. + +Authors: Brian Granger +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import sys +import gtk, gobject + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + + +def _main_quit(*args, **kwargs): + gtk.main_quit() + return False + +def inputhook_gtk(): + gobject.io_add_watch(sys.stdin, gobject.IO_IN, _main_quit) + gtk.main() + return 0 + diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/inputhookgtk3.py b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookgtk3.py new file mode 100644 index 00000000..b797e862 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookgtk3.py @@ -0,0 +1,34 @@ +# encoding: utf-8 +""" +Enable Gtk3 to be used interactively by IPython. + +Authors: Thomi Richards +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import sys +from gi.repository import Gtk, GLib + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +def _main_quit(*args, **kwargs): + Gtk.main_quit() + return False + + +def inputhook_gtk3(): + GLib.io_add_watch(sys.stdin, GLib.PRIORITY_DEFAULT, GLib.IO_IN, _main_quit) + Gtk.main() + return 0 diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/inputhookpyglet.py b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookpyglet.py new file mode 100644 index 00000000..fb91ffed --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookpyglet.py @@ -0,0 +1,111 @@ +# encoding: utf-8 +""" +Enable pyglet to be used interactively by setting PyOS_InputHook. + +Authors +------- + +* Nicolas P. Rougier +* Fernando Perez +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os +import sys +import time +from timeit import default_timer as clock +import pyglet + +#----------------------------------------------------------------------------- +# Platform-dependent imports and functions +#----------------------------------------------------------------------------- + +if os.name == 'posix': + import select + + def stdin_ready(): + infds, outfds, erfds = select.select([sys.stdin],[],[],0) + if infds: + return True + else: + return False + +elif sys.platform == 'win32': + import msvcrt + + def stdin_ready(): + return msvcrt.kbhit() + + +# On linux only, window.flip() has a bug that causes an AttributeError on +# window close. For details, see: +# http://groups.google.com/group/pyglet-users/browse_thread/thread/47c1aab9aa4a3d23/c22f9e819826799e?#c22f9e819826799e + +if sys.platform.startswith('linux'): + def flip(window): + try: + window.flip() + except AttributeError: + pass +else: + def flip(window): + window.flip() + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +def inputhook_pyglet(): + """Run the pyglet event loop by processing pending events only. + + This keeps processing pending events until stdin is ready. After + processing all pending events, a call to time.sleep is inserted. This is + needed, otherwise, CPU usage is at 100%. This sleep time should be tuned + though for best performance. + """ + # We need to protect against a user pressing Control-C when IPython is + # idle and this is running. We trap KeyboardInterrupt and pass. + try: + t = clock() + while not stdin_ready(): + pyglet.clock.tick() + for window in pyglet.app.windows: + window.switch_to() + window.dispatch_events() + window.dispatch_event('on_draw') + flip(window) + + # We need to sleep at this point to keep the idle CPU load + # low. However, if sleep to long, GUI response is poor. As + # a compromise, we watch how often GUI events are being processed + # and switch between a short and long sleep time. Here are some + # stats useful in helping to tune this. + # time CPU load + # 0.001 13% + # 0.005 3% + # 0.01 1.5% + # 0.05 0.5% + used_time = clock() - t + if used_time > 10.0: + # print 'Sleep for 1 s' # dbg + time.sleep(1.0) + elif used_time > 0.1: + # Few GUI events coming in, so we can sleep longer + # print 'Sleep for 0.05 s' # dbg + time.sleep(0.05) + else: + # Many GUI events coming in, so sleep only very little + time.sleep(0.001) + except KeyboardInterrupt: + pass + return 0 diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/inputhookqt4.py b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookqt4.py new file mode 100644 index 00000000..8a83902f --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookqt4.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +""" +Qt4's inputhook support function + +Author: Christian Boos +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import os +import signal +import threading + +from IPython.core.interactiveshell import InteractiveShell +from IPython.external.qt_for_kernel import QtCore, QtGui +from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready + +#----------------------------------------------------------------------------- +# Module Globals +#----------------------------------------------------------------------------- + +got_kbdint = False +sigint_timer = None + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +def create_inputhook_qt4(mgr, app=None): + """Create an input hook for running the Qt4 application event loop. + + Parameters + ---------- + mgr : an InputHookManager + + app : Qt Application, optional. + Running application to use. If not given, we probe Qt for an + existing application object, and create a new one if none is found. + + Returns + ------- + A pair consisting of a Qt Application (either the one given or the + one found or created) and a inputhook. + + Notes + ----- + We use a custom input hook instead of PyQt4's default one, as it + interacts better with the readline packages (issue #481). + + The inputhook function works in tandem with a 'pre_prompt_hook' + which automatically restores the hook as an inputhook in case the + latter has been temporarily disabled after having intercepted a + KeyboardInterrupt. + """ + + if app is None: + app = QtCore.QCoreApplication.instance() + if app is None: + app = QtGui.QApplication([" "]) + + # Re-use previously created inputhook if any + ip = InteractiveShell.instance() + if hasattr(ip, '_inputhook_qt4'): + return app, ip._inputhook_qt4 + + # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of + # hooks (they both share the got_kbdint flag) + + def inputhook_qt4(): + """PyOS_InputHook python hook for Qt4. + + Process pending Qt events and if there's no pending keyboard + input, spend a short slice of time (50ms) running the Qt event + loop. + + As a Python ctypes callback can't raise an exception, we catch + the KeyboardInterrupt and temporarily deactivate the hook, + which will let a *second* CTRL+C be processed normally and go + back to a clean prompt line. + """ + try: + allow_CTRL_C() + app = QtCore.QCoreApplication.instance() + if not app: # shouldn't happen, but safer if it happens anyway... + return 0 + app.processEvents(QtCore.QEventLoop.AllEvents, 300) + if not stdin_ready(): + # Generally a program would run QCoreApplication::exec() + # from main() to enter and process the Qt event loop until + # quit() or exit() is called and the program terminates. + # + # For our input hook integration, we need to repeatedly + # enter and process the Qt event loop for only a short + # amount of time (say 50ms) to ensure that Python stays + # responsive to other user inputs. + # + # A naive approach would be to repeatedly call + # QCoreApplication::exec(), using a timer to quit after a + # short amount of time. Unfortunately, QCoreApplication + # emits an aboutToQuit signal before stopping, which has + # the undesirable effect of closing all modal windows. + # + # To work around this problem, we instead create a + # QEventLoop and call QEventLoop::exec(). Other than + # setting some state variables which do not seem to be + # used anywhere, the only thing QCoreApplication adds is + # the aboutToQuit signal which is precisely what we are + # trying to avoid. + timer = QtCore.QTimer() + event_loop = QtCore.QEventLoop() + timer.timeout.connect(event_loop.quit) + while not stdin_ready(): + timer.start(50) + event_loop.exec_() + timer.stop() + except KeyboardInterrupt: + global got_kbdint, sigint_timer + + ignore_CTRL_C() + got_kbdint = True + mgr.clear_inputhook() + + # This generates a second SIGINT so the user doesn't have to + # press CTRL+C twice to get a clean prompt. + # + # Since we can't catch the resulting KeyboardInterrupt here + # (because this is a ctypes callback), we use a timer to + # generate the SIGINT after we leave this callback. + # + # Unfortunately this doesn't work on Windows (SIGINT kills + # Python and CTRL_C_EVENT doesn't work). + if(os.name == 'posix'): + pid = os.getpid() + if(not sigint_timer): + sigint_timer = threading.Timer(.01, os.kill, + args=[pid, signal.SIGINT] ) + sigint_timer.start() + else: + print("\nKeyboardInterrupt - Ctrl-C again for new prompt") + + + except: # NO exceptions are allowed to escape from a ctypes callback + ignore_CTRL_C() + from traceback import print_exc + print_exc() + print("Got exception from inputhook_qt4, unregistering.") + mgr.clear_inputhook() + finally: + allow_CTRL_C() + return 0 + + def preprompthook_qt4(ishell): + """'pre_prompt_hook' used to restore the Qt4 input hook + + (in case the latter was temporarily deactivated after a + CTRL+C) + """ + global got_kbdint, sigint_timer + + if(sigint_timer): + sigint_timer.cancel() + sigint_timer = None + + if got_kbdint: + mgr.set_inputhook(inputhook_qt4) + got_kbdint = False + + ip._inputhook_qt4 = inputhook_qt4 + ip.set_hook('pre_prompt_hook', preprompthook_qt4) + + return app, inputhook_qt4 diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/inputhookwx.py b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookwx.py new file mode 100644 index 00000000..60520a29 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/inputhookwx.py @@ -0,0 +1,167 @@ +# encoding: utf-8 + +""" +Enable wxPython to be used interactively by setting PyOS_InputHook. + +Authors: Robin Dunn, Brian Granger, Ondrej Certik +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2011 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import sys +import signal +import time +from timeit import default_timer as clock +import wx + +from IPython.lib.inputhook import stdin_ready + + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +def inputhook_wx1(): + """Run the wx event loop by processing pending events only. + + This approach seems to work, but its performance is not great as it + relies on having PyOS_InputHook called regularly. + """ + try: + app = wx.GetApp() + if app is not None: + assert wx.Thread_IsMain() + + # Make a temporary event loop and process system events until + # there are no more waiting, then allow idle events (which + # will also deal with pending or posted wx events.) + evtloop = wx.EventLoop() + ea = wx.EventLoopActivator(evtloop) + while evtloop.Pending(): + evtloop.Dispatch() + app.ProcessIdle() + del ea + except KeyboardInterrupt: + pass + return 0 + +class EventLoopTimer(wx.Timer): + + def __init__(self, func): + self.func = func + wx.Timer.__init__(self) + + def Notify(self): + self.func() + +class EventLoopRunner(object): + + def Run(self, time): + self.evtloop = wx.EventLoop() + self.timer = EventLoopTimer(self.check_stdin) + self.timer.Start(time) + self.evtloop.Run() + + def check_stdin(self): + if stdin_ready(): + self.timer.Stop() + self.evtloop.Exit() + +def inputhook_wx2(): + """Run the wx event loop, polling for stdin. + + This version runs the wx eventloop for an undetermined amount of time, + during which it periodically checks to see if anything is ready on + stdin. If anything is ready on stdin, the event loop exits. + + The argument to elr.Run controls how often the event loop looks at stdin. + This determines the responsiveness at the keyboard. A setting of 1000 + enables a user to type at most 1 char per second. I have found that a + setting of 10 gives good keyboard response. We can shorten it further, + but eventually performance would suffer from calling select/kbhit too + often. + """ + try: + app = wx.GetApp() + if app is not None: + assert wx.Thread_IsMain() + elr = EventLoopRunner() + # As this time is made shorter, keyboard response improves, but idle + # CPU load goes up. 10 ms seems like a good compromise. + elr.Run(time=10) # CHANGE time here to control polling interval + except KeyboardInterrupt: + pass + return 0 + +def inputhook_wx3(): + """Run the wx event loop by processing pending events only. + + This is like inputhook_wx1, but it keeps processing pending events + until stdin is ready. After processing all pending events, a call to + time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%. + This sleep time should be tuned though for best performance. + """ + # We need to protect against a user pressing Control-C when IPython is + # idle and this is running. We trap KeyboardInterrupt and pass. + try: + app = wx.GetApp() + if app is not None: + assert wx.Thread_IsMain() + + # The import of wx on Linux sets the handler for signal.SIGINT + # to 0. This is a bug in wx or gtk. We fix by just setting it + # back to the Python default. + if not callable(signal.getsignal(signal.SIGINT)): + signal.signal(signal.SIGINT, signal.default_int_handler) + + evtloop = wx.EventLoop() + ea = wx.EventLoopActivator(evtloop) + t = clock() + while not stdin_ready(): + while evtloop.Pending(): + t = clock() + evtloop.Dispatch() + app.ProcessIdle() + # We need to sleep at this point to keep the idle CPU load + # low. However, if sleep to long, GUI response is poor. As + # a compromise, we watch how often GUI events are being processed + # and switch between a short and long sleep time. Here are some + # stats useful in helping to tune this. + # time CPU load + # 0.001 13% + # 0.005 3% + # 0.01 1.5% + # 0.05 0.5% + used_time = clock() - t + if used_time > 10.0: + # print 'Sleep for 1 s' # dbg + time.sleep(1.0) + elif used_time > 0.1: + # Few GUI events coming in, so we can sleep longer + # print 'Sleep for 0.05 s' # dbg + time.sleep(0.05) + else: + # Many GUI events coming in, so sleep only very little + time.sleep(0.001) + del ea + except KeyboardInterrupt: + pass + return 0 + +if sys.platform == 'darwin': + # On OSX, evtloop.Pending() always returns True, regardless of there being + # any events pending. As such we can't use implementations 1 or 3 of the + # inputhook as those depend on a pending/dispatch loop. + inputhook_wx = inputhook_wx2 +else: + # This is our default implementation + inputhook_wx = inputhook_wx3 diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/kernel.py b/.venv/lib/python3.8/site-packages/IPython/lib/kernel.py new file mode 100644 index 00000000..af982766 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/kernel.py @@ -0,0 +1,13 @@ +"""[DEPRECATED] Utilities for connecting to kernels + +Moved to IPython.kernel.connect +""" + +import warnings +warnings.warn("IPython.lib.kernel moved to IPython.kernel.connect in IPython 1.0," + " and will be removed in IPython 6.0.", + DeprecationWarning +) + +from ipykernel.connect import * + diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/latextools.py b/.venv/lib/python3.8/site-packages/IPython/lib/latextools.py new file mode 100644 index 00000000..cbcc7d96 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/latextools.py @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- +"""Tools for handling LaTeX.""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +from io import BytesIO, open +import os +import tempfile +import shutil +import subprocess +from base64 import encodebytes +import textwrap + +from IPython.utils.process import find_cmd, FindCmdError +from traitlets.config import get_config +from traitlets.config.configurable import SingletonConfigurable +from traitlets import List, Bool, Unicode +from IPython.utils.py3compat import cast_unicode + + +class LaTeXTool(SingletonConfigurable): + """An object to store configuration of the LaTeX tool.""" + def _config_default(self): + return get_config() + + backends = List( + Unicode(), ["matplotlib", "dvipng"], + help="Preferred backend to draw LaTeX math equations. " + "Backends in the list are checked one by one and the first " + "usable one is used. Note that `matplotlib` backend " + "is usable only for inline style equations. To draw " + "display style equations, `dvipng` backend must be specified. ", + # It is a List instead of Enum, to make configuration more + # flexible. For example, to use matplotlib mainly but dvipng + # for display style, the default ["matplotlib", "dvipng"] can + # be used. To NOT use dvipng so that other repr such as + # unicode pretty printing is used, you can use ["matplotlib"]. + ).tag(config=True) + + use_breqn = Bool( + True, + help="Use breqn.sty to automatically break long equations. " + "This configuration takes effect only for dvipng backend.", + ).tag(config=True) + + packages = List( + ['amsmath', 'amsthm', 'amssymb', 'bm'], + help="A list of packages to use for dvipng backend. " + "'breqn' will be automatically appended when use_breqn=True.", + ).tag(config=True) + + preamble = Unicode( + help="Additional preamble to use when generating LaTeX source " + "for dvipng backend.", + ).tag(config=True) + + +def latex_to_png(s, encode=False, backend=None, wrap=False, color='Black', + scale=1.0): + """Render a LaTeX string to PNG. + + Parameters + ---------- + s : str + The raw string containing valid inline LaTeX. + encode : bool, optional + Should the PNG data base64 encoded to make it JSON'able. + backend : {matplotlib, dvipng} + Backend for producing PNG data. + wrap : bool + If true, Automatically wrap `s` as a LaTeX equation. + color : string + Foreground color name among dvipsnames, e.g. 'Maroon' or on hex RGB + format, e.g. '#AA20FA'. + scale : float + Scale factor for the resulting PNG. + + None is returned when the backend cannot be used. + + """ + s = cast_unicode(s) + allowed_backends = LaTeXTool.instance().backends + if backend is None: + backend = allowed_backends[0] + if backend not in allowed_backends: + return None + if backend == 'matplotlib': + f = latex_to_png_mpl + elif backend == 'dvipng': + f = latex_to_png_dvipng + if color.startswith('#'): + # Convert hex RGB color to LaTeX RGB color. + if len(color) == 7: + try: + color = "RGB {}".format(" ".join([str(int(x, 16)) for x in + textwrap.wrap(color[1:], 2)])) + except ValueError: + raise ValueError('Invalid color specification {}.'.format(color)) + else: + raise ValueError('Invalid color specification {}.'.format(color)) + else: + raise ValueError('No such backend {0}'.format(backend)) + bin_data = f(s, wrap, color, scale) + if encode and bin_data: + bin_data = encodebytes(bin_data) + return bin_data + + +def latex_to_png_mpl(s, wrap, color='Black', scale=1.0): + try: + from matplotlib import mathtext + from pyparsing import ParseFatalException + except ImportError: + return None + + # mpl mathtext doesn't support display math, force inline + s = s.replace('$$', '$') + if wrap: + s = u'${0}$'.format(s) + + try: + mt = mathtext.MathTextParser('bitmap') + f = BytesIO() + dpi = 120*scale + mt.to_png(f, s, fontsize=12, dpi=dpi, color=color) + return f.getvalue() + except (ValueError, RuntimeError, ParseFatalException): + return None + + +def latex_to_png_dvipng(s, wrap, color='Black', scale=1.0): + try: + find_cmd('latex') + find_cmd('dvipng') + except FindCmdError: + return None + try: + workdir = tempfile.mkdtemp() + tmpfile = os.path.join(workdir, "tmp.tex") + dvifile = os.path.join(workdir, "tmp.dvi") + outfile = os.path.join(workdir, "tmp.png") + + with open(tmpfile, "w", encoding='utf8') as f: + f.writelines(genelatex(s, wrap)) + + with open(os.devnull, 'wb') as devnull: + subprocess.check_call( + ["latex", "-halt-on-error", "-interaction", "batchmode", tmpfile], + cwd=workdir, stdout=devnull, stderr=devnull) + + resolution = round(150*scale) + subprocess.check_call( + ["dvipng", "-T", "tight", "-D", str(resolution), "-z", "9", + "-bg", "transparent", "-o", outfile, dvifile, "-fg", color], + cwd=workdir, stdout=devnull, stderr=devnull) + + with open(outfile, "rb") as f: + return f.read() + except subprocess.CalledProcessError: + return None + finally: + shutil.rmtree(workdir) + + +def kpsewhich(filename): + """Invoke kpsewhich command with an argument `filename`.""" + try: + find_cmd("kpsewhich") + proc = subprocess.Popen( + ["kpsewhich", filename], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = proc.communicate() + return stdout.strip().decode('utf8', 'replace') + except FindCmdError: + pass + + +def genelatex(body, wrap): + """Generate LaTeX document for dvipng backend.""" + lt = LaTeXTool.instance() + breqn = wrap and lt.use_breqn and kpsewhich("breqn.sty") + yield r'\documentclass{article}' + packages = lt.packages + if breqn: + packages = packages + ['breqn'] + for pack in packages: + yield r'\usepackage{{{0}}}'.format(pack) + yield r'\pagestyle{empty}' + if lt.preamble: + yield lt.preamble + yield r'\begin{document}' + if breqn: + yield r'\begin{dmath*}' + yield body + yield r'\end{dmath*}' + elif wrap: + yield u'$${0}$$'.format(body) + else: + yield body + yield u'\\end{document}' + + +_data_uri_template_png = u"""%s""" + +def latex_to_html(s, alt='image'): + """Render LaTeX to HTML with embedded PNG data using data URIs. + + Parameters + ---------- + s : str + The raw string containing valid inline LateX. + alt : str + The alt text to use for the HTML. + """ + base64_data = latex_to_png(s, encode=True).decode('ascii') + if base64_data: + return _data_uri_template_png % (base64_data, alt) + + diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/lexers.py b/.venv/lib/python3.8/site-packages/IPython/lib/lexers.py new file mode 100644 index 00000000..4494da56 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/lexers.py @@ -0,0 +1,532 @@ +# -*- coding: utf-8 -*- +""" +Defines a variety of Pygments lexers for highlighting IPython code. + +This includes: + + IPythonLexer, IPython3Lexer + Lexers for pure IPython (python + magic/shell commands) + + IPythonPartialTracebackLexer, IPythonTracebackLexer + Supports 2.x and 3.x via keyword `python3`. The partial traceback + lexer reads everything but the Python code appearing in a traceback. + The full lexer combines the partial lexer with an IPython lexer. + + IPythonConsoleLexer + A lexer for IPython console sessions, with support for tracebacks. + + IPyLexer + A friendly lexer which examines the first line of text and from it, + decides whether to use an IPython lexer or an IPython console lexer. + This is probably the only lexer that needs to be explicitly added + to Pygments. + +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2013, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +# Standard library +import re + +# Third party +from pygments.lexers import ( + BashLexer, HtmlLexer, JavascriptLexer, RubyLexer, PerlLexer, PythonLexer, + Python3Lexer, TexLexer) +from pygments.lexer import ( + Lexer, DelegatingLexer, RegexLexer, do_insertions, bygroups, using, +) +from pygments.token import ( + Generic, Keyword, Literal, Name, Operator, Other, Text, Error, +) +from pygments.util import get_bool_opt + +# Local + +line_re = re.compile('.*?\n') + +__all__ = ['build_ipy_lexer', 'IPython3Lexer', 'IPythonLexer', + 'IPythonPartialTracebackLexer', 'IPythonTracebackLexer', + 'IPythonConsoleLexer', 'IPyLexer'] + + +def build_ipy_lexer(python3): + """Builds IPython lexers depending on the value of `python3`. + + The lexer inherits from an appropriate Python lexer and then adds + information about IPython specific keywords (i.e. magic commands, + shell commands, etc.) + + Parameters + ---------- + python3 : bool + If `True`, then build an IPython lexer from a Python 3 lexer. + + """ + # It would be nice to have a single IPython lexer class which takes + # a boolean `python3`. But since there are two Python lexer classes, + # we will also have two IPython lexer classes. + if python3: + PyLexer = Python3Lexer + name = 'IPython3' + aliases = ['ipython3'] + doc = """IPython3 Lexer""" + else: + PyLexer = PythonLexer + name = 'IPython' + aliases = ['ipython2', 'ipython'] + doc = """IPython Lexer""" + + ipython_tokens = [ + (r'(?s)(\s*)(%%capture)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r'(?s)(\s*)(%%debug)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r'(?is)(\s*)(%%html)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(HtmlLexer))), + (r'(?s)(\s*)(%%javascript)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(JavascriptLexer))), + (r'(?s)(\s*)(%%js)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(JavascriptLexer))), + (r'(?s)(\s*)(%%latex)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(TexLexer))), + (r'(?s)(\s*)(%%perl)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PerlLexer))), + (r'(?s)(\s*)(%%prun)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r'(?s)(\s*)(%%pypy)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r'(?s)(\s*)(%%python)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r'(?s)(\s*)(%%python2)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PythonLexer))), + (r'(?s)(\s*)(%%python3)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(Python3Lexer))), + (r'(?s)(\s*)(%%ruby)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(RubyLexer))), + (r'(?s)(\s*)(%%time)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r'(?s)(\s*)(%%timeit)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r'(?s)(\s*)(%%writefile)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r'(?s)(\s*)(%%file)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(PyLexer))), + (r"(?s)(\s*)(%%)(\w+)(.*)", bygroups(Text, Operator, Keyword, Text)), + (r'(?s)(^\s*)(%%!)([^\n]*\n)(.*)', bygroups(Text, Operator, Text, using(BashLexer))), + (r"(%%?)(\w+)(\?\??)$", bygroups(Operator, Keyword, Operator)), + (r"\b(\?\??)(\s*)$", bygroups(Operator, Text)), + (r'(%)(sx|sc|system)(.*)(\n)', bygroups(Operator, Keyword, + using(BashLexer), Text)), + (r'(%)(\w+)(.*\n)', bygroups(Operator, Keyword, Text)), + (r'^(!!)(.+)(\n)', bygroups(Operator, using(BashLexer), Text)), + (r'(!)(?!=)(.+)(\n)', bygroups(Operator, using(BashLexer), Text)), + (r'^(\s*)(\?\??)(\s*%{0,2}[\w\.\*]*)', bygroups(Text, Operator, Text)), + (r'(\s*%{0,2}[\w\.\*]*)(\?\??)(\s*)$', bygroups(Text, Operator, Text)), + ] + + tokens = PyLexer.tokens.copy() + tokens['root'] = ipython_tokens + tokens['root'] + + attrs = {'name': name, 'aliases': aliases, 'filenames': [], + '__doc__': doc, 'tokens': tokens} + + return type(name, (PyLexer,), attrs) + + +IPython3Lexer = build_ipy_lexer(python3=True) +IPythonLexer = build_ipy_lexer(python3=False) + + +class IPythonPartialTracebackLexer(RegexLexer): + """ + Partial lexer for IPython tracebacks. + + Handles all the non-python output. + + """ + name = 'IPython Partial Traceback' + + tokens = { + 'root': [ + # Tracebacks for syntax errors have a different style. + # For both types of tracebacks, we mark the first line with + # Generic.Traceback. For syntax errors, we mark the filename + # as we mark the filenames for non-syntax tracebacks. + # + # These two regexps define how IPythonConsoleLexer finds a + # traceback. + # + ## Non-syntax traceback + (r'^(\^C)?(-+\n)', bygroups(Error, Generic.Traceback)), + ## Syntax traceback + (r'^( File)(.*)(, line )(\d+\n)', + bygroups(Generic.Traceback, Name.Namespace, + Generic.Traceback, Literal.Number.Integer)), + + # (Exception Identifier)(Whitespace)(Traceback Message) + (r'(?u)(^[^\d\W]\w*)(\s*)(Traceback.*?\n)', + bygroups(Name.Exception, Generic.Whitespace, Text)), + # (Module/Filename)(Text)(Callee)(Function Signature) + # Better options for callee and function signature? + (r'(.*)( in )(.*)(\(.*\)\n)', + bygroups(Name.Namespace, Text, Name.Entity, Name.Tag)), + # Regular line: (Whitespace)(Line Number)(Python Code) + (r'(\s*?)(\d+)(.*?\n)', + bygroups(Generic.Whitespace, Literal.Number.Integer, Other)), + # Emphasized line: (Arrow)(Line Number)(Python Code) + # Using Exception token so arrow color matches the Exception. + (r'(-*>?\s?)(\d+)(.*?\n)', + bygroups(Name.Exception, Literal.Number.Integer, Other)), + # (Exception Identifier)(Message) + (r'(?u)(^[^\d\W]\w*)(:.*?\n)', + bygroups(Name.Exception, Text)), + # Tag everything else as Other, will be handled later. + (r'.*\n', Other), + ], + } + + +class IPythonTracebackLexer(DelegatingLexer): + """ + IPython traceback lexer. + + For doctests, the tracebacks can be snipped as much as desired with the + exception to the lines that designate a traceback. For non-syntax error + tracebacks, this is the line of hyphens. For syntax error tracebacks, + this is the line which lists the File and line number. + + """ + # The lexer inherits from DelegatingLexer. The "root" lexer is an + # appropriate IPython lexer, which depends on the value of the boolean + # `python3`. First, we parse with the partial IPython traceback lexer. + # Then, any code marked with the "Other" token is delegated to the root + # lexer. + # + name = 'IPython Traceback' + aliases = ['ipythontb'] + + def __init__(self, **options): + self.python3 = get_bool_opt(options, 'python3', False) + if self.python3: + self.aliases = ['ipython3tb'] + else: + self.aliases = ['ipython2tb', 'ipythontb'] + + if self.python3: + IPyLexer = IPython3Lexer + else: + IPyLexer = IPythonLexer + + DelegatingLexer.__init__(self, IPyLexer, + IPythonPartialTracebackLexer, **options) + +class IPythonConsoleLexer(Lexer): + """ + An IPython console lexer for IPython code-blocks and doctests, such as: + + .. code-block:: rst + + .. code-block:: ipythonconsole + + In [1]: a = 'foo' + + In [2]: a + Out[2]: 'foo' + + In [3]: print a + foo + + In [4]: 1 / 0 + + + Support is also provided for IPython exceptions: + + .. code-block:: rst + + .. code-block:: ipythonconsole + + In [1]: raise Exception + + --------------------------------------------------------------------------- + Exception Traceback (most recent call last) + in + ----> 1 raise Exception + + Exception: + + """ + name = 'IPython console session' + aliases = ['ipythonconsole'] + mimetypes = ['text/x-ipython-console'] + + # The regexps used to determine what is input and what is output. + # The default prompts for IPython are: + # + # in = 'In [#]: ' + # continuation = ' .D.: ' + # template = 'Out[#]: ' + # + # Where '#' is the 'prompt number' or 'execution count' and 'D' + # D is a number of dots matching the width of the execution count + # + in1_regex = r'In \[[0-9]+\]: ' + in2_regex = r' \.\.+\.: ' + out_regex = r'Out\[[0-9]+\]: ' + + #: The regex to determine when a traceback starts. + ipytb_start = re.compile(r'^(\^C)?(-+\n)|^( File)(.*)(, line )(\d+\n)') + + def __init__(self, **options): + """Initialize the IPython console lexer. + + Parameters + ---------- + python3 : bool + If `True`, then the console inputs are parsed using a Python 3 + lexer. Otherwise, they are parsed using a Python 2 lexer. + in1_regex : RegexObject + The compiled regular expression used to detect the start + of inputs. Although the IPython configuration setting may have a + trailing whitespace, do not include it in the regex. If `None`, + then the default input prompt is assumed. + in2_regex : RegexObject + The compiled regular expression used to detect the continuation + of inputs. Although the IPython configuration setting may have a + trailing whitespace, do not include it in the regex. If `None`, + then the default input prompt is assumed. + out_regex : RegexObject + The compiled regular expression used to detect outputs. If `None`, + then the default output prompt is assumed. + + """ + self.python3 = get_bool_opt(options, 'python3', False) + if self.python3: + self.aliases = ['ipython3console'] + else: + self.aliases = ['ipython2console', 'ipythonconsole'] + + in1_regex = options.get('in1_regex', self.in1_regex) + in2_regex = options.get('in2_regex', self.in2_regex) + out_regex = options.get('out_regex', self.out_regex) + + # So that we can work with input and output prompts which have been + # rstrip'd (possibly by editors) we also need rstrip'd variants. If + # we do not do this, then such prompts will be tagged as 'output'. + # The reason can't just use the rstrip'd variants instead is because + # we want any whitespace associated with the prompt to be inserted + # with the token. This allows formatted code to be modified so as hide + # the appearance of prompts, with the whitespace included. One example + # use of this is in copybutton.js from the standard lib Python docs. + in1_regex_rstrip = in1_regex.rstrip() + '\n' + in2_regex_rstrip = in2_regex.rstrip() + '\n' + out_regex_rstrip = out_regex.rstrip() + '\n' + + # Compile and save them all. + attrs = ['in1_regex', 'in2_regex', 'out_regex', + 'in1_regex_rstrip', 'in2_regex_rstrip', 'out_regex_rstrip'] + for attr in attrs: + self.__setattr__(attr, re.compile(locals()[attr])) + + Lexer.__init__(self, **options) + + if self.python3: + pylexer = IPython3Lexer + tblexer = IPythonTracebackLexer + else: + pylexer = IPythonLexer + tblexer = IPythonTracebackLexer + + self.pylexer = pylexer(**options) + self.tblexer = tblexer(**options) + + self.reset() + + def reset(self): + self.mode = 'output' + self.index = 0 + self.buffer = u'' + self.insertions = [] + + def buffered_tokens(self): + """ + Generator of unprocessed tokens after doing insertions and before + changing to a new state. + + """ + if self.mode == 'output': + tokens = [(0, Generic.Output, self.buffer)] + elif self.mode == 'input': + tokens = self.pylexer.get_tokens_unprocessed(self.buffer) + else: # traceback + tokens = self.tblexer.get_tokens_unprocessed(self.buffer) + + for i, t, v in do_insertions(self.insertions, tokens): + # All token indexes are relative to the buffer. + yield self.index + i, t, v + + # Clear it all + self.index += len(self.buffer) + self.buffer = u'' + self.insertions = [] + + def get_mci(self, line): + """ + Parses the line and returns a 3-tuple: (mode, code, insertion). + + `mode` is the next mode (or state) of the lexer, and is always equal + to 'input', 'output', or 'tb'. + + `code` is a portion of the line that should be added to the buffer + corresponding to the next mode and eventually lexed by another lexer. + For example, `code` could be Python code if `mode` were 'input'. + + `insertion` is a 3-tuple (index, token, text) representing an + unprocessed "token" that will be inserted into the stream of tokens + that are created from the buffer once we change modes. This is usually + the input or output prompt. + + In general, the next mode depends on current mode and on the contents + of `line`. + + """ + # To reduce the number of regex match checks, we have multiple + # 'if' blocks instead of 'if-elif' blocks. + + # Check for possible end of input + in2_match = self.in2_regex.match(line) + in2_match_rstrip = self.in2_regex_rstrip.match(line) + if (in2_match and in2_match.group().rstrip() == line.rstrip()) or \ + in2_match_rstrip: + end_input = True + else: + end_input = False + if end_input and self.mode != 'tb': + # Only look for an end of input when not in tb mode. + # An ellipsis could appear within the traceback. + mode = 'output' + code = u'' + insertion = (0, Generic.Prompt, line) + return mode, code, insertion + + # Check for output prompt + out_match = self.out_regex.match(line) + out_match_rstrip = self.out_regex_rstrip.match(line) + if out_match or out_match_rstrip: + mode = 'output' + if out_match: + idx = out_match.end() + else: + idx = out_match_rstrip.end() + code = line[idx:] + # Use the 'heading' token for output. We cannot use Generic.Error + # since it would conflict with exceptions. + insertion = (0, Generic.Heading, line[:idx]) + return mode, code, insertion + + + # Check for input or continuation prompt (non stripped version) + in1_match = self.in1_regex.match(line) + if in1_match or (in2_match and self.mode != 'tb'): + # New input or when not in tb, continued input. + # We do not check for continued input when in tb since it is + # allowable to replace a long stack with an ellipsis. + mode = 'input' + if in1_match: + idx = in1_match.end() + else: # in2_match + idx = in2_match.end() + code = line[idx:] + insertion = (0, Generic.Prompt, line[:idx]) + return mode, code, insertion + + # Check for input or continuation prompt (stripped version) + in1_match_rstrip = self.in1_regex_rstrip.match(line) + if in1_match_rstrip or (in2_match_rstrip and self.mode != 'tb'): + # New input or when not in tb, continued input. + # We do not check for continued input when in tb since it is + # allowable to replace a long stack with an ellipsis. + mode = 'input' + if in1_match_rstrip: + idx = in1_match_rstrip.end() + else: # in2_match + idx = in2_match_rstrip.end() + code = line[idx:] + insertion = (0, Generic.Prompt, line[:idx]) + return mode, code, insertion + + # Check for traceback + if self.ipytb_start.match(line): + mode = 'tb' + code = line + insertion = None + return mode, code, insertion + + # All other stuff... + if self.mode in ('input', 'output'): + # We assume all other text is output. Multiline input that + # does not use the continuation marker cannot be detected. + # For example, the 3 in the following is clearly output: + # + # In [1]: print 3 + # 3 + # + # But the following second line is part of the input: + # + # In [2]: while True: + # print True + # + # In both cases, the 2nd line will be 'output'. + # + mode = 'output' + else: + mode = 'tb' + + code = line + insertion = None + + return mode, code, insertion + + def get_tokens_unprocessed(self, text): + self.reset() + for match in line_re.finditer(text): + line = match.group() + mode, code, insertion = self.get_mci(line) + + if mode != self.mode: + # Yield buffered tokens before transitioning to new mode. + for token in self.buffered_tokens(): + yield token + self.mode = mode + + if insertion: + self.insertions.append((len(self.buffer), [insertion])) + self.buffer += code + + for token in self.buffered_tokens(): + yield token + +class IPyLexer(Lexer): + r""" + Primary lexer for all IPython-like code. + + This is a simple helper lexer. If the first line of the text begins with + "In \[[0-9]+\]:", then the entire text is parsed with an IPython console + lexer. If not, then the entire text is parsed with an IPython lexer. + + The goal is to reduce the number of lexers that are registered + with Pygments. + + """ + name = 'IPy session' + aliases = ['ipy'] + + def __init__(self, **options): + self.python3 = get_bool_opt(options, 'python3', False) + if self.python3: + self.aliases = ['ipy3'] + else: + self.aliases = ['ipy2', 'ipy'] + + Lexer.__init__(self, **options) + + self.IPythonLexer = IPythonLexer(**options) + self.IPythonConsoleLexer = IPythonConsoleLexer(**options) + + def get_tokens_unprocessed(self, text): + # Search for the input prompt anywhere...this allows code blocks to + # begin with comments as well. + if re.match(r'.*(In \[[0-9]+\]:)', text.strip(), re.DOTALL): + lex = self.IPythonConsoleLexer + else: + lex = self.IPythonLexer + for token in lex.get_tokens_unprocessed(text): + yield token + diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/pretty.py b/.venv/lib/python3.8/site-packages/IPython/lib/pretty.py new file mode 100644 index 00000000..1cb46b14 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/pretty.py @@ -0,0 +1,873 @@ +# -*- coding: utf-8 -*- +""" +Python advanced pretty printer. This pretty printer is intended to +replace the old `pprint` python module which does not allow developers +to provide their own pretty print callbacks. + +This module is based on ruby's `prettyprint.rb` library by `Tanaka Akira`. + + +Example Usage +------------- + +To directly print the representation of an object use `pprint`:: + + from pretty import pprint + pprint(complex_object) + +To get a string of the output use `pretty`:: + + from pretty import pretty + string = pretty(complex_object) + + +Extending +--------- + +The pretty library allows developers to add pretty printing rules for their +own objects. This process is straightforward. All you have to do is to +add a `_repr_pretty_` method to your object and call the methods on the +pretty printer passed:: + + class MyObject(object): + + def _repr_pretty_(self, p, cycle): + ... + +Here is an example implementation of a `_repr_pretty_` method for a list +subclass:: + + class MyList(list): + + def _repr_pretty_(self, p, cycle): + if cycle: + p.text('MyList(...)') + else: + with p.group(8, 'MyList([', '])'): + for idx, item in enumerate(self): + if idx: + p.text(',') + p.breakable() + p.pretty(item) + +The `cycle` parameter is `True` if pretty detected a cycle. You *have* to +react to that or the result is an infinite loop. `p.text()` just adds +non breaking text to the output, `p.breakable()` either adds a whitespace +or breaks here. If you pass it an argument it's used instead of the +default space. `p.pretty` prettyprints another object using the pretty print +method. + +The first parameter to the `group` function specifies the extra indentation +of the next line. In this example the next item will either be on the same +line (if the items are short enough) or aligned with the right edge of the +opening bracket of `MyList`. + +If you just want to indent something you can use the group function +without open / close parameters. You can also use this code:: + + with p.indent(2): + ... + +Inheritance diagram: + +.. inheritance-diagram:: IPython.lib.pretty + :parts: 3 + +:copyright: 2007 by Armin Ronacher. + Portions (c) 2009 by Robert Kern. +:license: BSD License. +""" + +from contextlib import contextmanager +import datetime +import os +import re +import sys +import types +from collections import deque +from inspect import signature +from io import StringIO +from warnings import warn + +from IPython.utils.decorators import undoc +from IPython.utils.py3compat import PYPY + +__all__ = ['pretty', 'pprint', 'PrettyPrinter', 'RepresentationPrinter', + 'for_type', 'for_type_by_name'] + + +MAX_SEQ_LENGTH = 1000 +_re_pattern_type = type(re.compile('')) + +def _safe_getattr(obj, attr, default=None): + """Safe version of getattr. + + Same as getattr, but will return ``default`` on any Exception, + rather than raising. + """ + try: + return getattr(obj, attr, default) + except Exception: + return default + +@undoc +class CUnicodeIO(StringIO): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + warn(("CUnicodeIO is deprecated since IPython 6.0. " + "Please use io.StringIO instead."), + DeprecationWarning, stacklevel=2) + +def _sorted_for_pprint(items): + """ + Sort the given items for pretty printing. Since some predictable + sorting is better than no sorting at all, we sort on the string + representation if normal sorting fails. + """ + items = list(items) + try: + return sorted(items) + except Exception: + try: + return sorted(items, key=str) + except Exception: + return items + +def pretty(obj, verbose=False, max_width=79, newline='\n', max_seq_length=MAX_SEQ_LENGTH): + """ + Pretty print the object's representation. + """ + stream = StringIO() + printer = RepresentationPrinter(stream, verbose, max_width, newline, max_seq_length=max_seq_length) + printer.pretty(obj) + printer.flush() + return stream.getvalue() + + +def pprint(obj, verbose=False, max_width=79, newline='\n', max_seq_length=MAX_SEQ_LENGTH): + """ + Like `pretty` but print to stdout. + """ + printer = RepresentationPrinter(sys.stdout, verbose, max_width, newline, max_seq_length=max_seq_length) + printer.pretty(obj) + printer.flush() + sys.stdout.write(newline) + sys.stdout.flush() + +class _PrettyPrinterBase(object): + + @contextmanager + def indent(self, indent): + """with statement support for indenting/dedenting.""" + self.indentation += indent + try: + yield + finally: + self.indentation -= indent + + @contextmanager + def group(self, indent=0, open='', close=''): + """like begin_group / end_group but for the with statement.""" + self.begin_group(indent, open) + try: + yield + finally: + self.end_group(indent, close) + +class PrettyPrinter(_PrettyPrinterBase): + """ + Baseclass for the `RepresentationPrinter` prettyprinter that is used to + generate pretty reprs of objects. Contrary to the `RepresentationPrinter` + this printer knows nothing about the default pprinters or the `_repr_pretty_` + callback method. + """ + + def __init__(self, output, max_width=79, newline='\n', max_seq_length=MAX_SEQ_LENGTH): + self.output = output + self.max_width = max_width + self.newline = newline + self.max_seq_length = max_seq_length + self.output_width = 0 + self.buffer_width = 0 + self.buffer = deque() + + root_group = Group(0) + self.group_stack = [root_group] + self.group_queue = GroupQueue(root_group) + self.indentation = 0 + + def _break_one_group(self, group): + while group.breakables: + x = self.buffer.popleft() + self.output_width = x.output(self.output, self.output_width) + self.buffer_width -= x.width + while self.buffer and isinstance(self.buffer[0], Text): + x = self.buffer.popleft() + self.output_width = x.output(self.output, self.output_width) + self.buffer_width -= x.width + + def _break_outer_groups(self): + while self.max_width < self.output_width + self.buffer_width: + group = self.group_queue.deq() + if not group: + return + self._break_one_group(group) + + def text(self, obj): + """Add literal text to the output.""" + width = len(obj) + if self.buffer: + text = self.buffer[-1] + if not isinstance(text, Text): + text = Text() + self.buffer.append(text) + text.add(obj, width) + self.buffer_width += width + self._break_outer_groups() + else: + self.output.write(obj) + self.output_width += width + + def breakable(self, sep=' '): + """ + Add a breakable separator to the output. This does not mean that it + will automatically break here. If no breaking on this position takes + place the `sep` is inserted which default to one space. + """ + width = len(sep) + group = self.group_stack[-1] + if group.want_break: + self.flush() + self.output.write(self.newline) + self.output.write(' ' * self.indentation) + self.output_width = self.indentation + self.buffer_width = 0 + else: + self.buffer.append(Breakable(sep, width, self)) + self.buffer_width += width + self._break_outer_groups() + + def break_(self): + """ + Explicitly insert a newline into the output, maintaining correct indentation. + """ + group = self.group_queue.deq() + if group: + self._break_one_group(group) + self.flush() + self.output.write(self.newline) + self.output.write(' ' * self.indentation) + self.output_width = self.indentation + self.buffer_width = 0 + + + def begin_group(self, indent=0, open=''): + """ + Begin a group. + The first parameter specifies the indentation for the next line (usually + the width of the opening text), the second the opening text. All + parameters are optional. + """ + if open: + self.text(open) + group = Group(self.group_stack[-1].depth + 1) + self.group_stack.append(group) + self.group_queue.enq(group) + self.indentation += indent + + def _enumerate(self, seq): + """like enumerate, but with an upper limit on the number of items""" + for idx, x in enumerate(seq): + if self.max_seq_length and idx >= self.max_seq_length: + self.text(',') + self.breakable() + self.text('...') + return + yield idx, x + + def end_group(self, dedent=0, close=''): + """End a group. See `begin_group` for more details.""" + self.indentation -= dedent + group = self.group_stack.pop() + if not group.breakables: + self.group_queue.remove(group) + if close: + self.text(close) + + def flush(self): + """Flush data that is left in the buffer.""" + for data in self.buffer: + self.output_width += data.output(self.output, self.output_width) + self.buffer.clear() + self.buffer_width = 0 + + +def _get_mro(obj_class): + """ Get a reasonable method resolution order of a class and its superclasses + for both old-style and new-style classes. + """ + if not hasattr(obj_class, '__mro__'): + # Old-style class. Mix in object to make a fake new-style class. + try: + obj_class = type(obj_class.__name__, (obj_class, object), {}) + except TypeError: + # Old-style extension type that does not descend from object. + # FIXME: try to construct a more thorough MRO. + mro = [obj_class] + else: + mro = obj_class.__mro__[1:-1] + else: + mro = obj_class.__mro__ + return mro + + +class RepresentationPrinter(PrettyPrinter): + """ + Special pretty printer that has a `pretty` method that calls the pretty + printer for a python object. + + This class stores processing data on `self` so you must *never* use + this class in a threaded environment. Always lock it or reinstanciate + it. + + Instances also have a verbose flag callbacks can access to control their + output. For example the default instance repr prints all attributes and + methods that are not prefixed by an underscore if the printer is in + verbose mode. + """ + + def __init__(self, output, verbose=False, max_width=79, newline='\n', + singleton_pprinters=None, type_pprinters=None, deferred_pprinters=None, + max_seq_length=MAX_SEQ_LENGTH): + + PrettyPrinter.__init__(self, output, max_width, newline, max_seq_length=max_seq_length) + self.verbose = verbose + self.stack = [] + if singleton_pprinters is None: + singleton_pprinters = _singleton_pprinters.copy() + self.singleton_pprinters = singleton_pprinters + if type_pprinters is None: + type_pprinters = _type_pprinters.copy() + self.type_pprinters = type_pprinters + if deferred_pprinters is None: + deferred_pprinters = _deferred_type_pprinters.copy() + self.deferred_pprinters = deferred_pprinters + + def pretty(self, obj): + """Pretty print the given object.""" + obj_id = id(obj) + cycle = obj_id in self.stack + self.stack.append(obj_id) + self.begin_group() + try: + obj_class = _safe_getattr(obj, '__class__', None) or type(obj) + # First try to find registered singleton printers for the type. + try: + printer = self.singleton_pprinters[obj_id] + except (TypeError, KeyError): + pass + else: + return printer(obj, self, cycle) + # Next walk the mro and check for either: + # 1) a registered printer + # 2) a _repr_pretty_ method + for cls in _get_mro(obj_class): + if cls in self.type_pprinters: + # printer registered in self.type_pprinters + return self.type_pprinters[cls](obj, self, cycle) + else: + # deferred printer + printer = self._in_deferred_types(cls) + if printer is not None: + return printer(obj, self, cycle) + else: + # Finally look for special method names. + # Some objects automatically create any requested + # attribute. Try to ignore most of them by checking for + # callability. + if '_repr_pretty_' in cls.__dict__: + meth = cls._repr_pretty_ + if callable(meth): + return meth(obj, self, cycle) + if cls is not object \ + and callable(cls.__dict__.get('__repr__')): + return _repr_pprint(obj, self, cycle) + + return _default_pprint(obj, self, cycle) + finally: + self.end_group() + self.stack.pop() + + def _in_deferred_types(self, cls): + """ + Check if the given class is specified in the deferred type registry. + + Returns the printer from the registry if it exists, and None if the + class is not in the registry. Successful matches will be moved to the + regular type registry for future use. + """ + mod = _safe_getattr(cls, '__module__', None) + name = _safe_getattr(cls, '__name__', None) + key = (mod, name) + printer = None + if key in self.deferred_pprinters: + # Move the printer over to the regular registry. + printer = self.deferred_pprinters.pop(key) + self.type_pprinters[cls] = printer + return printer + + +class Printable(object): + + def output(self, stream, output_width): + return output_width + + +class Text(Printable): + + def __init__(self): + self.objs = [] + self.width = 0 + + def output(self, stream, output_width): + for obj in self.objs: + stream.write(obj) + return output_width + self.width + + def add(self, obj, width): + self.objs.append(obj) + self.width += width + + +class Breakable(Printable): + + def __init__(self, seq, width, pretty): + self.obj = seq + self.width = width + self.pretty = pretty + self.indentation = pretty.indentation + self.group = pretty.group_stack[-1] + self.group.breakables.append(self) + + def output(self, stream, output_width): + self.group.breakables.popleft() + if self.group.want_break: + stream.write(self.pretty.newline) + stream.write(' ' * self.indentation) + return self.indentation + if not self.group.breakables: + self.pretty.group_queue.remove(self.group) + stream.write(self.obj) + return output_width + self.width + + +class Group(Printable): + + def __init__(self, depth): + self.depth = depth + self.breakables = deque() + self.want_break = False + + +class GroupQueue(object): + + def __init__(self, *groups): + self.queue = [] + for group in groups: + self.enq(group) + + def enq(self, group): + depth = group.depth + while depth > len(self.queue) - 1: + self.queue.append([]) + self.queue[depth].append(group) + + def deq(self): + for stack in self.queue: + for idx, group in enumerate(reversed(stack)): + if group.breakables: + del stack[idx] + group.want_break = True + return group + for group in stack: + group.want_break = True + del stack[:] + + def remove(self, group): + try: + self.queue[group.depth].remove(group) + except ValueError: + pass + + +def _default_pprint(obj, p, cycle): + """ + The default print function. Used if an object does not provide one and + it's none of the builtin objects. + """ + klass = _safe_getattr(obj, '__class__', None) or type(obj) + if _safe_getattr(klass, '__repr__', None) is not object.__repr__: + # A user-provided repr. Find newlines and replace them with p.break_() + _repr_pprint(obj, p, cycle) + return + p.begin_group(1, '<') + p.pretty(klass) + p.text(' at 0x%x' % id(obj)) + if cycle: + p.text(' ...') + elif p.verbose: + first = True + for key in dir(obj): + if not key.startswith('_'): + try: + value = getattr(obj, key) + except AttributeError: + continue + if isinstance(value, types.MethodType): + continue + if not first: + p.text(',') + p.breakable() + p.text(key) + p.text('=') + step = len(key) + 1 + p.indentation += step + p.pretty(value) + p.indentation -= step + first = False + p.end_group(1, '>') + + +def _seq_pprinter_factory(start, end): + """ + Factory that returns a pprint function useful for sequences. Used by + the default pprint for tuples, dicts, and lists. + """ + def inner(obj, p, cycle): + if cycle: + return p.text(start + '...' + end) + step = len(start) + p.begin_group(step, start) + for idx, x in p._enumerate(obj): + if idx: + p.text(',') + p.breakable() + p.pretty(x) + if len(obj) == 1 and type(obj) is tuple: + # Special case for 1-item tuples. + p.text(',') + p.end_group(step, end) + return inner + + +def _set_pprinter_factory(start, end): + """ + Factory that returns a pprint function useful for sets and frozensets. + """ + def inner(obj, p, cycle): + if cycle: + return p.text(start + '...' + end) + if len(obj) == 0: + # Special case. + p.text(type(obj).__name__ + '()') + else: + step = len(start) + p.begin_group(step, start) + # Like dictionary keys, we will try to sort the items if there aren't too many + if not (p.max_seq_length and len(obj) >= p.max_seq_length): + items = _sorted_for_pprint(obj) + else: + items = obj + for idx, x in p._enumerate(items): + if idx: + p.text(',') + p.breakable() + p.pretty(x) + p.end_group(step, end) + return inner + + +def _dict_pprinter_factory(start, end): + """ + Factory that returns a pprint function used by the default pprint of + dicts and dict proxies. + """ + def inner(obj, p, cycle): + if cycle: + return p.text('{...}') + step = len(start) + p.begin_group(step, start) + keys = obj.keys() + for idx, key in p._enumerate(keys): + if idx: + p.text(',') + p.breakable() + p.pretty(key) + p.text(': ') + p.pretty(obj[key]) + p.end_group(step, end) + return inner + + +def _super_pprint(obj, p, cycle): + """The pprint for the super type.""" + p.begin_group(8, '') + + +def _re_pattern_pprint(obj, p, cycle): + """The pprint function for regular expression patterns.""" + p.text('re.compile(') + pattern = repr(obj.pattern) + if pattern[:1] in 'uU': + pattern = pattern[1:] + prefix = 'ur' + else: + prefix = 'r' + pattern = prefix + pattern.replace('\\\\', '\\') + p.text(pattern) + if obj.flags: + p.text(',') + p.breakable() + done_one = False + for flag in ('TEMPLATE', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL', + 'UNICODE', 'VERBOSE', 'DEBUG'): + if obj.flags & getattr(re, flag): + if done_one: + p.text('|') + p.text('re.' + flag) + done_one = True + p.text(')') + + +def _types_simplenamespace_pprint(obj, p, cycle): + """The pprint function for types.SimpleNamespace.""" + name = 'namespace' + with p.group(len(name) + 1, name + '(', ')'): + if cycle: + p.text('...') + else: + for idx, (attr, value) in enumerate(obj.__dict__.items()): + if idx: + p.text(',') + p.breakable() + attr_kwarg = '{}='.format(attr) + with p.group(len(attr_kwarg), attr_kwarg): + p.pretty(value) + + +def _type_pprint(obj, p, cycle): + """The pprint for classes and types.""" + # Heap allocated types might not have the module attribute, + # and others may set it to None. + + # Checks for a __repr__ override in the metaclass. Can't compare the + # type(obj).__repr__ directly because in PyPy the representation function + # inherited from type isn't the same type.__repr__ + if [m for m in _get_mro(type(obj)) if "__repr__" in vars(m)][:1] != [type]: + _repr_pprint(obj, p, cycle) + return + + mod = _safe_getattr(obj, '__module__', None) + try: + name = obj.__qualname__ + if not isinstance(name, str): + # This can happen if the type implements __qualname__ as a property + # or other descriptor in Python 2. + raise Exception("Try __name__") + except Exception: + name = obj.__name__ + if not isinstance(name, str): + name = '' + + if mod in (None, '__builtin__', 'builtins', 'exceptions'): + p.text(name) + else: + p.text(mod + '.' + name) + + +def _repr_pprint(obj, p, cycle): + """A pprint that just redirects to the normal repr function.""" + # Find newlines and replace them with p.break_() + output = repr(obj) + lines = output.splitlines() + with p.group(): + for idx, output_line in enumerate(lines): + if idx: + p.break_() + p.text(output_line) + + +def _function_pprint(obj, p, cycle): + """Base pprint for all functions and builtin functions.""" + name = _safe_getattr(obj, '__qualname__', obj.__name__) + mod = obj.__module__ + if mod and mod not in ('__builtin__', 'builtins', 'exceptions'): + name = mod + '.' + name + try: + func_def = name + str(signature(obj)) + except ValueError: + func_def = name + p.text('' % func_def) + + +def _exception_pprint(obj, p, cycle): + """Base pprint for all exceptions.""" + name = getattr(obj.__class__, '__qualname__', obj.__class__.__name__) + if obj.__class__.__module__ not in ('exceptions', 'builtins'): + name = '%s.%s' % (obj.__class__.__module__, name) + step = len(name) + 1 + p.begin_group(step, name + '(') + for idx, arg in enumerate(getattr(obj, 'args', ())): + if idx: + p.text(',') + p.breakable() + p.pretty(arg) + p.end_group(step, ')') + + +#: the exception base +try: + _exception_base = BaseException +except NameError: + _exception_base = Exception + + +#: printers for builtin types +_type_pprinters = { + int: _repr_pprint, + float: _repr_pprint, + str: _repr_pprint, + tuple: _seq_pprinter_factory('(', ')'), + list: _seq_pprinter_factory('[', ']'), + dict: _dict_pprinter_factory('{', '}'), + set: _set_pprinter_factory('{', '}'), + frozenset: _set_pprinter_factory('frozenset({', '})'), + super: _super_pprint, + _re_pattern_type: _re_pattern_pprint, + type: _type_pprint, + types.FunctionType: _function_pprint, + types.BuiltinFunctionType: _function_pprint, + types.MethodType: _repr_pprint, + types.SimpleNamespace: _types_simplenamespace_pprint, + datetime.datetime: _repr_pprint, + datetime.timedelta: _repr_pprint, + _exception_base: _exception_pprint +} + +# render os.environ like a dict +_env_type = type(os.environ) +# future-proof in case os.environ becomes a plain dict? +if _env_type is not dict: + _type_pprinters[_env_type] = _dict_pprinter_factory('environ{', '}') + +try: + # In PyPy, types.DictProxyType is dict, setting the dictproxy printer + # using dict.setdefault avoids overwriting the dict printer + _type_pprinters.setdefault(types.DictProxyType, + _dict_pprinter_factory('dict_proxy({', '})')) + _type_pprinters[types.ClassType] = _type_pprint + _type_pprinters[types.SliceType] = _repr_pprint +except AttributeError: # Python 3 + _type_pprinters[types.MappingProxyType] = \ + _dict_pprinter_factory('mappingproxy({', '})') + _type_pprinters[slice] = _repr_pprint + +_type_pprinters[range] = _repr_pprint +_type_pprinters[bytes] = _repr_pprint + +#: printers for types specified by name +_deferred_type_pprinters = { +} + +def for_type(typ, func): + """ + Add a pretty printer for a given type. + """ + oldfunc = _type_pprinters.get(typ, None) + if func is not None: + # To support easy restoration of old pprinters, we need to ignore Nones. + _type_pprinters[typ] = func + return oldfunc + +def for_type_by_name(type_module, type_name, func): + """ + Add a pretty printer for a type specified by the module and name of a type + rather than the type object itself. + """ + key = (type_module, type_name) + oldfunc = _deferred_type_pprinters.get(key, None) + if func is not None: + # To support easy restoration of old pprinters, we need to ignore Nones. + _deferred_type_pprinters[key] = func + return oldfunc + + +#: printers for the default singletons +_singleton_pprinters = dict.fromkeys(map(id, [None, True, False, Ellipsis, + NotImplemented]), _repr_pprint) + + +def _defaultdict_pprint(obj, p, cycle): + name = obj.__class__.__name__ + with p.group(len(name) + 1, name + '(', ')'): + if cycle: + p.text('...') + else: + p.pretty(obj.default_factory) + p.text(',') + p.breakable() + p.pretty(dict(obj)) + +def _ordereddict_pprint(obj, p, cycle): + name = obj.__class__.__name__ + with p.group(len(name) + 1, name + '(', ')'): + if cycle: + p.text('...') + elif len(obj): + p.pretty(list(obj.items())) + +def _deque_pprint(obj, p, cycle): + name = obj.__class__.__name__ + with p.group(len(name) + 1, name + '(', ')'): + if cycle: + p.text('...') + else: + p.pretty(list(obj)) + + +def _counter_pprint(obj, p, cycle): + name = obj.__class__.__name__ + with p.group(len(name) + 1, name + '(', ')'): + if cycle: + p.text('...') + elif len(obj): + p.pretty(dict(obj)) + +for_type_by_name('collections', 'defaultdict', _defaultdict_pprint) +for_type_by_name('collections', 'OrderedDict', _ordereddict_pprint) +for_type_by_name('collections', 'deque', _deque_pprint) +for_type_by_name('collections', 'Counter', _counter_pprint) + +if __name__ == '__main__': + from random import randrange + class Foo(object): + def __init__(self): + self.foo = 1 + self.bar = re.compile(r'\s+') + self.blub = dict.fromkeys(range(30), randrange(1, 40)) + self.hehe = 23424.234234 + self.list = ["blub", "blah", self] + + def get_foo(self): + print("foo") + + pprint(Foo(), verbose=True) diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/security.py b/.venv/lib/python3.8/site-packages/IPython/lib/security.py new file mode 100644 index 00000000..91a2344e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/security.py @@ -0,0 +1,114 @@ +""" +Password generation for the IPython notebook. +""" +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +# Stdlib +import getpass +import hashlib +import random + +# Our own +from IPython.core.error import UsageError +from IPython.utils.py3compat import encode + +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- + +# Length of the salt in nr of hex chars, which implies salt_len * 4 +# bits of randomness. +salt_len = 12 + +#----------------------------------------------------------------------------- +# Functions +#----------------------------------------------------------------------------- + +def passwd(passphrase=None, algorithm='sha1'): + """Generate hashed password and salt for use in notebook configuration. + + In the notebook configuration, set `c.NotebookApp.password` to + the generated string. + + Parameters + ---------- + passphrase : str + Password to hash. If unspecified, the user is asked to input + and verify a password. + algorithm : str + Hashing algorithm to use (e.g, 'sha1' or any argument supported + by :func:`hashlib.new`). + + Returns + ------- + hashed_passphrase : str + Hashed password, in the format 'hash_algorithm:salt:passphrase_hash'. + + Examples + -------- + >>> passwd('mypassword') + 'sha1:7cf3:b7d6da294ea9592a9480c8f52e63cd42cfb9dd12' + + """ + if passphrase is None: + for i in range(3): + p0 = getpass.getpass('Enter password: ') + p1 = getpass.getpass('Verify password: ') + if p0 == p1: + passphrase = p0 + break + else: + print('Passwords do not match.') + else: + raise UsageError('No matching passwords found. Giving up.') + + h = hashlib.new(algorithm) + salt = ('%0' + str(salt_len) + 'x') % random.getrandbits(4 * salt_len) + h.update(encode(passphrase, 'utf-8') + encode(salt, 'ascii')) + + return ':'.join((algorithm, salt, h.hexdigest())) + + +def passwd_check(hashed_passphrase, passphrase): + """Verify that a given passphrase matches its hashed version. + + Parameters + ---------- + hashed_passphrase : str + Hashed password, in the format returned by `passwd`. + passphrase : str + Passphrase to validate. + + Returns + ------- + valid : bool + True if the passphrase matches the hash. + + Examples + -------- + >>> from IPython.lib.security import passwd_check + >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a', + ... 'mypassword') + True + + >>> passwd_check('sha1:0e112c3ddfce:a68df677475c2b47b6e86d0467eec97ac5f4b85a', + ... 'anotherpassword') + False + """ + try: + algorithm, salt, pw_digest = hashed_passphrase.split(':', 2) + except (ValueError, TypeError): + return False + + try: + h = hashlib.new(algorithm) + except ValueError: + return False + + if len(pw_digest) == 0: + return False + + h.update(encode(passphrase, 'utf-8') + encode(salt, 'ascii')) + + return h.hexdigest() == pw_digest diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__init__.py b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..964ae8e4 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_backgroundjobs.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_backgroundjobs.cpython-38.pyc new file mode 100644 index 00000000..89de460a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_backgroundjobs.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_clipboard.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_clipboard.cpython-38.pyc new file mode 100644 index 00000000..6622a7fb Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_clipboard.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_deepreload.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_deepreload.cpython-38.pyc new file mode 100644 index 00000000..ff95f5e8 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_deepreload.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_display.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_display.cpython-38.pyc new file mode 100644 index 00000000..1377009b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_display.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_editorhooks.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_editorhooks.cpython-38.pyc new file mode 100644 index 00000000..8dc715a6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_editorhooks.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_imports.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_imports.cpython-38.pyc new file mode 100644 index 00000000..fa1b8ec8 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_imports.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_latextools.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_latextools.cpython-38.pyc new file mode 100644 index 00000000..14313194 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_latextools.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_lexers.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_lexers.cpython-38.pyc new file mode 100644 index 00000000..17eccbec Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_lexers.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_pretty.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_pretty.cpython-38.pyc new file mode 100644 index 00000000..e3976510 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_pretty.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_security.cpython-38.pyc b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_security.cpython-38.pyc new file mode 100644 index 00000000..36eaf8b6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/__pycache__/test_security.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/test.wav b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test.wav new file mode 100644 index 00000000..aa74fc5b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test.wav differ diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_backgroundjobs.py b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_backgroundjobs.py new file mode 100644 index 00000000..d7793f50 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_backgroundjobs.py @@ -0,0 +1,88 @@ +"""Tests for pylab tools module. +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2011, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Stdlib imports +import time + +# Third-party imports +import nose.tools as nt + +# Our own imports +from IPython.lib import backgroundjobs as bg + +#----------------------------------------------------------------------------- +# Globals and constants +#----------------------------------------------------------------------------- +t_short = 0.0001 # very short interval to wait on jobs + +#----------------------------------------------------------------------------- +# Local utilities +#----------------------------------------------------------------------------- +def sleeper(interval=t_short, *a, **kw): + args = dict(interval=interval, + other_args=a, + kw_args=kw) + time.sleep(interval) + return args + +def crasher(interval=t_short, *a, **kw): + time.sleep(interval) + raise Exception("Dead job with interval %s" % interval) + +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- + +def test_result(): + """Test job submission and result retrieval""" + jobs = bg.BackgroundJobManager() + j = jobs.new(sleeper) + j.join() + nt.assert_equal(j.result['interval'], t_short) + + +def test_flush(): + """Test job control""" + jobs = bg.BackgroundJobManager() + j = jobs.new(sleeper) + j.join() + nt.assert_equal(len(jobs.completed), 1) + nt.assert_equal(len(jobs.dead), 0) + jobs.flush() + nt.assert_equal(len(jobs.completed), 0) + + +def test_dead(): + """Test control of dead jobs""" + jobs = bg.BackgroundJobManager() + j = jobs.new(crasher) + j.join() + nt.assert_equal(len(jobs.completed), 0) + nt.assert_equal(len(jobs.dead), 1) + jobs.flush() + nt.assert_equal(len(jobs.dead), 0) + + +def test_longer(): + """Test control of longer-running jobs""" + jobs = bg.BackgroundJobManager() + # Sleep for long enough for the following two checks to still report the + # job as running, but not so long that it makes the test suite noticeably + # slower. + j = jobs.new(sleeper, 0.1) + nt.assert_equal(len(jobs.running), 1) + nt.assert_equal(len(jobs.completed), 0) + j.join() + nt.assert_equal(len(jobs.running), 0) + nt.assert_equal(len(jobs.completed), 1) diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_clipboard.py b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_clipboard.py new file mode 100644 index 00000000..f1050bfb --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_clipboard.py @@ -0,0 +1,21 @@ +import nose.tools as nt + +from IPython.core.error import TryNext +from IPython.lib.clipboard import ClipboardEmpty +from IPython.testing.decorators import skip_if_no_x11 + +@skip_if_no_x11 +def test_clipboard_get(): + # Smoketest for clipboard access - we can't easily guarantee that the + # clipboard is accessible and has something on it, but this tries to + # exercise the relevant code anyway. + try: + a = get_ipython().hooks.clipboard_get() + except ClipboardEmpty: + # Nothing in clipboard to get + pass + except TryNext: + # No clipboard access API available + pass + else: + nt.assert_is_instance(a, str) diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_deepreload.py b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_deepreload.py new file mode 100644 index 00000000..abc57a38 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_deepreload.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +"""Test suite for the deepreload module.""" + +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. + +import os + +import nose.tools as nt + +from IPython.utils.syspathcontext import prepended_to_syspath +from IPython.utils.tempdir import TemporaryDirectory +from IPython.lib.deepreload import reload as dreload + +def test_deepreload(): + "Test that dreload does deep reloads and skips excluded modules." + with TemporaryDirectory() as tmpdir: + with prepended_to_syspath(tmpdir): + with open(os.path.join(tmpdir, 'A.py'), 'w') as f: + f.write("class Object(object):\n pass\n") + with open(os.path.join(tmpdir, 'B.py'), 'w') as f: + f.write("import A\n") + import A + import B + + # Test that A is not reloaded. + obj = A.Object() + dreload(B, exclude=['A']) + nt.assert_true(isinstance(obj, A.Object)) + + # Test that A is reloaded. + obj = A.Object() + dreload(B) + nt.assert_false(isinstance(obj, A.Object)) diff --git a/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_display.py b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_display.py new file mode 100644 index 00000000..7e98a185 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/IPython/lib/tests/test_display.py @@ -0,0 +1,266 @@ +"""Tests for IPython.lib.display. + +""" +#----------------------------------------------------------------------------- +# Copyright (c) 2012, the IPython Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- +from tempfile import NamedTemporaryFile, mkdtemp +from os.path import split, join as pjoin, dirname +import pathlib +from unittest import TestCase, mock +import struct +import wave +from io import BytesIO + +# Third-party imports +import nose.tools as nt + +try: + import numpy +except ImportError: + pass + +# Our own imports +from IPython.lib import display + +from IPython.testing.decorators import skipif_not_numpy + +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- + +#-------------------------- +# FileLink tests +#-------------------------- + +def test_instantiation_FileLink(): + """FileLink: Test class can be instantiated""" + fl = display.FileLink('example.txt') + # TODO: remove if when only Python >= 3.6 is supported + fl = display.FileLink(pathlib.PurePath('example.txt')) + +def test_warning_on_non_existent_path_FileLink(): + """FileLink: Calling _repr_html_ on non-existent files returns a warning + """ + fl = display.FileLink('example.txt') + nt.assert_true(fl._repr_html_().startswith('Path (example.txt)')) + +def test_existing_path_FileLink(): + """FileLink: Calling _repr_html_ functions as expected on existing filepath + """ + tf = NamedTemporaryFile() + fl = display.FileLink(tf.name) + actual = fl._repr_html_() + expected = "%s
" % (tf.name,tf.name) + nt.assert_equal(actual,expected) + +def test_existing_path_FileLink_repr(): + """FileLink: Calling repr() functions as expected on existing filepath + """ + tf = NamedTemporaryFile() + fl = display.FileLink(tf.name) + actual = repr(fl) + expected = tf.name + nt.assert_equal(actual,expected) + +def test_error_on_directory_to_FileLink(): + """FileLink: Raises error when passed directory + """ + td = mkdtemp() + nt.assert_raises(ValueError,display.FileLink,td) + +#-------------------------- +# FileLinks tests +#-------------------------- + +def test_instantiation_FileLinks(): + """FileLinks: Test class can be instantiated + """ + fls = display.FileLinks('example') + +def test_warning_on_non_existent_path_FileLinks(): + """FileLinks: Calling _repr_html_ on non-existent files returns a warning + """ + fls = display.FileLinks('example') + nt.assert_true(fls._repr_html_().startswith('Path (example)')) + +def test_existing_path_FileLinks(): + """FileLinks: Calling _repr_html_ functions as expected on existing dir + """ + td = mkdtemp() + tf1 = NamedTemporaryFile(dir=td) + tf2 = NamedTemporaryFile(dir=td) + fl = display.FileLinks(td) + actual = fl._repr_html_() + actual = actual.split('\n') + actual.sort() + # the links should always have forward slashes, even on windows, so replace + # backslashes with forward slashes here + expected = ["%s/
" % td, + "  %s
" %\ + (tf2.name.replace("\\","/"),split(tf2.name)[1]), + "  %s
" %\ + (tf1.name.replace("\\","/"),split(tf1.name)[1])] + expected.sort() + # We compare the sorted list of links here as that's more reliable + nt.assert_equal(actual,expected) + +def test_existing_path_FileLinks_alt_formatter(): + """FileLinks: Calling _repr_html_ functions as expected w/ an alt formatter + """ + td = mkdtemp() + tf1 = NamedTemporaryFile(dir=td) + tf2 = NamedTemporaryFile(dir=td) + def fake_formatter(dirname,fnames,included_suffixes): + return ["hello","world"] + fl = display.FileLinks(td,notebook_display_formatter=fake_formatter) + actual = fl._repr_html_() + actual = actual.split('\n') + actual.sort() + expected = ["hello","world"] + expected.sort() + # We compare the sorted list of links here as that's more reliable + nt.assert_equal(actual,expected) + +def test_existing_path_FileLinks_repr(): + """FileLinks: Calling repr() functions as expected on existing directory """ + td = mkdtemp() + tf1 = NamedTemporaryFile(dir=td) + tf2 = NamedTemporaryFile(dir=td) + fl = display.FileLinks(td) + actual = repr(fl) + actual = actual.split('\n') + actual.sort() + expected = ['%s/' % td, ' %s' % split(tf1.name)[1],' %s' % split(tf2.name)[1]] + expected.sort() + # We compare the sorted list of links here as that's more reliable + nt.assert_equal(actual,expected) + +def test_existing_path_FileLinks_repr_alt_formatter(): + """FileLinks: Calling repr() functions as expected w/ alt formatter + """ + td = mkdtemp() + tf1 = NamedTemporaryFile(dir=td) + tf2 = NamedTemporaryFile(dir=td) + def fake_formatter(dirname,fnames,included_suffixes): + return ["hello","world"] + fl = display.FileLinks(td,terminal_display_formatter=fake_formatter) + actual = repr(fl) + actual = actual.split('\n') + actual.sort() + expected = ["hello","world"] + expected.sort() + # We compare the sorted list of links here as that's more reliable + nt.assert_equal(actual,expected) + +def test_error_on_file_to_FileLinks(): + """FileLinks: Raises error when passed file + """ + td = mkdtemp() + tf1 = NamedTemporaryFile(dir=td) + nt.assert_raises(ValueError,display.FileLinks,tf1.name) + +def test_recursive_FileLinks(): + """FileLinks: Does not recurse when recursive=False + """ + td = mkdtemp() + tf = NamedTemporaryFile(dir=td) + subtd = mkdtemp(dir=td) + subtf = NamedTemporaryFile(dir=subtd) + fl = display.FileLinks(td) + actual = str(fl) + actual = actual.split('\n') + nt.assert_equal(len(actual), 4, actual) + fl = display.FileLinks(td, recursive=False) + actual = str(fl) + actual = actual.split('\n') + nt.assert_equal(len(actual), 2, actual) + +def test_audio_from_file(): + path = pjoin(dirname(__file__), 'test.wav') + display.Audio(filename=path) + +class TestAudioDataWithNumpy(TestCase): + + @skipif_not_numpy + def test_audio_from_numpy_array(self): + test_tone = get_test_tone() + audio = display.Audio(test_tone, rate=44100) + nt.assert_equal(len(read_wav(audio.data)), len(test_tone)) + + @skipif_not_numpy + def test_audio_from_list(self): + test_tone = get_test_tone() + audio = display.Audio(list(test_tone), rate=44100) + nt.assert_equal(len(read_wav(audio.data)), len(test_tone)) + + @skipif_not_numpy + def test_audio_from_numpy_array_without_rate_raises(self): + nt.assert_raises(ValueError, display.Audio, get_test_tone()) + + @skipif_not_numpy + def test_audio_data_normalization(self): + expected_max_value = numpy.iinfo(numpy.int16).max + for scale in [1, 0.5, 2]: + audio = display.Audio(get_test_tone(scale), rate=44100) + actual_max_value = numpy.max(numpy.abs(read_wav(audio.data))) + nt.assert_equal(actual_max_value, expected_max_value) + + @skipif_not_numpy + def test_audio_data_without_normalization(self): + max_int16 = numpy.iinfo(numpy.int16).max + for scale in [1, 0.5, 0.2]: + test_tone = get_test_tone(scale) + test_tone_max_abs = numpy.max(numpy.abs(test_tone)) + expected_max_value = int(max_int16 * test_tone_max_abs) + audio = display.Audio(test_tone, rate=44100, normalize=False) + actual_max_value = numpy.max(numpy.abs(read_wav(audio.data))) + nt.assert_equal(actual_max_value, expected_max_value) + + def test_audio_data_without_normalization_raises_for_invalid_data(self): + nt.assert_raises( + ValueError, + lambda: display.Audio([1.001], rate=44100, normalize=False)) + nt.assert_raises( + ValueError, + lambda: display.Audio([-1.001], rate=44100, normalize=False)) + +def simulate_numpy_not_installed(): + try: + import numpy + return mock.patch('numpy.array', mock.MagicMock(side_effect=ImportError)) + except ModuleNotFoundError: + return lambda x:x + +@simulate_numpy_not_installed() +class TestAudioDataWithoutNumpy(TestAudioDataWithNumpy): + # All tests from `TestAudioDataWithNumpy` are inherited. + + @skipif_not_numpy + def test_audio_raises_for_nested_list(self): + stereo_signal = [list(get_test_tone())] * 2 + nt.assert_raises( + TypeError, + lambda: display.Audio(stereo_signal, rate=44100)) + +@skipif_not_numpy +def get_test_tone(scale=1): + return numpy.sin(2 * numpy.pi * 440 * numpy.linspace(0, 1, 44100)) * scale + +def read_wav(data): + with wave.open(BytesIO(data)) as wave_file: + wave_data = wave_file.readframes(wave_file.getnframes()) + num_samples = wave_file.getnframes() * wave_file.getnchannels() + return struct.unpack('<%sh' % num_samples, wave_data) + +def test_code_from_file(): + c = display.Code(filename=__file__) + assert c._repr_html_().startswith('') + + # Italic, bold and underline. + HTML('...') + HTML('...') + HTML('...') + + All HTML elements become available as a "class" in the style sheet. + E.g. ``...`` can be styled, by setting a style for + ``username``. + """ + + def __init__(self, value: str) -> None: + self.value = value + document = minidom.parseString("%s" % (value,)) + + result: StyleAndTextTuples = [] + name_stack: List[str] = [] + fg_stack: List[str] = [] + bg_stack: List[str] = [] + + def get_current_style() -> str: + " Build style string for current node. " + parts = [] + if name_stack: + parts.append("class:" + ",".join(name_stack)) + + if fg_stack: + parts.append("fg:" + fg_stack[-1]) + if bg_stack: + parts.append("bg:" + bg_stack[-1]) + return " ".join(parts) + + def process_node(node: Any) -> None: + " Process node recursively. " + for child in node.childNodes: + if child.nodeType == child.TEXT_NODE: + result.append((get_current_style(), child.data)) + else: + add_to_name_stack = child.nodeName not in ( + "#document", + "html-root", + "style", + ) + fg = bg = "" + + for k, v in child.attributes.items(): + if k == "fg": + fg = v + if k == "bg": + bg = v + if k == "color": + fg = v # Alias for 'fg'. + + # Check for spaces in attributes. This would result in + # invalid style strings otherwise. + if " " in fg: + raise ValueError('"fg" attribute contains a space.') + if " " in bg: + raise ValueError('"bg" attribute contains a space.') + + if add_to_name_stack: + name_stack.append(child.nodeName) + if fg: + fg_stack.append(fg) + if bg: + bg_stack.append(bg) + + process_node(child) + + if add_to_name_stack: + name_stack.pop() + if fg: + fg_stack.pop() + if bg: + bg_stack.pop() + + process_node(document) + + self.formatted_text = FormattedText(result) + + def __repr__(self) -> str: + return "HTML(%r)" % (self.value,) + + def __pt_formatted_text__(self) -> StyleAndTextTuples: + return self.formatted_text + + def format(self, *args: object, **kwargs: object) -> "HTML": + """ + Like `str.format`, but make sure that the arguments are properly + escaped. + """ + # Escape all the arguments. + escaped_args = [html_escape(a) for a in args] + escaped_kwargs = {k: html_escape(v) for k, v in kwargs.items()} + + return HTML(self.value.format(*escaped_args, **escaped_kwargs)) + + def __mod__(self, value: Union[object, Tuple[object, ...]]) -> "HTML": + """ + HTML('%s') % value + """ + if not isinstance(value, tuple): + value = (value,) + + value = tuple(html_escape(i) for i in value) + return HTML(self.value % value) + + +def html_escape(text: object) -> str: + # The string interpolation functions also take integers and other types. + # Convert to string first. + if not isinstance(text, str): + text = "{}".format(text) + + return ( + text.replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace('"', """) + ) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/formatted_text/pygments.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/formatted_text/pygments.py new file mode 100644 index 00000000..dd16f0ef --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/formatted_text/pygments.py @@ -0,0 +1,30 @@ +from typing import TYPE_CHECKING, List, Tuple + +from prompt_toolkit.styles.pygments import pygments_token_to_classname + +from .base import StyleAndTextTuples + +if TYPE_CHECKING: + from pygments.token import Token + +__all__ = [ + "PygmentsTokens", +] + + +class PygmentsTokens: + """ + Turn a pygments token list into a list of prompt_toolkit text fragments + (``(style_str, text)`` tuples). + """ + + def __init__(self, token_list: List[Tuple["Token", str]]) -> None: + self.token_list = token_list + + def __pt_formatted_text__(self) -> StyleAndTextTuples: + result: StyleAndTextTuples = [] + + for token, text in self.token_list: + result.append(("class:" + pygments_token_to_classname(token), text)) + + return result diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/formatted_text/utils.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/formatted_text/utils.py new file mode 100644 index 00000000..7d48762d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/formatted_text/utils.py @@ -0,0 +1,85 @@ +""" +Utilities for manipulating formatted text. + +When ``to_formatted_text`` has been called, we get a list of ``(style, text)`` +tuples. This file contains functions for manipulating such a list. +""" +from typing import Iterable, cast + +from prompt_toolkit.utils import get_cwidth + +from .base import OneStyleAndTextTuple, StyleAndTextTuples + +__all__ = [ + "fragment_list_len", + "fragment_list_width", + "fragment_list_to_text", + "split_lines", +] + + +def fragment_list_len(fragments: StyleAndTextTuples) -> int: + """ + Return the amount of characters in this text fragment list. + + :param fragments: List of ``(style_str, text)`` or + ``(style_str, text, mouse_handler)`` tuples. + """ + ZeroWidthEscape = "[ZeroWidthEscape]" + return sum(len(item[1]) for item in fragments if ZeroWidthEscape not in item[0]) + + +def fragment_list_width(fragments: StyleAndTextTuples) -> int: + """ + Return the character width of this text fragment list. + (Take double width characters into account.) + + :param fragments: List of ``(style_str, text)`` or + ``(style_str, text, mouse_handler)`` tuples. + """ + ZeroWidthEscape = "[ZeroWidthEscape]" + return sum( + get_cwidth(c) + for item in fragments + for c in item[1] + if ZeroWidthEscape not in item[0] + ) + + +def fragment_list_to_text(fragments: StyleAndTextTuples) -> str: + """ + Concatenate all the text parts again. + + :param fragments: List of ``(style_str, text)`` or + ``(style_str, text, mouse_handler)`` tuples. + """ + ZeroWidthEscape = "[ZeroWidthEscape]" + return "".join(item[1] for item in fragments if ZeroWidthEscape not in item[0]) + + +def split_lines(fragments: StyleAndTextTuples) -> Iterable[StyleAndTextTuples]: + """ + Take a single list of (style_str, text) tuples and yield one such list for each + line. Just like str.split, this will yield at least one item. + + :param fragments: List of (style_str, text) or (style_str, text, mouse_handler) + tuples. + """ + line: StyleAndTextTuples = [] + + for style, string, *mouse_handler in fragments: + parts = string.split("\n") + + for part in parts[:-1]: + if part: + line.append(cast(OneStyleAndTextTuple, (style, part, *mouse_handler))) + yield line + line = [] + + line.append(cast(OneStyleAndTextTuple, (style, parts[-1], *mouse_handler))) + + # Always yield the last line, even when this is an empty line. This ensures + # that when `fragments` ends with a newline character, an additional empty + # line is yielded. (Otherwise, there's no way to differentiate between the + # cases where `fragments` does and doesn't end with a newline.) + yield line diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/history.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/history.py new file mode 100644 index 00000000..72acec9d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/history.py @@ -0,0 +1,232 @@ +""" +Implementations for the history of a `Buffer`. + +NOTE: Notice that there is no `DynamicHistory`. This doesn't work well, because + the `Buffer` needs to be able to attach an event handler to the event + when a history entry is loaded. This loading can be done asynchronously + and making the history swappable would probably break this. +""" +import datetime +import os +from abc import ABCMeta, abstractmethod +from threading import Thread +from typing import Callable, Iterable, List, Optional + +__all__ = [ + "History", + "ThreadedHistory", + "DummyHistory", + "FileHistory", + "InMemoryHistory", +] + + +class History(metaclass=ABCMeta): + """ + Base ``History`` class. + + This also includes abstract methods for loading/storing history. + """ + + def __init__(self) -> None: + # In memory storage for strings. + self._loaded = False + self._loaded_strings: List[str] = [] + + # + # Methods expected by `Buffer`. + # + + def load(self, item_loaded_callback: Callable[[str], None]) -> None: + """ + Load the history and call the callback for every entry in the history. + + XXX: The callback can be called from another thread, which happens in + case of `ThreadedHistory`. + + We can't assume that an asyncio event loop is running, and + schedule the insertion into the `Buffer` using the event loop. + + The reason is that the creation of the :class:`.History` object as + well as the start of the loading happens *before* + `Application.run()` is called, and it can continue even after + `Application.run()` terminates. (Which is useful to have a + complete history during the next prompt.) + + Calling `get_event_loop()` right here is also not guaranteed to + return the same event loop which is used in `Application.run`, + because a new event loop can be created during the `run`. This is + useful in Python REPLs, where we want to use one event loop for + the prompt, and have another one active during the `eval` of the + commands. (Otherwise, the user can schedule a while/true loop and + freeze the UI.) + """ + if self._loaded: + for item in self._loaded_strings[::-1]: + item_loaded_callback(item) + return + + try: + for item in self.load_history_strings(): + self._loaded_strings.insert(0, item) + item_loaded_callback(item) + finally: + self._loaded = True + + def get_strings(self) -> List[str]: + """ + Get the strings from the history that are loaded so far. + """ + return self._loaded_strings + + def append_string(self, string: str) -> None: + " Add string to the history. " + self._loaded_strings.append(string) + self.store_string(string) + + # + # Implementation for specific backends. + # + + @abstractmethod + def load_history_strings(self) -> Iterable[str]: + """ + This should be a generator that yields `str` instances. + + It should yield the most recent items first, because they are the most + important. (The history can already be used, even when it's only + partially loaded.) + """ + while False: + yield + + @abstractmethod + def store_string(self, string: str) -> None: + """ + Store the string in persistent storage. + """ + + +class ThreadedHistory(History): + """ + Wrapper that runs the `load_history_strings` generator in a thread. + + Use this to increase the start-up time of prompt_toolkit applications. + History entries are available as soon as they are loaded. We don't have to + wait for everything to be loaded. + """ + + def __init__(self, history: History) -> None: + self.history = history + self._load_thread: Optional[Thread] = None + self._item_loaded_callbacks: List[Callable[[str], None]] = [] + super().__init__() + + def load(self, item_loaded_callback: Callable[[str], None]) -> None: + self._item_loaded_callbacks.append(item_loaded_callback) + + # Start the load thread, if we don't have a thread yet. + if not self._load_thread: + + def call_all_callbacks(item: str) -> None: + for cb in self._item_loaded_callbacks: + cb(item) + + self._load_thread = Thread( + target=self.history.load, args=(call_all_callbacks,) + ) + self._load_thread.daemon = True + self._load_thread.start() + + def get_strings(self) -> List[str]: + return self.history.get_strings() + + def append_string(self, string: str) -> None: + self.history.append_string(string) + + # All of the following are proxied to `self.history`. + + def load_history_strings(self) -> Iterable[str]: + return self.history.load_history_strings() + + def store_string(self, string: str) -> None: + self.history.store_string(string) + + def __repr__(self) -> str: + return "ThreadedHistory(%r)" % (self.history,) + + +class InMemoryHistory(History): + """ + :class:`.History` class that keeps a list of all strings in memory. + """ + + def load_history_strings(self) -> Iterable[str]: + return [] + + def store_string(self, string: str) -> None: + pass + + +class DummyHistory(History): + """ + :class:`.History` object that doesn't remember anything. + """ + + def load_history_strings(self) -> Iterable[str]: + return [] + + def store_string(self, string: str) -> None: + pass + + def append_string(self, string: str) -> None: + # Don't remember this. + pass + + +class FileHistory(History): + """ + :class:`.History` class that stores all strings in a file. + """ + + def __init__(self, filename: str) -> None: + self.filename = filename + super(FileHistory, self).__init__() + + def load_history_strings(self) -> Iterable[str]: + strings: List[str] = [] + lines: List[str] = [] + + def add() -> None: + if lines: + # Join and drop trailing newline. + string = "".join(lines)[:-1] + + strings.append(string) + + if os.path.exists(self.filename): + with open(self.filename, "rb") as f: + for line_bytes in f: + line = line_bytes.decode("utf-8") + + if line.startswith("+"): + lines.append(line[1:]) + else: + add() + lines = [] + + add() + + # Reverse the order, because newest items have to go first. + return reversed(strings) + + def store_string(self, string: str) -> None: + # Save to file. + with open(self.filename, "ab") as f: + + def write(t: str) -> None: + f.write(t.encode("utf-8")) + + write("\n# %s\n" % datetime.datetime.now()) + for line in string.split("\n"): + write("+%s\n" % line) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__init__.py new file mode 100644 index 00000000..421d4ccd --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__init__.py @@ -0,0 +1,11 @@ +from .base import DummyInput, Input +from .defaults import create_input, create_pipe_input + +__all__ = [ + # Base. + "Input", + "DummyInput", + # Defaults. + "create_input", + "create_pipe_input", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..ae32a7f6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/ansi_escape_sequences.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/ansi_escape_sequences.cpython-38.pyc new file mode 100644 index 00000000..1d7b9407 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/ansi_escape_sequences.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/base.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/base.cpython-38.pyc new file mode 100644 index 00000000..70afa10e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/base.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/defaults.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/defaults.cpython-38.pyc new file mode 100644 index 00000000..ce9e312d Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/defaults.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/posix_pipe.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/posix_pipe.cpython-38.pyc new file mode 100644 index 00000000..60e23e0c Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/posix_pipe.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/posix_utils.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/posix_utils.cpython-38.pyc new file mode 100644 index 00000000..caba89d1 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/posix_utils.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/typeahead.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/typeahead.cpython-38.pyc new file mode 100644 index 00000000..d1225da0 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/typeahead.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/vt100.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/vt100.cpython-38.pyc new file mode 100644 index 00000000..c8e0f430 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/vt100.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/vt100_parser.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/vt100_parser.cpython-38.pyc new file mode 100644 index 00000000..2a5e8536 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/vt100_parser.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/win32.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/win32.cpython-38.pyc new file mode 100644 index 00000000..5d69ed07 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/win32.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/win32_pipe.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/win32_pipe.cpython-38.pyc new file mode 100644 index 00000000..8783a9b7 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/__pycache__/win32_pipe.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/ansi_escape_sequences.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/ansi_escape_sequences.py new file mode 100644 index 00000000..4589fbf9 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/ansi_escape_sequences.py @@ -0,0 +1,331 @@ +""" +Mappings from VT100 (ANSI) escape sequences to the corresponding prompt_toolkit +keys. + +We are not using the terminfo/termcap databases to detect the ANSI escape +sequences for the input. Instead, we recognize 99% of the most common +sequences. This works well, because in practice, every modern terminal is +mostly Xterm compatible. + +Some useful docs: +- Mintty: https://github.com/mintty/mintty/blob/master/wiki/Keycodes.md +""" +from typing import Dict, Tuple, Union + +from ..keys import Keys + +__all__ = [ + "ANSI_SEQUENCES", + "REVERSE_ANSI_SEQUENCES", +] + +# Mapping of vt100 escape codes to Keys. +ANSI_SEQUENCES: Dict[str, Union[Keys, Tuple[Keys, ...]]] = { + # Control keys. + "\x00": Keys.ControlAt, # Control-At (Also for Ctrl-Space) + "\x01": Keys.ControlA, # Control-A (home) + "\x02": Keys.ControlB, # Control-B (emacs cursor left) + "\x03": Keys.ControlC, # Control-C (interrupt) + "\x04": Keys.ControlD, # Control-D (exit) + "\x05": Keys.ControlE, # Control-E (end) + "\x06": Keys.ControlF, # Control-F (cursor forward) + "\x07": Keys.ControlG, # Control-G + "\x08": Keys.ControlH, # Control-H (8) (Identical to '\b') + "\x09": Keys.ControlI, # Control-I (9) (Identical to '\t') + "\x0a": Keys.ControlJ, # Control-J (10) (Identical to '\n') + "\x0b": Keys.ControlK, # Control-K (delete until end of line; vertical tab) + "\x0c": Keys.ControlL, # Control-L (clear; form feed) + "\x0d": Keys.ControlM, # Control-M (13) (Identical to '\r') + "\x0e": Keys.ControlN, # Control-N (14) (history forward) + "\x0f": Keys.ControlO, # Control-O (15) + "\x10": Keys.ControlP, # Control-P (16) (history back) + "\x11": Keys.ControlQ, # Control-Q + "\x12": Keys.ControlR, # Control-R (18) (reverse search) + "\x13": Keys.ControlS, # Control-S (19) (forward search) + "\x14": Keys.ControlT, # Control-T + "\x15": Keys.ControlU, # Control-U + "\x16": Keys.ControlV, # Control-V + "\x17": Keys.ControlW, # Control-W + "\x18": Keys.ControlX, # Control-X + "\x19": Keys.ControlY, # Control-Y (25) + "\x1a": Keys.ControlZ, # Control-Z + "\x1b": Keys.Escape, # Also Control-[ + "\x9b": Keys.ShiftEscape, + "\x1c": Keys.ControlBackslash, # Both Control-\ (also Ctrl-| ) + "\x1d": Keys.ControlSquareClose, # Control-] + "\x1e": Keys.ControlCircumflex, # Control-^ + "\x1f": Keys.ControlUnderscore, # Control-underscore (Also for Ctrl-hyphen.) + # ASCII Delete (0x7f) + # Vt220 (and Linux terminal) send this when pressing backspace. We map this + # to ControlH, because that will make it easier to create key bindings that + # work everywhere, with the trade-off that it's no longer possible to + # handle backspace and control-h individually for the few terminals that + # support it. (Most terminals send ControlH when backspace is pressed.) + # See: http://www.ibb.net/~anne/keyboard.html + "\x7f": Keys.ControlH, + # -- + # Various + "\x1b[1~": Keys.Home, # tmux + "\x1b[2~": Keys.Insert, + "\x1b[3~": Keys.Delete, + "\x1b[4~": Keys.End, # tmux + "\x1b[5~": Keys.PageUp, + "\x1b[6~": Keys.PageDown, + "\x1b[7~": Keys.Home, # xrvt + "\x1b[8~": Keys.End, # xrvt + "\x1b[Z": Keys.BackTab, # shift + tab + # -- + # Function keys. + "\x1bOP": Keys.F1, + "\x1bOQ": Keys.F2, + "\x1bOR": Keys.F3, + "\x1bOS": Keys.F4, + "\x1b[[A": Keys.F1, # Linux console. + "\x1b[[B": Keys.F2, # Linux console. + "\x1b[[C": Keys.F3, # Linux console. + "\x1b[[D": Keys.F4, # Linux console. + "\x1b[[E": Keys.F5, # Linux console. + "\x1b[11~": Keys.F1, # rxvt-unicode + "\x1b[12~": Keys.F2, # rxvt-unicode + "\x1b[13~": Keys.F3, # rxvt-unicode + "\x1b[14~": Keys.F4, # rxvt-unicode + "\x1b[15~": Keys.F5, + "\x1b[17~": Keys.F6, + "\x1b[18~": Keys.F7, + "\x1b[19~": Keys.F8, + "\x1b[20~": Keys.F9, + "\x1b[21~": Keys.F10, + "\x1b[23~": Keys.F11, + "\x1b[24~": Keys.F12, + "\x1b[25~": Keys.F13, + "\x1b[26~": Keys.F14, + "\x1b[28~": Keys.F15, + "\x1b[29~": Keys.F16, + "\x1b[31~": Keys.F17, + "\x1b[32~": Keys.F18, + "\x1b[33~": Keys.F19, + "\x1b[34~": Keys.F20, + # Xterm + "\x1b[1;2P": Keys.F13, + "\x1b[1;2Q": Keys.F14, + # '\x1b[1;2R': Keys.F15, # Conflicts with CPR response. + "\x1b[1;2S": Keys.F16, + "\x1b[15;2~": Keys.F17, + "\x1b[17;2~": Keys.F18, + "\x1b[18;2~": Keys.F19, + "\x1b[19;2~": Keys.F20, + "\x1b[20;2~": Keys.F21, + "\x1b[21;2~": Keys.F22, + "\x1b[23;2~": Keys.F23, + "\x1b[24;2~": Keys.F24, + # -- + # Control + function keys. + "\x1b[1;5P": Keys.ControlF1, + "\x1b[1;5Q": Keys.ControlF2, + # "\x1b[1;5R": Keys.ControlF3, # Conflicts with CPR response. + "\x1b[1;5S": Keys.ControlF4, + "\x1b[15;5~": Keys.ControlF5, + "\x1b[17;5~": Keys.ControlF6, + "\x1b[18;5~": Keys.ControlF7, + "\x1b[19;5~": Keys.ControlF8, + "\x1b[20;5~": Keys.ControlF9, + "\x1b[21;5~": Keys.ControlF10, + "\x1b[23;5~": Keys.ControlF11, + "\x1b[24;5~": Keys.ControlF12, + "\x1b[1;6P": Keys.ControlF13, + "\x1b[1;6Q": Keys.ControlF14, + # "\x1b[1;6R": Keys.ControlF15, # Conflicts with CPR response. + "\x1b[1;6S": Keys.ControlF16, + "\x1b[15;6~": Keys.ControlF17, + "\x1b[17;6~": Keys.ControlF18, + "\x1b[18;6~": Keys.ControlF19, + "\x1b[19;6~": Keys.ControlF20, + "\x1b[20;6~": Keys.ControlF21, + "\x1b[21;6~": Keys.ControlF22, + "\x1b[23;6~": Keys.ControlF23, + "\x1b[24;6~": Keys.ControlF24, + # -- + # Tmux (Win32 subsystem) sends the following scroll events. + "\x1b[62~": Keys.ScrollUp, + "\x1b[63~": Keys.ScrollDown, + "\x1b[200~": Keys.BracketedPaste, # Start of bracketed paste. + # -- + # Sequences generated by numpad 5. Not sure what it means. (It doesn't + # appear in 'infocmp'. Just ignore. + "\x1b[E": Keys.Ignore, # Xterm. + "\x1b[G": Keys.Ignore, # Linux console. + # -- + # Meta/control/escape + pageup/pagedown/insert/delete. + "\x1b[3;2~": Keys.ShiftDelete, # xterm, gnome-terminal. + "\x1b[5;2~": Keys.ShiftPageUp, + "\x1b[6;2~": Keys.ShiftPageDown, + "\x1b[2;3~": (Keys.Escape, Keys.Insert), + "\x1b[3;3~": (Keys.Escape, Keys.Delete), + "\x1b[5;3~": (Keys.Escape, Keys.PageUp), + "\x1b[6;3~": (Keys.Escape, Keys.PageDown), + "\x1b[2;4~": (Keys.Escape, Keys.ShiftInsert), + "\x1b[3;4~": (Keys.Escape, Keys.ShiftDelete), + "\x1b[5;4~": (Keys.Escape, Keys.ShiftPageUp), + "\x1b[6;4~": (Keys.Escape, Keys.ShiftPageDown), + "\x1b[3;5~": Keys.ControlDelete, # xterm, gnome-terminal. + "\x1b[5;5~": Keys.ControlPageUp, + "\x1b[6;5~": Keys.ControlPageDown, + "\x1b[3;6~": Keys.ControlShiftDelete, + "\x1b[5;6~": Keys.ControlShiftPageUp, + "\x1b[6;6~": Keys.ControlShiftPageDown, + "\x1b[2;7~": (Keys.Escape, Keys.ControlInsert), + "\x1b[5;7~": (Keys.Escape, Keys.ControlPageDown), + "\x1b[6;7~": (Keys.Escape, Keys.ControlPageDown), + "\x1b[2;8~": (Keys.Escape, Keys.ControlShiftInsert), + "\x1b[5;8~": (Keys.Escape, Keys.ControlShiftPageDown), + "\x1b[6;8~": (Keys.Escape, Keys.ControlShiftPageDown), + # -- + # Arrows. + "\x1b[A": Keys.Up, + "\x1b[B": Keys.Down, + "\x1b[C": Keys.Right, + "\x1b[D": Keys.Left, + "\x1b[H": Keys.Home, + "\x1b[F": Keys.End, + # Tmux sends following keystrokes when control+arrow is pressed, but for + # Emacs ansi-term sends the same sequences for normal arrow keys. Consider + # it a normal arrow press, because that's more important. + "\x1bOA": Keys.Up, + "\x1bOB": Keys.Down, + "\x1bOC": Keys.Right, + "\x1bOD": Keys.Left, + "\x1bOF": Keys.End, + "\x1bOH": Keys.Home, + # Shift + arrows. + "\x1b[1;2A": Keys.ShiftUp, + "\x1b[1;2B": Keys.ShiftDown, + "\x1b[1;2C": Keys.ShiftRight, + "\x1b[1;2D": Keys.ShiftLeft, + "\x1b[1;2F": Keys.ShiftEnd, + "\x1b[1;2H": Keys.ShiftHome, + # Meta + arrow keys. Several terminals handle this differently. + # The following sequences are for xterm and gnome-terminal. + # (Iterm sends ESC followed by the normal arrow_up/down/left/right + # sequences, and the OSX Terminal sends ESCb and ESCf for "alt + # arrow_left" and "alt arrow_right." We don't handle these + # explicitly, in here, because would could not distinguish between + # pressing ESC (to go to Vi navigation mode), followed by just the + # 'b' or 'f' key. These combinations are handled in + # the input processor.) + "\x1b[1;3A": (Keys.Escape, Keys.Up), + "\x1b[1;3B": (Keys.Escape, Keys.Down), + "\x1b[1;3C": (Keys.Escape, Keys.Right), + "\x1b[1;3D": (Keys.Escape, Keys.Left), + "\x1b[1;3F": (Keys.Escape, Keys.End), + "\x1b[1;3H": (Keys.Escape, Keys.Home), + # Alt+shift+number. + "\x1b[1;4A": (Keys.Escape, Keys.ShiftDown), + "\x1b[1;4B": (Keys.Escape, Keys.ShiftUp), + "\x1b[1;4C": (Keys.Escape, Keys.ShiftRight), + "\x1b[1;4D": (Keys.Escape, Keys.ShiftLeft), + "\x1b[1;4F": (Keys.Escape, Keys.ShiftEnd), + "\x1b[1;4H": (Keys.Escape, Keys.ShiftHome), + # Control + arrows. + "\x1b[1;5A": Keys.ControlUp, # Cursor Mode + "\x1b[1;5B": Keys.ControlDown, # Cursor Mode + "\x1b[1;5C": Keys.ControlRight, # Cursor Mode + "\x1b[1;5D": Keys.ControlLeft, # Cursor Mode + "\x1b[1;5F": Keys.ControlEnd, + "\x1b[1;5H": Keys.ControlHome, + # Tmux sends following keystrokes when control+arrow is pressed, but for + # Emacs ansi-term sends the same sequences for normal arrow keys. Consider + # it a normal arrow press, because that's more important. + "\x1b[5A": Keys.ControlUp, + "\x1b[5B": Keys.ControlDown, + "\x1b[5C": Keys.ControlRight, + "\x1b[5D": Keys.ControlLeft, + "\x1bOc": Keys.ControlRight, # rxvt + "\x1bOd": Keys.ControlLeft, # rxvt + # Control + shift + arrows. + "\x1b[1;6A": Keys.ControlShiftDown, + "\x1b[1;6B": Keys.ControlShiftUp, + "\x1b[1;6C": Keys.ControlShiftRight, + "\x1b[1;6D": Keys.ControlShiftLeft, + "\x1b[1;6F": Keys.ControlShiftEnd, + "\x1b[1;6H": Keys.ControlShiftHome, + # Control + Meta + arrows. + "\x1b[1;7A": (Keys.Escape, Keys.ControlDown), + "\x1b[1;7B": (Keys.Escape, Keys.ControlUp), + "\x1b[1;7C": (Keys.Escape, Keys.ControlRight), + "\x1b[1;7D": (Keys.Escape, Keys.ControlLeft), + "\x1b[1;7F": (Keys.Escape, Keys.ControlEnd), + "\x1b[1;7H": (Keys.Escape, Keys.ControlHome), + # Meta + Shift + arrows. + "\x1b[1;8A": (Keys.Escape, Keys.ControlShiftDown), + "\x1b[1;8B": (Keys.Escape, Keys.ControlShiftUp), + "\x1b[1;8C": (Keys.Escape, Keys.ControlShiftRight), + "\x1b[1;8D": (Keys.Escape, Keys.ControlShiftLeft), + "\x1b[1;8F": (Keys.Escape, Keys.ControlShiftEnd), + "\x1b[1;8H": (Keys.Escape, Keys.ControlShiftHome), + # Meta + arrow on (some?) Macs when using iTerm defaults (see issue #483). + "\x1b[1;9A": (Keys.Escape, Keys.Up), + "\x1b[1;9B": (Keys.Escape, Keys.Down), + "\x1b[1;9C": (Keys.Escape, Keys.Right), + "\x1b[1;9D": (Keys.Escape, Keys.Left), + # -- + # Control/shift/meta + number in mintty. + # (c-2 will actually send c-@ and c-6 will send c-^.) + "\x1b[1;5p": Keys.Control0, + "\x1b[1;5q": Keys.Control1, + "\x1b[1;5r": Keys.Control2, + "\x1b[1;5s": Keys.Control3, + "\x1b[1;5t": Keys.Control4, + "\x1b[1;5u": Keys.Control5, + "\x1b[1;5v": Keys.Control6, + "\x1b[1;5w": Keys.Control7, + "\x1b[1;5x": Keys.Control8, + "\x1b[1;5y": Keys.Control9, + "\x1b[1;6p": Keys.ControlShift0, + "\x1b[1;6q": Keys.ControlShift1, + "\x1b[1;6r": Keys.ControlShift2, + "\x1b[1;6s": Keys.ControlShift3, + "\x1b[1;6t": Keys.ControlShift4, + "\x1b[1;6u": Keys.ControlShift5, + "\x1b[1;6v": Keys.ControlShift6, + "\x1b[1;6w": Keys.ControlShift7, + "\x1b[1;6x": Keys.ControlShift8, + "\x1b[1;6y": Keys.ControlShift9, + "\x1b[1;7p": (Keys.Escape, Keys.Control0), + "\x1b[1;7q": (Keys.Escape, Keys.Control1), + "\x1b[1;7r": (Keys.Escape, Keys.Control2), + "\x1b[1;7s": (Keys.Escape, Keys.Control3), + "\x1b[1;7t": (Keys.Escape, Keys.Control4), + "\x1b[1;7u": (Keys.Escape, Keys.Control5), + "\x1b[1;7v": (Keys.Escape, Keys.Control6), + "\x1b[1;7w": (Keys.Escape, Keys.Control7), + "\x1b[1;7x": (Keys.Escape, Keys.Control8), + "\x1b[1;7y": (Keys.Escape, Keys.Control9), + "\x1b[1;8p": (Keys.Escape, Keys.ControlShift0), + "\x1b[1;8q": (Keys.Escape, Keys.ControlShift1), + "\x1b[1;8r": (Keys.Escape, Keys.ControlShift2), + "\x1b[1;8s": (Keys.Escape, Keys.ControlShift3), + "\x1b[1;8t": (Keys.Escape, Keys.ControlShift4), + "\x1b[1;8u": (Keys.Escape, Keys.ControlShift5), + "\x1b[1;8v": (Keys.Escape, Keys.ControlShift6), + "\x1b[1;8w": (Keys.Escape, Keys.ControlShift7), + "\x1b[1;8x": (Keys.Escape, Keys.ControlShift8), + "\x1b[1;8y": (Keys.Escape, Keys.ControlShift9), +} + + +def _get_reverse_ansi_sequences() -> Dict[Keys, str]: + """ + Create a dictionary that maps prompt_toolkit keys back to the VT100 escape + sequences. + """ + result: Dict[Keys, str] = {} + + for sequence, key in ANSI_SEQUENCES.items(): + if not isinstance(key, tuple): + if key not in result: + result[key] = sequence + + return result + + +REVERSE_ANSI_SEQUENCES = _get_reverse_ansi_sequences() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/base.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/base.py new file mode 100644 index 00000000..e701aa90 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/base.py @@ -0,0 +1,137 @@ +""" +Abstraction of CLI Input. +""" +from abc import ABCMeta, abstractmethod, abstractproperty +from contextlib import contextmanager +from typing import Callable, ContextManager, Generator, List + +from prompt_toolkit.key_binding import KeyPress + +__all__ = [ + "Input", + "DummyInput", +] + + +class Input(metaclass=ABCMeta): + """ + Abstraction for any input. + + An instance of this class can be given to the constructor of a + :class:`~prompt_toolkit.application.Application` and will also be + passed to the :class:`~prompt_toolkit.eventloop.base.EventLoop`. + """ + + @abstractmethod + def fileno(self) -> int: + """ + Fileno for putting this in an event loop. + """ + + @abstractmethod + def typeahead_hash(self) -> str: + """ + Identifier for storing type ahead key presses. + """ + + @abstractmethod + def read_keys(self) -> List[KeyPress]: + """ + Return a list of Key objects which are read/parsed from the input. + """ + + def flush_keys(self) -> List[KeyPress]: + """ + Flush the underlying parser. and return the pending keys. + (Used for vt100 input.) + """ + return [] + + def flush(self) -> None: + " The event loop can call this when the input has to be flushed. " + pass + + @abstractproperty + def closed(self) -> bool: + " Should be true when the input stream is closed. " + return False + + @abstractmethod + def raw_mode(self) -> ContextManager[None]: + """ + Context manager that turns the input into raw mode. + """ + + @abstractmethod + def cooked_mode(self) -> ContextManager[None]: + """ + Context manager that turns the input into cooked mode. + """ + + @abstractmethod + def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]: + """ + Return a context manager that makes this input active in the current + event loop. + """ + + @abstractmethod + def detach(self) -> ContextManager[None]: + """ + Return a context manager that makes sure that this input is not active + in the current event loop. + """ + + def close(self) -> None: + " Close input. " + pass + + +class PipeInput(Input): + """ + Abstraction for pipe input. + """ + + @abstractmethod + def send_bytes(self, data: bytes) -> None: + """Feed byte string into the pipe""" + + @abstractmethod + def send_text(self, data: str) -> None: + """Feed a text string into the pipe""" + + +class DummyInput(Input): + """ + Input for use in a `DummyApplication` + """ + + def fileno(self) -> int: + raise NotImplementedError + + def typeahead_hash(self) -> str: + return "dummy-%s" % id(self) + + def read_keys(self) -> List[KeyPress]: + return [] + + @property + def closed(self) -> bool: + return True + + def raw_mode(self) -> ContextManager[None]: + return _dummy_context_manager() + + def cooked_mode(self) -> ContextManager[None]: + return _dummy_context_manager() + + def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]: + return _dummy_context_manager() + + def detach(self) -> ContextManager[None]: + return _dummy_context_manager() + + +@contextmanager +def _dummy_context_manager() -> Generator[None, None, None]: + yield diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/defaults.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/defaults.py new file mode 100644 index 00000000..27d28f44 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/defaults.py @@ -0,0 +1,58 @@ +import sys +from typing import Optional, TextIO + +from prompt_toolkit.utils import is_windows + +from .base import Input, PipeInput + +__all__ = [ + "create_input", + "create_pipe_input", +] + + +def create_input( + stdin: Optional[TextIO] = None, always_prefer_tty: bool = False +) -> Input: + """ + Create the appropriate `Input` object for the current os/environment. + + :param always_prefer_tty: When set, if `sys.stdin` is connected to a Unix + `pipe`, check whether `sys.stdout` or `sys.stderr` are connected to a + pseudo terminal. If so, open the tty for reading instead of reading for + `sys.stdin`. (We can open `stdout` or `stderr` for reading, this is how + a `$PAGER` works.) + """ + if is_windows(): + from .win32 import Win32Input + + return Win32Input(stdin or sys.stdin) + else: + from .vt100 import Vt100Input + + # If no input TextIO is given, use stdin/stdout. + if stdin is None: + stdin = sys.stdin + + if always_prefer_tty: + for io in [sys.stdin, sys.stdout, sys.stderr]: + if io.isatty(): + stdin = io + break + + return Vt100Input(stdin) + + +def create_pipe_input() -> PipeInput: + """ + Create an input pipe. + This is mostly useful for unit testing. + """ + if is_windows(): + from .win32_pipe import Win32PipeInput + + return Win32PipeInput() + else: + from .posix_pipe import PosixPipeInput + + return PosixPipeInput() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/posix_pipe.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/posix_pipe.py new file mode 100644 index 00000000..e01c4041 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/posix_pipe.py @@ -0,0 +1,72 @@ +import os +from typing import ContextManager, TextIO, cast + +from ..utils import DummyContext +from .base import PipeInput +from .vt100 import Vt100Input + +__all__ = [ + "PosixPipeInput", +] + + +class PosixPipeInput(Vt100Input, PipeInput): + """ + Input that is send through a pipe. + This is useful if we want to send the input programmatically into the + application. Mostly useful for unit testing. + + Usage:: + + input = PosixPipeInput() + input.send_text('inputdata') + """ + + _id = 0 + + def __init__(self, text: str = "") -> None: + self._r, self._w = os.pipe() + + class Stdin: + encoding = "utf-8" + + def isatty(stdin) -> bool: + return True + + def fileno(stdin) -> int: + return self._r + + super().__init__(cast(TextIO, Stdin())) + self.send_text(text) + + # Identifier for every PipeInput for the hash. + self.__class__._id += 1 + self._id = self.__class__._id + + def send_bytes(self, data: bytes) -> None: + os.write(self._w, data) + + def send_text(self, data: str) -> None: + " Send text to the input. " + os.write(self._w, data.encode("utf-8")) + + def raw_mode(self) -> ContextManager[None]: + return DummyContext() + + def cooked_mode(self) -> ContextManager[None]: + return DummyContext() + + def close(self) -> None: + " Close pipe fds. " + os.close(self._r) + os.close(self._w) + + # We should assign `None` to 'self._r` and 'self._w', + # The event loop still needs to know the the fileno for this input in order + # to properly remove it from the selectors. + + def typeahead_hash(self) -> str: + """ + This needs to be unique for every `PipeInput`. + """ + return "pipe-input-%s" % (self._id,) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/posix_utils.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/posix_utils.py new file mode 100644 index 00000000..f32f683f --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/posix_utils.py @@ -0,0 +1,95 @@ +import os +import select +from codecs import getincrementaldecoder + +__all__ = [ + "PosixStdinReader", +] + + +class PosixStdinReader: + """ + Wrapper around stdin which reads (nonblocking) the next available 1024 + bytes and decodes it. + + Note that you can't be sure that the input file is closed if the ``read`` + function returns an empty string. When ``errors=ignore`` is passed, + ``read`` can return an empty string if all malformed input was replaced by + an empty string. (We can't block here and wait for more input.) So, because + of that, check the ``closed`` attribute, to be sure that the file has been + closed. + + :param stdin_fd: File descriptor from which we read. + :param errors: Can be 'ignore', 'strict' or 'replace'. + On Python3, this can be 'surrogateescape', which is the default. + + 'surrogateescape' is preferred, because this allows us to transfer + unrecognised bytes to the key bindings. Some terminals, like lxterminal + and Guake, use the 'Mxx' notation to send mouse events, where each 'x' + can be any possible byte. + """ + + # By default, we want to 'ignore' errors here. The input stream can be full + # of junk. One occurrence of this that I had was when using iTerm2 on OS X, + # with "Option as Meta" checked (You should choose "Option as +Esc".) + + def __init__( + self, stdin_fd: int, errors: str = "surrogateescape", encoding: str = "utf-8" + ) -> None: + self.stdin_fd = stdin_fd + self.errors = errors + + # Create incremental decoder for decoding stdin. + # We can not just do `os.read(stdin.fileno(), 1024).decode('utf-8')`, because + # it could be that we are in the middle of a utf-8 byte sequence. + self._stdin_decoder_cls = getincrementaldecoder(encoding) + self._stdin_decoder = self._stdin_decoder_cls(errors=errors) + + #: True when there is nothing anymore to read. + self.closed = False + + def read(self, count: int = 1024) -> str: + # By default we choose a rather small chunk size, because reading + # big amounts of input at once, causes the event loop to process + # all these key bindings also at once without going back to the + # loop. This will make the application feel unresponsive. + """ + Read the input and return it as a string. + + Return the text. Note that this can return an empty string, even when + the input stream was not yet closed. This means that something went + wrong during the decoding. + """ + if self.closed: + return "" + + # Check whether there is some input to read. `os.read` would block + # otherwise. + # (Actually, the event loop is responsible to make sure that this + # function is only called when there is something to read, but for some + # reason this happens in certain situations.) + try: + if not select.select([self.stdin_fd], [], [], 0)[0]: + return "" + except IOError: + # Happens for instance when the file descriptor was closed. + # (We had this in ptterm, where the FD became ready, a callback was + # scheduled, but in the meantime another callback closed it already.) + self.closed = True + + # Note: the following works better than wrapping `self.stdin` like + # `codecs.getreader('utf-8')(stdin)` and doing `read(1)`. + # Somehow that causes some latency when the escape + # character is pressed. (Especially on combination with the `select`.) + try: + data = os.read(self.stdin_fd, count) + + # Nothing more to read, stream is closed. + if data == b"": + self.closed = True + return "" + except OSError: + # In case of SIGWINCH + data = b"" + + return self._stdin_decoder.decode(data) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/typeahead.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/typeahead.py new file mode 100644 index 00000000..a3d78664 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/typeahead.py @@ -0,0 +1,76 @@ +r""" +Store input key strokes if we did read more than was required. + +The input classes `Vt100Input` and `Win32Input` read the input text in chunks +of a few kilobytes. This means that if we read input from stdin, it could be +that we read a couple of lines (with newlines in between) at once. + +This creates a problem: potentially, we read too much from stdin. Sometimes +people paste several lines at once because they paste input in a REPL and +expect each input() call to process one line. Or they rely on type ahead +because the application can't keep up with the processing. + +However, we need to read input in bigger chunks. We need this mostly to support +pasting of larger chunks of text. We don't want everything to become +unresponsive because we: + - read one character; + - parse one character; + - call the key binding, which does a string operation with one character; + - and render the user interface. +Doing text operations on single characters is very inefficient in Python, so we +prefer to work on bigger chunks of text. This is why we have to read the input +in bigger chunks. + +Further, line buffering is also not an option, because it doesn't work well in +the architecture. We use lower level Posix APIs, that work better with the +event loop and so on. In fact, there is also nothing that defines that only \n +can accept the input, you could create a key binding for any key to accept the +input. + +To support type ahead, this module will store all the key strokes that were +read too early, so that they can be feed into to the next `prompt()` call or to +the next prompt_toolkit `Application`. +""" +from collections import defaultdict +from typing import Dict, List + +from ..key_binding import KeyPress +from .base import Input + +__all__ = [ + "store_typeahead", + "get_typeahead", + "clear_typeahead", +] + +_buffer: Dict[str, List[KeyPress]] = defaultdict(list) + + +def store_typeahead(input_obj: Input, key_presses: List[KeyPress]) -> None: + """ + Insert typeahead key presses for the given input. + """ + global _buffer + key = input_obj.typeahead_hash() + _buffer[key].extend(key_presses) + + +def get_typeahead(input_obj: Input) -> List[KeyPress]: + """ + Retrieve typeahead and reset the buffer for this input. + """ + global _buffer + + key = input_obj.typeahead_hash() + result = _buffer[key] + _buffer[key] = [] + return result + + +def clear_typeahead(input_obj: Input) -> None: + """ + Clear typeahead buffer. + """ + global _buffer + key = input_obj.typeahead_hash() + _buffer[key] = [] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/vt100.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/vt100.py new file mode 100644 index 00000000..ca06200a --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/vt100.py @@ -0,0 +1,308 @@ +import contextlib +import io +import os +import sys +import termios +import tty +from asyncio import AbstractEventLoop, get_event_loop +from typing import ( + Callable, + ContextManager, + Dict, + Generator, + List, + Optional, + Set, + TextIO, + Tuple, + Union, +) + +from ..key_binding import KeyPress +from .base import Input +from .posix_utils import PosixStdinReader +from .vt100_parser import Vt100Parser + +__all__ = [ + "Vt100Input", + "raw_mode", + "cooked_mode", +] + + +class Vt100Input(Input): + """ + Vt100 input for Posix systems. + (This uses a posix file descriptor that can be registered in the event loop.) + """ + + # For the error messages. Only display "Input is not a terminal" once per + # file descriptor. + _fds_not_a_terminal: Set[int] = set() + + def __init__(self, stdin: TextIO) -> None: + # Test whether the given input object has a file descriptor. + # (Idle reports stdin to be a TTY, but fileno() is not implemented.) + try: + # This should not raise, but can return 0. + stdin.fileno() + except io.UnsupportedOperation as e: + if "idlelib.run" in sys.modules: + raise io.UnsupportedOperation( + "Stdin is not a terminal. Running from Idle is not supported." + ) from e + else: + raise io.UnsupportedOperation("Stdin is not a terminal.") from e + + # Even when we have a file descriptor, it doesn't mean it's a TTY. + # Normally, this requires a real TTY device, but people instantiate + # this class often during unit tests as well. They use for instance + # pexpect to pipe data into an application. For convenience, we print + # an error message and go on. + isatty = stdin.isatty() + fd = stdin.fileno() + + if not isatty and fd not in Vt100Input._fds_not_a_terminal: + msg = "Warning: Input is not a terminal (fd=%r).\n" + sys.stderr.write(msg % fd) + sys.stderr.flush() + Vt100Input._fds_not_a_terminal.add(fd) + + # + self.stdin = stdin + + # Create a backup of the fileno(). We want this to work even if the + # underlying file is closed, so that `typeahead_hash()` keeps working. + self._fileno = stdin.fileno() + + self._buffer: List[KeyPress] = [] # Buffer to collect the Key objects. + self.stdin_reader = PosixStdinReader(self._fileno, encoding=stdin.encoding) + self.vt100_parser = Vt100Parser( + lambda key_press: self._buffer.append(key_press) + ) + + def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]: + """ + Return a context manager that makes this input active in the current + event loop. + """ + return _attached_input(self, input_ready_callback) + + def detach(self) -> ContextManager[None]: + """ + Return a context manager that makes sure that this input is not active + in the current event loop. + """ + return _detached_input(self) + + def read_keys(self) -> List[KeyPress]: + " Read list of KeyPress. " + # Read text from stdin. + data = self.stdin_reader.read() + + # Pass it through our vt100 parser. + self.vt100_parser.feed(data) + + # Return result. + result = self._buffer + self._buffer = [] + return result + + def flush_keys(self) -> List[KeyPress]: + """ + Flush pending keys and return them. + (Used for flushing the 'escape' key.) + """ + # Flush all pending keys. (This is most important to flush the vt100 + # 'Escape' key early when nothing else follows.) + self.vt100_parser.flush() + + # Return result. + result = self._buffer + self._buffer = [] + return result + + @property + def closed(self) -> bool: + return self.stdin_reader.closed + + def raw_mode(self) -> ContextManager[None]: + return raw_mode(self.stdin.fileno()) + + def cooked_mode(self) -> ContextManager[None]: + return cooked_mode(self.stdin.fileno()) + + def fileno(self) -> int: + return self.stdin.fileno() + + def typeahead_hash(self) -> str: + return "fd-%s" % (self._fileno,) + + +_current_callbacks: Dict[ + Tuple[AbstractEventLoop, int], Optional[Callable[[], None]] +] = {} # (loop, fd) -> current callback + + +@contextlib.contextmanager +def _attached_input( + input: Vt100Input, callback: Callable[[], None] +) -> Generator[None, None, None]: + """ + Context manager that makes this input active in the current event loop. + + :param input: :class:`~prompt_toolkit.input.Input` object. + :param callback: Called when the input is ready to read. + """ + loop = get_event_loop() + fd = input.fileno() + previous = _current_callbacks.get((loop, fd)) + + def callback_wrapper() -> None: + """Wrapper around the callback that already removes the reader when + the input is closed. Otherwise, we keep continuously calling this + callback, until we leave the context manager (which can happen a bit + later). This fixes issues when piping /dev/null into a prompt_toolkit + application.""" + if input.closed: + loop.remove_reader(fd) + callback() + + loop.add_reader(fd, callback_wrapper) + _current_callbacks[loop, fd] = callback + + try: + yield + finally: + loop.remove_reader(fd) + + if previous: + loop.add_reader(fd, previous) + _current_callbacks[loop, fd] = previous + else: + del _current_callbacks[loop, fd] + + +@contextlib.contextmanager +def _detached_input(input: Vt100Input) -> Generator[None, None, None]: + loop = get_event_loop() + fd = input.fileno() + previous = _current_callbacks.get((loop, fd)) + + if previous: + loop.remove_reader(fd) + _current_callbacks[loop, fd] = None + + try: + yield + finally: + if previous: + loop.add_reader(fd, previous) + _current_callbacks[loop, fd] = previous + + +class raw_mode: + """ + :: + + with raw_mode(stdin): + ''' the pseudo-terminal stdin is now used in raw mode ''' + + We ignore errors when executing `tcgetattr` fails. + """ + + # There are several reasons for ignoring errors: + # 1. To avoid the "Inappropriate ioctl for device" crash if somebody would + # execute this code (In a Python REPL, for instance): + # + # import os; f = open(os.devnull); os.dup2(f.fileno(), 0) + # + # The result is that the eventloop will stop correctly, because it has + # to logic to quit when stdin is closed. However, we should not fail at + # this point. See: + # https://github.com/jonathanslenders/python-prompt-toolkit/pull/393 + # https://github.com/jonathanslenders/python-prompt-toolkit/issues/392 + + # 2. Related, when stdin is an SSH pipe, and no full terminal was allocated. + # See: https://github.com/jonathanslenders/python-prompt-toolkit/pull/165 + def __init__(self, fileno: int) -> None: + self.fileno = fileno + self.attrs_before: Optional[List[Union[int, List[bytes]]]] + try: + self.attrs_before = termios.tcgetattr(fileno) + except termios.error: + # Ignore attribute errors. + self.attrs_before = None + + def __enter__(self) -> None: + # NOTE: On os X systems, using pty.setraw() fails. Therefor we are using this: + try: + newattr = termios.tcgetattr(self.fileno) + except termios.error: + pass + else: + newattr[tty.LFLAG] = self._patch_lflag(newattr[tty.LFLAG]) + newattr[tty.IFLAG] = self._patch_iflag(newattr[tty.IFLAG]) + + # VMIN defines the number of characters read at a time in + # non-canonical mode. It seems to default to 1 on Linux, but on + # Solaris and derived operating systems it defaults to 4. (This is + # because the VMIN slot is the same as the VEOF slot, which + # defaults to ASCII EOT = Ctrl-D = 4.) + newattr[tty.CC][termios.VMIN] = 1 # type: ignore + + termios.tcsetattr(self.fileno, termios.TCSANOW, newattr) + + # Put the terminal in cursor mode. (Instead of application mode.) + os.write(self.fileno, b"\x1b[?1l") + + @classmethod + def _patch_lflag(cls, attrs): + return attrs & ~(termios.ECHO | termios.ICANON | termios.IEXTEN | termios.ISIG) + + @classmethod + def _patch_iflag(cls, attrs): + return attrs & ~( + # Disable XON/XOFF flow control on output and input. + # (Don't capture Ctrl-S and Ctrl-Q.) + # Like executing: "stty -ixon." + termios.IXON + | termios.IXOFF + | + # Don't translate carriage return into newline on input. + termios.ICRNL + | termios.INLCR + | termios.IGNCR + ) + + def __exit__(self, *a: object) -> None: + if self.attrs_before is not None: + try: + termios.tcsetattr(self.fileno, termios.TCSANOW, self.attrs_before) + except termios.error: + pass + + # # Put the terminal in application mode. + # self._stdout.write('\x1b[?1h') + + +class cooked_mode(raw_mode): + """ + The opposite of ``raw_mode``, used when we need cooked mode inside a + `raw_mode` block. Used in `Application.run_in_terminal`.:: + + with cooked_mode(stdin): + ''' the pseudo-terminal stdin is now used in cooked mode. ''' + """ + + @classmethod + def _patch_lflag(cls, attrs): + return attrs | (termios.ECHO | termios.ICANON | termios.IEXTEN | termios.ISIG) + + @classmethod + def _patch_iflag(cls, attrs): + # Turn the ICRNL flag back on. (Without this, calling `input()` in + # run_in_terminal doesn't work and displays ^M instead. Ptpython + # evaluates commands using `run_in_terminal`, so it's important that + # they translate ^M back into ^J.) + return attrs | termios.ICRNL diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/vt100_parser.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/vt100_parser.py new file mode 100644 index 00000000..afca12b2 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/vt100_parser.py @@ -0,0 +1,247 @@ +""" +Parser for VT100 input stream. +""" +import re +from typing import Callable, Dict, Generator, Tuple, Union + +from ..key_binding.key_processor import KeyPress +from ..keys import Keys +from .ansi_escape_sequences import ANSI_SEQUENCES + +__all__ = [ + "Vt100Parser", +] + + +# Regex matching any CPR response +# (Note that we use '\Z' instead of '$', because '$' could include a trailing +# newline.) +_cpr_response_re = re.compile("^" + re.escape("\x1b[") + r"\d+;\d+R\Z") + +# Mouse events: +# Typical: "Esc[MaB*" Urxvt: "Esc[96;14;13M" and for Xterm SGR: "Esc[<64;85;12M" +_mouse_event_re = re.compile("^" + re.escape("\x1b[") + r"( bool: + # (hard coded) If this could be a prefix of a CPR response, return + # True. + if _cpr_response_prefix_re.match(prefix) or _mouse_event_prefix_re.match( + prefix + ): + result = True + else: + # If this could be a prefix of anything else, also return True. + result = any( + v + for k, v in ANSI_SEQUENCES.items() + if k.startswith(prefix) and k != prefix + ) + + self[prefix] = result + return result + + +_IS_PREFIX_OF_LONGER_MATCH_CACHE = _IsPrefixOfLongerMatchCache() + + +class Vt100Parser: + """ + Parser for VT100 input stream. + Data can be fed through the `feed` method and the given callback will be + called with KeyPress objects. + + :: + + def callback(key): + pass + i = Vt100Parser(callback) + i.feed('data\x01...') + + :attr feed_key_callback: Function that will be called when a key is parsed. + """ + + # Lookup table of ANSI escape sequences for a VT100 terminal + # Hint: in order to know what sequences your terminal writes to stdin, run + # "od -c" and start typing. + def __init__(self, feed_key_callback: Callable[[KeyPress], None]) -> None: + self.feed_key_callback = feed_key_callback + self.reset() + + def reset(self, request: bool = False) -> None: + self._in_bracketed_paste = False + self._start_parser() + + def _start_parser(self) -> None: + """ + Start the parser coroutine. + """ + self._input_parser = self._input_parser_generator() + self._input_parser.send(None) # type: ignore + + def _get_match(self, prefix: str) -> Union[None, Keys, Tuple[Keys, ...]]: + """ + Return the key (or keys) that maps to this prefix. + """ + # (hard coded) If we match a CPR response, return Keys.CPRResponse. + # (This one doesn't fit in the ANSI_SEQUENCES, because it contains + # integer variables.) + if _cpr_response_re.match(prefix): + return Keys.CPRResponse + + elif _mouse_event_re.match(prefix): + return Keys.Vt100MouseEvent + + # Otherwise, use the mappings. + try: + return ANSI_SEQUENCES[prefix] + except KeyError: + return None + + def _input_parser_generator(self) -> Generator[None, Union[str, _Flush], None]: + """ + Coroutine (state machine) for the input parser. + """ + prefix = "" + retry = False + flush = False + + while True: + flush = False + + if retry: + retry = False + else: + # Get next character. + c = yield + + if isinstance(c, _Flush): + flush = True + else: + prefix += c + + # If we have some data, check for matches. + if prefix: + is_prefix_of_longer_match = _IS_PREFIX_OF_LONGER_MATCH_CACHE[prefix] + match = self._get_match(prefix) + + # Exact matches found, call handlers.. + if (flush or not is_prefix_of_longer_match) and match: + self._call_handler(match, prefix) + prefix = "" + + # No exact match found. + elif (flush or not is_prefix_of_longer_match) and not match: + found = False + retry = True + + # Loop over the input, try the longest match first and + # shift. + for i in range(len(prefix), 0, -1): + match = self._get_match(prefix[:i]) + if match: + self._call_handler(match, prefix[:i]) + prefix = prefix[i:] + found = True + + if not found: + self._call_handler(prefix[0], prefix[0]) + prefix = prefix[1:] + + def _call_handler( + self, key: Union[str, Keys, Tuple[Keys, ...]], insert_text: str + ) -> None: + """ + Callback to handler. + """ + if isinstance(key, tuple): + # Received ANSI sequence that corresponds with multiple keys + # (probably alt+something). Handle keys individually, but only pass + # data payload to first KeyPress (so that we won't insert it + # multiple times). + for i, k in enumerate(key): + self._call_handler(k, insert_text if i == 0 else "") + else: + if key == Keys.BracketedPaste: + self._in_bracketed_paste = True + self._paste_buffer = "" + else: + self.feed_key_callback(KeyPress(key, insert_text)) + + def feed(self, data: str) -> None: + """ + Feed the input stream. + + :param data: Input string (unicode). + """ + # Handle bracketed paste. (We bypass the parser that matches all other + # key presses and keep reading input until we see the end mark.) + # This is much faster then parsing character by character. + if self._in_bracketed_paste: + self._paste_buffer += data + end_mark = "\x1b[201~" + + if end_mark in self._paste_buffer: + end_index = self._paste_buffer.index(end_mark) + + # Feed content to key bindings. + paste_content = self._paste_buffer[:end_index] + self.feed_key_callback(KeyPress(Keys.BracketedPaste, paste_content)) + + # Quit bracketed paste mode and handle remaining input. + self._in_bracketed_paste = False + remaining = self._paste_buffer[end_index + len(end_mark) :] + self._paste_buffer = "" + + self.feed(remaining) + + # Handle normal input character by character. + else: + for i, c in enumerate(data): + if self._in_bracketed_paste: + # Quit loop and process from this position when the parser + # entered bracketed paste. + self.feed(data[i:]) + break + else: + self._input_parser.send(c) + + def flush(self) -> None: + """ + Flush the buffer of the input stream. + + This will allow us to handle the escape key (or maybe meta) sooner. + The input received by the escape key is actually the same as the first + characters of e.g. Arrow-Up, so without knowing what follows the escape + sequence, we don't know whether escape has been pressed, or whether + it's something else. This flush function should be called after a + timeout, and processes everything that's still in the buffer as-is, so + without assuming any characters will follow. + """ + self._input_parser.send(_Flush()) + + def feed_and_flush(self, data: str) -> None: + """ + Wrapper around ``feed`` and ``flush``. + """ + self.feed(data) + self.flush() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/win32.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/win32.py new file mode 100644 index 00000000..3c557d23 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/win32.py @@ -0,0 +1,655 @@ +import msvcrt +import os +import sys +from abc import abstractmethod +from asyncio import get_event_loop +from contextlib import contextmanager +from ctypes import pointer, windll +from ctypes.wintypes import DWORD, HANDLE +from typing import Callable, ContextManager, Dict, Iterable, List, Optional, TextIO + +from prompt_toolkit.eventloop import run_in_executor_with_context +from prompt_toolkit.eventloop.win32 import create_win32_event, wait_for_handles +from prompt_toolkit.key_binding.key_processor import KeyPress +from prompt_toolkit.keys import Keys +from prompt_toolkit.mouse_events import MouseEventType +from prompt_toolkit.win32_types import ( + INPUT_RECORD, + KEY_EVENT_RECORD, + MOUSE_EVENT_RECORD, + STD_INPUT_HANDLE, + EventTypes, +) + +from .ansi_escape_sequences import REVERSE_ANSI_SEQUENCES +from .base import Input + +__all__ = [ + "Win32Input", + "ConsoleInputReader", + "raw_mode", + "cooked_mode", + "attach_win32_input", + "detach_win32_input", +] + + +class _Win32InputBase(Input): + """ + Base class for `Win32Input` and `Win32PipeInput`. + """ + + def __init__(self) -> None: + self.win32_handles = _Win32Handles() + + @property + @abstractmethod + def handle(self) -> HANDLE: + pass + + +class Win32Input(_Win32InputBase): + """ + `Input` class that reads from the Windows console. + """ + + def __init__(self, stdin: Optional[TextIO] = None) -> None: + super().__init__() + self.console_input_reader = ConsoleInputReader() + + def attach(self, input_ready_callback: Callable[[], None]) -> ContextManager[None]: + """ + Return a context manager that makes this input active in the current + event loop. + """ + return attach_win32_input(self, input_ready_callback) + + def detach(self) -> ContextManager[None]: + """ + Return a context manager that makes sure that this input is not active + in the current event loop. + """ + return detach_win32_input(self) + + def read_keys(self) -> List[KeyPress]: + return list(self.console_input_reader.read()) + + def flush(self) -> None: + pass + + @property + def closed(self) -> bool: + return False + + def raw_mode(self) -> ContextManager[None]: + return raw_mode() + + def cooked_mode(self) -> ContextManager[None]: + return cooked_mode() + + def fileno(self) -> int: + # The windows console doesn't depend on the file handle, so + # this is not used for the event loop (which uses the + # handle instead). But it's used in `Application.run_system_command` + # which opens a subprocess with a given stdin/stdout. + return sys.stdin.fileno() + + def typeahead_hash(self) -> str: + return "win32-input" + + def close(self) -> None: + self.console_input_reader.close() + + @property + def handle(self) -> HANDLE: + return self.console_input_reader.handle + + +class ConsoleInputReader: + """ + :param recognize_paste: When True, try to discover paste actions and turn + the event into a BracketedPaste. + """ + + # Keys with character data. + mappings = { + b"\x1b": Keys.Escape, + b"\x00": Keys.ControlSpace, # Control-Space (Also for Ctrl-@) + b"\x01": Keys.ControlA, # Control-A (home) + b"\x02": Keys.ControlB, # Control-B (emacs cursor left) + b"\x03": Keys.ControlC, # Control-C (interrupt) + b"\x04": Keys.ControlD, # Control-D (exit) + b"\x05": Keys.ControlE, # Control-E (end) + b"\x06": Keys.ControlF, # Control-F (cursor forward) + b"\x07": Keys.ControlG, # Control-G + b"\x08": Keys.ControlH, # Control-H (8) (Identical to '\b') + b"\x09": Keys.ControlI, # Control-I (9) (Identical to '\t') + b"\x0a": Keys.ControlJ, # Control-J (10) (Identical to '\n') + b"\x0b": Keys.ControlK, # Control-K (delete until end of line; vertical tab) + b"\x0c": Keys.ControlL, # Control-L (clear; form feed) + b"\x0d": Keys.ControlM, # Control-M (enter) + b"\x0e": Keys.ControlN, # Control-N (14) (history forward) + b"\x0f": Keys.ControlO, # Control-O (15) + b"\x10": Keys.ControlP, # Control-P (16) (history back) + b"\x11": Keys.ControlQ, # Control-Q + b"\x12": Keys.ControlR, # Control-R (18) (reverse search) + b"\x13": Keys.ControlS, # Control-S (19) (forward search) + b"\x14": Keys.ControlT, # Control-T + b"\x15": Keys.ControlU, # Control-U + b"\x16": Keys.ControlV, # Control-V + b"\x17": Keys.ControlW, # Control-W + b"\x18": Keys.ControlX, # Control-X + b"\x19": Keys.ControlY, # Control-Y (25) + b"\x1a": Keys.ControlZ, # Control-Z + b"\x1c": Keys.ControlBackslash, # Both Control-\ and Ctrl-| + b"\x1d": Keys.ControlSquareClose, # Control-] + b"\x1e": Keys.ControlCircumflex, # Control-^ + b"\x1f": Keys.ControlUnderscore, # Control-underscore (Also for Ctrl-hyphen.) + b"\x7f": Keys.Backspace, # (127) Backspace (ASCII Delete.) + } + + # Keys that don't carry character data. + keycodes = { + # Home/End + 33: Keys.PageUp, + 34: Keys.PageDown, + 35: Keys.End, + 36: Keys.Home, + # Arrows + 37: Keys.Left, + 38: Keys.Up, + 39: Keys.Right, + 40: Keys.Down, + 45: Keys.Insert, + 46: Keys.Delete, + # F-keys. + 112: Keys.F1, + 113: Keys.F2, + 114: Keys.F3, + 115: Keys.F4, + 116: Keys.F5, + 117: Keys.F6, + 118: Keys.F7, + 119: Keys.F8, + 120: Keys.F9, + 121: Keys.F10, + 122: Keys.F11, + 123: Keys.F12, + } + + LEFT_ALT_PRESSED = 0x0002 + RIGHT_ALT_PRESSED = 0x0001 + SHIFT_PRESSED = 0x0010 + LEFT_CTRL_PRESSED = 0x0008 + RIGHT_CTRL_PRESSED = 0x0004 + + def __init__(self, recognize_paste: bool = True) -> None: + self._fdcon = None + self.recognize_paste = recognize_paste + + # When stdin is a tty, use that handle, otherwise, create a handle from + # CONIN$. + self.handle: HANDLE + if sys.stdin.isatty(): + self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) + else: + self._fdcon = os.open("CONIN$", os.O_RDWR | os.O_BINARY) + self.handle = HANDLE(msvcrt.get_osfhandle(self._fdcon)) + + def close(self) -> None: + " Close fdcon. " + if self._fdcon is not None: + os.close(self._fdcon) + + def read(self) -> Iterable[KeyPress]: + """ + Return a list of `KeyPress` instances. It won't return anything when + there was nothing to read. (This function doesn't block.) + + http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961(v=vs.85).aspx + """ + max_count = 2048 # Max events to read at the same time. + + read = DWORD(0) + arrtype = INPUT_RECORD * max_count + input_records = arrtype() + + # Check whether there is some input to read. `ReadConsoleInputW` would + # block otherwise. + # (Actually, the event loop is responsible to make sure that this + # function is only called when there is something to read, but for some + # reason this happened in the asyncio_win32 loop, and it's better to be + # safe anyway.) + if not wait_for_handles([self.handle], timeout=0): + return + + # Get next batch of input event. + windll.kernel32.ReadConsoleInputW( + self.handle, pointer(input_records), max_count, pointer(read) + ) + + # First, get all the keys from the input buffer, in order to determine + # whether we should consider this a paste event or not. + all_keys = list(self._get_keys(read, input_records)) + + # Fill in 'data' for key presses. + all_keys = [self._insert_key_data(key) for key in all_keys] + + if self.recognize_paste and self._is_paste(all_keys): + gen = iter(all_keys) + k: Optional[KeyPress] + + for k in gen: + # Pasting: if the current key consists of text or \n, turn it + # into a BracketedPaste. + data = [] + while k and (isinstance(k.key, str) or k.key == Keys.ControlJ): + data.append(k.data) + try: + k = next(gen) + except StopIteration: + k = None + + if data: + yield KeyPress(Keys.BracketedPaste, "".join(data)) + if k is not None: + yield k + else: + for k2 in all_keys: + yield k2 + + def _insert_key_data(self, key_press: KeyPress) -> KeyPress: + """ + Insert KeyPress data, for vt100 compatibility. + """ + if key_press.data: + return key_press + + if isinstance(key_press.key, Keys): + data = REVERSE_ANSI_SEQUENCES.get(key_press.key, "") + else: + data = "" + + return KeyPress(key_press.key, data) + + def _get_keys(self, read, input_records): + """ + Generator that yields `KeyPress` objects from the input records. + """ + for i in range(read.value): + ir = input_records[i] + + # Get the right EventType from the EVENT_RECORD. + # (For some reason the Windows console application 'cmder' + # [http://gooseberrycreative.com/cmder/] can return '0' for + # ir.EventType. -- Just ignore that.) + if ir.EventType in EventTypes: + ev = getattr(ir.Event, EventTypes[ir.EventType]) + + # Process if this is a key event. (We also have mouse, menu and + # focus events.) + if type(ev) == KEY_EVENT_RECORD and ev.KeyDown: + for key_press in self._event_to_key_presses(ev): + yield key_press + + elif type(ev) == MOUSE_EVENT_RECORD: + for key_press in self._handle_mouse(ev): + yield key_press + + @staticmethod + def _is_paste(keys) -> bool: + """ + Return `True` when we should consider this list of keys as a paste + event. Pasted text on windows will be turned into a + `Keys.BracketedPaste` event. (It's not 100% correct, but it is probably + the best possible way to detect pasting of text and handle that + correctly.) + """ + # Consider paste when it contains at least one newline and at least one + # other character. + text_count = 0 + newline_count = 0 + + for k in keys: + if isinstance(k.key, str): + text_count += 1 + if k.key == Keys.ControlM: + newline_count += 1 + + return newline_count >= 1 and text_count > 1 + + def _event_to_key_presses(self, ev): + """ + For this `KEY_EVENT_RECORD`, return a list of `KeyPress` instances. + """ + assert type(ev) == KEY_EVENT_RECORD and ev.KeyDown + + result = None + + u_char = ev.uChar.UnicodeChar + ascii_char = u_char.encode("utf-8") + + # NOTE: We don't use `ev.uChar.AsciiChar`. That appears to be latin-1 + # encoded. See also: + # https://github.com/ipython/ipython/issues/10004 + # https://github.com/jonathanslenders/python-prompt-toolkit/issues/389 + + if u_char == "\x00": + if ev.VirtualKeyCode in self.keycodes: + result = KeyPress(self.keycodes[ev.VirtualKeyCode], "") + else: + if ascii_char in self.mappings: + if self.mappings[ascii_char] == Keys.ControlJ: + u_char = ( + "\n" # Windows sends \n, turn into \r for unix compatibility. + ) + result = KeyPress(self.mappings[ascii_char], u_char) + else: + result = KeyPress(u_char, u_char) + + # First we handle Shift-Control-Arrow/Home/End (need to do this first) + if ( + ( + ev.ControlKeyState & self.LEFT_CTRL_PRESSED + or ev.ControlKeyState & self.RIGHT_CTRL_PRESSED + ) + and ev.ControlKeyState & self.SHIFT_PRESSED + and result + ): + result.key = { + Keys.Left: Keys.ControlShiftLeft, + Keys.Right: Keys.ControlShiftRight, + Keys.Up: Keys.ControlShiftUp, + Keys.Down: Keys.ControlShiftDown, + Keys.Home: Keys.ControlShiftHome, + Keys.End: Keys.ControlShiftEnd, + Keys.Insert: Keys.ControlShiftInsert, + Keys.PageUp: Keys.ControlShiftPageUp, + Keys.PageDown: Keys.ControlShiftPageDown, + }.get(result.key, result.key) + + # Correctly handle Control-Arrow/Home/End and Control-Insert keys. + if ( + ev.ControlKeyState & self.LEFT_CTRL_PRESSED + or ev.ControlKeyState & self.RIGHT_CTRL_PRESSED + ) and result: + result.key = { + Keys.Left: Keys.ControlLeft, + Keys.Right: Keys.ControlRight, + Keys.Up: Keys.ControlUp, + Keys.Down: Keys.ControlDown, + Keys.Home: Keys.ControlHome, + Keys.End: Keys.ControlEnd, + Keys.Insert: Keys.ControlInsert, + Keys.PageUp: Keys.ControlPageUp, + Keys.PageDown: Keys.ControlPageDown, + }.get(result.key, result.key) + + # Turn 'Tab' into 'BackTab' when shift was pressed. + # Also handle other shift-key combination + if ev.ControlKeyState & self.SHIFT_PRESSED and result: + result.key = { + Keys.Tab: Keys.BackTab, + Keys.Left: Keys.ShiftLeft, + Keys.Right: Keys.ShiftRight, + Keys.Up: Keys.ShiftUp, + Keys.Down: Keys.ShiftDown, + Keys.Home: Keys.ShiftHome, + Keys.End: Keys.ShiftEnd, + Keys.Insert: Keys.ShiftInsert, + Keys.PageUp: Keys.ShiftPageUp, + Keys.PageDown: Keys.ShiftPageDown, + }.get(result.key, result.key) + + # Turn 'Space' into 'ControlSpace' when control was pressed. + if ( + ( + ev.ControlKeyState & self.LEFT_CTRL_PRESSED + or ev.ControlKeyState & self.RIGHT_CTRL_PRESSED + ) + and result + and result.data == " " + ): + result = KeyPress(Keys.ControlSpace, " ") + + # Turn Control-Enter into META-Enter. (On a vt100 terminal, we cannot + # detect this combination. But it's really practical on Windows.) + if ( + ( + ev.ControlKeyState & self.LEFT_CTRL_PRESSED + or ev.ControlKeyState & self.RIGHT_CTRL_PRESSED + ) + and result + and result.key == Keys.ControlJ + ): + return [KeyPress(Keys.Escape, ""), result] + + # Return result. If alt was pressed, prefix the result with an + # 'Escape' key, just like unix VT100 terminals do. + + # NOTE: Only replace the left alt with escape. The right alt key often + # acts as altgr and is used in many non US keyboard layouts for + # typing some special characters, like a backslash. We don't want + # all backslashes to be prefixed with escape. (Esc-\ has a + # meaning in E-macs, for instance.) + if result: + meta_pressed = ev.ControlKeyState & self.LEFT_ALT_PRESSED + + if meta_pressed: + return [KeyPress(Keys.Escape, ""), result] + else: + return [result] + + else: + return [] + + def _handle_mouse(self, ev): + """ + Handle mouse events. Return a list of KeyPress instances. + """ + FROM_LEFT_1ST_BUTTON_PRESSED = 0x1 + + result = [] + + # Check event type. + if ev.ButtonState == FROM_LEFT_1ST_BUTTON_PRESSED: + # On a key press, generate both the mouse down and up event. + for event_type in [MouseEventType.MOUSE_DOWN, MouseEventType.MOUSE_UP]: + data = ";".join( + [event_type.value, str(ev.MousePosition.X), str(ev.MousePosition.Y)] + ) + result.append(KeyPress(Keys.WindowsMouseEvent, data)) + + return result + + +class _Win32Handles: + """ + Utility to keep track of which handles are connectod to which callbacks. + + `add_win32_handle` starts a tiny event loop in another thread which waits + for the Win32 handle to become ready. When this happens, the callback will + be called in the current asyncio event loop using `call_soon_threadsafe`. + + `remove_win32_handle` will stop this tiny event loop. + + NOTE: We use this technique, so that we don't have to use the + `ProactorEventLoop` on Windows and we can wait for things like stdin + in a `SelectorEventLoop`. This is important, because our inputhook + mechanism (used by IPython), only works with the `SelectorEventLoop`. + """ + + def __init__(self) -> None: + self._handle_callbacks: Dict[int, Callable[[], None]] = {} + + # Windows Events that are triggered when we have to stop watching this + # handle. + self._remove_events: Dict[int, HANDLE] = {} + + def add_win32_handle(self, handle: HANDLE, callback: Callable[[], None]) -> None: + """ + Add a Win32 handle to the event loop. + """ + handle_value = handle.value + + if handle_value is None: + raise ValueError("Invalid handle.") + + # Make sure to remove a previous registered handler first. + self.remove_win32_handle(handle) + + loop = get_event_loop() + self._handle_callbacks[handle_value] = callback + + # Create remove event. + remove_event = create_win32_event() + self._remove_events[handle_value] = remove_event + + # Add reader. + def ready() -> None: + # Tell the callback that input's ready. + try: + callback() + finally: + run_in_executor_with_context(wait, loop=loop) + + # Wait for the input to become ready. + # (Use an executor for this, the Windows asyncio event loop doesn't + # allow us to wait for handles like stdin.) + def wait() -> None: + # Wait until either the handle becomes ready, or the remove event + # has been set. + result = wait_for_handles([remove_event, handle]) + + if result is remove_event: + windll.kernel32.CloseHandle(remove_event) + return + else: + loop.call_soon_threadsafe(ready) + + run_in_executor_with_context(wait, loop=loop) + + def remove_win32_handle(self, handle: HANDLE) -> Optional[Callable[[], None]]: + """ + Remove a Win32 handle from the event loop. + Return either the registered handler or `None`. + """ + if handle.value is None: + return None # Ignore. + + # Trigger remove events, so that the reader knows to stop. + try: + event = self._remove_events.pop(handle.value) + except KeyError: + pass + else: + windll.kernel32.SetEvent(event) + + try: + return self._handle_callbacks.pop(handle.value) + except KeyError: + return None + + +@contextmanager +def attach_win32_input(input: _Win32InputBase, callback: Callable[[], None]): + """ + Context manager that makes this input active in the current event loop. + + :param input: :class:`~prompt_toolkit.input.Input` object. + :param input_ready_callback: Called when the input is ready to read. + """ + win32_handles = input.win32_handles + handle = input.handle + + if handle.value is None: + raise ValueError("Invalid handle.") + + # Add reader. + previous_callback = win32_handles.remove_win32_handle(handle) + win32_handles.add_win32_handle(handle, callback) + + try: + yield + finally: + win32_handles.remove_win32_handle(handle) + + if previous_callback: + win32_handles.add_win32_handle(handle, previous_callback) + + +@contextmanager +def detach_win32_input(input: _Win32InputBase): + win32_handles = input.win32_handles + handle = input.handle + + if handle.value is None: + raise ValueError("Invalid handle.") + + previous_callback = win32_handles.remove_win32_handle(handle) + + try: + yield + finally: + if previous_callback: + win32_handles.add_win32_handle(handle, previous_callback) + + +class raw_mode: + """ + :: + + with raw_mode(stdin): + ''' the windows terminal is now in 'raw' mode. ''' + + The ``fileno`` attribute is ignored. This is to be compatible with the + `raw_input` method of `.vt100_input`. + """ + + def __init__(self, fileno=None): + self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) + + def __enter__(self): + # Remember original mode. + original_mode = DWORD() + windll.kernel32.GetConsoleMode(self.handle, pointer(original_mode)) + self.original_mode = original_mode + + self._patch() + + def _patch(self) -> None: + # Set raw + ENABLE_ECHO_INPUT = 0x0004 + ENABLE_LINE_INPUT = 0x0002 + ENABLE_PROCESSED_INPUT = 0x0001 + + windll.kernel32.SetConsoleMode( + self.handle, + self.original_mode.value + & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT), + ) + + def __exit__(self, *a: object) -> None: + # Restore original mode + windll.kernel32.SetConsoleMode(self.handle, self.original_mode) + + +class cooked_mode(raw_mode): + """ + :: + + with cooked_mode(stdin): + ''' The pseudo-terminal stdin is now used in cooked mode. ''' + """ + + def _patch(self) -> None: + # Set cooked. + ENABLE_ECHO_INPUT = 0x0004 + ENABLE_LINE_INPUT = 0x0002 + ENABLE_PROCESSED_INPUT = 0x0001 + + windll.kernel32.SetConsoleMode( + self.handle, + self.original_mode.value + | (ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT), + ) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/input/win32_pipe.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/win32_pipe.py new file mode 100644 index 00000000..892af2d1 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/input/win32_pipe.py @@ -0,0 +1,134 @@ +from ctypes import windll +from typing import Callable, ContextManager, List + +from prompt_toolkit.eventloop.win32 import create_win32_event + +from ..key_binding import KeyPress +from ..utils import DummyContext +from .base import PipeInput +from .vt100_parser import Vt100Parser +from .win32 import _Win32InputBase, attach_win32_input, detach_win32_input + +__all__ = ["Win32PipeInput"] + + +class Win32PipeInput(_Win32InputBase, PipeInput): + """ + This is an input pipe that works on Windows. + Text or bytes can be feed into the pipe, and key strokes can be read from + the pipe. This is useful if we want to send the input programmatically into + the application. Mostly useful for unit testing. + + Notice that even though it's Windows, we use vt100 escape sequences over + the pipe. + + Usage:: + + input = Win32PipeInput() + input.send_text('inputdata') + """ + + _id = 0 + + def __init__(self) -> None: + super().__init__() + # Event (handle) for registering this input in the event loop. + # This event is set when there is data available to read from the pipe. + # Note: We use this approach instead of using a regular pipe, like + # returned from `os.pipe()`, because making such a regular pipe + # non-blocking is tricky and this works really well. + self._event = create_win32_event() + + self._closed = False + + # Parser for incoming keys. + self._buffer: List[KeyPress] = [] # Buffer to collect the Key objects. + self.vt100_parser = Vt100Parser(lambda key: self._buffer.append(key)) + + # Identifier for every PipeInput for the hash. + self.__class__._id += 1 + self._id = self.__class__._id + + @property + def closed(self) -> bool: + return self._closed + + def fileno(self): + """ + The windows pipe doesn't depend on the file handle. + """ + raise NotImplementedError + + @property + def handle(self): + " The handle used for registering this pipe in the event loop. " + return self._event + + def attach(self, input_ready_callback: Callable) -> ContextManager[None]: + """ + Return a context manager that makes this input active in the current + event loop. + """ + return attach_win32_input(self, input_ready_callback) + + def detach(self) -> ContextManager[None]: + """ + Return a context manager that makes sure that this input is not active + in the current event loop. + """ + return detach_win32_input(self) + + def read_keys(self) -> List[KeyPress]: + " Read list of KeyPress. " + + # Return result. + result = self._buffer + self._buffer = [] + + # Reset event. + windll.kernel32.ResetEvent(self._event) + + return result + + def flush_keys(self) -> List[KeyPress]: + """ + Flush pending keys and return them. + (Used for flushing the 'escape' key.) + """ + # Flush all pending keys. (This is most important to flush the vt100 + # 'Escape' key early when nothing else follows.) + self.vt100_parser.flush() + + # Return result. + result = self._buffer + self._buffer = [] + return result + + def send_bytes(self, data: bytes) -> None: + " Send bytes to the input. " + self.send_text(data.decode("utf-8", "ignore")) + + def send_text(self, text: str) -> None: + " Send text to the input. " + # Pass it through our vt100 parser. + self.vt100_parser.feed(text) + + # Set event. + windll.kernel32.SetEvent(self._event) + + def raw_mode(self) -> ContextManager[None]: + return DummyContext() + + def cooked_mode(self) -> ContextManager[None]: + return DummyContext() + + def close(self) -> None: + " Close pipe handles. " + windll.kernel32.CloseHandle(self._event) + self._closed = True + + def typeahead_hash(self) -> str: + """ + This needs to be unique for every `PipeInput`. + """ + return "pipe-input-%s" % (self._id,) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__init__.py new file mode 100644 index 00000000..be105369 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__init__.py @@ -0,0 +1,20 @@ +from .key_bindings import ( + ConditionalKeyBindings, + DynamicKeyBindings, + KeyBindings, + KeyBindingsBase, + merge_key_bindings, +) +from .key_processor import KeyPress, KeyPressEvent + +__all__ = [ + # key_bindings. + "ConditionalKeyBindings", + "DynamicKeyBindings", + "KeyBindings", + "KeyBindingsBase", + "merge_key_bindings", + # key_processor + "KeyPress", + "KeyPressEvent", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..0a052c1a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/defaults.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/defaults.cpython-38.pyc new file mode 100644 index 00000000..e5df98e1 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/defaults.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/digraphs.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/digraphs.cpython-38.pyc new file mode 100644 index 00000000..b38b2d9e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/digraphs.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/emacs_state.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/emacs_state.cpython-38.pyc new file mode 100644 index 00000000..6b783b54 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/emacs_state.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/key_bindings.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/key_bindings.cpython-38.pyc new file mode 100644 index 00000000..9b1c07aa Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/key_bindings.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/key_processor.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/key_processor.cpython-38.pyc new file mode 100644 index 00000000..ddcc8f5f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/key_processor.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/vi_state.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/vi_state.cpython-38.pyc new file mode 100644 index 00000000..619f36cc Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/__pycache__/vi_state.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..87e290de Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/auto_suggest.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/auto_suggest.cpython-38.pyc new file mode 100644 index 00000000..e7438c4b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/auto_suggest.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/basic.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/basic.cpython-38.pyc new file mode 100644 index 00000000..33c8dbb4 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/basic.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/completion.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/completion.cpython-38.pyc new file mode 100644 index 00000000..b500e1b1 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/completion.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/cpr.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/cpr.cpython-38.pyc new file mode 100644 index 00000000..f5fe71ed Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/cpr.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/emacs.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/emacs.cpython-38.pyc new file mode 100644 index 00000000..5a6812f3 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/emacs.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/focus.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/focus.cpython-38.pyc new file mode 100644 index 00000000..33f5236b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/focus.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/mouse.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/mouse.cpython-38.pyc new file mode 100644 index 00000000..8fcd5e99 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/mouse.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/named_commands.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/named_commands.cpython-38.pyc new file mode 100644 index 00000000..88e43bb0 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/named_commands.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/open_in_editor.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/open_in_editor.cpython-38.pyc new file mode 100644 index 00000000..3f4790f5 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/open_in_editor.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/page_navigation.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/page_navigation.cpython-38.pyc new file mode 100644 index 00000000..ceaabe5f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/page_navigation.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/scroll.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/scroll.cpython-38.pyc new file mode 100644 index 00000000..336bdf43 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/scroll.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/search.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/search.cpython-38.pyc new file mode 100644 index 00000000..73f7ecfb Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/search.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/vi.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/vi.cpython-38.pyc new file mode 100644 index 00000000..57b71bf5 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/__pycache__/vi.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/auto_suggest.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/auto_suggest.py new file mode 100644 index 00000000..c016c068 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/auto_suggest.py @@ -0,0 +1,63 @@ +""" +Key bindings for auto suggestion (for fish-style auto suggestion). +""" +import re + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.filters import Condition, emacs_mode +from prompt_toolkit.key_binding.key_bindings import KeyBindings +from prompt_toolkit.key_binding.key_processor import KeyPressEvent + +__all__ = [ + "load_auto_suggest_bindings", +] + +E = KeyPressEvent + + +def load_auto_suggest_bindings() -> KeyBindings: + """ + Key bindings for accepting auto suggestion text. + + (This has to come after the Vi bindings, because they also have an + implementation for the "right arrow", but we really want the suggestion + binding when a suggestion is available.) + """ + key_bindings = KeyBindings() + handle = key_bindings.add + + @Condition + def suggestion_available() -> bool: + app = get_app() + return ( + app.current_buffer.suggestion is not None + and len(app.current_buffer.suggestion.text) > 0 + and app.current_buffer.document.is_cursor_at_the_end + ) + + @handle("c-f", filter=suggestion_available) + @handle("c-e", filter=suggestion_available) + @handle("right", filter=suggestion_available) + def _accept(event: E) -> None: + """ + Accept suggestion. + """ + b = event.current_buffer + suggestion = b.suggestion + + if suggestion: + b.insert_text(suggestion.text) + + @handle("escape", "f", filter=suggestion_available & emacs_mode) + def _fill(event: E) -> None: + """ + Fill partial suggestion. + """ + b = event.current_buffer + suggestion = b.suggestion + + if suggestion: + t = re.split(r"(\S+\s+)", suggestion.text) + b.insert_text(next(x for x in t if x)) + + return key_bindings diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/basic.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/basic.py new file mode 100644 index 00000000..56efe5d6 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/basic.py @@ -0,0 +1,252 @@ +# pylint: disable=function-redefined +from prompt_toolkit.application.current import get_app +from prompt_toolkit.filters import ( + Condition, + emacs_insert_mode, + has_selection, + in_paste_mode, + is_multiline, + vi_insert_mode, +) +from prompt_toolkit.key_binding.key_processor import KeyPress, KeyPressEvent +from prompt_toolkit.keys import Keys + +from ..key_bindings import KeyBindings +from .named_commands import get_by_name + +__all__ = [ + "load_basic_bindings", +] + +E = KeyPressEvent + + +def if_no_repeat(event: E) -> bool: + """Callable that returns True when the previous event was delivered to + another handler.""" + return not event.is_repeat + + +def load_basic_bindings() -> KeyBindings: + key_bindings = KeyBindings() + insert_mode = vi_insert_mode | emacs_insert_mode + handle = key_bindings.add + + @handle("c-a") + @handle("c-b") + @handle("c-c") + @handle("c-d") + @handle("c-e") + @handle("c-f") + @handle("c-g") + @handle("c-h") + @handle("c-i") + @handle("c-j") + @handle("c-k") + @handle("c-l") + @handle("c-m") + @handle("c-n") + @handle("c-o") + @handle("c-p") + @handle("c-q") + @handle("c-r") + @handle("c-s") + @handle("c-t") + @handle("c-u") + @handle("c-v") + @handle("c-w") + @handle("c-x") + @handle("c-y") + @handle("c-z") + @handle("f1") + @handle("f2") + @handle("f3") + @handle("f4") + @handle("f5") + @handle("f6") + @handle("f7") + @handle("f8") + @handle("f9") + @handle("f10") + @handle("f11") + @handle("f12") + @handle("f13") + @handle("f14") + @handle("f15") + @handle("f16") + @handle("f17") + @handle("f18") + @handle("f19") + @handle("f20") + @handle("f21") + @handle("f22") + @handle("f23") + @handle("f24") + @handle("c-@") # Also c-space. + @handle("c-\\") + @handle("c-]") + @handle("c-^") + @handle("c-_") + @handle("backspace") + @handle("up") + @handle("down") + @handle("right") + @handle("left") + @handle("s-up") + @handle("s-down") + @handle("s-right") + @handle("s-left") + @handle("home") + @handle("end") + @handle("s-home") + @handle("s-end") + @handle("delete") + @handle("s-delete") + @handle("c-delete") + @handle("pageup") + @handle("pagedown") + @handle("s-tab") + @handle("tab") + @handle("c-s-left") + @handle("c-s-right") + @handle("c-s-home") + @handle("c-s-end") + @handle("c-left") + @handle("c-right") + @handle("c-up") + @handle("c-down") + @handle("c-home") + @handle("c-end") + @handle("insert") + @handle("s-insert") + @handle("c-insert") + @handle(Keys.Ignore) + def _ignore(event: E) -> None: + """ + First, for any of these keys, Don't do anything by default. Also don't + catch them in the 'Any' handler which will insert them as data. + + If people want to insert these characters as a literal, they can always + do by doing a quoted insert. (ControlQ in emacs mode, ControlV in Vi + mode.) + """ + pass + + # Readline-style bindings. + handle("home")(get_by_name("beginning-of-line")) + handle("end")(get_by_name("end-of-line")) + handle("left")(get_by_name("backward-char")) + handle("right")(get_by_name("forward-char")) + handle("c-up")(get_by_name("previous-history")) + handle("c-down")(get_by_name("next-history")) + handle("c-l")(get_by_name("clear-screen")) + + handle("c-k", filter=insert_mode)(get_by_name("kill-line")) + handle("c-u", filter=insert_mode)(get_by_name("unix-line-discard")) + handle("backspace", filter=insert_mode, save_before=if_no_repeat)( + get_by_name("backward-delete-char") + ) + handle("delete", filter=insert_mode, save_before=if_no_repeat)( + get_by_name("delete-char") + ) + handle("c-delete", filter=insert_mode, save_before=if_no_repeat)( + get_by_name("delete-char") + ) + handle(Keys.Any, filter=insert_mode, save_before=if_no_repeat)( + get_by_name("self-insert") + ) + handle("c-t", filter=insert_mode)(get_by_name("transpose-chars")) + handle("c-i", filter=insert_mode)(get_by_name("menu-complete")) + handle("s-tab", filter=insert_mode)(get_by_name("menu-complete-backward")) + + # Control-W should delete, using whitespace as separator, while M-Del + # should delete using [^a-zA-Z0-9] as a boundary. + handle("c-w", filter=insert_mode)(get_by_name("unix-word-rubout")) + + handle("pageup", filter=~has_selection)(get_by_name("previous-history")) + handle("pagedown", filter=~has_selection)(get_by_name("next-history")) + + # CTRL keys. + + @Condition + def has_text_before_cursor() -> bool: + return bool(get_app().current_buffer.text) + + handle("c-d", filter=has_text_before_cursor & insert_mode)( + get_by_name("delete-char") + ) + + @handle("enter", filter=insert_mode & is_multiline) + def _newline(event: E) -> None: + """ + Newline (in case of multiline input. + """ + event.current_buffer.newline(copy_margin=not in_paste_mode()) + + @handle("c-j") + def _newline2(event: E) -> None: + r""" + By default, handle \n as if it were a \r (enter). + (It appears that some terminals send \n instead of \r when pressing + enter. - at least the Linux subsystem for Windows.) + """ + event.key_processor.feed(KeyPress(Keys.ControlM, "\r"), first=True) + + # Delete the word before the cursor. + + @handle("up") + def _go_up(event: E) -> None: + event.current_buffer.auto_up(count=event.arg) + + @handle("down") + def _go_down(event: E) -> None: + event.current_buffer.auto_down(count=event.arg) + + @handle("delete", filter=has_selection) + def _cut(event: E) -> None: + data = event.current_buffer.cut_selection() + event.app.clipboard.set_data(data) + + # Global bindings. + + @handle("c-z") + def _insert_ctrl_z(event: E) -> None: + """ + By default, control-Z should literally insert Ctrl-Z. + (Ansi Ctrl-Z, code 26 in MSDOS means End-Of-File. + In a Python REPL for instance, it's possible to type + Control-Z followed by enter to quit.) + + When the system bindings are loaded and suspend-to-background is + supported, that will override this binding. + """ + event.current_buffer.insert_text(event.data) + + @handle(Keys.BracketedPaste) + def _paste(event: E) -> None: + """ + Pasting from clipboard. + """ + data = event.data + + # Be sure to use \n as line ending. + # Some terminals (Like iTerm2) seem to paste \r\n line endings in a + # bracketed paste. See: https://github.com/ipython/ipython/issues/9737 + data = data.replace("\r\n", "\n") + data = data.replace("\r", "\n") + + event.current_buffer.insert_text(data) + + @Condition + def in_quoted_insert() -> bool: + return get_app().quoted_insert + + @handle(Keys.Any, filter=in_quoted_insert, eager=True) + def _insert_text(event: E) -> None: + """ + Handle quoted insert. + """ + event.current_buffer.insert_text(event.data, overwrite=False) + event.app.quoted_insert = False + + return key_bindings diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/completion.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/completion.py new file mode 100644 index 00000000..a9196240 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/completion.py @@ -0,0 +1,203 @@ +""" +Key binding handlers for displaying completions. +""" +import asyncio +import math +from typing import TYPE_CHECKING, List + +from prompt_toolkit.application.run_in_terminal import in_terminal +from prompt_toolkit.completion import ( + CompleteEvent, + Completion, + get_common_complete_suffix, +) +from prompt_toolkit.formatted_text import StyleAndTextTuples +from prompt_toolkit.key_binding.key_bindings import KeyBindings +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.keys import Keys +from prompt_toolkit.utils import get_cwidth + +if TYPE_CHECKING: + from prompt_toolkit.application import Application + from prompt_toolkit.shortcuts import PromptSession + +__all__ = [ + "generate_completions", + "display_completions_like_readline", +] + +E = KeyPressEvent + + +def generate_completions(event: E) -> None: + r""" + Tab-completion: where the first tab completes the common suffix and the + second tab lists all the completions. + """ + b = event.current_buffer + + # When already navigating through completions, select the next one. + if b.complete_state: + b.complete_next() + else: + b.start_completion(insert_common_part=True) + + +def display_completions_like_readline(event: E) -> None: + """ + Key binding handler for readline-style tab completion. + This is meant to be as similar as possible to the way how readline displays + completions. + + Generate the completions immediately (blocking) and display them above the + prompt in columns. + + Usage:: + + # Call this handler when 'Tab' has been pressed. + key_bindings.add(Keys.ControlI)(display_completions_like_readline) + """ + # Request completions. + b = event.current_buffer + if b.completer is None: + return + complete_event = CompleteEvent(completion_requested=True) + completions = list(b.completer.get_completions(b.document, complete_event)) + + # Calculate the common suffix. + common_suffix = get_common_complete_suffix(b.document, completions) + + # One completion: insert it. + if len(completions) == 1: + b.delete_before_cursor(-completions[0].start_position) + b.insert_text(completions[0].text) + # Multiple completions with common part. + elif common_suffix: + b.insert_text(common_suffix) + # Otherwise: display all completions. + elif completions: + _display_completions_like_readline(event.app, completions) + + +def _display_completions_like_readline( + app: "Application", completions: List[Completion] +) -> "asyncio.Task[None]": + """ + Display the list of completions in columns above the prompt. + This will ask for a confirmation if there are too many completions to fit + on a single page and provide a paginator to walk through them. + """ + from prompt_toolkit.formatted_text import to_formatted_text + from prompt_toolkit.shortcuts.prompt import create_confirm_session + + # Get terminal dimensions. + term_size = app.output.get_size() + term_width = term_size.columns + term_height = term_size.rows + + # Calculate amount of required columns/rows for displaying the + # completions. (Keep in mind that completions are displayed + # alphabetically column-wise.) + max_compl_width = min( + term_width, max(get_cwidth(c.display_text) for c in completions) + 1 + ) + column_count = max(1, term_width // max_compl_width) + completions_per_page = column_count * (term_height - 1) + page_count = int(math.ceil(len(completions) / float(completions_per_page))) + # Note: math.ceil can return float on Python2. + + def display(page: int) -> None: + # Display completions. + page_completions = completions[ + page * completions_per_page : (page + 1) * completions_per_page + ] + + page_row_count = int(math.ceil(len(page_completions) / float(column_count))) + page_columns = [ + page_completions[i * page_row_count : (i + 1) * page_row_count] + for i in range(column_count) + ] + + result: StyleAndTextTuples = [] + + for r in range(page_row_count): + for c in range(column_count): + try: + completion = page_columns[c][r] + style = "class:readline-like-completions.completion " + ( + completion.style or "" + ) + + result.extend(to_formatted_text(completion.display, style=style)) + + # Add padding. + padding = max_compl_width - get_cwidth(completion.display_text) + result.append((completion.style, " " * padding)) + except IndexError: + pass + result.append(("", "\n")) + + app.print_text(to_formatted_text(result, "class:readline-like-completions")) + + # User interaction through an application generator function. + async def run_compl() -> None: + " Coroutine. " + async with in_terminal(render_cli_done=True): + if len(completions) > completions_per_page: + # Ask confirmation if it doesn't fit on the screen. + confirm = await create_confirm_session( + "Display all {} possibilities?".format(len(completions)), + ).prompt_async() + + if confirm: + # Display pages. + for page in range(page_count): + display(page) + + if page != page_count - 1: + # Display --MORE-- and go to the next page. + show_more = await _create_more_session( + "--MORE--" + ).prompt_async() + + if not show_more: + return + else: + app.output.flush() + else: + # Display all completions. + display(0) + + return app.create_background_task(run_compl()) + + +def _create_more_session(message: str = "--MORE--") -> "PromptSession": + """ + Create a `PromptSession` object for displaying the "--MORE--". + """ + from prompt_toolkit.shortcuts import PromptSession + + bindings = KeyBindings() + + @bindings.add(" ") + @bindings.add("y") + @bindings.add("Y") + @bindings.add(Keys.ControlJ) + @bindings.add(Keys.ControlM) + @bindings.add(Keys.ControlI) # Tab. + def _yes(event: E) -> None: + event.app.exit(result=True) + + @bindings.add("n") + @bindings.add("N") + @bindings.add("q") + @bindings.add("Q") + @bindings.add(Keys.ControlC) + def _no(event: E) -> None: + event.app.exit(result=False) + + @bindings.add(Keys.Any) + def _ignore(event: E) -> None: + " Disable inserting of text. " + + return PromptSession(message, key_bindings=bindings, erase_when_done=True) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/cpr.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/cpr.py new file mode 100644 index 00000000..07b0fa75 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/cpr.py @@ -0,0 +1,28 @@ +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.keys import Keys + +from ..key_bindings import KeyBindings + +__all__ = [ + "load_cpr_bindings", +] + +E = KeyPressEvent + + +def load_cpr_bindings() -> KeyBindings: + key_bindings = KeyBindings() + + @key_bindings.add(Keys.CPRResponse, save_before=lambda e: False) + def _(event: E) -> None: + """ + Handle incoming Cursor-Position-Request response. + """ + # The incoming data looks like u'\x1b[35;1R' + # Parse row/col information. + row, col = map(int, event.data[2:-1].split(";")) + + # Report absolute cursor position to the renderer. + event.app.renderer.report_absolute_cursor_row(row) + + return key_bindings diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/emacs.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/emacs.py new file mode 100644 index 00000000..35c8aa4e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/emacs.py @@ -0,0 +1,556 @@ +# pylint: disable=function-redefined +from typing import Dict, Union + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.buffer import Buffer, SelectionType, indent, unindent +from prompt_toolkit.completion import CompleteEvent +from prompt_toolkit.filters import ( + Condition, + emacs_insert_mode, + emacs_mode, + has_arg, + has_selection, + in_paste_mode, + is_multiline, + is_read_only, + shift_selection_mode, + vi_search_direction_reversed, +) +from prompt_toolkit.key_binding.key_bindings import Binding +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.keys import Keys + +from ..key_bindings import ConditionalKeyBindings, KeyBindings, KeyBindingsBase +from .named_commands import get_by_name + +__all__ = [ + "load_emacs_bindings", + "load_emacs_search_bindings", + "load_emacs_shift_selection_bindings", +] + +E = KeyPressEvent + + +def load_emacs_bindings() -> KeyBindingsBase: + """ + Some e-macs extensions. + """ + # Overview of Readline emacs commands: + # http://www.catonmat.net/download/readline-emacs-editing-mode-cheat-sheet.pdf + key_bindings = KeyBindings() + handle = key_bindings.add + + insert_mode = emacs_insert_mode + + @handle("escape") + def _esc(event: E) -> None: + """ + By default, ignore escape key. + + (If we don't put this here, and Esc is followed by a key which sequence + is not handled, we'll insert an Escape character in the input stream. + Something we don't want and happens to easily in emacs mode. + Further, people can always use ControlQ to do a quoted insert.) + """ + pass + + handle("c-a")(get_by_name("beginning-of-line")) + handle("c-b")(get_by_name("backward-char")) + handle("c-delete", filter=insert_mode)(get_by_name("kill-word")) + handle("c-e")(get_by_name("end-of-line")) + handle("c-f")(get_by_name("forward-char")) + handle("c-left")(get_by_name("backward-word")) + handle("c-right")(get_by_name("forward-word")) + handle("c-x", "r", "y", filter=insert_mode)(get_by_name("yank")) + handle("c-y", filter=insert_mode)(get_by_name("yank")) + handle("escape", "b")(get_by_name("backward-word")) + handle("escape", "c", filter=insert_mode)(get_by_name("capitalize-word")) + handle("escape", "d", filter=insert_mode)(get_by_name("kill-word")) + handle("escape", "f")(get_by_name("forward-word")) + handle("escape", "l", filter=insert_mode)(get_by_name("downcase-word")) + handle("escape", "u", filter=insert_mode)(get_by_name("uppercase-word")) + handle("escape", "y", filter=insert_mode)(get_by_name("yank-pop")) + handle("escape", "backspace", filter=insert_mode)(get_by_name("backward-kill-word")) + handle("escape", "\\", filter=insert_mode)(get_by_name("delete-horizontal-space")) + + handle("c-home")(get_by_name("beginning-of-buffer")) + handle("c-end")(get_by_name("end-of-buffer")) + + handle("c-_", save_before=(lambda e: False), filter=insert_mode)( + get_by_name("undo") + ) + + handle("c-x", "c-u", save_before=(lambda e: False), filter=insert_mode)( + get_by_name("undo") + ) + + handle("escape", "<", filter=~has_selection)(get_by_name("beginning-of-history")) + handle("escape", ">", filter=~has_selection)(get_by_name("end-of-history")) + + handle("escape", ".", filter=insert_mode)(get_by_name("yank-last-arg")) + handle("escape", "_", filter=insert_mode)(get_by_name("yank-last-arg")) + handle("escape", "c-y", filter=insert_mode)(get_by_name("yank-nth-arg")) + handle("escape", "#", filter=insert_mode)(get_by_name("insert-comment")) + handle("c-o")(get_by_name("operate-and-get-next")) + + # ControlQ does a quoted insert. Not that for vt100 terminals, you have to + # disable flow control by running ``stty -ixon``, otherwise Ctrl-Q and + # Ctrl-S are captured by the terminal. + handle("c-q", filter=~has_selection)(get_by_name("quoted-insert")) + + handle("c-x", "(")(get_by_name("start-kbd-macro")) + handle("c-x", ")")(get_by_name("end-kbd-macro")) + handle("c-x", "e")(get_by_name("call-last-kbd-macro")) + + @handle("c-n") + def _next(event: E) -> None: + " Next line. " + event.current_buffer.auto_down() + + @handle("c-p") + def _prev(event: E) -> None: + " Previous line. " + event.current_buffer.auto_up(count=event.arg) + + def handle_digit(c: str) -> None: + """ + Handle input of arguments. + The first number needs to be preceded by escape. + """ + + @handle(c, filter=has_arg) + @handle("escape", c) + def _(event: E) -> None: + event.append_to_arg_count(c) + + for c in "0123456789": + handle_digit(c) + + @handle("escape", "-", filter=~has_arg) + def _meta_dash(event: E) -> None: + """""" + if event._arg is None: + event.append_to_arg_count("-") + + @handle("-", filter=Condition(lambda: get_app().key_processor.arg == "-")) + def _dash(event: E) -> None: + """ + When '-' is typed again, after exactly '-' has been given as an + argument, ignore this. + """ + event.app.key_processor.arg = "-" + + @Condition + def is_returnable() -> bool: + return get_app().current_buffer.is_returnable + + # Meta + Enter: always accept input. + handle("escape", "enter", filter=insert_mode & is_returnable)( + get_by_name("accept-line") + ) + + # Enter: accept input in single line mode. + handle("enter", filter=insert_mode & is_returnable & ~is_multiline)( + get_by_name("accept-line") + ) + + def character_search(buff: Buffer, char: str, count: int) -> None: + if count < 0: + match = buff.document.find_backwards( + char, in_current_line=True, count=-count + ) + else: + match = buff.document.find(char, in_current_line=True, count=count) + + if match is not None: + buff.cursor_position += match + + @handle("c-]", Keys.Any) + def _goto_char(event: E) -> None: + " When Ctl-] + a character is pressed. go to that character. " + # Also named 'character-search' + character_search(event.current_buffer, event.data, event.arg) + + @handle("escape", "c-]", Keys.Any) + def _goto_char_backwards(event: E) -> None: + " Like Ctl-], but backwards. " + # Also named 'character-search-backward' + character_search(event.current_buffer, event.data, -event.arg) + + @handle("escape", "a") + def _prev_sentence(event: E) -> None: + " Previous sentence. " + # TODO: + + @handle("escape", "e") + def _end_of_sentence(event: E) -> None: + " Move to end of sentence. " + # TODO: + + @handle("escape", "t", filter=insert_mode) + def _swap_characters(event: E) -> None: + """ + Swap the last two words before the cursor. + """ + # TODO + + @handle("escape", "*", filter=insert_mode) + def _insert_all_completions(event: E) -> None: + """ + `meta-*`: Insert all possible completions of the preceding text. + """ + buff = event.current_buffer + + # List all completions. + complete_event = CompleteEvent(text_inserted=False, completion_requested=True) + completions = list( + buff.completer.get_completions(buff.document, complete_event) + ) + + # Insert them. + text_to_insert = " ".join(c.text for c in completions) + buff.insert_text(text_to_insert) + + @handle("c-x", "c-x") + def _toggle_start_end(event: E) -> None: + """ + Move cursor back and forth between the start and end of the current + line. + """ + buffer = event.current_buffer + + if buffer.document.is_cursor_at_the_end_of_line: + buffer.cursor_position += buffer.document.get_start_of_line_position( + after_whitespace=False + ) + else: + buffer.cursor_position += buffer.document.get_end_of_line_position() + + @handle("c-@") # Control-space or Control-@ + def _start_selection(event: E) -> None: + """ + Start of the selection (if the current buffer is not empty). + """ + # Take the current cursor position as the start of this selection. + buff = event.current_buffer + if buff.text: + buff.start_selection(selection_type=SelectionType.CHARACTERS) + + @handle("c-g", filter=~has_selection) + def _cancel(event: E) -> None: + """ + Control + G: Cancel completion menu and validation state. + """ + event.current_buffer.complete_state = None + event.current_buffer.validation_error = None + + @handle("c-g", filter=has_selection) + def _cancel_selection(event: E) -> None: + """ + Cancel selection. + """ + event.current_buffer.exit_selection() + + @handle("c-w", filter=has_selection) + @handle("c-x", "r", "k", filter=has_selection) + def _cut(event: E) -> None: + """ + Cut selected text. + """ + data = event.current_buffer.cut_selection() + event.app.clipboard.set_data(data) + + @handle("escape", "w", filter=has_selection) + def _copy(event: E) -> None: + """ + Copy selected text. + """ + data = event.current_buffer.copy_selection() + event.app.clipboard.set_data(data) + + @handle("escape", "left") + def _start_of_word(event: E) -> None: + """ + Cursor to start of previous word. + """ + buffer = event.current_buffer + buffer.cursor_position += ( + buffer.document.find_previous_word_beginning(count=event.arg) or 0 + ) + + @handle("escape", "right") + def _start_next_word(event: E) -> None: + """ + Cursor to start of next word. + """ + buffer = event.current_buffer + buffer.cursor_position += ( + buffer.document.find_next_word_beginning(count=event.arg) + or buffer.document.get_end_of_document_position() + ) + + @handle("escape", "/", filter=insert_mode) + def _complete(event: E) -> None: + """ + M-/: Complete. + """ + b = event.current_buffer + if b.complete_state: + b.complete_next() + else: + b.start_completion(select_first=True) + + @handle("c-c", ">", filter=has_selection) + def _indent(event: E) -> None: + """ + Indent selected text. + """ + buffer = event.current_buffer + + buffer.cursor_position += buffer.document.get_start_of_line_position( + after_whitespace=True + ) + + from_, to = buffer.document.selection_range() + from_, _ = buffer.document.translate_index_to_position(from_) + to, _ = buffer.document.translate_index_to_position(to) + + indent(buffer, from_, to + 1, count=event.arg) + + @handle("c-c", "<", filter=has_selection) + def _unindent(event: E) -> None: + """ + Unindent selected text. + """ + buffer = event.current_buffer + + from_, to = buffer.document.selection_range() + from_, _ = buffer.document.translate_index_to_position(from_) + to, _ = buffer.document.translate_index_to_position(to) + + unindent(buffer, from_, to + 1, count=event.arg) + + return ConditionalKeyBindings(key_bindings, emacs_mode) + + +def load_emacs_search_bindings() -> KeyBindingsBase: + key_bindings = KeyBindings() + handle = key_bindings.add + from . import search + + # NOTE: We don't bind 'Escape' to 'abort_search'. The reason is that we + # want Alt+Enter to accept input directly in incremental search mode. + # Instead, we have double escape. + + handle("c-r")(search.start_reverse_incremental_search) + handle("c-s")(search.start_forward_incremental_search) + + handle("c-c")(search.abort_search) + handle("c-g")(search.abort_search) + handle("c-r")(search.reverse_incremental_search) + handle("c-s")(search.forward_incremental_search) + handle("up")(search.reverse_incremental_search) + handle("down")(search.forward_incremental_search) + handle("enter")(search.accept_search) + + # Handling of escape. + handle("escape", eager=True)(search.accept_search) + + # Like Readline, it's more natural to accept the search when escape has + # been pressed, however instead the following two bindings could be used + # instead. + # #handle('escape', 'escape', eager=True)(search.abort_search) + # #handle('escape', 'enter', eager=True)(search.accept_search_and_accept_input) + + # If Read-only: also include the following key bindings: + + # '/' and '?' key bindings for searching, just like Vi mode. + handle("?", filter=is_read_only & ~vi_search_direction_reversed)( + search.start_reverse_incremental_search + ) + handle("/", filter=is_read_only & ~vi_search_direction_reversed)( + search.start_forward_incremental_search + ) + handle("?", filter=is_read_only & vi_search_direction_reversed)( + search.start_forward_incremental_search + ) + handle("/", filter=is_read_only & vi_search_direction_reversed)( + search.start_reverse_incremental_search + ) + + @handle("n", filter=is_read_only) + def _jump_next(event: E) -> None: + " Jump to next match. " + event.current_buffer.apply_search( + event.app.current_search_state, + include_current_position=False, + count=event.arg, + ) + + @handle("N", filter=is_read_only) + def _jump_prev(event: E) -> None: + " Jump to previous match. " + event.current_buffer.apply_search( + ~event.app.current_search_state, + include_current_position=False, + count=event.arg, + ) + + return ConditionalKeyBindings(key_bindings, emacs_mode) + + +def load_emacs_shift_selection_bindings() -> KeyBindingsBase: + """ + Bindings to select text with shift + cursor movements + """ + + key_bindings = KeyBindings() + handle = key_bindings.add + + def unshift_move(event: E) -> None: + """ + Used for the shift selection mode. When called with + a shift + movement key press event, moves the cursor + as if shift is not pressed. + """ + key = event.key_sequence[0].key + + if key == Keys.ShiftUp: + event.current_buffer.auto_up(count=event.arg) + return + if key == Keys.ShiftDown: + event.current_buffer.auto_down(count=event.arg) + return + + # the other keys are handled through their readline command + key_to_command: Dict[Union[Keys, str], str] = { + Keys.ShiftLeft: "backward-char", + Keys.ShiftRight: "forward-char", + Keys.ShiftHome: "beginning-of-line", + Keys.ShiftEnd: "end-of-line", + Keys.ControlShiftLeft: "backward-word", + Keys.ControlShiftRight: "forward-word", + Keys.ControlShiftHome: "beginning-of-buffer", + Keys.ControlShiftEnd: "end-of-buffer", + } + + try: + # Both the dict lookup and `get_by_name` can raise KeyError. + binding = get_by_name(key_to_command[key]) + except KeyError: + pass + else: # (`else` is not really needed here.) + if isinstance(binding, Binding): + # (It should always be a binding here) + binding.call(event) + + @handle("s-left", filter=~has_selection) + @handle("s-right", filter=~has_selection) + @handle("s-up", filter=~has_selection) + @handle("s-down", filter=~has_selection) + @handle("s-home", filter=~has_selection) + @handle("s-end", filter=~has_selection) + @handle("c-s-left", filter=~has_selection) + @handle("c-s-right", filter=~has_selection) + @handle("c-s-home", filter=~has_selection) + @handle("c-s-end", filter=~has_selection) + def _start_selection(event: E) -> None: + """ + Start selection with shift + movement. + """ + # Take the current cursor position as the start of this selection. + buff = event.current_buffer + if buff.text: + buff.start_selection(selection_type=SelectionType.CHARACTERS) + + if buff.selection_state is not None: + # (`selection_state` should never be `None`, it is created by + # `start_selection`.) + buff.selection_state.enter_shift_mode() + + # Then move the cursor + original_position = buff.cursor_position + unshift_move(event) + if buff.cursor_position == original_position: + # Cursor didn't actually move - so cancel selection + # to avoid having an empty selection + buff.exit_selection() + + @handle("s-left", filter=shift_selection_mode) + @handle("s-right", filter=shift_selection_mode) + @handle("s-up", filter=shift_selection_mode) + @handle("s-down", filter=shift_selection_mode) + @handle("s-home", filter=shift_selection_mode) + @handle("s-end", filter=shift_selection_mode) + @handle("c-s-left", filter=shift_selection_mode) + @handle("c-s-right", filter=shift_selection_mode) + @handle("c-s-home", filter=shift_selection_mode) + @handle("c-s-end", filter=shift_selection_mode) + def _extend_selection(event: E) -> None: + """ + Extend the selection + """ + # Just move the cursor, like shift was not pressed + unshift_move(event) + buff = event.current_buffer + + if buff.selection_state is not None: + if buff.cursor_position == buff.selection_state.original_cursor_position: + # selection is now empty, so cancel selection + buff.exit_selection() + + @handle(Keys.Any, filter=shift_selection_mode) + def _replace_selection(event: E) -> None: + """ + Replace selection by what is typed + """ + event.current_buffer.cut_selection() + get_by_name("self-insert").call(event) + + @handle("enter", filter=shift_selection_mode & is_multiline) + def _newline(event: E) -> None: + """ + A newline replaces the selection + """ + event.current_buffer.cut_selection() + event.current_buffer.newline(copy_margin=not in_paste_mode()) + + @handle("backspace", filter=shift_selection_mode) + def _delete(event: E) -> None: + """ + Delete selection. + """ + event.current_buffer.cut_selection() + + @handle("c-y", filter=shift_selection_mode) + def _yank(event: E) -> None: + """ + In shift selection mode, yanking (pasting) replace the selection. + """ + buff = event.current_buffer + if buff.selection_state: + buff.cut_selection() + get_by_name("yank").call(event) + + # moving the cursor in shift selection mode cancels the selection + @handle("left", filter=shift_selection_mode) + @handle("right", filter=shift_selection_mode) + @handle("up", filter=shift_selection_mode) + @handle("down", filter=shift_selection_mode) + @handle("home", filter=shift_selection_mode) + @handle("end", filter=shift_selection_mode) + @handle("c-left", filter=shift_selection_mode) + @handle("c-right", filter=shift_selection_mode) + @handle("c-home", filter=shift_selection_mode) + @handle("c-end", filter=shift_selection_mode) + def _cancel(event: E) -> None: + """ + Cancel selection. + """ + event.current_buffer.exit_selection() + # we then process the cursor movement + key_press = event.key_sequence[0] + event.key_processor.feed(key_press, first=True) + + return ConditionalKeyBindings(key_bindings, emacs_mode) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/focus.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/focus.py new file mode 100644 index 00000000..40844db6 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/focus.py @@ -0,0 +1,24 @@ +from prompt_toolkit.key_binding.key_processor import KeyPressEvent + +__all__ = [ + "focus_next", + "focus_previous", +] + +E = KeyPressEvent + + +def focus_next(event: E) -> None: + """ + Focus the next visible Window. + (Often bound to the `Tab` key.) + """ + event.app.layout.focus_next() + + +def focus_previous(event: E) -> None: + """ + Focus the previous visible Window. + (Often bound to the `BackTab` key.) + """ + event.app.layout.focus_previous() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/mouse.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/mouse.py new file mode 100644 index 00000000..b82f23f7 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/mouse.py @@ -0,0 +1,146 @@ +from prompt_toolkit.data_structures import Point +from prompt_toolkit.key_binding.key_processor import KeyPress, KeyPressEvent +from prompt_toolkit.keys import Keys +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.utils import is_windows + +from ..key_bindings import KeyBindings + +__all__ = [ + "load_mouse_bindings", +] + +E = KeyPressEvent + + +def load_mouse_bindings() -> KeyBindings: + """ + Key bindings, required for mouse support. + (Mouse events enter through the key binding system.) + """ + key_bindings = KeyBindings() + + @key_bindings.add(Keys.Vt100MouseEvent) + def _(event: E) -> None: + """ + Handling of incoming mouse event. + """ + # TypicaL: "eSC[MaB*" + # Urxvt: "Esc[96;14;13M" + # Xterm SGR: "Esc[<64;85;12M" + + # Parse incoming packet. + if event.data[2] == "M": + # Typical. + mouse_event, x, y = map(ord, event.data[3:]) + mouse_event_type = { + 32: MouseEventType.MOUSE_DOWN, + 35: MouseEventType.MOUSE_UP, + 96: MouseEventType.SCROLL_UP, + 97: MouseEventType.SCROLL_DOWN, + }.get(mouse_event) + + # Handle situations where `PosixStdinReader` used surrogateescapes. + if x >= 0xDC00: + x -= 0xDC00 + if y >= 0xDC00: + y -= 0xDC00 + + x -= 32 + y -= 32 + else: + # Urxvt and Xterm SGR. + # When the '<' is not present, we are not using the Xterm SGR mode, + # but Urxvt instead. + data = event.data[2:] + if data[:1] == "<": + sgr = True + data = data[1:] + else: + sgr = False + + # Extract coordinates. + mouse_event, x, y = map(int, data[:-1].split(";")) + m = data[-1] + + # Parse event type. + if sgr: + mouse_event_type = { + (0, "M"): MouseEventType.MOUSE_DOWN, + (0, "m"): MouseEventType.MOUSE_UP, + (64, "M"): MouseEventType.SCROLL_UP, + (65, "M"): MouseEventType.SCROLL_DOWN, + }.get((mouse_event, m)) + else: + mouse_event_type = { + 32: MouseEventType.MOUSE_DOWN, + 35: MouseEventType.MOUSE_UP, + 96: MouseEventType.SCROLL_UP, + 97: MouseEventType.SCROLL_DOWN, + }.get(mouse_event) + + x -= 1 + y -= 1 + + # Only handle mouse events when we know the window height. + if event.app.renderer.height_is_known and mouse_event_type is not None: + # Take region above the layout into account. The reported + # coordinates are absolute to the visible part of the terminal. + from prompt_toolkit.renderer import HeightIsUnknownError + + try: + y -= event.app.renderer.rows_above_layout + except HeightIsUnknownError: + return + + # Call the mouse handler from the renderer. + handler = event.app.renderer.mouse_handlers.mouse_handlers[x, y] + handler(MouseEvent(position=Point(x=x, y=y), event_type=mouse_event_type)) + + @key_bindings.add(Keys.ScrollUp) + def _scroll_up(event: E) -> None: + """ + Scroll up event without cursor position. + """ + # We don't receive a cursor position, so we don't know which window to + # scroll. Just send an 'up' key press instead. + event.key_processor.feed(KeyPress(Keys.Up), first=True) + + @key_bindings.add(Keys.ScrollDown) + def _scroll_down(event: E) -> None: + """ + Scroll down event without cursor position. + """ + event.key_processor.feed(KeyPress(Keys.Down), first=True) + + @key_bindings.add(Keys.WindowsMouseEvent) + def _mouse(event: E) -> None: + """ + Handling of mouse events for Windows. + """ + assert is_windows() # This key binding should only exist for Windows. + + # Parse data. + pieces = event.data.split(";") + + event_type = MouseEventType(pieces[0]) + x = int(pieces[1]) + y = int(pieces[2]) + + # Make coordinates absolute to the visible part of the terminal. + output = event.app.renderer.output + + from prompt_toolkit.output.win32 import Win32Output + + if isinstance(output, Win32Output): + screen_buffer_info = output.get_win32_screen_buffer_info() + rows_above_cursor = ( + screen_buffer_info.dwCursorPosition.Y - event.app.renderer._cursor_pos.y + ) + y -= rows_above_cursor + + # Call the mouse event handler. + handler = event.app.renderer.mouse_handlers.mouse_handlers[x, y] + handler(MouseEvent(position=Point(x=x, y=y), event_type=event_type)) + + return key_bindings diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/named_commands.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/named_commands.py new file mode 100644 index 00000000..74c81a03 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/named_commands.py @@ -0,0 +1,687 @@ +""" +Key bindings which are also known by GNU Readline by the given names. + +See: http://www.delorie.com/gnu/docs/readline/rlman_13.html +""" +from typing import Callable, Dict, TypeVar, Union, cast + +from prompt_toolkit.document import Document +from prompt_toolkit.enums import EditingMode +from prompt_toolkit.key_binding.key_bindings import Binding, key_binding +from prompt_toolkit.key_binding.key_processor import KeyPress, KeyPressEvent +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout.controls import BufferControl +from prompt_toolkit.search import SearchDirection +from prompt_toolkit.selection import PasteMode + +from .completion import display_completions_like_readline, generate_completions + +__all__ = [ + "get_by_name", +] + + +# Typing. +_Handler = Callable[[KeyPressEvent], None] +_HandlerOrBinding = Union[_Handler, Binding] +_T = TypeVar("_T", bound=_HandlerOrBinding) +E = KeyPressEvent + + +# Registry that maps the Readline command names to their handlers. +_readline_commands: Dict[str, Binding] = {} + + +def register(name: str) -> Callable[[_T], _T]: + """ + Store handler in the `_readline_commands` dictionary. + """ + + def decorator(handler: _T) -> _T: + " `handler` is a callable or Binding. " + if isinstance(handler, Binding): + _readline_commands[name] = handler + else: + _readline_commands[name] = key_binding()(cast(_Handler, handler)) + + return handler + + return decorator + + +def get_by_name(name: str) -> Binding: + """ + Return the handler for the (Readline) command with the given name. + """ + try: + return _readline_commands[name] + except KeyError as e: + raise KeyError("Unknown Readline command: %r" % name) from e + + +# +# Commands for moving +# See: http://www.delorie.com/gnu/docs/readline/rlman_14.html +# + + +@register("beginning-of-buffer") +def beginning_of_buffer(event: E) -> None: + """ + Move to the start of the buffer. + """ + buff = event.current_buffer + buff.cursor_position = 0 + + +@register("end-of-buffer") +def end_of_buffer(event: E) -> None: + """ + Move to the end of the buffer. + """ + buff = event.current_buffer + buff.cursor_position = len(buff.text) + + +@register("beginning-of-line") +def beginning_of_line(event: E) -> None: + """ + Move to the start of the current line. + """ + buff = event.current_buffer + buff.cursor_position += buff.document.get_start_of_line_position( + after_whitespace=False + ) + + +@register("end-of-line") +def end_of_line(event: E) -> None: + """ + Move to the end of the line. + """ + buff = event.current_buffer + buff.cursor_position += buff.document.get_end_of_line_position() + + +@register("forward-char") +def forward_char(event: E) -> None: + """ + Move forward a character. + """ + buff = event.current_buffer + buff.cursor_position += buff.document.get_cursor_right_position(count=event.arg) + + +@register("backward-char") +def backward_char(event: E) -> None: + " Move back a character. " + buff = event.current_buffer + buff.cursor_position += buff.document.get_cursor_left_position(count=event.arg) + + +@register("forward-word") +def forward_word(event: E) -> None: + """ + Move forward to the end of the next word. Words are composed of letters and + digits. + """ + buff = event.current_buffer + pos = buff.document.find_next_word_ending(count=event.arg) + + if pos: + buff.cursor_position += pos + + +@register("backward-word") +def backward_word(event: E) -> None: + """ + Move back to the start of the current or previous word. Words are composed + of letters and digits. + """ + buff = event.current_buffer + pos = buff.document.find_previous_word_beginning(count=event.arg) + + if pos: + buff.cursor_position += pos + + +@register("clear-screen") +def clear_screen(event: E) -> None: + """ + Clear the screen and redraw everything at the top of the screen. + """ + event.app.renderer.clear() + + +@register("redraw-current-line") +def redraw_current_line(event: E) -> None: + """ + Refresh the current line. + (Readline defines this command, but prompt-toolkit doesn't have it.) + """ + pass + + +# +# Commands for manipulating the history. +# See: http://www.delorie.com/gnu/docs/readline/rlman_15.html +# + + +@register("accept-line") +def accept_line(event: E) -> None: + """ + Accept the line regardless of where the cursor is. + """ + event.current_buffer.validate_and_handle() + + +@register("previous-history") +def previous_history(event: E) -> None: + """ + Move `back` through the history list, fetching the previous command. + """ + event.current_buffer.history_backward(count=event.arg) + + +@register("next-history") +def next_history(event: E) -> None: + """ + Move `forward` through the history list, fetching the next command. + """ + event.current_buffer.history_forward(count=event.arg) + + +@register("beginning-of-history") +def beginning_of_history(event: E) -> None: + """ + Move to the first line in the history. + """ + event.current_buffer.go_to_history(0) + + +@register("end-of-history") +def end_of_history(event: E) -> None: + """ + Move to the end of the input history, i.e., the line currently being entered. + """ + event.current_buffer.history_forward(count=10 ** 100) + buff = event.current_buffer + buff.go_to_history(len(buff._working_lines) - 1) + + +@register("reverse-search-history") +def reverse_search_history(event: E) -> None: + """ + Search backward starting at the current line and moving `up` through + the history as necessary. This is an incremental search. + """ + control = event.app.layout.current_control + + if isinstance(control, BufferControl) and control.search_buffer_control: + event.app.current_search_state.direction = SearchDirection.BACKWARD + event.app.layout.current_control = control.search_buffer_control + + +# +# Commands for changing text +# + + +@register("end-of-file") +def end_of_file(event: E) -> None: + """ + Exit. + """ + event.app.exit() + + +@register("delete-char") +def delete_char(event: E) -> None: + """ + Delete character before the cursor. + """ + deleted = event.current_buffer.delete(count=event.arg) + if not deleted: + event.app.output.bell() + + +@register("backward-delete-char") +def backward_delete_char(event: E) -> None: + """ + Delete the character behind the cursor. + """ + if event.arg < 0: + # When a negative argument has been given, this should delete in front + # of the cursor. + deleted = event.current_buffer.delete(count=-event.arg) + else: + deleted = event.current_buffer.delete_before_cursor(count=event.arg) + + if not deleted: + event.app.output.bell() + + +@register("self-insert") +def self_insert(event: E) -> None: + """ + Insert yourself. + """ + event.current_buffer.insert_text(event.data * event.arg) + + +@register("transpose-chars") +def transpose_chars(event: E) -> None: + """ + Emulate Emacs transpose-char behavior: at the beginning of the buffer, + do nothing. At the end of a line or buffer, swap the characters before + the cursor. Otherwise, move the cursor right, and then swap the + characters before the cursor. + """ + b = event.current_buffer + p = b.cursor_position + if p == 0: + return + elif p == len(b.text) or b.text[p] == "\n": + b.swap_characters_before_cursor() + else: + b.cursor_position += b.document.get_cursor_right_position() + b.swap_characters_before_cursor() + + +@register("uppercase-word") +def uppercase_word(event: E) -> None: + """ + Uppercase the current (or following) word. + """ + buff = event.current_buffer + + for i in range(event.arg): + pos = buff.document.find_next_word_ending() + words = buff.document.text_after_cursor[:pos] + buff.insert_text(words.upper(), overwrite=True) + + +@register("downcase-word") +def downcase_word(event: E) -> None: + """ + Lowercase the current (or following) word. + """ + buff = event.current_buffer + + for i in range(event.arg): # XXX: not DRY: see meta_c and meta_u!! + pos = buff.document.find_next_word_ending() + words = buff.document.text_after_cursor[:pos] + buff.insert_text(words.lower(), overwrite=True) + + +@register("capitalize-word") +def capitalize_word(event: E) -> None: + """ + Capitalize the current (or following) word. + """ + buff = event.current_buffer + + for i in range(event.arg): + pos = buff.document.find_next_word_ending() + words = buff.document.text_after_cursor[:pos] + buff.insert_text(words.title(), overwrite=True) + + +@register("quoted-insert") +def quoted_insert(event: E) -> None: + """ + Add the next character typed to the line verbatim. This is how to insert + key sequences like C-q, for example. + """ + event.app.quoted_insert = True + + +# +# Killing and yanking. +# + + +@register("kill-line") +def kill_line(event: E) -> None: + """ + Kill the text from the cursor to the end of the line. + + If we are at the end of the line, this should remove the newline. + (That way, it is possible to delete multiple lines by executing this + command multiple times.) + """ + buff = event.current_buffer + if event.arg < 0: + deleted = buff.delete_before_cursor( + count=-buff.document.get_start_of_line_position() + ) + else: + if buff.document.current_char == "\n": + deleted = buff.delete(1) + else: + deleted = buff.delete(count=buff.document.get_end_of_line_position()) + event.app.clipboard.set_text(deleted) + + +@register("kill-word") +def kill_word(event: E) -> None: + """ + Kill from point to the end of the current word, or if between words, to the + end of the next word. Word boundaries are the same as forward-word. + """ + buff = event.current_buffer + pos = buff.document.find_next_word_ending(count=event.arg) + + if pos: + deleted = buff.delete(count=pos) + + if event.is_repeat: + deleted = event.app.clipboard.get_data().text + deleted + + event.app.clipboard.set_text(deleted) + + +@register("unix-word-rubout") +def unix_word_rubout(event: E, WORD: bool = True) -> None: + """ + Kill the word behind point, using whitespace as a word boundary. + Usually bound to ControlW. + """ + buff = event.current_buffer + pos = buff.document.find_start_of_previous_word(count=event.arg, WORD=WORD) + + if pos is None: + # Nothing found? delete until the start of the document. (The + # input starts with whitespace and no words were found before the + # cursor.) + pos = -buff.cursor_position + + if pos: + deleted = buff.delete_before_cursor(count=-pos) + + # If the previous key press was also Control-W, concatenate deleted + # text. + if event.is_repeat: + deleted += event.app.clipboard.get_data().text + + event.app.clipboard.set_text(deleted) + else: + # Nothing to delete. Bell. + event.app.output.bell() + + +@register("backward-kill-word") +def backward_kill_word(event: E) -> None: + """ + Kills the word before point, using "not a letter nor a digit" as a word boundary. + Usually bound to M-Del or M-Backspace. + """ + unix_word_rubout(event, WORD=False) + + +@register("delete-horizontal-space") +def delete_horizontal_space(event: E) -> None: + """ + Delete all spaces and tabs around point. + """ + buff = event.current_buffer + text_before_cursor = buff.document.text_before_cursor + text_after_cursor = buff.document.text_after_cursor + + delete_before = len(text_before_cursor) - len(text_before_cursor.rstrip("\t ")) + delete_after = len(text_after_cursor) - len(text_after_cursor.lstrip("\t ")) + + buff.delete_before_cursor(count=delete_before) + buff.delete(count=delete_after) + + +@register("unix-line-discard") +def unix_line_discard(event: E) -> None: + """ + Kill backward from the cursor to the beginning of the current line. + """ + buff = event.current_buffer + + if buff.document.cursor_position_col == 0 and buff.document.cursor_position > 0: + buff.delete_before_cursor(count=1) + else: + deleted = buff.delete_before_cursor( + count=-buff.document.get_start_of_line_position() + ) + event.app.clipboard.set_text(deleted) + + +@register("yank") +def yank(event: E) -> None: + """ + Paste before cursor. + """ + event.current_buffer.paste_clipboard_data( + event.app.clipboard.get_data(), count=event.arg, paste_mode=PasteMode.EMACS + ) + + +@register("yank-nth-arg") +def yank_nth_arg(event: E) -> None: + """ + Insert the first argument of the previous command. With an argument, insert + the nth word from the previous command (start counting at 0). + """ + n = event.arg if event.arg_present else None + event.current_buffer.yank_nth_arg(n) + + +@register("yank-last-arg") +def yank_last_arg(event: E) -> None: + """ + Like `yank_nth_arg`, but if no argument has been given, yank the last word + of each line. + """ + n = event.arg if event.arg_present else None + event.current_buffer.yank_last_arg(n) + + +@register("yank-pop") +def yank_pop(event: E) -> None: + """ + Rotate the kill ring, and yank the new top. Only works following yank or + yank-pop. + """ + buff = event.current_buffer + doc_before_paste = buff.document_before_paste + clipboard = event.app.clipboard + + if doc_before_paste is not None: + buff.document = doc_before_paste + clipboard.rotate() + buff.paste_clipboard_data(clipboard.get_data(), paste_mode=PasteMode.EMACS) + + +# +# Completion. +# + + +@register("complete") +def complete(event: E) -> None: + """ + Attempt to perform completion. + """ + display_completions_like_readline(event) + + +@register("menu-complete") +def menu_complete(event: E) -> None: + """ + Generate completions, or go to the next completion. (This is the default + way of completing input in prompt_toolkit.) + """ + generate_completions(event) + + +@register("menu-complete-backward") +def menu_complete_backward(event: E) -> None: + """ + Move backward through the list of possible completions. + """ + event.current_buffer.complete_previous() + + +# +# Keyboard macros. +# + + +@register("start-kbd-macro") +def start_kbd_macro(event: E) -> None: + """ + Begin saving the characters typed into the current keyboard macro. + """ + event.app.emacs_state.start_macro() + + +@register("end-kbd-macro") +def end_kbd_macro(event: E) -> None: + """ + Stop saving the characters typed into the current keyboard macro and save + the definition. + """ + event.app.emacs_state.end_macro() + + +@register("call-last-kbd-macro") +@key_binding(record_in_macro=False) +def call_last_kbd_macro(event: E) -> None: + """ + Re-execute the last keyboard macro defined, by making the characters in the + macro appear as if typed at the keyboard. + + Notice that we pass `record_in_macro=False`. This ensures that the 'c-x e' + key sequence doesn't appear in the recording itself. This function inserts + the body of the called macro back into the KeyProcessor, so these keys will + be added later on to the macro of their handlers have `record_in_macro=True`. + """ + # Insert the macro. + macro = event.app.emacs_state.macro + + if macro: + event.app.key_processor.feed_multiple(macro, first=True) + + +@register("print-last-kbd-macro") +def print_last_kbd_macro(event: E) -> None: + """ + Print the last keyboard macro. + """ + # TODO: Make the format suitable for the inputrc file. + def print_macro() -> None: + macro = event.app.emacs_state.macro + if macro: + for k in macro: + print(k) + + from prompt_toolkit.application.run_in_terminal import run_in_terminal + + run_in_terminal(print_macro) + + +# +# Miscellaneous Commands. +# + + +@register("undo") +def undo(event: E) -> None: + """ + Incremental undo. + """ + event.current_buffer.undo() + + +@register("insert-comment") +def insert_comment(event: E) -> None: + """ + Without numeric argument, comment all lines. + With numeric argument, uncomment all lines. + In any case accept the input. + """ + buff = event.current_buffer + + # Transform all lines. + if event.arg != 1: + + def change(line: str) -> str: + return line[1:] if line.startswith("#") else line + + else: + + def change(line: str) -> str: + return "#" + line + + buff.document = Document( + text="\n".join(map(change, buff.text.splitlines())), cursor_position=0 + ) + + # Accept input. + buff.validate_and_handle() + + +@register("vi-editing-mode") +def vi_editing_mode(event: E) -> None: + """ + Switch to Vi editing mode. + """ + event.app.editing_mode = EditingMode.VI + + +@register("emacs-editing-mode") +def emacs_editing_mode(event: E) -> None: + """ + Switch to Emacs editing mode. + """ + event.app.editing_mode = EditingMode.EMACS + + +@register("prefix-meta") +def prefix_meta(event: E) -> None: + """ + Metafy the next character typed. This is for keyboards without a meta key. + + Sometimes people also want to bind other keys to Meta, e.g. 'jj':: + + key_bindings.add_key_binding('j', 'j', filter=ViInsertMode())(prefix_meta) + """ + # ('first' should be true, because we want to insert it at the current + # position in the queue.) + event.app.key_processor.feed(KeyPress(Keys.Escape), first=True) + + +@register("operate-and-get-next") +def operate_and_get_next(event: E) -> None: + """ + Accept the current line for execution and fetch the next line relative to + the current line from the history for editing. + """ + buff = event.current_buffer + new_index = buff.working_index + 1 + + # Accept the current input. (This will also redraw the interface in the + # 'done' state.) + buff.validate_and_handle() + + # Set the new index at the start of the next run. + def set_working_index() -> None: + if new_index < len(buff._working_lines): + buff.working_index = new_index + + event.app.pre_run_callables.append(set_working_index) + + +@register("edit-and-execute-command") +def edit_and_execute(event: E) -> None: + """ + Invoke an editor on the current command line, and accept the result. + """ + buff = event.current_buffer + buff.open_in_editor(validate_and_handle=True) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/open_in_editor.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/open_in_editor.py new file mode 100644 index 00000000..f8699f4a --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/open_in_editor.py @@ -0,0 +1,49 @@ +""" +Open in editor key bindings. +""" +from prompt_toolkit.filters import emacs_mode, has_selection, vi_navigation_mode + +from ..key_bindings import KeyBindings, KeyBindingsBase, merge_key_bindings +from .named_commands import get_by_name + +__all__ = [ + "load_open_in_editor_bindings", + "load_emacs_open_in_editor_bindings", + "load_vi_open_in_editor_bindings", +] + + +def load_open_in_editor_bindings() -> KeyBindingsBase: + """ + Load both the Vi and emacs key bindings for handling edit-and-execute-command. + """ + return merge_key_bindings( + [ + load_emacs_open_in_editor_bindings(), + load_vi_open_in_editor_bindings(), + ] + ) + + +def load_emacs_open_in_editor_bindings() -> KeyBindings: + """ + Pressing C-X C-E will open the buffer in an external editor. + """ + key_bindings = KeyBindings() + + key_bindings.add("c-x", "c-e", filter=emacs_mode & ~has_selection)( + get_by_name("edit-and-execute-command") + ) + + return key_bindings + + +def load_vi_open_in_editor_bindings() -> KeyBindings: + """ + Pressing 'v' in navigation mode will open the buffer in an external editor. + """ + key_bindings = KeyBindings() + key_bindings.add("v", filter=vi_navigation_mode)( + get_by_name("edit-and-execute-command") + ) + return key_bindings diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/page_navigation.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/page_navigation.py new file mode 100644 index 00000000..4d531c04 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/page_navigation.py @@ -0,0 +1,82 @@ +""" +Key bindings for extra page navigation: bindings for up/down scrolling through +long pages, like in Emacs or Vi. +""" +from prompt_toolkit.filters import buffer_has_focus, emacs_mode, vi_mode +from prompt_toolkit.key_binding.key_bindings import ( + ConditionalKeyBindings, + KeyBindings, + KeyBindingsBase, + merge_key_bindings, +) + +from .scroll import ( + scroll_backward, + scroll_forward, + scroll_half_page_down, + scroll_half_page_up, + scroll_one_line_down, + scroll_one_line_up, + scroll_page_down, + scroll_page_up, +) + +__all__ = [ + "load_page_navigation_bindings", + "load_emacs_page_navigation_bindings", + "load_vi_page_navigation_bindings", +] + + +def load_page_navigation_bindings() -> KeyBindingsBase: + """ + Load both the Vi and Emacs bindings for page navigation. + """ + # Only enable when a `Buffer` is focused, otherwise, we would catch keys + # when another widget is focused (like for instance `c-d` in a + # ptterm.Terminal). + return ConditionalKeyBindings( + merge_key_bindings( + [ + load_emacs_page_navigation_bindings(), + load_vi_page_navigation_bindings(), + ] + ), + buffer_has_focus, + ) + + +def load_emacs_page_navigation_bindings() -> KeyBindingsBase: + """ + Key bindings, for scrolling up and down through pages. + This are separate bindings, because GNU readline doesn't have them. + """ + key_bindings = KeyBindings() + handle = key_bindings.add + + handle("c-v")(scroll_page_down) + handle("pagedown")(scroll_page_down) + handle("escape", "v")(scroll_page_up) + handle("pageup")(scroll_page_up) + + return ConditionalKeyBindings(key_bindings, emacs_mode) + + +def load_vi_page_navigation_bindings() -> KeyBindingsBase: + """ + Key bindings, for scrolling up and down through pages. + This are separate bindings, because GNU readline doesn't have them. + """ + key_bindings = KeyBindings() + handle = key_bindings.add + + handle("c-f")(scroll_forward) + handle("c-b")(scroll_backward) + handle("c-d")(scroll_half_page_down) + handle("c-u")(scroll_half_page_up) + handle("c-e")(scroll_one_line_down) + handle("c-y")(scroll_one_line_up) + handle("pagedown")(scroll_page_down) + handle("pageup")(scroll_page_up) + + return ConditionalKeyBindings(key_bindings, vi_mode) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/scroll.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/scroll.py new file mode 100644 index 00000000..4a43ff58 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/scroll.py @@ -0,0 +1,187 @@ +""" +Key bindings, for scrolling up and down through pages. + +This are separate bindings, because GNU readline doesn't have them, but +they are very useful for navigating through long multiline buffers, like in +Vi, Emacs, etc... +""" +from prompt_toolkit.key_binding.key_processor import KeyPressEvent + +__all__ = [ + "scroll_forward", + "scroll_backward", + "scroll_half_page_up", + "scroll_half_page_down", + "scroll_one_line_up", + "scroll_one_line_down", +] + +E = KeyPressEvent + + +def scroll_forward(event: E, half: bool = False) -> None: + """ + Scroll window down. + """ + w = event.app.layout.current_window + b = event.app.current_buffer + + if w and w.render_info: + info = w.render_info + ui_content = info.ui_content + + # Height to scroll. + scroll_height = info.window_height + if half: + scroll_height //= 2 + + # Calculate how many lines is equivalent to that vertical space. + y = b.document.cursor_position_row + 1 + height = 0 + while y < ui_content.line_count: + line_height = info.get_height_for_line(y) + + if height + line_height < scroll_height: + height += line_height + y += 1 + else: + break + + b.cursor_position = b.document.translate_row_col_to_index(y, 0) + + +def scroll_backward(event: E, half: bool = False) -> None: + """ + Scroll window up. + """ + w = event.app.layout.current_window + b = event.app.current_buffer + + if w and w.render_info: + info = w.render_info + + # Height to scroll. + scroll_height = info.window_height + if half: + scroll_height //= 2 + + # Calculate how many lines is equivalent to that vertical space. + y = max(0, b.document.cursor_position_row - 1) + height = 0 + while y > 0: + line_height = info.get_height_for_line(y) + + if height + line_height < scroll_height: + height += line_height + y -= 1 + else: + break + + b.cursor_position = b.document.translate_row_col_to_index(y, 0) + + +def scroll_half_page_down(event: E) -> None: + """ + Same as ControlF, but only scroll half a page. + """ + scroll_forward(event, half=True) + + +def scroll_half_page_up(event: E) -> None: + """ + Same as ControlB, but only scroll half a page. + """ + scroll_backward(event, half=True) + + +def scroll_one_line_down(event: E) -> None: + """ + scroll_offset += 1 + """ + w = event.app.layout.current_window + b = event.app.current_buffer + + if w: + # When the cursor is at the top, move to the next line. (Otherwise, only scroll.) + if w.render_info: + info = w.render_info + + if w.vertical_scroll < info.content_height - info.window_height: + if info.cursor_position.y <= info.configured_scroll_offsets.top: + b.cursor_position += b.document.get_cursor_down_position() + + w.vertical_scroll += 1 + + +def scroll_one_line_up(event: E) -> None: + """ + scroll_offset -= 1 + """ + w = event.app.layout.current_window + b = event.app.current_buffer + + if w: + # When the cursor is at the bottom, move to the previous line. (Otherwise, only scroll.) + if w.render_info: + info = w.render_info + + if w.vertical_scroll > 0: + first_line_height = info.get_height_for_line(info.first_visible_line()) + + cursor_up = info.cursor_position.y - ( + info.window_height + - 1 + - first_line_height + - info.configured_scroll_offsets.bottom + ) + + # Move cursor up, as many steps as the height of the first line. + # TODO: not entirely correct yet, in case of line wrapping and many long lines. + for _ in range(max(0, cursor_up)): + b.cursor_position += b.document.get_cursor_up_position() + + # Scroll window + w.vertical_scroll -= 1 + + +def scroll_page_down(event: E) -> None: + """ + Scroll page down. (Prefer the cursor at the top of the page, after scrolling.) + """ + w = event.app.layout.current_window + b = event.app.current_buffer + + if w and w.render_info: + # Scroll down one page. + line_index = max(w.render_info.last_visible_line(), w.vertical_scroll + 1) + w.vertical_scroll = line_index + + b.cursor_position = b.document.translate_row_col_to_index(line_index, 0) + b.cursor_position += b.document.get_start_of_line_position( + after_whitespace=True + ) + + +def scroll_page_up(event: E) -> None: + """ + Scroll page up. (Prefer the cursor at the bottom of the page, after scrolling.) + """ + w = event.app.layout.current_window + b = event.app.current_buffer + + if w and w.render_info: + # Put cursor at the first visible line. (But make sure that the cursor + # moves at least one line up.) + line_index = max( + 0, + min(w.render_info.first_visible_line(), b.document.cursor_position_row - 1), + ) + + b.cursor_position = b.document.translate_row_col_to_index(line_index, 0) + b.cursor_position += b.document.get_start_of_line_position( + after_whitespace=True + ) + + # Set the scroll offset. We can safely set it to zero; the Window will + # make sure that it scrolls at least until the cursor becomes visible. + w.vertical_scroll = 0 diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/search.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/search.py new file mode 100644 index 00000000..06a047e4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/search.py @@ -0,0 +1,93 @@ +""" +Search related key bindings. +""" +from prompt_toolkit import search +from prompt_toolkit.application.current import get_app +from prompt_toolkit.filters import Condition, control_is_searchable, is_searching +from prompt_toolkit.key_binding.key_processor import KeyPressEvent + +from ..key_bindings import key_binding + +__all__ = [ + "abort_search", + "accept_search", + "start_reverse_incremental_search", + "start_forward_incremental_search", + "reverse_incremental_search", + "forward_incremental_search", + "accept_search_and_accept_input", +] + +E = KeyPressEvent + + +@key_binding(filter=is_searching) +def abort_search(event: E) -> None: + """ + Abort an incremental search and restore the original + line. + (Usually bound to ControlG/ControlC.) + """ + search.stop_search() + + +@key_binding(filter=is_searching) +def accept_search(event: E) -> None: + """ + When enter pressed in isearch, quit isearch mode. (Multiline + isearch would be too complicated.) + (Usually bound to Enter.) + """ + search.accept_search() + + +@key_binding(filter=control_is_searchable) +def start_reverse_incremental_search(event: E) -> None: + """ + Enter reverse incremental search. + (Usually ControlR.) + """ + search.start_search(direction=search.SearchDirection.BACKWARD) + + +@key_binding(filter=control_is_searchable) +def start_forward_incremental_search(event: E) -> None: + """ + Enter forward incremental search. + (Usually ControlS.) + """ + search.start_search(direction=search.SearchDirection.FORWARD) + + +@key_binding(filter=is_searching) +def reverse_incremental_search(event: E) -> None: + """ + Apply reverse incremental search, but keep search buffer focused. + """ + search.do_incremental_search(search.SearchDirection.BACKWARD, count=event.arg) + + +@key_binding(filter=is_searching) +def forward_incremental_search(event: E) -> None: + """ + Apply forward incremental search, but keep search buffer focused. + """ + search.do_incremental_search(search.SearchDirection.FORWARD, count=event.arg) + + +@Condition +def _previous_buffer_is_returnable() -> bool: + """ + True if the previously focused buffer has a return handler. + """ + prev_control = get_app().layout.search_target_buffer_control + return bool(prev_control and prev_control.buffer.is_returnable) + + +@key_binding(filter=is_searching & _previous_buffer_is_returnable) +def accept_search_and_accept_input(event: E) -> None: + """ + Accept the search operation first, then accept the input. + """ + search.accept_search() + event.current_buffer.validate_and_handle() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/vi.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/vi.py new file mode 100644 index 00000000..51519032 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/bindings/vi.py @@ -0,0 +1,2221 @@ +# pylint: disable=function-redefined +import codecs +import string +from enum import Enum +from itertools import accumulate +from typing import Callable, Iterable, List, Optional, Tuple, TypeVar, Union, cast + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.buffer import Buffer, indent, reshape_text, unindent +from prompt_toolkit.clipboard import ClipboardData +from prompt_toolkit.document import Document +from prompt_toolkit.filters import ( + Always, + Condition, + Filter, + has_arg, + is_read_only, + is_searching, +) +from prompt_toolkit.filters.app import ( + in_paste_mode, + is_multiline, + vi_digraph_mode, + vi_insert_mode, + vi_insert_multiple_mode, + vi_mode, + vi_navigation_mode, + vi_recording_macro, + vi_replace_mode, + vi_replace_single_mode, + vi_search_direction_reversed, + vi_selection_mode, + vi_waiting_for_text_object_mode, +) +from prompt_toolkit.input.vt100_parser import Vt100Parser +from prompt_toolkit.key_binding.digraphs import DIGRAPHS +from prompt_toolkit.key_binding.key_processor import KeyPress, KeyPressEvent +from prompt_toolkit.key_binding.vi_state import CharacterFind, InputMode +from prompt_toolkit.keys import Keys +from prompt_toolkit.search import SearchDirection +from prompt_toolkit.selection import PasteMode, SelectionState, SelectionType + +from ..key_bindings import ConditionalKeyBindings, KeyBindings, KeyBindingsBase +from .named_commands import get_by_name + +__all__ = [ + "load_vi_bindings", + "load_vi_search_bindings", +] + +E = KeyPressEvent + +ascii_lowercase = string.ascii_lowercase + +vi_register_names = ascii_lowercase + "0123456789" + + +class TextObjectType(Enum): + EXCLUSIVE = "EXCLUSIVE" + INCLUSIVE = "INCLUSIVE" + LINEWISE = "LINEWISE" + BLOCK = "BLOCK" + + +class TextObject: + """ + Return struct for functions wrapped in ``text_object``. + Both `start` and `end` are relative to the current cursor position. + """ + + def __init__( + self, start: int, end: int = 0, type: TextObjectType = TextObjectType.EXCLUSIVE + ): + + self.start = start + self.end = end + self.type = type + + @property + def selection_type(self) -> SelectionType: + if self.type == TextObjectType.LINEWISE: + return SelectionType.LINES + if self.type == TextObjectType.BLOCK: + return SelectionType.BLOCK + else: + return SelectionType.CHARACTERS + + def sorted(self) -> Tuple[int, int]: + """ + Return a (start, end) tuple where start <= end. + """ + if self.start < self.end: + return self.start, self.end + else: + return self.end, self.start + + def operator_range(self, document: Document) -> Tuple[int, int]: + """ + Return a (start, end) tuple with start <= end that indicates the range + operators should operate on. + `buffer` is used to get start and end of line positions. + + This should return something that can be used in a slice, so the `end` + position is *not* included. + """ + start, end = self.sorted() + doc = document + + if ( + self.type == TextObjectType.EXCLUSIVE + and doc.translate_index_to_position(end + doc.cursor_position)[1] == 0 + ): + # If the motion is exclusive and the end of motion is on the first + # column, the end position becomes end of previous line. + end -= 1 + if self.type == TextObjectType.INCLUSIVE: + end += 1 + if self.type == TextObjectType.LINEWISE: + # Select whole lines + row, col = doc.translate_index_to_position(start + doc.cursor_position) + start = doc.translate_row_col_to_index(row, 0) - doc.cursor_position + row, col = doc.translate_index_to_position(end + doc.cursor_position) + end = ( + doc.translate_row_col_to_index(row, len(doc.lines[row])) + - doc.cursor_position + ) + return start, end + + def get_line_numbers(self, buffer: Buffer) -> Tuple[int, int]: + """ + Return a (start_line, end_line) pair. + """ + # Get absolute cursor positions from the text object. + from_, to = self.operator_range(buffer.document) + from_ += buffer.cursor_position + to += buffer.cursor_position + + # Take the start of the lines. + from_, _ = buffer.document.translate_index_to_position(from_) + to, _ = buffer.document.translate_index_to_position(to) + + return from_, to + + def cut(self, buffer: Buffer) -> Tuple[Document, ClipboardData]: + """ + Turn text object into `ClipboardData` instance. + """ + from_, to = self.operator_range(buffer.document) + + from_ += buffer.cursor_position + to += buffer.cursor_position + + # For Vi mode, the SelectionState does include the upper position, + # while `self.operator_range` does not. So, go one to the left, unless + # we're in the line mode, then we don't want to risk going to the + # previous line, and missing one line in the selection. + if self.type != TextObjectType.LINEWISE: + to -= 1 + + document = Document( + buffer.text, + to, + SelectionState(original_cursor_position=from_, type=self.selection_type), + ) + + new_document, clipboard_data = document.cut_selection() + return new_document, clipboard_data + + +# Typevar for any text object function: +TextObjectFunction = Callable[[E], TextObject] +_TOF = TypeVar("_TOF", bound=TextObjectFunction) + + +def create_text_object_decorator( + key_bindings: KeyBindings, +) -> Callable[..., Callable[[_TOF], _TOF]]: + """ + Create a decorator that can be used to register Vi text object implementations. + """ + + def text_object_decorator( + *keys: Union[Keys, str], + filter: Filter = Always(), + no_move_handler: bool = False, + no_selection_handler: bool = False, + eager: bool = False, + ) -> Callable[[_TOF], _TOF]: + """ + Register a text object function. + + Usage:: + + @text_object('w', filter=..., no_move_handler=False) + def handler(event): + # Return a text object for this key. + return TextObject(...) + + :param no_move_handler: Disable the move handler in navigation mode. + (It's still active in selection mode.) + """ + + def decorator(text_object_func: _TOF) -> _TOF: + @key_bindings.add( + *keys, filter=vi_waiting_for_text_object_mode & filter, eager=eager + ) + def _apply_operator_to_text_object(event: E) -> None: + # Arguments are multiplied. + vi_state = event.app.vi_state + event._arg = str((vi_state.operator_arg or 1) * (event.arg or 1)) + + # Call the text object handler. + text_obj = text_object_func(event) + + # Get the operator function. + # (Should never be None here, given the + # `vi_waiting_for_text_object_mode` filter state.) + operator_func = vi_state.operator_func + + if text_obj is not None and operator_func is not None: + # Call the operator function with the text object. + operator_func(event, text_obj) + + # Clear operator. + event.app.vi_state.operator_func = None + event.app.vi_state.operator_arg = None + + # Register a move operation. (Doesn't need an operator.) + if not no_move_handler: + + @key_bindings.add( + *keys, + filter=~vi_waiting_for_text_object_mode + & filter + & vi_navigation_mode, + eager=eager, + ) + def _move_in_navigation_mode(event: E) -> None: + """ + Move handler for navigation mode. + """ + text_object = text_object_func(event) + event.current_buffer.cursor_position += text_object.start + + # Register a move selection operation. + if not no_selection_handler: + + @key_bindings.add( + *keys, + filter=~vi_waiting_for_text_object_mode + & filter + & vi_selection_mode, + eager=eager, + ) + def _move_in_selection_mode(event: E) -> None: + """ + Move handler for selection mode. + """ + text_object = text_object_func(event) + buff = event.current_buffer + selection_state = buff.selection_state + + if selection_state is None: + return # Should not happen, because of the `vi_selection_mode` filter. + + # When the text object has both a start and end position, like 'i(' or 'iw', + # Turn this into a selection, otherwise the cursor. + if text_object.end: + # Take selection positions from text object. + start, end = text_object.operator_range(buff.document) + start += buff.cursor_position + end += buff.cursor_position + + selection_state.original_cursor_position = start + buff.cursor_position = end + + # Take selection type from text object. + if text_object.type == TextObjectType.LINEWISE: + selection_state.type = SelectionType.LINES + else: + selection_state.type = SelectionType.CHARACTERS + else: + event.current_buffer.cursor_position += text_object.start + + # Make it possible to chain @text_object decorators. + return text_object_func + + return decorator + + return text_object_decorator + + +# Typevar for any operator function: +OperatorFunction = Callable[[E, TextObject], None] +_OF = TypeVar("_OF", bound=OperatorFunction) + + +def create_operator_decorator( + key_bindings: KeyBindings, +) -> Callable[..., Callable[[_OF], _OF]]: + """ + Create a decorator that can be used for registering Vi operators. + """ + + def operator_decorator( + *keys: Union[Keys, str], filter: Filter = Always(), eager: bool = False + ) -> Callable[[_OF], _OF]: + """ + Register a Vi operator. + + Usage:: + + @operator('d', filter=...) + def handler(event, text_object): + # Do something with the text object here. + """ + + def decorator(operator_func: _OF) -> _OF: + @key_bindings.add( + *keys, + filter=~vi_waiting_for_text_object_mode & filter & vi_navigation_mode, + eager=eager, + ) + def _operator_in_navigation(event: E) -> None: + """ + Handle operator in navigation mode. + """ + # When this key binding is matched, only set the operator + # function in the ViState. We should execute it after a text + # object has been received. + event.app.vi_state.operator_func = operator_func + event.app.vi_state.operator_arg = event.arg + + @key_bindings.add( + *keys, + filter=~vi_waiting_for_text_object_mode & filter & vi_selection_mode, + eager=eager, + ) + def _operator_in_selection(event: E) -> None: + """ + Handle operator in selection mode. + """ + buff = event.current_buffer + selection_state = buff.selection_state + + if selection_state is not None: + # Create text object from selection. + if selection_state.type == SelectionType.LINES: + text_obj_type = TextObjectType.LINEWISE + elif selection_state.type == SelectionType.BLOCK: + text_obj_type = TextObjectType.BLOCK + else: + text_obj_type = TextObjectType.INCLUSIVE + + text_object = TextObject( + selection_state.original_cursor_position - buff.cursor_position, + type=text_obj_type, + ) + + # Execute operator. + operator_func(event, text_object) + + # Quit selection mode. + buff.selection_state = None + + return operator_func + + return decorator + + return operator_decorator + + +def load_vi_bindings() -> KeyBindingsBase: + """ + Vi extensions. + + # Overview of Readline Vi commands: + # http://www.catonmat.net/download/bash-vi-editing-mode-cheat-sheet.pdf + """ + # Note: Some key bindings have the "~IsReadOnly()" filter added. This + # prevents the handler to be executed when the focus is on a + # read-only buffer. + # This is however only required for those that change the ViState to + # INSERT mode. The `Buffer` class itself throws the + # `EditReadOnlyBuffer` exception for any text operations which is + # handled correctly. There is no need to add "~IsReadOnly" to all key + # bindings that do text manipulation. + + key_bindings = KeyBindings() + handle = key_bindings.add + + # (Note: Always take the navigation bindings in read-only mode, even when + # ViState says different.) + + TransformFunction = Tuple[Tuple[str, ...], Filter, Callable[[str], str]] + + vi_transform_functions: List[TransformFunction] = [ + # Rot 13 transformation + ( + ("g", "?"), + Always(), + lambda string: cast(str, codecs.encode(string, "rot_13")), + ), + # To lowercase + (("g", "u"), Always(), lambda string: string.lower()), + # To uppercase. + (("g", "U"), Always(), lambda string: string.upper()), + # Swap case. + (("g", "~"), Always(), lambda string: string.swapcase()), + ( + ("~",), + Condition(lambda: get_app().vi_state.tilde_operator), + lambda string: string.swapcase(), + ), + ] + + # Insert a character literally (quoted insert). + handle("c-v", filter=vi_insert_mode)(get_by_name("quoted-insert")) + + @handle("escape") + def _back_to_navigation(event: E) -> None: + """ + Escape goes to vi navigation mode. + """ + buffer = event.current_buffer + vi_state = event.app.vi_state + + if vi_state.input_mode in (InputMode.INSERT, InputMode.REPLACE): + buffer.cursor_position += buffer.document.get_cursor_left_position() + + vi_state.input_mode = InputMode.NAVIGATION + + if bool(buffer.selection_state): + buffer.exit_selection() + + @handle("k", filter=vi_selection_mode) + def _up_in_selection(event: E) -> None: + """ + Arrow up in selection mode. + """ + event.current_buffer.cursor_up(count=event.arg) + + @handle("j", filter=vi_selection_mode) + def _down_in_selection(event: E) -> None: + """ + Arrow down in selection mode. + """ + event.current_buffer.cursor_down(count=event.arg) + + @handle("up", filter=vi_navigation_mode) + @handle("c-p", filter=vi_navigation_mode) + def _up_in_navigation(event: E) -> None: + """ + Arrow up and ControlP in navigation mode go up. + """ + event.current_buffer.auto_up(count=event.arg) + + @handle("k", filter=vi_navigation_mode) + def _go_up(event: E) -> None: + """ + Go up, but if we enter a new history entry, move to the start of the + line. + """ + event.current_buffer.auto_up( + count=event.arg, go_to_start_of_line_if_history_changes=True + ) + + @handle("down", filter=vi_navigation_mode) + @handle("c-n", filter=vi_navigation_mode) + def _go_down(event: E) -> None: + """ + Arrow down and Control-N in navigation mode. + """ + event.current_buffer.auto_down(count=event.arg) + + @handle("j", filter=vi_navigation_mode) + def _go_down2(event: E) -> None: + """ + Go down, but if we enter a new history entry, go to the start of the line. + """ + event.current_buffer.auto_down( + count=event.arg, go_to_start_of_line_if_history_changes=True + ) + + @handle("backspace", filter=vi_navigation_mode) + def _go_left(event: E) -> None: + """ + In navigation-mode, move cursor. + """ + event.current_buffer.cursor_position += ( + event.current_buffer.document.get_cursor_left_position(count=event.arg) + ) + + @handle("c-n", filter=vi_insert_mode) + def _complete_next(event: E) -> None: + b = event.current_buffer + + if b.complete_state: + b.complete_next() + else: + b.start_completion(select_first=True) + + @handle("c-p", filter=vi_insert_mode) + def _complete_prev(event: E) -> None: + """ + Control-P: To previous completion. + """ + b = event.current_buffer + + if b.complete_state: + b.complete_previous() + else: + b.start_completion(select_last=True) + + @handle("c-g", filter=vi_insert_mode) + @handle("c-y", filter=vi_insert_mode) + def _accept_completion(event: E) -> None: + """ + Accept current completion. + """ + event.current_buffer.complete_state = None + + @handle("c-e", filter=vi_insert_mode) + def _cancel_completion(event: E) -> None: + """ + Cancel completion. Go back to originally typed text. + """ + event.current_buffer.cancel_completion() + + @Condition + def is_returnable() -> bool: + return get_app().current_buffer.is_returnable + + # In navigation mode, pressing enter will always return the input. + handle("enter", filter=vi_navigation_mode & is_returnable)( + get_by_name("accept-line") + ) + + # In insert mode, also accept input when enter is pressed, and the buffer + # has been marked as single line. + handle("enter", filter=is_returnable & ~is_multiline)(get_by_name("accept-line")) + + @handle("enter", filter=~is_returnable & vi_navigation_mode) + def _start_of_next_line(event: E) -> None: + """ + Go to the beginning of next line. + """ + b = event.current_buffer + b.cursor_down(count=event.arg) + b.cursor_position += b.document.get_start_of_line_position( + after_whitespace=True + ) + + # ** In navigation mode ** + + # List of navigation commands: http://hea-www.harvard.edu/~fine/Tech/vi.html + + @handle("insert", filter=vi_navigation_mode) + def _insert_mode(event: E) -> None: + """ + Pressing the Insert key. + """ + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("insert", filter=vi_insert_mode) + def _navigation_mode(event: E) -> None: + """ + Pressing the Insert key. + """ + event.app.vi_state.input_mode = InputMode.NAVIGATION + + @handle("a", filter=vi_navigation_mode & ~is_read_only) + # ~IsReadOnly, because we want to stay in navigation mode for + # read-only buffers. + def _a(event: E) -> None: + event.current_buffer.cursor_position += ( + event.current_buffer.document.get_cursor_right_position() + ) + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("A", filter=vi_navigation_mode & ~is_read_only) + def _A(event: E) -> None: + event.current_buffer.cursor_position += ( + event.current_buffer.document.get_end_of_line_position() + ) + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("C", filter=vi_navigation_mode & ~is_read_only) + def _change_until_end_of_line(event: E) -> None: + """ + Change to end of line. + Same as 'c$' (which is implemented elsewhere.) + """ + buffer = event.current_buffer + + deleted = buffer.delete(count=buffer.document.get_end_of_line_position()) + event.app.clipboard.set_text(deleted) + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("c", "c", filter=vi_navigation_mode & ~is_read_only) + @handle("S", filter=vi_navigation_mode & ~is_read_only) + def _change_current_line(event: E) -> None: # TODO: implement 'arg' + """ + Change current line + """ + buffer = event.current_buffer + + # We copy the whole line. + data = ClipboardData(buffer.document.current_line, SelectionType.LINES) + event.app.clipboard.set_data(data) + + # But we delete after the whitespace + buffer.cursor_position += buffer.document.get_start_of_line_position( + after_whitespace=True + ) + buffer.delete(count=buffer.document.get_end_of_line_position()) + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("D", filter=vi_navigation_mode) + def _delete_until_end_of_line(event: E) -> None: + """ + Delete from cursor position until the end of the line. + """ + buffer = event.current_buffer + deleted = buffer.delete(count=buffer.document.get_end_of_line_position()) + event.app.clipboard.set_text(deleted) + + @handle("d", "d", filter=vi_navigation_mode) + def _delete_line(event: E) -> None: + """ + Delete line. (Or the following 'n' lines.) + """ + buffer = event.current_buffer + + # Split string in before/deleted/after text. + lines = buffer.document.lines + + before = "\n".join(lines[: buffer.document.cursor_position_row]) + deleted = "\n".join( + lines[ + buffer.document.cursor_position_row : buffer.document.cursor_position_row + + event.arg + ] + ) + after = "\n".join(lines[buffer.document.cursor_position_row + event.arg :]) + + # Set new text. + if before and after: + before = before + "\n" + + # Set text and cursor position. + buffer.document = Document( + text=before + after, + # Cursor At the start of the first 'after' line, after the leading whitespace. + cursor_position=len(before) + len(after) - len(after.lstrip(" ")), + ) + + # Set clipboard data + event.app.clipboard.set_data(ClipboardData(deleted, SelectionType.LINES)) + + @handle("x", filter=vi_selection_mode) + def _cut(event: E) -> None: + """ + Cut selection. + ('x' is not an operator.) + """ + clipboard_data = event.current_buffer.cut_selection() + event.app.clipboard.set_data(clipboard_data) + + @handle("i", filter=vi_navigation_mode & ~is_read_only) + def _i(event: E) -> None: + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("I", filter=vi_navigation_mode & ~is_read_only) + def _I(event: E) -> None: + event.app.vi_state.input_mode = InputMode.INSERT + event.current_buffer.cursor_position += ( + event.current_buffer.document.get_start_of_line_position( + after_whitespace=True + ) + ) + + @Condition + def in_block_selection() -> bool: + buff = get_app().current_buffer + return bool( + buff.selection_state and buff.selection_state.type == SelectionType.BLOCK + ) + + @handle("I", filter=in_block_selection & ~is_read_only) + def insert_in_block_selection(event: E, after: bool = False) -> None: + """ + Insert in block selection mode. + """ + buff = event.current_buffer + + # Store all cursor positions. + positions = [] + + if after: + + def get_pos(from_to: Tuple[int, int]) -> int: + return from_to[1] + + else: + + def get_pos(from_to: Tuple[int, int]) -> int: + return from_to[0] + + for i, from_to in enumerate(buff.document.selection_ranges()): + positions.append(get_pos(from_to)) + if i == 0: + buff.cursor_position = get_pos(from_to) + + buff.multiple_cursor_positions = positions + + # Go to 'INSERT_MULTIPLE' mode. + event.app.vi_state.input_mode = InputMode.INSERT_MULTIPLE + buff.exit_selection() + + @handle("A", filter=in_block_selection & ~is_read_only) + def _append_after_block(event: E) -> None: + insert_in_block_selection(event, after=True) + + @handle("J", filter=vi_navigation_mode & ~is_read_only) + def _join(event: E) -> None: + """ + Join lines. + """ + for i in range(event.arg): + event.current_buffer.join_next_line() + + @handle("g", "J", filter=vi_navigation_mode & ~is_read_only) + def _join_nospace(event: E) -> None: + """ + Join lines without space. + """ + for i in range(event.arg): + event.current_buffer.join_next_line(separator="") + + @handle("J", filter=vi_selection_mode & ~is_read_only) + def _join_selection(event: E) -> None: + """ + Join selected lines. + """ + event.current_buffer.join_selected_lines() + + @handle("g", "J", filter=vi_selection_mode & ~is_read_only) + def _join_selection_nospace(event: E) -> None: + """ + Join selected lines without space. + """ + event.current_buffer.join_selected_lines(separator="") + + @handle("p", filter=vi_navigation_mode) + def _paste(event: E) -> None: + """ + Paste after + """ + event.current_buffer.paste_clipboard_data( + event.app.clipboard.get_data(), + count=event.arg, + paste_mode=PasteMode.VI_AFTER, + ) + + @handle("P", filter=vi_navigation_mode) + def _paste_before(event: E) -> None: + """ + Paste before + """ + event.current_buffer.paste_clipboard_data( + event.app.clipboard.get_data(), + count=event.arg, + paste_mode=PasteMode.VI_BEFORE, + ) + + @handle('"', Keys.Any, "p", filter=vi_navigation_mode) + def _paste_register(event: E) -> None: + """ + Paste from named register. + """ + c = event.key_sequence[1].data + if c in vi_register_names: + data = event.app.vi_state.named_registers.get(c) + if data: + event.current_buffer.paste_clipboard_data( + data, count=event.arg, paste_mode=PasteMode.VI_AFTER + ) + + @handle('"', Keys.Any, "P", filter=vi_navigation_mode) + def _paste_register_before(event: E) -> None: + """ + Paste (before) from named register. + """ + c = event.key_sequence[1].data + if c in vi_register_names: + data = event.app.vi_state.named_registers.get(c) + if data: + event.current_buffer.paste_clipboard_data( + data, count=event.arg, paste_mode=PasteMode.VI_BEFORE + ) + + @handle("r", filter=vi_navigation_mode) + def _replace(event: E) -> None: + """ + Go to 'replace-single'-mode. + """ + event.app.vi_state.input_mode = InputMode.REPLACE_SINGLE + + @handle("R", filter=vi_navigation_mode) + def _replace_mode(event: E) -> None: + """ + Go to 'replace'-mode. + """ + event.app.vi_state.input_mode = InputMode.REPLACE + + @handle("s", filter=vi_navigation_mode & ~is_read_only) + def _substitute(event: E) -> None: + """ + Substitute with new text + (Delete character(s) and go to insert mode.) + """ + text = event.current_buffer.delete(count=event.arg) + event.app.clipboard.set_text(text) + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("u", filter=vi_navigation_mode, save_before=(lambda e: False)) + def _undo(event: E) -> None: + for i in range(event.arg): + event.current_buffer.undo() + + @handle("V", filter=vi_navigation_mode) + def _visual_line(event: E) -> None: + """ + Start lines selection. + """ + event.current_buffer.start_selection(selection_type=SelectionType.LINES) + + @handle("c-v", filter=vi_navigation_mode) + def _visual_block(event: E) -> None: + """ + Enter block selection mode. + """ + event.current_buffer.start_selection(selection_type=SelectionType.BLOCK) + + @handle("V", filter=vi_selection_mode) + def _visual_line2(event: E) -> None: + """ + Exit line selection mode, or go from non line selection mode to line + selection mode. + """ + selection_state = event.current_buffer.selection_state + + if selection_state is not None: + if selection_state.type != SelectionType.LINES: + selection_state.type = SelectionType.LINES + else: + event.current_buffer.exit_selection() + + @handle("v", filter=vi_navigation_mode) + def _visual(event: E) -> None: + """ + Enter character selection mode. + """ + event.current_buffer.start_selection(selection_type=SelectionType.CHARACTERS) + + @handle("v", filter=vi_selection_mode) + def _visual2(event: E) -> None: + """ + Exit character selection mode, or go from non-character-selection mode + to character selection mode. + """ + selection_state = event.current_buffer.selection_state + + if selection_state is not None: + if selection_state.type != SelectionType.CHARACTERS: + selection_state.type = SelectionType.CHARACTERS + else: + event.current_buffer.exit_selection() + + @handle("c-v", filter=vi_selection_mode) + def _visual_block2(event: E) -> None: + """ + Exit block selection mode, or go from non block selection mode to block + selection mode. + """ + selection_state = event.current_buffer.selection_state + + if selection_state is not None: + if selection_state.type != SelectionType.BLOCK: + selection_state.type = SelectionType.BLOCK + else: + event.current_buffer.exit_selection() + + @handle("a", "w", filter=vi_selection_mode) + @handle("a", "W", filter=vi_selection_mode) + def _visual_auto_word(event: E) -> None: + """ + Switch from visual linewise mode to visual characterwise mode. + """ + buffer = event.current_buffer + + if ( + buffer.selection_state + and buffer.selection_state.type == SelectionType.LINES + ): + buffer.selection_state.type = SelectionType.CHARACTERS + + @handle("x", filter=vi_navigation_mode) + def _delete(event: E) -> None: + """ + Delete character. + """ + buff = event.current_buffer + count = min(event.arg, len(buff.document.current_line_after_cursor)) + if count: + text = event.current_buffer.delete(count=count) + event.app.clipboard.set_text(text) + + @handle("X", filter=vi_navigation_mode) + def _delete_before_cursor(event: E) -> None: + buff = event.current_buffer + count = min(event.arg, len(buff.document.current_line_before_cursor)) + if count: + text = event.current_buffer.delete_before_cursor(count=count) + event.app.clipboard.set_text(text) + + @handle("y", "y", filter=vi_navigation_mode) + @handle("Y", filter=vi_navigation_mode) + def _yank_line(event: E) -> None: + """ + Yank the whole line. + """ + text = "\n".join(event.current_buffer.document.lines_from_current[: event.arg]) + event.app.clipboard.set_data(ClipboardData(text, SelectionType.LINES)) + + @handle("+", filter=vi_navigation_mode) + def _next_line(event: E) -> None: + """ + Move to first non whitespace of next line + """ + buffer = event.current_buffer + buffer.cursor_position += buffer.document.get_cursor_down_position( + count=event.arg + ) + buffer.cursor_position += buffer.document.get_start_of_line_position( + after_whitespace=True + ) + + @handle("-", filter=vi_navigation_mode) + def _prev_line(event: E) -> None: + """ + Move to first non whitespace of previous line + """ + buffer = event.current_buffer + buffer.cursor_position += buffer.document.get_cursor_up_position( + count=event.arg + ) + buffer.cursor_position += buffer.document.get_start_of_line_position( + after_whitespace=True + ) + + @handle(">", ">", filter=vi_navigation_mode) + def _indent(event: E) -> None: + """ + Indent lines. + """ + buffer = event.current_buffer + current_row = buffer.document.cursor_position_row + indent(buffer, current_row, current_row + event.arg) + + @handle("<", "<", filter=vi_navigation_mode) + def _unindent(event: E) -> None: + """ + Unindent lines. + """ + current_row = event.current_buffer.document.cursor_position_row + unindent(event.current_buffer, current_row, current_row + event.arg) + + @handle("O", filter=vi_navigation_mode & ~is_read_only) + def _open_above(event: E) -> None: + """ + Open line above and enter insertion mode + """ + event.current_buffer.insert_line_above(copy_margin=not in_paste_mode()) + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("o", filter=vi_navigation_mode & ~is_read_only) + def _open_below(event: E) -> None: + """ + Open line below and enter insertion mode + """ + event.current_buffer.insert_line_below(copy_margin=not in_paste_mode()) + event.app.vi_state.input_mode = InputMode.INSERT + + @handle("~", filter=vi_navigation_mode) + def _reverse_case(event: E) -> None: + """ + Reverse case of current character and move cursor forward. + """ + buffer = event.current_buffer + c = buffer.document.current_char + + if c is not None and c != "\n": + buffer.insert_text(c.swapcase(), overwrite=True) + + @handle("g", "u", "u", filter=vi_navigation_mode & ~is_read_only) + def _lowercase_line(event: E) -> None: + """ + Lowercase current line. + """ + buff = event.current_buffer + buff.transform_current_line(lambda s: s.lower()) + + @handle("g", "U", "U", filter=vi_navigation_mode & ~is_read_only) + def _uppercase_line(event: E) -> None: + """ + Uppercase current line. + """ + buff = event.current_buffer + buff.transform_current_line(lambda s: s.upper()) + + @handle("g", "~", "~", filter=vi_navigation_mode & ~is_read_only) + def _swapcase_line(event: E) -> None: + """ + Swap case of the current line. + """ + buff = event.current_buffer + buff.transform_current_line(lambda s: s.swapcase()) + + @handle("#", filter=vi_navigation_mode) + def _prev_occurence(event: E) -> None: + """ + Go to previous occurrence of this word. + """ + b = event.current_buffer + search_state = event.app.current_search_state + + search_state.text = b.document.get_word_under_cursor() + search_state.direction = SearchDirection.BACKWARD + + b.apply_search(search_state, count=event.arg, include_current_position=False) + + @handle("*", filter=vi_navigation_mode) + def _next_occurance(event: E) -> None: + """ + Go to next occurrence of this word. + """ + b = event.current_buffer + search_state = event.app.current_search_state + + search_state.text = b.document.get_word_under_cursor() + search_state.direction = SearchDirection.FORWARD + + b.apply_search(search_state, count=event.arg, include_current_position=False) + + @handle("(", filter=vi_navigation_mode) + def _begin_of_sentence(event: E) -> None: + # TODO: go to begin of sentence. + # XXX: should become text_object. + pass + + @handle(")", filter=vi_navigation_mode) + def _end_of_sentence(event: E) -> None: + # TODO: go to end of sentence. + # XXX: should become text_object. + pass + + operator = create_operator_decorator(key_bindings) + text_object = create_text_object_decorator(key_bindings) + + @handle(Keys.Any, filter=vi_waiting_for_text_object_mode) + def _unknown_text_object(event: E) -> None: + """ + Unknown key binding while waiting for a text object. + """ + event.app.output.bell() + + # + # *** Operators *** + # + + def create_delete_and_change_operators( + delete_only: bool, with_register: bool = False + ) -> None: + """ + Delete and change operators. + + :param delete_only: Create an operator that deletes, but doesn't go to insert mode. + :param with_register: Copy the deleted text to this named register instead of the clipboard. + """ + handler_keys: Iterable[str] + if with_register: + handler_keys = ('"', Keys.Any, "cd"[delete_only]) + else: + handler_keys = "cd"[delete_only] + + @operator(*handler_keys, filter=~is_read_only) + def delete_or_change_operator(event: E, text_object: TextObject) -> None: + clipboard_data = None + buff = event.current_buffer + + if text_object: + new_document, clipboard_data = text_object.cut(buff) + buff.document = new_document + + # Set deleted/changed text to clipboard or named register. + if clipboard_data and clipboard_data.text: + if with_register: + reg_name = event.key_sequence[1].data + if reg_name in vi_register_names: + event.app.vi_state.named_registers[reg_name] = clipboard_data + else: + event.app.clipboard.set_data(clipboard_data) + + # Only go back to insert mode in case of 'change'. + if not delete_only: + event.app.vi_state.input_mode = InputMode.INSERT + + create_delete_and_change_operators(False, False) + create_delete_and_change_operators(False, True) + create_delete_and_change_operators(True, False) + create_delete_and_change_operators(True, True) + + def create_transform_handler( + filter: Filter, transform_func: Callable[[str], str], *a: str + ) -> None: + @operator(*a, filter=filter & ~is_read_only) + def _(event: E, text_object: TextObject) -> None: + """ + Apply transformation (uppercase, lowercase, rot13, swap case). + """ + buff = event.current_buffer + start, end = text_object.operator_range(buff.document) + + if start < end: + # Transform. + buff.transform_region( + buff.cursor_position + start, + buff.cursor_position + end, + transform_func, + ) + + # Move cursor + buff.cursor_position += text_object.end or text_object.start + + for k, f, func in vi_transform_functions: + create_transform_handler(f, func, *k) + + @operator("y") + def _yank(event: E, text_object: TextObject) -> None: + """ + Yank operator. (Copy text.) + """ + _, clipboard_data = text_object.cut(event.current_buffer) + if clipboard_data.text: + event.app.clipboard.set_data(clipboard_data) + + @operator('"', Keys.Any, "y") + def _yank_to_register(event: E, text_object: TextObject) -> None: + """ + Yank selection to named register. + """ + c = event.key_sequence[1].data + if c in vi_register_names: + _, clipboard_data = text_object.cut(event.current_buffer) + event.app.vi_state.named_registers[c] = clipboard_data + + @operator(">") + def _indent_text_object(event: E, text_object: TextObject) -> None: + """ + Indent. + """ + buff = event.current_buffer + from_, to = text_object.get_line_numbers(buff) + indent(buff, from_, to + 1, count=event.arg) + + @operator("<") + def _unindent_text_object(event: E, text_object: TextObject) -> None: + """ + Unindent. + """ + buff = event.current_buffer + from_, to = text_object.get_line_numbers(buff) + unindent(buff, from_, to + 1, count=event.arg) + + @operator("g", "q") + def _reshape(event: E, text_object: TextObject) -> None: + """ + Reshape text. + """ + buff = event.current_buffer + from_, to = text_object.get_line_numbers(buff) + reshape_text(buff, from_, to) + + # + # *** Text objects *** + # + + @text_object("b") + def _b(event: E) -> TextObject: + """ + Move one word or token left. + """ + return TextObject( + event.current_buffer.document.find_start_of_previous_word(count=event.arg) + or 0 + ) + + @text_object("B") + def _B(event: E) -> TextObject: + """ + Move one non-blank word left + """ + return TextObject( + event.current_buffer.document.find_start_of_previous_word( + count=event.arg, WORD=True + ) + or 0 + ) + + @text_object("$") + def _dollar(event: E) -> TextObject: + """ + 'c$', 'd$' and '$': Delete/change/move until end of line. + """ + return TextObject(event.current_buffer.document.get_end_of_line_position()) + + @text_object("w") + def _word_forward(event: E) -> TextObject: + """ + 'word' forward. 'cw', 'dw', 'w': Delete/change/move one word. + """ + return TextObject( + event.current_buffer.document.find_next_word_beginning(count=event.arg) + or event.current_buffer.document.get_end_of_document_position() + ) + + @text_object("W") + def _WORD_forward(event: E) -> TextObject: + """ + 'WORD' forward. 'cW', 'dW', 'W': Delete/change/move one WORD. + """ + return TextObject( + event.current_buffer.document.find_next_word_beginning( + count=event.arg, WORD=True + ) + or event.current_buffer.document.get_end_of_document_position() + ) + + @text_object("e") + def _end_of_word(event: E) -> TextObject: + """ + End of 'word': 'ce', 'de', 'e' + """ + end = event.current_buffer.document.find_next_word_ending(count=event.arg) + return TextObject(end - 1 if end else 0, type=TextObjectType.INCLUSIVE) + + @text_object("E") + def _end_of_WORD(event: E) -> TextObject: + """ + End of 'WORD': 'cE', 'dE', 'E' + """ + end = event.current_buffer.document.find_next_word_ending( + count=event.arg, WORD=True + ) + return TextObject(end - 1 if end else 0, type=TextObjectType.INCLUSIVE) + + @text_object("i", "w", no_move_handler=True) + def _inner_word(event: E) -> TextObject: + """ + Inner 'word': ciw and diw + """ + start, end = event.current_buffer.document.find_boundaries_of_current_word() + return TextObject(start, end) + + @text_object("a", "w", no_move_handler=True) + def _a_word(event: E) -> TextObject: + """ + A 'word': caw and daw + """ + start, end = event.current_buffer.document.find_boundaries_of_current_word( + include_trailing_whitespace=True + ) + return TextObject(start, end) + + @text_object("i", "W", no_move_handler=True) + def _inner_WORD(event: E) -> TextObject: + """ + Inner 'WORD': ciW and diW + """ + start, end = event.current_buffer.document.find_boundaries_of_current_word( + WORD=True + ) + return TextObject(start, end) + + @text_object("a", "W", no_move_handler=True) + def _a_WORD(event: E) -> TextObject: + """ + A 'WORD': caw and daw + """ + start, end = event.current_buffer.document.find_boundaries_of_current_word( + WORD=True, include_trailing_whitespace=True + ) + return TextObject(start, end) + + @text_object("a", "p", no_move_handler=True) + def _paragraph(event: E) -> TextObject: + """ + Auto paragraph. + """ + start = event.current_buffer.document.start_of_paragraph() + end = event.current_buffer.document.end_of_paragraph(count=event.arg) + return TextObject(start, end) + + @text_object("^") + def _start_of_line(event: E) -> TextObject: + """ 'c^', 'd^' and '^': Soft start of line, after whitespace. """ + return TextObject( + event.current_buffer.document.get_start_of_line_position( + after_whitespace=True + ) + ) + + @text_object("0") + def _hard_start_of_line(event: E) -> TextObject: + """ + 'c0', 'd0': Hard start of line, before whitespace. + (The move '0' key is implemented elsewhere, because a '0' could also change the `arg`.) + """ + return TextObject( + event.current_buffer.document.get_start_of_line_position( + after_whitespace=False + ) + ) + + def create_ci_ca_handles( + ci_start: str, ci_end: str, inner: bool, key: Optional[str] = None + ) -> None: + # TODO: 'dat', 'dit', (tags (like xml) + """ + Delete/Change string between this start and stop character. But keep these characters. + This implements all the ci", ci<, ci{, ci(, di", di<, ca", ca<, ... combinations. + """ + + def handler(event: E) -> TextObject: + if ci_start == ci_end: + # Quotes + start = event.current_buffer.document.find_backwards( + ci_start, in_current_line=False + ) + end = event.current_buffer.document.find(ci_end, in_current_line=False) + else: + # Brackets + start = event.current_buffer.document.find_enclosing_bracket_left( + ci_start, ci_end + ) + end = event.current_buffer.document.find_enclosing_bracket_right( + ci_start, ci_end + ) + + if start is not None and end is not None: + offset = 0 if inner else 1 + return TextObject(start + 1 - offset, end + offset) + else: + # Nothing found. + return TextObject(0) + + if key is None: + text_object("ai"[inner], ci_start, no_move_handler=True)(handler) + text_object("ai"[inner], ci_end, no_move_handler=True)(handler) + else: + text_object("ai"[inner], key, no_move_handler=True)(handler) + + for inner in (False, True): + for ci_start, ci_end in [ + ('"', '"'), + ("'", "'"), + ("`", "`"), + ("[", "]"), + ("<", ">"), + ("{", "}"), + ("(", ")"), + ]: + create_ci_ca_handles(ci_start, ci_end, inner) + + create_ci_ca_handles("(", ")", inner, "b") # 'dab', 'dib' + create_ci_ca_handles("{", "}", inner, "B") # 'daB', 'diB' + + @text_object("{") + def _previous_section(event: E) -> TextObject: + """ + Move to previous blank-line separated section. + Implements '{', 'c{', 'd{', 'y{' + """ + index = event.current_buffer.document.start_of_paragraph( + count=event.arg, before=True + ) + return TextObject(index) + + @text_object("}") + def _next_section(event: E) -> TextObject: + """ + Move to next blank-line separated section. + Implements '}', 'c}', 'd}', 'y}' + """ + index = event.current_buffer.document.end_of_paragraph( + count=event.arg, after=True + ) + return TextObject(index) + + @text_object("f", Keys.Any) + def _next_occurence(event: E) -> TextObject: + """ + Go to next occurrence of character. Typing 'fx' will move the + cursor to the next occurrence of character. 'x'. + """ + event.app.vi_state.last_character_find = CharacterFind(event.data, False) + match = event.current_buffer.document.find( + event.data, in_current_line=True, count=event.arg + ) + if match: + return TextObject(match, type=TextObjectType.INCLUSIVE) + else: + return TextObject(0) + + @text_object("F", Keys.Any) + def _previous_occurance(event: E) -> TextObject: + """ + Go to previous occurrence of character. Typing 'Fx' will move the + cursor to the previous occurrence of character. 'x'. + """ + event.app.vi_state.last_character_find = CharacterFind(event.data, True) + return TextObject( + event.current_buffer.document.find_backwards( + event.data, in_current_line=True, count=event.arg + ) + or 0 + ) + + @text_object("t", Keys.Any) + def _t(event: E) -> TextObject: + """ + Move right to the next occurrence of c, then one char backward. + """ + event.app.vi_state.last_character_find = CharacterFind(event.data, False) + match = event.current_buffer.document.find( + event.data, in_current_line=True, count=event.arg + ) + if match: + return TextObject(match - 1, type=TextObjectType.INCLUSIVE) + else: + return TextObject(0) + + @text_object("T", Keys.Any) + def _T(event: E) -> TextObject: + """ + Move left to the previous occurrence of c, then one char forward. + """ + event.app.vi_state.last_character_find = CharacterFind(event.data, True) + match = event.current_buffer.document.find_backwards( + event.data, in_current_line=True, count=event.arg + ) + return TextObject(match + 1 if match else 0) + + def repeat(reverse: bool) -> None: + """ + Create ',' and ';' commands. + """ + + @text_object("," if reverse else ";") + def _(event: E) -> TextObject: + """ + Repeat the last 'f'/'F'/'t'/'T' command. + """ + pos: Optional[int] = 0 + vi_state = event.app.vi_state + + type = TextObjectType.EXCLUSIVE + + if vi_state.last_character_find: + char = vi_state.last_character_find.character + backwards = vi_state.last_character_find.backwards + + if reverse: + backwards = not backwards + + if backwards: + pos = event.current_buffer.document.find_backwards( + char, in_current_line=True, count=event.arg + ) + else: + pos = event.current_buffer.document.find( + char, in_current_line=True, count=event.arg + ) + type = TextObjectType.INCLUSIVE + if pos: + return TextObject(pos, type=type) + else: + return TextObject(0) + + repeat(True) + repeat(False) + + @text_object("h") + @text_object("left") + def _left(event: E) -> TextObject: + """ + Implements 'ch', 'dh', 'h': Cursor left. + """ + return TextObject( + event.current_buffer.document.get_cursor_left_position(count=event.arg) + ) + + @text_object("j", no_move_handler=True, no_selection_handler=True) + # Note: We also need `no_selection_handler`, because we in + # selection mode, we prefer the other 'j' binding that keeps + # `buffer.preferred_column`. + def _down(event: E) -> TextObject: + """ + Implements 'cj', 'dj', 'j', ... Cursor up. + """ + return TextObject( + event.current_buffer.document.get_cursor_down_position(count=event.arg), + type=TextObjectType.LINEWISE, + ) + + @text_object("k", no_move_handler=True, no_selection_handler=True) + def _up(event: E) -> TextObject: + """ + Implements 'ck', 'dk', 'k', ... Cursor up. + """ + return TextObject( + event.current_buffer.document.get_cursor_up_position(count=event.arg), + type=TextObjectType.LINEWISE, + ) + + @text_object("l") + @text_object(" ") + @text_object("right") + def _right(event: E) -> TextObject: + """ + Implements 'cl', 'dl', 'l', 'c ', 'd ', ' '. Cursor right. + """ + return TextObject( + event.current_buffer.document.get_cursor_right_position(count=event.arg) + ) + + @text_object("H") + def _top_of_screen(event: E) -> TextObject: + """ + Moves to the start of the visible region. (Below the scroll offset.) + Implements 'cH', 'dH', 'H'. + """ + w = event.app.layout.current_window + b = event.current_buffer + + if w and w.render_info: + # When we find a Window that has BufferControl showing this window, + # move to the start of the visible area. + pos = ( + b.document.translate_row_col_to_index( + w.render_info.first_visible_line(after_scroll_offset=True), 0 + ) + - b.cursor_position + ) + + else: + # Otherwise, move to the start of the input. + pos = -len(b.document.text_before_cursor) + return TextObject(pos, type=TextObjectType.LINEWISE) + + @text_object("M") + def _middle_of_screen(event: E) -> TextObject: + """ + Moves cursor to the vertical center of the visible region. + Implements 'cM', 'dM', 'M'. + """ + w = event.app.layout.current_window + b = event.current_buffer + + if w and w.render_info: + # When we find a Window that has BufferControl showing this window, + # move to the center of the visible area. + pos = ( + b.document.translate_row_col_to_index( + w.render_info.center_visible_line(), 0 + ) + - b.cursor_position + ) + + else: + # Otherwise, move to the start of the input. + pos = -len(b.document.text_before_cursor) + return TextObject(pos, type=TextObjectType.LINEWISE) + + @text_object("L") + def _end_of_screen(event: E) -> TextObject: + """ + Moves to the end of the visible region. (Above the scroll offset.) + """ + w = event.app.layout.current_window + b = event.current_buffer + + if w and w.render_info: + # When we find a Window that has BufferControl showing this window, + # move to the end of the visible area. + pos = ( + b.document.translate_row_col_to_index( + w.render_info.last_visible_line(before_scroll_offset=True), 0 + ) + - b.cursor_position + ) + + else: + # Otherwise, move to the end of the input. + pos = len(b.document.text_after_cursor) + return TextObject(pos, type=TextObjectType.LINEWISE) + + @text_object("n", no_move_handler=True) + def _search_next(event: E) -> TextObject: + """ + Search next. + """ + buff = event.current_buffer + search_state = event.app.current_search_state + + cursor_position = buff.get_search_position( + search_state, include_current_position=False, count=event.arg + ) + return TextObject(cursor_position - buff.cursor_position) + + @handle("n", filter=vi_navigation_mode) + def _search_next2(event: E) -> None: + """ + Search next in navigation mode. (This goes through the history.) + """ + search_state = event.app.current_search_state + + event.current_buffer.apply_search( + search_state, include_current_position=False, count=event.arg + ) + + @text_object("N", no_move_handler=True) + def _search_previous(event: E) -> TextObject: + """ + Search previous. + """ + buff = event.current_buffer + search_state = event.app.current_search_state + + cursor_position = buff.get_search_position( + ~search_state, include_current_position=False, count=event.arg + ) + return TextObject(cursor_position - buff.cursor_position) + + @handle("N", filter=vi_navigation_mode) + def _search_previous2(event: E) -> None: + """ + Search previous in navigation mode. (This goes through the history.) + """ + search_state = event.app.current_search_state + + event.current_buffer.apply_search( + ~search_state, include_current_position=False, count=event.arg + ) + + @handle("z", "+", filter=vi_navigation_mode | vi_selection_mode) + @handle("z", "t", filter=vi_navigation_mode | vi_selection_mode) + @handle("z", "enter", filter=vi_navigation_mode | vi_selection_mode) + def _scroll_top(event: E) -> None: + """ + Scrolls the window to makes the current line the first line in the visible region. + """ + b = event.current_buffer + event.app.layout.current_window.vertical_scroll = b.document.cursor_position_row + + @handle("z", "-", filter=vi_navigation_mode | vi_selection_mode) + @handle("z", "b", filter=vi_navigation_mode | vi_selection_mode) + def _scroll_bottom(event: E) -> None: + """ + Scrolls the window to makes the current line the last line in the visible region. + """ + # We can safely set the scroll offset to zero; the Window will make + # sure that it scrolls at least enough to make the cursor visible + # again. + event.app.layout.current_window.vertical_scroll = 0 + + @handle("z", "z", filter=vi_navigation_mode | vi_selection_mode) + def _scroll_center(event: E) -> None: + """ + Center Window vertically around cursor. + """ + w = event.app.layout.current_window + b = event.current_buffer + + if w and w.render_info: + info = w.render_info + + # Calculate the offset that we need in order to position the row + # containing the cursor in the center. + scroll_height = info.window_height // 2 + + y = max(0, b.document.cursor_position_row - 1) + height = 0 + while y > 0: + line_height = info.get_height_for_line(y) + + if height + line_height < scroll_height: + height += line_height + y -= 1 + else: + break + + w.vertical_scroll = y + + @text_object("%") + def _goto_corresponding_bracket(event: E) -> TextObject: + """ + Implements 'c%', 'd%', '%, 'y%' (Move to corresponding bracket.) + If an 'arg' has been given, go this this % position in the file. + """ + buffer = event.current_buffer + + if event._arg: + # If 'arg' has been given, the meaning of % is to go to the 'x%' + # row in the file. + if 0 < event.arg <= 100: + absolute_index = buffer.document.translate_row_col_to_index( + int((event.arg * buffer.document.line_count - 1) / 100), 0 + ) + return TextObject( + absolute_index - buffer.document.cursor_position, + type=TextObjectType.LINEWISE, + ) + else: + return TextObject(0) # Do nothing. + + else: + # Move to the corresponding opening/closing bracket (()'s, []'s and {}'s). + match = buffer.document.find_matching_bracket_position() + if match: + return TextObject(match, type=TextObjectType.INCLUSIVE) + else: + return TextObject(0) + + @text_object("|") + def _to_column(event: E) -> TextObject: + """ + Move to the n-th column (you may specify the argument n by typing it on + number keys, for example, 20|). + """ + return TextObject( + event.current_buffer.document.get_column_cursor_position(event.arg - 1) + ) + + @text_object("g", "g") + def _goto_first_line(event: E) -> TextObject: + """ + Go to the start of the very first line. + Implements 'gg', 'cgg', 'ygg' + """ + d = event.current_buffer.document + + if event._arg: + # Move to the given line. + return TextObject( + d.translate_row_col_to_index(event.arg - 1, 0) - d.cursor_position, + type=TextObjectType.LINEWISE, + ) + else: + # Move to the top of the input. + return TextObject( + d.get_start_of_document_position(), type=TextObjectType.LINEWISE + ) + + @text_object("g", "_") + def _goto_last_line(event: E) -> TextObject: + """ + Go to last non-blank of line. + 'g_', 'cg_', 'yg_', etc.. + """ + return TextObject( + event.current_buffer.document.last_non_blank_of_current_line_position(), + type=TextObjectType.INCLUSIVE, + ) + + @text_object("g", "e") + def _ge(event: E) -> TextObject: + """ + Go to last character of previous word. + 'ge', 'cge', 'yge', etc.. + """ + prev_end = event.current_buffer.document.find_previous_word_ending( + count=event.arg + ) + return TextObject( + prev_end - 1 if prev_end is not None else 0, type=TextObjectType.INCLUSIVE + ) + + @text_object("g", "E") + def _gE(event: E) -> TextObject: + """ + Go to last character of previous WORD. + 'gE', 'cgE', 'ygE', etc.. + """ + prev_end = event.current_buffer.document.find_previous_word_ending( + count=event.arg, WORD=True + ) + return TextObject( + prev_end - 1 if prev_end is not None else 0, type=TextObjectType.INCLUSIVE + ) + + @text_object("g", "m") + def _gm(event: E) -> TextObject: + """ + Like g0, but half a screenwidth to the right. (Or as much as possible.) + """ + w = event.app.layout.current_window + buff = event.current_buffer + + if w and w.render_info: + width = w.render_info.window_width + start = buff.document.get_start_of_line_position(after_whitespace=False) + start += int(min(width / 2, len(buff.document.current_line))) + + return TextObject(start, type=TextObjectType.INCLUSIVE) + return TextObject(0) + + @text_object("G") + def _last_line(event: E) -> TextObject: + """ + Go to the end of the document. (If no arg has been given.) + """ + buf = event.current_buffer + return TextObject( + buf.document.translate_row_col_to_index(buf.document.line_count - 1, 0) + - buf.cursor_position, + type=TextObjectType.LINEWISE, + ) + + # + # *** Other *** + # + + @handle("G", filter=has_arg) + def _to_nth_history_line(event: E) -> None: + """ + If an argument is given, move to this line in the history. (for + example, 15G) + """ + event.current_buffer.go_to_history(event.arg - 1) + + for n in "123456789": + + @handle( + n, + filter=vi_navigation_mode + | vi_selection_mode + | vi_waiting_for_text_object_mode, + ) + def _arg(event: E) -> None: + """ + Always handle numberics in navigation mode as arg. + """ + event.append_to_arg_count(event.data) + + @handle( + "0", + filter=( + vi_navigation_mode | vi_selection_mode | vi_waiting_for_text_object_mode + ) + & has_arg, + ) + def _0_arg(event: E) -> None: + """ + Zero when an argument was already give. + """ + event.append_to_arg_count(event.data) + + @handle(Keys.Any, filter=vi_replace_mode) + def _insert_text(event: E) -> None: + """ + Insert data at cursor position. + """ + event.current_buffer.insert_text(event.data, overwrite=True) + + @handle(Keys.Any, filter=vi_replace_single_mode) + def _replace_single(event: E) -> None: + """ + Replace single character at cursor position. + """ + event.current_buffer.insert_text(event.data, overwrite=True) + event.current_buffer.cursor_position -= 1 + event.app.vi_state.input_mode = InputMode.NAVIGATION + + @handle( + Keys.Any, + filter=vi_insert_multiple_mode, + save_before=(lambda e: not e.is_repeat), + ) + def _insert_text_multiple_cursors(event: E) -> None: + """ + Insert data at multiple cursor positions at once. + (Usually a result of pressing 'I' or 'A' in block-selection mode.) + """ + buff = event.current_buffer + original_text = buff.text + + # Construct new text. + text = [] + p = 0 + + for p2 in buff.multiple_cursor_positions: + text.append(original_text[p:p2]) + text.append(event.data) + p = p2 + + text.append(original_text[p:]) + + # Shift all cursor positions. + new_cursor_positions = [ + pos + i + 1 for i, pos in enumerate(buff.multiple_cursor_positions) + ] + + # Set result. + buff.text = "".join(text) + buff.multiple_cursor_positions = new_cursor_positions + buff.cursor_position += 1 + + @handle("backspace", filter=vi_insert_multiple_mode) + def _delete_before_multiple_cursors(event: E) -> None: + """ + Backspace, using multiple cursors. + """ + buff = event.current_buffer + original_text = buff.text + + # Construct new text. + deleted_something = False + text = [] + p = 0 + + for p2 in buff.multiple_cursor_positions: + if p2 > 0 and original_text[p2 - 1] != "\n": # Don't delete across lines. + text.append(original_text[p : p2 - 1]) + deleted_something = True + else: + text.append(original_text[p:p2]) + p = p2 + + text.append(original_text[p:]) + + if deleted_something: + # Shift all cursor positions. + lengths = [len(part) for part in text[:-1]] + new_cursor_positions = list(accumulate(lengths)) + + # Set result. + buff.text = "".join(text) + buff.multiple_cursor_positions = new_cursor_positions + buff.cursor_position -= 1 + else: + event.app.output.bell() + + @handle("delete", filter=vi_insert_multiple_mode) + def _delete_after_multiple_cursors(event): + """ + Delete, using multiple cursors. + """ + buff = event.current_buffer + original_text = buff.text + + # Construct new text. + deleted_something = False + text = [] + new_cursor_positions = [] + p = 0 + + for p2 in buff.multiple_cursor_positions: + text.append(original_text[p:p2]) + if p2 >= len(original_text) or original_text[p2] == "\n": + # Don't delete across lines. + p = p2 + else: + p = p2 + 1 + deleted_something = True + + text.append(original_text[p:]) + + if deleted_something: + # Shift all cursor positions. + lengths = [len(part) for part in text[:-1]] + new_cursor_positions = list(accumulate(lengths)) + + # Set result. + buff.text = "".join(text) + buff.multiple_cursor_positions = new_cursor_positions + else: + event.app.output.bell() + + @handle("left", filter=vi_insert_multiple_mode) + def _left_multiple(event: E) -> None: + """ + Move all cursors to the left. + (But keep all cursors on the same line.) + """ + buff = event.current_buffer + new_positions = [] + + for p in buff.multiple_cursor_positions: + if buff.document.translate_index_to_position(p)[1] > 0: + p -= 1 + new_positions.append(p) + + buff.multiple_cursor_positions = new_positions + + if buff.document.cursor_position_col > 0: + buff.cursor_position -= 1 + + @handle("right", filter=vi_insert_multiple_mode) + def _right_multiple(event: E) -> None: + """ + Move all cursors to the right. + (But keep all cursors on the same line.) + """ + buff = event.current_buffer + new_positions = [] + + for p in buff.multiple_cursor_positions: + row, column = buff.document.translate_index_to_position(p) + if column < len(buff.document.lines[row]): + p += 1 + new_positions.append(p) + + buff.multiple_cursor_positions = new_positions + + if not buff.document.is_cursor_at_the_end_of_line: + buff.cursor_position += 1 + + @handle("up", filter=vi_insert_multiple_mode) + @handle("down", filter=vi_insert_multiple_mode) + def _updown_multiple(event: E) -> None: + """ + Ignore all up/down key presses when in multiple cursor mode. + """ + + @handle("c-x", "c-l", filter=vi_insert_mode) + def _complete_line(event: E) -> None: + """ + Pressing the ControlX - ControlL sequence in Vi mode does line + completion based on the other lines in the document and the history. + """ + event.current_buffer.start_history_lines_completion() + + @handle("c-x", "c-f", filter=vi_insert_mode) + def _complete_filename(event: E) -> None: + """ + Complete file names. + """ + # TODO + pass + + @handle("c-k", filter=vi_insert_mode | vi_replace_mode) + def _digraph(event: E) -> None: + """ + Go into digraph mode. + """ + event.app.vi_state.waiting_for_digraph = True + + @Condition + def digraph_symbol_1_given() -> bool: + return get_app().vi_state.digraph_symbol1 is not None + + @handle(Keys.Any, filter=vi_digraph_mode & ~digraph_symbol_1_given) + def _digraph1(event: E) -> None: + """ + First digraph symbol. + """ + event.app.vi_state.digraph_symbol1 = event.data + + @handle(Keys.Any, filter=vi_digraph_mode & digraph_symbol_1_given) + def _create_digraph(event: E) -> None: + """ + Insert digraph. + """ + try: + # Lookup. + code: Tuple[str, str] = ( + event.app.vi_state.digraph_symbol1 or "", + event.data, + ) + if code not in DIGRAPHS: + code = code[::-1] # Try reversing. + symbol = DIGRAPHS[code] + except KeyError: + # Unknown digraph. + event.app.output.bell() + else: + # Insert digraph. + overwrite = event.app.vi_state.input_mode == InputMode.REPLACE + event.current_buffer.insert_text(chr(symbol), overwrite=overwrite) + event.app.vi_state.waiting_for_digraph = False + finally: + event.app.vi_state.waiting_for_digraph = False + event.app.vi_state.digraph_symbol1 = None + + @handle("c-o", filter=vi_insert_mode | vi_replace_mode) + def _quick_normal_mode(event: E) -> None: + """ + Go into normal mode for one single action. + """ + event.app.vi_state.temporary_navigation_mode = True + + @handle("q", Keys.Any, filter=vi_navigation_mode & ~vi_recording_macro) + def _start_macro(event: E) -> None: + """ + Start recording macro. + """ + c = event.key_sequence[1].data + if c in vi_register_names: + vi_state = event.app.vi_state + + vi_state.recording_register = c + vi_state.current_recording = "" + + @handle("q", filter=vi_navigation_mode & vi_recording_macro) + def _stop_macro(event: E) -> None: + """ + Stop recording macro. + """ + vi_state = event.app.vi_state + + # Store and stop recording. + if vi_state.recording_register: + vi_state.named_registers[vi_state.recording_register] = ClipboardData( + vi_state.current_recording + ) + vi_state.recording_register = None + vi_state.current_recording = "" + + @handle("@", Keys.Any, filter=vi_navigation_mode, record_in_macro=False) + def _execute_macro(event: E) -> None: + """ + Execute macro. + + Notice that we pass `record_in_macro=False`. This ensures that the `@x` + keys don't appear in the recording itself. This function inserts the + body of the called macro back into the KeyProcessor, so these keys will + be added later on to the macro of their handlers have + `record_in_macro=True`. + """ + # Retrieve macro. + c = event.key_sequence[1].data + try: + macro = event.app.vi_state.named_registers[c] + except KeyError: + return + + # Expand macro (which is a string in the register), in individual keys. + # Use vt100 parser for this. + keys: List[KeyPress] = [] + + parser = Vt100Parser(keys.append) + parser.feed(macro.text) + parser.flush() + + # Now feed keys back to the input processor. + for _ in range(event.arg): + event.app.key_processor.feed_multiple(keys, first=True) + + return ConditionalKeyBindings(key_bindings, vi_mode) + + +def load_vi_search_bindings() -> KeyBindingsBase: + key_bindings = KeyBindings() + handle = key_bindings.add + from . import search + + @Condition + def search_buffer_is_empty() -> bool: + " Returns True when the search buffer is empty. " + return get_app().current_buffer.text == "" + + # Vi-style forward search. + handle( + "/", + filter=(vi_navigation_mode | vi_selection_mode) & ~vi_search_direction_reversed, + )(search.start_forward_incremental_search) + handle( + "?", + filter=(vi_navigation_mode | vi_selection_mode) & vi_search_direction_reversed, + )(search.start_forward_incremental_search) + handle("c-s")(search.start_forward_incremental_search) + + # Vi-style backward search. + handle( + "?", + filter=(vi_navigation_mode | vi_selection_mode) & ~vi_search_direction_reversed, + )(search.start_reverse_incremental_search) + handle( + "/", + filter=(vi_navigation_mode | vi_selection_mode) & vi_search_direction_reversed, + )(search.start_reverse_incremental_search) + handle("c-r")(search.start_reverse_incremental_search) + + # Apply the search. (At the / or ? prompt.) + handle("enter", filter=is_searching)(search.accept_search) + + handle("c-r", filter=is_searching)(search.reverse_incremental_search) + handle("c-s", filter=is_searching)(search.forward_incremental_search) + + handle("c-c")(search.abort_search) + handle("c-g")(search.abort_search) + handle("backspace", filter=search_buffer_is_empty)(search.abort_search) + + # Handle escape. This should accept the search, just like readline. + # `abort_search` would be a meaningful alternative. + handle("escape")(search.accept_search) + + return ConditionalKeyBindings(key_bindings, vi_mode) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/defaults.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/defaults.py new file mode 100644 index 00000000..baa59743 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/defaults.py @@ -0,0 +1,60 @@ +""" +Default key bindings.:: + + key_bindings = load_key_bindings() + app = Application(key_bindings=key_bindings) +""" +from prompt_toolkit.filters import buffer_has_focus +from prompt_toolkit.key_binding.bindings.basic import load_basic_bindings +from prompt_toolkit.key_binding.bindings.cpr import load_cpr_bindings +from prompt_toolkit.key_binding.bindings.emacs import ( + load_emacs_bindings, + load_emacs_search_bindings, + load_emacs_shift_selection_bindings, +) +from prompt_toolkit.key_binding.bindings.mouse import load_mouse_bindings +from prompt_toolkit.key_binding.bindings.vi import ( + load_vi_bindings, + load_vi_search_bindings, +) +from prompt_toolkit.key_binding.key_bindings import ( + ConditionalKeyBindings, + KeyBindingsBase, + merge_key_bindings, +) + +__all__ = [ + "load_key_bindings", +] + + +def load_key_bindings() -> KeyBindingsBase: + """ + Create a KeyBindings object that contains the default key bindings. + """ + all_bindings = merge_key_bindings( + [ + # Load basic bindings. + load_basic_bindings(), + # Load emacs bindings. + load_emacs_bindings(), + load_emacs_search_bindings(), + load_emacs_shift_selection_bindings(), + # Load Vi bindings. + load_vi_bindings(), + load_vi_search_bindings(), + ] + ) + + return merge_key_bindings( + [ + # Make sure that the above key bindings are only active if the + # currently focused control is a `BufferControl`. For other controls, we + # don't want these key bindings to intervene. (This would break "ptterm" + # for instance, which handles 'Keys.Any' in the user control itself.) + ConditionalKeyBindings(all_bindings, buffer_has_focus), + # Active, even when no buffer has been focused. + load_mouse_bindings(), + load_cpr_bindings(), + ] + ) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/digraphs.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/digraphs.py new file mode 100644 index 00000000..70606086 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/digraphs.py @@ -0,0 +1,1378 @@ +# encoding: utf-8 +""" +Vi Digraphs. +This is a list of special characters that can be inserted in Vi insert mode by +pressing Control-K followed by to normal characters. + +Taken from Neovim and translated to Python: +https://raw.githubusercontent.com/neovim/neovim/master/src/nvim/digraph.c +""" +from typing import Dict, Tuple + +__all__ = [ + "DIGRAPHS", +] + +# digraphs for Unicode from RFC1345 +# (also work for ISO-8859-1 aka latin1) +DIGRAPHS: Dict[Tuple[str, str], int] = { + ("N", "U"): 0x00, + ("S", "H"): 0x01, + ("S", "X"): 0x02, + ("E", "X"): 0x03, + ("E", "T"): 0x04, + ("E", "Q"): 0x05, + ("A", "K"): 0x06, + ("B", "L"): 0x07, + ("B", "S"): 0x08, + ("H", "T"): 0x09, + ("L", "F"): 0x0A, + ("V", "T"): 0x0B, + ("F", "F"): 0x0C, + ("C", "R"): 0x0D, + ("S", "O"): 0x0E, + ("S", "I"): 0x0F, + ("D", "L"): 0x10, + ("D", "1"): 0x11, + ("D", "2"): 0x12, + ("D", "3"): 0x13, + ("D", "4"): 0x14, + ("N", "K"): 0x15, + ("S", "Y"): 0x16, + ("E", "B"): 0x17, + ("C", "N"): 0x18, + ("E", "M"): 0x19, + ("S", "B"): 0x1A, + ("E", "C"): 0x1B, + ("F", "S"): 0x1C, + ("G", "S"): 0x1D, + ("R", "S"): 0x1E, + ("U", "S"): 0x1F, + ("S", "P"): 0x20, + ("N", "b"): 0x23, + ("D", "O"): 0x24, + ("A", "t"): 0x40, + ("<", "("): 0x5B, + ("/", "/"): 0x5C, + (")", ">"): 0x5D, + ("'", ">"): 0x5E, + ("'", "!"): 0x60, + ("(", "!"): 0x7B, + ("!", "!"): 0x7C, + ("!", ")"): 0x7D, + ("'", "?"): 0x7E, + ("D", "T"): 0x7F, + ("P", "A"): 0x80, + ("H", "O"): 0x81, + ("B", "H"): 0x82, + ("N", "H"): 0x83, + ("I", "N"): 0x84, + ("N", "L"): 0x85, + ("S", "A"): 0x86, + ("E", "S"): 0x87, + ("H", "S"): 0x88, + ("H", "J"): 0x89, + ("V", "S"): 0x8A, + ("P", "D"): 0x8B, + ("P", "U"): 0x8C, + ("R", "I"): 0x8D, + ("S", "2"): 0x8E, + ("S", "3"): 0x8F, + ("D", "C"): 0x90, + ("P", "1"): 0x91, + ("P", "2"): 0x92, + ("T", "S"): 0x93, + ("C", "C"): 0x94, + ("M", "W"): 0x95, + ("S", "G"): 0x96, + ("E", "G"): 0x97, + ("S", "S"): 0x98, + ("G", "C"): 0x99, + ("S", "C"): 0x9A, + ("C", "I"): 0x9B, + ("S", "T"): 0x9C, + ("O", "C"): 0x9D, + ("P", "M"): 0x9E, + ("A", "C"): 0x9F, + ("N", "S"): 0xA0, + ("!", "I"): 0xA1, + ("C", "t"): 0xA2, + ("P", "d"): 0xA3, + ("C", "u"): 0xA4, + ("Y", "e"): 0xA5, + ("B", "B"): 0xA6, + ("S", "E"): 0xA7, + ("'", ":"): 0xA8, + ("C", "o"): 0xA9, + ("-", "a"): 0xAA, + ("<", "<"): 0xAB, + ("N", "O"): 0xAC, + ("-", "-"): 0xAD, + ("R", "g"): 0xAE, + ("'", "m"): 0xAF, + ("D", "G"): 0xB0, + ("+", "-"): 0xB1, + ("2", "S"): 0xB2, + ("3", "S"): 0xB3, + ("'", "'"): 0xB4, + ("M", "y"): 0xB5, + ("P", "I"): 0xB6, + (".", "M"): 0xB7, + ("'", ","): 0xB8, + ("1", "S"): 0xB9, + ("-", "o"): 0xBA, + (">", ">"): 0xBB, + ("1", "4"): 0xBC, + ("1", "2"): 0xBD, + ("3", "4"): 0xBE, + ("?", "I"): 0xBF, + ("A", "!"): 0xC0, + ("A", "'"): 0xC1, + ("A", ">"): 0xC2, + ("A", "?"): 0xC3, + ("A", ":"): 0xC4, + ("A", "A"): 0xC5, + ("A", "E"): 0xC6, + ("C", ","): 0xC7, + ("E", "!"): 0xC8, + ("E", "'"): 0xC9, + ("E", ">"): 0xCA, + ("E", ":"): 0xCB, + ("I", "!"): 0xCC, + ("I", "'"): 0xCD, + ("I", ">"): 0xCE, + ("I", ":"): 0xCF, + ("D", "-"): 0xD0, + ("N", "?"): 0xD1, + ("O", "!"): 0xD2, + ("O", "'"): 0xD3, + ("O", ">"): 0xD4, + ("O", "?"): 0xD5, + ("O", ":"): 0xD6, + ("*", "X"): 0xD7, + ("O", "/"): 0xD8, + ("U", "!"): 0xD9, + ("U", "'"): 0xDA, + ("U", ">"): 0xDB, + ("U", ":"): 0xDC, + ("Y", "'"): 0xDD, + ("T", "H"): 0xDE, + ("s", "s"): 0xDF, + ("a", "!"): 0xE0, + ("a", "'"): 0xE1, + ("a", ">"): 0xE2, + ("a", "?"): 0xE3, + ("a", ":"): 0xE4, + ("a", "a"): 0xE5, + ("a", "e"): 0xE6, + ("c", ","): 0xE7, + ("e", "!"): 0xE8, + ("e", "'"): 0xE9, + ("e", ">"): 0xEA, + ("e", ":"): 0xEB, + ("i", "!"): 0xEC, + ("i", "'"): 0xED, + ("i", ">"): 0xEE, + ("i", ":"): 0xEF, + ("d", "-"): 0xF0, + ("n", "?"): 0xF1, + ("o", "!"): 0xF2, + ("o", "'"): 0xF3, + ("o", ">"): 0xF4, + ("o", "?"): 0xF5, + ("o", ":"): 0xF6, + ("-", ":"): 0xF7, + ("o", "/"): 0xF8, + ("u", "!"): 0xF9, + ("u", "'"): 0xFA, + ("u", ">"): 0xFB, + ("u", ":"): 0xFC, + ("y", "'"): 0xFD, + ("t", "h"): 0xFE, + ("y", ":"): 0xFF, + ("A", "-"): 0x0100, + ("a", "-"): 0x0101, + ("A", "("): 0x0102, + ("a", "("): 0x0103, + ("A", ";"): 0x0104, + ("a", ";"): 0x0105, + ("C", "'"): 0x0106, + ("c", "'"): 0x0107, + ("C", ">"): 0x0108, + ("c", ">"): 0x0109, + ("C", "."): 0x010A, + ("c", "."): 0x010B, + ("C", "<"): 0x010C, + ("c", "<"): 0x010D, + ("D", "<"): 0x010E, + ("d", "<"): 0x010F, + ("D", "/"): 0x0110, + ("d", "/"): 0x0111, + ("E", "-"): 0x0112, + ("e", "-"): 0x0113, + ("E", "("): 0x0114, + ("e", "("): 0x0115, + ("E", "."): 0x0116, + ("e", "."): 0x0117, + ("E", ";"): 0x0118, + ("e", ";"): 0x0119, + ("E", "<"): 0x011A, + ("e", "<"): 0x011B, + ("G", ">"): 0x011C, + ("g", ">"): 0x011D, + ("G", "("): 0x011E, + ("g", "("): 0x011F, + ("G", "."): 0x0120, + ("g", "."): 0x0121, + ("G", ","): 0x0122, + ("g", ","): 0x0123, + ("H", ">"): 0x0124, + ("h", ">"): 0x0125, + ("H", "/"): 0x0126, + ("h", "/"): 0x0127, + ("I", "?"): 0x0128, + ("i", "?"): 0x0129, + ("I", "-"): 0x012A, + ("i", "-"): 0x012B, + ("I", "("): 0x012C, + ("i", "("): 0x012D, + ("I", ";"): 0x012E, + ("i", ";"): 0x012F, + ("I", "."): 0x0130, + ("i", "."): 0x0131, + ("I", "J"): 0x0132, + ("i", "j"): 0x0133, + ("J", ">"): 0x0134, + ("j", ">"): 0x0135, + ("K", ","): 0x0136, + ("k", ","): 0x0137, + ("k", "k"): 0x0138, + ("L", "'"): 0x0139, + ("l", "'"): 0x013A, + ("L", ","): 0x013B, + ("l", ","): 0x013C, + ("L", "<"): 0x013D, + ("l", "<"): 0x013E, + ("L", "."): 0x013F, + ("l", "."): 0x0140, + ("L", "/"): 0x0141, + ("l", "/"): 0x0142, + ("N", "'"): 0x0143, + ("n", "'"): 0x0144, + ("N", ","): 0x0145, + ("n", ","): 0x0146, + ("N", "<"): 0x0147, + ("n", "<"): 0x0148, + ("'", "n"): 0x0149, + ("N", "G"): 0x014A, + ("n", "g"): 0x014B, + ("O", "-"): 0x014C, + ("o", "-"): 0x014D, + ("O", "("): 0x014E, + ("o", "("): 0x014F, + ("O", '"'): 0x0150, + ("o", '"'): 0x0151, + ("O", "E"): 0x0152, + ("o", "e"): 0x0153, + ("R", "'"): 0x0154, + ("r", "'"): 0x0155, + ("R", ","): 0x0156, + ("r", ","): 0x0157, + ("R", "<"): 0x0158, + ("r", "<"): 0x0159, + ("S", "'"): 0x015A, + ("s", "'"): 0x015B, + ("S", ">"): 0x015C, + ("s", ">"): 0x015D, + ("S", ","): 0x015E, + ("s", ","): 0x015F, + ("S", "<"): 0x0160, + ("s", "<"): 0x0161, + ("T", ","): 0x0162, + ("t", ","): 0x0163, + ("T", "<"): 0x0164, + ("t", "<"): 0x0165, + ("T", "/"): 0x0166, + ("t", "/"): 0x0167, + ("U", "?"): 0x0168, + ("u", "?"): 0x0169, + ("U", "-"): 0x016A, + ("u", "-"): 0x016B, + ("U", "("): 0x016C, + ("u", "("): 0x016D, + ("U", "0"): 0x016E, + ("u", "0"): 0x016F, + ("U", '"'): 0x0170, + ("u", '"'): 0x0171, + ("U", ";"): 0x0172, + ("u", ";"): 0x0173, + ("W", ">"): 0x0174, + ("w", ">"): 0x0175, + ("Y", ">"): 0x0176, + ("y", ">"): 0x0177, + ("Y", ":"): 0x0178, + ("Z", "'"): 0x0179, + ("z", "'"): 0x017A, + ("Z", "."): 0x017B, + ("z", "."): 0x017C, + ("Z", "<"): 0x017D, + ("z", "<"): 0x017E, + ("O", "9"): 0x01A0, + ("o", "9"): 0x01A1, + ("O", "I"): 0x01A2, + ("o", "i"): 0x01A3, + ("y", "r"): 0x01A6, + ("U", "9"): 0x01AF, + ("u", "9"): 0x01B0, + ("Z", "/"): 0x01B5, + ("z", "/"): 0x01B6, + ("E", "D"): 0x01B7, + ("A", "<"): 0x01CD, + ("a", "<"): 0x01CE, + ("I", "<"): 0x01CF, + ("i", "<"): 0x01D0, + ("O", "<"): 0x01D1, + ("o", "<"): 0x01D2, + ("U", "<"): 0x01D3, + ("u", "<"): 0x01D4, + ("A", "1"): 0x01DE, + ("a", "1"): 0x01DF, + ("A", "7"): 0x01E0, + ("a", "7"): 0x01E1, + ("A", "3"): 0x01E2, + ("a", "3"): 0x01E3, + ("G", "/"): 0x01E4, + ("g", "/"): 0x01E5, + ("G", "<"): 0x01E6, + ("g", "<"): 0x01E7, + ("K", "<"): 0x01E8, + ("k", "<"): 0x01E9, + ("O", ";"): 0x01EA, + ("o", ";"): 0x01EB, + ("O", "1"): 0x01EC, + ("o", "1"): 0x01ED, + ("E", "Z"): 0x01EE, + ("e", "z"): 0x01EF, + ("j", "<"): 0x01F0, + ("G", "'"): 0x01F4, + ("g", "'"): 0x01F5, + (";", "S"): 0x02BF, + ("'", "<"): 0x02C7, + ("'", "("): 0x02D8, + ("'", "."): 0x02D9, + ("'", "0"): 0x02DA, + ("'", ";"): 0x02DB, + ("'", '"'): 0x02DD, + ("A", "%"): 0x0386, + ("E", "%"): 0x0388, + ("Y", "%"): 0x0389, + ("I", "%"): 0x038A, + ("O", "%"): 0x038C, + ("U", "%"): 0x038E, + ("W", "%"): 0x038F, + ("i", "3"): 0x0390, + ("A", "*"): 0x0391, + ("B", "*"): 0x0392, + ("G", "*"): 0x0393, + ("D", "*"): 0x0394, + ("E", "*"): 0x0395, + ("Z", "*"): 0x0396, + ("Y", "*"): 0x0397, + ("H", "*"): 0x0398, + ("I", "*"): 0x0399, + ("K", "*"): 0x039A, + ("L", "*"): 0x039B, + ("M", "*"): 0x039C, + ("N", "*"): 0x039D, + ("C", "*"): 0x039E, + ("O", "*"): 0x039F, + ("P", "*"): 0x03A0, + ("R", "*"): 0x03A1, + ("S", "*"): 0x03A3, + ("T", "*"): 0x03A4, + ("U", "*"): 0x03A5, + ("F", "*"): 0x03A6, + ("X", "*"): 0x03A7, + ("Q", "*"): 0x03A8, + ("W", "*"): 0x03A9, + ("J", "*"): 0x03AA, + ("V", "*"): 0x03AB, + ("a", "%"): 0x03AC, + ("e", "%"): 0x03AD, + ("y", "%"): 0x03AE, + ("i", "%"): 0x03AF, + ("u", "3"): 0x03B0, + ("a", "*"): 0x03B1, + ("b", "*"): 0x03B2, + ("g", "*"): 0x03B3, + ("d", "*"): 0x03B4, + ("e", "*"): 0x03B5, + ("z", "*"): 0x03B6, + ("y", "*"): 0x03B7, + ("h", "*"): 0x03B8, + ("i", "*"): 0x03B9, + ("k", "*"): 0x03BA, + ("l", "*"): 0x03BB, + ("m", "*"): 0x03BC, + ("n", "*"): 0x03BD, + ("c", "*"): 0x03BE, + ("o", "*"): 0x03BF, + ("p", "*"): 0x03C0, + ("r", "*"): 0x03C1, + ("*", "s"): 0x03C2, + ("s", "*"): 0x03C3, + ("t", "*"): 0x03C4, + ("u", "*"): 0x03C5, + ("f", "*"): 0x03C6, + ("x", "*"): 0x03C7, + ("q", "*"): 0x03C8, + ("w", "*"): 0x03C9, + ("j", "*"): 0x03CA, + ("v", "*"): 0x03CB, + ("o", "%"): 0x03CC, + ("u", "%"): 0x03CD, + ("w", "%"): 0x03CE, + ("'", "G"): 0x03D8, + (",", "G"): 0x03D9, + ("T", "3"): 0x03DA, + ("t", "3"): 0x03DB, + ("M", "3"): 0x03DC, + ("m", "3"): 0x03DD, + ("K", "3"): 0x03DE, + ("k", "3"): 0x03DF, + ("P", "3"): 0x03E0, + ("p", "3"): 0x03E1, + ("'", "%"): 0x03F4, + ("j", "3"): 0x03F5, + ("I", "O"): 0x0401, + ("D", "%"): 0x0402, + ("G", "%"): 0x0403, + ("I", "E"): 0x0404, + ("D", "S"): 0x0405, + ("I", "I"): 0x0406, + ("Y", "I"): 0x0407, + ("J", "%"): 0x0408, + ("L", "J"): 0x0409, + ("N", "J"): 0x040A, + ("T", "s"): 0x040B, + ("K", "J"): 0x040C, + ("V", "%"): 0x040E, + ("D", "Z"): 0x040F, + ("A", "="): 0x0410, + ("B", "="): 0x0411, + ("V", "="): 0x0412, + ("G", "="): 0x0413, + ("D", "="): 0x0414, + ("E", "="): 0x0415, + ("Z", "%"): 0x0416, + ("Z", "="): 0x0417, + ("I", "="): 0x0418, + ("J", "="): 0x0419, + ("K", "="): 0x041A, + ("L", "="): 0x041B, + ("M", "="): 0x041C, + ("N", "="): 0x041D, + ("O", "="): 0x041E, + ("P", "="): 0x041F, + ("R", "="): 0x0420, + ("S", "="): 0x0421, + ("T", "="): 0x0422, + ("U", "="): 0x0423, + ("F", "="): 0x0424, + ("H", "="): 0x0425, + ("C", "="): 0x0426, + ("C", "%"): 0x0427, + ("S", "%"): 0x0428, + ("S", "c"): 0x0429, + ("=", '"'): 0x042A, + ("Y", "="): 0x042B, + ("%", '"'): 0x042C, + ("J", "E"): 0x042D, + ("J", "U"): 0x042E, + ("J", "A"): 0x042F, + ("a", "="): 0x0430, + ("b", "="): 0x0431, + ("v", "="): 0x0432, + ("g", "="): 0x0433, + ("d", "="): 0x0434, + ("e", "="): 0x0435, + ("z", "%"): 0x0436, + ("z", "="): 0x0437, + ("i", "="): 0x0438, + ("j", "="): 0x0439, + ("k", "="): 0x043A, + ("l", "="): 0x043B, + ("m", "="): 0x043C, + ("n", "="): 0x043D, + ("o", "="): 0x043E, + ("p", "="): 0x043F, + ("r", "="): 0x0440, + ("s", "="): 0x0441, + ("t", "="): 0x0442, + ("u", "="): 0x0443, + ("f", "="): 0x0444, + ("h", "="): 0x0445, + ("c", "="): 0x0446, + ("c", "%"): 0x0447, + ("s", "%"): 0x0448, + ("s", "c"): 0x0449, + ("=", "'"): 0x044A, + ("y", "="): 0x044B, + ("%", "'"): 0x044C, + ("j", "e"): 0x044D, + ("j", "u"): 0x044E, + ("j", "a"): 0x044F, + ("i", "o"): 0x0451, + ("d", "%"): 0x0452, + ("g", "%"): 0x0453, + ("i", "e"): 0x0454, + ("d", "s"): 0x0455, + ("i", "i"): 0x0456, + ("y", "i"): 0x0457, + ("j", "%"): 0x0458, + ("l", "j"): 0x0459, + ("n", "j"): 0x045A, + ("t", "s"): 0x045B, + ("k", "j"): 0x045C, + ("v", "%"): 0x045E, + ("d", "z"): 0x045F, + ("Y", "3"): 0x0462, + ("y", "3"): 0x0463, + ("O", "3"): 0x046A, + ("o", "3"): 0x046B, + ("F", "3"): 0x0472, + ("f", "3"): 0x0473, + ("V", "3"): 0x0474, + ("v", "3"): 0x0475, + ("C", "3"): 0x0480, + ("c", "3"): 0x0481, + ("G", "3"): 0x0490, + ("g", "3"): 0x0491, + ("A", "+"): 0x05D0, + ("B", "+"): 0x05D1, + ("G", "+"): 0x05D2, + ("D", "+"): 0x05D3, + ("H", "+"): 0x05D4, + ("W", "+"): 0x05D5, + ("Z", "+"): 0x05D6, + ("X", "+"): 0x05D7, + ("T", "j"): 0x05D8, + ("J", "+"): 0x05D9, + ("K", "%"): 0x05DA, + ("K", "+"): 0x05DB, + ("L", "+"): 0x05DC, + ("M", "%"): 0x05DD, + ("M", "+"): 0x05DE, + ("N", "%"): 0x05DF, + ("N", "+"): 0x05E0, + ("S", "+"): 0x05E1, + ("E", "+"): 0x05E2, + ("P", "%"): 0x05E3, + ("P", "+"): 0x05E4, + ("Z", "j"): 0x05E5, + ("Z", "J"): 0x05E6, + ("Q", "+"): 0x05E7, + ("R", "+"): 0x05E8, + ("S", "h"): 0x05E9, + ("T", "+"): 0x05EA, + (",", "+"): 0x060C, + (";", "+"): 0x061B, + ("?", "+"): 0x061F, + ("H", "'"): 0x0621, + ("a", "M"): 0x0622, + ("a", "H"): 0x0623, + ("w", "H"): 0x0624, + ("a", "h"): 0x0625, + ("y", "H"): 0x0626, + ("a", "+"): 0x0627, + ("b", "+"): 0x0628, + ("t", "m"): 0x0629, + ("t", "+"): 0x062A, + ("t", "k"): 0x062B, + ("g", "+"): 0x062C, + ("h", "k"): 0x062D, + ("x", "+"): 0x062E, + ("d", "+"): 0x062F, + ("d", "k"): 0x0630, + ("r", "+"): 0x0631, + ("z", "+"): 0x0632, + ("s", "+"): 0x0633, + ("s", "n"): 0x0634, + ("c", "+"): 0x0635, + ("d", "d"): 0x0636, + ("t", "j"): 0x0637, + ("z", "H"): 0x0638, + ("e", "+"): 0x0639, + ("i", "+"): 0x063A, + ("+", "+"): 0x0640, + ("f", "+"): 0x0641, + ("q", "+"): 0x0642, + ("k", "+"): 0x0643, + ("l", "+"): 0x0644, + ("m", "+"): 0x0645, + ("n", "+"): 0x0646, + ("h", "+"): 0x0647, + ("w", "+"): 0x0648, + ("j", "+"): 0x0649, + ("y", "+"): 0x064A, + (":", "+"): 0x064B, + ('"', "+"): 0x064C, + ("=", "+"): 0x064D, + ("/", "+"): 0x064E, + ("'", "+"): 0x064F, + ("1", "+"): 0x0650, + ("3", "+"): 0x0651, + ("0", "+"): 0x0652, + ("a", "S"): 0x0670, + ("p", "+"): 0x067E, + ("v", "+"): 0x06A4, + ("g", "f"): 0x06AF, + ("0", "a"): 0x06F0, + ("1", "a"): 0x06F1, + ("2", "a"): 0x06F2, + ("3", "a"): 0x06F3, + ("4", "a"): 0x06F4, + ("5", "a"): 0x06F5, + ("6", "a"): 0x06F6, + ("7", "a"): 0x06F7, + ("8", "a"): 0x06F8, + ("9", "a"): 0x06F9, + ("B", "."): 0x1E02, + ("b", "."): 0x1E03, + ("B", "_"): 0x1E06, + ("b", "_"): 0x1E07, + ("D", "."): 0x1E0A, + ("d", "."): 0x1E0B, + ("D", "_"): 0x1E0E, + ("d", "_"): 0x1E0F, + ("D", ","): 0x1E10, + ("d", ","): 0x1E11, + ("F", "."): 0x1E1E, + ("f", "."): 0x1E1F, + ("G", "-"): 0x1E20, + ("g", "-"): 0x1E21, + ("H", "."): 0x1E22, + ("h", "."): 0x1E23, + ("H", ":"): 0x1E26, + ("h", ":"): 0x1E27, + ("H", ","): 0x1E28, + ("h", ","): 0x1E29, + ("K", "'"): 0x1E30, + ("k", "'"): 0x1E31, + ("K", "_"): 0x1E34, + ("k", "_"): 0x1E35, + ("L", "_"): 0x1E3A, + ("l", "_"): 0x1E3B, + ("M", "'"): 0x1E3E, + ("m", "'"): 0x1E3F, + ("M", "."): 0x1E40, + ("m", "."): 0x1E41, + ("N", "."): 0x1E44, + ("n", "."): 0x1E45, + ("N", "_"): 0x1E48, + ("n", "_"): 0x1E49, + ("P", "'"): 0x1E54, + ("p", "'"): 0x1E55, + ("P", "."): 0x1E56, + ("p", "."): 0x1E57, + ("R", "."): 0x1E58, + ("r", "."): 0x1E59, + ("R", "_"): 0x1E5E, + ("r", "_"): 0x1E5F, + ("S", "."): 0x1E60, + ("s", "."): 0x1E61, + ("T", "."): 0x1E6A, + ("t", "."): 0x1E6B, + ("T", "_"): 0x1E6E, + ("t", "_"): 0x1E6F, + ("V", "?"): 0x1E7C, + ("v", "?"): 0x1E7D, + ("W", "!"): 0x1E80, + ("w", "!"): 0x1E81, + ("W", "'"): 0x1E82, + ("w", "'"): 0x1E83, + ("W", ":"): 0x1E84, + ("w", ":"): 0x1E85, + ("W", "."): 0x1E86, + ("w", "."): 0x1E87, + ("X", "."): 0x1E8A, + ("x", "."): 0x1E8B, + ("X", ":"): 0x1E8C, + ("x", ":"): 0x1E8D, + ("Y", "."): 0x1E8E, + ("y", "."): 0x1E8F, + ("Z", ">"): 0x1E90, + ("z", ">"): 0x1E91, + ("Z", "_"): 0x1E94, + ("z", "_"): 0x1E95, + ("h", "_"): 0x1E96, + ("t", ":"): 0x1E97, + ("w", "0"): 0x1E98, + ("y", "0"): 0x1E99, + ("A", "2"): 0x1EA2, + ("a", "2"): 0x1EA3, + ("E", "2"): 0x1EBA, + ("e", "2"): 0x1EBB, + ("E", "?"): 0x1EBC, + ("e", "?"): 0x1EBD, + ("I", "2"): 0x1EC8, + ("i", "2"): 0x1EC9, + ("O", "2"): 0x1ECE, + ("o", "2"): 0x1ECF, + ("U", "2"): 0x1EE6, + ("u", "2"): 0x1EE7, + ("Y", "!"): 0x1EF2, + ("y", "!"): 0x1EF3, + ("Y", "2"): 0x1EF6, + ("y", "2"): 0x1EF7, + ("Y", "?"): 0x1EF8, + ("y", "?"): 0x1EF9, + (";", "'"): 0x1F00, + (",", "'"): 0x1F01, + (";", "!"): 0x1F02, + (",", "!"): 0x1F03, + ("?", ";"): 0x1F04, + ("?", ","): 0x1F05, + ("!", ":"): 0x1F06, + ("?", ":"): 0x1F07, + ("1", "N"): 0x2002, + ("1", "M"): 0x2003, + ("3", "M"): 0x2004, + ("4", "M"): 0x2005, + ("6", "M"): 0x2006, + ("1", "T"): 0x2009, + ("1", "H"): 0x200A, + ("-", "1"): 0x2010, + ("-", "N"): 0x2013, + ("-", "M"): 0x2014, + ("-", "3"): 0x2015, + ("!", "2"): 0x2016, + ("=", "2"): 0x2017, + ("'", "6"): 0x2018, + ("'", "9"): 0x2019, + (".", "9"): 0x201A, + ("9", "'"): 0x201B, + ('"', "6"): 0x201C, + ('"', "9"): 0x201D, + (":", "9"): 0x201E, + ("9", '"'): 0x201F, + ("/", "-"): 0x2020, + ("/", "="): 0x2021, + (".", "."): 0x2025, + ("%", "0"): 0x2030, + ("1", "'"): 0x2032, + ("2", "'"): 0x2033, + ("3", "'"): 0x2034, + ("1", '"'): 0x2035, + ("2", '"'): 0x2036, + ("3", '"'): 0x2037, + ("C", "a"): 0x2038, + ("<", "1"): 0x2039, + (">", "1"): 0x203A, + (":", "X"): 0x203B, + ("'", "-"): 0x203E, + ("/", "f"): 0x2044, + ("0", "S"): 0x2070, + ("4", "S"): 0x2074, + ("5", "S"): 0x2075, + ("6", "S"): 0x2076, + ("7", "S"): 0x2077, + ("8", "S"): 0x2078, + ("9", "S"): 0x2079, + ("+", "S"): 0x207A, + ("-", "S"): 0x207B, + ("=", "S"): 0x207C, + ("(", "S"): 0x207D, + (")", "S"): 0x207E, + ("n", "S"): 0x207F, + ("0", "s"): 0x2080, + ("1", "s"): 0x2081, + ("2", "s"): 0x2082, + ("3", "s"): 0x2083, + ("4", "s"): 0x2084, + ("5", "s"): 0x2085, + ("6", "s"): 0x2086, + ("7", "s"): 0x2087, + ("8", "s"): 0x2088, + ("9", "s"): 0x2089, + ("+", "s"): 0x208A, + ("-", "s"): 0x208B, + ("=", "s"): 0x208C, + ("(", "s"): 0x208D, + (")", "s"): 0x208E, + ("L", "i"): 0x20A4, + ("P", "t"): 0x20A7, + ("W", "="): 0x20A9, + ("=", "e"): 0x20AC, # euro + ("E", "u"): 0x20AC, # euro + ("=", "R"): 0x20BD, # rouble + ("=", "P"): 0x20BD, # rouble + ("o", "C"): 0x2103, + ("c", "o"): 0x2105, + ("o", "F"): 0x2109, + ("N", "0"): 0x2116, + ("P", "O"): 0x2117, + ("R", "x"): 0x211E, + ("S", "M"): 0x2120, + ("T", "M"): 0x2122, + ("O", "m"): 0x2126, + ("A", "O"): 0x212B, + ("1", "3"): 0x2153, + ("2", "3"): 0x2154, + ("1", "5"): 0x2155, + ("2", "5"): 0x2156, + ("3", "5"): 0x2157, + ("4", "5"): 0x2158, + ("1", "6"): 0x2159, + ("5", "6"): 0x215A, + ("1", "8"): 0x215B, + ("3", "8"): 0x215C, + ("5", "8"): 0x215D, + ("7", "8"): 0x215E, + ("1", "R"): 0x2160, + ("2", "R"): 0x2161, + ("3", "R"): 0x2162, + ("4", "R"): 0x2163, + ("5", "R"): 0x2164, + ("6", "R"): 0x2165, + ("7", "R"): 0x2166, + ("8", "R"): 0x2167, + ("9", "R"): 0x2168, + ("a", "R"): 0x2169, + ("b", "R"): 0x216A, + ("c", "R"): 0x216B, + ("1", "r"): 0x2170, + ("2", "r"): 0x2171, + ("3", "r"): 0x2172, + ("4", "r"): 0x2173, + ("5", "r"): 0x2174, + ("6", "r"): 0x2175, + ("7", "r"): 0x2176, + ("8", "r"): 0x2177, + ("9", "r"): 0x2178, + ("a", "r"): 0x2179, + ("b", "r"): 0x217A, + ("c", "r"): 0x217B, + ("<", "-"): 0x2190, + ("-", "!"): 0x2191, + ("-", ">"): 0x2192, + ("-", "v"): 0x2193, + ("<", ">"): 0x2194, + ("U", "D"): 0x2195, + ("<", "="): 0x21D0, + ("=", ">"): 0x21D2, + ("=", "="): 0x21D4, + ("F", "A"): 0x2200, + ("d", "P"): 0x2202, + ("T", "E"): 0x2203, + ("/", "0"): 0x2205, + ("D", "E"): 0x2206, + ("N", "B"): 0x2207, + ("(", "-"): 0x2208, + ("-", ")"): 0x220B, + ("*", "P"): 0x220F, + ("+", "Z"): 0x2211, + ("-", "2"): 0x2212, + ("-", "+"): 0x2213, + ("*", "-"): 0x2217, + ("O", "b"): 0x2218, + ("S", "b"): 0x2219, + ("R", "T"): 0x221A, + ("0", "("): 0x221D, + ("0", "0"): 0x221E, + ("-", "L"): 0x221F, + ("-", "V"): 0x2220, + ("P", "P"): 0x2225, + ("A", "N"): 0x2227, + ("O", "R"): 0x2228, + ("(", "U"): 0x2229, + (")", "U"): 0x222A, + ("I", "n"): 0x222B, + ("D", "I"): 0x222C, + ("I", "o"): 0x222E, + (".", ":"): 0x2234, + (":", "."): 0x2235, + (":", "R"): 0x2236, + (":", ":"): 0x2237, + ("?", "1"): 0x223C, + ("C", "G"): 0x223E, + ("?", "-"): 0x2243, + ("?", "="): 0x2245, + ("?", "2"): 0x2248, + ("=", "?"): 0x224C, + ("H", "I"): 0x2253, + ("!", "="): 0x2260, + ("=", "3"): 0x2261, + ("=", "<"): 0x2264, + (">", "="): 0x2265, + ("<", "*"): 0x226A, + ("*", ">"): 0x226B, + ("!", "<"): 0x226E, + ("!", ">"): 0x226F, + ("(", "C"): 0x2282, + (")", "C"): 0x2283, + ("(", "_"): 0x2286, + (")", "_"): 0x2287, + ("0", "."): 0x2299, + ("0", "2"): 0x229A, + ("-", "T"): 0x22A5, + (".", "P"): 0x22C5, + (":", "3"): 0x22EE, + (".", "3"): 0x22EF, + ("E", "h"): 0x2302, + ("<", "7"): 0x2308, + (">", "7"): 0x2309, + ("7", "<"): 0x230A, + ("7", ">"): 0x230B, + ("N", "I"): 0x2310, + ("(", "A"): 0x2312, + ("T", "R"): 0x2315, + ("I", "u"): 0x2320, + ("I", "l"): 0x2321, + ("<", "/"): 0x2329, + ("/", ">"): 0x232A, + ("V", "s"): 0x2423, + ("1", "h"): 0x2440, + ("3", "h"): 0x2441, + ("2", "h"): 0x2442, + ("4", "h"): 0x2443, + ("1", "j"): 0x2446, + ("2", "j"): 0x2447, + ("3", "j"): 0x2448, + ("4", "j"): 0x2449, + ("1", "."): 0x2488, + ("2", "."): 0x2489, + ("3", "."): 0x248A, + ("4", "."): 0x248B, + ("5", "."): 0x248C, + ("6", "."): 0x248D, + ("7", "."): 0x248E, + ("8", "."): 0x248F, + ("9", "."): 0x2490, + ("h", "h"): 0x2500, + ("H", "H"): 0x2501, + ("v", "v"): 0x2502, + ("V", "V"): 0x2503, + ("3", "-"): 0x2504, + ("3", "_"): 0x2505, + ("3", "!"): 0x2506, + ("3", "/"): 0x2507, + ("4", "-"): 0x2508, + ("4", "_"): 0x2509, + ("4", "!"): 0x250A, + ("4", "/"): 0x250B, + ("d", "r"): 0x250C, + ("d", "R"): 0x250D, + ("D", "r"): 0x250E, + ("D", "R"): 0x250F, + ("d", "l"): 0x2510, + ("d", "L"): 0x2511, + ("D", "l"): 0x2512, + ("L", "D"): 0x2513, + ("u", "r"): 0x2514, + ("u", "R"): 0x2515, + ("U", "r"): 0x2516, + ("U", "R"): 0x2517, + ("u", "l"): 0x2518, + ("u", "L"): 0x2519, + ("U", "l"): 0x251A, + ("U", "L"): 0x251B, + ("v", "r"): 0x251C, + ("v", "R"): 0x251D, + ("V", "r"): 0x2520, + ("V", "R"): 0x2523, + ("v", "l"): 0x2524, + ("v", "L"): 0x2525, + ("V", "l"): 0x2528, + ("V", "L"): 0x252B, + ("d", "h"): 0x252C, + ("d", "H"): 0x252F, + ("D", "h"): 0x2530, + ("D", "H"): 0x2533, + ("u", "h"): 0x2534, + ("u", "H"): 0x2537, + ("U", "h"): 0x2538, + ("U", "H"): 0x253B, + ("v", "h"): 0x253C, + ("v", "H"): 0x253F, + ("V", "h"): 0x2542, + ("V", "H"): 0x254B, + ("F", "D"): 0x2571, + ("B", "D"): 0x2572, + ("T", "B"): 0x2580, + ("L", "B"): 0x2584, + ("F", "B"): 0x2588, + ("l", "B"): 0x258C, + ("R", "B"): 0x2590, + (".", "S"): 0x2591, + (":", "S"): 0x2592, + ("?", "S"): 0x2593, + ("f", "S"): 0x25A0, + ("O", "S"): 0x25A1, + ("R", "O"): 0x25A2, + ("R", "r"): 0x25A3, + ("R", "F"): 0x25A4, + ("R", "Y"): 0x25A5, + ("R", "H"): 0x25A6, + ("R", "Z"): 0x25A7, + ("R", "K"): 0x25A8, + ("R", "X"): 0x25A9, + ("s", "B"): 0x25AA, + ("S", "R"): 0x25AC, + ("O", "r"): 0x25AD, + ("U", "T"): 0x25B2, + ("u", "T"): 0x25B3, + ("P", "R"): 0x25B6, + ("T", "r"): 0x25B7, + ("D", "t"): 0x25BC, + ("d", "T"): 0x25BD, + ("P", "L"): 0x25C0, + ("T", "l"): 0x25C1, + ("D", "b"): 0x25C6, + ("D", "w"): 0x25C7, + ("L", "Z"): 0x25CA, + ("0", "m"): 0x25CB, + ("0", "o"): 0x25CE, + ("0", "M"): 0x25CF, + ("0", "L"): 0x25D0, + ("0", "R"): 0x25D1, + ("S", "n"): 0x25D8, + ("I", "c"): 0x25D9, + ("F", "d"): 0x25E2, + ("B", "d"): 0x25E3, + ("*", "2"): 0x2605, + ("*", "1"): 0x2606, + ("<", "H"): 0x261C, + (">", "H"): 0x261E, + ("0", "u"): 0x263A, + ("0", "U"): 0x263B, + ("S", "U"): 0x263C, + ("F", "m"): 0x2640, + ("M", "l"): 0x2642, + ("c", "S"): 0x2660, + ("c", "H"): 0x2661, + ("c", "D"): 0x2662, + ("c", "C"): 0x2663, + ("M", "d"): 0x2669, + ("M", "8"): 0x266A, + ("M", "2"): 0x266B, + ("M", "b"): 0x266D, + ("M", "x"): 0x266E, + ("M", "X"): 0x266F, + ("O", "K"): 0x2713, + ("X", "X"): 0x2717, + ("-", "X"): 0x2720, + ("I", "S"): 0x3000, + (",", "_"): 0x3001, + (".", "_"): 0x3002, + ("+", '"'): 0x3003, + ("+", "_"): 0x3004, + ("*", "_"): 0x3005, + (";", "_"): 0x3006, + ("0", "_"): 0x3007, + ("<", "+"): 0x300A, + (">", "+"): 0x300B, + ("<", "'"): 0x300C, + (">", "'"): 0x300D, + ("<", '"'): 0x300E, + (">", '"'): 0x300F, + ("(", '"'): 0x3010, + (")", '"'): 0x3011, + ("=", "T"): 0x3012, + ("=", "_"): 0x3013, + ("(", "'"): 0x3014, + (")", "'"): 0x3015, + ("(", "I"): 0x3016, + (")", "I"): 0x3017, + ("-", "?"): 0x301C, + ("A", "5"): 0x3041, + ("a", "5"): 0x3042, + ("I", "5"): 0x3043, + ("i", "5"): 0x3044, + ("U", "5"): 0x3045, + ("u", "5"): 0x3046, + ("E", "5"): 0x3047, + ("e", "5"): 0x3048, + ("O", "5"): 0x3049, + ("o", "5"): 0x304A, + ("k", "a"): 0x304B, + ("g", "a"): 0x304C, + ("k", "i"): 0x304D, + ("g", "i"): 0x304E, + ("k", "u"): 0x304F, + ("g", "u"): 0x3050, + ("k", "e"): 0x3051, + ("g", "e"): 0x3052, + ("k", "o"): 0x3053, + ("g", "o"): 0x3054, + ("s", "a"): 0x3055, + ("z", "a"): 0x3056, + ("s", "i"): 0x3057, + ("z", "i"): 0x3058, + ("s", "u"): 0x3059, + ("z", "u"): 0x305A, + ("s", "e"): 0x305B, + ("z", "e"): 0x305C, + ("s", "o"): 0x305D, + ("z", "o"): 0x305E, + ("t", "a"): 0x305F, + ("d", "a"): 0x3060, + ("t", "i"): 0x3061, + ("d", "i"): 0x3062, + ("t", "U"): 0x3063, + ("t", "u"): 0x3064, + ("d", "u"): 0x3065, + ("t", "e"): 0x3066, + ("d", "e"): 0x3067, + ("t", "o"): 0x3068, + ("d", "o"): 0x3069, + ("n", "a"): 0x306A, + ("n", "i"): 0x306B, + ("n", "u"): 0x306C, + ("n", "e"): 0x306D, + ("n", "o"): 0x306E, + ("h", "a"): 0x306F, + ("b", "a"): 0x3070, + ("p", "a"): 0x3071, + ("h", "i"): 0x3072, + ("b", "i"): 0x3073, + ("p", "i"): 0x3074, + ("h", "u"): 0x3075, + ("b", "u"): 0x3076, + ("p", "u"): 0x3077, + ("h", "e"): 0x3078, + ("b", "e"): 0x3079, + ("p", "e"): 0x307A, + ("h", "o"): 0x307B, + ("b", "o"): 0x307C, + ("p", "o"): 0x307D, + ("m", "a"): 0x307E, + ("m", "i"): 0x307F, + ("m", "u"): 0x3080, + ("m", "e"): 0x3081, + ("m", "o"): 0x3082, + ("y", "A"): 0x3083, + ("y", "a"): 0x3084, + ("y", "U"): 0x3085, + ("y", "u"): 0x3086, + ("y", "O"): 0x3087, + ("y", "o"): 0x3088, + ("r", "a"): 0x3089, + ("r", "i"): 0x308A, + ("r", "u"): 0x308B, + ("r", "e"): 0x308C, + ("r", "o"): 0x308D, + ("w", "A"): 0x308E, + ("w", "a"): 0x308F, + ("w", "i"): 0x3090, + ("w", "e"): 0x3091, + ("w", "o"): 0x3092, + ("n", "5"): 0x3093, + ("v", "u"): 0x3094, + ('"', "5"): 0x309B, + ("0", "5"): 0x309C, + ("*", "5"): 0x309D, + ("+", "5"): 0x309E, + ("a", "6"): 0x30A1, + ("A", "6"): 0x30A2, + ("i", "6"): 0x30A3, + ("I", "6"): 0x30A4, + ("u", "6"): 0x30A5, + ("U", "6"): 0x30A6, + ("e", "6"): 0x30A7, + ("E", "6"): 0x30A8, + ("o", "6"): 0x30A9, + ("O", "6"): 0x30AA, + ("K", "a"): 0x30AB, + ("G", "a"): 0x30AC, + ("K", "i"): 0x30AD, + ("G", "i"): 0x30AE, + ("K", "u"): 0x30AF, + ("G", "u"): 0x30B0, + ("K", "e"): 0x30B1, + ("G", "e"): 0x30B2, + ("K", "o"): 0x30B3, + ("G", "o"): 0x30B4, + ("S", "a"): 0x30B5, + ("Z", "a"): 0x30B6, + ("S", "i"): 0x30B7, + ("Z", "i"): 0x30B8, + ("S", "u"): 0x30B9, + ("Z", "u"): 0x30BA, + ("S", "e"): 0x30BB, + ("Z", "e"): 0x30BC, + ("S", "o"): 0x30BD, + ("Z", "o"): 0x30BE, + ("T", "a"): 0x30BF, + ("D", "a"): 0x30C0, + ("T", "i"): 0x30C1, + ("D", "i"): 0x30C2, + ("T", "U"): 0x30C3, + ("T", "u"): 0x30C4, + ("D", "u"): 0x30C5, + ("T", "e"): 0x30C6, + ("D", "e"): 0x30C7, + ("T", "o"): 0x30C8, + ("D", "o"): 0x30C9, + ("N", "a"): 0x30CA, + ("N", "i"): 0x30CB, + ("N", "u"): 0x30CC, + ("N", "e"): 0x30CD, + ("N", "o"): 0x30CE, + ("H", "a"): 0x30CF, + ("B", "a"): 0x30D0, + ("P", "a"): 0x30D1, + ("H", "i"): 0x30D2, + ("B", "i"): 0x30D3, + ("P", "i"): 0x30D4, + ("H", "u"): 0x30D5, + ("B", "u"): 0x30D6, + ("P", "u"): 0x30D7, + ("H", "e"): 0x30D8, + ("B", "e"): 0x30D9, + ("P", "e"): 0x30DA, + ("H", "o"): 0x30DB, + ("B", "o"): 0x30DC, + ("P", "o"): 0x30DD, + ("M", "a"): 0x30DE, + ("M", "i"): 0x30DF, + ("M", "u"): 0x30E0, + ("M", "e"): 0x30E1, + ("M", "o"): 0x30E2, + ("Y", "A"): 0x30E3, + ("Y", "a"): 0x30E4, + ("Y", "U"): 0x30E5, + ("Y", "u"): 0x30E6, + ("Y", "O"): 0x30E7, + ("Y", "o"): 0x30E8, + ("R", "a"): 0x30E9, + ("R", "i"): 0x30EA, + ("R", "u"): 0x30EB, + ("R", "e"): 0x30EC, + ("R", "o"): 0x30ED, + ("W", "A"): 0x30EE, + ("W", "a"): 0x30EF, + ("W", "i"): 0x30F0, + ("W", "e"): 0x30F1, + ("W", "o"): 0x30F2, + ("N", "6"): 0x30F3, + ("V", "u"): 0x30F4, + ("K", "A"): 0x30F5, + ("K", "E"): 0x30F6, + ("V", "a"): 0x30F7, + ("V", "i"): 0x30F8, + ("V", "e"): 0x30F9, + ("V", "o"): 0x30FA, + (".", "6"): 0x30FB, + ("-", "6"): 0x30FC, + ("*", "6"): 0x30FD, + ("+", "6"): 0x30FE, + ("b", "4"): 0x3105, + ("p", "4"): 0x3106, + ("m", "4"): 0x3107, + ("f", "4"): 0x3108, + ("d", "4"): 0x3109, + ("t", "4"): 0x310A, + ("n", "4"): 0x310B, + ("l", "4"): 0x310C, + ("g", "4"): 0x310D, + ("k", "4"): 0x310E, + ("h", "4"): 0x310F, + ("j", "4"): 0x3110, + ("q", "4"): 0x3111, + ("x", "4"): 0x3112, + ("z", "h"): 0x3113, + ("c", "h"): 0x3114, + ("s", "h"): 0x3115, + ("r", "4"): 0x3116, + ("z", "4"): 0x3117, + ("c", "4"): 0x3118, + ("s", "4"): 0x3119, + ("a", "4"): 0x311A, + ("o", "4"): 0x311B, + ("e", "4"): 0x311C, + ("a", "i"): 0x311E, + ("e", "i"): 0x311F, + ("a", "u"): 0x3120, + ("o", "u"): 0x3121, + ("a", "n"): 0x3122, + ("e", "n"): 0x3123, + ("a", "N"): 0x3124, + ("e", "N"): 0x3125, + ("e", "r"): 0x3126, + ("i", "4"): 0x3127, + ("u", "4"): 0x3128, + ("i", "u"): 0x3129, + ("v", "4"): 0x312A, + ("n", "G"): 0x312B, + ("g", "n"): 0x312C, + ("1", "c"): 0x3220, + ("2", "c"): 0x3221, + ("3", "c"): 0x3222, + ("4", "c"): 0x3223, + ("5", "c"): 0x3224, + ("6", "c"): 0x3225, + ("7", "c"): 0x3226, + ("8", "c"): 0x3227, + ("9", "c"): 0x3228, + # code points 0xe000 - 0xefff excluded, they have no assigned + # characters, only used in proposals. + ("f", "f"): 0xFB00, + ("f", "i"): 0xFB01, + ("f", "l"): 0xFB02, + ("f", "t"): 0xFB05, + ("s", "t"): 0xFB06, + # Vim 5.x compatible digraphs that don't conflict with the above + ("~", "!"): 161, + ("c", "|"): 162, + ("$", "$"): 163, + ("o", "x"): 164, # currency symbol in ISO 8859-1 + ("Y", "-"): 165, + ("|", "|"): 166, + ("c", "O"): 169, + ("-", ","): 172, + ("-", "="): 175, + ("~", "o"): 176, + ("2", "2"): 178, + ("3", "3"): 179, + ("p", "p"): 182, + ("~", "."): 183, + ("1", "1"): 185, + ("~", "?"): 191, + ("A", "`"): 192, + ("A", "^"): 194, + ("A", "~"): 195, + ("A", '"'): 196, + ("A", "@"): 197, + ("E", "`"): 200, + ("E", "^"): 202, + ("E", '"'): 203, + ("I", "`"): 204, + ("I", "^"): 206, + ("I", '"'): 207, + ("N", "~"): 209, + ("O", "`"): 210, + ("O", "^"): 212, + ("O", "~"): 213, + ("/", "\\"): 215, # multiplication symbol in ISO 8859-1 + ("U", "`"): 217, + ("U", "^"): 219, + ("I", "p"): 222, + ("a", "`"): 224, + ("a", "^"): 226, + ("a", "~"): 227, + ("a", '"'): 228, + ("a", "@"): 229, + ("e", "`"): 232, + ("e", "^"): 234, + ("e", '"'): 235, + ("i", "`"): 236, + ("i", "^"): 238, + ("n", "~"): 241, + ("o", "`"): 242, + ("o", "^"): 244, + ("o", "~"): 245, + ("u", "`"): 249, + ("u", "^"): 251, + ("y", '"'): 255, +} diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/emacs_state.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/emacs_state.py new file mode 100644 index 00000000..00967163 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/emacs_state.py @@ -0,0 +1,36 @@ +from typing import List, Optional + +from .key_processor import KeyPress + +__all__ = [ + "EmacsState", +] + + +class EmacsState: + """ + Mutable class to hold Emacs specific state. + """ + + def __init__(self) -> None: + # Simple macro recording. (Like Readline does.) + # (For Emacs mode.) + self.macro: Optional[List[KeyPress]] = [] + self.current_recording: Optional[List[KeyPress]] = None + + def reset(self) -> None: + self.current_recording = None + + @property + def is_recording(self) -> bool: + " Tell whether we are recording a macro. " + return self.current_recording is not None + + def start_macro(self) -> None: + " Start recording macro. " + self.current_recording = [] + + def end_macro(self) -> None: + " End recording macro. " + self.macro = self.current_recording + self.current_recording = None diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/key_bindings.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/key_bindings.py new file mode 100644 index 00000000..437f5716 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/key_bindings.py @@ -0,0 +1,635 @@ +""" +Key bindings registry. + +A `KeyBindings` object is a container that holds a list of key bindings. It has a +very efficient internal data structure for checking which key bindings apply +for a pressed key. + +Typical usage:: + + kb = KeyBindings() + + @kb.add(Keys.ControlX, Keys.ControlC, filter=INSERT) + def handler(event): + # Handle ControlX-ControlC key sequence. + pass + +It is also possible to combine multiple KeyBindings objects. We do this in the +default key bindings. There are some KeyBindings objects that contain the Emacs +bindings, while others contain the Vi bindings. They are merged together using +`merge_key_bindings`. + +We also have a `ConditionalKeyBindings` object that can enable/disable a group of +key bindings at once. + + +It is also possible to add a filter to a function, before a key binding has +been assigned, through the `key_binding` decorator.:: + + # First define a key handler with the `filter`. + @key_binding(filter=condition) + def my_key_binding(event): + ... + + # Later, add it to the key bindings. + kb.add(Keys.A, my_key_binding) +""" +from abc import ABCMeta, abstractmethod, abstractproperty +from typing import ( + TYPE_CHECKING, + Callable, + Hashable, + List, + Optional, + Sequence, + Tuple, + TypeVar, + Union, + cast, +) + +from prompt_toolkit.cache import SimpleCache +from prompt_toolkit.filters import FilterOrBool, Never, to_filter +from prompt_toolkit.keys import KEY_ALIASES, Keys + +# Avoid circular imports. +if TYPE_CHECKING: + from .key_processor import KeyPressEvent + + +__all__ = [ + "Binding", + "KeyBindingsBase", + "KeyBindings", + "ConditionalKeyBindings", + "merge_key_bindings", + "DynamicKeyBindings", + "GlobalOnlyKeyBindings", +] + +KeyHandlerCallable = Callable[["KeyPressEvent"], None] + + +class Binding: + """ + Key binding: (key sequence + handler + filter). + (Immutable binding class.) + + :param record_in_macro: When True, don't record this key binding when a + macro is recorded. + """ + + def __init__( + self, + keys: Tuple[Union[Keys, str], ...], + handler: KeyHandlerCallable, + filter: FilterOrBool = True, + eager: FilterOrBool = False, + is_global: FilterOrBool = False, + save_before: Callable[["KeyPressEvent"], bool] = (lambda e: True), + record_in_macro: FilterOrBool = True, + ) -> None: + self.keys = keys + self.handler = handler + self.filter = to_filter(filter) + self.eager = to_filter(eager) + self.is_global = to_filter(is_global) + self.save_before = save_before + self.record_in_macro = to_filter(record_in_macro) + + def call(self, event: "KeyPressEvent") -> None: + self.handler(event) + + def __repr__(self) -> str: + return "%s(keys=%r, handler=%r)" % ( + self.__class__.__name__, + self.keys, + self.handler, + ) + + +# Sequence of keys presses. +KeysTuple = Tuple[Union[Keys, str], ...] + + +class KeyBindingsBase(metaclass=ABCMeta): + """ + Interface for a KeyBindings. + """ + + @abstractproperty + def _version(self) -> Hashable: + """ + For cache invalidation. - This should increase every time that + something changes. + """ + return 0 + + @abstractmethod + def get_bindings_for_keys(self, keys: KeysTuple) -> List[Binding]: + """ + Return a list of key bindings that can handle these keys. + (This return also inactive bindings, so the `filter` still has to be + called, for checking it.) + + :param keys: tuple of keys. + """ + return [] + + @abstractmethod + def get_bindings_starting_with_keys(self, keys: KeysTuple) -> List[Binding]: + """ + Return a list of key bindings that handle a key sequence starting with + `keys`. (It does only return bindings for which the sequences are + longer than `keys`. And like `get_bindings_for_keys`, it also includes + inactive bindings.) + + :param keys: tuple of keys. + """ + return [] + + @abstractproperty + def bindings(self) -> List[Binding]: + """ + List of `Binding` objects. + (These need to be exposed, so that `KeyBindings` objects can be merged + together.) + """ + return [] + + # `add` and `remove` don't have to be part of this interface. + + +T = TypeVar("T", bound=Union[KeyHandlerCallable, Binding]) + + +class KeyBindings(KeyBindingsBase): + """ + A container for a set of key bindings. + + Example usage:: + + kb = KeyBindings() + + @kb.add('c-t') + def _(event): + print('Control-T pressed') + + @kb.add('c-a', 'c-b') + def _(event): + print('Control-A pressed, followed by Control-B') + + @kb.add('c-x', filter=is_searching) + def _(event): + print('Control-X pressed') # Works only if we are searching. + + """ + + def __init__(self) -> None: + self._bindings: List[Binding] = [] + self._get_bindings_for_keys_cache: SimpleCache[ + KeysTuple, List[Binding] + ] = SimpleCache(maxsize=10000) + self._get_bindings_starting_with_keys_cache: SimpleCache[ + KeysTuple, List[Binding] + ] = SimpleCache(maxsize=1000) + self.__version = 0 # For cache invalidation. + + def _clear_cache(self) -> None: + self.__version += 1 + self._get_bindings_for_keys_cache.clear() + self._get_bindings_starting_with_keys_cache.clear() + + @property + def bindings(self) -> List[Binding]: + return self._bindings + + @property + def _version(self) -> Hashable: + return self.__version + + def add( + self, + *keys: Union[Keys, str], + filter: FilterOrBool = True, + eager: FilterOrBool = False, + is_global: FilterOrBool = False, + save_before: Callable[["KeyPressEvent"], bool] = (lambda e: True), + record_in_macro: FilterOrBool = True, + ) -> Callable[[T], T]: + """ + Decorator for adding a key bindings. + + :param filter: :class:`~prompt_toolkit.filters.Filter` to determine + when this key binding is active. + :param eager: :class:`~prompt_toolkit.filters.Filter` or `bool`. + When True, ignore potential longer matches when this key binding is + hit. E.g. when there is an active eager key binding for Ctrl-X, + execute the handler immediately and ignore the key binding for + Ctrl-X Ctrl-E of which it is a prefix. + :param is_global: When this key bindings is added to a `Container` or + `Control`, make it a global (always active) binding. + :param save_before: Callable that takes an `Event` and returns True if + we should save the current buffer, before handling the event. + (That's the default.) + :param record_in_macro: Record these key bindings when a macro is + being recorded. (True by default.) + """ + assert keys + + keys = tuple(_parse_key(k) for k in keys) + + if isinstance(filter, Never): + # When a filter is Never, it will always stay disabled, so in that + # case don't bother putting it in the key bindings. It will slow + # down every key press otherwise. + def decorator(func: T) -> T: + return func + + else: + + def decorator(func: T) -> T: + if isinstance(func, Binding): + # We're adding an existing Binding object. + self.bindings.append( + Binding( + keys, + func.handler, + filter=func.filter & to_filter(filter), + eager=to_filter(eager) | func.eager, + is_global=to_filter(is_global) | func.is_global, + save_before=func.save_before, + record_in_macro=func.record_in_macro, + ) + ) + else: + self.bindings.append( + Binding( + keys, + cast(KeyHandlerCallable, func), + filter=filter, + eager=eager, + is_global=is_global, + save_before=save_before, + record_in_macro=record_in_macro, + ) + ) + self._clear_cache() + + return func + + return decorator + + def remove(self, *args: Union[Keys, str, KeyHandlerCallable]) -> None: + """ + Remove a key binding. + + This expects either a function that was given to `add` method as + parameter or a sequence of key bindings. + + Raises `ValueError` when no bindings was found. + + Usage:: + + remove(handler) # Pass handler. + remove('c-x', 'c-a') # Or pass the key bindings. + """ + found = False + + if callable(args[0]): + assert len(args) == 1 + function = args[0] + + # Remove the given function. + for b in self.bindings: + if b.handler == function: + self.bindings.remove(b) + found = True + + else: + assert len(args) > 0 + args = cast(Tuple[Union[Keys, str]], args) + + # Remove this sequence of key bindings. + keys = tuple(_parse_key(k) for k in args) + + for b in self.bindings: + if b.keys == keys: + self.bindings.remove(b) + found = True + + if found: + self._clear_cache() + else: + # No key binding found for this function. Raise ValueError. + raise ValueError("Binding not found: %r" % (function,)) + + # For backwards-compatibility. + add_binding = add + remove_binding = remove + + def get_bindings_for_keys(self, keys: KeysTuple) -> List[Binding]: + """ + Return a list of key bindings that can handle this key. + (This return also inactive bindings, so the `filter` still has to be + called, for checking it.) + + :param keys: tuple of keys. + """ + + def get() -> List[Binding]: + result: List[Tuple[int, Binding]] = [] + + for b in self.bindings: + if len(keys) == len(b.keys): + match = True + any_count = 0 + + for i, j in zip(b.keys, keys): + if i != j and i != Keys.Any: + match = False + break + + if i == Keys.Any: + any_count += 1 + + if match: + result.append((any_count, b)) + + # Place bindings that have more 'Any' occurrences in them at the end. + result = sorted(result, key=lambda item: -item[0]) + + return [item[1] for item in result] + + return self._get_bindings_for_keys_cache.get(keys, get) + + def get_bindings_starting_with_keys(self, keys: KeysTuple) -> List[Binding]: + """ + Return a list of key bindings that handle a key sequence starting with + `keys`. (It does only return bindings for which the sequences are + longer than `keys`. And like `get_bindings_for_keys`, it also includes + inactive bindings.) + + :param keys: tuple of keys. + """ + + def get() -> List[Binding]: + result = [] + for b in self.bindings: + if len(keys) < len(b.keys): + match = True + for i, j in zip(b.keys, keys): + if i != j and i != Keys.Any: + match = False + break + if match: + result.append(b) + return result + + return self._get_bindings_starting_with_keys_cache.get(keys, get) + + +def _parse_key(key: Union[Keys, str]) -> Union[str, Keys]: + """ + Replace key by alias and verify whether it's a valid one. + """ + # Already a parse key? -> Return it. + if isinstance(key, Keys): + return key + + # Lookup aliases. + key = KEY_ALIASES.get(key, key) + + # Replace 'space' by ' ' + if key == "space": + key = " " + + # Return as `Key` object when it's a special key. + try: + return Keys(key) + except ValueError: + pass + + # Final validation. + if len(key) != 1: + raise ValueError("Invalid key: %s" % (key,)) + + return key + + +def key_binding( + filter: FilterOrBool = True, + eager: FilterOrBool = False, + is_global: FilterOrBool = False, + save_before: Callable[["KeyPressEvent"], bool] = (lambda event: True), + record_in_macro: FilterOrBool = True, +) -> Callable[[KeyHandlerCallable], Binding]: + """ + Decorator that turn a function into a `Binding` object. This can be added + to a `KeyBindings` object when a key binding is assigned. + """ + assert save_before is None or callable(save_before) + + filter = to_filter(filter) + eager = to_filter(eager) + is_global = to_filter(is_global) + save_before = save_before + record_in_macro = to_filter(record_in_macro) + keys = () + + def decorator(function: KeyHandlerCallable) -> Binding: + return Binding( + keys, + function, + filter=filter, + eager=eager, + is_global=is_global, + save_before=save_before, + record_in_macro=record_in_macro, + ) + + return decorator + + +class _Proxy(KeyBindingsBase): + """ + Common part for ConditionalKeyBindings and _MergedKeyBindings. + """ + + def __init__(self) -> None: + # `KeyBindings` to be synchronized with all the others. + self._bindings2: KeyBindingsBase = KeyBindings() + self._last_version: Hashable = () + + def _update_cache(self) -> None: + """ + If `self._last_version` is outdated, then this should update + the version and `self._bindings2`. + """ + raise NotImplementedError + + # Proxy methods to self._bindings2. + + @property + def bindings(self) -> List[Binding]: + self._update_cache() + return self._bindings2.bindings + + @property + def _version(self) -> Hashable: + self._update_cache() + return self._last_version + + def get_bindings_for_keys(self, *a, **kw): + self._update_cache() + return self._bindings2.get_bindings_for_keys(*a, **kw) + + def get_bindings_starting_with_keys(self, *a, **kw): + self._update_cache() + return self._bindings2.get_bindings_starting_with_keys(*a, **kw) + + +class ConditionalKeyBindings(_Proxy): + """ + Wraps around a `KeyBindings`. Disable/enable all the key bindings according to + the given (additional) filter.:: + + @Condition + def setting_is_true(): + return True # or False + + registry = ConditionalKeyBindings(key_bindings, setting_is_true) + + When new key bindings are added to this object. They are also + enable/disabled according to the given `filter`. + + :param registries: List of :class:`.KeyBindings` objects. + :param filter: :class:`~prompt_toolkit.filters.Filter` object. + """ + + def __init__( + self, key_bindings: KeyBindingsBase, filter: FilterOrBool = True + ) -> None: + + _Proxy.__init__(self) + + self.key_bindings = key_bindings + self.filter = to_filter(filter) + + def _update_cache(self) -> None: + " If the original key bindings was changed. Update our copy version. " + expected_version = self.key_bindings._version + + if self._last_version != expected_version: + bindings2 = KeyBindings() + + # Copy all bindings from `self.key_bindings`, adding our condition. + for b in self.key_bindings.bindings: + bindings2.bindings.append( + Binding( + keys=b.keys, + handler=b.handler, + filter=self.filter & b.filter, + eager=b.eager, + is_global=b.is_global, + save_before=b.save_before, + record_in_macro=b.record_in_macro, + ) + ) + + self._bindings2 = bindings2 + self._last_version = expected_version + + +class _MergedKeyBindings(_Proxy): + """ + Merge multiple registries of key bindings into one. + + This class acts as a proxy to multiple :class:`.KeyBindings` objects, but + behaves as if this is just one bigger :class:`.KeyBindings`. + + :param registries: List of :class:`.KeyBindings` objects. + """ + + def __init__(self, registries: Sequence[KeyBindingsBase]) -> None: + _Proxy.__init__(self) + self.registries = registries + + def _update_cache(self) -> None: + """ + If one of the original registries was changed. Update our merged + version. + """ + expected_version = tuple(r._version for r in self.registries) + + if self._last_version != expected_version: + bindings2 = KeyBindings() + + for reg in self.registries: + bindings2.bindings.extend(reg.bindings) + + self._bindings2 = bindings2 + self._last_version = expected_version + + +def merge_key_bindings(bindings: Sequence[KeyBindingsBase]) -> _MergedKeyBindings: + """ + Merge multiple :class:`.Keybinding` objects together. + + Usage:: + + bindings = merge_key_bindings([bindings1, bindings2, ...]) + """ + return _MergedKeyBindings(bindings) + + +class DynamicKeyBindings(_Proxy): + """ + KeyBindings class that can dynamically returns any KeyBindings. + + :param get_key_bindings: Callable that returns a :class:`.KeyBindings` instance. + """ + + def __init__( + self, get_key_bindings: Callable[[], Optional[KeyBindingsBase]] + ) -> None: + self.get_key_bindings = get_key_bindings + self.__version = 0 + self._last_child_version = None + self._dummy = KeyBindings() # Empty key bindings. + + def _update_cache(self) -> None: + key_bindings = self.get_key_bindings() or self._dummy + assert isinstance(key_bindings, KeyBindingsBase) + version = id(key_bindings), key_bindings._version + + self._bindings2 = key_bindings + self._last_version = version + + +class GlobalOnlyKeyBindings(_Proxy): + """ + Wrapper around a :class:`.KeyBindings` object that only exposes the global + key bindings. + """ + + def __init__(self, key_bindings: KeyBindingsBase) -> None: + _Proxy.__init__(self) + self.key_bindings = key_bindings + + def _update_cache(self) -> None: + """ + If one of the original registries was changed. Update our merged + version. + """ + expected_version = self.key_bindings._version + + if self._last_version != expected_version: + bindings2 = KeyBindings() + + for b in self.key_bindings.bindings: + if b.is_global(): + bindings2.bindings.append(b) + + self._bindings2 = bindings2 + self._last_version = expected_version diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/key_processor.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/key_processor.py new file mode 100644 index 00000000..99c32d1e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/key_processor.py @@ -0,0 +1,530 @@ +# *** encoding: utf-8 *** +""" +An :class:`~.KeyProcessor` receives callbacks for the keystrokes parsed from +the input in the :class:`~prompt_toolkit.inputstream.InputStream` instance. + +The `KeyProcessor` will according to the implemented keybindings call the +correct callbacks when new key presses are feed through `feed`. +""" +import weakref +from asyncio import Task, sleep +from collections import deque +from typing import TYPE_CHECKING, Any, Deque, Generator, List, Optional, Union + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.enums import EditingMode +from prompt_toolkit.filters.app import vi_navigation_mode +from prompt_toolkit.keys import Keys +from prompt_toolkit.utils import Event + +from .key_bindings import Binding, KeyBindingsBase + +if TYPE_CHECKING: + from prompt_toolkit.application import Application + from prompt_toolkit.buffer import Buffer + + +__all__ = [ + "KeyProcessor", + "KeyPress", + "KeyPressEvent", +] + + +class KeyPress: + """ + :param key: A `Keys` instance or text (one character). + :param data: The received string on stdin. (Often vt100 escape codes.) + """ + + def __init__(self, key: Union[Keys, str], data: Optional[str] = None) -> None: + assert isinstance(key, Keys) or len(key) == 1 + + if data is None: + if isinstance(key, Keys): + data = key.value + else: + data = key # 'key' is a one character string. + + self.key = key + self.data = data + + def __repr__(self) -> str: + return "%s(key=%r, data=%r)" % (self.__class__.__name__, self.key, self.data) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, KeyPress): + return False + return self.key == other.key and self.data == other.data + + +""" +Helper object to indicate flush operation in the KeyProcessor. +NOTE: the implementation is very similar to the VT100 parser. +""" +_Flush = KeyPress("?", data="_Flush") + + +class KeyProcessor: + """ + Statemachine that receives :class:`KeyPress` instances and according to the + key bindings in the given :class:`KeyBindings`, calls the matching handlers. + + :: + + p = KeyProcessor(key_bindings) + + # Send keys into the processor. + p.feed(KeyPress(Keys.ControlX, '\x18')) + p.feed(KeyPress(Keys.ControlC, '\x03') + + # Process all the keys in the queue. + p.process_keys() + + # Now the ControlX-ControlC callback will be called if this sequence is + # registered in the key bindings. + + :param key_bindings: `KeyBindingsBase` instance. + """ + + def __init__(self, key_bindings: KeyBindingsBase) -> None: + self._bindings = key_bindings + + self.before_key_press = Event(self) + self.after_key_press = Event(self) + + self._flush_wait_task: Optional[Task] = None + + self.reset() + + def reset(self) -> None: + self._previous_key_sequence: List[KeyPress] = [] + self._previous_handler: Optional[Binding] = None + + # The queue of keys not yet send to our _process generator/state machine. + self.input_queue: Deque[KeyPress] = deque() + + # The key buffer that is matched in the generator state machine. + # (This is at at most the amount of keys that make up for one key binding.) + self.key_buffer: List[KeyPress] = [] + + #: Readline argument (for repetition of commands.) + #: https://www.gnu.org/software/bash/manual/html_node/Readline-Arguments.html + self.arg: Optional[str] = None + + # Start the processor coroutine. + self._process_coroutine = self._process() + self._process_coroutine.send(None) # type: ignore + + def _get_matches(self, key_presses: List[KeyPress]) -> List[Binding]: + """ + For a list of :class:`KeyPress` instances. Give the matching handlers + that would handle this. + """ + keys = tuple(k.key for k in key_presses) + + # Try match, with mode flag + return [b for b in self._bindings.get_bindings_for_keys(keys) if b.filter()] + + def _is_prefix_of_longer_match(self, key_presses: List[KeyPress]) -> bool: + """ + For a list of :class:`KeyPress` instances. Return True if there is any + handler that is bound to a suffix of this keys. + """ + keys = tuple(k.key for k in key_presses) + + # Get the filters for all the key bindings that have a longer match. + # Note that we transform it into a `set`, because we don't care about + # the actual bindings and executing it more than once doesn't make + # sense. (Many key bindings share the same filter.) + filters = set( + b.filter for b in self._bindings.get_bindings_starting_with_keys(keys) + ) + + # When any key binding is active, return True. + return any(f() for f in filters) + + def _process(self) -> Generator[None, KeyPress, None]: + """ + Coroutine implementing the key match algorithm. Key strokes are sent + into this generator, and it calls the appropriate handlers. + """ + buffer = self.key_buffer + retry = False + + while True: + flush = False + + if retry: + retry = False + else: + key = yield + if key is _Flush: + flush = True + else: + buffer.append(key) + + # If we have some key presses, check for matches. + if buffer: + matches = self._get_matches(buffer) + + if flush: + is_prefix_of_longer_match = False + else: + is_prefix_of_longer_match = self._is_prefix_of_longer_match(buffer) + + # When eager matches were found, give priority to them and also + # ignore all the longer matches. + eager_matches = [m for m in matches if m.eager()] + + if eager_matches: + matches = eager_matches + is_prefix_of_longer_match = False + + # Exact matches found, call handler. + if not is_prefix_of_longer_match and matches: + self._call_handler(matches[-1], key_sequence=buffer[:]) + del buffer[:] # Keep reference. + + # No match found. + elif not is_prefix_of_longer_match and not matches: + retry = True + found = False + + # Loop over the input, try longest match first and shift. + for i in range(len(buffer), 0, -1): + matches = self._get_matches(buffer[:i]) + if matches: + self._call_handler(matches[-1], key_sequence=buffer[:i]) + del buffer[:i] + found = True + break + + if not found: + del buffer[:1] + + def feed(self, key_press: KeyPress, first: bool = False) -> None: + """ + Add a new :class:`KeyPress` to the input queue. + (Don't forget to call `process_keys` in order to process the queue.) + + :param first: If true, insert before everything else. + """ + if first: + self.input_queue.appendleft(key_press) + else: + self.input_queue.append(key_press) + + def feed_multiple(self, key_presses: List[KeyPress], first: bool = False) -> None: + """ + :param first: If true, insert before everything else. + """ + if first: + self.input_queue.extendleft(reversed(key_presses)) + else: + self.input_queue.extend(key_presses) + + def process_keys(self) -> None: + """ + Process all the keys in the `input_queue`. + (To be called after `feed`.) + + Note: because of the `feed`/`process_keys` separation, it is + possible to call `feed` from inside a key binding. + This function keeps looping until the queue is empty. + """ + app = get_app() + + def not_empty() -> bool: + # When the application result is set, stop processing keys. (E.g. + # if ENTER was received, followed by a few additional key strokes, + # leave the other keys in the queue.) + if app.is_done: + # But if there are still CPRResponse keys in the queue, these + # need to be processed. + return any(k for k in self.input_queue if k.key == Keys.CPRResponse) + else: + return bool(self.input_queue) + + def get_next() -> KeyPress: + if app.is_done: + # Only process CPR responses. Everything else is typeahead. + cpr = [k for k in self.input_queue if k.key == Keys.CPRResponse][0] + self.input_queue.remove(cpr) + return cpr + else: + return self.input_queue.popleft() + + keys_processed = False + is_flush = False + + while not_empty(): + keys_processed = True + + # Process next key. + key_press = get_next() + + is_flush = key_press is _Flush + is_cpr = key_press.key == Keys.CPRResponse + + if not is_flush and not is_cpr: + self.before_key_press.fire() + + try: + self._process_coroutine.send(key_press) + except Exception: + # If for some reason something goes wrong in the parser, (maybe + # an exception was raised) restart the processor for next time. + self.reset() + self.empty_queue() + app.invalidate() + raise + + if not is_flush and not is_cpr: + self.after_key_press.fire() + + if keys_processed: + # Invalidate user interface. + app.invalidate() + + # Skip timeout if the last key was flush. + if not is_flush: + self._start_timeout() + + def empty_queue(self) -> List[KeyPress]: + """ + Empty the input queue. Return the unprocessed input. + """ + key_presses = list(self.input_queue) + self.input_queue.clear() + + # Filter out CPRs. We don't want to return these. + key_presses = [k for k in key_presses if k.key != Keys.CPRResponse] + return key_presses + + def _call_handler(self, handler: Binding, key_sequence: List[KeyPress]) -> None: + app = get_app() + was_recording_emacs = app.emacs_state.is_recording + was_recording_vi = bool(app.vi_state.recording_register) + was_temporary_navigation_mode = app.vi_state.temporary_navigation_mode + arg = self.arg + self.arg = None + + event = KeyPressEvent( + weakref.ref(self), + arg=arg, + key_sequence=key_sequence, + previous_key_sequence=self._previous_key_sequence, + is_repeat=(handler == self._previous_handler), + ) + + # Save the state of the current buffer. + if handler.save_before(event): + event.app.current_buffer.save_to_undo_stack() + + # Call handler. + from prompt_toolkit.buffer import EditReadOnlyBuffer + + try: + handler.call(event) + self._fix_vi_cursor_position(event) + + except EditReadOnlyBuffer: + # When a key binding does an attempt to change a buffer which is + # read-only, we can ignore that. We sound a bell and go on. + app.output.bell() + + if was_temporary_navigation_mode: + self._leave_vi_temp_navigation_mode(event) + + self._previous_key_sequence = key_sequence + self._previous_handler = handler + + # Record the key sequence in our macro. (Only if we're in macro mode + # before and after executing the key.) + if handler.record_in_macro(): + if app.emacs_state.is_recording and was_recording_emacs: + recording = app.emacs_state.current_recording + if recording is not None: # Should always be true, given that + # `was_recording_emacs` is set. + recording.extend(key_sequence) + + if app.vi_state.recording_register and was_recording_vi: + for k in key_sequence: + app.vi_state.current_recording += k.data + + def _fix_vi_cursor_position(self, event: "KeyPressEvent") -> None: + """ + After every command, make sure that if we are in Vi navigation mode, we + never put the cursor after the last character of a line. (Unless it's + an empty line.) + """ + app = event.app + buff = app.current_buffer + preferred_column = buff.preferred_column + + if ( + vi_navigation_mode() + and buff.document.is_cursor_at_the_end_of_line + and len(buff.document.current_line) > 0 + ): + buff.cursor_position -= 1 + + # Set the preferred_column for arrow up/down again. + # (This was cleared after changing the cursor position.) + buff.preferred_column = preferred_column + + def _leave_vi_temp_navigation_mode(self, event: "KeyPressEvent") -> None: + """ + If we're in Vi temporary navigation (normal) mode, return to + insert/replace mode after executing one action. + """ + app = event.app + + if app.editing_mode == EditingMode.VI: + # Not waiting for a text object and no argument has been given. + if app.vi_state.operator_func is None and self.arg is None: + app.vi_state.temporary_navigation_mode = False + + def _start_timeout(self) -> None: + """ + Start auto flush timeout. Similar to Vim's `timeoutlen` option. + + Start a background coroutine with a timer. When this timeout expires + and no key was pressed in the meantime, we flush all data in the queue + and call the appropriate key binding handlers. + """ + app = get_app() + timeout = app.timeoutlen + + if timeout is None: + return + + async def wait() -> None: + " Wait for timeout. " + # This sleep can be cancelled. In that case we don't flush. + await sleep(timeout) + + if len(self.key_buffer) > 0: + # (No keys pressed in the meantime.) + flush_keys() + + def flush_keys() -> None: + " Flush keys. " + self.feed(_Flush) + self.process_keys() + + # Automatically flush keys. + if self._flush_wait_task: + self._flush_wait_task.cancel() + self._flush_wait_task = app.create_background_task(wait()) + + +class KeyPressEvent: + """ + Key press event, delivered to key bindings. + + :param key_processor_ref: Weak reference to the `KeyProcessor`. + :param arg: Repetition argument. + :param key_sequence: List of `KeyPress` instances. + :param previouskey_sequence: Previous list of `KeyPress` instances. + :param is_repeat: True when the previous event was delivered to the same handler. + """ + + def __init__( + self, + key_processor_ref: "weakref.ReferenceType[KeyProcessor]", + arg: Optional[str], + key_sequence: List[KeyPress], + previous_key_sequence: List[KeyPress], + is_repeat: bool, + ) -> None: + + self._key_processor_ref = key_processor_ref + self.key_sequence = key_sequence + self.previous_key_sequence = previous_key_sequence + + #: True when the previous key sequence was handled by the same handler. + self.is_repeat = is_repeat + + self._arg = arg + self._app = get_app() + + def __repr__(self) -> str: + return "KeyPressEvent(arg=%r, key_sequence=%r, is_repeat=%r)" % ( + self.arg, + self.key_sequence, + self.is_repeat, + ) + + @property + def data(self) -> str: + return self.key_sequence[-1].data + + @property + def key_processor(self) -> KeyProcessor: + processor = self._key_processor_ref() + if processor is None: + raise Exception("KeyProcessor was lost. This should not happen.") + return processor + + @property + def app(self) -> "Application[Any]": + """ + The current `Application` object. + """ + return self._app + + @property + def current_buffer(self) -> "Buffer": + """ + The current buffer. + """ + return self.app.current_buffer + + @property + def arg(self) -> int: + """ + Repetition argument. + """ + if self._arg == "-": + return -1 + + result = int(self._arg or 1) + + # Don't exceed a million. + if int(result) >= 1000000: + result = 1 + + return result + + @property + def arg_present(self) -> bool: + """ + True if repetition argument was explicitly provided. + """ + return self._arg is not None + + def append_to_arg_count(self, data: str) -> None: + """ + Add digit to the input argument. + + :param data: the typed digit as string + """ + assert data in "-0123456789" + current = self._arg + + if data == "-": + assert current is None or current == "-" + result = data + elif current is None: + result = data + else: + result = "%s%s" % (current, data) + + self.key_processor.arg = result + + @property + def cli(self) -> "Application": + " For backward-compatibility. " + return self.app diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/vi_state.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/vi_state.py new file mode 100644 index 00000000..017bb4b9 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/key_binding/vi_state.py @@ -0,0 +1,107 @@ +from enum import Enum +from typing import TYPE_CHECKING, Callable, Dict, Optional + +from prompt_toolkit.clipboard import ClipboardData + +if TYPE_CHECKING: + from .key_bindings.vi import TextObject + from .key_processor import KeyPressEvent + +__all__ = [ + "InputMode", + "CharacterFind", + "ViState", +] + + +class InputMode(str, Enum): + value: str + + INSERT = "vi-insert" + INSERT_MULTIPLE = "vi-insert-multiple" + NAVIGATION = "vi-navigation" # Normal mode. + REPLACE = "vi-replace" + REPLACE_SINGLE = "vi-replace-single" + + +class CharacterFind: + def __init__(self, character: str, backwards: bool = False) -> None: + self.character = character + self.backwards = backwards + + +class ViState: + """ + Mutable class to hold the state of the Vi navigation. + """ + + def __init__(self) -> None: + #: None or CharacterFind instance. (This is used to repeat the last + #: search in Vi mode, by pressing the 'n' or 'N' in navigation mode.) + self.last_character_find: Optional[CharacterFind] = None + + # When an operator is given and we are waiting for text object, + # -- e.g. in the case of 'dw', after the 'd' --, an operator callback + # is set here. + self.operator_func: Optional[ + Callable[["KeyPressEvent", "TextObject"], None] + ] = None + self.operator_arg: Optional[int] = None + + #: Named registers. Maps register name (e.g. 'a') to + #: :class:`ClipboardData` instances. + self.named_registers: Dict[str, ClipboardData] = {} + + #: The Vi mode we're currently in to. + self.__input_mode = InputMode.INSERT + + #: Waiting for digraph. + self.waiting_for_digraph = False + self.digraph_symbol1: Optional[str] = None # (None or a symbol.) + + #: When true, make ~ act as an operator. + self.tilde_operator = False + + #: Register in which we are recording a macro. + #: `None` when not recording anything. + # Note that the recording is only stored in the register after the + # recording is stopped. So we record in a separate `current_recording` + # variable. + self.recording_register: Optional[str] = None + self.current_recording: str = "" + + # Temporary navigation (normal) mode. + # This happens when control-o has been pressed in insert or replace + # mode. The user can now do one navigation action and we'll return back + # to insert/replace. + self.temporary_navigation_mode = False + + @property + def input_mode(self) -> InputMode: + " Get `InputMode`. " + return self.__input_mode + + @input_mode.setter + def input_mode(self, value: InputMode) -> None: + " Set `InputMode`. " + if value == InputMode.NAVIGATION: + self.waiting_for_digraph = False + self.operator_func = None + self.operator_arg = None + + self.__input_mode = value + + def reset(self) -> None: + """ + Reset state, go back to the given mode. INSERT by default. + """ + # Go back to insert mode. + self.input_mode = InputMode.INSERT + + self.waiting_for_digraph = False + self.operator_func = None + self.operator_arg = None + + # Reset recording state. + self.recording_register = None + self.current_recording = "" diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/keys.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/keys.py new file mode 100644 index 00000000..58b2e4b7 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/keys.py @@ -0,0 +1,219 @@ +from enum import Enum +from typing import Dict, List + +__all__ = [ + "Keys", + "ALL_KEYS", +] + + +class Keys(str, Enum): + """ + List of keys for use in key bindings. + + Note that this is an "StrEnum", all values can be compared against + strings. + """ + + value: str + + Escape = "escape" # Also Control-[ + ShiftEscape = "s-escape" + + ControlAt = "c-@" # Also Control-Space. + + ControlA = "c-a" + ControlB = "c-b" + ControlC = "c-c" + ControlD = "c-d" + ControlE = "c-e" + ControlF = "c-f" + ControlG = "c-g" + ControlH = "c-h" + ControlI = "c-i" # Tab + ControlJ = "c-j" # Newline + ControlK = "c-k" + ControlL = "c-l" + ControlM = "c-m" # Carriage return + ControlN = "c-n" + ControlO = "c-o" + ControlP = "c-p" + ControlQ = "c-q" + ControlR = "c-r" + ControlS = "c-s" + ControlT = "c-t" + ControlU = "c-u" + ControlV = "c-v" + ControlW = "c-w" + ControlX = "c-x" + ControlY = "c-y" + ControlZ = "c-z" + + Control1 = "c-1" + Control2 = "c-2" + Control3 = "c-3" + Control4 = "c-4" + Control5 = "c-5" + Control6 = "c-6" + Control7 = "c-7" + Control8 = "c-8" + Control9 = "c-9" + Control0 = "c-0" + + ControlShift1 = "c-s-1" + ControlShift2 = "c-s-2" + ControlShift3 = "c-s-3" + ControlShift4 = "c-s-4" + ControlShift5 = "c-s-5" + ControlShift6 = "c-s-6" + ControlShift7 = "c-s-7" + ControlShift8 = "c-s-8" + ControlShift9 = "c-s-9" + ControlShift0 = "c-s-0" + + ControlBackslash = "c-\\" + ControlSquareClose = "c-]" + ControlCircumflex = "c-^" + ControlUnderscore = "c-_" + + Left = "left" + Right = "right" + Up = "up" + Down = "down" + Home = "home" + End = "end" + Insert = "insert" + Delete = "delete" + PageUp = "pageup" + PageDown = "pagedown" + + ControlLeft = "c-left" + ControlRight = "c-right" + ControlUp = "c-up" + ControlDown = "c-down" + ControlHome = "c-home" + ControlEnd = "c-end" + ControlInsert = "c-insert" + ControlDelete = "c-delete" + ControlPageUp = "c-pageup" + ControlPageDown = "c-pagedown" + + ShiftLeft = "s-left" + ShiftRight = "s-right" + ShiftUp = "s-up" + ShiftDown = "s-down" + ShiftHome = "s-home" + ShiftEnd = "s-end" + ShiftInsert = "s-insert" + ShiftDelete = "s-delete" + ShiftPageUp = "s-pageup" + ShiftPageDown = "s-pagedown" + + ControlShiftLeft = "c-s-left" + ControlShiftRight = "c-s-right" + ControlShiftUp = "c-s-up" + ControlShiftDown = "c-s-down" + ControlShiftHome = "c-s-home" + ControlShiftEnd = "c-s-end" + ControlShiftInsert = "c-s-insert" + ControlShiftDelete = "c-s-delete" + ControlShiftPageUp = "c-s-pageup" + ControlShiftPageDown = "c-s-pagedown" + + BackTab = "s-tab" # shift + tab + + F1 = "f1" + F2 = "f2" + F3 = "f3" + F4 = "f4" + F5 = "f5" + F6 = "f6" + F7 = "f7" + F8 = "f8" + F9 = "f9" + F10 = "f10" + F11 = "f11" + F12 = "f12" + F13 = "f13" + F14 = "f14" + F15 = "f15" + F16 = "f16" + F17 = "f17" + F18 = "f18" + F19 = "f19" + F20 = "f20" + F21 = "f21" + F22 = "f22" + F23 = "f23" + F24 = "f24" + + ControlF1 = "c-f1" + ControlF2 = "c-f2" + ControlF3 = "c-f3" + ControlF4 = "c-f4" + ControlF5 = "c-f5" + ControlF6 = "c-f6" + ControlF7 = "c-f7" + ControlF8 = "c-f8" + ControlF9 = "c-f9" + ControlF10 = "c-f10" + ControlF11 = "c-f11" + ControlF12 = "c-f12" + ControlF13 = "c-f13" + ControlF14 = "c-f14" + ControlF15 = "c-f15" + ControlF16 = "c-f16" + ControlF17 = "c-f17" + ControlF18 = "c-f18" + ControlF19 = "c-f19" + ControlF20 = "c-f20" + ControlF21 = "c-f21" + ControlF22 = "c-f22" + ControlF23 = "c-f23" + ControlF24 = "c-f24" + + # Matches any key. + Any = "" + + # Special. + ScrollUp = "" + ScrollDown = "" + + CPRResponse = "" + Vt100MouseEvent = "" + WindowsMouseEvent = "" + BracketedPaste = "" + + # For internal use: key which is ignored. + # (The key binding for this key should not do anything.) + Ignore = "" + + # Some 'Key' aliases (for backwards-compatibility). + ControlSpace = ControlAt + Tab = ControlI + Enter = ControlM + Backspace = ControlH + + # ShiftControl was renamed to ControlShift in + # 888fcb6fa4efea0de8333177e1bbc792f3ff3c24 (20 Feb 2020). + ShiftControlLeft = ControlShiftLeft + ShiftControlRight = ControlShiftRight + ShiftControlHome = ControlShiftHome + ShiftControlEnd = ControlShiftEnd + + +ALL_KEYS: List[str] = [k.value for k in Keys] + + +# Aliases. +KEY_ALIASES: Dict[str, str] = { + "backspace": "c-h", + "c-space": "c-@", + "enter": "c-m", + "tab": "c-i", + # ShiftControl was renamed to ControlShift. + "s-c-left": "c-s-left", + "s-c-right": "c-s-right", + "s-c-home": "c-s-home", + "s-c-end": "c-s-end", +} diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__init__.py new file mode 100644 index 00000000..a3292081 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__init__.py @@ -0,0 +1,142 @@ +""" +Command line layout definitions +------------------------------- + +The layout of a command line interface is defined by a Container instance. +There are two main groups of classes here. Containers and controls: + +- A container can contain other containers or controls, it can have multiple + children and it decides about the dimensions. +- A control is responsible for rendering the actual content to a screen. + A control can propose some dimensions, but it's the container who decides + about the dimensions -- or when the control consumes more space -- which part + of the control will be visible. + + +Container classes:: + + - Container (Abstract base class) + |- HSplit (Horizontal split) + |- VSplit (Vertical split) + |- FloatContainer (Container which can also contain menus and other floats) + `- Window (Container which contains one actual control + +Control classes:: + + - UIControl (Abstract base class) + |- FormattedTextControl (Renders formatted text, or a simple list of text fragments) + `- BufferControl (Renders an input buffer.) + + +Usually, you end up wrapping every control inside a `Window` object, because +that's the only way to render it in a layout. + +There are some prepared toolbars which are ready to use:: + +- SystemToolbar (Shows the 'system' input buffer, for entering system commands.) +- ArgToolbar (Shows the input 'arg', for repetition of input commands.) +- SearchToolbar (Shows the 'search' input buffer, for incremental search.) +- CompletionsToolbar (Shows the completions of the current buffer.) +- ValidationToolbar (Shows validation errors of the current buffer.) + +And one prepared menu: + +- CompletionsMenu + +""" +from .containers import ( + AnyContainer, + ColorColumn, + ConditionalContainer, + Container, + DynamicContainer, + Float, + FloatContainer, + HorizontalAlign, + HSplit, + ScrollOffsets, + VerticalAlign, + VSplit, + Window, + WindowAlign, + WindowRenderInfo, + is_container, + to_container, + to_window, +) +from .controls import ( + BufferControl, + DummyControl, + FormattedTextControl, + SearchBufferControl, + UIContent, + UIControl, +) +from .dimension import ( + AnyDimension, + D, + Dimension, + is_dimension, + max_layout_dimensions, + sum_layout_dimensions, + to_dimension, +) +from .layout import InvalidLayoutError, Layout, walk +from .margins import ( + ConditionalMargin, + Margin, + NumberedMargin, + PromptMargin, + ScrollbarMargin, +) +from .menus import CompletionsMenu, MultiColumnCompletionsMenu + +__all__ = [ + # Layout. + "Layout", + "InvalidLayoutError", + "walk", + # Dimensions. + "AnyDimension", + "Dimension", + "D", + "sum_layout_dimensions", + "max_layout_dimensions", + "to_dimension", + "is_dimension", + # Containers. + "AnyContainer", + "Container", + "HorizontalAlign", + "VerticalAlign", + "HSplit", + "VSplit", + "FloatContainer", + "Float", + "WindowAlign", + "Window", + "WindowRenderInfo", + "ConditionalContainer", + "ScrollOffsets", + "ColorColumn", + "to_container", + "to_window", + "is_container", + "DynamicContainer", + # Controls. + "BufferControl", + "SearchBufferControl", + "DummyControl", + "FormattedTextControl", + "UIControl", + "UIContent", + # Margins. + "Margin", + "NumberedMargin", + "ScrollbarMargin", + "ConditionalMargin", + "PromptMargin", + # Menus. + "CompletionsMenu", + "MultiColumnCompletionsMenu", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..b1a31f0a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/containers.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/containers.cpython-38.pyc new file mode 100644 index 00000000..c86d55e6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/containers.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/controls.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/controls.cpython-38.pyc new file mode 100644 index 00000000..3e76aab2 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/controls.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/dimension.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/dimension.cpython-38.pyc new file mode 100644 index 00000000..a8044512 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/dimension.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/dummy.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/dummy.cpython-38.pyc new file mode 100644 index 00000000..a85b407e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/dummy.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/layout.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/layout.cpython-38.pyc new file mode 100644 index 00000000..d7c8143b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/layout.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/margins.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/margins.cpython-38.pyc new file mode 100644 index 00000000..dd712a2b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/margins.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/menus.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/menus.cpython-38.pyc new file mode 100644 index 00000000..7b6a4445 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/menus.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/mouse_handlers.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/mouse_handlers.cpython-38.pyc new file mode 100644 index 00000000..fe248dfd Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/mouse_handlers.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/processors.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/processors.cpython-38.pyc new file mode 100644 index 00000000..ce013569 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/processors.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/screen.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/screen.cpython-38.pyc new file mode 100644 index 00000000..3ef3c20f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/screen.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/utils.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/utils.cpython-38.pyc new file mode 100644 index 00000000..db4d0542 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/__pycache__/utils.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/containers.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/containers.py new file mode 100644 index 00000000..f09e4d94 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/containers.py @@ -0,0 +1,2702 @@ +""" +Container for the layout. +(Containers can contain other containers or user interface controls.) +""" +from abc import ABCMeta, abstractmethod +from enum import Enum +from functools import partial +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.cache import SimpleCache +from prompt_toolkit.data_structures import Point +from prompt_toolkit.filters import ( + FilterOrBool, + emacs_insert_mode, + to_filter, + vi_insert_mode, +) +from prompt_toolkit.formatted_text import ( + AnyFormattedText, + StyleAndTextTuples, + to_formatted_text, +) +from prompt_toolkit.formatted_text.utils import ( + fragment_list_to_text, + fragment_list_width, +) +from prompt_toolkit.key_binding import KeyBindingsBase +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.utils import get_cwidth, take_using_weights, to_int, to_str + +from .controls import ( + DummyControl, + FormattedTextControl, + GetLinePrefixCallable, + UIContent, + UIControl, +) +from .dimension import ( + AnyDimension, + Dimension, + max_layout_dimensions, + sum_layout_dimensions, + to_dimension, +) +from .margins import Margin +from .mouse_handlers import MouseHandlers +from .screen import _CHAR_CACHE, Screen, WritePosition +from .utils import explode_text_fragments + +if TYPE_CHECKING: + from typing_extensions import Protocol + + NotImplementedOrNone = object + +__all__ = [ + "AnyContainer", + "Container", + "HorizontalAlign", + "VerticalAlign", + "HSplit", + "VSplit", + "FloatContainer", + "Float", + "WindowAlign", + "Window", + "WindowRenderInfo", + "ConditionalContainer", + "ScrollOffsets", + "ColorColumn", + "to_container", + "to_window", + "is_container", + "DynamicContainer", +] + + +class Container(metaclass=ABCMeta): + """ + Base class for user interface layout. + """ + + @abstractmethod + def reset(self) -> None: + """ + Reset the state of this container and all the children. + (E.g. reset scroll offsets, etc...) + """ + + @abstractmethod + def preferred_width(self, max_available_width: int) -> Dimension: + """ + Return a :class:`~prompt_toolkit.layout.Dimension` that represents the + desired width for this container. + """ + + @abstractmethod + def preferred_height(self, width: int, max_available_height: int) -> Dimension: + """ + Return a :class:`~prompt_toolkit.layout.Dimension` that represents the + desired height for this container. + """ + + @abstractmethod + def write_to_screen( + self, + screen: Screen, + mouse_handlers: MouseHandlers, + write_position: WritePosition, + parent_style: str, + erase_bg: bool, + z_index: Optional[int], + ) -> None: + """ + Write the actual content to the screen. + + :param screen: :class:`~prompt_toolkit.layout.screen.Screen` + :param mouse_handlers: :class:`~prompt_toolkit.layout.mouse_handlers.MouseHandlers`. + :param parent_style: Style string to pass to the :class:`.Window` + object. This will be applied to all content of the windows. + :class:`.VSplit` and :class:`.HSplit` can use it to pass their + style down to the windows that they contain. + :param z_index: Used for propagating z_index from parent to child. + """ + + def is_modal(self) -> bool: + """ + When this container is modal, key bindings from parent containers are + not taken into account if a user control in this container is focused. + """ + return False + + def get_key_bindings(self) -> Optional[KeyBindingsBase]: + """ + Returns a :class:`.KeyBindings` object. These bindings become active when any + user control in this container has the focus, except if any containers + between this container and the focused user control is modal. + """ + return None + + @abstractmethod + def get_children(self) -> List["Container"]: + """ + Return the list of child :class:`.Container` objects. + """ + return [] + + +if TYPE_CHECKING: + + class MagicContainer(Protocol): + """ + Any object that implements ``__pt_container__`` represents a container. + """ + + def __pt_container__(self) -> "AnyContainer": + ... + + +AnyContainer = Union[Container, "MagicContainer"] + + +def _window_too_small() -> "Window": + " Create a `Window` that displays the 'Window too small' text. " + return Window( + FormattedTextControl(text=[("class:window-too-small", " Window too small... ")]) + ) + + +class VerticalAlign(Enum): + " Alignment for `HSplit`. " + TOP = "TOP" + CENTER = "CENTER" + BOTTOM = "BOTTOM" + JUSTIFY = "JUSTIFY" + + +class HorizontalAlign(Enum): + " Alignment for `VSplit`. " + LEFT = "LEFT" + CENTER = "CENTER" + RIGHT = "RIGHT" + JUSTIFY = "JUSTIFY" + + +class _Split(Container): + """ + The common parts of `VSplit` and `HSplit`. + """ + + def __init__( + self, + children: Sequence[AnyContainer], + window_too_small: Optional[Container] = None, + padding: AnyDimension = Dimension.exact(0), + padding_char: Optional[str] = None, + padding_style: str = "", + width: AnyDimension = None, + height: AnyDimension = None, + z_index: Optional[int] = None, + modal: bool = False, + key_bindings: Optional[KeyBindingsBase] = None, + style: Union[str, Callable[[], str]] = "", + ) -> None: + + self.children = [to_container(c) for c in children] + self.window_too_small = window_too_small or _window_too_small() + self.padding = padding + self.padding_char = padding_char + self.padding_style = padding_style + + self.width = width + self.height = height + self.z_index = z_index + + self.modal = modal + self.key_bindings = key_bindings + self.style = style + + def is_modal(self) -> bool: + return self.modal + + def get_key_bindings(self) -> Optional[KeyBindingsBase]: + return self.key_bindings + + def get_children(self) -> List[Container]: + return self.children + + +class HSplit(_Split): + """ + Several layouts, one stacked above/under the other. :: + + +--------------------+ + | | + +--------------------+ + | | + +--------------------+ + + By default, this doesn't display a horizontal line between the children, + but if this is something you need, then create a HSplit as follows:: + + HSplit(children=[ ... ], padding_char='-', + padding=1, padding_style='#ffff00') + + :param children: List of child :class:`.Container` objects. + :param window_too_small: A :class:`.Container` object that is displayed if + there is not enough space for all the children. By default, this is a + "Window too small" message. + :param align: `VerticalAlign` value. + :param width: When given, use this width instead of looking at the children. + :param height: When given, use this height instead of looking at the children. + :param z_index: (int or None) When specified, this can be used to bring + element in front of floating elements. `None` means: inherit from parent. + :param style: A style string. + :param modal: ``True`` or ``False``. + :param key_bindings: ``None`` or a :class:`.KeyBindings` object. + + :param padding: (`Dimension` or int), size to be used for the padding. + :param padding_char: Character to be used for filling in the padding. + :param padding_style: Style to applied to the padding. + """ + + def __init__( + self, + children: Sequence[AnyContainer], + window_too_small: Optional[Container] = None, + align: VerticalAlign = VerticalAlign.JUSTIFY, + padding: AnyDimension = 0, + padding_char: Optional[str] = None, + padding_style: str = "", + width: AnyDimension = None, + height: AnyDimension = None, + z_index: Optional[int] = None, + modal: bool = False, + key_bindings: Optional[KeyBindingsBase] = None, + style: Union[str, Callable[[], str]] = "", + ) -> None: + + super().__init__( + children=children, + window_too_small=window_too_small, + padding=padding, + padding_char=padding_char, + padding_style=padding_style, + width=width, + height=height, + z_index=z_index, + modal=modal, + key_bindings=key_bindings, + style=style, + ) + + self.align = align + + self._children_cache: SimpleCache[ + Tuple[Container, ...], List[Container] + ] = SimpleCache(maxsize=1) + self._remaining_space_window = Window() # Dummy window. + + def preferred_width(self, max_available_width: int) -> Dimension: + if self.width is not None: + return to_dimension(self.width) + + if self.children: + dimensions = [c.preferred_width(max_available_width) for c in self.children] + return max_layout_dimensions(dimensions) + else: + return Dimension() + + def preferred_height(self, width: int, max_available_height: int) -> Dimension: + if self.height is not None: + return to_dimension(self.height) + + dimensions = [ + c.preferred_height(width, max_available_height) for c in self._all_children + ] + return sum_layout_dimensions(dimensions) + + def reset(self) -> None: + for c in self.children: + c.reset() + + @property + def _all_children(self) -> List[Container]: + """ + List of child objects, including padding. + """ + + def get() -> List[Container]: + result: List[Container] = [] + + # Padding Top. + if self.align in (VerticalAlign.CENTER, VerticalAlign.BOTTOM): + result.append(Window(width=Dimension(preferred=0))) + + # The children with padding. + for child in self.children: + result.append(child) + result.append( + Window( + height=self.padding, + char=self.padding_char, + style=self.padding_style, + ) + ) + if result: + result.pop() + + # Padding right. + if self.align in (VerticalAlign.CENTER, VerticalAlign.TOP): + result.append(Window(width=Dimension(preferred=0))) + + return result + + return self._children_cache.get(tuple(self.children), get) + + def write_to_screen( + self, + screen: Screen, + mouse_handlers: MouseHandlers, + write_position: WritePosition, + parent_style: str, + erase_bg: bool, + z_index: Optional[int], + ) -> None: + """ + Render the prompt to a `Screen` instance. + + :param screen: The :class:`~prompt_toolkit.layout.screen.Screen` class + to which the output has to be written. + """ + sizes = self._divide_heights(write_position) + style = parent_style + " " + to_str(self.style) + z_index = z_index if self.z_index is None else self.z_index + + if sizes is None: + self.window_too_small.write_to_screen( + screen, mouse_handlers, write_position, style, erase_bg, z_index + ) + else: + # + ypos = write_position.ypos + xpos = write_position.xpos + width = write_position.width + + # Draw child panes. + for s, c in zip(sizes, self._all_children): + c.write_to_screen( + screen, + mouse_handlers, + WritePosition(xpos, ypos, width, s), + style, + erase_bg, + z_index, + ) + ypos += s + + # Fill in the remaining space. This happens when a child control + # refuses to take more space and we don't have any padding. Adding a + # dummy child control for this (in `self._all_children`) is not + # desired, because in some situations, it would take more space, even + # when it's not required. This is required to apply the styling. + remaining_height = write_position.ypos + write_position.height - ypos + if remaining_height > 0: + self._remaining_space_window.write_to_screen( + screen, + mouse_handlers, + WritePosition(xpos, ypos, width, remaining_height), + style, + erase_bg, + z_index, + ) + + def _divide_heights(self, write_position: WritePosition) -> Optional[List[int]]: + """ + Return the heights for all rows. + Or None when there is not enough space. + """ + if not self.children: + return [] + + width = write_position.width + height = write_position.height + + # Calculate heights. + dimensions = [c.preferred_height(width, height) for c in self._all_children] + + # Sum dimensions + sum_dimensions = sum_layout_dimensions(dimensions) + + # If there is not enough space for both. + # Don't do anything. + if sum_dimensions.min > height: + return None + + # Find optimal sizes. (Start with minimal size, increase until we cover + # the whole height.) + sizes = [d.min for d in dimensions] + + child_generator = take_using_weights( + items=list(range(len(dimensions))), weights=[d.weight for d in dimensions] + ) + + i = next(child_generator) + + # Increase until we meet at least the 'preferred' size. + preferred_stop = min(height, sum_dimensions.preferred) + preferred_dimensions = [d.preferred for d in dimensions] + + while sum(sizes) < preferred_stop: + if sizes[i] < preferred_dimensions[i]: + sizes[i] += 1 + i = next(child_generator) + + # Increase until we use all the available space. (or until "max") + if not get_app().is_done: + max_stop = min(height, sum_dimensions.max) + max_dimensions = [d.max for d in dimensions] + + while sum(sizes) < max_stop: + if sizes[i] < max_dimensions[i]: + sizes[i] += 1 + i = next(child_generator) + + return sizes + + +class VSplit(_Split): + """ + Several layouts, one stacked left/right of the other. :: + + +---------+----------+ + | | | + | | | + +---------+----------+ + + By default, this doesn't display a vertical line between the children, but + if this is something you need, then create a HSplit as follows:: + + VSplit(children=[ ... ], padding_char='|', + padding=1, padding_style='#ffff00') + + :param children: List of child :class:`.Container` objects. + :param window_too_small: A :class:`.Container` object that is displayed if + there is not enough space for all the children. By default, this is a + "Window too small" message. + :param align: `HorizontalAlign` value. + :param width: When given, use this width instead of looking at the children. + :param height: When given, use this height instead of looking at the children. + :param z_index: (int or None) When specified, this can be used to bring + element in front of floating elements. `None` means: inherit from parent. + :param style: A style string. + :param modal: ``True`` or ``False``. + :param key_bindings: ``None`` or a :class:`.KeyBindings` object. + + :param padding: (`Dimension` or int), size to be used for the padding. + :param padding_char: Character to be used for filling in the padding. + :param padding_style: Style to applied to the padding. + """ + + def __init__( + self, + children: Sequence[AnyContainer], + window_too_small: Optional[Container] = None, + align: HorizontalAlign = HorizontalAlign.JUSTIFY, + padding: AnyDimension = 0, + padding_char: Optional[str] = None, + padding_style: str = "", + width: AnyDimension = None, + height: AnyDimension = None, + z_index: Optional[int] = None, + modal: bool = False, + key_bindings: Optional[KeyBindingsBase] = None, + style: Union[str, Callable[[], str]] = "", + ) -> None: + + super().__init__( + children=children, + window_too_small=window_too_small, + padding=padding, + padding_char=padding_char, + padding_style=padding_style, + width=width, + height=height, + z_index=z_index, + modal=modal, + key_bindings=key_bindings, + style=style, + ) + + self.align = align + + self._children_cache: SimpleCache[ + Tuple[Container, ...], List[Container] + ] = SimpleCache(maxsize=1) + self._remaining_space_window = Window() # Dummy window. + + def preferred_width(self, max_available_width: int) -> Dimension: + if self.width is not None: + return to_dimension(self.width) + + dimensions = [ + c.preferred_width(max_available_width) for c in self._all_children + ] + + return sum_layout_dimensions(dimensions) + + def preferred_height(self, width: int, max_available_height: int) -> Dimension: + if self.height is not None: + return to_dimension(self.height) + + # At the point where we want to calculate the heights, the widths have + # already been decided. So we can trust `width` to be the actual + # `width` that's going to be used for the rendering. So, + # `divide_widths` is supposed to use all of the available width. + # Using only the `preferred` width caused a bug where the reported + # height was more than required. (we had a `BufferControl` which did + # wrap lines because of the smaller width returned by `_divide_widths`. + + sizes = self._divide_widths(width) + children = self._all_children + + if sizes is None: + return Dimension() + else: + dimensions = [ + c.preferred_height(s, max_available_height) + for s, c in zip(sizes, children) + ] + return max_layout_dimensions(dimensions) + + def reset(self) -> None: + for c in self.children: + c.reset() + + @property + def _all_children(self) -> List[Container]: + """ + List of child objects, including padding. + """ + + def get() -> List[Container]: + result: List[Container] = [] + + # Padding left. + if self.align in (HorizontalAlign.CENTER, HorizontalAlign.RIGHT): + result.append(Window(width=Dimension(preferred=0))) + + # The children with padding. + for child in self.children: + result.append(child) + result.append( + Window( + width=self.padding, + char=self.padding_char, + style=self.padding_style, + ) + ) + if result: + result.pop() + + # Padding right. + if self.align in (HorizontalAlign.CENTER, HorizontalAlign.LEFT): + result.append(Window(width=Dimension(preferred=0))) + + return result + + return self._children_cache.get(tuple(self.children), get) + + def _divide_widths(self, width: int) -> Optional[List[int]]: + """ + Return the widths for all columns. + Or None when there is not enough space. + """ + children = self._all_children + + if not children: + return [] + + # Calculate widths. + dimensions = [c.preferred_width(width) for c in children] + preferred_dimensions = [d.preferred for d in dimensions] + + # Sum dimensions + sum_dimensions = sum_layout_dimensions(dimensions) + + # If there is not enough space for both. + # Don't do anything. + if sum_dimensions.min > width: + return None + + # Find optimal sizes. (Start with minimal size, increase until we cover + # the whole width.) + sizes = [d.min for d in dimensions] + + child_generator = take_using_weights( + items=list(range(len(dimensions))), weights=[d.weight for d in dimensions] + ) + + i = next(child_generator) + + # Increase until we meet at least the 'preferred' size. + preferred_stop = min(width, sum_dimensions.preferred) + + while sum(sizes) < preferred_stop: + if sizes[i] < preferred_dimensions[i]: + sizes[i] += 1 + i = next(child_generator) + + # Increase until we use all the available space. + max_dimensions = [d.max for d in dimensions] + max_stop = min(width, sum_dimensions.max) + + while sum(sizes) < max_stop: + if sizes[i] < max_dimensions[i]: + sizes[i] += 1 + i = next(child_generator) + + return sizes + + def write_to_screen( + self, + screen: Screen, + mouse_handlers: MouseHandlers, + write_position: WritePosition, + parent_style: str, + erase_bg: bool, + z_index: Optional[int], + ) -> None: + """ + Render the prompt to a `Screen` instance. + + :param screen: The :class:`~prompt_toolkit.layout.screen.Screen` class + to which the output has to be written. + """ + if not self.children: + return + + children = self._all_children + sizes = self._divide_widths(write_position.width) + style = parent_style + " " + to_str(self.style) + z_index = z_index if self.z_index is None else self.z_index + + # If there is not enough space. + if sizes is None: + self.window_too_small.write_to_screen( + screen, mouse_handlers, write_position, style, erase_bg, z_index + ) + return + + # Calculate heights, take the largest possible, but not larger than + # write_position.height. + heights = [ + child.preferred_height(width, write_position.height).preferred + for width, child in zip(sizes, children) + ] + height = max(write_position.height, min(write_position.height, max(heights))) + + # + ypos = write_position.ypos + xpos = write_position.xpos + + # Draw all child panes. + for s, c in zip(sizes, children): + c.write_to_screen( + screen, + mouse_handlers, + WritePosition(xpos, ypos, s, height), + style, + erase_bg, + z_index, + ) + xpos += s + + # Fill in the remaining space. This happens when a child control + # refuses to take more space and we don't have any padding. Adding a + # dummy child control for this (in `self._all_children`) is not + # desired, because in some situations, it would take more space, even + # when it's not required. This is required to apply the styling. + remaining_width = write_position.xpos + write_position.width - xpos + if remaining_width > 0: + self._remaining_space_window.write_to_screen( + screen, + mouse_handlers, + WritePosition(xpos, ypos, remaining_width, height), + style, + erase_bg, + z_index, + ) + + +class FloatContainer(Container): + """ + Container which can contain another container for the background, as well + as a list of floating containers on top of it. + + Example Usage:: + + FloatContainer(content=Window(...), + floats=[ + Float(xcursor=True, + ycursor=True, + layout=CompletionMenu(...)) + ]) + + :param z_index: (int or None) When specified, this can be used to bring + element in front of floating elements. `None` means: inherit from parent. + This is the z_index for the whole `Float` container as a whole. + """ + + def __init__( + self, + content: AnyContainer, + floats: List["Float"], + modal: bool = False, + key_bindings: Optional[KeyBindingsBase] = None, + style: Union[str, Callable[[], str]] = "", + z_index: Optional[int] = None, + ) -> None: + + self.content = to_container(content) + self.floats = floats + + self.modal = modal + self.key_bindings = key_bindings + self.style = style + self.z_index = z_index + + def reset(self) -> None: + self.content.reset() + + for f in self.floats: + f.content.reset() + + def preferred_width(self, max_available_width: int) -> Dimension: + return self.content.preferred_width(max_available_width) + + def preferred_height(self, width: int, max_available_height: int) -> Dimension: + """ + Return the preferred height of the float container. + (We don't care about the height of the floats, they should always fit + into the dimensions provided by the container.) + """ + return self.content.preferred_height(width, max_available_height) + + def write_to_screen( + self, + screen: Screen, + mouse_handlers: MouseHandlers, + write_position: WritePosition, + parent_style: str, + erase_bg: bool, + z_index: Optional[int], + ) -> None: + style = parent_style + " " + to_str(self.style) + z_index = z_index if self.z_index is None else self.z_index + + self.content.write_to_screen( + screen, mouse_handlers, write_position, style, erase_bg, z_index + ) + + for number, fl in enumerate(self.floats): + # z_index of a Float is computed by summing the z_index of the + # container and the `Float`. + new_z_index = (z_index or 0) + fl.z_index + style = parent_style + " " + to_str(self.style) + + # If the float that we have here, is positioned relative to the + # cursor position, but the Window that specifies the cursor + # position is not drawn yet, because it's a Float itself, we have + # to postpone this calculation. (This is a work-around, but good + # enough for now.) + postpone = fl.xcursor is not None or fl.ycursor is not None + + if postpone: + new_z_index = ( + number + 10 ** 8 + ) # Draw as late as possible, but keep the order. + screen.draw_with_z_index( + z_index=new_z_index, + draw_func=partial( + self._draw_float, + fl, + screen, + mouse_handlers, + write_position, + style, + erase_bg, + new_z_index, + ), + ) + else: + self._draw_float( + fl, + screen, + mouse_handlers, + write_position, + style, + erase_bg, + new_z_index, + ) + + def _draw_float( + self, + fl: "Float", + screen: Screen, + mouse_handlers: MouseHandlers, + write_position: WritePosition, + style: str, + erase_bg: bool, + z_index: Optional[int], + ) -> None: + " Draw a single Float. " + # When a menu_position was given, use this instead of the cursor + # position. (These cursor positions are absolute, translate again + # relative to the write_position.) + # Note: This should be inside the for-loop, because one float could + # set the cursor position to be used for the next one. + cpos = screen.get_menu_position( + fl.attach_to_window or get_app().layout.current_window + ) + cursor_position = Point( + x=cpos.x - write_position.xpos, y=cpos.y - write_position.ypos + ) + + fl_width = fl.get_width() + fl_height = fl.get_height() + width: int + height: int + xpos: int + ypos: int + + # Left & width given. + if fl.left is not None and fl_width is not None: + xpos = fl.left + width = fl_width + # Left & right given -> calculate width. + elif fl.left is not None and fl.right is not None: + xpos = fl.left + width = write_position.width - fl.left - fl.right + # Width & right given -> calculate left. + elif fl_width is not None and fl.right is not None: + xpos = write_position.width - fl.right - fl_width + width = fl_width + # Near x position of cursor. + elif fl.xcursor: + if fl_width is None: + width = fl.content.preferred_width(write_position.width).preferred + width = min(write_position.width, width) + else: + width = fl_width + + xpos = cursor_position.x + if xpos + width > write_position.width: + xpos = max(0, write_position.width - width) + # Only width given -> center horizontally. + elif fl_width: + xpos = int((write_position.width - fl_width) / 2) + width = fl_width + # Otherwise, take preferred width from float content. + else: + width = fl.content.preferred_width(write_position.width).preferred + + if fl.left is not None: + xpos = fl.left + elif fl.right is not None: + xpos = max(0, write_position.width - width - fl.right) + else: # Center horizontally. + xpos = max(0, int((write_position.width - width) / 2)) + + # Trim. + width = min(width, write_position.width - xpos) + + # Top & height given. + if fl.top is not None and fl_height is not None: + ypos = fl.top + height = fl_height + # Top & bottom given -> calculate height. + elif fl.top is not None and fl.bottom is not None: + ypos = fl.top + height = write_position.height - fl.top - fl.bottom + # Height & bottom given -> calculate top. + elif fl_height is not None and fl.bottom is not None: + ypos = write_position.height - fl_height - fl.bottom + height = fl_height + # Near cursor. + elif fl.ycursor: + ypos = cursor_position.y + (0 if fl.allow_cover_cursor else 1) + + if fl_height is None: + height = fl.content.preferred_height( + width, write_position.height + ).preferred + else: + height = fl_height + + # Reduce height if not enough space. (We can use the height + # when the content requires it.) + if height > write_position.height - ypos: + if write_position.height - ypos + 1 >= ypos: + # When the space below the cursor is more than + # the space above, just reduce the height. + height = write_position.height - ypos + else: + # Otherwise, fit the float above the cursor. + height = min(height, cursor_position.y) + ypos = cursor_position.y - height + + # Only height given -> center vertically. + elif fl_height: + ypos = int((write_position.height - fl_height) / 2) + height = fl_height + # Otherwise, take preferred height from content. + else: + height = fl.content.preferred_height(width, write_position.height).preferred + + if fl.top is not None: + ypos = fl.top + elif fl.bottom is not None: + ypos = max(0, write_position.height - height - fl.bottom) + else: # Center vertically. + ypos = max(0, int((write_position.height - height) / 2)) + + # Trim. + height = min(height, write_position.height - ypos) + + # Write float. + # (xpos and ypos can be negative: a float can be partially visible.) + if height > 0 and width > 0: + wp = WritePosition( + xpos=xpos + write_position.xpos, + ypos=ypos + write_position.ypos, + width=width, + height=height, + ) + + if not fl.hide_when_covering_content or self._area_is_empty(screen, wp): + fl.content.write_to_screen( + screen, + mouse_handlers, + wp, + style, + erase_bg=not fl.transparent(), + z_index=z_index, + ) + + def _area_is_empty(self, screen: Screen, write_position: WritePosition) -> bool: + """ + Return True when the area below the write position is still empty. + (For floats that should not hide content underneath.) + """ + wp = write_position + + for y in range(wp.ypos, wp.ypos + wp.height): + if y in screen.data_buffer: + row = screen.data_buffer[y] + + for x in range(wp.xpos, wp.xpos + wp.width): + c = row[x] + if c.char != " ": + return False + + return True + + def is_modal(self) -> bool: + return self.modal + + def get_key_bindings(self) -> Optional[KeyBindingsBase]: + return self.key_bindings + + def get_children(self) -> List[Container]: + children = [self.content] + children.extend(f.content for f in self.floats) + return children + + +class Float: + """ + Float for use in a :class:`.FloatContainer`. + Except for the `content` parameter, all other options are optional. + + :param content: :class:`.Container` instance. + + :param width: :class:`.Dimension` or callable which returns a :class:`.Dimension`. + :param height: :class:`.Dimension` or callable which returns a :class:`.Dimension`. + + :param left: Distance to the left edge of the :class:`.FloatContainer`. + :param right: Distance to the right edge of the :class:`.FloatContainer`. + :param top: Distance to the top of the :class:`.FloatContainer`. + :param bottom: Distance to the bottom of the :class:`.FloatContainer`. + + :param attach_to_window: Attach to the cursor from this window, instead of + the current window. + :param hide_when_covering_content: Hide the float when it covers content underneath. + :param allow_cover_cursor: When `False`, make sure to display the float + below the cursor. Not on top of the indicated position. + :param z_index: Z-index position. For a Float, this needs to be at least + one. It is relative to the z_index of the parent container. + :param transparent: :class:`.Filter` indicating whether this float needs to be + drawn transparently. + """ + + def __init__( + self, + content: AnyContainer, + top: Optional[int] = None, + right: Optional[int] = None, + bottom: Optional[int] = None, + left: Optional[int] = None, + width: Optional[Union[int, Callable[[], int]]] = None, + height: Optional[Union[int, Callable[[], int]]] = None, + xcursor: bool = False, + ycursor: bool = False, + attach_to_window: Optional[AnyContainer] = None, + hide_when_covering_content: bool = False, + allow_cover_cursor: bool = False, + z_index: int = 1, + transparent: bool = False, + ): + + assert z_index >= 1 + + self.left = left + self.right = right + self.top = top + self.bottom = bottom + + self.width = width + self.height = height + + self.xcursor = xcursor + self.ycursor = ycursor + + self.attach_to_window = ( + to_window(attach_to_window) if attach_to_window else None + ) + + self.content = to_container(content) + self.hide_when_covering_content = hide_when_covering_content + self.allow_cover_cursor = allow_cover_cursor + self.z_index = z_index + self.transparent = to_filter(transparent) + + def get_width(self) -> Optional[int]: + if callable(self.width): + return self.width() + return self.width + + def get_height(self) -> Optional[int]: + if callable(self.height): + return self.height() + return self.height + + def __repr__(self) -> str: + return "Float(content=%r)" % self.content + + +class WindowRenderInfo: + """ + Render information for the last render time of this control. + It stores mapping information between the input buffers (in case of a + :class:`~prompt_toolkit.layout.controls.BufferControl`) and the actual + render position on the output screen. + + (Could be used for implementation of the Vi 'H' and 'L' key bindings as + well as implementing mouse support.) + + :param ui_content: The original :class:`.UIContent` instance that contains + the whole input, without clipping. (ui_content) + :param horizontal_scroll: The horizontal scroll of the :class:`.Window` instance. + :param vertical_scroll: The vertical scroll of the :class:`.Window` instance. + :param window_width: The width of the window that displays the content, + without the margins. + :param window_height: The height of the window that displays the content. + :param configured_scroll_offsets: The scroll offsets as configured for the + :class:`Window` instance. + :param visible_line_to_row_col: Mapping that maps the row numbers on the + displayed screen (starting from zero for the first visible line) to + (row, col) tuples pointing to the row and column of the :class:`.UIContent`. + :param rowcol_to_yx: Mapping that maps (row, column) tuples representing + coordinates of the :class:`UIContent` to (y, x) absolute coordinates at + the rendered screen. + """ + + def __init__( + self, + window: "Window", + ui_content: UIContent, + horizontal_scroll: int, + vertical_scroll: int, + window_width: int, + window_height: int, + configured_scroll_offsets: "ScrollOffsets", + visible_line_to_row_col: Dict[int, Tuple[int, int]], + rowcol_to_yx: Dict[Tuple[int, int], Tuple[int, int]], + x_offset: int, + y_offset: int, + wrap_lines: bool, + ) -> None: + + self.window = window + self.ui_content = ui_content + self.vertical_scroll = vertical_scroll + self.window_width = window_width # Width without margins. + self.window_height = window_height + + self.configured_scroll_offsets = configured_scroll_offsets + self.visible_line_to_row_col = visible_line_to_row_col + self.wrap_lines = wrap_lines + + self._rowcol_to_yx = rowcol_to_yx # row/col from input to absolute y/x + # screen coordinates. + self._x_offset = x_offset + self._y_offset = y_offset + + @property + def visible_line_to_input_line(self) -> Dict[int, int]: + return { + visible_line: rowcol[0] + for visible_line, rowcol in self.visible_line_to_row_col.items() + } + + @property + def cursor_position(self) -> Point: + """ + Return the cursor position coordinates, relative to the left/top corner + of the rendered screen. + """ + cpos = self.ui_content.cursor_position + try: + y, x = self._rowcol_to_yx[cpos.y, cpos.x] + except KeyError: + # For `DummyControl` for instance, the content can be empty, and so + # will `_rowcol_to_yx` be. Return 0/0 by default. + return Point(x=0, y=0) + else: + return Point(x=x - self._x_offset, y=y - self._y_offset) + + @property + def applied_scroll_offsets(self) -> "ScrollOffsets": + """ + Return a :class:`.ScrollOffsets` instance that indicates the actual + offset. This can be less than or equal to what's configured. E.g, when + the cursor is completely at the top, the top offset will be zero rather + than what's configured. + """ + if self.displayed_lines[0] == 0: + top = 0 + else: + # Get row where the cursor is displayed. + y = self.input_line_to_visible_line[self.ui_content.cursor_position.y] + top = min(y, self.configured_scroll_offsets.top) + + return ScrollOffsets( + top=top, + bottom=min( + self.ui_content.line_count - self.displayed_lines[-1] - 1, + self.configured_scroll_offsets.bottom, + ), + # For left/right, it probably doesn't make sense to return something. + # (We would have to calculate the widths of all the lines and keep + # double width characters in mind.) + left=0, + right=0, + ) + + @property + def displayed_lines(self) -> List[int]: + """ + List of all the visible rows. (Line numbers of the input buffer.) + The last line may not be entirely visible. + """ + return sorted(row for row, col in self.visible_line_to_row_col.values()) + + @property + def input_line_to_visible_line(self) -> Dict[int, int]: + """ + Return the dictionary mapping the line numbers of the input buffer to + the lines of the screen. When a line spans several rows at the screen, + the first row appears in the dictionary. + """ + result: Dict[int, int] = {} + for k, v in self.visible_line_to_input_line.items(): + if v in result: + result[v] = min(result[v], k) + else: + result[v] = k + return result + + def first_visible_line(self, after_scroll_offset: bool = False) -> int: + """ + Return the line number (0 based) of the input document that corresponds + with the first visible line. + """ + if after_scroll_offset: + return self.displayed_lines[self.applied_scroll_offsets.top] + else: + return self.displayed_lines[0] + + def last_visible_line(self, before_scroll_offset: bool = False) -> int: + """ + Like `first_visible_line`, but for the last visible line. + """ + if before_scroll_offset: + return self.displayed_lines[-1 - self.applied_scroll_offsets.bottom] + else: + return self.displayed_lines[-1] + + def center_visible_line( + self, before_scroll_offset: bool = False, after_scroll_offset: bool = False + ) -> int: + """ + Like `first_visible_line`, but for the center visible line. + """ + return ( + self.first_visible_line(after_scroll_offset) + + ( + self.last_visible_line(before_scroll_offset) + - self.first_visible_line(after_scroll_offset) + ) + // 2 + ) + + @property + def content_height(self) -> int: + """ + The full height of the user control. + """ + return self.ui_content.line_count + + @property + def full_height_visible(self) -> bool: + """ + True when the full height is visible (There is no vertical scroll.) + """ + return ( + self.vertical_scroll == 0 + and self.last_visible_line() == self.content_height + ) + + @property + def top_visible(self) -> bool: + """ + True when the top of the buffer is visible. + """ + return self.vertical_scroll == 0 + + @property + def bottom_visible(self) -> bool: + """ + True when the bottom of the buffer is visible. + """ + return self.last_visible_line() == self.content_height - 1 + + @property + def vertical_scroll_percentage(self) -> int: + """ + Vertical scroll as a percentage. (0 means: the top is visible, + 100 means: the bottom is visible.) + """ + if self.bottom_visible: + return 100 + else: + return 100 * self.vertical_scroll // self.content_height + + def get_height_for_line(self, lineno: int) -> int: + """ + Return the height of the given line. + (The height that it would take, if this line became visible.) + """ + if self.wrap_lines: + return self.ui_content.get_height_for_line( + lineno, self.window_width, self.window.get_line_prefix + ) + else: + return 1 + + +class ScrollOffsets: + """ + Scroll offsets for the :class:`.Window` class. + + Note that left/right offsets only make sense if line wrapping is disabled. + """ + + def __init__( + self, + top: Union[int, Callable[[], int]] = 0, + bottom: Union[int, Callable[[], int]] = 0, + left: Union[int, Callable[[], int]] = 0, + right: Union[int, Callable[[], int]] = 0, + ) -> None: + + self._top = top + self._bottom = bottom + self._left = left + self._right = right + + @property + def top(self) -> int: + return to_int(self._top) + + @property + def bottom(self) -> int: + return to_int(self._bottom) + + @property + def left(self) -> int: + return to_int(self._left) + + @property + def right(self) -> int: + return to_int(self._right) + + def __repr__(self) -> str: + return "ScrollOffsets(top=%r, bottom=%r, left=%r, right=%r)" % ( + self._top, + self._bottom, + self._left, + self._right, + ) + + +class ColorColumn: + """ + Column for a :class:`.Window` to be colored. + """ + + def __init__(self, position: int, style: str = "class:color-column") -> None: + self.position = position + self.style = style + + +_in_insert_mode = vi_insert_mode | emacs_insert_mode + + +class WindowAlign(Enum): + """ + Alignment of the Window content. + + Note that this is different from `HorizontalAlign` and `VerticalAlign`, + which are used for the alignment of the child containers in respectively + `VSplit` and `HSplit`. + """ + + LEFT = "LEFT" + RIGHT = "RIGHT" + CENTER = "CENTER" + + +class Window(Container): + """ + Container that holds a control. + + :param content: :class:`.UIControl` instance. + :param width: :class:`.Dimension` instance or callable. + :param height: :class:`.Dimension` instance or callable. + :param z_index: When specified, this can be used to bring element in front + of floating elements. + :param dont_extend_width: When `True`, don't take up more width then the + preferred width reported by the control. + :param dont_extend_height: When `True`, don't take up more width then the + preferred height reported by the control. + :param ignore_content_width: A `bool` or :class:`.Filter` instance. Ignore + the :class:`.UIContent` width when calculating the dimensions. + :param ignore_content_height: A `bool` or :class:`.Filter` instance. Ignore + the :class:`.UIContent` height when calculating the dimensions. + :param left_margins: A list of :class:`.Margin` instance to be displayed on + the left. For instance: :class:`~prompt_toolkit.layout.NumberedMargin` + can be one of them in order to show line numbers. + :param right_margins: Like `left_margins`, but on the other side. + :param scroll_offsets: :class:`.ScrollOffsets` instance, representing the + preferred amount of lines/columns to be always visible before/after the + cursor. When both top and bottom are a very high number, the cursor + will be centered vertically most of the time. + :param allow_scroll_beyond_bottom: A `bool` or + :class:`.Filter` instance. When True, allow scrolling so far, that the + top part of the content is not visible anymore, while there is still + empty space available at the bottom of the window. In the Vi editor for + instance, this is possible. You will see tildes while the top part of + the body is hidden. + :param wrap_lines: A `bool` or :class:`.Filter` instance. When True, don't + scroll horizontally, but wrap lines instead. + :param get_vertical_scroll: Callable that takes this window + instance as input and returns a preferred vertical scroll. + (When this is `None`, the scroll is only determined by the last and + current cursor position.) + :param get_horizontal_scroll: Callable that takes this window + instance as input and returns a preferred vertical scroll. + :param always_hide_cursor: A `bool` or + :class:`.Filter` instance. When True, never display the cursor, even + when the user control specifies a cursor position. + :param cursorline: A `bool` or :class:`.Filter` instance. When True, + display a cursorline. + :param cursorcolumn: A `bool` or :class:`.Filter` instance. When True, + display a cursorcolumn. + :param colorcolumns: A list of :class:`.ColorColumn` instances that + describe the columns to be highlighted, or a callable that returns such + a list. + :param align: :class:`.WindowAlign` value or callable that returns an + :class:`.WindowAlign` value. alignment of content. + :param style: A style string. Style to be applied to all the cells in this + window. (This can be a callable that returns a string.) + :param char: (string) Character to be used for filling the background. This + can also be a callable that returns a character. + :param get_line_prefix: None or a callable that returns formatted text to + be inserted before a line. It takes a line number (int) and a + wrap_count and returns formatted text. This can be used for + implementation of line continuations, things like Vim "breakindent" and + so on. + """ + + def __init__( + self, + content: Optional[UIControl] = None, + width: AnyDimension = None, + height: AnyDimension = None, + z_index: Optional[int] = None, + dont_extend_width: FilterOrBool = False, + dont_extend_height: FilterOrBool = False, + ignore_content_width: FilterOrBool = False, + ignore_content_height: FilterOrBool = False, + left_margins: Optional[Sequence[Margin]] = None, + right_margins: Optional[Sequence[Margin]] = None, + scroll_offsets: Optional[ScrollOffsets] = None, + allow_scroll_beyond_bottom: FilterOrBool = False, + wrap_lines: FilterOrBool = False, + get_vertical_scroll: Optional[Callable[["Window"], int]] = None, + get_horizontal_scroll: Optional[Callable[["Window"], int]] = None, + always_hide_cursor: FilterOrBool = False, + cursorline: FilterOrBool = False, + cursorcolumn: FilterOrBool = False, + colorcolumns: Union[ + None, List[ColorColumn], Callable[[], List[ColorColumn]] + ] = None, + align: Union[WindowAlign, Callable[[], WindowAlign]] = WindowAlign.LEFT, + style: Union[str, Callable[[], str]] = "", + char: Union[None, str, Callable[[], str]] = None, + get_line_prefix: Optional[GetLinePrefixCallable] = None, + ) -> None: + + self.allow_scroll_beyond_bottom = to_filter(allow_scroll_beyond_bottom) + self.always_hide_cursor = to_filter(always_hide_cursor) + self.wrap_lines = to_filter(wrap_lines) + self.cursorline = to_filter(cursorline) + self.cursorcolumn = to_filter(cursorcolumn) + + self.content = content or DummyControl() + self.dont_extend_width = to_filter(dont_extend_width) + self.dont_extend_height = to_filter(dont_extend_height) + self.ignore_content_width = to_filter(ignore_content_width) + self.ignore_content_height = to_filter(ignore_content_height) + self.left_margins = left_margins or [] + self.right_margins = right_margins or [] + self.scroll_offsets = scroll_offsets or ScrollOffsets() + self.get_vertical_scroll = get_vertical_scroll + self.get_horizontal_scroll = get_horizontal_scroll + self.colorcolumns = colorcolumns or [] + self.align = align + self.style = style + self.char = char + self.get_line_prefix = get_line_prefix + + self.width = width + self.height = height + self.z_index = z_index + + # Cache for the screens generated by the margin. + self._ui_content_cache: SimpleCache[ + Tuple[int, int, int], UIContent + ] = SimpleCache(maxsize=8) + self._margin_width_cache: SimpleCache[Tuple[Margin, int], int] = SimpleCache( + maxsize=1 + ) + + self.reset() + + def __repr__(self) -> str: + return "Window(content=%r)" % self.content + + def reset(self) -> None: + self.content.reset() + + #: Scrolling position of the main content. + self.vertical_scroll = 0 + self.horizontal_scroll = 0 + + # Vertical scroll 2: this is the vertical offset that a line is + # scrolled if a single line (the one that contains the cursor) consumes + # all of the vertical space. + self.vertical_scroll_2 = 0 + + #: Keep render information (mappings between buffer input and render + #: output.) + self.render_info: Optional[WindowRenderInfo] = None + + def _get_margin_width(self, margin: Margin) -> int: + """ + Return the width for this margin. + (Calculate only once per render time.) + """ + # Margin.get_width, needs to have a UIContent instance. + def get_ui_content() -> UIContent: + return self._get_ui_content(width=0, height=0) + + def get_width() -> int: + return margin.get_width(get_ui_content) + + key = (margin, get_app().render_counter) + return self._margin_width_cache.get(key, get_width) + + def _get_total_margin_width(self) -> int: + """ + Calculate and return the width of the margin (left + right). + """ + return sum(self._get_margin_width(m) for m in self.left_margins) + sum( + self._get_margin_width(m) for m in self.right_margins + ) + + def preferred_width(self, max_available_width: int) -> Dimension: + """ + Calculate the preferred width for this window. + """ + + def preferred_content_width() -> Optional[int]: + """Content width: is only calculated if no exact width for the + window was given.""" + if self.ignore_content_width(): + return None + + # Calculate the width of the margin. + total_margin_width = self._get_total_margin_width() + + # Window of the content. (Can be `None`.) + preferred_width = self.content.preferred_width( + max_available_width - total_margin_width + ) + + if preferred_width is not None: + # Include width of the margins. + preferred_width += total_margin_width + return preferred_width + + # Merge. + return self._merge_dimensions( + dimension=to_dimension(self.width), + get_preferred=preferred_content_width, + dont_extend=self.dont_extend_width(), + ) + + def preferred_height(self, width: int, max_available_height: int) -> Dimension: + """ + Calculate the preferred height for this window. + """ + + def preferred_content_height() -> Optional[int]: + """Content height: is only calculated if no exact height for the + window was given.""" + if self.ignore_content_height(): + return None + + total_margin_width = self._get_total_margin_width() + wrap_lines = self.wrap_lines() + + return self.content.preferred_height( + width - total_margin_width, + max_available_height, + wrap_lines, + self.get_line_prefix, + ) + + return self._merge_dimensions( + dimension=to_dimension(self.height), + get_preferred=preferred_content_height, + dont_extend=self.dont_extend_height(), + ) + + @staticmethod + def _merge_dimensions( + dimension: Optional[Dimension], + get_preferred: Callable[[], Optional[int]], + dont_extend: bool = False, + ) -> Dimension: + """ + Take the Dimension from this `Window` class and the received preferred + size from the `UIControl` and return a `Dimension` to report to the + parent container. + """ + dimension = dimension or Dimension() + + # When a preferred dimension was explicitly given to the Window, + # ignore the UIControl. + preferred: Optional[int] + + if dimension.preferred_specified: + preferred = dimension.preferred + else: + # Otherwise, calculate the preferred dimension from the UI control + # content. + preferred = get_preferred() + + # When a 'preferred' dimension is given by the UIControl, make sure + # that it stays within the bounds of the Window. + if preferred is not None: + if dimension.max_specified: + preferred = min(preferred, dimension.max) + + if dimension.min_specified: + preferred = max(preferred, dimension.min) + + # When a `dont_extend` flag has been given, use the preferred dimension + # also as the max dimension. + max_: Optional[int] + min_: Optional[int] + + if dont_extend and preferred is not None: + max_ = min(dimension.max, preferred) + else: + max_ = dimension.max if dimension.max_specified else None + + min_ = dimension.min if dimension.min_specified else None + + return Dimension( + min=min_, max=max_, preferred=preferred, weight=dimension.weight + ) + + def _get_ui_content(self, width: int, height: int) -> UIContent: + """ + Create a `UIContent` instance. + """ + + def get_content() -> UIContent: + return self.content.create_content(width=width, height=height) + + key = (get_app().render_counter, width, height) + return self._ui_content_cache.get(key, get_content) + + def _get_digraph_char(self) -> Optional[str]: + " Return `False`, or the Digraph symbol to be used. " + app = get_app() + if app.quoted_insert: + return "^" + if app.vi_state.waiting_for_digraph: + if app.vi_state.digraph_symbol1: + return app.vi_state.digraph_symbol1 + return "?" + return None + + def write_to_screen( + self, + screen: Screen, + mouse_handlers: MouseHandlers, + write_position: WritePosition, + parent_style: str, + erase_bg: bool, + z_index: Optional[int], + ) -> None: + """ + Write window to screen. This renders the user control, the margins and + copies everything over to the absolute position at the given screen. + """ + z_index = z_index if self.z_index is None else self.z_index + + draw_func = partial( + self._write_to_screen_at_index, + screen, + mouse_handlers, + write_position, + parent_style, + erase_bg, + ) + + if z_index is None or z_index <= 0: + # When no z_index is given, draw right away. + draw_func() + else: + # Otherwise, postpone. + screen.draw_with_z_index(z_index=z_index, draw_func=draw_func) + + def _write_to_screen_at_index( + self, + screen: Screen, + mouse_handlers: MouseHandlers, + write_position: WritePosition, + parent_style: str, + erase_bg: bool, + ) -> None: + # Don't bother writing invisible windows. + # (We save some time, but also avoid applying last-line styling.) + if write_position.height <= 0 or write_position.width <= 0: + return + + # Calculate margin sizes. + left_margin_widths = [self._get_margin_width(m) for m in self.left_margins] + right_margin_widths = [self._get_margin_width(m) for m in self.right_margins] + total_margin_width = sum(left_margin_widths + right_margin_widths) + + # Render UserControl. + ui_content = self.content.create_content( + write_position.width - total_margin_width, write_position.height + ) + assert isinstance(ui_content, UIContent) + + # Scroll content. + wrap_lines = self.wrap_lines() + self._scroll( + ui_content, write_position.width - total_margin_width, write_position.height + ) + + # Erase background and fill with `char`. + self._fill_bg(screen, write_position, erase_bg) + + # Resolve `align` attribute. + align = self.align() if callable(self.align) else self.align + + # Write body + visible_line_to_row_col, rowcol_to_yx = self._copy_body( + ui_content, + screen, + write_position, + sum(left_margin_widths), + write_position.width - total_margin_width, + self.vertical_scroll, + self.horizontal_scroll, + wrap_lines=wrap_lines, + highlight_lines=True, + vertical_scroll_2=self.vertical_scroll_2, + always_hide_cursor=self.always_hide_cursor(), + has_focus=get_app().layout.current_control == self.content, + align=align, + get_line_prefix=self.get_line_prefix, + ) + + # Remember render info. (Set before generating the margins. They need this.) + x_offset = write_position.xpos + sum(left_margin_widths) + y_offset = write_position.ypos + + render_info = WindowRenderInfo( + window=self, + ui_content=ui_content, + horizontal_scroll=self.horizontal_scroll, + vertical_scroll=self.vertical_scroll, + window_width=write_position.width - total_margin_width, + window_height=write_position.height, + configured_scroll_offsets=self.scroll_offsets, + visible_line_to_row_col=visible_line_to_row_col, + rowcol_to_yx=rowcol_to_yx, + x_offset=x_offset, + y_offset=y_offset, + wrap_lines=wrap_lines, + ) + self.render_info = render_info + + # Set mouse handlers. + def mouse_handler(mouse_event: MouseEvent) -> None: + """Wrapper around the mouse_handler of the `UIControl` that turns + screen coordinates into line coordinates.""" + # Don't handle mouse events outside of the current modal part of + # the UI. + if self not in get_app().layout.walk_through_modal_area(): + return + + # Find row/col position first. + yx_to_rowcol = {v: k for k, v in rowcol_to_yx.items()} + y = mouse_event.position.y + x = mouse_event.position.x + + # If clicked below the content area, look for a position in the + # last line instead. + max_y = write_position.ypos + len(visible_line_to_row_col) - 1 + y = min(max_y, y) + result: NotImplementedOrNone + + while x >= 0: + try: + row, col = yx_to_rowcol[y, x] + except KeyError: + # Try again. (When clicking on the right side of double + # width characters, or on the right side of the input.) + x -= 1 + else: + # Found position, call handler of UIControl. + result = self.content.mouse_handler( + MouseEvent( + position=Point(x=col, y=row), + event_type=mouse_event.event_type, + ) + ) + break + else: + # nobreak. + # (No x/y coordinate found for the content. This happens in + # case of a DummyControl, that does not have any content. + # Report (0,0) instead.) + result = self.content.mouse_handler( + MouseEvent( + position=Point(x=0, y=0), event_type=mouse_event.event_type + ) + ) + + # If it returns NotImplemented, handle it here. + if result == NotImplemented: + self._mouse_handler(mouse_event) + + mouse_handlers.set_mouse_handler_for_range( + x_min=write_position.xpos + sum(left_margin_widths), + x_max=write_position.xpos + write_position.width - total_margin_width, + y_min=write_position.ypos, + y_max=write_position.ypos + write_position.height, + handler=mouse_handler, + ) + + # Render and copy margins. + move_x = 0 + + def render_margin(m: Margin, width: int) -> UIContent: + " Render margin. Return `Screen`. " + # Retrieve margin fragments. + fragments = m.create_margin(render_info, width, write_position.height) + + # Turn it into a UIContent object. + # already rendered those fragments using this size.) + return FormattedTextControl(fragments).create_content( + width + 1, write_position.height + ) + + for m, width in zip(self.left_margins, left_margin_widths): + if width > 0: # (ConditionalMargin returns a zero width. -- Don't render.) + # Create screen for margin. + margin_content = render_margin(m, width) + + # Copy and shift X. + self._copy_margin(margin_content, screen, write_position, move_x, width) + move_x += width + + move_x = write_position.width - sum(right_margin_widths) + + for m, width in zip(self.right_margins, right_margin_widths): + # Create screen for margin. + margin_content = render_margin(m, width) + + # Copy and shift X. + self._copy_margin(margin_content, screen, write_position, move_x, width) + move_x += width + + # Apply 'self.style' + self._apply_style(screen, write_position, parent_style) + + # Tell the screen that this user control has been painted. + screen.visible_windows.append(self) + + def _copy_body( + self, + ui_content: UIContent, + new_screen: Screen, + write_position: WritePosition, + move_x: int, + width: int, + vertical_scroll: int = 0, + horizontal_scroll: int = 0, + wrap_lines: bool = False, + highlight_lines: bool = False, + vertical_scroll_2: int = 0, + always_hide_cursor: bool = False, + has_focus: bool = False, + align: WindowAlign = WindowAlign.LEFT, + get_line_prefix: Optional[Callable[[int, int], AnyFormattedText]] = None, + ) -> Tuple[Dict[int, Tuple[int, int]], Dict[Tuple[int, int], Tuple[int, int]]]: + """ + Copy the UIContent into the output screen. + Return (visible_line_to_row_col, rowcol_to_yx) tuple. + + :param get_line_prefix: None or a callable that takes a line number + (int) and a wrap_count (int) and returns formatted text. + """ + xpos = write_position.xpos + move_x + ypos = write_position.ypos + line_count = ui_content.line_count + new_buffer = new_screen.data_buffer + empty_char = _CHAR_CACHE["", ""] + + # Map visible line number to (row, col) of input. + # 'col' will always be zero if line wrapping is off. + visible_line_to_row_col: Dict[int, Tuple[int, int]] = {} + + # Maps (row, col) from the input to (y, x) screen coordinates. + rowcol_to_yx: Dict[Tuple[int, int], Tuple[int, int]] = {} + + def copy_line( + line: StyleAndTextTuples, + lineno: int, + x: int, + y: int, + is_input: bool = False, + ) -> Tuple[int, int]: + """ + Copy over a single line to the output screen. This can wrap over + multiple lines in the output. It will call the prefix (prompt) + function before every line. + """ + if is_input: + current_rowcol_to_yx = rowcol_to_yx + else: + current_rowcol_to_yx = {} # Throwaway dictionary. + + # Draw line prefix. + if is_input and get_line_prefix: + prompt = to_formatted_text(get_line_prefix(lineno, 0)) + x, y = copy_line(prompt, lineno, x, y, is_input=False) + + # Scroll horizontally. + skipped = 0 # Characters skipped because of horizontal scrolling. + if horizontal_scroll and is_input: + h_scroll = horizontal_scroll + line = explode_text_fragments(line) + while h_scroll > 0 and line: + h_scroll -= get_cwidth(line[0][1]) + skipped += 1 + del line[:1] # Remove first character. + + x -= h_scroll # When scrolling over double width character, + # this can end up being negative. + + # Align this line. (Note that this doesn't work well when we use + # get_line_prefix and that function returns variable width prefixes.) + if align == WindowAlign.CENTER: + line_width = fragment_list_width(line) + if line_width < width: + x += (width - line_width) // 2 + elif align == WindowAlign.RIGHT: + line_width = fragment_list_width(line) + if line_width < width: + x += width - line_width + + col = 0 + wrap_count = 0 + for style, text, *_ in line: + new_buffer_row = new_buffer[y + ypos] + + # Remember raw VT escape sequences. (E.g. FinalTerm's + # escape sequences.) + if "[ZeroWidthEscape]" in style: + new_screen.zero_width_escapes[y + ypos][x + xpos] += text + continue + + for c in text: + char = _CHAR_CACHE[c, style] + char_width = char.width + + # Wrap when the line width is exceeded. + if wrap_lines and x + char_width > width: + visible_line_to_row_col[y + 1] = ( + lineno, + visible_line_to_row_col[y][1] + x, + ) + y += 1 + wrap_count += 1 + x = 0 + + # Insert line prefix (continuation prompt). + if is_input and get_line_prefix: + prompt = to_formatted_text( + get_line_prefix(lineno, wrap_count) + ) + x, y = copy_line(prompt, lineno, x, y, is_input=False) + + new_buffer_row = new_buffer[y + ypos] + + if y >= write_position.height: + return x, y # Break out of all for loops. + + # Set character in screen and shift 'x'. + if x >= 0 and y >= 0 and x < write_position.width: + new_buffer_row[x + xpos] = char + + # When we print a multi width character, make sure + # to erase the neighbours positions in the screen. + # (The empty string if different from everything, + # so next redraw this cell will repaint anyway.) + if char_width > 1: + for i in range(1, char_width): + new_buffer_row[x + xpos + i] = empty_char + + # If this is a zero width characters, then it's + # probably part of a decomposed unicode character. + # See: https://en.wikipedia.org/wiki/Unicode_equivalence + # Merge it in the previous cell. + elif char_width == 0: + # Handle all character widths. If the previous + # character is a multiwidth character, then + # merge it two positions back. + for pw in [2, 1]: # Previous character width. + if ( + x - pw >= 0 + and new_buffer_row[x + xpos - pw].width == pw + ): + prev_char = new_buffer_row[x + xpos - pw] + char2 = _CHAR_CACHE[ + prev_char.char + c, prev_char.style + ] + new_buffer_row[x + xpos - pw] = char2 + + # Keep track of write position for each character. + current_rowcol_to_yx[lineno, col + skipped] = ( + y + ypos, + x + xpos, + ) + + col += 1 + x += char_width + return x, y + + # Copy content. + def copy() -> int: + y = -vertical_scroll_2 + lineno = vertical_scroll + + while y < write_position.height and lineno < line_count: + # Take the next line and copy it in the real screen. + line = ui_content.get_line(lineno) + + visible_line_to_row_col[y] = (lineno, horizontal_scroll) + + # Copy margin and actual line. + x = 0 + x, y = copy_line(line, lineno, x, y, is_input=True) + + lineno += 1 + y += 1 + return y + + copy() + + def cursor_pos_to_screen_pos(row: int, col: int) -> Point: + " Translate row/col from UIContent to real Screen coordinates. " + try: + y, x = rowcol_to_yx[row, col] + except KeyError: + # Normally this should never happen. (It is a bug, if it happens.) + # But to be sure, return (0, 0) + return Point(x=0, y=0) + + # raise ValueError( + # 'Invalid position. row=%r col=%r, vertical_scroll=%r, ' + # 'horizontal_scroll=%r, height=%r' % + # (row, col, vertical_scroll, horizontal_scroll, write_position.height)) + else: + return Point(x=x, y=y) + + # Set cursor and menu positions. + if ui_content.cursor_position: + screen_cursor_position = cursor_pos_to_screen_pos( + ui_content.cursor_position.y, ui_content.cursor_position.x + ) + + if has_focus: + new_screen.set_cursor_position(self, screen_cursor_position) + + if always_hide_cursor: + new_screen.show_cursor = False + else: + new_screen.show_cursor = ui_content.show_cursor + + self._highlight_digraph(new_screen) + + if highlight_lines: + self._highlight_cursorlines( + new_screen, + screen_cursor_position, + xpos, + ypos, + width, + write_position.height, + ) + + # Draw input characters from the input processor queue. + if has_focus and ui_content.cursor_position: + self._show_key_processor_key_buffer(new_screen) + + # Set menu position. + if ui_content.menu_position: + new_screen.set_menu_position( + self, + cursor_pos_to_screen_pos( + ui_content.menu_position.y, ui_content.menu_position.x + ), + ) + + # Update output screen height. + new_screen.height = max(new_screen.height, ypos + write_position.height) + + return visible_line_to_row_col, rowcol_to_yx + + def _fill_bg( + self, screen: Screen, write_position: WritePosition, erase_bg: bool + ) -> None: + """ + Erase/fill the background. + (Useful for floats and when a `char` has been given.) + """ + char: Optional[str] + if callable(self.char): + char = self.char() + else: + char = self.char + + if erase_bg or char: + wp = write_position + char_obj = _CHAR_CACHE[char or " ", ""] + + for y in range(wp.ypos, wp.ypos + wp.height): + row = screen.data_buffer[y] + for x in range(wp.xpos, wp.xpos + wp.width): + row[x] = char_obj + + def _apply_style( + self, new_screen: Screen, write_position: WritePosition, parent_style: str + ) -> None: + + # Apply `self.style`. + style = parent_style + " " + to_str(self.style) + + new_screen.fill_area(write_position, style=style, after=False) + + # Apply the 'last-line' class to the last line of each Window. This can + # be used to apply an 'underline' to the user control. + wp = WritePosition( + write_position.xpos, + write_position.ypos + write_position.height - 1, + write_position.width, + 1, + ) + new_screen.fill_area(wp, "class:last-line", after=True) + + def _highlight_digraph(self, new_screen: Screen) -> None: + """ + When we are in Vi digraph mode, put a question mark underneath the + cursor. + """ + digraph_char = self._get_digraph_char() + if digraph_char: + cpos = new_screen.get_cursor_position(self) + new_screen.data_buffer[cpos.y][cpos.x] = _CHAR_CACHE[ + digraph_char, "class:digraph" + ] + + def _show_key_processor_key_buffer(self, new_screen: Screen) -> None: + """ + When the user is typing a key binding that consists of several keys, + display the last pressed key if the user is in insert mode and the key + is meaningful to be displayed. + E.g. Some people want to bind 'jj' to escape in Vi insert mode. But the + first 'j' needs to be displayed in order to get some feedback. + """ + app = get_app() + key_buffer = app.key_processor.key_buffer + + if key_buffer and _in_insert_mode() and not app.is_done: + # The textual data for the given key. (Can be a VT100 escape + # sequence.) + data = key_buffer[-1].data + + # Display only if this is a 1 cell width character. + if get_cwidth(data) == 1: + cpos = new_screen.get_cursor_position(self) + new_screen.data_buffer[cpos.y][cpos.x] = _CHAR_CACHE[ + data, "class:partial-key-binding" + ] + + def _highlight_cursorlines( + self, new_screen: Screen, cpos: Point, x: int, y: int, width: int, height: int + ) -> None: + """ + Highlight cursor row/column. + """ + cursor_line_style = " class:cursor-line " + cursor_column_style = " class:cursor-column " + + data_buffer = new_screen.data_buffer + + # Highlight cursor line. + if self.cursorline(): + row = data_buffer[cpos.y] + for x in range(x, x + width): + original_char = row[x] + row[x] = _CHAR_CACHE[ + original_char.char, original_char.style + cursor_line_style + ] + + # Highlight cursor column. + if self.cursorcolumn(): + for y2 in range(y, y + height): + row = data_buffer[y2] + original_char = row[cpos.x] + row[cpos.x] = _CHAR_CACHE[ + original_char.char, original_char.style + cursor_column_style + ] + + # Highlight color columns + colorcolumns = self.colorcolumns + if callable(colorcolumns): + colorcolumns = colorcolumns() + + for cc in colorcolumns: + assert isinstance(cc, ColorColumn) + column = cc.position + + if column < x + width: # Only draw when visible. + color_column_style = " " + cc.style + + for y2 in range(y, y + height): + row = data_buffer[y2] + original_char = row[column + x] + row[column + x] = _CHAR_CACHE[ + original_char.char, original_char.style + color_column_style + ] + + def _copy_margin( + self, + margin_content: UIContent, + new_screen: Screen, + write_position: WritePosition, + move_x: int, + width: int, + ) -> None: + """ + Copy characters from the margin screen to the real screen. + """ + xpos = write_position.xpos + move_x + ypos = write_position.ypos + + margin_write_position = WritePosition(xpos, ypos, width, write_position.height) + self._copy_body(margin_content, new_screen, margin_write_position, 0, width) + + def _scroll(self, ui_content: UIContent, width: int, height: int) -> None: + """ + Scroll body. Ensure that the cursor is visible. + """ + if self.wrap_lines(): + func = self._scroll_when_linewrapping + else: + func = self._scroll_without_linewrapping + + func(ui_content, width, height) + + def _scroll_when_linewrapping( + self, ui_content: UIContent, width: int, height: int + ) -> None: + """ + Scroll to make sure the cursor position is visible and that we maintain + the requested scroll offset. + + Set `self.horizontal_scroll/vertical_scroll`. + """ + scroll_offsets_bottom = self.scroll_offsets.bottom + scroll_offsets_top = self.scroll_offsets.top + + # We don't have horizontal scrolling. + self.horizontal_scroll = 0 + + def get_line_height(lineno: int) -> int: + return ui_content.get_height_for_line(lineno, width, self.get_line_prefix) + + # When there is no space, reset `vertical_scroll_2` to zero and abort. + # This can happen if the margin is bigger than the window width. + # Otherwise the text height will become "infinite" (a big number) and + # the copy_line will spend a huge amount of iterations trying to render + # nothing. + if width <= 0: + self.vertical_scroll = ui_content.cursor_position.y + self.vertical_scroll_2 = 0 + return + + # If the current line consumes more than the whole window height, + # then we have to scroll vertically inside this line. (We don't take + # the scroll offsets into account for this.) + # Also, ignore the scroll offsets in this case. Just set the vertical + # scroll to this line. + line_height = get_line_height(ui_content.cursor_position.y) + if line_height > height - scroll_offsets_top: + # Calculate the height of the text before the cursor (including + # line prefixes). + text_before_height = ui_content.get_height_for_line( + ui_content.cursor_position.y, + width, + self.get_line_prefix, + slice_stop=ui_content.cursor_position.x, + ) + + # Adjust scroll offset. + self.vertical_scroll = ui_content.cursor_position.y + self.vertical_scroll_2 = min( + text_before_height - 1, # Keep the cursor visible. + line_height + - height, # Avoid blank lines at the bottom when scolling up again. + self.vertical_scroll_2, + ) + self.vertical_scroll_2 = max( + 0, text_before_height - height, self.vertical_scroll_2 + ) + return + else: + self.vertical_scroll_2 = 0 + + # Current line doesn't consume the whole height. Take scroll offsets into account. + def get_min_vertical_scroll() -> int: + # Make sure that the cursor line is not below the bottom. + # (Calculate how many lines can be shown between the cursor and the .) + used_height = 0 + prev_lineno = ui_content.cursor_position.y + + for lineno in range(ui_content.cursor_position.y, -1, -1): + used_height += get_line_height(lineno) + + if used_height > height - scroll_offsets_bottom: + return prev_lineno + else: + prev_lineno = lineno + return 0 + + def get_max_vertical_scroll() -> int: + # Make sure that the cursor line is not above the top. + prev_lineno = ui_content.cursor_position.y + used_height = 0 + + for lineno in range(ui_content.cursor_position.y - 1, -1, -1): + used_height += get_line_height(lineno) + + if used_height > scroll_offsets_top: + return prev_lineno + else: + prev_lineno = lineno + return prev_lineno + + def get_topmost_visible() -> int: + """ + Calculate the upper most line that can be visible, while the bottom + is still visible. We should not allow scroll more than this if + `allow_scroll_beyond_bottom` is false. + """ + prev_lineno = ui_content.line_count - 1 + used_height = 0 + for lineno in range(ui_content.line_count - 1, -1, -1): + used_height += get_line_height(lineno) + if used_height > height: + return prev_lineno + else: + prev_lineno = lineno + return prev_lineno + + # Scroll vertically. (Make sure that the whole line which contains the + # cursor is visible. + topmost_visible = get_topmost_visible() + + # Note: the `min(topmost_visible, ...)` is to make sure that we + # don't require scrolling up because of the bottom scroll offset, + # when we are at the end of the document. + self.vertical_scroll = max( + self.vertical_scroll, min(topmost_visible, get_min_vertical_scroll()) + ) + self.vertical_scroll = min(self.vertical_scroll, get_max_vertical_scroll()) + + # Disallow scrolling beyond bottom? + if not self.allow_scroll_beyond_bottom(): + self.vertical_scroll = min(self.vertical_scroll, topmost_visible) + + def _scroll_without_linewrapping( + self, ui_content: UIContent, width: int, height: int + ) -> None: + """ + Scroll to make sure the cursor position is visible and that we maintain + the requested scroll offset. + + Set `self.horizontal_scroll/vertical_scroll`. + """ + cursor_position = ui_content.cursor_position or Point(x=0, y=0) + + # Without line wrapping, we will never have to scroll vertically inside + # a single line. + self.vertical_scroll_2 = 0 + + if ui_content.line_count == 0: + self.vertical_scroll = 0 + self.horizontal_scroll = 0 + return + else: + current_line_text = fragment_list_to_text( + ui_content.get_line(cursor_position.y) + ) + + def do_scroll( + current_scroll: int, + scroll_offset_start: int, + scroll_offset_end: int, + cursor_pos: int, + window_size: int, + content_size: int, + ) -> int: + " Scrolling algorithm. Used for both horizontal and vertical scrolling. " + # Calculate the scroll offset to apply. + # This can obviously never be more than have the screen size. Also, when the + # cursor appears at the top or bottom, we don't apply the offset. + scroll_offset_start = int( + min(scroll_offset_start, window_size / 2, cursor_pos) + ) + scroll_offset_end = int( + min(scroll_offset_end, window_size / 2, content_size - 1 - cursor_pos) + ) + + # Prevent negative scroll offsets. + if current_scroll < 0: + current_scroll = 0 + + # Scroll back if we scrolled to much and there's still space to show more of the document. + if ( + not self.allow_scroll_beyond_bottom() + and current_scroll > content_size - window_size + ): + current_scroll = max(0, content_size - window_size) + + # Scroll up if cursor is before visible part. + if current_scroll > cursor_pos - scroll_offset_start: + current_scroll = max(0, cursor_pos - scroll_offset_start) + + # Scroll down if cursor is after visible part. + if current_scroll < (cursor_pos + 1) - window_size + scroll_offset_end: + current_scroll = (cursor_pos + 1) - window_size + scroll_offset_end + + return current_scroll + + # When a preferred scroll is given, take that first into account. + if self.get_vertical_scroll: + self.vertical_scroll = self.get_vertical_scroll(self) + assert isinstance(self.vertical_scroll, int) + if self.get_horizontal_scroll: + self.horizontal_scroll = self.get_horizontal_scroll(self) + assert isinstance(self.horizontal_scroll, int) + + # Update horizontal/vertical scroll to make sure that the cursor + # remains visible. + offsets = self.scroll_offsets + + self.vertical_scroll = do_scroll( + current_scroll=self.vertical_scroll, + scroll_offset_start=offsets.top, + scroll_offset_end=offsets.bottom, + cursor_pos=ui_content.cursor_position.y, + window_size=height, + content_size=ui_content.line_count, + ) + + if self.get_line_prefix: + current_line_prefix_width = fragment_list_width( + to_formatted_text(self.get_line_prefix(ui_content.cursor_position.y, 0)) + ) + else: + current_line_prefix_width = 0 + + self.horizontal_scroll = do_scroll( + current_scroll=self.horizontal_scroll, + scroll_offset_start=offsets.left, + scroll_offset_end=offsets.right, + cursor_pos=get_cwidth(current_line_text[: ui_content.cursor_position.x]), + window_size=width - current_line_prefix_width, + # We can only analyse the current line. Calculating the width off + # all the lines is too expensive. + content_size=max( + get_cwidth(current_line_text), self.horizontal_scroll + width + ), + ) + + def _mouse_handler(self, mouse_event: MouseEvent) -> None: + """ + Mouse handler. Called when the UI control doesn't handle this + particular event. + """ + if mouse_event.event_type == MouseEventType.SCROLL_DOWN: + self._scroll_down() + elif mouse_event.event_type == MouseEventType.SCROLL_UP: + self._scroll_up() + + def _scroll_down(self) -> None: + " Scroll window down. " + info = self.render_info + + if info is None: + return + + if self.vertical_scroll < info.content_height - info.window_height: + if info.cursor_position.y <= info.configured_scroll_offsets.top: + self.content.move_cursor_down() + + self.vertical_scroll += 1 + + def _scroll_up(self) -> None: + " Scroll window up. " + info = self.render_info + + if info is None: + return + + if info.vertical_scroll > 0: + # TODO: not entirely correct yet in case of line wrapping and long lines. + if ( + info.cursor_position.y + >= info.window_height - 1 - info.configured_scroll_offsets.bottom + ): + self.content.move_cursor_up() + + self.vertical_scroll -= 1 + + def get_key_bindings(self) -> Optional[KeyBindingsBase]: + return self.content.get_key_bindings() + + def get_children(self) -> List[Container]: + return [] + + +class ConditionalContainer(Container): + """ + Wrapper around any other container that can change the visibility. The + received `filter` determines whether the given container should be + displayed or not. + + :param content: :class:`.Container` instance. + :param filter: :class:`.Filter` instance. + """ + + def __init__(self, content: AnyContainer, filter: FilterOrBool) -> None: + self.content = to_container(content) + self.filter = to_filter(filter) + + def __repr__(self) -> str: + return "ConditionalContainer(%r, filter=%r)" % (self.content, self.filter) + + def reset(self) -> None: + self.content.reset() + + def preferred_width(self, max_available_width: int) -> Dimension: + if self.filter(): + return self.content.preferred_width(max_available_width) + else: + return Dimension.zero() + + def preferred_height(self, width: int, max_available_height: int) -> Dimension: + if self.filter(): + return self.content.preferred_height(width, max_available_height) + else: + return Dimension.zero() + + def write_to_screen( + self, + screen: Screen, + mouse_handlers: MouseHandlers, + write_position: WritePosition, + parent_style: str, + erase_bg: bool, + z_index: Optional[int], + ) -> None: + if self.filter(): + return self.content.write_to_screen( + screen, mouse_handlers, write_position, parent_style, erase_bg, z_index + ) + + def get_children(self) -> List[Container]: + return [self.content] + + +class DynamicContainer(Container): + """ + Container class that dynamically returns any Container. + + :param get_container: Callable that returns a :class:`.Container` instance + or any widget with a ``__pt_container__`` method. + """ + + def __init__(self, get_container: Callable[[], AnyContainer]) -> None: + self.get_container = get_container + + def _get_container(self) -> Container: + """ + Return the current container object. + + We call `to_container`, because `get_container` can also return a + widget with a ``__pt_container__`` method. + """ + obj = self.get_container() + return to_container(obj) + + def reset(self) -> None: + self._get_container().reset() + + def preferred_width(self, max_available_width: int) -> Dimension: + return self._get_container().preferred_width(max_available_width) + + def preferred_height(self, width: int, max_available_height: int) -> Dimension: + return self._get_container().preferred_height(width, max_available_height) + + def write_to_screen(self, *a, **kw): + self._get_container().write_to_screen(*a, **kw) + + def is_modal(self) -> bool: + return False + + def get_key_bindings(self) -> Optional[KeyBindingsBase]: + # Key bindings will be collected when `layout.walk()` finds the child + # container. + return None + + def get_children(self) -> List[Container]: + # Here we have to return the current active container itself, not its + # children. Otherwise, we run into issues where `layout.walk()` will + # never see an object of type `Window` if this contains a window. We + # can't/shouldn't proxy the "isinstance" check. + return [self._get_container()] + + +def to_container(container: AnyContainer) -> Container: + """ + Make sure that the given object is a :class:`.Container`. + """ + if isinstance(container, Container): + return container + elif hasattr(container, "__pt_container__"): + return to_container(cast("MagicContainer", container).__pt_container__()) + else: + raise ValueError("Not a container object: %r" % (container,)) + + +def to_window(container: AnyContainer) -> Window: + """ + Make sure that the given argument is a :class:`.Window`. + """ + if isinstance(container, Window): + return container + elif hasattr(container, "__pt_container__"): + return to_window(cast("MagicContainer", container).__pt_container__()) + else: + raise ValueError("Not a Window object: %r." % (container,)) + + +def is_container(value: object) -> bool: + """ + Checks whether the given value is a container object + (for use in assert statements). + """ + if isinstance(value, Container): + return True + if hasattr(value, "__pt_container__"): + return is_container(cast("MagicContainer", value).__pt_container__()) + return False diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/controls.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/controls.py new file mode 100644 index 00000000..f2c4a213 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/controls.py @@ -0,0 +1,947 @@ +""" +User interface Controls for the layout. +""" +import time +from abc import ABCMeta, abstractmethod +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + Hashable, + Iterable, + List, + NamedTuple, + Optional, + Union, +) + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.cache import SimpleCache +from prompt_toolkit.data_structures import Point +from prompt_toolkit.document import Document +from prompt_toolkit.filters import FilterOrBool, to_filter +from prompt_toolkit.formatted_text import ( + AnyFormattedText, + StyleAndTextTuples, + to_formatted_text, +) +from prompt_toolkit.formatted_text.utils import ( + fragment_list_to_text, + fragment_list_width, + split_lines, +) +from prompt_toolkit.lexers import Lexer, SimpleLexer +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.search import SearchState +from prompt_toolkit.selection import SelectionType +from prompt_toolkit.utils import get_cwidth + +from .processors import ( + DisplayMultipleCursors, + HighlightIncrementalSearchProcessor, + HighlightSearchProcessor, + HighlightSelectionProcessor, + Processor, + TransformationInput, + merge_processors, +) + +if TYPE_CHECKING: + from prompt_toolkit.key_binding.key_bindings import KeyBindingsBase + from prompt_toolkit.utils import Event + + # The only two return values for a mouse hander are `None` and + # `NotImplemented`. For the type checker it's best to annotate this as + # `object`. (The consumer never expects a more specific instance: checking + # for NotImplemented can be done using `is NotImplemented`.) + NotImplementedOrNone = object + # Other non-working options are: + # * Optional[Literal[NotImplemented]] + # --> Doesn't work, Literal can't take an Any. + # * None + # --> Doesn't work. We can't assign the result of a function that + # returns `None` to a variable. + # * Any + # --> Works, but too broad. + + +__all__ = [ + "BufferControl", + "SearchBufferControl", + "DummyControl", + "FormattedTextControl", + "UIControl", + "UIContent", +] + +GetLinePrefixCallable = Callable[[int, int], AnyFormattedText] + + +class UIControl(metaclass=ABCMeta): + """ + Base class for all user interface controls. + """ + + def reset(self) -> None: + # Default reset. (Doesn't have to be implemented.) + pass + + def preferred_width(self, max_available_width: int) -> Optional[int]: + return None + + def preferred_height( + self, + width: int, + max_available_height: int, + wrap_lines: bool, + get_line_prefix: Optional[GetLinePrefixCallable], + ) -> Optional[int]: + return None + + def is_focusable(self) -> bool: + """ + Tell whether this user control is focusable. + """ + return False + + @abstractmethod + def create_content(self, width: int, height: int) -> "UIContent": + """ + Generate the content for this user control. + + Returns a :class:`.UIContent` instance. + """ + + def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone": + """ + Handle mouse events. + + When `NotImplemented` is returned, it means that the given event is not + handled by the `UIControl` itself. The `Window` or key bindings can + decide to handle this event as scrolling or changing focus. + + :param mouse_event: `MouseEvent` instance. + """ + return NotImplemented + + def move_cursor_down(self) -> None: + """ + Request to move the cursor down. + This happens when scrolling down and the cursor is completely at the + top. + """ + + def move_cursor_up(self) -> None: + """ + Request to move the cursor up. + """ + + def get_key_bindings(self) -> Optional["KeyBindingsBase"]: + """ + The key bindings that are specific for this user control. + + Return a :class:`.KeyBindings` object if some key bindings are + specified, or `None` otherwise. + """ + + def get_invalidate_events(self) -> Iterable["Event[object]"]: + """ + Return a list of `Event` objects. This can be a generator. + (The application collects all these events, in order to bind redraw + handlers to these events.) + """ + return [] + + +class UIContent: + """ + Content generated by a user control. This content consists of a list of + lines. + + :param get_line: Callable that takes a line number and returns the current + line. This is a list of (style_str, text) tuples. + :param line_count: The number of lines. + :param cursor_position: a :class:`.Point` for the cursor position. + :param menu_position: a :class:`.Point` for the menu position. + :param show_cursor: Make the cursor visible. + """ + + def __init__( + self, + get_line: Callable[[int], StyleAndTextTuples] = (lambda i: []), + line_count: int = 0, + cursor_position: Optional[Point] = None, + menu_position: Optional[Point] = None, + show_cursor: bool = True, + ): + + self.get_line = get_line + self.line_count = line_count + self.cursor_position = cursor_position or Point(x=0, y=0) + self.menu_position = menu_position + self.show_cursor = show_cursor + + # Cache for line heights. Maps cache key -> height + self._line_heights_cache: Dict[Hashable, int] = {} + + def __getitem__(self, lineno: int) -> StyleAndTextTuples: + " Make it iterable (iterate line by line). " + if lineno < self.line_count: + return self.get_line(lineno) + else: + raise IndexError + + def get_height_for_line( + self, + lineno: int, + width: int, + get_line_prefix: Optional[GetLinePrefixCallable], + slice_stop: Optional[int] = None, + ) -> int: + """ + Return the height that a given line would need if it is rendered in a + space with the given width (using line wrapping). + + :param get_line_prefix: None or a `Window.get_line_prefix` callable + that returns the prefix to be inserted before this line. + :param slice_stop: Wrap only "line[:slice_stop]" and return that + partial result. This is needed for scrolling the window correctly + when line wrapping. + :returns: The computed height. + """ + # Instead of using `get_line_prefix` as key, we use render_counter + # instead. This is more reliable, because this function could still be + # the same, while the content would change over time. + key = get_app().render_counter, lineno, width, slice_stop + + try: + return self._line_heights_cache[key] + except KeyError: + if width == 0: + height = 10 ** 8 + else: + # Calculate line width first. + line = fragment_list_to_text(self.get_line(lineno))[:slice_stop] + text_width = get_cwidth(line) + + if get_line_prefix: + # Add prefix width. + text_width += fragment_list_width( + to_formatted_text(get_line_prefix(lineno, 0)) + ) + + # Slower path: compute path when there's a line prefix. + height = 1 + + # Keep wrapping as long as the line doesn't fit. + # Keep adding new prefixes for every wrapped line. + while text_width > width: + height += 1 + text_width -= width + + fragments2 = to_formatted_text( + get_line_prefix(lineno, height - 1) + ) + prefix_width = get_cwidth(fragment_list_to_text(fragments2)) + + if prefix_width >= width: # Prefix doesn't fit. + height = 10 ** 8 + break + + text_width += prefix_width + else: + # Fast path: compute height when there's no line prefix. + try: + quotient, remainder = divmod(text_width, width) + except ZeroDivisionError: + height = 10 ** 8 + else: + if remainder: + quotient += 1 # Like math.ceil. + height = max(1, quotient) + + # Cache and return + self._line_heights_cache[key] = height + return height + + +class FormattedTextControl(UIControl): + """ + Control that displays formatted text. This can be either plain text, an + :class:`~prompt_toolkit.formatted_text.HTML` object an + :class:`~prompt_toolkit.formatted_text.ANSI` object, a list of ``(style_str, + text)`` tuples or a callable that takes no argument and returns one of + those, depending on how you prefer to do the formatting. See + ``prompt_toolkit.layout.formatted_text`` for more information. + + (It's mostly optimized for rather small widgets, like toolbars, menus, etc...) + + When this UI control has the focus, the cursor will be shown in the upper + left corner of this control by default. There are two ways for specifying + the cursor position: + + - Pass a `get_cursor_position` function which returns a `Point` instance + with the current cursor position. + + - If the (formatted) text is passed as a list of ``(style, text)`` tuples + and there is one that looks like ``('[SetCursorPosition]', '')``, then + this will specify the cursor position. + + Mouse support: + + The list of fragments can also contain tuples of three items, looking like: + (style_str, text, handler). When mouse support is enabled and the user + clicks on this fragment, then the given handler is called. That handler + should accept two inputs: (Application, MouseEvent) and it should + either handle the event or return `NotImplemented` in case we want the + containing Window to handle this event. + + :param focusable: `bool` or :class:`.Filter`: Tell whether this control is + focusable. + + :param text: Text or formatted text to be displayed. + :param style: Style string applied to the content. (If you want to style + the whole :class:`~prompt_toolkit.layout.Window`, pass the style to the + :class:`~prompt_toolkit.layout.Window` instead.) + :param key_bindings: a :class:`.KeyBindings` object. + :param get_cursor_position: A callable that returns the cursor position as + a `Point` instance. + """ + + def __init__( + self, + text: AnyFormattedText = "", + style: str = "", + focusable: FilterOrBool = False, + key_bindings: Optional["KeyBindingsBase"] = None, + show_cursor: bool = True, + modal: bool = False, + get_cursor_position: Optional[Callable[[], Optional[Point]]] = None, + ) -> None: + + self.text = text # No type check on 'text'. This is done dynamically. + self.style = style + self.focusable = to_filter(focusable) + + # Key bindings. + self.key_bindings = key_bindings + self.show_cursor = show_cursor + self.modal = modal + self.get_cursor_position = get_cursor_position + + #: Cache for the content. + self._content_cache: SimpleCache[Hashable, UIContent] = SimpleCache(maxsize=18) + self._fragment_cache: SimpleCache[int, StyleAndTextTuples] = SimpleCache( + maxsize=1 + ) + # Only cache one fragment list. We don't need the previous item. + + # Render info for the mouse support. + self._fragments: Optional[StyleAndTextTuples] = None + + def reset(self) -> None: + self._fragments = None + + def is_focusable(self) -> bool: + return self.focusable() + + def __repr__(self) -> str: + return "%s(%r)" % (self.__class__.__name__, self.text) + + def _get_formatted_text_cached(self) -> StyleAndTextTuples: + """ + Get fragments, but only retrieve fragments once during one render run. + (This function is called several times during one rendering, because + we also need those for calculating the dimensions.) + """ + return self._fragment_cache.get( + get_app().render_counter, lambda: to_formatted_text(self.text, self.style) + ) + + def preferred_width(self, max_available_width: int) -> int: + """ + Return the preferred width for this control. + That is the width of the longest line. + """ + text = fragment_list_to_text(self._get_formatted_text_cached()) + line_lengths = [get_cwidth(l) for l in text.split("\n")] + return max(line_lengths) + + def preferred_height( + self, + width: int, + max_available_height: int, + wrap_lines: bool, + get_line_prefix: Optional[GetLinePrefixCallable], + ) -> Optional[int]: + + content = self.create_content(width, None) + if wrap_lines: + height = 0 + for i in range(content.line_count): + height += content.get_height_for_line(i, width, get_line_prefix) + if height >= max_available_height: + return max_available_height + return height + else: + return content.line_count + + def create_content(self, width: int, height: Optional[int]) -> UIContent: + # Get fragments + fragments_with_mouse_handlers = self._get_formatted_text_cached() + fragment_lines_with_mouse_handlers = list( + split_lines(fragments_with_mouse_handlers) + ) + + # Strip mouse handlers from fragments. + fragment_lines: List[StyleAndTextTuples] = [ + [(item[0], item[1]) for item in line] + for line in fragment_lines_with_mouse_handlers + ] + + # Keep track of the fragments with mouse handler, for later use in + # `mouse_handler`. + self._fragments = fragments_with_mouse_handlers + + # If there is a `[SetCursorPosition]` in the fragment list, set the + # cursor position here. + def get_cursor_position( + fragment: str = "[SetCursorPosition]", + ) -> Optional[Point]: + for y, line in enumerate(fragment_lines): + x = 0 + for style_str, text, *_ in line: + if fragment in style_str: + return Point(x=x, y=y) + x += len(text) + return None + + # If there is a `[SetMenuPosition]`, set the menu over here. + def get_menu_position() -> Optional[Point]: + return get_cursor_position("[SetMenuPosition]") + + cursor_position = (self.get_cursor_position or get_cursor_position)() + + # Create content, or take it from the cache. + key = (tuple(fragments_with_mouse_handlers), width, cursor_position) + + def get_content() -> UIContent: + return UIContent( + get_line=lambda i: fragment_lines[i], + line_count=len(fragment_lines), + show_cursor=self.show_cursor, + cursor_position=cursor_position, + menu_position=get_menu_position(), + ) + + return self._content_cache.get(key, get_content) + + def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone": + """ + Handle mouse events. + + (When the fragment list contained mouse handlers and the user clicked on + on any of these, the matching handler is called. This handler can still + return `NotImplemented` in case we want the + :class:`~prompt_toolkit.layout.Window` to handle this particular + event.) + """ + if self._fragments: + # Read the generator. + fragments_for_line = list(split_lines(self._fragments)) + + try: + fragments = fragments_for_line[mouse_event.position.y] + except IndexError: + return NotImplemented + else: + # Find position in the fragment list. + xpos = mouse_event.position.x + + # Find mouse handler for this character. + count = 0 + for item in fragments: + count += len(item[1]) + if count >= xpos: + if len(item) >= 3: + # Handler found. Call it. + # (Handler can return NotImplemented, so return + # that result.) + handler = item[2] # type: ignore + return handler(mouse_event) + else: + break + + # Otherwise, don't handle here. + return NotImplemented + + def is_modal(self) -> bool: + return self.modal + + def get_key_bindings(self) -> Optional["KeyBindingsBase"]: + return self.key_bindings + + +class DummyControl(UIControl): + """ + A dummy control object that doesn't paint any content. + + Useful for filling a :class:`~prompt_toolkit.layout.Window`. (The + `fragment` and `char` attributes of the `Window` class can be used to + define the filling.) + """ + + def create_content(self, width: int, height: int) -> UIContent: + def get_line(i: int) -> StyleAndTextTuples: + return [] + + return UIContent( + get_line=get_line, line_count=100 ** 100 + ) # Something very big. + + def is_focusable(self) -> bool: + return False + + +_ProcessedLine = NamedTuple( + "_ProcessedLine", + [ + ("fragments", StyleAndTextTuples), + ("source_to_display", Callable[[int], int]), + ("display_to_source", Callable[[int], int]), + ], +) + + +class BufferControl(UIControl): + """ + Control for visualising the content of a :class:`.Buffer`. + + :param buffer: The :class:`.Buffer` object to be displayed. + :param input_processors: A list of + :class:`~prompt_toolkit.layout.processors.Processor` objects. + :param include_default_input_processors: When True, include the default + processors for highlighting of selection, search and displaying of + multiple cursors. + :param lexer: :class:`.Lexer` instance for syntax highlighting. + :param preview_search: `bool` or :class:`.Filter`: Show search while + typing. When this is `True`, probably you want to add a + ``HighlightIncrementalSearchProcessor`` as well. Otherwise only the + cursor position will move, but the text won't be highlighted. + :param focusable: `bool` or :class:`.Filter`: Tell whether this control is focusable. + :param focus_on_click: Focus this buffer when it's click, but not yet focused. + :param key_bindings: a :class:`.KeyBindings` object. + """ + + def __init__( + self, + buffer: Optional[Buffer] = None, + input_processors: Optional[List[Processor]] = None, + include_default_input_processors: bool = True, + lexer: Optional[Lexer] = None, + preview_search: FilterOrBool = False, + focusable: FilterOrBool = True, + search_buffer_control: Union[ + None, "SearchBufferControl", Callable[[], "SearchBufferControl"] + ] = None, + menu_position: Optional[Callable] = None, + focus_on_click: FilterOrBool = False, + key_bindings: Optional["KeyBindingsBase"] = None, + ): + + self.input_processors = input_processors + self.include_default_input_processors = include_default_input_processors + + self.default_input_processors = [ + HighlightSearchProcessor(), + HighlightIncrementalSearchProcessor(), + HighlightSelectionProcessor(), + DisplayMultipleCursors(), + ] + + self.preview_search = to_filter(preview_search) + self.focusable = to_filter(focusable) + self.focus_on_click = to_filter(focus_on_click) + + self.buffer = buffer or Buffer() + self.menu_position = menu_position + self.lexer = lexer or SimpleLexer() + self.key_bindings = key_bindings + self._search_buffer_control = search_buffer_control + + #: Cache for the lexer. + #: Often, due to cursor movement, undo/redo and window resizing + #: operations, it happens that a short time, the same document has to be + #: lexed. This is a fairly easy way to cache such an expensive operation. + self._fragment_cache: SimpleCache[ + Hashable, Callable[[int], StyleAndTextTuples] + ] = SimpleCache(maxsize=8) + + self._last_click_timestamp: Optional[float] = None + self._last_get_processed_line: Optional[Callable[[int], _ProcessedLine]] = None + + def __repr__(self) -> str: + return "<%s buffer=%r at %r>" % (self.__class__.__name__, self.buffer, id(self)) + + @property + def search_buffer_control(self) -> Optional["SearchBufferControl"]: + result: Optional[SearchBufferControl] + + if callable(self._search_buffer_control): + result = self._search_buffer_control() + else: + result = self._search_buffer_control + + assert result is None or isinstance(result, SearchBufferControl) + return result + + @property + def search_buffer(self) -> Optional[Buffer]: + control = self.search_buffer_control + if control is not None: + return control.buffer + return None + + @property + def search_state(self) -> SearchState: + """ + Return the `SearchState` for searching this `BufferControl`. This is + always associated with the search control. If one search bar is used + for searching multiple `BufferControls`, then they share the same + `SearchState`. + """ + search_buffer_control = self.search_buffer_control + if search_buffer_control: + return search_buffer_control.searcher_search_state + else: + return SearchState() + + def is_focusable(self) -> bool: + return self.focusable() + + def preferred_width(self, max_available_width: int) -> Optional[int]: + """ + This should return the preferred width. + + Note: We don't specify a preferred width according to the content, + because it would be too expensive. Calculating the preferred + width can be done by calculating the longest line, but this would + require applying all the processors to each line. This is + unfeasible for a larger document, and doing it for small + documents only would result in inconsistent behaviour. + """ + return None + + def preferred_height( + self, + width: int, + max_available_height: int, + wrap_lines: bool, + get_line_prefix: Optional[GetLinePrefixCallable], + ) -> Optional[int]: + + # Calculate the content height, if it was drawn on a screen with the + # given width. + height = 0 + content = self.create_content(width, height=1) # Pass a dummy '1' as height. + + # When line wrapping is off, the height should be equal to the amount + # of lines. + if not wrap_lines: + return content.line_count + + # When the number of lines exceeds the max_available_height, just + # return max_available_height. No need to calculate anything. + if content.line_count >= max_available_height: + return max_available_height + + for i in range(content.line_count): + height += content.get_height_for_line(i, width, get_line_prefix) + + if height >= max_available_height: + return max_available_height + + return height + + def _get_formatted_text_for_line_func( + self, document: Document + ) -> Callable[[int], StyleAndTextTuples]: + """ + Create a function that returns the fragments for a given line. + """ + # Cache using `document.text`. + def get_formatted_text_for_line() -> Callable[[int], StyleAndTextTuples]: + return self.lexer.lex_document(document) + + key = (document.text, self.lexer.invalidation_hash()) + return self._fragment_cache.get(key, get_formatted_text_for_line) + + def _create_get_processed_line_func( + self, document: Document, width: int, height: int + ) -> Callable[[int], _ProcessedLine]: + """ + Create a function that takes a line number of the current document and + returns a _ProcessedLine(processed_fragments, source_to_display, display_to_source) + tuple. + """ + # Merge all input processors together. + input_processors = self.input_processors or [] + if self.include_default_input_processors: + input_processors = self.default_input_processors + input_processors + + merged_processor = merge_processors(input_processors) + + def transform(lineno: int, fragments: StyleAndTextTuples) -> _ProcessedLine: + " Transform the fragments for a given line number. " + # Get cursor position at this line. + def source_to_display(i: int) -> int: + """X position from the buffer to the x position in the + processed fragment list. By default, we start from the 'identity' + operation.""" + return i + + transformation = merged_processor.apply_transformation( + TransformationInput( + self, document, lineno, source_to_display, fragments, width, height + ) + ) + + return _ProcessedLine( + transformation.fragments, + transformation.source_to_display, + transformation.display_to_source, + ) + + def create_func() -> Callable[[int], _ProcessedLine]: + get_line = self._get_formatted_text_for_line_func(document) + cache: Dict[int, _ProcessedLine] = {} + + def get_processed_line(i: int) -> _ProcessedLine: + try: + return cache[i] + except KeyError: + processed_line = transform(i, get_line(i)) + cache[i] = processed_line + return processed_line + + return get_processed_line + + return create_func() + + def create_content( + self, width: int, height: int, preview_search: bool = False + ) -> UIContent: + """ + Create a UIContent. + """ + buffer = self.buffer + + # Get the document to be shown. If we are currently searching (the + # search buffer has focus, and the preview_search filter is enabled), + # then use the search document, which has possibly a different + # text/cursor position.) + search_control = self.search_buffer_control + preview_now = preview_search or bool( + # Only if this feature is enabled. + self.preview_search() + and + # And something was typed in the associated search field. + search_control + and search_control.buffer.text + and + # And we are searching in this control. (Many controls can point to + # the same search field, like in Pyvim.) + get_app().layout.search_target_buffer_control == self + ) + + if preview_now and search_control is not None: + ss = self.search_state + + document = buffer.document_for_search( + SearchState( + text=search_control.buffer.text, + direction=ss.direction, + ignore_case=ss.ignore_case, + ) + ) + else: + document = buffer.document + + get_processed_line = self._create_get_processed_line_func( + document, width, height + ) + self._last_get_processed_line = get_processed_line + + def translate_rowcol(row: int, col: int) -> Point: + " Return the content column for this coordinate. " + return Point(x=get_processed_line(row).source_to_display(col), y=row) + + def get_line(i: int) -> StyleAndTextTuples: + " Return the fragments for a given line number. " + fragments = get_processed_line(i).fragments + + # Add a space at the end, because that is a possible cursor + # position. (When inserting after the input.) We should do this on + # all the lines, not just the line containing the cursor. (Because + # otherwise, line wrapping/scrolling could change when moving the + # cursor around.) + fragments = fragments + [("", " ")] + return fragments + + content = UIContent( + get_line=get_line, + line_count=document.line_count, + cursor_position=translate_rowcol( + document.cursor_position_row, document.cursor_position_col + ), + ) + + # If there is an auto completion going on, use that start point for a + # pop-up menu position. (But only when this buffer has the focus -- + # there is only one place for a menu, determined by the focused buffer.) + if get_app().layout.current_control == self: + menu_position = self.menu_position() if self.menu_position else None + if menu_position is not None: + assert isinstance(menu_position, int) + menu_row, menu_col = buffer.document.translate_index_to_position( + menu_position + ) + content.menu_position = translate_rowcol(menu_row, menu_col) + elif buffer.complete_state: + # Position for completion menu. + # Note: We use 'min', because the original cursor position could be + # behind the input string when the actual completion is for + # some reason shorter than the text we had before. (A completion + # can change and shorten the input.) + menu_row, menu_col = buffer.document.translate_index_to_position( + min( + buffer.cursor_position, + buffer.complete_state.original_document.cursor_position, + ) + ) + content.menu_position = translate_rowcol(menu_row, menu_col) + else: + content.menu_position = None + + return content + + def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone": + """ + Mouse handler for this control. + """ + buffer = self.buffer + position = mouse_event.position + + # Focus buffer when clicked. + if get_app().layout.current_control == self: + if self._last_get_processed_line: + processed_line = self._last_get_processed_line(position.y) + + # Translate coordinates back to the cursor position of the + # original input. + xpos = processed_line.display_to_source(position.x) + index = buffer.document.translate_row_col_to_index(position.y, xpos) + + # Set the cursor position. + if mouse_event.event_type == MouseEventType.MOUSE_DOWN: + buffer.exit_selection() + buffer.cursor_position = index + + elif mouse_event.event_type == MouseEventType.MOUSE_UP: + # When the cursor was moved to another place, select the text. + # (The >1 is actually a small but acceptable workaround for + # selecting text in Vi navigation mode. In navigation mode, + # the cursor can never be after the text, so the cursor + # will be repositioned automatically.) + if abs(buffer.cursor_position - index) > 1: + buffer.start_selection(selection_type=SelectionType.CHARACTERS) + buffer.cursor_position = index + + # Select word around cursor on double click. + # Two MOUSE_UP events in a short timespan are considered a double click. + double_click = ( + self._last_click_timestamp + and time.time() - self._last_click_timestamp < 0.3 + ) + self._last_click_timestamp = time.time() + + if double_click: + start, end = buffer.document.find_boundaries_of_current_word() + buffer.cursor_position += start + buffer.start_selection(selection_type=SelectionType.CHARACTERS) + buffer.cursor_position += end - start + else: + # Don't handle scroll events here. + return NotImplemented + + # Not focused, but focusing on click events. + else: + if ( + self.focus_on_click() + and mouse_event.event_type == MouseEventType.MOUSE_UP + ): + # Focus happens on mouseup. (If we did this on mousedown, the + # up event will be received at the point where this widget is + # focused and be handled anyway.) + get_app().layout.current_control = self + else: + return NotImplemented + + return None + + def move_cursor_down(self) -> None: + b = self.buffer + b.cursor_position += b.document.get_cursor_down_position() + + def move_cursor_up(self) -> None: + b = self.buffer + b.cursor_position += b.document.get_cursor_up_position() + + def get_key_bindings(self) -> Optional["KeyBindingsBase"]: + """ + When additional key bindings are given. Return these. + """ + return self.key_bindings + + def get_invalidate_events(self) -> Iterable["Event[object]"]: + """ + Return the Window invalidate events. + """ + # Whenever the buffer changes, the UI has to be updated. + yield self.buffer.on_text_changed + yield self.buffer.on_cursor_position_changed + + yield self.buffer.on_completions_changed + yield self.buffer.on_suggestion_set + + +class SearchBufferControl(BufferControl): + """ + :class:`.BufferControl` which is used for searching another + :class:`.BufferControl`. + + :param ignore_case: Search case insensitive. + """ + + def __init__( + self, + buffer: Optional[Buffer] = None, + input_processors: Optional[List[Processor]] = None, + lexer: Optional[Lexer] = None, + focus_on_click: FilterOrBool = False, + key_bindings: Optional["KeyBindingsBase"] = None, + ignore_case: FilterOrBool = False, + ): + + super().__init__( + buffer=buffer, + input_processors=input_processors, + lexer=lexer, + focus_on_click=focus_on_click, + key_bindings=key_bindings, + ) + + # If this BufferControl is used as a search field for one or more other + # BufferControls, then represents the search state. + self.searcher_search_state = SearchState(ignore_case=ignore_case) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/dimension.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/dimension.py new file mode 100644 index 00000000..1e41ccdc --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/dimension.py @@ -0,0 +1,214 @@ +""" +Layout dimensions are used to give the minimum, maximum and preferred +dimensions for containers and controls. +""" +from typing import Any, Callable, List, Optional, Union + +__all__ = [ + "Dimension", + "D", + "sum_layout_dimensions", + "max_layout_dimensions", + "AnyDimension", + "to_dimension", + "is_dimension", +] + + +class Dimension: + """ + Specified dimension (width/height) of a user control or window. + + The layout engine tries to honor the preferred size. If that is not + possible, because the terminal is larger or smaller, it tries to keep in + between min and max. + + :param min: Minimum size. + :param max: Maximum size. + :param weight: For a VSplit/HSplit, the actual size will be determined + by taking the proportion of weights from all the children. + E.g. When there are two children, one with a weight of 1, + and the other with a weight of 2, the second will always be + twice as big as the first, if the min/max values allow it. + :param preferred: Preferred size. + """ + + def __init__( + self, + min: Optional[int] = None, + max: Optional[int] = None, + weight: Optional[int] = None, + preferred: Optional[int] = None, + ) -> None: + if weight is not None: + assert weight >= 0 # Also cannot be a float. + + assert min is None or min >= 0 + assert max is None or max >= 0 + assert preferred is None or preferred >= 0 + + self.min_specified = min is not None + self.max_specified = max is not None + self.preferred_specified = preferred is not None + self.weight_specified = weight is not None + + if min is None: + min = 0 # Smallest possible value. + if max is None: # 0-values are allowed, so use "is None" + max = 1000 ** 10 # Something huge. + if preferred is None: + preferred = min + if weight is None: + weight = 1 + + self.min = min + self.max = max + self.preferred = preferred + self.weight = weight + + # Don't allow situations where max < min. (This would be a bug.) + if max < min: + raise ValueError("Invalid Dimension: max < min.") + + # Make sure that the 'preferred' size is always in the min..max range. + if self.preferred < self.min: + self.preferred = self.min + + if self.preferred > self.max: + self.preferred = self.max + + @classmethod + def exact(cls, amount: int) -> "Dimension": + """ + Return a :class:`.Dimension` with an exact size. (min, max and + preferred set to ``amount``). + """ + return cls(min=amount, max=amount, preferred=amount) + + @classmethod + def zero(cls) -> "Dimension": + """ + Create a dimension that represents a zero size. (Used for 'invisible' + controls.) + """ + return cls.exact(amount=0) + + def is_zero(self) -> bool: + " True if this `Dimension` represents a zero size. " + return self.preferred == 0 or self.max == 0 + + def __repr__(self) -> str: + fields = [] + if self.min_specified: + fields.append("min=%r" % self.min) + if self.max_specified: + fields.append("max=%r" % self.max) + if self.preferred_specified: + fields.append("preferred=%r" % self.preferred) + if self.weight_specified: + fields.append("weight=%r" % self.weight) + + return "Dimension(%s)" % ", ".join(fields) + + +def sum_layout_dimensions(dimensions: List[Dimension]) -> Dimension: + """ + Sum a list of :class:`.Dimension` instances. + """ + min = sum(d.min for d in dimensions) + max = sum(d.max for d in dimensions) + preferred = sum(d.preferred for d in dimensions) + + return Dimension(min=min, max=max, preferred=preferred) + + +def max_layout_dimensions(dimensions: List[Dimension]) -> Dimension: + """ + Take the maximum of a list of :class:`.Dimension` instances. + Used when we have a HSplit/VSplit, and we want to get the best width/height.) + """ + if not len(dimensions): + return Dimension.zero() + + # If all dimensions are size zero. Return zero. + # (This is important for HSplit/VSplit, to report the right values to their + # parent when all children are invisible.) + if all(d.is_zero() for d in dimensions): + return dimensions[0] + + # Ignore empty dimensions. (They should not reduce the size of others.) + dimensions = [d for d in dimensions if not d.is_zero()] + + if dimensions: + # Take the highest minimum dimension. + min_ = max(d.min for d in dimensions) + + # For the maximum, we would prefer not to go larger than then smallest + # 'max' value, unless other dimensions have a bigger preferred value. + # This seems to work best: + # - We don't want that a widget with a small height in a VSplit would + # shrink other widgets in the split. + # If it doesn't work well enough, then it's up to the UI designer to + # explicitly pass dimensions. + max_ = min(d.max for d in dimensions) + max_ = max(max_, max(d.preferred for d in dimensions)) + + # Make sure that min>=max. In some scenarios, when certain min..max + # ranges don't have any overlap, we can end up in such an impossible + # situation. In that case, give priority to the max value. + # E.g. taking (1..5) and (8..9) would return (8..5). Instead take (8..8). + if min_ > max_: + max_ = min_ + + preferred = max(d.preferred for d in dimensions) + + return Dimension(min=min_, max=max_, preferred=preferred) + else: + return Dimension() + + +# Anything that can be converted to a dimension. +AnyDimension = Union[ + None, # None is a valid dimension that will fit anything. + int, + Dimension, + # Callable[[], 'AnyDimension'] # Recursive definition not supported by mypy. + Callable[[], Any], +] + + +def to_dimension(value: AnyDimension) -> Dimension: + """ + Turn the given object into a `Dimension` object. + """ + if value is None: + return Dimension() + if isinstance(value, int): + return Dimension.exact(value) + if isinstance(value, Dimension): + return value + if callable(value): + return to_dimension(value()) + + raise ValueError("Not an integer or Dimension object.") + + +def is_dimension(value: object) -> bool: + """ + Test whether the given value could be a valid dimension. + (For usage in an assertion. It's not guaranteed in case of a callable.) + """ + if value is None: + return True + if callable(value): + return True # Assume it's a callable that doesn't take arguments. + if isinstance(value, (int, Dimension)): + return True + return False + + +# Common alias. +D = Dimension + +# For backward-compatibility. +LayoutDimension = Dimension diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/dummy.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/dummy.py new file mode 100644 index 00000000..dcd54e9f --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/dummy.py @@ -0,0 +1,37 @@ +""" +Dummy layout. Used when somebody creates an `Application` without specifying a +`Layout`. +""" +from prompt_toolkit.formatted_text import HTML +from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.key_binding.key_processor import KeyPressEvent + +from .containers import Window +from .controls import FormattedTextControl +from .dimension import D +from .layout import Layout + +__all__ = [ + "create_dummy_layout", +] + +E = KeyPressEvent + + +def create_dummy_layout() -> Layout: + """ + Create a dummy layout for use in an 'Application' that doesn't have a + layout specified. When ENTER is pressed, the application quits. + """ + kb = KeyBindings() + + @kb.add("enter") + def enter(event: E) -> None: + event.app.exit() + + control = FormattedTextControl( + HTML("No layout specified. Press ENTER to quit."), + key_bindings=kb, + ) + window = Window(content=control, height=D(min=1)) + return Layout(container=window, focused_element=window) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/layout.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/layout.py new file mode 100644 index 00000000..457558fd --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/layout.py @@ -0,0 +1,417 @@ +""" +Wrapper for the layout. +""" +from typing import Dict, Generator, Iterable, List, Optional, Union + +from prompt_toolkit.buffer import Buffer + +from .containers import ( + AnyContainer, + ConditionalContainer, + Container, + Window, + to_container, +) +from .controls import BufferControl, SearchBufferControl, UIControl + +__all__ = [ + "Layout", + "InvalidLayoutError", + "walk", +] + +FocusableElement = Union[str, Buffer, UIControl, AnyContainer] + + +class Layout: + """ + The layout for a prompt_toolkit + :class:`~prompt_toolkit.application.Application`. + This also keeps track of which user control is focused. + + :param container: The "root" container for the layout. + :param focused_element: element to be focused initially. (Can be anything + the `focus` function accepts.) + """ + + def __init__( + self, + container: AnyContainer, + focused_element: Optional[FocusableElement] = None, + ) -> None: + + self.container = to_container(container) + self._stack: List[Window] = [] + + # Map search BufferControl back to the original BufferControl. + # This is used to keep track of when exactly we are searching, and for + # applying the search. + # When a link exists in this dictionary, that means the search is + # currently active. + # Map: search_buffer_control -> original buffer control. + self.search_links: Dict[SearchBufferControl, BufferControl] = {} + + # Mapping that maps the children in the layout to their parent. + # This relationship is calculated dynamically, each time when the UI + # is rendered. (UI elements have only references to their children.) + self._child_to_parent: Dict[Container, Container] = {} + + if focused_element is None: + try: + self._stack.append(next(self.find_all_windows())) + except StopIteration as e: + raise InvalidLayoutError( + "Invalid layout. The layout does not contain any Window object." + ) from e + else: + self.focus(focused_element) + + # List of visible windows. + self.visible_windows: List[Window] = [] # List of `Window` objects. + + def __repr__(self) -> str: + return "Layout(%r, current_window=%r)" % (self.container, self.current_window) + + def find_all_windows(self) -> Generator[Window, None, None]: + """ + Find all the :class:`.UIControl` objects in this layout. + """ + for item in self.walk(): + if isinstance(item, Window): + yield item + + def find_all_controls(self) -> Iterable[UIControl]: + for container in self.find_all_windows(): + yield container.content + + def focus(self, value: FocusableElement) -> None: + """ + Focus the given UI element. + + `value` can be either: + + - a :class:`.UIControl` + - a :class:`.Buffer` instance or the name of a :class:`.Buffer` + - a :class:`.Window` + - Any container object. In this case we will focus the :class:`.Window` + from this container that was focused most recent, or the very first + focusable :class:`.Window` of the container. + """ + # BufferControl by buffer name. + if isinstance(value, str): + for control in self.find_all_controls(): + if isinstance(control, BufferControl) and control.buffer.name == value: + self.focus(control) + return + raise ValueError( + "Couldn't find Buffer in the current layout: %r." % (value,) + ) + + # BufferControl by buffer object. + elif isinstance(value, Buffer): + for control in self.find_all_controls(): + if isinstance(control, BufferControl) and control.buffer == value: + self.focus(control) + return + raise ValueError( + "Couldn't find Buffer in the current layout: %r." % (value,) + ) + + # Focus UIControl. + elif isinstance(value, UIControl): + if value not in self.find_all_controls(): + raise ValueError( + "Invalid value. Container does not appear in the layout." + ) + if not value.is_focusable(): + raise ValueError("Invalid value. UIControl is not focusable.") + + self.current_control = value + + # Otherwise, expecting any Container object. + else: + value = to_container(value) + + if isinstance(value, Window): + # This is a `Window`: focus that. + if value not in self.find_all_windows(): + raise ValueError( + "Invalid value. Window does not appear in the layout: %r" + % (value,) + ) + + self.current_window = value + else: + # Focus a window in this container. + # If we have many windows as part of this container, and some + # of them have been focused before, take the last focused + # item. (This is very useful when the UI is composed of more + # complex sub components.) + windows = [] + for c in walk(value, skip_hidden=True): + if isinstance(c, Window) and c.content.is_focusable(): + windows.append(c) + + # Take the first one that was focused before. + for w in reversed(self._stack): + if w in windows: + self.current_window = w + return + + # None was focused before: take the very first focusable window. + if windows: + self.current_window = windows[0] + return + + raise ValueError( + "Invalid value. Container cannot be focused: %r" % (value,) + ) + + def has_focus(self, value: FocusableElement) -> bool: + """ + Check whether the given control has the focus. + :param value: :class:`.UIControl` or :class:`.Window` instance. + """ + if isinstance(value, str): + if self.current_buffer is None: + return False + return self.current_buffer.name == value + if isinstance(value, Buffer): + return self.current_buffer == value + if isinstance(value, UIControl): + return self.current_control == value + else: + value = to_container(value) + if isinstance(value, Window): + return self.current_window == value + else: + # Check whether this "container" is focused. This is true if + # one of the elements inside is focused. + for element in walk(value): + if element == self.current_window: + return True + return False + + @property + def current_control(self) -> UIControl: + """ + Get the :class:`.UIControl` to currently has the focus. + """ + return self._stack[-1].content + + @current_control.setter + def current_control(self, control: UIControl) -> None: + """ + Set the :class:`.UIControl` to receive the focus. + """ + for window in self.find_all_windows(): + if window.content == control: + self.current_window = window + return + + raise ValueError("Control not found in the user interface.") + + @property + def current_window(self) -> Window: + " Return the :class:`.Window` object that is currently focused. " + return self._stack[-1] + + @current_window.setter + def current_window(self, value: Window): + " Set the :class:`.Window` object to be currently focused. " + self._stack.append(value) + + @property + def is_searching(self) -> bool: + " True if we are searching right now. " + return self.current_control in self.search_links + + @property + def search_target_buffer_control(self) -> Optional[BufferControl]: + """ + Return the :class:`.BufferControl` in which we are searching or `None`. + """ + # Not every `UIControl` is a `BufferControl`. This only applies to + # `BufferControl`. + control = self.current_control + + if isinstance(control, SearchBufferControl): + return self.search_links.get(control) + else: + return None + + def get_focusable_windows(self) -> Iterable[Window]: + """ + Return all the :class:`.Window` objects which are focusable (in the + 'modal' area). + """ + for w in self.walk_through_modal_area(): + if isinstance(w, Window) and w.content.is_focusable(): + yield w + + def get_visible_focusable_windows(self) -> List[Window]: + """ + Return a list of :class:`.Window` objects that are focusable. + """ + # focusable windows are windows that are visible, but also part of the + # modal container. Make sure to keep the ordering. + visible_windows = self.visible_windows + return [w for w in self.get_focusable_windows() if w in visible_windows] + + @property + def current_buffer(self) -> Optional[Buffer]: + """ + The currently focused :class:`~.Buffer` or `None`. + """ + ui_control = self.current_control + if isinstance(ui_control, BufferControl): + return ui_control.buffer + return None + + def get_buffer_by_name(self, buffer_name: str) -> Optional[Buffer]: + """ + Look in the layout for a buffer with the given name. + Return `None` when nothing was found. + """ + for w in self.walk(): + if isinstance(w, Window) and isinstance(w.content, BufferControl): + if w.content.buffer.name == buffer_name: + return w.content.buffer + return None + + @property + def buffer_has_focus(self) -> bool: + """ + Return `True` if the currently focused control is a + :class:`.BufferControl`. (For instance, used to determine whether the + default key bindings should be active or not.) + """ + ui_control = self.current_control + return isinstance(ui_control, BufferControl) + + @property + def previous_control(self) -> UIControl: + """ + Get the :class:`.UIControl` to previously had the focus. + """ + try: + return self._stack[-2].content + except IndexError: + return self._stack[-1].content + + def focus_last(self) -> None: + """ + Give the focus to the last focused control. + """ + if len(self._stack) > 1: + self._stack = self._stack[:-1] + + def focus_next(self) -> None: + """ + Focus the next visible/focusable Window. + """ + windows = self.get_visible_focusable_windows() + + if len(windows) > 0: + try: + index = windows.index(self.current_window) + except ValueError: + index = 0 + else: + index = (index + 1) % len(windows) + + self.focus(windows[index]) + + def focus_previous(self) -> None: + """ + Focus the previous visible/focusable Window. + """ + windows = self.get_visible_focusable_windows() + + if len(windows) > 0: + try: + index = windows.index(self.current_window) + except ValueError: + index = 0 + else: + index = (index - 1) % len(windows) + + self.focus(windows[index]) + + def walk(self) -> Iterable[Container]: + """ + Walk through all the layout nodes (and their children) and yield them. + """ + for i in walk(self.container): + yield i + + def walk_through_modal_area(self) -> Iterable[Container]: + """ + Walk through all the containers which are in the current 'modal' part + of the layout. + """ + # Go up in the tree, and find the root. (it will be a part of the + # layout, if the focus is in a modal part.) + root: Container = self.current_window + while not root.is_modal() and root in self._child_to_parent: + root = self._child_to_parent[root] + + for container in walk(root): + yield container + + def update_parents_relations(self) -> None: + """ + Update child->parent relationships mapping. + """ + parents = {} + + def walk(e: Container) -> None: + for c in e.get_children(): + parents[c] = e + walk(c) + + walk(self.container) + + self._child_to_parent = parents + + def reset(self) -> None: + # Remove all search links when the UI starts. + # (Important, for instance when control-c is been pressed while + # searching. The prompt cancels, but next `run()` call the search + # links are still there.) + self.search_links.clear() + + self.container.reset() + + def get_parent(self, container: Container) -> Optional[Container]: + """ + Return the parent container for the given container, or ``None``, if it + wasn't found. + """ + try: + return self._child_to_parent[container] + except KeyError: + return None + + +class InvalidLayoutError(Exception): + pass + + +def walk(container: Container, skip_hidden: bool = False) -> Iterable[Container]: + """ + Walk through layout, starting at this container. + """ + # When `skip_hidden` is set, don't go into disabled ConditionalContainer containers. + if ( + skip_hidden + and isinstance(container, ConditionalContainer) + and not container.filter() + ): + return + + yield container + + for c in container.get_children(): + # yield from walk(c) + yield from walk(c, skip_hidden=skip_hidden) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/margins.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/margins.py new file mode 100644 index 00000000..9348fb5e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/margins.py @@ -0,0 +1,305 @@ +""" +Margin implementations for a :class:`~prompt_toolkit.layout.containers.Window`. +""" +from abc import ABCMeta, abstractmethod +from typing import TYPE_CHECKING, Callable, Optional + +from prompt_toolkit.filters import FilterOrBool, to_filter +from prompt_toolkit.formatted_text import ( + StyleAndTextTuples, + fragment_list_to_text, + to_formatted_text, +) +from prompt_toolkit.utils import get_cwidth + +from .controls import UIContent + +if TYPE_CHECKING: + from .containers import WindowRenderInfo + +__all__ = [ + "Margin", + "NumberedMargin", + "ScrollbarMargin", + "ConditionalMargin", + "PromptMargin", +] + + +class Margin(metaclass=ABCMeta): + """ + Base interface for a margin. + """ + + @abstractmethod + def get_width(self, get_ui_content: Callable[[], UIContent]) -> int: + """ + Return the width that this margin is going to consume. + + :param get_ui_content: Callable that asks the user control to create + a :class:`.UIContent` instance. This can be used for instance to + obtain the number of lines. + """ + return 0 + + @abstractmethod + def create_margin( + self, window_render_info: "WindowRenderInfo", width: int, height: int + ) -> StyleAndTextTuples: + """ + Creates a margin. + This should return a list of (style_str, text) tuples. + + :param window_render_info: + :class:`~prompt_toolkit.layout.containers.WindowRenderInfo` + instance, generated after rendering and copying the visible part of + the :class:`~prompt_toolkit.layout.controls.UIControl` into the + :class:`~prompt_toolkit.layout.containers.Window`. + :param width: The width that's available for this margin. (As reported + by :meth:`.get_width`.) + :param height: The height that's available for this margin. (The height + of the :class:`~prompt_toolkit.layout.containers.Window`.) + """ + return [] + + +class NumberedMargin(Margin): + """ + Margin that displays the line numbers. + + :param relative: Number relative to the cursor position. Similar to the Vi + 'relativenumber' option. + :param display_tildes: Display tildes after the end of the document, just + like Vi does. + """ + + def __init__( + self, relative: FilterOrBool = False, display_tildes: FilterOrBool = False + ) -> None: + + self.relative = to_filter(relative) + self.display_tildes = to_filter(display_tildes) + + def get_width(self, get_ui_content: Callable[[], UIContent]) -> int: + line_count = get_ui_content().line_count + return max(3, len("%s" % line_count) + 1) + + def create_margin( + self, window_render_info: "WindowRenderInfo", width: int, height: int + ) -> StyleAndTextTuples: + relative = self.relative() + + style = "class:line-number" + style_current = "class:line-number.current" + + # Get current line number. + current_lineno = window_render_info.ui_content.cursor_position.y + + # Construct margin. + result: StyleAndTextTuples = [] + last_lineno = None + + for y, lineno in enumerate(window_render_info.displayed_lines): + # Only display line number if this line is not a continuation of the previous line. + if lineno != last_lineno: + if lineno is None: + pass + elif lineno == current_lineno: + # Current line. + if relative: + # Left align current number in relative mode. + result.append((style_current, "%i" % (lineno + 1))) + else: + result.append( + (style_current, ("%i " % (lineno + 1)).rjust(width)) + ) + else: + # Other lines. + if relative: + lineno = abs(lineno - current_lineno) - 1 + + result.append((style, ("%i " % (lineno + 1)).rjust(width))) + + last_lineno = lineno + result.append(("", "\n")) + + # Fill with tildes. + if self.display_tildes(): + while y < window_render_info.window_height: + result.append(("class:tilde", "~\n")) + y += 1 + + return result + + +class ConditionalMargin(Margin): + """ + Wrapper around other :class:`.Margin` classes to show/hide them. + """ + + def __init__(self, margin: Margin, filter: FilterOrBool) -> None: + self.margin = margin + self.filter = to_filter(filter) + + def get_width(self, get_ui_content: Callable[[], UIContent]) -> int: + if self.filter(): + return self.margin.get_width(get_ui_content) + else: + return 0 + + def create_margin( + self, window_render_info: "WindowRenderInfo", width: int, height: int + ) -> StyleAndTextTuples: + if width and self.filter(): + return self.margin.create_margin(window_render_info, width, height) + else: + return [] + + +class ScrollbarMargin(Margin): + """ + Margin displaying a scrollbar. + + :param display_arrows: Display scroll up/down arrows. + """ + + def __init__( + self, + display_arrows: FilterOrBool = False, + up_arrow_symbol: str = "^", + down_arrow_symbol: str = "v", + ) -> None: + + self.display_arrows = to_filter(display_arrows) + self.up_arrow_symbol = up_arrow_symbol + self.down_arrow_symbol = down_arrow_symbol + + def get_width(self, get_ui_content: Callable[[], UIContent]) -> int: + return 1 + + def create_margin( + self, window_render_info: "WindowRenderInfo", width: int, height: int + ) -> StyleAndTextTuples: + content_height = window_render_info.content_height + window_height = window_render_info.window_height + display_arrows = self.display_arrows() + + if display_arrows: + window_height -= 2 + + try: + fraction_visible = len(window_render_info.displayed_lines) / float( + content_height + ) + fraction_above = window_render_info.vertical_scroll / float(content_height) + + scrollbar_height = int( + min(window_height, max(1, window_height * fraction_visible)) + ) + scrollbar_top = int(window_height * fraction_above) + except ZeroDivisionError: + return [] + else: + + def is_scroll_button(row: int) -> bool: + " True if we should display a button on this row. " + return scrollbar_top <= row <= scrollbar_top + scrollbar_height + + # Up arrow. + result: StyleAndTextTuples = [] + if display_arrows: + result.extend( + [ + ("class:scrollbar.arrow", self.up_arrow_symbol), + ("class:scrollbar", "\n"), + ] + ) + + # Scrollbar body. + scrollbar_background = "class:scrollbar.background" + scrollbar_background_start = "class:scrollbar.background,scrollbar.start" + scrollbar_button = "class:scrollbar.button" + scrollbar_button_end = "class:scrollbar.button,scrollbar.end" + + for i in range(window_height): + if is_scroll_button(i): + if not is_scroll_button(i + 1): + # Give the last cell a different style, because we + # want to underline this. + result.append((scrollbar_button_end, " ")) + else: + result.append((scrollbar_button, " ")) + else: + if is_scroll_button(i + 1): + result.append((scrollbar_background_start, " ")) + else: + result.append((scrollbar_background, " ")) + result.append(("", "\n")) + + # Down arrow + if display_arrows: + result.append(("class:scrollbar.arrow", self.down_arrow_symbol)) + + return result + + +class PromptMargin(Margin): + """ + [Deprecated] + + Create margin that displays a prompt. + This can display one prompt at the first line, and a continuation prompt + (e.g, just dots) on all the following lines. + + This `PromptMargin` implementation has been largely superseded in favor of + the `get_line_prefix` attribute of `Window`. The reason is that a margin is + always a fixed width, while `get_line_prefix` can return a variable width + prefix in front of every line, making it more powerful, especially for line + continuations. + + :param get_prompt: Callable returns formatted text or a list of + `(style_str, type)` tuples to be shown as the prompt at the first line. + :param get_continuation: Callable that takes three inputs. The width (int), + line_number (int), and is_soft_wrap (bool). It should return formatted + text or a list of `(style_str, type)` tuples for the next lines of the + input. + """ + + def __init__( + self, + get_prompt: Callable[[], StyleAndTextTuples], + get_continuation: Optional[ + Callable[[int, int, bool], StyleAndTextTuples] + ] = None, + ) -> None: + + self.get_prompt = get_prompt + self.get_continuation = get_continuation + + def get_width(self, get_ui_content: Callable[[], UIContent]) -> int: + " Width to report to the `Window`. " + # Take the width from the first line. + text = fragment_list_to_text(self.get_prompt()) + return get_cwidth(text) + + def create_margin( + self, window_render_info: "WindowRenderInfo", width: int, height: int + ) -> StyleAndTextTuples: + get_continuation = self.get_continuation + result: StyleAndTextTuples = [] + + # First line. + result.extend(to_formatted_text(self.get_prompt())) + + # Next lines. + if get_continuation: + last_y = None + + for y in window_render_info.displayed_lines[1:]: + result.append(("", "\n")) + result.extend( + to_formatted_text(get_continuation(width, y, y == last_y)) + ) + last_y = y + + return result diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/menus.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/menus.py new file mode 100644 index 00000000..2663bd29 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/menus.py @@ -0,0 +1,720 @@ +import math +from itertools import zip_longest +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + Iterable, + List, + Optional, + Tuple, + TypeVar, + Union, + cast, +) + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.buffer import CompletionState +from prompt_toolkit.completion import Completion +from prompt_toolkit.data_structures import Point +from prompt_toolkit.filters import ( + Condition, + FilterOrBool, + has_completions, + is_done, + to_filter, +) +from prompt_toolkit.formatted_text import ( + StyleAndTextTuples, + fragment_list_width, + to_formatted_text, +) +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.layout.utils import explode_text_fragments +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.utils import get_cwidth + +from .containers import ConditionalContainer, HSplit, ScrollOffsets, Window +from .controls import GetLinePrefixCallable, UIContent, UIControl +from .dimension import Dimension +from .margins import ScrollbarMargin + +if TYPE_CHECKING: + from prompt_toolkit.key_binding.key_bindings import KeyBindings + + NotImplementedOrNone = object + +__all__ = [ + "CompletionsMenu", + "MultiColumnCompletionsMenu", +] + +E = KeyPressEvent + + +class CompletionsMenuControl(UIControl): + """ + Helper for drawing the complete menu to the screen. + + :param scroll_offset: Number (integer) representing the preferred amount of + completions to be displayed before and after the current one. When this + is a very high number, the current completion will be shown in the + middle most of the time. + """ + + # Preferred minimum size of the menu control. + # The CompletionsMenu class defines a width of 8, and there is a scrollbar + # of 1.) + MIN_WIDTH = 7 + + def has_focus(self) -> bool: + return False + + def preferred_width(self, max_available_width: int) -> Optional[int]: + complete_state = get_app().current_buffer.complete_state + if complete_state: + menu_width = self._get_menu_width(500, complete_state) + menu_meta_width = self._get_menu_meta_width(500, complete_state) + + return menu_width + menu_meta_width + else: + return 0 + + def preferred_height( + self, + width: int, + max_available_height: int, + wrap_lines: bool, + get_line_prefix: Optional[GetLinePrefixCallable], + ) -> Optional[int]: + + complete_state = get_app().current_buffer.complete_state + if complete_state: + return len(complete_state.completions) + else: + return 0 + + def create_content(self, width: int, height: int) -> UIContent: + """ + Create a UIContent object for this control. + """ + complete_state = get_app().current_buffer.complete_state + if complete_state: + completions = complete_state.completions + index = complete_state.complete_index # Can be None! + + # Calculate width of completions menu. + menu_width = self._get_menu_width(width, complete_state) + menu_meta_width = self._get_menu_meta_width( + width - menu_width, complete_state + ) + show_meta = self._show_meta(complete_state) + + def get_line(i: int) -> StyleAndTextTuples: + c = completions[i] + is_current_completion = i == index + result = _get_menu_item_fragments( + c, is_current_completion, menu_width, space_after=True + ) + + if show_meta: + result += self._get_menu_item_meta_fragments( + c, is_current_completion, menu_meta_width + ) + return result + + return UIContent( + get_line=get_line, + cursor_position=Point(x=0, y=index or 0), + line_count=len(completions), + ) + + return UIContent() + + def _show_meta(self, complete_state: CompletionState) -> bool: + """ + Return ``True`` if we need to show a column with meta information. + """ + return any(c.display_meta_text for c in complete_state.completions) + + def _get_menu_width(self, max_width: int, complete_state: CompletionState) -> int: + """ + Return the width of the main column. + """ + return min( + max_width, + max( + self.MIN_WIDTH, + max(get_cwidth(c.display_text) for c in complete_state.completions) + 2, + ), + ) + + def _get_menu_meta_width( + self, max_width: int, complete_state: CompletionState + ) -> int: + """ + Return the width of the meta column. + """ + + def meta_width(completion: Completion) -> int: + return get_cwidth(completion.display_meta_text) + + if self._show_meta(complete_state): + return min( + max_width, max(meta_width(c) for c in complete_state.completions) + 2 + ) + else: + return 0 + + def _get_menu_item_meta_fragments( + self, completion: Completion, is_current_completion: bool, width: int + ) -> StyleAndTextTuples: + + if is_current_completion: + style_str = "class:completion-menu.meta.completion.current" + else: + style_str = "class:completion-menu.meta.completion" + + text, tw = _trim_formatted_text(completion.display_meta, width - 2) + padding = " " * (width - 1 - tw) + + return to_formatted_text( + cast(StyleAndTextTuples, []) + [("", " ")] + text + [("", padding)], + style=style_str, + ) + + def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone": + """ + Handle mouse events: clicking and scrolling. + """ + b = get_app().current_buffer + + if mouse_event.event_type == MouseEventType.MOUSE_UP: + # Select completion. + b.go_to_completion(mouse_event.position.y) + b.complete_state = None + + elif mouse_event.event_type == MouseEventType.SCROLL_DOWN: + # Scroll up. + b.complete_next(count=3, disable_wrap_around=True) + + elif mouse_event.event_type == MouseEventType.SCROLL_UP: + # Scroll down. + b.complete_previous(count=3, disable_wrap_around=True) + + return None + + +def _get_menu_item_fragments( + completion: Completion, + is_current_completion: bool, + width: int, + space_after: bool = False, +) -> StyleAndTextTuples: + """ + Get the style/text tuples for a menu item, styled and trimmed to the given + width. + """ + if is_current_completion: + style_str = "class:completion-menu.completion.current %s %s" % ( + completion.style, + completion.selected_style, + ) + else: + style_str = "class:completion-menu.completion " + completion.style + + text, tw = _trim_formatted_text( + completion.display, (width - 2 if space_after else width - 1) + ) + + padding = " " * (width - 1 - tw) + + return to_formatted_text( + cast(StyleAndTextTuples, []) + [("", " ")] + text + [("", padding)], + style=style_str, + ) + + +def _trim_formatted_text( + formatted_text: StyleAndTextTuples, max_width: int +) -> Tuple[StyleAndTextTuples, int]: + """ + Trim the text to `max_width`, append dots when the text is too long. + Returns (text, width) tuple. + """ + width = fragment_list_width(formatted_text) + + # When the text is too wide, trim it. + if width > max_width: + result = [] # Text fragments. + remaining_width = max_width - 3 + + for style_and_ch in explode_text_fragments(formatted_text): + ch_width = get_cwidth(style_and_ch[1]) + + if ch_width <= remaining_width: + result.append(style_and_ch) + remaining_width -= ch_width + else: + break + + result.append(("", "...")) + + return result, max_width - remaining_width + else: + return formatted_text, width + + +class CompletionsMenu(ConditionalContainer): + # NOTE: We use a pretty big z_index by default. Menus are supposed to be + # above anything else. We also want to make sure that the content is + # visible at the point where we draw this menu. + def __init__( + self, + max_height: Optional[int] = None, + scroll_offset: Union[int, Callable[[], int]] = 0, + extra_filter: FilterOrBool = True, + display_arrows: FilterOrBool = False, + z_index: int = 10 ** 8, + ) -> None: + + extra_filter = to_filter(extra_filter) + display_arrows = to_filter(display_arrows) + + super().__init__( + content=Window( + content=CompletionsMenuControl(), + width=Dimension(min=8), + height=Dimension(min=1, max=max_height), + scroll_offsets=ScrollOffsets(top=scroll_offset, bottom=scroll_offset), + right_margins=[ScrollbarMargin(display_arrows=display_arrows)], + dont_extend_width=True, + style="class:completion-menu", + z_index=z_index, + ), + # Show when there are completions but not at the point we are + # returning the input. + filter=has_completions & ~is_done & extra_filter, + ) + + +class MultiColumnCompletionMenuControl(UIControl): + """ + Completion menu that displays all the completions in several columns. + When there are more completions than space for them to be displayed, an + arrow is shown on the left or right side. + + `min_rows` indicates how many rows will be available in any possible case. + When this is larger than one, it will try to use less columns and more + rows until this value is reached. + Be careful passing in a too big value, if less than the given amount of + rows are available, more columns would have been required, but + `preferred_width` doesn't know about that and reports a too small value. + This results in less completions displayed and additional scrolling. + (It's a limitation of how the layout engine currently works: first the + widths are calculated, then the heights.) + + :param suggested_max_column_width: The suggested max width of a column. + The column can still be bigger than this, but if there is place for two + columns of this width, we will display two columns. This to avoid that + if there is one very wide completion, that it doesn't significantly + reduce the amount of columns. + """ + + _required_margin = 3 # One extra padding on the right + space for arrows. + + def __init__(self, min_rows: int = 3, suggested_max_column_width: int = 30) -> None: + assert min_rows >= 1 + + self.min_rows = min_rows + self.suggested_max_column_width = suggested_max_column_width + self.scroll = 0 + + # Info of last rendering. + self._rendered_rows = 0 + self._rendered_columns = 0 + self._total_columns = 0 + self._render_pos_to_completion: Dict[Tuple[int, int], Completion] = {} + self._render_left_arrow = False + self._render_right_arrow = False + self._render_width = 0 + + def reset(self) -> None: + self.scroll = 0 + + def has_focus(self) -> bool: + return False + + def preferred_width(self, max_available_width: int) -> Optional[int]: + """ + Preferred width: prefer to use at least min_rows, but otherwise as much + as possible horizontally. + """ + complete_state = get_app().current_buffer.complete_state + if complete_state is None: + return 0 + + column_width = self._get_column_width(complete_state) + result = int( + column_width + * math.ceil(len(complete_state.completions) / float(self.min_rows)) + ) + + # When the desired width is still more than the maximum available, + # reduce by removing columns until we are less than the available + # width. + while ( + result > column_width + and result > max_available_width - self._required_margin + ): + result -= column_width + return result + self._required_margin + + def preferred_height( + self, + width: int, + max_available_height: int, + wrap_lines: bool, + get_line_prefix: Optional[GetLinePrefixCallable], + ) -> Optional[int]: + """ + Preferred height: as much as needed in order to display all the completions. + """ + complete_state = get_app().current_buffer.complete_state + if complete_state is None: + return 0 + + column_width = self._get_column_width(complete_state) + column_count = max(1, (width - self._required_margin) // column_width) + + return int(math.ceil(len(complete_state.completions) / float(column_count))) + + def create_content(self, width: int, height: int) -> UIContent: + """ + Create a UIContent object for this menu. + """ + complete_state = get_app().current_buffer.complete_state + if complete_state is None: + return UIContent() + + column_width = self._get_column_width(complete_state) + self._render_pos_to_completion = {} + + _T = TypeVar("_T") + + def grouper( + n: int, iterable: Iterable[_T], fillvalue: Optional[_T] = None + ) -> Iterable[List[_T]]: + " grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx " + args = [iter(iterable)] * n + return zip_longest(fillvalue=fillvalue, *args) + + def is_current_completion(completion: Completion) -> bool: + " Returns True when this completion is the currently selected one. " + return ( + complete_state is not None + and complete_state.complete_index is not None + and c == complete_state.current_completion + ) + + # Space required outside of the regular columns, for displaying the + # left and right arrow. + HORIZONTAL_MARGIN_REQUIRED = 3 + + # There should be at least one column, but it cannot be wider than + # the available width. + column_width = min(width - HORIZONTAL_MARGIN_REQUIRED, column_width) + + # However, when the columns tend to be very wide, because there are + # some very wide entries, shrink it anyway. + if column_width > self.suggested_max_column_width: + # `column_width` can still be bigger that `suggested_max_column_width`, + # but if there is place for two columns, we divide by two. + column_width //= column_width // self.suggested_max_column_width + + visible_columns = max(1, (width - self._required_margin) // column_width) + + columns_ = list(grouper(height, complete_state.completions)) + rows_ = list(zip(*columns_)) + + # Make sure the current completion is always visible: update scroll offset. + selected_column = (complete_state.complete_index or 0) // height + self.scroll = min( + selected_column, max(self.scroll, selected_column - visible_columns + 1) + ) + + render_left_arrow = self.scroll > 0 + render_right_arrow = self.scroll < len(rows_[0]) - visible_columns + + # Write completions to screen. + fragments_for_line = [] + + for row_index, row in enumerate(rows_): + fragments: StyleAndTextTuples = [] + middle_row = row_index == len(rows_) // 2 + + # Draw left arrow if we have hidden completions on the left. + if render_left_arrow: + fragments.append(("class:scrollbar", "<" if middle_row else " ")) + elif render_right_arrow: + # Reserve one column empty space. (If there is a right + # arrow right now, there can be a left arrow as well.) + fragments.append(("", " ")) + + # Draw row content. + for column_index, c in enumerate(row[self.scroll :][:visible_columns]): + if c is not None: + fragments += _get_menu_item_fragments( + c, is_current_completion(c), column_width, space_after=False + ) + + # Remember render position for mouse click handler. + for x in range(column_width): + self._render_pos_to_completion[ + (column_index * column_width + x, row_index) + ] = c + else: + fragments.append(("class:completion", " " * column_width)) + + # Draw trailing padding for this row. + # (_get_menu_item_fragments only returns padding on the left.) + if render_left_arrow or render_right_arrow: + fragments.append(("class:completion", " ")) + + # Draw right arrow if we have hidden completions on the right. + if render_right_arrow: + fragments.append(("class:scrollbar", ">" if middle_row else " ")) + elif render_left_arrow: + fragments.append(("class:completion", " ")) + + # Add line. + fragments_for_line.append( + to_formatted_text(fragments, style="class:completion-menu") + ) + + self._rendered_rows = height + self._rendered_columns = visible_columns + self._total_columns = len(columns_) + self._render_left_arrow = render_left_arrow + self._render_right_arrow = render_right_arrow + self._render_width = ( + column_width * visible_columns + render_left_arrow + render_right_arrow + 1 + ) + + def get_line(i: int) -> StyleAndTextTuples: + return fragments_for_line[i] + + return UIContent(get_line=get_line, line_count=len(rows_)) + + def _get_column_width(self, complete_state: CompletionState) -> int: + """ + Return the width of each column. + """ + return max(get_cwidth(c.display_text) for c in complete_state.completions) + 1 + + def mouse_handler(self, mouse_event: MouseEvent) -> "NotImplementedOrNone": + """ + Handle scroll and click events. + """ + b = get_app().current_buffer + + def scroll_left() -> None: + b.complete_previous(count=self._rendered_rows, disable_wrap_around=True) + self.scroll = max(0, self.scroll - 1) + + def scroll_right() -> None: + b.complete_next(count=self._rendered_rows, disable_wrap_around=True) + self.scroll = min( + self._total_columns - self._rendered_columns, self.scroll + 1 + ) + + if mouse_event.event_type == MouseEventType.SCROLL_DOWN: + scroll_right() + + elif mouse_event.event_type == MouseEventType.SCROLL_UP: + scroll_left() + + elif mouse_event.event_type == MouseEventType.MOUSE_UP: + x = mouse_event.position.x + y = mouse_event.position.y + + # Mouse click on left arrow. + if x == 0: + if self._render_left_arrow: + scroll_left() + + # Mouse click on right arrow. + elif x == self._render_width - 1: + if self._render_right_arrow: + scroll_right() + + # Mouse click on completion. + else: + completion = self._render_pos_to_completion.get((x, y)) + if completion: + b.apply_completion(completion) + + return None + + def get_key_bindings(self) -> "KeyBindings": + """ + Expose key bindings that handle the left/right arrow keys when the menu + is displayed. + """ + from prompt_toolkit.key_binding.key_bindings import KeyBindings + + kb = KeyBindings() + + @Condition + def filter() -> bool: + " Only handle key bindings if this menu is visible. " + app = get_app() + complete_state = app.current_buffer.complete_state + + # There need to be completions, and one needs to be selected. + if complete_state is None or complete_state.complete_index is None: + return False + + # This menu needs to be visible. + return any(window.content == self for window in app.layout.visible_windows) + + def move(right: bool = False) -> None: + buff = get_app().current_buffer + complete_state = buff.complete_state + + if complete_state is not None and complete_state.complete_index is not None: + # Calculate new complete index. + new_index = complete_state.complete_index + if right: + new_index += self._rendered_rows + else: + new_index -= self._rendered_rows + + if 0 <= new_index < len(complete_state.completions): + buff.go_to_completion(new_index) + + # NOTE: the is_global is required because the completion menu will + # never be focussed. + + @kb.add("left", is_global=True, filter=filter) + def _left(event: E) -> None: + move() + + @kb.add("right", is_global=True, filter=filter) + def _right(event: E) -> None: + move(True) + + return kb + + +class MultiColumnCompletionsMenu(HSplit): + """ + Container that displays the completions in several columns. + When `show_meta` (a :class:`~prompt_toolkit.filters.Filter`) evaluates + to True, it shows the meta information at the bottom. + """ + + def __init__( + self, + min_rows: int = 3, + suggested_max_column_width: int = 30, + show_meta: FilterOrBool = True, + extra_filter: FilterOrBool = True, + z_index: int = 10 ** 8, + ) -> None: + + show_meta = to_filter(show_meta) + extra_filter = to_filter(extra_filter) + + # Display filter: show when there are completions but not at the point + # we are returning the input. + full_filter = has_completions & ~is_done & extra_filter + + @Condition + def any_completion_has_meta() -> bool: + complete_state = get_app().current_buffer.complete_state + return complete_state is not None and any( + c.display_meta for c in complete_state.completions + ) + + # Create child windows. + # NOTE: We don't set style='class:completion-menu' to the + # `MultiColumnCompletionMenuControl`, because this is used in a + # Float that is made transparent, and the size of the control + # doesn't always correspond exactly with the size of the + # generated content. + completions_window = ConditionalContainer( + content=Window( + content=MultiColumnCompletionMenuControl( + min_rows=min_rows, + suggested_max_column_width=suggested_max_column_width, + ), + width=Dimension(min=8), + height=Dimension(min=1), + ), + filter=full_filter, + ) + + meta_window = ConditionalContainer( + content=Window(content=_SelectedCompletionMetaControl()), + filter=show_meta & full_filter & any_completion_has_meta, + ) + + # Initialise split. + super().__init__([completions_window, meta_window], z_index=z_index) + + +class _SelectedCompletionMetaControl(UIControl): + """ + Control that shows the meta information of the selected completion. + """ + + def preferred_width(self, max_available_width: int) -> Optional[int]: + """ + Report the width of the longest meta text as the preferred width of this control. + + It could be that we use less width, but this way, we're sure that the + layout doesn't change when we select another completion (E.g. that + completions are suddenly shown in more or fewer columns.) + """ + app = get_app() + if app.current_buffer.complete_state: + state = app.current_buffer.complete_state + return 2 + max(get_cwidth(c.display_meta_text) for c in state.completions) + else: + return 0 + + def preferred_height( + self, + width: int, + max_available_height: int, + wrap_lines: bool, + get_line_prefix: Optional[GetLinePrefixCallable], + ) -> Optional[int]: + return 1 + + def create_content(self, width: int, height: int) -> UIContent: + fragments = self._get_text_fragments() + + def get_line(i: int) -> StyleAndTextTuples: + return fragments + + return UIContent(get_line=get_line, line_count=1 if fragments else 0) + + def _get_text_fragments(self) -> StyleAndTextTuples: + style = "class:completion-menu.multi-column-meta" + state = get_app().current_buffer.complete_state + + if ( + state + and state.current_completion + and state.current_completion.display_meta_text + ): + return to_formatted_text( + cast(StyleAndTextTuples, [("", " ")]) + + state.current_completion.display_meta + + [("", " ")], + style=style, + ) + + return [] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/mouse_handlers.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/mouse_handlers.py new file mode 100644 index 00000000..754897dc --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/mouse_handlers.py @@ -0,0 +1,40 @@ +from collections import defaultdict +from itertools import product +from typing import Callable, DefaultDict, Tuple + +from prompt_toolkit.mouse_events import MouseEvent + +__all__ = [ + "MouseHandlers", +] + + +class MouseHandlers: + """ + Two dimensional raster of callbacks for mouse events. + """ + + def __init__(self) -> None: + def dummy_callback(mouse_event: MouseEvent) -> None: + """ + :param mouse_event: `MouseEvent` instance. + """ + + # Map (x,y) tuples to handlers. + self.mouse_handlers: DefaultDict[ + Tuple[int, int], Callable[[MouseEvent], None] + ] = defaultdict(lambda: dummy_callback) + + def set_mouse_handler_for_range( + self, + x_min: int, + x_max: int, + y_min: int, + y_max: int, + handler: Callable[[MouseEvent], None], + ) -> None: + """ + Set mouse handler for a region. + """ + for x, y in product(range(x_min, x_max), range(y_min, y_max)): + self.mouse_handlers[x, y] = handler diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/processors.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/processors.py new file mode 100644 index 00000000..a8f93b0e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/processors.py @@ -0,0 +1,1029 @@ +""" +Processors are little transformation blocks that transform the fragments list +from a buffer before the BufferControl will render it to the screen. + +They can insert fragments before or after, or highlight fragments by replacing the +fragment types. +""" +import re +from abc import ABCMeta, abstractmethod +from typing import ( + TYPE_CHECKING, + Callable, + Hashable, + List, + Optional, + Tuple, + Type, + Union, + cast, +) + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.cache import SimpleCache +from prompt_toolkit.document import Document +from prompt_toolkit.filters import FilterOrBool, to_filter, vi_insert_multiple_mode +from prompt_toolkit.formatted_text import ( + AnyFormattedText, + StyleAndTextTuples, + to_formatted_text, +) +from prompt_toolkit.formatted_text.utils import fragment_list_len, fragment_list_to_text +from prompt_toolkit.search import SearchDirection +from prompt_toolkit.utils import to_int, to_str + +from .utils import explode_text_fragments + +if TYPE_CHECKING: + from .controls import BufferControl, UIContent + +__all__ = [ + "Processor", + "TransformationInput", + "Transformation", + "DummyProcessor", + "HighlightSearchProcessor", + "HighlightIncrementalSearchProcessor", + "HighlightSelectionProcessor", + "PasswordProcessor", + "HighlightMatchingBracketProcessor", + "DisplayMultipleCursors", + "BeforeInput", + "ShowArg", + "AfterInput", + "AppendAutoSuggestion", + "ConditionalProcessor", + "ShowLeadingWhiteSpaceProcessor", + "ShowTrailingWhiteSpaceProcessor", + "TabsProcessor", + "ReverseSearchProcessor", + "DynamicProcessor", + "merge_processors", +] + + +class Processor(metaclass=ABCMeta): + """ + Manipulate the fragments for a given line in a + :class:`~prompt_toolkit.layout.controls.BufferControl`. + """ + + @abstractmethod + def apply_transformation( + self, transformation_input: "TransformationInput" + ) -> "Transformation": + """ + Apply transformation. Returns a :class:`.Transformation` instance. + + :param transformation_input: :class:`.TransformationInput` object. + """ + return Transformation(transformation_input.fragments) + + +SourceToDisplay = Callable[[int], int] +DisplayToSource = Callable[[int], int] + + +class TransformationInput: + """ + :param control: :class:`.BufferControl` instance. + :param lineno: The number of the line to which we apply the processor. + :param source_to_display: A function that returns the position in the + `fragments` for any position in the source string. (This takes + previous processors into account.) + :param fragments: List of fragments that we can transform. (Received from the + previous processor.) + """ + + def __init__( + self, + buffer_control: "BufferControl", + document: Document, + lineno: int, + source_to_display: SourceToDisplay, + fragments: StyleAndTextTuples, + width: int, + height: int, + ) -> None: + + self.buffer_control = buffer_control + self.document = document + self.lineno = lineno + self.source_to_display = source_to_display + self.fragments = fragments + self.width = width + self.height = height + + def unpack( + self, + ) -> Tuple[ + "BufferControl", Document, int, SourceToDisplay, StyleAndTextTuples, int, int + ]: + return ( + self.buffer_control, + self.document, + self.lineno, + self.source_to_display, + self.fragments, + self.width, + self.height, + ) + + +class Transformation: + """ + Transformation result, as returned by :meth:`.Processor.apply_transformation`. + + Important: Always make sure that the length of `document.text` is equal to + the length of all the text in `fragments`! + + :param fragments: The transformed fragments. To be displayed, or to pass to + the next processor. + :param source_to_display: Cursor position transformation from original + string to transformed string. + :param display_to_source: Cursor position transformed from source string to + original string. + """ + + def __init__( + self, + fragments: StyleAndTextTuples, + source_to_display: Optional[SourceToDisplay] = None, + display_to_source: Optional[DisplayToSource] = None, + ) -> None: + + self.fragments = fragments + self.source_to_display = source_to_display or (lambda i: i) + self.display_to_source = display_to_source or (lambda i: i) + + +class DummyProcessor(Processor): + """ + A `Processor` that doesn't do anything. + """ + + def apply_transformation( + self, transformation_input: TransformationInput + ) -> Transformation: + return Transformation(transformation_input.fragments) + + +class HighlightSearchProcessor(Processor): + """ + Processor that highlights search matches in the document. + Note that this doesn't support multiline search matches yet. + + The style classes 'search' and 'search.current' will be applied to the + content. + """ + + _classname = "search" + _classname_current = "search.current" + + def _get_search_text(self, buffer_control: "BufferControl") -> str: + """ + The text we are searching for. + """ + return buffer_control.search_state.text + + def apply_transformation( + self, transformation_input: TransformationInput + ) -> Transformation: + + ( + buffer_control, + document, + lineno, + source_to_display, + fragments, + _, + _, + ) = transformation_input.unpack() + + search_text = self._get_search_text(buffer_control) + searchmatch_fragment = " class:%s " % (self._classname,) + searchmatch_current_fragment = " class:%s " % (self._classname_current,) + + if search_text and not get_app().is_done: + # For each search match, replace the style string. + line_text = fragment_list_to_text(fragments) + fragments = explode_text_fragments(fragments) + + if buffer_control.search_state.ignore_case(): + flags = re.IGNORECASE + else: + flags = re.RegexFlag(0) + + # Get cursor column. + cursor_column: Optional[int] + if document.cursor_position_row == lineno: + cursor_column = source_to_display(document.cursor_position_col) + else: + cursor_column = None + + for match in re.finditer(re.escape(search_text), line_text, flags=flags): + if cursor_column is not None: + on_cursor = match.start() <= cursor_column < match.end() + else: + on_cursor = False + + for i in range(match.start(), match.end()): + old_fragment, text, *_ = fragments[i] + if on_cursor: + fragments[i] = ( + old_fragment + searchmatch_current_fragment, + fragments[i][1], + ) + else: + fragments[i] = ( + old_fragment + searchmatch_fragment, + fragments[i][1], + ) + + return Transformation(fragments) + + +class HighlightIncrementalSearchProcessor(HighlightSearchProcessor): + """ + Highlight the search terms that are used for highlighting the incremental + search. The style class 'incsearch' will be applied to the content. + + Important: this requires the `preview_search=True` flag to be set for the + `BufferControl`. Otherwise, the cursor position won't be set to the search + match while searching, and nothing happens. + """ + + _classname = "incsearch" + _classname_current = "incsearch.current" + + def _get_search_text(self, buffer_control: "BufferControl") -> str: + """ + The text we are searching for. + """ + # When the search buffer has focus, take that text. + search_buffer = buffer_control.search_buffer + if search_buffer is not None and search_buffer.text: + return search_buffer.text + return "" + + +class HighlightSelectionProcessor(Processor): + """ + Processor that highlights the selection in the document. + """ + + def apply_transformation( + self, transformation_input: TransformationInput + ) -> Transformation: + ( + buffer_control, + document, + lineno, + source_to_display, + fragments, + _, + _, + ) = transformation_input.unpack() + + selected_fragment = " class:selected " + + # In case of selection, highlight all matches. + selection_at_line = document.selection_range_at_line(lineno) + + if selection_at_line: + from_, to = selection_at_line + from_ = source_to_display(from_) + to = source_to_display(to) + + fragments = explode_text_fragments(fragments) + + if from_ == 0 and to == 0 and len(fragments) == 0: + # When this is an empty line, insert a space in order to + # visualise the selection. + return Transformation([(selected_fragment, " ")]) + else: + for i in range(from_, to): + if i < len(fragments): + old_fragment, old_text, *_ = fragments[i] + fragments[i] = (old_fragment + selected_fragment, old_text) + elif i == len(fragments): + fragments.append((selected_fragment, " ")) + + return Transformation(fragments) + + +class PasswordProcessor(Processor): + """ + Processor that turns masks the input. (For passwords.) + + :param char: (string) Character to be used. "*" by default. + """ + + def __init__(self, char: str = "*") -> None: + self.char = char + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + fragments: StyleAndTextTuples = cast( + StyleAndTextTuples, + [ + (style, self.char * len(text), *handler) + for style, text, *handler in ti.fragments + ], + ) + + return Transformation(fragments) + + +class HighlightMatchingBracketProcessor(Processor): + """ + When the cursor is on or right after a bracket, it highlights the matching + bracket. + + :param max_cursor_distance: Only highlight matching brackets when the + cursor is within this distance. (From inside a `Processor`, we can't + know which lines will be visible on the screen. But we also don't want + to scan the whole document for matching brackets on each key press, so + we limit to this value.) + """ + + _closing_braces = "])}>" + + def __init__( + self, chars: str = "[](){}<>", max_cursor_distance: int = 1000 + ) -> None: + self.chars = chars + self.max_cursor_distance = max_cursor_distance + + self._positions_cache: SimpleCache[ + Hashable, List[Tuple[int, int]] + ] = SimpleCache(maxsize=8) + + def _get_positions_to_highlight(self, document: Document) -> List[Tuple[int, int]]: + """ + Return a list of (row, col) tuples that need to be highlighted. + """ + pos: Optional[int] + + # Try for the character under the cursor. + if document.current_char and document.current_char in self.chars: + pos = document.find_matching_bracket_position( + start_pos=document.cursor_position - self.max_cursor_distance, + end_pos=document.cursor_position + self.max_cursor_distance, + ) + + # Try for the character before the cursor. + elif ( + document.char_before_cursor + and document.char_before_cursor in self._closing_braces + and document.char_before_cursor in self.chars + ): + document = Document(document.text, document.cursor_position - 1) + + pos = document.find_matching_bracket_position( + start_pos=document.cursor_position - self.max_cursor_distance, + end_pos=document.cursor_position + self.max_cursor_distance, + ) + else: + pos = None + + # Return a list of (row, col) tuples that need to be highlighted. + if pos: + pos += document.cursor_position # pos is relative. + row, col = document.translate_index_to_position(pos) + return [ + (row, col), + (document.cursor_position_row, document.cursor_position_col), + ] + else: + return [] + + def apply_transformation( + self, transformation_input: TransformationInput + ) -> Transformation: + + ( + buffer_control, + document, + lineno, + source_to_display, + fragments, + _, + _, + ) = transformation_input.unpack() + + # When the application is in the 'done' state, don't highlight. + if get_app().is_done: + return Transformation(fragments) + + # Get the highlight positions. + key = (get_app().render_counter, document.text, document.cursor_position) + positions = self._positions_cache.get( + key, lambda: self._get_positions_to_highlight(document) + ) + + # Apply if positions were found at this line. + if positions: + for row, col in positions: + if row == lineno: + col = source_to_display(col) + fragments = explode_text_fragments(fragments) + style, text, *_ = fragments[col] + + if col == document.cursor_position_col: + style += " class:matching-bracket.cursor " + else: + style += " class:matching-bracket.other " + + fragments[col] = (style, text) + + return Transformation(fragments) + + +class DisplayMultipleCursors(Processor): + """ + When we're in Vi block insert mode, display all the cursors. + """ + + def apply_transformation( + self, transformation_input: TransformationInput + ) -> Transformation: + + ( + buffer_control, + document, + lineno, + source_to_display, + fragments, + _, + _, + ) = transformation_input.unpack() + + buff = buffer_control.buffer + + if vi_insert_multiple_mode(): + cursor_positions = buff.multiple_cursor_positions + fragments = explode_text_fragments(fragments) + + # If any cursor appears on the current line, highlight that. + start_pos = document.translate_row_col_to_index(lineno, 0) + end_pos = start_pos + len(document.lines[lineno]) + + fragment_suffix = " class:multiple-cursors" + + for p in cursor_positions: + if start_pos <= p <= end_pos: + column = source_to_display(p - start_pos) + + # Replace fragment. + try: + style, text, *_ = fragments[column] + except IndexError: + # Cursor needs to be displayed after the current text. + fragments.append((fragment_suffix, " ")) + else: + style += fragment_suffix + fragments[column] = (style, text) + + return Transformation(fragments) + else: + return Transformation(fragments) + + +class BeforeInput(Processor): + """ + Insert text before the input. + + :param text: This can be either plain text or formatted text + (or a callable that returns any of those). + :param style: style to be applied to this prompt/prefix. + """ + + def __init__(self, text: AnyFormattedText, style: str = "") -> None: + self.text = text + self.style = style + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + source_to_display: Optional[SourceToDisplay] + display_to_source: Optional[DisplayToSource] + + if ti.lineno == 0: + # Get fragments. + fragments_before = to_formatted_text(self.text, self.style) + fragments = fragments_before + ti.fragments + + shift_position = fragment_list_len(fragments_before) + source_to_display = lambda i: i + shift_position + display_to_source = lambda i: i - shift_position + else: + fragments = ti.fragments + source_to_display = None + display_to_source = None + + return Transformation( + fragments, + source_to_display=source_to_display, + display_to_source=display_to_source, + ) + + def __repr__(self) -> str: + return "BeforeInput(%r, %r)" % (self.text, self.style) + + +class ShowArg(BeforeInput): + """ + Display the 'arg' in front of the input. + + This was used by the `PromptSession`, but now it uses the + `Window.get_line_prefix` function instead. + """ + + def __init__(self) -> None: + super().__init__(self._get_text_fragments) + + def _get_text_fragments(self) -> StyleAndTextTuples: + app = get_app() + if app.key_processor.arg is None: + return [] + else: + arg = app.key_processor.arg + + return [ + ("class:prompt.arg", "(arg: "), + ("class:prompt.arg.text", str(arg)), + ("class:prompt.arg", ") "), + ] + + def __repr__(self) -> str: + return "ShowArg()" + + +class AfterInput(Processor): + """ + Insert text after the input. + + :param text: This can be either plain text or formatted text + (or a callable that returns any of those). + :param style: style to be applied to this prompt/prefix. + """ + + def __init__(self, text: AnyFormattedText, style: str = "") -> None: + self.text = text + self.style = style + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + # Insert fragments after the last line. + if ti.lineno == ti.document.line_count - 1: + # Get fragments. + fragments_after = to_formatted_text(self.text, self.style) + return Transformation(fragments=ti.fragments + fragments_after) + else: + return Transformation(fragments=ti.fragments) + + def __repr__(self) -> str: + return "%s(%r, style=%r)" % (self.__class__.__name__, self.text, self.style) + + +class AppendAutoSuggestion(Processor): + """ + Append the auto suggestion to the input. + (The user can then press the right arrow the insert the suggestion.) + """ + + def __init__(self, style: str = "class:auto-suggestion") -> None: + self.style = style + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + # Insert fragments after the last line. + if ti.lineno == ti.document.line_count - 1: + buffer = ti.buffer_control.buffer + + if buffer.suggestion and ti.document.is_cursor_at_the_end: + suggestion = buffer.suggestion.text + else: + suggestion = "" + + return Transformation(fragments=ti.fragments + [(self.style, suggestion)]) + else: + return Transformation(fragments=ti.fragments) + + +class ShowLeadingWhiteSpaceProcessor(Processor): + """ + Make leading whitespace visible. + + :param get_char: Callable that returns one character. + """ + + def __init__( + self, + get_char: Optional[Callable[[], str]] = None, + style: str = "class:leading-whitespace", + ) -> None: + def default_get_char() -> str: + if "\xb7".encode(get_app().output.encoding(), "replace") == b"?": + return "." + else: + return "\xb7" + + self.style = style + self.get_char = get_char or default_get_char + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + fragments = ti.fragments + + # Walk through all te fragments. + if fragments and fragment_list_to_text(fragments).startswith(" "): + t = (self.style, self.get_char()) + fragments = explode_text_fragments(fragments) + + for i in range(len(fragments)): + if fragments[i][1] == " ": + fragments[i] = t + else: + break + + return Transformation(fragments) + + +class ShowTrailingWhiteSpaceProcessor(Processor): + """ + Make trailing whitespace visible. + + :param get_char: Callable that returns one character. + """ + + def __init__( + self, + get_char: Optional[Callable[[], str]] = None, + style: str = "class:training-whitespace", + ) -> None: + def default_get_char() -> str: + if "\xb7".encode(get_app().output.encoding(), "replace") == b"?": + return "." + else: + return "\xb7" + + self.style = style + self.get_char = get_char or default_get_char + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + fragments = ti.fragments + + if fragments and fragments[-1][1].endswith(" "): + t = (self.style, self.get_char()) + fragments = explode_text_fragments(fragments) + + # Walk backwards through all te fragments and replace whitespace. + for i in range(len(fragments) - 1, -1, -1): + char = fragments[i][1] + if char == " ": + fragments[i] = t + else: + break + + return Transformation(fragments) + + +class TabsProcessor(Processor): + """ + Render tabs as spaces (instead of ^I) or make them visible (for instance, + by replacing them with dots.) + + :param tabstop: Horizontal space taken by a tab. (`int` or callable that + returns an `int`). + :param char1: Character or callable that returns a character (text of + length one). This one is used for the first space taken by the tab. + :param char2: Like `char1`, but for the rest of the space. + """ + + def __init__( + self, + tabstop: Union[int, Callable[[], int]] = 4, + char1: Union[str, Callable[[], str]] = "|", + char2: Union[str, Callable[[], str]] = "\u2508", + style: str = "class:tab", + ) -> None: + + self.char1 = char1 + self.char2 = char2 + self.tabstop = tabstop + self.style = style + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + tabstop = to_int(self.tabstop) + style = self.style + + # Create separator for tabs. + separator1 = to_str(self.char1) + separator2 = to_str(self.char2) + + # Transform fragments. + fragments = explode_text_fragments(ti.fragments) + + position_mappings = {} + result_fragments: StyleAndTextTuples = [] + pos = 0 + + for i, fragment_and_text in enumerate(fragments): + position_mappings[i] = pos + + if fragment_and_text[1] == "\t": + # Calculate how many characters we have to insert. + count = tabstop - (pos % tabstop) + if count == 0: + count = tabstop + + # Insert tab. + result_fragments.append((style, separator1)) + result_fragments.append((style, separator2 * (count - 1))) + pos += count + else: + result_fragments.append(fragment_and_text) + pos += 1 + + position_mappings[len(fragments)] = pos + # Add `pos+1` to mapping, because the cursor can be right after the + # line as well. + position_mappings[len(fragments) + 1] = pos + 1 + + def source_to_display(from_position: int) -> int: + " Maps original cursor position to the new one. " + return position_mappings[from_position] + + def display_to_source(display_pos: int) -> int: + " Maps display cursor position to the original one. " + position_mappings_reversed = {v: k for k, v in position_mappings.items()} + + while display_pos >= 0: + try: + return position_mappings_reversed[display_pos] + except KeyError: + display_pos -= 1 + return 0 + + return Transformation( + result_fragments, + source_to_display=source_to_display, + display_to_source=display_to_source, + ) + + +class ReverseSearchProcessor(Processor): + """ + Process to display the "(reverse-i-search)`...`:..." stuff around + the search buffer. + + Note: This processor is meant to be applied to the BufferControl that + contains the search buffer, it's not meant for the original input. + """ + + _excluded_input_processors: List[Type[Processor]] = [ + HighlightSearchProcessor, + HighlightSelectionProcessor, + BeforeInput, + AfterInput, + ] + + def _get_main_buffer( + self, buffer_control: "BufferControl" + ) -> Optional["BufferControl"]: + from prompt_toolkit.layout.controls import BufferControl + + prev_control = get_app().layout.search_target_buffer_control + if ( + isinstance(prev_control, BufferControl) + and prev_control.search_buffer_control == buffer_control + ): + return prev_control + return None + + def _content( + self, main_control: "BufferControl", ti: TransformationInput + ) -> "UIContent": + from prompt_toolkit.layout.controls import BufferControl + + # Emulate the BufferControl through which we are searching. + # For this we filter out some of the input processors. + excluded_processors = tuple(self._excluded_input_processors) + + def filter_processor(item: Processor) -> Optional[Processor]: + """Filter processors from the main control that we want to disable + here. This returns either an accepted processor or None.""" + # For a `_MergedProcessor`, check each individual processor, recursively. + if isinstance(item, _MergedProcessor): + accepted_processors = [filter_processor(p) for p in item.processors] + return merge_processors( + [p for p in accepted_processors if p is not None] + ) + + # For a `ConditionalProcessor`, check the body. + elif isinstance(item, ConditionalProcessor): + p = filter_processor(item.processor) + if p: + return ConditionalProcessor(p, item.filter) + + # Otherwise, check the processor itself. + else: + if not isinstance(item, excluded_processors): + return item + + return None + + filtered_processor = filter_processor( + merge_processors(main_control.input_processors or []) + ) + highlight_processor = HighlightIncrementalSearchProcessor() + + if filtered_processor: + new_processors = [filtered_processor, highlight_processor] + else: + new_processors = [highlight_processor] + + from .controls import SearchBufferControl + + assert isinstance(ti.buffer_control, SearchBufferControl) + + buffer_control = BufferControl( + buffer=main_control.buffer, + input_processors=new_processors, + include_default_input_processors=False, + lexer=main_control.lexer, + preview_search=True, + search_buffer_control=cast(SearchBufferControl, ti.buffer_control), + ) + + return buffer_control.create_content(ti.width, ti.height, preview_search=True) + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + from .controls import SearchBufferControl + + assert isinstance( + ti.buffer_control, SearchBufferControl + ), "`ReverseSearchProcessor` should be applied to a `SearchBufferControl` only." + + source_to_display: Optional[SourceToDisplay] + display_to_source: Optional[DisplayToSource] + + main_control = self._get_main_buffer(ti.buffer_control) + + if ti.lineno == 0 and main_control: + content = self._content(main_control, ti) + + # Get the line from the original document for this search. + line_fragments = content.get_line(content.cursor_position.y) + + if main_control.search_state.direction == SearchDirection.FORWARD: + direction_text = "i-search" + else: + direction_text = "reverse-i-search" + + fragments_before: StyleAndTextTuples = [ + ("class:prompt.search", "("), + ("class:prompt.search", direction_text), + ("class:prompt.search", ")`"), + ] + + fragments = ( + fragments_before + + [ + ("class:prompt.search.text", fragment_list_to_text(ti.fragments)), + ("", "': "), + ] + + line_fragments + ) + + shift_position = fragment_list_len(fragments_before) + source_to_display = lambda i: i + shift_position + display_to_source = lambda i: i - shift_position + else: + source_to_display = None + display_to_source = None + fragments = ti.fragments + + return Transformation( + fragments, + source_to_display=source_to_display, + display_to_source=display_to_source, + ) + + +class ConditionalProcessor(Processor): + """ + Processor that applies another processor, according to a certain condition. + Example:: + + # Create a function that returns whether or not the processor should + # currently be applied. + def highlight_enabled(): + return true_or_false + + # Wrapped it in a `ConditionalProcessor` for usage in a `BufferControl`. + BufferControl(input_processors=[ + ConditionalProcessor(HighlightSearchProcessor(), + Condition(highlight_enabled))]) + + :param processor: :class:`.Processor` instance. + :param filter: :class:`~prompt_toolkit.filters.Filter` instance. + """ + + def __init__(self, processor: Processor, filter: FilterOrBool) -> None: + self.processor = processor + self.filter = to_filter(filter) + + def apply_transformation( + self, transformation_input: TransformationInput + ) -> Transformation: + # Run processor when enabled. + if self.filter(): + return self.processor.apply_transformation(transformation_input) + else: + return Transformation(transformation_input.fragments) + + def __repr__(self) -> str: + return "%s(processor=%r, filter=%r)" % ( + self.__class__.__name__, + self.processor, + self.filter, + ) + + +class DynamicProcessor(Processor): + """ + Processor class that can dynamically returns any Processor. + + :param get_processor: Callable that returns a :class:`.Processor` instance. + """ + + def __init__(self, get_processor: Callable[[], Optional[Processor]]) -> None: + self.get_processor = get_processor + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + processor = self.get_processor() or DummyProcessor() + return processor.apply_transformation(ti) + + +def merge_processors(processors: List[Processor]) -> Processor: + """ + Merge multiple `Processor` objects into one. + """ + if len(processors) == 0: + return DummyProcessor() + + if len(processors) == 1: + return processors[0] # Nothing to merge. + + return _MergedProcessor(processors) + + +class _MergedProcessor(Processor): + """ + Processor that groups multiple other `Processor` objects, but exposes an + API as if it is one `Processor`. + """ + + def __init__(self, processors: List[Processor]): + self.processors = processors + + def apply_transformation(self, ti: TransformationInput) -> Transformation: + source_to_display_functions = [ti.source_to_display] + display_to_source_functions = [] + fragments = ti.fragments + + def source_to_display(i: int) -> int: + """Translate x position from the buffer to the x position in the + processor fragments list.""" + for f in source_to_display_functions: + i = f(i) + return i + + for p in self.processors: + transformation = p.apply_transformation( + TransformationInput( + ti.buffer_control, + ti.document, + ti.lineno, + source_to_display, + fragments, + ti.width, + ti.height, + ) + ) + fragments = transformation.fragments + display_to_source_functions.append(transformation.display_to_source) + source_to_display_functions.append(transformation.source_to_display) + + def display_to_source(i: int) -> int: + for f in reversed(display_to_source_functions): + i = f(i) + return i + + # In the case of a nested _MergedProcessor, each processor wants to + # receive a 'source_to_display' function (as part of the + # TransformationInput) that has everything in the chain before + # included, because it can be called as part of the + # `apply_transformation` function. However, this first + # `source_to_display` should not be part of the output that we are + # returning. (This is the most consistent with `display_to_source`.) + del source_to_display_functions[:1] + + return Transformation(fragments, source_to_display, display_to_source) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/screen.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/screen.py new file mode 100644 index 00000000..ca311955 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/screen.py @@ -0,0 +1,316 @@ +from collections import defaultdict +from typing import TYPE_CHECKING, Callable, DefaultDict, Dict, List, Optional, Tuple + +from prompt_toolkit.cache import FastDictCache +from prompt_toolkit.data_structures import Point +from prompt_toolkit.utils import get_cwidth + +if TYPE_CHECKING: + from .containers import Window + + +__all__ = [ + "Screen", + "Char", +] + + +class Char: + """ + Represent a single character in a :class:`.Screen`. + + This should be considered immutable. + + :param char: A single character (can be a double-width character). + :param style: A style string. (Can contain classnames.) + """ + + __slots__ = ("char", "style", "width") + + # If we end up having one of these special control sequences in the input string, + # we should display them as follows: + # Usually this happens after a "quoted insert". + display_mappings: Dict[str, str] = { + "\x00": "^@", # Control space + "\x01": "^A", + "\x02": "^B", + "\x03": "^C", + "\x04": "^D", + "\x05": "^E", + "\x06": "^F", + "\x07": "^G", + "\x08": "^H", + "\x09": "^I", + "\x0a": "^J", + "\x0b": "^K", + "\x0c": "^L", + "\x0d": "^M", + "\x0e": "^N", + "\x0f": "^O", + "\x10": "^P", + "\x11": "^Q", + "\x12": "^R", + "\x13": "^S", + "\x14": "^T", + "\x15": "^U", + "\x16": "^V", + "\x17": "^W", + "\x18": "^X", + "\x19": "^Y", + "\x1a": "^Z", + "\x1b": "^[", # Escape + "\x1c": "^\\", + "\x1d": "^]", + "\x1f": "^_", + "\x7f": "^?", # ASCII Delete (backspace). + # Special characters. All visualized like Vim does. + "\x80": "<80>", + "\x81": "<81>", + "\x82": "<82>", + "\x83": "<83>", + "\x84": "<84>", + "\x85": "<85>", + "\x86": "<86>", + "\x87": "<87>", + "\x88": "<88>", + "\x89": "<89>", + "\x8a": "<8a>", + "\x8b": "<8b>", + "\x8c": "<8c>", + "\x8d": "<8d>", + "\x8e": "<8e>", + "\x8f": "<8f>", + "\x90": "<90>", + "\x91": "<91>", + "\x92": "<92>", + "\x93": "<93>", + "\x94": "<94>", + "\x95": "<95>", + "\x96": "<96>", + "\x97": "<97>", + "\x98": "<98>", + "\x99": "<99>", + "\x9a": "<9a>", + "\x9b": "<9b>", + "\x9c": "<9c>", + "\x9d": "<9d>", + "\x9e": "<9e>", + "\x9f": "<9f>", + # For the non-breaking space: visualize like Emacs does by default. + # (Print a space, but attach the 'nbsp' class that applies the + # underline style.) + "\xa0": " ", + } + + def __init__(self, char: str = " ", style: str = ""): + # If this character has to be displayed otherwise, take that one. + if char in self.display_mappings: + if char == "\xa0": + style += " class:nbsp " # Will be underlined. + else: + style += " class:control-character " + + char = self.display_mappings[char] + + self.char = char + self.style = style + + # Calculate width. (We always need this, so better to store it directly + # as a member for performance.) + self.width = get_cwidth(char) + + def __eq__(self, other) -> bool: + return self.char == other.char and self.style == other.style + + def __ne__(self, other) -> bool: + # Not equal: We don't do `not char.__eq__` here, because of the + # performance of calling yet another function. + return self.char != other.char or self.style != other.style + + def __repr__(self) -> str: + return "%s(%r, %r)" % (self.__class__.__name__, self.char, self.style) + + +_CHAR_CACHE: FastDictCache[Tuple[str, str], Char] = FastDictCache( + Char, size=1000 * 1000 +) +Transparent = "[transparent]" + + +class Screen: + """ + Two dimensional buffer of :class:`.Char` instances. + """ + + def __init__( + self, + default_char: Optional[Char] = None, + initial_width: int = 0, + initial_height: int = 0, + ) -> None: + + if default_char is None: + default_char2 = _CHAR_CACHE[" ", Transparent] + else: + default_char2 = default_char + + self.data_buffer: DefaultDict[int, DefaultDict[int, Char]] = defaultdict( + lambda: defaultdict(lambda: default_char2) + ) + + #: Escape sequences to be injected. + self.zero_width_escapes: DefaultDict[int, DefaultDict[int, str]] = defaultdict( + lambda: defaultdict(lambda: "") + ) + + #: Position of the cursor. + self.cursor_positions: Dict[ + "Window", Point + ] = {} # Map `Window` objects to `Point` objects. + + #: Visibility of the cursor. + self.show_cursor = True + + #: (Optional) Where to position the menu. E.g. at the start of a completion. + #: (We can't use the cursor position, because we don't want the + #: completion menu to change its position when we browse through all the + #: completions.) + self.menu_positions: Dict[ + "Window", Point + ] = {} # Map `Window` objects to `Point` objects. + + #: Currently used width/height of the screen. This will increase when + #: data is written to the screen. + self.width = initial_width or 0 + self.height = initial_height or 0 + + # Windows that have been drawn. (Each `Window` class will add itself to + # this list.) + self.visible_windows: List["Window"] = [] + + # List of (z_index, draw_func) + self._draw_float_functions: List[Tuple[int, Callable[[], None]]] = [] + + def set_cursor_position(self, window: "Window", position: Point) -> None: + """ + Set the cursor position for a given window. + """ + self.cursor_positions[window] = position + + def set_menu_position(self, window: "Window", position: Point) -> None: + """ + Set the cursor position for a given window. + """ + self.menu_positions[window] = position + + def get_cursor_position(self, window: "Window") -> Point: + """ + Get the cursor position for a given window. + Returns a `Point`. + """ + try: + return self.cursor_positions[window] + except KeyError: + return Point(x=0, y=0) + + def get_menu_position(self, window: "Window") -> Point: + """ + Get the menu position for a given window. + (This falls back to the cursor position if no menu position was set.) + """ + try: + return self.menu_positions[window] + except KeyError: + try: + return self.cursor_positions[window] + except KeyError: + return Point(x=0, y=0) + + def draw_with_z_index(self, z_index: int, draw_func: Callable[[], None]) -> None: + """ + Add a draw-function for a `Window` which has a >= 0 z_index. + This will be postponed until `draw_all_floats` is called. + """ + self._draw_float_functions.append((z_index, draw_func)) + + def draw_all_floats(self) -> None: + """ + Draw all float functions in order of z-index. + """ + # We keep looping because some draw functions could add new functions + # to this list. See `FloatContainer`. + while self._draw_float_functions: + # Sort the floats that we have so far by z_index. + functions = sorted(self._draw_float_functions, key=lambda item: item[0]) + + # Draw only one at a time, then sort everything again. Now floats + # might have been added. + self._draw_float_functions = functions[1:] + functions[0][1]() + + def append_style_to_content(self, style_str: str) -> None: + """ + For all the characters in the screen. + Set the style string to the given `style_str`. + """ + b = self.data_buffer + char_cache = _CHAR_CACHE + + append_style = " " + style_str + + for y, row in b.items(): + for x, char in row.items(): + b[y][x] = char_cache[char.char, char.style + append_style] + + def fill_area( + self, write_position: "WritePosition", style: str = "", after: bool = False + ) -> None: + """ + Fill the content of this area, using the given `style`. + The style is prepended before whatever was here before. + """ + if not style.strip(): + return + + xmin = write_position.xpos + xmax = write_position.xpos + write_position.width + char_cache = _CHAR_CACHE + data_buffer = self.data_buffer + + if after: + append_style = " " + style + prepend_style = "" + else: + append_style = "" + prepend_style = style + " " + + for y in range( + write_position.ypos, write_position.ypos + write_position.height + ): + row = data_buffer[y] + for x in range(xmin, xmax): + cell = row[x] + row[x] = char_cache[ + cell.char, prepend_style + cell.style + append_style + ] + + +class WritePosition: + def __init__(self, xpos: int, ypos: int, width: int, height: int) -> None: + assert height >= 0 + assert width >= 0 + # xpos and ypos can be negative. (A float can be partially visible.) + + self.xpos = xpos + self.ypos = ypos + self.width = width + self.height = height + + def __repr__(self) -> str: + return "%s(x=%r, y=%r, width=%r, height=%r)" % ( + self.__class__.__name__, + self.xpos, + self.ypos, + self.width, + self.height, + ) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/utils.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/utils.py new file mode 100644 index 00000000..35673ccb --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/layout/utils.py @@ -0,0 +1,76 @@ +from typing import Iterable, List, TypeVar, Union, cast, overload + +from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple + +__all__ = [ + "explode_text_fragments", +] + +_T = TypeVar("_T", bound=OneStyleAndTextTuple) + + +class _ExplodedList(List[_T]): + """ + Wrapper around a list, that marks it as 'exploded'. + + As soon as items are added or the list is extended, the new items are + automatically exploded as well. + """ + + exploded = True + + def append(self, item: _T) -> None: + self.extend([item]) + + def extend(self, lst: Iterable[_T]) -> None: + super().extend(explode_text_fragments(lst)) + + def insert(self, index: int, item: _T) -> None: + raise NotImplementedError # TODO + + # TODO: When creating a copy() or [:], return also an _ExplodedList. + + @overload + def __setitem__(self, index: int, value: _T) -> None: + ... + + @overload + def __setitem__(self, index: slice, value: Iterable[_T]) -> None: + ... + + def __setitem__( + self, index: Union[int, slice], value: Union[_T, Iterable[_T]] + ) -> None: + """ + Ensure that when `(style_str, 'long string')` is set, the string will be + exploded. + """ + if not isinstance(index, slice): + index = slice(index, index + 1) + if isinstance(value, tuple): # In case of `OneStyleAndTextTuple`. + value = cast("List[_T]", [value]) + + super().__setitem__(index, explode_text_fragments(cast("Iterable[_T]", value))) + + +def explode_text_fragments(fragments: Iterable[_T]) -> _ExplodedList[_T]: + """ + Turn a list of (style_str, text) tuples into another list where each string is + exactly one character. + + It should be fine to call this function several times. Calling this on a + list that is already exploded, is a null operation. + + :param fragments: List of (style, text) tuples. + """ + # When the fragments is already exploded, don't explode again. + if isinstance(fragments, _ExplodedList): + return fragments + + result: List[_T] = [] + + for style, string, *rest in fragments: # type: ignore + for c in string: # type: ignore + result.append((style, c, *rest)) # type: ignore + + return _ExplodedList(result) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__init__.py new file mode 100644 index 00000000..3e875e65 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__init__.py @@ -0,0 +1,18 @@ +""" +Lexer interface and implementations. +Used for syntax highlighting. +""" +from .base import DynamicLexer, Lexer, SimpleLexer +from .pygments import PygmentsLexer, RegexSync, SyncFromStart, SyntaxSync + +__all__ = [ + # Base. + "Lexer", + "SimpleLexer", + "DynamicLexer", + # Pygments. + "PygmentsLexer", + "RegexSync", + "SyncFromStart", + "SyntaxSync", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..6365453a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/base.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/base.cpython-38.pyc new file mode 100644 index 00000000..aab8a642 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/base.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/pygments.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/pygments.cpython-38.pyc new file mode 100644 index 00000000..e7a3dc35 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/__pycache__/pygments.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/base.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/base.py new file mode 100644 index 00000000..ef36cfd4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/base.py @@ -0,0 +1,82 @@ +""" +Base classes for prompt_toolkit lexers. +""" +from abc import ABCMeta, abstractmethod +from typing import Callable, Hashable, Optional + +from prompt_toolkit.document import Document +from prompt_toolkit.formatted_text.base import StyleAndTextTuples + +__all__ = [ + "Lexer", + "SimpleLexer", + "DynamicLexer", +] + + +class Lexer(metaclass=ABCMeta): + """ + Base class for all lexers. + """ + + @abstractmethod + def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]: + """ + Takes a :class:`~prompt_toolkit.document.Document` and returns a + callable that takes a line number and returns a list of + ``(style_str, text)`` tuples for that line. + + XXX: Note that in the past, this was supposed to return a list + of ``(Token, text)`` tuples, just like a Pygments lexer. + """ + + def invalidation_hash(self) -> Hashable: + """ + When this changes, `lex_document` could give a different output. + (Only used for `DynamicLexer`.) + """ + return id(self) + + +class SimpleLexer(Lexer): + """ + Lexer that doesn't do any tokenizing and returns the whole input as one + token. + + :param style: The style string for this lexer. + """ + + def __init__(self, style: str = "") -> None: + self.style = style + + def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]: + lines = document.lines + + def get_line(lineno: int) -> StyleAndTextTuples: + " Return the tokens for the given line. " + try: + return [(self.style, lines[lineno])] + except IndexError: + return [] + + return get_line + + +class DynamicLexer(Lexer): + """ + Lexer class that can dynamically returns any Lexer. + + :param get_lexer: Callable that returns a :class:`.Lexer` instance. + """ + + def __init__(self, get_lexer: Callable[[], Optional[Lexer]]) -> None: + self.get_lexer = get_lexer + self._dummy = SimpleLexer() + + def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]: + lexer = self.get_lexer() or self._dummy + return lexer.lex_document(document) + + def invalidation_hash(self) -> Hashable: + lexer = self.get_lexer() or self._dummy + return id(lexer) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/pygments.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/pygments.py new file mode 100644 index 00000000..025078be --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/lexers/pygments.py @@ -0,0 +1,335 @@ +""" +Adaptor classes for using Pygments lexers within prompt_toolkit. + +This includes syntax synchronization code, so that we don't have to start +lexing at the beginning of a document, when displaying a very large text. +""" +import re +from abc import ABCMeta, abstractmethod +from typing import ( + TYPE_CHECKING, + Callable, + Dict, + Generator, + Iterable, + Optional, + Tuple, + Type, +) + +from prompt_toolkit.document import Document +from prompt_toolkit.filters import FilterOrBool, to_filter +from prompt_toolkit.formatted_text.base import StyleAndTextTuples +from prompt_toolkit.formatted_text.utils import split_lines +from prompt_toolkit.styles.pygments import pygments_token_to_classname + +from .base import Lexer, SimpleLexer + +if TYPE_CHECKING: + from pygments.lexer import Lexer as PygmentsLexerCls + +__all__ = [ + "PygmentsLexer", + "SyntaxSync", + "SyncFromStart", + "RegexSync", +] + + +class SyntaxSync(metaclass=ABCMeta): + """ + Syntax synchroniser. This is a tool that finds a start position for the + lexer. This is especially important when editing big documents; we don't + want to start the highlighting by running the lexer from the beginning of + the file. That is very slow when editing. + """ + + @abstractmethod + def get_sync_start_position( + self, document: Document, lineno: int + ) -> Tuple[int, int]: + """ + Return the position from where we can start lexing as a (row, column) + tuple. + + :param document: `Document` instance that contains all the lines. + :param lineno: The line that we want to highlight. (We need to return + this line, or an earlier position.) + """ + + +class SyncFromStart(SyntaxSync): + """ + Always start the syntax highlighting from the beginning. + """ + + def get_sync_start_position( + self, document: Document, lineno: int + ) -> Tuple[int, int]: + return 0, 0 + + +class RegexSync(SyntaxSync): + """ + Synchronize by starting at a line that matches the given regex pattern. + """ + + # Never go more than this amount of lines backwards for synchronisation. + # That would be too CPU intensive. + MAX_BACKWARDS = 500 + + # Start lexing at the start, if we are in the first 'n' lines and no + # synchronisation position was found. + FROM_START_IF_NO_SYNC_POS_FOUND = 100 + + def __init__(self, pattern: str) -> None: + self._compiled_pattern = re.compile(pattern) + + def get_sync_start_position( + self, document: Document, lineno: int + ) -> Tuple[int, int]: + """ + Scan backwards, and find a possible position to start. + """ + pattern = self._compiled_pattern + lines = document.lines + + # Scan upwards, until we find a point where we can start the syntax + # synchronisation. + for i in range(lineno, max(-1, lineno - self.MAX_BACKWARDS), -1): + match = pattern.match(lines[i]) + if match: + return i, match.start() + + # No synchronisation point found. If we aren't that far from the + # beginning, start at the very beginning, otherwise, just try to start + # at the current line. + if lineno < self.FROM_START_IF_NO_SYNC_POS_FOUND: + return 0, 0 + else: + return lineno, 0 + + @classmethod + def from_pygments_lexer_cls(cls, lexer_cls: "PygmentsLexerCls") -> "RegexSync": + """ + Create a :class:`.RegexSync` instance for this Pygments lexer class. + """ + patterns = { + # For Python, start highlighting at any class/def block. + "Python": r"^\s*(class|def)\s+", + "Python 3": r"^\s*(class|def)\s+", + # For HTML, start at any open/close tag definition. + "HTML": r"<[/a-zA-Z]", + # For javascript, start at a function. + "JavaScript": r"\bfunction\b" + # TODO: Add definitions for other languages. + # By default, we start at every possible line. + } + p = patterns.get(lexer_cls.name, "^") + return cls(p) + + +class _TokenCache(Dict[Tuple[str, ...], str]): + """ + Cache that converts Pygments tokens into `prompt_toolkit` style objects. + + ``Token.A.B.C`` will be converted into: + ``class:pygments,pygments.A,pygments.A.B,pygments.A.B.C`` + """ + + def __missing__(self, key: Tuple[str, ...]) -> str: + result = "class:" + pygments_token_to_classname(key) + self[key] = result + return result + + +_token_cache = _TokenCache() + + +class PygmentsLexer(Lexer): + """ + Lexer that calls a pygments lexer. + + Example:: + + from pygments.lexers.html import HtmlLexer + lexer = PygmentsLexer(HtmlLexer) + + Note: Don't forget to also load a Pygments compatible style. E.g.:: + + from prompt_toolkit.styles.from_pygments import style_from_pygments_cls + from pygments.styles import get_style_by_name + style = style_from_pygments_cls(get_style_by_name('monokai')) + + :param pygments_lexer_cls: A `Lexer` from Pygments. + :param sync_from_start: Start lexing at the start of the document. This + will always give the best results, but it will be slow for bigger + documents. (When the last part of the document is display, then the + whole document will be lexed by Pygments on every key stroke.) It is + recommended to disable this for inputs that are expected to be more + than 1,000 lines. + :param syntax_sync: `SyntaxSync` object. + """ + + # Minimum amount of lines to go backwards when starting the parser. + # This is important when the lines are retrieved in reverse order, or when + # scrolling upwards. (Due to the complexity of calculating the vertical + # scroll offset in the `Window` class, lines are not always retrieved in + # order.) + MIN_LINES_BACKWARDS = 50 + + # When a parser was started this amount of lines back, read the parser + # until we get the current line. Otherwise, start a new parser. + # (This should probably be bigger than MIN_LINES_BACKWARDS.) + REUSE_GENERATOR_MAX_DISTANCE = 100 + + def __init__( + self, + pygments_lexer_cls: Type["PygmentsLexerCls"], + sync_from_start: FilterOrBool = True, + syntax_sync: Optional[SyntaxSync] = None, + ) -> None: + + self.pygments_lexer_cls = pygments_lexer_cls + self.sync_from_start = to_filter(sync_from_start) + + # Instantiate the Pygments lexer. + self.pygments_lexer = pygments_lexer_cls( + stripnl=False, stripall=False, ensurenl=False + ) + + # Create syntax sync instance. + self.syntax_sync = syntax_sync or RegexSync.from_pygments_lexer_cls( + pygments_lexer_cls + ) + + @classmethod + def from_filename( + cls, filename: str, sync_from_start: FilterOrBool = True + ) -> "Lexer": + """ + Create a `Lexer` from a filename. + """ + # Inline imports: the Pygments dependency is optional! + from pygments.lexers import get_lexer_for_filename + from pygments.util import ClassNotFound + + try: + pygments_lexer = get_lexer_for_filename(filename) + except ClassNotFound: + return SimpleLexer() + else: + return cls(pygments_lexer.__class__, sync_from_start=sync_from_start) + + def lex_document(self, document: Document) -> Callable[[int], StyleAndTextTuples]: + """ + Create a lexer function that takes a line number and returns the list + of (style_str, text) tuples as the Pygments lexer returns for that line. + """ + LineGenerator = Generator[Tuple[int, StyleAndTextTuples], None, None] + + # Cache of already lexed lines. + cache: Dict[int, StyleAndTextTuples] = {} + + # Pygments generators that are currently lexing. + # Map lexer generator to the line number. + line_generators: Dict[LineGenerator, int] = {} + + def get_syntax_sync() -> SyntaxSync: + " The Syntax synchronisation object that we currently use. " + if self.sync_from_start(): + return SyncFromStart() + else: + return self.syntax_sync + + def find_closest_generator(i: int) -> Optional[LineGenerator]: + " Return a generator close to line 'i', or None if none was found. " + for generator, lineno in line_generators.items(): + if lineno < i and i - lineno < self.REUSE_GENERATOR_MAX_DISTANCE: + return generator + return None + + def create_line_generator(start_lineno: int, column: int = 0) -> LineGenerator: + """ + Create a generator that yields the lexed lines. + Each iteration it yields a (line_number, [(style_str, text), ...]) tuple. + """ + + def get_text_fragments() -> Iterable[Tuple[str, str]]: + text = "\n".join(document.lines[start_lineno:])[column:] + + # We call `get_text_fragments_unprocessed`, because `get_tokens` will + # still replace \r\n and \r by \n. (We don't want that, + # Pygments should return exactly the same amount of text, as we + # have given as input.) + for _, t, v in self.pygments_lexer.get_tokens_unprocessed(text): + # Turn Pygments `Token` object into prompt_toolkit style + # strings. + yield _token_cache[t], v + + yield from enumerate(split_lines(list(get_text_fragments())), start_lineno) + + def get_generator(i: int) -> LineGenerator: + """ + Find an already started generator that is close, or create a new one. + """ + # Find closest line generator. + generator = find_closest_generator(i) + if generator: + return generator + + # No generator found. Determine starting point for the syntax + # synchronisation first. + + # Go at least x lines back. (Make scrolling upwards more + # efficient.) + i = max(0, i - self.MIN_LINES_BACKWARDS) + + if i == 0: + row = 0 + column = 0 + else: + row, column = get_syntax_sync().get_sync_start_position(document, i) + + # Find generator close to this point, or otherwise create a new one. + generator = find_closest_generator(i) + if generator: + return generator + else: + generator = create_line_generator(row, column) + + # If the column is not 0, ignore the first line. (Which is + # incomplete. This happens when the synchronisation algorithm tells + # us to start parsing in the middle of a line.) + if column: + next(generator) + row += 1 + + line_generators[generator] = row + return generator + + def get_line(i: int) -> StyleAndTextTuples: + " Return the tokens for a given line number. " + try: + return cache[i] + except KeyError: + generator = get_generator(i) + + # Exhaust the generator, until we find the requested line. + for num, line in generator: + cache[num] = line + if num == i: + line_generators[generator] = i + + # Remove the next item from the cache. + # (It could happen that it's already there, because of + # another generator that started filling these lines, + # but we want to synchronise these lines with the + # current lexer's state.) + if num + 1 in cache: + del cache[num + 1] + + return cache[num] + return [] + + return get_line diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/log.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/log.py new file mode 100644 index 00000000..36ceced4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/log.py @@ -0,0 +1,10 @@ +""" +Logging configuration. +""" +import logging + +__all__ = [ + "logger", +] + +logger = logging.getLogger(__package__) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/mouse_events.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/mouse_events.py new file mode 100644 index 00000000..7c2a9069 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/mouse_events.py @@ -0,0 +1,45 @@ +""" +Mouse events. + + +How it works +------------ + +The renderer has a 2 dimensional grid of mouse event handlers. +(`prompt_toolkit.layout.MouseHandlers`.) When the layout is rendered, the +`Window` class will make sure that this grid will also be filled with +callbacks. For vt100 terminals, mouse events are received through stdin, just +like any other key press. There is a handler among the key bindings that +catches these events and forwards them to such a mouse event handler. It passes +through the `Window` class where the coordinates are translated from absolute +coordinates to coordinates relative to the user control, and there +`UIControl.mouse_handler` is called. +""" +from enum import Enum + +from .data_structures import Point + +__all__ = ["MouseEventType", "MouseEvent"] + + +class MouseEventType(Enum): + MOUSE_UP = "MOUSE_UP" + MOUSE_DOWN = "MOUSE_DOWN" + SCROLL_UP = "SCROLL_UP" + SCROLL_DOWN = "SCROLL_DOWN" + + +class MouseEvent: + """ + Mouse event, sent to `UIControl.mouse_handler`. + + :param position: `Point` instance. + :param event_type: `MouseEventType`. + """ + + def __init__(self, position: Point, event_type: MouseEventType) -> None: + self.position = position + self.event_type = event_type + + def __repr__(self) -> str: + return "MouseEvent(%r, %r)" % (self.position, self.event_type) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__init__.py new file mode 100644 index 00000000..7b90b476 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__init__.py @@ -0,0 +1,13 @@ +from .base import DummyOutput, Output +from .color_depth import ColorDepth +from .defaults import create_output + +__all__ = [ + # Base. + "Output", + "DummyOutput", + # Color depth. + "ColorDepth", + # Defaults. + "create_output", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..21f6fa48 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/base.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/base.cpython-38.pyc new file mode 100644 index 00000000..a2a522b4 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/base.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/color_depth.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/color_depth.cpython-38.pyc new file mode 100644 index 00000000..f3a13a65 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/color_depth.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/conemu.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/conemu.cpython-38.pyc new file mode 100644 index 00000000..0e0654ff Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/conemu.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/defaults.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/defaults.cpython-38.pyc new file mode 100644 index 00000000..d7440d6e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/defaults.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/vt100.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/vt100.cpython-38.pyc new file mode 100644 index 00000000..9e5c53a7 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/vt100.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/win32.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/win32.cpython-38.pyc new file mode 100644 index 00000000..b26b04af Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/win32.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/windows10.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/windows10.cpython-38.pyc new file mode 100644 index 00000000..920d4dd1 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/__pycache__/windows10.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/base.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/base.py new file mode 100644 index 00000000..38744953 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/base.py @@ -0,0 +1,303 @@ +""" +Interface for an output. +""" +from abc import ABCMeta, abstractmethod + +from prompt_toolkit.data_structures import Size +from prompt_toolkit.styles import Attrs + +from .color_depth import ColorDepth + +__all__ = [ + "Output", + "DummyOutput", +] + + +class Output(metaclass=ABCMeta): + """ + Base class defining the output interface for a + :class:`~prompt_toolkit.renderer.Renderer`. + + Actual implementations are + :class:`~prompt_toolkit.output.vt100.Vt100_Output` and + :class:`~prompt_toolkit.output.win32.Win32Output`. + """ + + @abstractmethod + def fileno(self) -> int: + " Return the file descriptor to which we can write for the output. " + + @abstractmethod + def encoding(self) -> str: + """ + Return the encoding for this output, e.g. 'utf-8'. + (This is used mainly to know which characters are supported by the + output the data, so that the UI can provide alternatives, when + required.) + """ + + @abstractmethod + def write(self, data: str) -> None: + " Write text (Terminal escape sequences will be removed/escaped.) " + + @abstractmethod + def write_raw(self, data: str) -> None: + " Write text. " + + @abstractmethod + def set_title(self, title: str) -> None: + " Set terminal title. " + + @abstractmethod + def clear_title(self) -> None: + " Clear title again. (or restore previous title.) " + + @abstractmethod + def flush(self) -> None: + " Write to output stream and flush. " + + @abstractmethod + def erase_screen(self) -> None: + """ + Erases the screen with the background colour and moves the cursor to + home. + """ + + @abstractmethod + def enter_alternate_screen(self) -> None: + " Go to the alternate screen buffer. (For full screen applications). " + + @abstractmethod + def quit_alternate_screen(self) -> None: + " Leave the alternate screen buffer. " + + @abstractmethod + def enable_mouse_support(self) -> None: + " Enable mouse. " + + @abstractmethod + def disable_mouse_support(self) -> None: + " Disable mouse. " + + @abstractmethod + def erase_end_of_line(self) -> None: + """ + Erases from the current cursor position to the end of the current line. + """ + + @abstractmethod + def erase_down(self) -> None: + """ + Erases the screen from the current line down to the bottom of the + screen. + """ + + @abstractmethod + def reset_attributes(self) -> None: + " Reset color and styling attributes. " + + @abstractmethod + def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: + " Set new color and styling attributes. " + + @abstractmethod + def disable_autowrap(self) -> None: + " Disable auto line wrapping. " + + @abstractmethod + def enable_autowrap(self) -> None: + " Enable auto line wrapping. " + + @abstractmethod + def cursor_goto(self, row: int = 0, column: int = 0) -> None: + " Move cursor position. " + + @abstractmethod + def cursor_up(self, amount: int) -> None: + " Move cursor `amount` place up. " + + @abstractmethod + def cursor_down(self, amount: int) -> None: + " Move cursor `amount` place down. " + + @abstractmethod + def cursor_forward(self, amount: int) -> None: + " Move cursor `amount` place forward. " + + @abstractmethod + def cursor_backward(self, amount: int) -> None: + " Move cursor `amount` place backward. " + + @abstractmethod + def hide_cursor(self) -> None: + " Hide cursor. " + + @abstractmethod + def show_cursor(self) -> None: + " Show cursor. " + + def ask_for_cpr(self) -> None: + """ + Asks for a cursor position report (CPR). + (VT100 only.) + """ + + @property + def responds_to_cpr(self) -> bool: + """ + `True` if the `Application` can expect to receive a CPR response after + calling `ask_for_cpr` (this will come back through the corresponding + `Input`). + + This is used to determine the amount of available rows we have below + the cursor position. In the first place, we have this so that the drop + down autocompletion menus are sized according to the available space. + + On Windows, we don't need this, there we have + `get_rows_below_cursor_position`. + """ + return False + + @abstractmethod + def get_size(self) -> Size: + " Return the size of the output window. " + + def bell(self) -> None: + " Sound bell. " + + def enable_bracketed_paste(self) -> None: + " For vt100 only. " + + def disable_bracketed_paste(self) -> None: + " For vt100 only. " + + def scroll_buffer_to_prompt(self) -> None: + " For Win32 only. " + + def get_rows_below_cursor_position(self) -> int: + " For Windows only. " + raise NotImplementedError + + @abstractmethod + def get_default_color_depth(self) -> ColorDepth: + """ + Get default color depth for this output. + + This value will be used if no color depth was explicitely passed to the + `Application`. + + .. note:: + + If the `$PROMPT_TOOLKIT_COLOR_DEPTH` environment variable has been + set, then `outputs.defaults.create_output` will pass this value to + the implementation as the default_color_depth, which is returned + here. (This is not used when the output corresponds to a + prompt_toolkit SSH/Telnet session.) + """ + + +class DummyOutput(Output): + """ + For testing. An output class that doesn't render anything. + """ + + def fileno(self) -> int: + " There is no sensible default for fileno(). " + raise NotImplementedError + + def encoding(self) -> str: + return "utf-8" + + def write(self, data: str) -> None: + pass + + def write_raw(self, data: str) -> None: + pass + + def set_title(self, title: str) -> None: + pass + + def clear_title(self) -> None: + pass + + def flush(self) -> None: + pass + + def erase_screen(self) -> None: + pass + + def enter_alternate_screen(self) -> None: + pass + + def quit_alternate_screen(self) -> None: + pass + + def enable_mouse_support(self) -> None: + pass + + def disable_mouse_support(self) -> None: + pass + + def erase_end_of_line(self) -> None: + pass + + def erase_down(self) -> None: + pass + + def reset_attributes(self) -> None: + pass + + def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: + pass + + def disable_autowrap(self) -> None: + pass + + def enable_autowrap(self) -> None: + pass + + def cursor_goto(self, row: int = 0, column: int = 0) -> None: + pass + + def cursor_up(self, amount: int) -> None: + pass + + def cursor_down(self, amount: int) -> None: + pass + + def cursor_forward(self, amount: int) -> None: + pass + + def cursor_backward(self, amount: int) -> None: + pass + + def hide_cursor(self) -> None: + pass + + def show_cursor(self) -> None: + pass + + def ask_for_cpr(self) -> None: + pass + + def bell(self) -> None: + pass + + def enable_bracketed_paste(self) -> None: + pass + + def disable_bracketed_paste(self) -> None: + pass + + def scroll_buffer_to_prompt(self) -> None: + pass + + def get_size(self) -> Size: + return Size(rows=40, columns=80) + + def get_rows_below_cursor_position(self) -> int: + return 40 + + def get_default_color_depth(self) -> ColorDepth: + return ColorDepth.DEPTH_1_BIT diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/color_depth.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/color_depth.py new file mode 100644 index 00000000..a6166bac --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/color_depth.py @@ -0,0 +1,58 @@ +import os +from enum import Enum +from typing import Optional + +__all__ = [ + "ColorDepth", +] + + +class ColorDepth(str, Enum): + """ + Possible color depth values for the output. + """ + + value: str + + #: One color only. + DEPTH_1_BIT = "DEPTH_1_BIT" + + #: ANSI Colors. + DEPTH_4_BIT = "DEPTH_4_BIT" + + #: The default. + DEPTH_8_BIT = "DEPTH_8_BIT" + + #: 24 bit True color. + DEPTH_24_BIT = "DEPTH_24_BIT" + + # Aliases. + MONOCHROME = DEPTH_1_BIT + ANSI_COLORS_ONLY = DEPTH_4_BIT + DEFAULT = DEPTH_8_BIT + TRUE_COLOR = DEPTH_24_BIT + + @classmethod + def from_env(cls) -> Optional["ColorDepth"]: + """ + Return the color depth if the $PROMPT_TOOLKIT_COLOR_DEPTH environment + variable has been set. + + This is a way to enforce a certain color depth in all prompt_toolkit + applications. + """ + # Check the `PROMPT_TOOLKIT_COLOR_DEPTH` environment variable. + all_values = [i.value for i in ColorDepth] + if os.environ.get("PROMPT_TOOLKIT_COLOR_DEPTH") in all_values: + return cls(os.environ["PROMPT_TOOLKIT_COLOR_DEPTH"]) + + return None + + @classmethod + def default(cls) -> "ColorDepth": + """ + Return the default color depth for the default output. + """ + from .defaults import create_output + + return create_output().get_default_color_depth() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/conemu.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/conemu.py new file mode 100644 index 00000000..616cf5b1 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/conemu.py @@ -0,0 +1,59 @@ +from typing import Any, Optional, TextIO + +from prompt_toolkit.data_structures import Size +from prompt_toolkit.renderer import Output + +from .color_depth import ColorDepth +from .vt100 import Vt100_Output +from .win32 import Win32Output + +__all__ = [ + "ConEmuOutput", +] + + +class ConEmuOutput: + """ + ConEmu (Windows) output abstraction. + + ConEmu is a Windows console application, but it also supports ANSI escape + sequences. This output class is actually a proxy to both `Win32Output` and + `Vt100_Output`. It uses `Win32Output` for console sizing and scrolling, but + all cursor movements and scrolling happens through the `Vt100_Output`. + + This way, we can have 256 colors in ConEmu and Cmder. Rendering will be + even a little faster as well. + + http://conemu.github.io/ + http://gooseberrycreative.com/cmder/ + """ + + def __init__( + self, stdout: TextIO, default_color_depth: Optional[ColorDepth] = None + ) -> None: + self.win32_output = Win32Output(stdout, default_color_depth=default_color_depth) + self.vt100_output = Vt100_Output( + stdout, lambda: Size(0, 0), default_color_depth=default_color_depth + ) + + @property + def responds_to_cpr(self) -> bool: + return False # We don't need this on Windows. + + def __getattr__(self, name: str) -> Any: + if name in ( + "get_size", + "get_rows_below_cursor_position", + "enable_mouse_support", + "disable_mouse_support", + "scroll_buffer_to_prompt", + "get_win32_screen_buffer_info", + "enable_bracketed_paste", + "disable_bracketed_paste", + ): + return getattr(self.win32_output, name) + else: + return getattr(self.vt100_output, name) + + +Output.register(ConEmuOutput) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/defaults.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/defaults.py new file mode 100644 index 00000000..96eac455 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/defaults.py @@ -0,0 +1,76 @@ +import sys +from typing import Optional, TextIO, cast + +from prompt_toolkit.patch_stdout import StdoutProxy +from prompt_toolkit.utils import ( + get_term_environment_variable, + is_conemu_ansi, + is_windows, +) + +from .base import Output +from .color_depth import ColorDepth + +__all__ = [ + "create_output", +] + + +def create_output( + stdout: Optional[TextIO] = None, always_prefer_tty: bool = True +) -> Output: + """ + Return an :class:`~prompt_toolkit.output.Output` instance for the command + line. + + :param stdout: The stdout object + :param always_prefer_tty: When set, look for `sys.stderr` if `sys.stdout` + is not a TTY. (The prompt_toolkit render output is not meant to be + consumed by something other then a terminal, so this is a reasonable + default.) + """ + # Consider TERM and PROMPT_TOOLKIT_COLOR_DEPTH environment variables. + # Notice that PROMPT_TOOLKIT_COLOR_DEPTH value is the default that's used + # if the Application doesn't override it. + term_from_env = get_term_environment_variable() + color_depth_from_env = ColorDepth.from_env() + + if stdout is None: + # By default, render to stdout. If the output is piped somewhere else, + # render to stderr. + stdout = sys.stdout + + if always_prefer_tty: + for io in [sys.stdout, sys.stderr]: + if io.isatty(): + stdout = io + break + + # If the patch_stdout context manager has been used, then sys.stdout is + # replaced by this proxy. For prompt_toolkit applications, we want to use + # the real stdout. + while isinstance(stdout, StdoutProxy): + stdout = stdout.original_stdout + + if is_windows(): + from .conemu import ConEmuOutput + from .win32 import Win32Output + from .windows10 import Windows10_Output, is_win_vt100_enabled + + if is_win_vt100_enabled(): + return cast( + Output, + Windows10_Output(stdout, default_color_depth=color_depth_from_env), + ) + if is_conemu_ansi(): + return cast( + Output, ConEmuOutput(stdout, default_color_depth=color_depth_from_env) + ) + else: + return Win32Output(stdout, default_color_depth=color_depth_from_env) + else: + from .vt100 import Vt100_Output + + return Vt100_Output.from_pty( + stdout, term=term_from_env, default_color_depth=color_depth_from_env + ) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/vt100.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/vt100.py new file mode 100644 index 00000000..bfa61bd1 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/vt100.py @@ -0,0 +1,738 @@ +""" +Output for vt100 terminals. + +A lot of thanks, regarding outputting of colors, goes to the Pygments project: +(We don't rely on Pygments anymore, because many things are very custom, and +everything has been highly optimized.) +http://pygments.org/ +""" +import array +import errno +import io +import os +import sys +from typing import ( + IO, + Callable, + Dict, + Hashable, + Iterable, + List, + Optional, + Sequence, + Set, + TextIO, + Tuple, +) + +from prompt_toolkit.data_structures import Size +from prompt_toolkit.output import Output +from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs +from prompt_toolkit.utils import is_dumb_terminal + +from .color_depth import ColorDepth + +__all__ = [ + "Vt100_Output", +] + + +FG_ANSI_COLORS = { + "ansidefault": 39, + # Low intensity. + "ansiblack": 30, + "ansired": 31, + "ansigreen": 32, + "ansiyellow": 33, + "ansiblue": 34, + "ansimagenta": 35, + "ansicyan": 36, + "ansigray": 37, + # High intensity. + "ansibrightblack": 90, + "ansibrightred": 91, + "ansibrightgreen": 92, + "ansibrightyellow": 93, + "ansibrightblue": 94, + "ansibrightmagenta": 95, + "ansibrightcyan": 96, + "ansiwhite": 97, +} + +BG_ANSI_COLORS = { + "ansidefault": 49, + # Low intensity. + "ansiblack": 40, + "ansired": 41, + "ansigreen": 42, + "ansiyellow": 43, + "ansiblue": 44, + "ansimagenta": 45, + "ansicyan": 46, + "ansigray": 47, + # High intensity. + "ansibrightblack": 100, + "ansibrightred": 101, + "ansibrightgreen": 102, + "ansibrightyellow": 103, + "ansibrightblue": 104, + "ansibrightmagenta": 105, + "ansibrightcyan": 106, + "ansiwhite": 107, +} + + +ANSI_COLORS_TO_RGB = { + "ansidefault": ( + 0x00, + 0x00, + 0x00, + ), # Don't use, 'default' doesn't really have a value. + "ansiblack": (0x00, 0x00, 0x00), + "ansigray": (0xE5, 0xE5, 0xE5), + "ansibrightblack": (0x7F, 0x7F, 0x7F), + "ansiwhite": (0xFF, 0xFF, 0xFF), + # Low intensity. + "ansired": (0xCD, 0x00, 0x00), + "ansigreen": (0x00, 0xCD, 0x00), + "ansiyellow": (0xCD, 0xCD, 0x00), + "ansiblue": (0x00, 0x00, 0xCD), + "ansimagenta": (0xCD, 0x00, 0xCD), + "ansicyan": (0x00, 0xCD, 0xCD), + # High intensity. + "ansibrightred": (0xFF, 0x00, 0x00), + "ansibrightgreen": (0x00, 0xFF, 0x00), + "ansibrightyellow": (0xFF, 0xFF, 0x00), + "ansibrightblue": (0x00, 0x00, 0xFF), + "ansibrightmagenta": (0xFF, 0x00, 0xFF), + "ansibrightcyan": (0x00, 0xFF, 0xFF), +} + + +assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) +assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) +assert set(ANSI_COLORS_TO_RGB) == set(ANSI_COLOR_NAMES) + + +def _get_closest_ansi_color(r: int, g: int, b: int, exclude: Sequence[str] = ()) -> str: + """ + Find closest ANSI color. Return it by name. + + :param r: Red (Between 0 and 255.) + :param g: Green (Between 0 and 255.) + :param b: Blue (Between 0 and 255.) + :param exclude: A tuple of color names to exclude. (E.g. ``('ansired', )``.) + """ + exclude = list(exclude) + + # When we have a bit of saturation, avoid the gray-like colors, otherwise, + # too often the distance to the gray color is less. + saturation = abs(r - g) + abs(g - b) + abs(b - r) # Between 0..510 + + if saturation > 30: + exclude.extend(["ansilightgray", "ansidarkgray", "ansiwhite", "ansiblack"]) + + # Take the closest color. + # (Thanks to Pygments for this part.) + distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) + match = "ansidefault" + + for name, (r2, g2, b2) in ANSI_COLORS_TO_RGB.items(): + if name != "ansidefault" and name not in exclude: + d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 + + if d < distance: + match = name + distance = d + + return match + + +_ColorCodeAndName = Tuple[int, str] + + +class _16ColorCache: + """ + Cache which maps (r, g, b) tuples to 16 ansi colors. + + :param bg: Cache for background colors, instead of foreground. + """ + + def __init__(self, bg: bool = False) -> None: + self.bg = bg + self._cache: Dict[Hashable, _ColorCodeAndName] = {} + + def get_code( + self, value: Tuple[int, int, int], exclude: Sequence[str] = () + ) -> _ColorCodeAndName: + """ + Return a (ansi_code, ansi_name) tuple. (E.g. ``(44, 'ansiblue')``.) for + a given (r,g,b) value. + """ + key: Hashable = (value, tuple(exclude)) + cache = self._cache + + if key not in cache: + cache[key] = self._get(value, exclude) + + return cache[key] + + def _get( + self, value: Tuple[int, int, int], exclude: Sequence[str] = () + ) -> _ColorCodeAndName: + + r, g, b = value + match = _get_closest_ansi_color(r, g, b, exclude=exclude) + + # Turn color name into code. + if self.bg: + code = BG_ANSI_COLORS[match] + else: + code = FG_ANSI_COLORS[match] + + return code, match + + +class _256ColorCache(Dict[Tuple[int, int, int], int]): + """ + Cache which maps (r, g, b) tuples to 256 colors. + """ + + def __init__(self) -> None: + # Build color table. + colors: List[Tuple[int, int, int]] = [] + + # colors 0..15: 16 basic colors + colors.append((0x00, 0x00, 0x00)) # 0 + colors.append((0xCD, 0x00, 0x00)) # 1 + colors.append((0x00, 0xCD, 0x00)) # 2 + colors.append((0xCD, 0xCD, 0x00)) # 3 + colors.append((0x00, 0x00, 0xEE)) # 4 + colors.append((0xCD, 0x00, 0xCD)) # 5 + colors.append((0x00, 0xCD, 0xCD)) # 6 + colors.append((0xE5, 0xE5, 0xE5)) # 7 + colors.append((0x7F, 0x7F, 0x7F)) # 8 + colors.append((0xFF, 0x00, 0x00)) # 9 + colors.append((0x00, 0xFF, 0x00)) # 10 + colors.append((0xFF, 0xFF, 0x00)) # 11 + colors.append((0x5C, 0x5C, 0xFF)) # 12 + colors.append((0xFF, 0x00, 0xFF)) # 13 + colors.append((0x00, 0xFF, 0xFF)) # 14 + colors.append((0xFF, 0xFF, 0xFF)) # 15 + + # colors 16..232: the 6x6x6 color cube + valuerange = (0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF) + + for i in range(217): + r = valuerange[(i // 36) % 6] + g = valuerange[(i // 6) % 6] + b = valuerange[i % 6] + colors.append((r, g, b)) + + # colors 233..253: grayscale + for i in range(1, 22): + v = 8 + i * 10 + colors.append((v, v, v)) + + self.colors = colors + + def __missing__(self, value: Tuple[int, int, int]) -> int: + r, g, b = value + + # Find closest color. + # (Thanks to Pygments for this!) + distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) + match = 0 + + for i, (r2, g2, b2) in enumerate(self.colors): + if i >= 16: # XXX: We ignore the 16 ANSI colors when mapping RGB + # to the 256 colors, because these highly depend on + # the color scheme of the terminal. + d = (r - r2) ** 2 + (g - g2) ** 2 + (b - b2) ** 2 + + if d < distance: + match = i + distance = d + + # Turn color name into code. + self[value] = match + return match + + +_16_fg_colors = _16ColorCache(bg=False) +_16_bg_colors = _16ColorCache(bg=True) +_256_colors = _256ColorCache() + + +class _EscapeCodeCache(Dict[Attrs, str]): + """ + Cache for VT100 escape codes. It maps + (fgcolor, bgcolor, bold, underline, reverse) tuples to VT100 escape sequences. + + :param true_color: When True, use 24bit colors instead of 256 colors. + """ + + def __init__(self, color_depth: ColorDepth) -> None: + self.color_depth = color_depth + + def __missing__(self, attrs: Attrs) -> str: + fgcolor, bgcolor, bold, underline, italic, blink, reverse, hidden = attrs + parts: List[str] = [] + + parts.extend(self._colors_to_code(fgcolor or "", bgcolor or "")) + + if bold: + parts.append("1") + if italic: + parts.append("3") + if blink: + parts.append("5") + if underline: + parts.append("4") + if reverse: + parts.append("7") + if hidden: + parts.append("8") + + if parts: + result = "\x1b[0;" + ";".join(parts) + "m" + else: + result = "\x1b[0m" + + self[attrs] = result + return result + + def _color_name_to_rgb(self, color: str) -> Tuple[int, int, int]: + " Turn 'ffffff', into (0xff, 0xff, 0xff). " + try: + rgb = int(color, 16) + except ValueError: + raise + else: + r = (rgb >> 16) & 0xFF + g = (rgb >> 8) & 0xFF + b = rgb & 0xFF + return r, g, b + + def _colors_to_code(self, fg_color: str, bg_color: str) -> Iterable[str]: + """ + Return a tuple with the vt100 values that represent this color. + """ + # When requesting ANSI colors only, and both fg/bg color were converted + # to ANSI, ensure that the foreground and background color are not the + # same. (Unless they were explicitly defined to be the same color.) + fg_ansi = "" + + def get(color: str, bg: bool) -> List[int]: + nonlocal fg_ansi + + table = BG_ANSI_COLORS if bg else FG_ANSI_COLORS + + if not color or self.color_depth == ColorDepth.DEPTH_1_BIT: + return [] + + # 16 ANSI colors. (Given by name.) + elif color in table: + return [table[color]] + + # RGB colors. (Defined as 'ffffff'.) + else: + try: + rgb = self._color_name_to_rgb(color) + except ValueError: + return [] + + # When only 16 colors are supported, use that. + if self.color_depth == ColorDepth.DEPTH_4_BIT: + if bg: # Background. + if fg_color != bg_color: + exclude = [fg_ansi] + else: + exclude = [] + code, name = _16_bg_colors.get_code(rgb, exclude=exclude) + return [code] + else: # Foreground. + code, name = _16_fg_colors.get_code(rgb) + fg_ansi = name + return [code] + + # True colors. (Only when this feature is enabled.) + elif self.color_depth == ColorDepth.DEPTH_24_BIT: + r, g, b = rgb + return [(48 if bg else 38), 2, r, g, b] + + # 256 RGB colors. + else: + return [(48 if bg else 38), 5, _256_colors[rgb]] + + result: List[int] = [] + result.extend(get(fg_color, False)) + result.extend(get(bg_color, True)) + + return map(str, result) + + +def _get_size(fileno: int) -> Tuple[int, int]: + # Thanks to fabric (fabfile.org), and + # http://sqizit.bartletts.id.au/2011/02/14/pseudo-terminals-in-python/ + """ + Get the size of this pseudo terminal. + + :param fileno: stdout.fileno() + :returns: A (rows, cols) tuple. + """ + # Inline imports, because these modules are not available on Windows. + # (This file is used by ConEmuOutput, which is used on Windows.) + import fcntl + import termios + + # Buffer for the C call + buf = array.array("h", [0, 0, 0, 0]) + + # Do TIOCGWINSZ (Get) + # Note: We should not pass 'True' as a fourth parameter to 'ioctl'. (True + # is the default.) This causes segmentation faults on some systems. + # See: https://github.com/jonathanslenders/python-prompt-toolkit/pull/364 + fcntl.ioctl(fileno, termios.TIOCGWINSZ, buf) # type: ignore + + # Return rows, cols + return buf[0], buf[1] + + +class Vt100_Output(Output): + """ + :param get_size: A callable which returns the `Size` of the output terminal. + :param stdout: Any object with has a `write` and `flush` method + an 'encoding' property. + :param term: The terminal environment variable. (xterm, xterm-256color, linux, ...) + :param write_binary: Encode the output before writing it. If `True` (the + default), the `stdout` object is supposed to expose an `encoding` attribute. + """ + + # For the error messages. Only display "Output is not a terminal" once per + # file descriptor. + _fds_not_a_terminal: Set[int] = set() + + def __init__( + self, + stdout: TextIO, + get_size: Callable[[], Size], + term: Optional[str] = None, + write_binary: bool = True, + default_color_depth: Optional[ColorDepth] = None, + ) -> None: + + assert all(hasattr(stdout, a) for a in ("write", "flush")) + + if write_binary: + assert hasattr(stdout, "encoding") + + self._buffer: List[str] = [] + self.stdout = stdout + self.write_binary = write_binary + self.default_color_depth = default_color_depth + self._get_size = get_size + self.term = term + + # Cache for escape codes. + self._escape_code_caches: Dict[ColorDepth, _EscapeCodeCache] = { + ColorDepth.DEPTH_1_BIT: _EscapeCodeCache(ColorDepth.DEPTH_1_BIT), + ColorDepth.DEPTH_4_BIT: _EscapeCodeCache(ColorDepth.DEPTH_4_BIT), + ColorDepth.DEPTH_8_BIT: _EscapeCodeCache(ColorDepth.DEPTH_8_BIT), + ColorDepth.DEPTH_24_BIT: _EscapeCodeCache(ColorDepth.DEPTH_24_BIT), + } + + @classmethod + def from_pty( + cls, + stdout: TextIO, + term: Optional[str] = None, + default_color_depth: Optional[ColorDepth] = None, + ) -> "Vt100_Output": + """ + Create an Output class from a pseudo terminal. + (This will take the dimensions by reading the pseudo + terminal attributes.) + """ + fd: Optional[int] + # Normally, this requires a real TTY device, but people instantiate + # this class often during unit tests as well. For convenience, we print + # an error message, use standard dimensions, and go on. + try: + fd = stdout.fileno() + except io.UnsupportedOperation: + fd = None + + if not stdout.isatty() and (fd is None or fd not in cls._fds_not_a_terminal): + msg = "Warning: Output is not a terminal (fd=%r).\n" + sys.stderr.write(msg % fd) + sys.stderr.flush() + if fd is not None: + cls._fds_not_a_terminal.add(fd) + + def get_size() -> Size: + # If terminal (incorrectly) reports its size as 0, pick a + # reasonable default. See + # https://github.com/ipython/ipython/issues/10071 + rows, columns = (None, None) + + # It is possible that `stdout` is no longer a TTY device at this + # point. In that case we get an `OSError` in the ioctl call in + # `get_size`. See: + # https://github.com/prompt-toolkit/python-prompt-toolkit/pull/1021 + try: + rows, columns = _get_size(stdout.fileno()) + except OSError: + pass + return Size(rows=rows or 24, columns=columns or 80) + + return cls(stdout, get_size, term=term, default_color_depth=default_color_depth) + + def get_size(self) -> Size: + return self._get_size() + + def fileno(self) -> int: + " Return file descriptor. " + return self.stdout.fileno() + + def encoding(self) -> str: + " Return encoding used for stdout. " + return self.stdout.encoding + + def write_raw(self, data: str) -> None: + """ + Write raw data to output. + """ + self._buffer.append(data) + + def write(self, data: str) -> None: + """ + Write text to output. + (Removes vt100 escape codes. -- used for safely writing text.) + """ + self._buffer.append(data.replace("\x1b", "?")) + + def set_title(self, title: str) -> None: + """ + Set terminal title. + """ + if self.term not in ( + "linux", + "eterm-color", + ): # Not supported by the Linux console. + self.write_raw( + "\x1b]2;%s\x07" % title.replace("\x1b", "").replace("\x07", "") + ) + + def clear_title(self) -> None: + self.set_title("") + + def erase_screen(self) -> None: + """ + Erases the screen with the background colour and moves the cursor to + home. + """ + self.write_raw("\x1b[2J") + + def enter_alternate_screen(self) -> None: + self.write_raw("\x1b[?1049h\x1b[H") + + def quit_alternate_screen(self) -> None: + self.write_raw("\x1b[?1049l") + + def enable_mouse_support(self) -> None: + self.write_raw("\x1b[?1000h") + + # Enable urxvt Mouse mode. (For terminals that understand this.) + self.write_raw("\x1b[?1015h") + + # Also enable Xterm SGR mouse mode. (For terminals that understand this.) + self.write_raw("\x1b[?1006h") + + # Note: E.g. lxterminal understands 1000h, but not the urxvt or sgr + # extensions. + + def disable_mouse_support(self) -> None: + self.write_raw("\x1b[?1000l") + self.write_raw("\x1b[?1015l") + self.write_raw("\x1b[?1006l") + + def erase_end_of_line(self) -> None: + """ + Erases from the current cursor position to the end of the current line. + """ + self.write_raw("\x1b[K") + + def erase_down(self) -> None: + """ + Erases the screen from the current line down to the bottom of the + screen. + """ + self.write_raw("\x1b[J") + + def reset_attributes(self) -> None: + self.write_raw("\x1b[0m") + + def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: + """ + Create new style and output. + + :param attrs: `Attrs` instance. + """ + # Get current depth. + escape_code_cache = self._escape_code_caches[color_depth] + + # Write escape character. + self.write_raw(escape_code_cache[attrs]) + + def disable_autowrap(self) -> None: + self.write_raw("\x1b[?7l") + + def enable_autowrap(self) -> None: + self.write_raw("\x1b[?7h") + + def enable_bracketed_paste(self) -> None: + self.write_raw("\x1b[?2004h") + + def disable_bracketed_paste(self) -> None: + self.write_raw("\x1b[?2004l") + + def cursor_goto(self, row: int = 0, column: int = 0) -> None: + """ + Move cursor position. + """ + self.write_raw("\x1b[%i;%iH" % (row, column)) + + def cursor_up(self, amount: int) -> None: + if amount == 0: + pass + elif amount == 1: + self.write_raw("\x1b[A") + else: + self.write_raw("\x1b[%iA" % amount) + + def cursor_down(self, amount: int) -> None: + if amount == 0: + pass + elif amount == 1: + # Note: Not the same as '\n', '\n' can cause the window content to + # scroll. + self.write_raw("\x1b[B") + else: + self.write_raw("\x1b[%iB" % amount) + + def cursor_forward(self, amount: int) -> None: + if amount == 0: + pass + elif amount == 1: + self.write_raw("\x1b[C") + else: + self.write_raw("\x1b[%iC" % amount) + + def cursor_backward(self, amount: int) -> None: + if amount == 0: + pass + elif amount == 1: + self.write_raw("\b") # '\x1b[D' + else: + self.write_raw("\x1b[%iD" % amount) + + def hide_cursor(self) -> None: + self.write_raw("\x1b[?25l") + + def show_cursor(self) -> None: + self.write_raw("\x1b[?12l\x1b[?25h") # Stop blinking cursor and show. + + def flush(self) -> None: + """ + Write to output stream and flush. + """ + if not self._buffer: + return + + data = "".join(self._buffer) + + try: + # (We try to encode ourself, because that way we can replace + # characters that don't exist in the character set, avoiding + # UnicodeEncodeError crashes. E.g. u'\xb7' does not appear in 'ascii'.) + # My Arch Linux installation of july 2015 reported 'ANSI_X3.4-1968' + # for sys.stdout.encoding in xterm. + out: IO + if self.write_binary: + if hasattr(self.stdout, "buffer"): + out = self.stdout.buffer # Py3. + else: + out = self.stdout + out.write(data.encode(self.stdout.encoding or "utf-8", "replace")) + else: + self.stdout.write(data) + + self.stdout.flush() + except IOError as e: + if e.args and e.args[0] == errno.EINTR: + # Interrupted system call. Can happen in case of a window + # resize signal. (Just ignore. The resize handler will render + # again anyway.) + pass + elif e.args and e.args[0] == 0: + # This can happen when there is a lot of output and the user + # sends a KeyboardInterrupt by pressing Control-C. E.g. in + # a Python REPL when we execute "while True: print('test')". + # (The `ptpython` REPL uses this `Output` class instead of + # `stdout` directly -- in order to be network transparent.) + # So, just ignore. + pass + else: + raise + + self._buffer = [] + + def ask_for_cpr(self) -> None: + """ + Asks for a cursor position report (CPR). + """ + self.write_raw("\x1b[6n") + self.flush() + + @property + def responds_to_cpr(self) -> bool: + # When the input is a tty, we assume that CPR is supported. + # It's not when the input is piped from Pexpect. + if os.environ.get("PROMPT_TOOLKIT_NO_CPR", "") == "1": + return False + + if is_dumb_terminal(self.term): + return False + try: + return self.stdout.isatty() + except ValueError: + return False # ValueError: I/O operation on closed file + + def bell(self) -> None: + " Sound bell. " + self.write_raw("\a") + self.flush() + + def get_default_color_depth(self) -> ColorDepth: + """ + Return the default color depth for a vt100 terminal, according to the + our term value. + + We prefer 256 colors almost always, because this is what most terminals + support these days, and is a good default. + """ + if self.default_color_depth is not None: + return self.default_color_depth + + term = self.term + + if term is None: + return ColorDepth.DEFAULT + + if is_dumb_terminal(term): + return ColorDepth.DEPTH_1_BIT + + if term in ("linux", "eterm-color"): + return ColorDepth.DEPTH_4_BIT + + return ColorDepth.DEFAULT diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/win32.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/win32.py new file mode 100644 index 00000000..4519c14b --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/win32.py @@ -0,0 +1,656 @@ +import os +from ctypes import ( + ArgumentError, + byref, + c_char, + c_long, + c_uint, + c_ulong, + pointer, + windll, +) +from ctypes.wintypes import DWORD, HANDLE +from typing import Dict, List, Optional, TextIO, Tuple + +from prompt_toolkit.data_structures import Size +from prompt_toolkit.renderer import Output +from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs +from prompt_toolkit.utils import get_cwidth +from prompt_toolkit.win32_types import ( + CONSOLE_SCREEN_BUFFER_INFO, + COORD, + SMALL_RECT, + STD_INPUT_HANDLE, + STD_OUTPUT_HANDLE, +) + +from .color_depth import ColorDepth + +__all__ = [ + "Win32Output", +] + + +def _coord_byval(coord): + """ + Turns a COORD object into a c_long. + This will cause it to be passed by value instead of by reference. (That is what I think at least.) + + When running ``ptipython`` is run (only with IPython), we often got the following error:: + + Error in 'SetConsoleCursorPosition'. + ArgumentError("argument 2: : wrong type",) + argument 2: : wrong type + + It was solved by turning ``COORD`` parameters into a ``c_long`` like this. + + More info: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686025(v=vs.85).aspx + """ + return c_long(coord.Y * 0x10000 | coord.X & 0xFFFF) + + +#: If True: write the output of the renderer also to the following file. This +#: is very useful for debugging. (e.g.: to see that we don't write more bytes +#: than required.) +_DEBUG_RENDER_OUTPUT = False +_DEBUG_RENDER_OUTPUT_FILENAME = r"prompt-toolkit-windows-output.log" + + +class NoConsoleScreenBufferError(Exception): + """ + Raised when the application is not running inside a Windows Console, but + the user tries to instantiate Win32Output. + """ + + def __init__(self) -> None: + # Are we running in 'xterm' on Windows, like git-bash for instance? + xterm = "xterm" in os.environ.get("TERM", "") + + if xterm: + message = ( + "Found %s, while expecting a Windows console. " + 'Maybe try to run this program using "winpty" ' + "or run it in cmd.exe instead. Or otherwise, " + "in case of Cygwin, use the Python executable " + "that is compiled for Cygwin." % os.environ["TERM"] + ) + else: + message = "No Windows console found. Are you running cmd.exe?" + super().__init__(message) + + +class Win32Output(Output): + """ + I/O abstraction for rendering to Windows consoles. + (cmd.exe and similar.) + """ + + def __init__( + self, + stdout: TextIO, + use_complete_width: bool = False, + default_color_depth: Optional[ColorDepth] = None, + ) -> None: + self.use_complete_width = use_complete_width + self.default_color_depth = default_color_depth + + self._buffer: List[str] = [] + self.stdout = stdout + self.hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) + + self._in_alternate_screen = False + self._hidden = False + + self.color_lookup_table = ColorLookupTable() + + # Remember the default console colors. + info = self.get_win32_screen_buffer_info() + self.default_attrs = info.wAttributes if info else 15 + + if _DEBUG_RENDER_OUTPUT: + self.LOG = open(_DEBUG_RENDER_OUTPUT_FILENAME, "ab") + + def fileno(self) -> int: + " Return file descriptor. " + return self.stdout.fileno() + + def encoding(self) -> str: + " Return encoding used for stdout. " + return self.stdout.encoding + + def write(self, data: str) -> None: + if self._hidden: + data = " " * get_cwidth(data) + + self._buffer.append(data) + + def write_raw(self, data: str) -> None: + " For win32, there is no difference between write and write_raw. " + self.write(data) + + def get_size(self) -> Size: + info = self.get_win32_screen_buffer_info() + + # We take the width of the *visible* region as the size. Not the width + # of the complete screen buffer. (Unless use_complete_width has been + # set.) + if self.use_complete_width: + width = info.dwSize.X + else: + width = info.srWindow.Right - info.srWindow.Left + + height = info.srWindow.Bottom - info.srWindow.Top + 1 + + # We avoid the right margin, windows will wrap otherwise. + maxwidth = info.dwSize.X - 1 + width = min(maxwidth, width) + + # Create `Size` object. + return Size(rows=height, columns=width) + + def _winapi(self, func, *a, **kw): + """ + Flush and call win API function. + """ + self.flush() + + if _DEBUG_RENDER_OUTPUT: + self.LOG.write(("%r" % func.__name__).encode("utf-8") + b"\n") + self.LOG.write( + b" " + ", ".join(["%r" % i for i in a]).encode("utf-8") + b"\n" + ) + self.LOG.write( + b" " + + ", ".join(["%r" % type(i) for i in a]).encode("utf-8") + + b"\n" + ) + self.LOG.flush() + + try: + return func(*a, **kw) + except ArgumentError as e: + if _DEBUG_RENDER_OUTPUT: + self.LOG.write( + (" Error in %r %r %s\n" % (func.__name__, e, e)).encode("utf-8") + ) + + def get_win32_screen_buffer_info(self): + """ + Return Screen buffer info. + """ + # NOTE: We don't call the `GetConsoleScreenBufferInfo` API through + # `self._winapi`. Doing so causes Python to crash on certain 64bit + # Python versions. (Reproduced with 64bit Python 2.7.6, on Windows + # 10). It is not clear why. Possibly, it has to do with passing + # these objects as an argument, or through *args. + + # The Python documentation contains the following - possibly related - warning: + # ctypes does not support passing unions or structures with + # bit-fields to functions by value. While this may work on 32-bit + # x86, it's not guaranteed by the library to work in the general + # case. Unions and structures with bit-fields should always be + # passed to functions by pointer. + + # Also see: + # - https://github.com/ipython/ipython/issues/10070 + # - https://github.com/jonathanslenders/python-prompt-toolkit/issues/406 + # - https://github.com/jonathanslenders/python-prompt-toolkit/issues/86 + + self.flush() + sbinfo = CONSOLE_SCREEN_BUFFER_INFO() + success = windll.kernel32.GetConsoleScreenBufferInfo( + self.hconsole, byref(sbinfo) + ) + + # success = self._winapi(windll.kernel32.GetConsoleScreenBufferInfo, + # self.hconsole, byref(sbinfo)) + + if success: + return sbinfo + else: + raise NoConsoleScreenBufferError + + def set_title(self, title: str) -> None: + """ + Set terminal title. + """ + self._winapi(windll.kernel32.SetConsoleTitleW, title) + + def clear_title(self) -> None: + self._winapi(windll.kernel32.SetConsoleTitleW, "") + + def erase_screen(self) -> None: + start = COORD(0, 0) + sbinfo = self.get_win32_screen_buffer_info() + length = sbinfo.dwSize.X * sbinfo.dwSize.Y + + self.cursor_goto(row=0, column=0) + self._erase(start, length) + + def erase_down(self) -> None: + sbinfo = self.get_win32_screen_buffer_info() + size = sbinfo.dwSize + + start = sbinfo.dwCursorPosition + length = (size.X - size.X) + size.X * (size.Y - sbinfo.dwCursorPosition.Y) + + self._erase(start, length) + + def erase_end_of_line(self) -> None: + """""" + sbinfo = self.get_win32_screen_buffer_info() + start = sbinfo.dwCursorPosition + length = sbinfo.dwSize.X - sbinfo.dwCursorPosition.X + + self._erase(start, length) + + def _erase(self, start, length): + chars_written = c_ulong() + + self._winapi( + windll.kernel32.FillConsoleOutputCharacterA, + self.hconsole, + c_char(b" "), + DWORD(length), + _coord_byval(start), + byref(chars_written), + ) + + # Reset attributes. + sbinfo = self.get_win32_screen_buffer_info() + self._winapi( + windll.kernel32.FillConsoleOutputAttribute, + self.hconsole, + sbinfo.wAttributes, + length, + _coord_byval(start), + byref(chars_written), + ) + + def reset_attributes(self) -> None: + " Reset the console foreground/background color. " + self._winapi( + windll.kernel32.SetConsoleTextAttribute, self.hconsole, self.default_attrs + ) + self._hidden = False + + def set_attributes(self, attrs: Attrs, color_depth: ColorDepth) -> None: + fgcolor, bgcolor, bold, underline, italic, blink, reverse, hidden = attrs + self._hidden = bool(hidden) + + # Start from the default attributes. + win_attrs: int = self.default_attrs + + if color_depth != ColorDepth.DEPTH_1_BIT: + # Override the last four bits: foreground color. + if fgcolor: + win_attrs = win_attrs & ~0xF + win_attrs |= self.color_lookup_table.lookup_fg_color(fgcolor) + + # Override the next four bits: background color. + if bgcolor: + win_attrs = win_attrs & ~0xF0 + win_attrs |= self.color_lookup_table.lookup_bg_color(bgcolor) + + # Reverse: swap these four bits groups. + if reverse: + win_attrs = ( + (win_attrs & ~0xFF) + | ((win_attrs & 0xF) << 4) + | ((win_attrs & 0xF0) >> 4) + ) + + self._winapi(windll.kernel32.SetConsoleTextAttribute, self.hconsole, win_attrs) + + def disable_autowrap(self) -> None: + # Not supported by Windows. + pass + + def enable_autowrap(self) -> None: + # Not supported by Windows. + pass + + def cursor_goto(self, row: int = 0, column: int = 0) -> None: + pos = COORD(x=column, y=row) + self._winapi( + windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) + ) + + def cursor_up(self, amount: int) -> None: + sr = self.get_win32_screen_buffer_info().dwCursorPosition + pos = COORD(sr.X, sr.Y - amount) + self._winapi( + windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) + ) + + def cursor_down(self, amount: int) -> None: + self.cursor_up(-amount) + + def cursor_forward(self, amount: int) -> None: + sr = self.get_win32_screen_buffer_info().dwCursorPosition + # assert sr.X + amount >= 0, 'Negative cursor position: x=%r amount=%r' % (sr.X, amount) + + pos = COORD(max(0, sr.X + amount), sr.Y) + self._winapi( + windll.kernel32.SetConsoleCursorPosition, self.hconsole, _coord_byval(pos) + ) + + def cursor_backward(self, amount: int) -> None: + self.cursor_forward(-amount) + + def flush(self) -> None: + """ + Write to output stream and flush. + """ + if not self._buffer: + # Only flush stdout buffer. (It could be that Python still has + # something in its buffer. -- We want to be sure to print that in + # the correct color.) + self.stdout.flush() + return + + data = "".join(self._buffer) + + if _DEBUG_RENDER_OUTPUT: + self.LOG.write(("%r" % data).encode("utf-8") + b"\n") + self.LOG.flush() + + # Print characters one by one. This appears to be the best solution + # in oder to avoid traces of vertical lines when the completion + # menu disappears. + for b in data: + written = DWORD() + + retval = windll.kernel32.WriteConsoleW( + self.hconsole, b, 1, byref(written), None + ) + assert retval != 0 + + self._buffer = [] + + def get_rows_below_cursor_position(self) -> int: + info = self.get_win32_screen_buffer_info() + return info.srWindow.Bottom - info.dwCursorPosition.Y + 1 + + def scroll_buffer_to_prompt(self) -> None: + """ + To be called before drawing the prompt. This should scroll the console + to left, with the cursor at the bottom (if possible). + """ + # Get current window size + info = self.get_win32_screen_buffer_info() + sr = info.srWindow + cursor_pos = info.dwCursorPosition + + result = SMALL_RECT() + + # Scroll to the left. + result.Left = 0 + result.Right = sr.Right - sr.Left + + # Scroll vertical + win_height = sr.Bottom - sr.Top + if 0 < sr.Bottom - cursor_pos.Y < win_height - 1: + # no vertical scroll if cursor already on the screen + result.Bottom = sr.Bottom + else: + result.Bottom = max(win_height, cursor_pos.Y) + result.Top = result.Bottom - win_height + + # Scroll API + self._winapi( + windll.kernel32.SetConsoleWindowInfo, self.hconsole, True, byref(result) + ) + + def enter_alternate_screen(self) -> None: + """ + Go to alternate screen buffer. + """ + if not self._in_alternate_screen: + GENERIC_READ = 0x80000000 + GENERIC_WRITE = 0x40000000 + + # Create a new console buffer and activate that one. + handle = HANDLE( + self._winapi( + windll.kernel32.CreateConsoleScreenBuffer, + GENERIC_READ | GENERIC_WRITE, + DWORD(0), + None, + DWORD(1), + None, + ) + ) + + self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, handle) + self.hconsole = handle + self._in_alternate_screen = True + + def quit_alternate_screen(self) -> None: + """ + Make stdout again the active buffer. + """ + if self._in_alternate_screen: + stdout = HANDLE( + self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE) + ) + self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, stdout) + self._winapi(windll.kernel32.CloseHandle, self.hconsole) + self.hconsole = stdout + self._in_alternate_screen = False + + def enable_mouse_support(self) -> None: + ENABLE_MOUSE_INPUT = 0x10 + handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) + + original_mode = DWORD() + self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode)) + self._winapi( + windll.kernel32.SetConsoleMode, + handle, + original_mode.value | ENABLE_MOUSE_INPUT, + ) + + def disable_mouse_support(self) -> None: + ENABLE_MOUSE_INPUT = 0x10 + handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)) + + original_mode = DWORD() + self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode)) + self._winapi( + windll.kernel32.SetConsoleMode, + handle, + original_mode.value & ~ENABLE_MOUSE_INPUT, + ) + + def hide_cursor(self) -> None: + pass + + def show_cursor(self) -> None: + pass + + @classmethod + def win32_refresh_window(cls) -> None: + """ + Call win32 API to refresh the whole Window. + + This is sometimes necessary when the application paints background + for completion menus. When the menu disappears, it leaves traces due + to a bug in the Windows Console. Sending a repaint request solves it. + """ + # Get console handle + handle = HANDLE(windll.kernel32.GetConsoleWindow()) + + RDW_INVALIDATE = 0x0001 + windll.user32.RedrawWindow(handle, None, None, c_uint(RDW_INVALIDATE)) + + def get_default_color_depth(self) -> ColorDepth: + """ + Return the default color depth for a windows terminal. + + Contrary to the Vt100 implementation, this doesn't depend on a $TERM + variable. + """ + if self.default_color_depth is not None: + return self.default_color_depth + + # For now, by default, always use 4 bit color on Windows 10 by default, + # even when vt100 escape sequences with + # ENABLE_VIRTUAL_TERMINAL_PROCESSING are supported. We don't have a + # reliable way yet to know whether our console supports true color or + # only 4-bit. + return ColorDepth.DEPTH_4_BIT + + +class FOREGROUND_COLOR: + BLACK = 0x0000 + BLUE = 0x0001 + GREEN = 0x0002 + CYAN = 0x0003 + RED = 0x0004 + MAGENTA = 0x0005 + YELLOW = 0x0006 + GRAY = 0x0007 + INTENSITY = 0x0008 # Foreground color is intensified. + + +class BACKGROUND_COLOR: + BLACK = 0x0000 + BLUE = 0x0010 + GREEN = 0x0020 + CYAN = 0x0030 + RED = 0x0040 + MAGENTA = 0x0050 + YELLOW = 0x0060 + GRAY = 0x0070 + INTENSITY = 0x0080 # Background color is intensified. + + +def _create_ansi_color_dict(color_cls) -> Dict[str, int]: + " Create a table that maps the 16 named ansi colors to their Windows code. " + return { + "ansidefault": color_cls.BLACK, + "ansiblack": color_cls.BLACK, + "ansigray": color_cls.GRAY, + "ansibrightblack": color_cls.BLACK | color_cls.INTENSITY, + "ansiwhite": color_cls.GRAY | color_cls.INTENSITY, + # Low intensity. + "ansired": color_cls.RED, + "ansigreen": color_cls.GREEN, + "ansiyellow": color_cls.YELLOW, + "ansiblue": color_cls.BLUE, + "ansimagenta": color_cls.MAGENTA, + "ansicyan": color_cls.CYAN, + # High intensity. + "ansibrightred": color_cls.RED | color_cls.INTENSITY, + "ansibrightgreen": color_cls.GREEN | color_cls.INTENSITY, + "ansibrightyellow": color_cls.YELLOW | color_cls.INTENSITY, + "ansibrightblue": color_cls.BLUE | color_cls.INTENSITY, + "ansibrightmagenta": color_cls.MAGENTA | color_cls.INTENSITY, + "ansibrightcyan": color_cls.CYAN | color_cls.INTENSITY, + } + + +FG_ANSI_COLORS = _create_ansi_color_dict(FOREGROUND_COLOR) +BG_ANSI_COLORS = _create_ansi_color_dict(BACKGROUND_COLOR) + +assert set(FG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) +assert set(BG_ANSI_COLORS) == set(ANSI_COLOR_NAMES) + + +class ColorLookupTable: + """ + Inspired by pygments/formatters/terminal256.py + """ + + def __init__(self) -> None: + self._win32_colors = self._build_color_table() + + # Cache (map color string to foreground and background code). + self.best_match: Dict[str, Tuple[int, int]] = {} + + @staticmethod + def _build_color_table() -> List[Tuple[int, int, int, int, int]]: + """ + Build an RGB-to-256 color conversion table + """ + FG = FOREGROUND_COLOR + BG = BACKGROUND_COLOR + + return [ + (0x00, 0x00, 0x00, FG.BLACK, BG.BLACK), + (0x00, 0x00, 0xAA, FG.BLUE, BG.BLUE), + (0x00, 0xAA, 0x00, FG.GREEN, BG.GREEN), + (0x00, 0xAA, 0xAA, FG.CYAN, BG.CYAN), + (0xAA, 0x00, 0x00, FG.RED, BG.RED), + (0xAA, 0x00, 0xAA, FG.MAGENTA, BG.MAGENTA), + (0xAA, 0xAA, 0x00, FG.YELLOW, BG.YELLOW), + (0x88, 0x88, 0x88, FG.GRAY, BG.GRAY), + (0x44, 0x44, 0xFF, FG.BLUE | FG.INTENSITY, BG.BLUE | BG.INTENSITY), + (0x44, 0xFF, 0x44, FG.GREEN | FG.INTENSITY, BG.GREEN | BG.INTENSITY), + (0x44, 0xFF, 0xFF, FG.CYAN | FG.INTENSITY, BG.CYAN | BG.INTENSITY), + (0xFF, 0x44, 0x44, FG.RED | FG.INTENSITY, BG.RED | BG.INTENSITY), + (0xFF, 0x44, 0xFF, FG.MAGENTA | FG.INTENSITY, BG.MAGENTA | BG.INTENSITY), + (0xFF, 0xFF, 0x44, FG.YELLOW | FG.INTENSITY, BG.YELLOW | BG.INTENSITY), + (0x44, 0x44, 0x44, FG.BLACK | FG.INTENSITY, BG.BLACK | BG.INTENSITY), + (0xFF, 0xFF, 0xFF, FG.GRAY | FG.INTENSITY, BG.GRAY | BG.INTENSITY), + ] + + def _closest_color(self, r: int, g: int, b: int) -> Tuple[int, int]: + distance = 257 * 257 * 3 # "infinity" (>distance from #000000 to #ffffff) + fg_match = 0 + bg_match = 0 + + for r_, g_, b_, fg_, bg_ in self._win32_colors: + rd = r - r_ + gd = g - g_ + bd = b - b_ + + d = rd * rd + gd * gd + bd * bd + + if d < distance: + fg_match = fg_ + bg_match = bg_ + distance = d + return fg_match, bg_match + + def _color_indexes(self, color: str) -> Tuple[int, int]: + indexes = self.best_match.get(color, None) + if indexes is None: + try: + rgb = int(str(color), 16) + except ValueError: + rgb = 0 + + r = (rgb >> 16) & 0xFF + g = (rgb >> 8) & 0xFF + b = rgb & 0xFF + indexes = self._closest_color(r, g, b) + self.best_match[color] = indexes + return indexes + + def lookup_fg_color(self, fg_color: str) -> int: + """ + Return the color for use in the + `windll.kernel32.SetConsoleTextAttribute` API call. + + :param fg_color: Foreground as text. E.g. 'ffffff' or 'red' + """ + # Foreground. + if fg_color in FG_ANSI_COLORS: + return FG_ANSI_COLORS[fg_color] + else: + return self._color_indexes(fg_color)[0] + + def lookup_bg_color(self, bg_color: str) -> int: + """ + Return the color for use in the + `windll.kernel32.SetConsoleTextAttribute` API call. + + :param bg_color: Background as text. E.g. 'ffffff' or 'red' + """ + # Background. + if bg_color in BG_ANSI_COLORS: + return BG_ANSI_COLORS[bg_color] + else: + return self._color_indexes(bg_color)[1] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/output/windows10.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/windows10.py new file mode 100644 index 00000000..1a481a2e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/output/windows10.py @@ -0,0 +1,104 @@ +from ctypes import byref, windll +from ctypes.wintypes import DWORD, HANDLE +from typing import Any, Optional, TextIO + +from prompt_toolkit.data_structures import Size +from prompt_toolkit.renderer import Output +from prompt_toolkit.utils import is_windows +from prompt_toolkit.win32_types import STD_OUTPUT_HANDLE + +from .color_depth import ColorDepth +from .vt100 import Vt100_Output +from .win32 import Win32Output + +__all__ = [ + "Windows10_Output", +] + +# See: https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms686033(v=vs.85).aspx +ENABLE_PROCESSED_INPUT = 0x0001 +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + + +class Windows10_Output: + """ + Windows 10 output abstraction. This enables and uses vt100 escape sequences. + """ + + def __init__( + self, stdout: TextIO, default_color_depth: Optional[ColorDepth] = None + ) -> None: + self.win32_output = Win32Output(stdout, default_color_depth=default_color_depth) + self.vt100_output = Vt100_Output( + stdout, lambda: Size(0, 0), default_color_depth=default_color_depth + ) + self._hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) + + def flush(self) -> None: + """ + Write to output stream and flush. + """ + original_mode = DWORD(0) + + # Remember the previous console mode. + windll.kernel32.GetConsoleMode(self._hconsole, byref(original_mode)) + + # Enable processing of vt100 sequences. + windll.kernel32.SetConsoleMode( + self._hconsole, + DWORD(ENABLE_PROCESSED_INPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING), + ) + + try: + self.vt100_output.flush() + finally: + # Restore console mode. + windll.kernel32.SetConsoleMode(self._hconsole, original_mode) + + @property + def responds_to_cpr(self) -> bool: + return False # We don't need this on Windows. + + def __getattr__(self, name: str) -> Any: + if name in ( + "get_size", + "get_rows_below_cursor_position", + "enable_mouse_support", + "disable_mouse_support", + "scroll_buffer_to_prompt", + "get_win32_screen_buffer_info", + "enable_bracketed_paste", + "disable_bracketed_paste", + "get_default_color_depth", + ): + return getattr(self.win32_output, name) + else: + return getattr(self.vt100_output, name) + + +Output.register(Windows10_Output) + + +def is_win_vt100_enabled() -> bool: + """ + Returns True when we're running Windows and VT100 escape sequences are + supported. + """ + if not is_windows(): + return False + + hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)) + + # Get original console mode. + original_mode = DWORD(0) + windll.kernel32.GetConsoleMode(hconsole, byref(original_mode)) + + try: + # Try to enable VT100 sequences. + result = windll.kernel32.SetConsoleMode( + hconsole, DWORD(ENABLE_PROCESSED_INPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING) + ) + + return result == 1 + finally: + windll.kernel32.SetConsoleMode(hconsole, original_mode) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/patch_stdout.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/patch_stdout.py new file mode 100644 index 00000000..a2cb5a14 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/patch_stdout.py @@ -0,0 +1,167 @@ +""" +patch_stdout +============ + +This implements a context manager that ensures that print statements within +it won't destroy the user interface. The context manager will replace +`sys.stdout` by something that draws the output above the current prompt, +rather than overwriting the UI. + +Usage:: + + with patch_stdout(application): + ... + application.run() + ... + +Multiple applications can run in the body of the context manager, one after the +other. +""" +import sys +import threading +from asyncio import get_event_loop +from contextlib import contextmanager +from typing import Generator, List, Optional, TextIO, cast + +from .application import run_in_terminal + +__all__ = [ + "patch_stdout", + "StdoutProxy", +] + + +@contextmanager +def patch_stdout(raw: bool = False) -> Generator[None, None, None]: + """ + Replace `sys.stdout` by an :class:`_StdoutProxy` instance. + + Writing to this proxy will make sure that the text appears above the + prompt, and that it doesn't destroy the output from the renderer. If no + application is curring, the behaviour should be identical to writing to + `sys.stdout` directly. + + Warning: If a new event loop is installed using `asyncio.set_event_loop()`, + then make sure that the context manager is applied after the event loop + is changed. Printing to stdout will be scheduled in the event loop + that's active when the context manager is created. + + :param raw: (`bool`) When True, vt100 terminal escape sequences are not + removed/escaped. + """ + proxy = cast(TextIO, StdoutProxy(raw=raw)) + + original_stdout = sys.stdout + original_stderr = sys.stderr + + # Enter. + sys.stdout = proxy + sys.stderr = proxy + + try: + yield + finally: + # Exit. + proxy.flush() + + sys.stdout = original_stdout + sys.stderr = original_stderr + + +class StdoutProxy: + """ + Proxy object for stdout which captures everything and prints output above + the current application. + """ + + def __init__( + self, raw: bool = False, original_stdout: Optional[TextIO] = None + ) -> None: + + original_stdout = original_stdout or sys.__stdout__ + + self.original_stdout = original_stdout + + self._lock = threading.RLock() + self._raw = raw + self._buffer: List[str] = [] + + # errors/encoding attribute for compatibility with sys.__stdout__. + self.errors = original_stdout.errors + self.encoding = original_stdout.encoding + + self.loop = get_event_loop() + + def _write_and_flush(self, text: str) -> None: + """ + Write the given text to stdout and flush. + If an application is running, use `run_in_terminal`. + """ + if not text: + # Don't bother calling `run_in_terminal` when there is nothing to + # display. + return + + def write_and_flush() -> None: + self.original_stdout.write(text) + self.original_stdout.flush() + + def write_and_flush_in_loop() -> None: + # If an application is running, use `run_in_terminal`, otherwise + # call it directly. + run_in_terminal.run_in_terminal(write_and_flush, in_executor=False) + + # Make sure `write_and_flush` is executed *in* the event loop, not in + # another thread. + self.loop.call_soon_threadsafe(write_and_flush_in_loop) + + def _write(self, data: str) -> None: + """ + Note: print()-statements cause to multiple write calls. + (write('line') and write('\n')). Of course we don't want to call + `run_in_terminal` for every individual call, because that's too + expensive, and as long as the newline hasn't been written, the + text itself is again overwritten by the rendering of the input + command line. Therefor, we have a little buffer which holds the + text until a newline is written to stdout. + """ + if "\n" in data: + # When there is a newline in the data, write everything before the + # newline, including the newline itself. + before, after = data.rsplit("\n", 1) + to_write = self._buffer + [before, "\n"] + self._buffer = [after] + + text = "".join(to_write) + self._write_and_flush(text) + else: + # Otherwise, cache in buffer. + self._buffer.append(data) + + def _flush(self) -> None: + text = "".join(self._buffer) + self._buffer = [] + self._write_and_flush(text) + + def write(self, data: str) -> int: + with self._lock: + self._write(data) + + return len(data) # Pretend everything was written. + + def flush(self) -> None: + """ + Flush buffered output. + """ + with self._lock: + self._flush() + + def fileno(self) -> int: + """ + Return file descriptor. + """ + # This is important for code that expects sys.stdout.fileno() to work. + return self.original_stdout.fileno() + + def isatty(self) -> bool: + return self.original_stdout.isatty() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/py.typed b/.venv/lib/python3.8/site-packages/prompt_toolkit/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/renderer.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/renderer.py new file mode 100644 index 00000000..b6000a11 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/renderer.py @@ -0,0 +1,783 @@ +""" +Renders the command line on the console. +(Redraws parts of the input line that were changed.) +""" +from asyncio import FIRST_COMPLETED, Future, sleep, wait +from collections import deque +from enum import Enum +from typing import TYPE_CHECKING, Any, Callable, Deque, Dict, Hashable, Optional, Tuple + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.data_structures import Point, Size +from prompt_toolkit.filters import FilterOrBool, to_filter +from prompt_toolkit.formatted_text import AnyFormattedText, to_formatted_text +from prompt_toolkit.layout.mouse_handlers import MouseHandlers +from prompt_toolkit.layout.screen import Char, Screen, WritePosition +from prompt_toolkit.output import ColorDepth, Output +from prompt_toolkit.styles import ( + Attrs, + BaseStyle, + DummyStyleTransformation, + StyleTransformation, +) + +if TYPE_CHECKING: + from prompt_toolkit.application import Application + from prompt_toolkit.layout.layout import Layout + + +__all__ = [ + "Renderer", + "print_formatted_text", +] + + +def _output_screen_diff( + app: "Application[Any]", + output: Output, + screen: Screen, + current_pos: Point, + color_depth: ColorDepth, + previous_screen: Optional[Screen], + last_style: Optional[str], + is_done: bool, # XXX: drop is_done + full_screen: bool, + attrs_for_style_string: "_StyleStringToAttrsCache", + style_string_has_style: "_StyleStringHasStyleCache", + size: Size, + previous_width: int, +) -> Tuple[Point, Optional[str]]: + """ + Render the diff between this screen and the previous screen. + + This takes two `Screen` instances. The one that represents the output like + it was during the last rendering and one that represents the current + output raster. Looking at these two `Screen` instances, this function will + render the difference by calling the appropriate methods of the `Output` + object that only paint the changes to the terminal. + + This is some performance-critical code which is heavily optimized. + Don't change things without profiling first. + + :param current_pos: Current cursor position. + :param last_style: The style string, used for drawing the last drawn + character. (Color/attributes.) + :param attrs_for_style_string: :class:`._StyleStringToAttrsCache` instance. + :param width: The width of the terminal. + :param previous_width: The width of the terminal during the last rendering. + """ + width, height = size.columns, size.rows + + #: Variable for capturing the output. + write = output.write + write_raw = output.write_raw + + # Create locals for the most used output methods. + # (Save expensive attribute lookups.) + _output_set_attributes = output.set_attributes + _output_reset_attributes = output.reset_attributes + _output_cursor_forward = output.cursor_forward + _output_cursor_up = output.cursor_up + _output_cursor_backward = output.cursor_backward + + # Hide cursor before rendering. (Avoid flickering.) + output.hide_cursor() + + def reset_attributes() -> None: + " Wrapper around Output.reset_attributes. " + nonlocal last_style + _output_reset_attributes() + last_style = None # Forget last char after resetting attributes. + + def move_cursor(new: Point) -> Point: + " Move cursor to this `new` point. Returns the given Point. " + current_x, current_y = current_pos.x, current_pos.y + + if new.y > current_y: + # Use newlines instead of CURSOR_DOWN, because this might add new lines. + # CURSOR_DOWN will never create new lines at the bottom. + # Also reset attributes, otherwise the newline could draw a + # background color. + reset_attributes() + write("\r\n" * (new.y - current_y)) + current_x = 0 + _output_cursor_forward(new.x) + return new + elif new.y < current_y: + _output_cursor_up(current_y - new.y) + + if current_x >= width - 1: + write("\r") + _output_cursor_forward(new.x) + elif new.x < current_x or current_x >= width - 1: + _output_cursor_backward(current_x - new.x) + elif new.x > current_x: + _output_cursor_forward(new.x - current_x) + + return new + + def output_char(char: Char) -> None: + """ + Write the output of this character. + """ + nonlocal last_style + + # If the last printed character has the same style, don't output the + # style again. + if last_style == char.style: + write(char.char) + else: + # Look up `Attr` for this style string. Only set attributes if different. + # (Two style strings can still have the same formatting.) + # Note that an empty style string can have formatting that needs to + # be applied, because of style transformations. + new_attrs = attrs_for_style_string[char.style] + if not last_style or new_attrs != attrs_for_style_string[last_style]: + _output_set_attributes(new_attrs, color_depth) + + write(char.char) + last_style = char.style + + def get_max_column_index(row: Dict[int, Char]) -> int: + """ + Return max used column index, ignoring whitespace (without style) at + the end of the line. This is important for people that copy/paste + terminal output. + + There are two reasons we are sometimes seeing whitespace at the end: + - `BufferControl` adds a trailing space to each line, because it's a + possible cursor position, so that the line wrapping won't change if + the cursor position moves around. + - The `Window` adds a style class to the current line for highlighting + (cursor-line). + """ + numbers = [ + index + for index, cell in row.items() + if cell.char != " " or style_string_has_style[cell.style] + ] + numbers.append(0) + return max(numbers) + + # Render for the first time: reset styling. + if not previous_screen: + reset_attributes() + + # Disable autowrap. (When entering a the alternate screen, or anytime when + # we have a prompt. - In the case of a REPL, like IPython, people can have + # background threads, and it's hard for debugging if their output is not + # wrapped.) + if not previous_screen or not full_screen: + output.disable_autowrap() + + # When the previous screen has a different size, redraw everything anyway. + # Also when we are done. (We might take up less rows, so clearing is important.) + if ( + is_done or not previous_screen or previous_width != width + ): # XXX: also consider height?? + current_pos = move_cursor(Point(x=0, y=0)) + reset_attributes() + output.erase_down() + + previous_screen = Screen() + + # Get height of the screen. + # (height changes as we loop over data_buffer, so remember the current value.) + # (Also make sure to clip the height to the size of the output.) + current_height = min(screen.height, height) + + # Loop over the rows. + row_count = min(max(screen.height, previous_screen.height), height) + c = 0 # Column counter. + + for y in range(row_count): + new_row = screen.data_buffer[y] + previous_row = previous_screen.data_buffer[y] + zero_width_escapes_row = screen.zero_width_escapes[y] + + new_max_line_len = min(width - 1, get_max_column_index(new_row)) + previous_max_line_len = min(width - 1, get_max_column_index(previous_row)) + + # Loop over the columns. + c = 0 + while c <= new_max_line_len: + new_char = new_row[c] + old_char = previous_row[c] + char_width = new_char.width or 1 + + # When the old and new character at this position are different, + # draw the output. (Because of the performance, we don't call + # `Char.__ne__`, but inline the same expression.) + if new_char.char != old_char.char or new_char.style != old_char.style: + current_pos = move_cursor(Point(x=c, y=y)) + + # Send injected escape sequences to output. + if c in zero_width_escapes_row: + write_raw(zero_width_escapes_row[c]) + + output_char(new_char) + current_pos = Point(x=current_pos.x + char_width, y=current_pos.y) + + c += char_width + + # If the new line is shorter, trim it. + if previous_screen and new_max_line_len < previous_max_line_len: + current_pos = move_cursor(Point(x=new_max_line_len + 1, y=y)) + reset_attributes() + output.erase_end_of_line() + + # Correctly reserve vertical space as required by the layout. + # When this is a new screen (drawn for the first time), or for some reason + # higher than the previous one. Move the cursor once to the bottom of the + # output. That way, we're sure that the terminal scrolls up, even when the + # lower lines of the canvas just contain whitespace. + + # The most obvious reason that we actually want this behaviour is the avoid + # the artifact of the input scrolling when the completion menu is shown. + # (If the scrolling is actually wanted, the layout can still be build in a + # way to behave that way by setting a dynamic height.) + if current_height > previous_screen.height: + current_pos = move_cursor(Point(x=0, y=current_height - 1)) + + # Move cursor: + if is_done: + current_pos = move_cursor(Point(x=0, y=current_height)) + output.erase_down() + else: + current_pos = move_cursor(screen.get_cursor_position(app.layout.current_window)) + + if is_done or not full_screen: + output.enable_autowrap() + + # Always reset the color attributes. This is important because a background + # thread could print data to stdout and we want that to be displayed in the + # default colors. (Also, if a background color has been set, many terminals + # give weird artifacts on resize events.) + reset_attributes() + + if screen.show_cursor or is_done: + output.show_cursor() + + return current_pos, last_style + + +class HeightIsUnknownError(Exception): + " Information unavailable. Did not yet receive the CPR response. " + + +class _StyleStringToAttrsCache(Dict[str, Attrs]): + """ + A cache structure that maps style strings to :class:`.Attr`. + (This is an important speed up.) + """ + + def __init__( + self, + get_attrs_for_style_str: Callable[["str"], Attrs], + style_transformation: StyleTransformation, + ) -> None: + + self.get_attrs_for_style_str = get_attrs_for_style_str + self.style_transformation = style_transformation + + def __missing__(self, style_str: str) -> Attrs: + attrs = self.get_attrs_for_style_str(style_str) + attrs = self.style_transformation.transform_attrs(attrs) + + self[style_str] = attrs + return attrs + + +class _StyleStringHasStyleCache(Dict[str, bool]): + """ + Cache for remember which style strings don't render the default output + style (default fg/bg, no underline and no reverse and no blink). That way + we know that we should render these cells, even when they're empty (when + they contain a space). + + Note: we don't consider bold/italic/hidden because they don't change the + output if there's no text in the cell. + """ + + def __init__(self, style_string_to_attrs: Dict[str, Attrs]) -> None: + self.style_string_to_attrs = style_string_to_attrs + + def __missing__(self, style_str: str) -> bool: + attrs = self.style_string_to_attrs[style_str] + is_default = bool( + attrs.color + or attrs.bgcolor + or attrs.underline + or attrs.blink + or attrs.reverse + ) + + self[style_str] = is_default + return is_default + + +class CPR_Support(Enum): + " Enum: whether or not CPR is supported. " + SUPPORTED = "SUPPORTED" + NOT_SUPPORTED = "NOT_SUPPORTED" + UNKNOWN = "UNKNOWN" + + +class Renderer: + """ + Typical usage: + + :: + + output = Vt100_Output.from_pty(sys.stdout) + r = Renderer(style, output) + r.render(app, layout=...) + """ + + CPR_TIMEOUT = 2 # Time to wait until we consider CPR to be not supported. + + def __init__( + self, + style: BaseStyle, + output: Output, + full_screen: bool = False, + mouse_support: FilterOrBool = False, + cpr_not_supported_callback: Optional[Callable[[], None]] = None, + ) -> None: + + self.style = style + self.output = output + self.full_screen = full_screen + self.mouse_support = to_filter(mouse_support) + self.cpr_not_supported_callback = cpr_not_supported_callback + + self._in_alternate_screen = False + self._mouse_support_enabled = False + self._bracketed_paste_enabled = False + + # Future set when we are waiting for a CPR flag. + self._waiting_for_cpr_futures: Deque[Future[None]] = deque() + self.cpr_support = CPR_Support.UNKNOWN + + if not output.responds_to_cpr: + self.cpr_support = CPR_Support.NOT_SUPPORTED + + # Cache for the style. + self._attrs_for_style: Optional[_StyleStringToAttrsCache] = None + self._style_string_has_style: Optional[_StyleStringHasStyleCache] = None + self._last_style_hash: Optional[Hashable] = None + self._last_transformation_hash: Optional[Hashable] = None + self._last_color_depth: Optional[ColorDepth] = None + + self.reset(_scroll=True) + + def reset(self, _scroll: bool = False, leave_alternate_screen: bool = True) -> None: + + # Reset position + self._cursor_pos = Point(x=0, y=0) + + # Remember the last screen instance between renderers. This way, + # we can create a `diff` between two screens and only output the + # difference. It's also to remember the last height. (To show for + # instance a toolbar at the bottom position.) + self._last_screen: Optional[Screen] = None + self._last_size: Optional[Size] = None + self._last_style: Optional[str] = None + + # Default MouseHandlers. (Just empty.) + self.mouse_handlers = MouseHandlers() + + #: Space from the top of the layout, until the bottom of the terminal. + #: We don't know this until a `report_absolute_cursor_row` call. + self._min_available_height = 0 + + # In case of Windows, also make sure to scroll to the current cursor + # position. (Only when rendering the first time.) + # It does nothing for vt100 terminals. + if _scroll: + self.output.scroll_buffer_to_prompt() + + # Quit alternate screen. + if self._in_alternate_screen and leave_alternate_screen: + self.output.quit_alternate_screen() + self._in_alternate_screen = False + + # Disable mouse support. + if self._mouse_support_enabled: + self.output.disable_mouse_support() + self._mouse_support_enabled = False + + # Disable bracketed paste. + if self._bracketed_paste_enabled: + self.output.disable_bracketed_paste() + self._bracketed_paste_enabled = False + + # Flush output. `disable_mouse_support` needs to write to stdout. + self.output.flush() + + @property + def last_rendered_screen(self) -> Optional[Screen]: + """ + The `Screen` class that was generated during the last rendering. + This can be `None`. + """ + return self._last_screen + + @property + def height_is_known(self) -> bool: + """ + True when the height from the cursor until the bottom of the terminal + is known. (It's often nicer to draw bottom toolbars only if the height + is known, in order to avoid flickering when the CPR response arrives.) + """ + if self.full_screen or self._min_available_height > 0: + return True + try: + self._min_available_height = self.output.get_rows_below_cursor_position() + return True + except NotImplementedError: + return False + + @property + def rows_above_layout(self) -> int: + """ + Return the number of rows visible in the terminal above the layout. + """ + if self._in_alternate_screen: + return 0 + elif self._min_available_height > 0: + total_rows = self.output.get_size().rows + last_screen_height = self._last_screen.height if self._last_screen else 0 + return total_rows - max(self._min_available_height, last_screen_height) + else: + raise HeightIsUnknownError("Rows above layout is unknown.") + + def request_absolute_cursor_position(self) -> None: + """ + Get current cursor position. + + We do this to calculate the minimum available height that we can + consume for rendering the prompt. This is the available space below te + cursor. + + For vt100: Do CPR request. (answer will arrive later.) + For win32: Do API call. (Answer comes immediately.) + """ + # Only do this request when the cursor is at the top row. (after a + # clear or reset). We will rely on that in `report_absolute_cursor_row`. + assert self._cursor_pos.y == 0 + + # In full-screen mode, always use the total height as min-available-height. + if self.full_screen: + self._min_available_height = self.output.get_size().rows + return + + # For Win32, we have an API call to get the number of rows below the + # cursor. + try: + self._min_available_height = self.output.get_rows_below_cursor_position() + return + except NotImplementedError: + pass + + # Use CPR. + if self.cpr_support == CPR_Support.NOT_SUPPORTED: + return + + def do_cpr() -> None: + # Asks for a cursor position report (CPR). + self._waiting_for_cpr_futures.append(Future()) + self.output.ask_for_cpr() + + if self.cpr_support == CPR_Support.SUPPORTED: + do_cpr() + return + + # If we don't know whether CPR is supported, only do a request if + # none is pending, and test it, using a timer. + if self.waiting_for_cpr: + return + + do_cpr() + + async def timer() -> None: + await sleep(self.CPR_TIMEOUT) + + # Not set in the meantime -> not supported. + if self.cpr_support == CPR_Support.UNKNOWN: + self.cpr_support = CPR_Support.NOT_SUPPORTED + + if self.cpr_not_supported_callback: + # Make sure to call this callback in the main thread. + self.cpr_not_supported_callback() + + get_app().create_background_task(timer()) + + def report_absolute_cursor_row(self, row: int) -> None: + """ + To be called when we know the absolute cursor position. + (As an answer of a "Cursor Position Request" response.) + """ + self.cpr_support = CPR_Support.SUPPORTED + + # Calculate the amount of rows from the cursor position until the + # bottom of the terminal. + total_rows = self.output.get_size().rows + rows_below_cursor = total_rows - row + 1 + + # Set the minimum available height. + self._min_available_height = rows_below_cursor + + # Pop and set waiting for CPR future. + try: + f = self._waiting_for_cpr_futures.popleft() + except IndexError: + pass # Received CPR response without having a CPR. + else: + f.set_result(None) + + @property + def waiting_for_cpr(self) -> bool: + """ + Waiting for CPR flag. True when we send the request, but didn't got a + response. + """ + return bool(self._waiting_for_cpr_futures) + + async def wait_for_cpr_responses(self, timeout: int = 1) -> None: + """ + Wait for a CPR response. + """ + cpr_futures = list(self._waiting_for_cpr_futures) # Make copy. + + # When there are no CPRs in the queue. Don't do anything. + if not cpr_futures or self.cpr_support == CPR_Support.NOT_SUPPORTED: + return None + + async def wait_for_responses() -> None: + for response_f in cpr_futures: + await response_f + + async def wait_for_timeout() -> None: + await sleep(timeout) + + # Got timeout, erase queue. + for response_f in cpr_futures: + response_f.cancel() + self._waiting_for_cpr_futures = deque() + + coroutines = [ + wait_for_responses(), + wait_for_timeout(), + ] + _, pending = await wait(coroutines, return_when=FIRST_COMPLETED) + for task in pending: + task.cancel() + + def render( + self, app: "Application[Any]", layout: "Layout", is_done: bool = False + ) -> None: + """ + Render the current interface to the output. + + :param is_done: When True, put the cursor at the end of the interface. We + won't print any changes to this part. + """ + output = self.output + + # Enter alternate screen. + if self.full_screen and not self._in_alternate_screen: + self._in_alternate_screen = True + output.enter_alternate_screen() + + # Enable bracketed paste. + if not self._bracketed_paste_enabled: + self.output.enable_bracketed_paste() + self._bracketed_paste_enabled = True + + # Enable/disable mouse support. + needs_mouse_support = self.mouse_support() + + if needs_mouse_support and not self._mouse_support_enabled: + output.enable_mouse_support() + self._mouse_support_enabled = True + + elif not needs_mouse_support and self._mouse_support_enabled: + output.disable_mouse_support() + self._mouse_support_enabled = False + + # Create screen and write layout to it. + size = output.get_size() + screen = Screen() + screen.show_cursor = False # Hide cursor by default, unless one of the + # containers decides to display it. + mouse_handlers = MouseHandlers() + + # Calculate height. + if self.full_screen: + height = size.rows + elif is_done: + # When we are done, we don't necessary want to fill up until the bottom. + height = layout.container.preferred_height( + size.columns, size.rows + ).preferred + else: + last_height = self._last_screen.height if self._last_screen else 0 + height = max( + self._min_available_height, + last_height, + layout.container.preferred_height(size.columns, size.rows).preferred, + ) + + height = min(height, size.rows) + + # When te size changes, don't consider the previous screen. + if self._last_size != size: + self._last_screen = None + + # When we render using another style or another color depth, do a full + # repaint. (Forget about the previous rendered screen.) + # (But note that we still use _last_screen to calculate the height.) + if ( + self.style.invalidation_hash() != self._last_style_hash + or app.style_transformation.invalidation_hash() + != self._last_transformation_hash + or app.color_depth != self._last_color_depth + ): + self._last_screen = None + self._attrs_for_style = None + self._style_string_has_style = None + + if self._attrs_for_style is None: + self._attrs_for_style = _StyleStringToAttrsCache( + self.style.get_attrs_for_style_str, app.style_transformation + ) + if self._style_string_has_style is None: + self._style_string_has_style = _StyleStringHasStyleCache( + self._attrs_for_style + ) + + self._last_style_hash = self.style.invalidation_hash() + self._last_transformation_hash = app.style_transformation.invalidation_hash() + self._last_color_depth = app.color_depth + + layout.container.write_to_screen( + screen, + mouse_handlers, + WritePosition(xpos=0, ypos=0, width=size.columns, height=height), + parent_style="", + erase_bg=False, + z_index=None, + ) + screen.draw_all_floats() + + # When grayed. Replace all styles in the new screen. + if app.exit_style: + screen.append_style_to_content(app.exit_style) + + # Process diff and write to output. + self._cursor_pos, self._last_style = _output_screen_diff( + app, + output, + screen, + self._cursor_pos, + app.color_depth, + self._last_screen, + self._last_style, + is_done, + full_screen=self.full_screen, + attrs_for_style_string=self._attrs_for_style, + style_string_has_style=self._style_string_has_style, + size=size, + previous_width=(self._last_size.columns if self._last_size else 0), + ) + self._last_screen = screen + self._last_size = size + self.mouse_handlers = mouse_handlers + + output.flush() + + # Set visible windows in layout. + app.layout.visible_windows = screen.visible_windows + + if is_done: + self.reset() + + def erase(self, leave_alternate_screen: bool = True) -> None: + """ + Hide all output and put the cursor back at the first line. This is for + instance used for running a system command (while hiding the CLI) and + later resuming the same CLI.) + + :param leave_alternate_screen: When True, and when inside an alternate + screen buffer, quit the alternate screen. + """ + output = self.output + + output.cursor_backward(self._cursor_pos.x) + output.cursor_up(self._cursor_pos.y) + output.erase_down() + output.reset_attributes() + output.enable_autowrap() + output.flush() + + self.reset(leave_alternate_screen=leave_alternate_screen) + + def clear(self) -> None: + """ + Clear screen and go to 0,0 + """ + # Erase current output first. + self.erase() + + # Send "Erase Screen" command and go to (0, 0). + output = self.output + + output.erase_screen() + output.cursor_goto(0, 0) + output.flush() + + self.request_absolute_cursor_position() + + +def print_formatted_text( + output: Output, + formatted_text: AnyFormattedText, + style: BaseStyle, + style_transformation: Optional[StyleTransformation] = None, + color_depth: Optional[ColorDepth] = None, +) -> None: + """ + Print a list of (style_str, text) tuples in the given style to the output. + """ + fragments = to_formatted_text(formatted_text) + style_transformation = style_transformation or DummyStyleTransformation() + color_depth = color_depth or output.get_default_color_depth() + + # Reset first. + output.reset_attributes() + output.enable_autowrap() + + # Print all (style_str, text) tuples. + attrs_for_style_string = _StyleStringToAttrsCache( + style.get_attrs_for_style_str, style_transformation + ) + + for style_str, text, *_ in fragments: + attrs = attrs_for_style_string[style_str] + + if attrs: + output.set_attributes(attrs, color_depth) + else: + output.reset_attributes() + + # Eliminate carriage returns + text = text.replace("\r", "") + + # Assume that the output is raw, and insert a carriage return before + # every newline. (Also important when the front-end is a telnet client.) + output.write(text.replace("\n", "\r\n")) + + # Reset again. + output.reset_attributes() + output.flush() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/search.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/search.py new file mode 100644 index 00000000..6f94afe9 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/search.py @@ -0,0 +1,229 @@ +""" +Search operations. + +For the key bindings implementation with attached filters, check +`prompt_toolkit.key_binding.bindings.search`. (Use these for new key bindings +instead of calling these function directly.) +""" +from enum import Enum +from typing import TYPE_CHECKING, Dict, Optional + +from .application.current import get_app +from .filters import FilterOrBool, is_searching, to_filter +from .key_binding.vi_state import InputMode + +if TYPE_CHECKING: + from prompt_toolkit.layout.controls import BufferControl, SearchBufferControl + from prompt_toolkit.layout.layout import Layout + +__all__ = [ + "SearchDirection", + "start_search", + "stop_search", +] + + +class SearchDirection(Enum): + FORWARD = "FORWARD" + BACKWARD = "BACKWARD" + + +class SearchState: + """ + A search 'query', associated with a search field (like a SearchToolbar). + + Every searchable `BufferControl` points to a `search_buffer_control` + (another `BufferControls`) which represents the search field. The + `SearchState` attached to that search field is used for storing the current + search query. + + It is possible to have one searchfield for multiple `BufferControls`. In + that case, they'll share the same `SearchState`. + If there are multiple `BufferControls` that display the same `Buffer`, then + they can have a different `SearchState` each (if they have a different + search control). + """ + + __slots__ = ("text", "direction", "ignore_case") + + def __init__( + self, + text: str = "", + direction: SearchDirection = SearchDirection.FORWARD, + ignore_case: FilterOrBool = False, + ) -> None: + + self.text = text + self.direction = direction + self.ignore_case = to_filter(ignore_case) + + def __repr__(self) -> str: + return "%s(%r, direction=%r, ignore_case=%r)" % ( + self.__class__.__name__, + self.text, + self.direction, + self.ignore_case, + ) + + def __invert__(self) -> "SearchState": + """ + Create a new SearchState where backwards becomes forwards and the other + way around. + """ + if self.direction == SearchDirection.BACKWARD: + direction = SearchDirection.FORWARD + else: + direction = SearchDirection.BACKWARD + + return SearchState( + text=self.text, direction=direction, ignore_case=self.ignore_case + ) + + +def start_search( + buffer_control: Optional["BufferControl"] = None, + direction: SearchDirection = SearchDirection.FORWARD, +) -> None: + """ + Start search through the given `buffer_control` using the + `search_buffer_control`. + + :param buffer_control: Start search for this `BufferControl`. If not given, + search through the current control. + """ + from prompt_toolkit.layout.controls import BufferControl + + assert buffer_control is None or isinstance(buffer_control, BufferControl) + + layout = get_app().layout + + # When no control is given, use the current control if that's a BufferControl. + if buffer_control is None: + if not isinstance(layout.current_control, BufferControl): + return + buffer_control = layout.current_control + + # Only if this control is searchable. + search_buffer_control = buffer_control.search_buffer_control + + if search_buffer_control: + buffer_control.search_state.direction = direction + + # Make sure to focus the search BufferControl + layout.focus(search_buffer_control) + + # Remember search link. + layout.search_links[search_buffer_control] = buffer_control + + # If we're in Vi mode, make sure to go into insert mode. + get_app().vi_state.input_mode = InputMode.INSERT + + +def stop_search(buffer_control: Optional["BufferControl"] = None) -> None: + """ + Stop search through the given `buffer_control`. + """ + layout = get_app().layout + + if buffer_control is None: + buffer_control = layout.search_target_buffer_control + if buffer_control is None: + # (Should not happen, but possible when `stop_search` is called + # when we're not searching.) + return + search_buffer_control = buffer_control.search_buffer_control + else: + assert buffer_control in layout.search_links.values() + search_buffer_control = _get_reverse_search_links(layout)[buffer_control] + + # Focus the original buffer again. + layout.focus(buffer_control) + + if search_buffer_control is not None: + # Remove the search link. + del layout.search_links[search_buffer_control] + + # Reset content of search control. + search_buffer_control.buffer.reset() + + # If we're in Vi mode, go back to navigation mode. + get_app().vi_state.input_mode = InputMode.NAVIGATION + + +def do_incremental_search(direction: SearchDirection, count: int = 1) -> None: + """ + Apply search, but keep search buffer focused. + """ + assert is_searching() + + layout = get_app().layout + + # Only search if the current control is a `BufferControl`. + from prompt_toolkit.layout.controls import BufferControl + + search_control = layout.current_control + if not isinstance(search_control, BufferControl): + return + + prev_control = layout.search_target_buffer_control + if prev_control is None: + return + search_state = prev_control.search_state + + # Update search_state. + direction_changed = search_state.direction != direction + + search_state.text = search_control.buffer.text + search_state.direction = direction + + # Apply search to current buffer. + if not direction_changed: + prev_control.buffer.apply_search( + search_state, include_current_position=False, count=count + ) + + +def accept_search() -> None: + """ + Accept current search query. Focus original `BufferControl` again. + """ + layout = get_app().layout + + search_control = layout.current_control + target_buffer_control = layout.search_target_buffer_control + + from prompt_toolkit.layout.controls import BufferControl + + if not isinstance(search_control, BufferControl): + return + if target_buffer_control is None: + return + + search_state = target_buffer_control.search_state + + # Update search state. + if search_control.buffer.text: + search_state.text = search_control.buffer.text + + # Apply search. + target_buffer_control.buffer.apply_search( + search_state, include_current_position=True + ) + + # Add query to history of search line. + search_control.buffer.append_to_history() + + # Stop search and focus previous control again. + stop_search(target_buffer_control) + + +def _get_reverse_search_links( + layout: "Layout", +) -> Dict["BufferControl", "SearchBufferControl"]: + """ + Return mapping from BufferControl to SearchBufferControl. + """ + return { + buffer_control: search_buffer_control + for search_buffer_control, buffer_control in layout.search_links.items() + } diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/selection.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/selection.py new file mode 100644 index 00000000..5d2545bd --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/selection.py @@ -0,0 +1,60 @@ +""" +Data structures for the selection. +""" +from enum import Enum + +__all__ = [ + "SelectionType", + "PasteMode", + "SelectionState", +] + + +class SelectionType(Enum): + """ + Type of selection. + """ + + #: Characters. (Visual in Vi.) + CHARACTERS = "CHARACTERS" + + #: Whole lines. (Visual-Line in Vi.) + LINES = "LINES" + + #: A block selection. (Visual-Block in Vi.) + BLOCK = "BLOCK" + + +class PasteMode(Enum): + EMACS = "EMACS" # Yank like emacs. + VI_AFTER = "VI_AFTER" # When pressing 'p' in Vi. + VI_BEFORE = "VI_BEFORE" # When pressing 'P' in Vi. + + +class SelectionState: + """ + State of the current selection. + + :param original_cursor_position: int + :param type: :class:`~.SelectionType` + """ + + def __init__( + self, + original_cursor_position: int = 0, + type: SelectionType = SelectionType.CHARACTERS, + ) -> None: + + self.original_cursor_position = original_cursor_position + self.type = type + self.shift_mode = False + + def enter_shift_mode(self) -> None: + self.shift_mode = True + + def __repr__(self) -> str: + return "%s(original_cursor_position=%r, type=%r)" % ( + self.__class__.__name__, + self.original_cursor_position, + self.type, + ) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__init__.py new file mode 100644 index 00000000..10ad3148 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__init__.py @@ -0,0 +1,44 @@ +from .dialogs import ( + button_dialog, + checkboxlist_dialog, + input_dialog, + message_dialog, + progress_dialog, + radiolist_dialog, + yes_no_dialog, +) +from .progress_bar import ProgressBar, ProgressBarCounter +from .prompt import ( + CompleteStyle, + PromptSession, + confirm, + create_confirm_session, + prompt, +) +from .utils import clear, clear_title, print_container, print_formatted_text, set_title + +__all__ = [ + # Dialogs. + "input_dialog", + "message_dialog", + "progress_dialog", + "checkboxlist_dialog", + "radiolist_dialog", + "yes_no_dialog", + "button_dialog", + # Prompts. + "PromptSession", + "prompt", + "confirm", + "create_confirm_session", + "CompleteStyle", + # Progress bars. + "ProgressBar", + "ProgressBarCounter", + # Utils. + "clear", + "clear_title", + "print_container", + "print_formatted_text", + "set_title", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..db7c2182 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/dialogs.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/dialogs.cpython-38.pyc new file mode 100644 index 00000000..36accea6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/dialogs.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/prompt.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/prompt.cpython-38.pyc new file mode 100644 index 00000000..cbc3ef71 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/prompt.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/utils.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/utils.cpython-38.pyc new file mode 100644 index 00000000..f09d893f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/__pycache__/utils.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/dialogs.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/dialogs.py new file mode 100644 index 00000000..06b8eb6f --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/dialogs.py @@ -0,0 +1,324 @@ +import functools +from asyncio import get_event_loop +from typing import Any, Callable, List, Optional, Tuple, TypeVar + +from prompt_toolkit.application import Application +from prompt_toolkit.application.current import get_app +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.completion import Completer +from prompt_toolkit.eventloop import run_in_executor_with_context +from prompt_toolkit.filters import FilterOrBool +from prompt_toolkit.formatted_text import AnyFormattedText +from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous +from prompt_toolkit.key_binding.defaults import load_key_bindings +from prompt_toolkit.key_binding.key_bindings import KeyBindings, merge_key_bindings +from prompt_toolkit.layout import Layout +from prompt_toolkit.layout.containers import AnyContainer, HSplit +from prompt_toolkit.layout.dimension import Dimension as D +from prompt_toolkit.styles import BaseStyle +from prompt_toolkit.validation import Validator +from prompt_toolkit.widgets import ( + Box, + Button, + CheckboxList, + Dialog, + Label, + ProgressBar, + RadioList, + TextArea, + ValidationToolbar, +) + +__all__ = [ + "yes_no_dialog", + "button_dialog", + "input_dialog", + "message_dialog", + "radiolist_dialog", + "checkboxlist_dialog", + "progress_dialog", +] + + +def yes_no_dialog( + title: AnyFormattedText = "", + text: AnyFormattedText = "", + yes_text: str = "Yes", + no_text: str = "No", + style: Optional[BaseStyle] = None, +) -> Application[bool]: + """ + Display a Yes/No dialog. + Return a boolean. + """ + + def yes_handler() -> None: + get_app().exit(result=True) + + def no_handler() -> None: + get_app().exit(result=False) + + dialog = Dialog( + title=title, + body=Label(text=text, dont_extend_height=True), + buttons=[ + Button(text=yes_text, handler=yes_handler), + Button(text=no_text, handler=no_handler), + ], + with_background=True, + ) + + return _create_app(dialog, style) + + +_T = TypeVar("_T") + + +def button_dialog( + title: AnyFormattedText = "", + text: AnyFormattedText = "", + buttons: List[Tuple[str, _T]] = [], + style: Optional[BaseStyle] = None, +) -> Application[_T]: + """ + Display a dialog with button choices (given as a list of tuples). + Return the value associated with button. + """ + + def button_handler(v: _T) -> None: + get_app().exit(result=v) + + dialog = Dialog( + title=title, + body=Label(text=text, dont_extend_height=True), + buttons=[ + Button(text=t, handler=functools.partial(button_handler, v)) + for t, v in buttons + ], + with_background=True, + ) + + return _create_app(dialog, style) + + +def input_dialog( + title: AnyFormattedText = "", + text: AnyFormattedText = "", + ok_text: str = "OK", + cancel_text: str = "Cancel", + completer: Optional[Completer] = None, + validator: Optional[Validator] = None, + password: FilterOrBool = False, + style: Optional[BaseStyle] = None, +) -> Application[str]: + """ + Display a text input box. + Return the given text, or None when cancelled. + """ + + def accept(buf: Buffer) -> bool: + get_app().layout.focus(ok_button) + return True # Keep text. + + def ok_handler() -> None: + get_app().exit(result=textfield.text) + + ok_button = Button(text=ok_text, handler=ok_handler) + cancel_button = Button(text=cancel_text, handler=_return_none) + + textfield = TextArea( + multiline=False, + password=password, + completer=completer, + validator=validator, + accept_handler=accept, + ) + + dialog = Dialog( + title=title, + body=HSplit( + [ + Label(text=text, dont_extend_height=True), + textfield, + ValidationToolbar(), + ], + padding=D(preferred=1, max=1), + ), + buttons=[ok_button, cancel_button], + with_background=True, + ) + + return _create_app(dialog, style) + + +def message_dialog( + title: AnyFormattedText = "", + text: AnyFormattedText = "", + ok_text: str = "Ok", + style: Optional[BaseStyle] = None, +) -> Application[None]: + """ + Display a simple message box and wait until the user presses enter. + """ + dialog = Dialog( + title=title, + body=Label(text=text, dont_extend_height=True), + buttons=[Button(text=ok_text, handler=_return_none)], + with_background=True, + ) + + return _create_app(dialog, style) + + +def radiolist_dialog( + title: AnyFormattedText = "", + text: AnyFormattedText = "", + ok_text: str = "Ok", + cancel_text: str = "Cancel", + values: Optional[List[Tuple[_T, AnyFormattedText]]] = None, + style: Optional[BaseStyle] = None, +) -> Application[_T]: + """ + Display a simple list of element the user can choose amongst. + + Only one element can be selected at a time using Arrow keys and Enter. + The focus can be moved between the list and the Ok/Cancel button with tab. + """ + if values is None: + values = [] + + def ok_handler() -> None: + get_app().exit(result=radio_list.current_value) + + radio_list = RadioList(values) + + dialog = Dialog( + title=title, + body=HSplit( + [Label(text=text, dont_extend_height=True), radio_list], + padding=1, + ), + buttons=[ + Button(text=ok_text, handler=ok_handler), + Button(text=cancel_text, handler=_return_none), + ], + with_background=True, + ) + + return _create_app(dialog, style) + + +def checkboxlist_dialog( + title: AnyFormattedText = "", + text: AnyFormattedText = "", + ok_text: str = "Ok", + cancel_text: str = "Cancel", + values: Optional[List[Tuple[_T, AnyFormattedText]]] = None, + style: Optional[BaseStyle] = None, +) -> Application[List[_T]]: + """ + Display a simple list of element the user can choose multiple values amongst. + + Several elements can be selected at a time using Arrow keys and Enter. + The focus can be moved between the list and the Ok/Cancel button with tab. + """ + if values is None: + values = [] + + def ok_handler() -> None: + get_app().exit(result=cb_list.current_values) + + cb_list = CheckboxList(values) + + dialog = Dialog( + title=title, + body=HSplit( + [Label(text=text, dont_extend_height=True), cb_list], + padding=1, + ), + buttons=[ + Button(text=ok_text, handler=ok_handler), + Button(text=cancel_text, handler=_return_none), + ], + with_background=True, + ) + + return _create_app(dialog, style) + + +def progress_dialog( + title: AnyFormattedText = "", + text: AnyFormattedText = "", + run_callback: Callable[[Callable[[int], None], Callable[[str], None]], None] = ( + lambda *a: None + ), + style: Optional[BaseStyle] = None, +) -> Application[None]: + """ + :param run_callback: A function that receives as input a `set_percentage` + function and it does the work. + """ + loop = get_event_loop() + progressbar = ProgressBar() + text_area = TextArea( + focusable=False, + # Prefer this text area as big as possible, to avoid having a window + # that keeps resizing when we add text to it. + height=D(preferred=10 ** 10), + ) + + dialog = Dialog( + body=HSplit( + [ + Box(Label(text=text)), + Box(text_area, padding=D.exact(1)), + progressbar, + ] + ), + title=title, + with_background=True, + ) + app = _create_app(dialog, style) + + def set_percentage(value: int) -> None: + progressbar.percentage = int(value) + app.invalidate() + + def log_text(text: str) -> None: + loop.call_soon_threadsafe(text_area.buffer.insert_text, text) + app.invalidate() + + # Run the callback in the executor. When done, set a return value for the + # UI, so that it quits. + def start() -> None: + try: + run_callback(set_percentage, log_text) + finally: + app.exit() + + def pre_run() -> None: + run_in_executor_with_context(start) + + app.pre_run_callables.append(pre_run) + + return app + + +def _create_app(dialog: AnyContainer, style: Optional[BaseStyle]) -> Application[Any]: + # Key bindings. + bindings = KeyBindings() + bindings.add("tab")(focus_next) + bindings.add("s-tab")(focus_previous) + + return Application( + layout=Layout(dialog), + key_bindings=merge_key_bindings([load_key_bindings(), bindings]), + mouse_support=True, + style=style, + full_screen=True, + ) + + +def _return_none() -> None: + " Button handler that returns None. " + get_app().exit() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__init__.py new file mode 100644 index 00000000..7d0fbb50 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__init__.py @@ -0,0 +1,31 @@ +from .base import ProgressBar, ProgressBarCounter +from .formatters import ( + Bar, + Formatter, + IterationsPerSecond, + Label, + Percentage, + Progress, + Rainbow, + SpinningWheel, + Text, + TimeElapsed, + TimeLeft, +) + +__all__ = [ + "ProgressBar", + "ProgressBarCounter", + # Formatters. + "Formatter", + "Text", + "Label", + "Percentage", + "Bar", + "Progress", + "TimeElapsed", + "TimeLeft", + "IterationsPerSecond", + "SpinningWheel", + "Rainbow", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..951f8053 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/base.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/base.cpython-38.pyc new file mode 100644 index 00000000..f9de17d9 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/base.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/formatters.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/formatters.cpython-38.pyc new file mode 100644 index 00000000..cad46222 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/__pycache__/formatters.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/base.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/base.py new file mode 100644 index 00000000..0872617a --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/base.py @@ -0,0 +1,451 @@ +""" +Progress bar implementation on top of prompt_toolkit. + +:: + + with ProgressBar(...) as pb: + for item in pb(data): + ... +""" +import datetime +import functools +import os +import signal +import threading +import traceback +from asyncio import get_event_loop, new_event_loop, set_event_loop +from typing import ( + TYPE_CHECKING, + Generic, + Iterable, + List, + Optional, + Sequence, + Sized, + TextIO, + TypeVar, + cast, +) + +from prompt_toolkit.application import Application +from prompt_toolkit.application.current import get_app_session +from prompt_toolkit.filters import Condition, is_done, renderer_height_is_known +from prompt_toolkit.formatted_text import ( + AnyFormattedText, + StyleAndTextTuples, + to_formatted_text, +) +from prompt_toolkit.input import Input +from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.layout import ( + ConditionalContainer, + FormattedTextControl, + HSplit, + Layout, + VSplit, + Window, +) +from prompt_toolkit.layout.controls import UIContent, UIControl +from prompt_toolkit.layout.dimension import AnyDimension, D +from prompt_toolkit.output import ColorDepth, Output +from prompt_toolkit.styles import BaseStyle +from prompt_toolkit.utils import in_main_thread + +from .formatters import Formatter, create_default_formatters + +try: + import contextvars +except ImportError: + from prompt_toolkit.eventloop import dummy_contextvars + + contextvars = dummy_contextvars # type: ignore + + +__all__ = ["ProgressBar"] + +E = KeyPressEvent + +_SIGWINCH = getattr(signal, "SIGWINCH", None) + + +def create_key_bindings() -> KeyBindings: + """ + Key bindings handled by the progress bar. + (The main thread is not supposed to handle any key bindings.) + """ + kb = KeyBindings() + + @kb.add("c-l") + def _clear(event: E) -> None: + event.app.renderer.clear() + + @kb.add("c-c") + def _interrupt(event: E) -> None: + # Send KeyboardInterrupt to the main thread. + os.kill(os.getpid(), signal.SIGINT) + + return kb + + +_T = TypeVar("_T") + + +class ProgressBar: + """ + Progress bar context manager. + + Usage :: + + with ProgressBar(...) as pb: + for item in pb(data): + ... + + :param title: Text to be displayed above the progress bars. This can be a + callable or formatted text as well. + :param formatters: List of :class:`.Formatter` instances. + :param bottom_toolbar: Text to be displayed in the bottom toolbar. This + can be a callable or formatted text. + :param style: :class:`prompt_toolkit.styles.BaseStyle` instance. + :param key_bindings: :class:`.KeyBindings` instance. + :param file: The file object used for rendering, by default `sys.stderr` is used. + + :param color_depth: `prompt_toolkit` `ColorDepth` instance. + :param output: :class:`~prompt_toolkit.output.Output` instance. + :param input: :class:`~prompt_toolkit.input.Input` instance. + """ + + def __init__( + self, + title: AnyFormattedText = None, + formatters: Optional[Sequence[Formatter]] = None, + bottom_toolbar: AnyFormattedText = None, + style: Optional[BaseStyle] = None, + key_bindings: Optional[KeyBindings] = None, + file: Optional[TextIO] = None, + color_depth: Optional[ColorDepth] = None, + output: Optional[Output] = None, + input: Optional[Input] = None, + ) -> None: + + self.title = title + self.formatters = formatters or create_default_formatters() + self.bottom_toolbar = bottom_toolbar + self.counters: List[ProgressBarCounter[object]] = [] + self.style = style + self.key_bindings = key_bindings + + # Note that we use __stderr__ as default error output, because that + # works best with `patch_stdout`. + self.color_depth = color_depth + self.output = output or get_app_session().output + self.input = input or get_app_session().input + + self._thread: Optional[threading.Thread] = None + + self._loop = get_event_loop() + self._app_loop = new_event_loop() + + self._previous_winch_handler = None + self._has_sigwinch = False + + if TYPE_CHECKING: + # Infer type from getsignal result, as defined in typeshed. Too + # complex to repeat here. + self._previous_winch_handler = signal.getsignal(_SIGWINCH) + + def __enter__(self) -> "ProgressBar": + # Create UI Application. + title_toolbar = ConditionalContainer( + Window( + FormattedTextControl(lambda: self.title), + height=1, + style="class:progressbar,title", + ), + filter=Condition(lambda: self.title is not None), + ) + + bottom_toolbar = ConditionalContainer( + Window( + FormattedTextControl( + lambda: self.bottom_toolbar, style="class:bottom-toolbar.text" + ), + style="class:bottom-toolbar", + height=1, + ), + filter=~is_done + & renderer_height_is_known + & Condition(lambda: self.bottom_toolbar is not None), + ) + + def width_for_formatter(formatter: Formatter) -> AnyDimension: + # Needs to be passed as callable (partial) to the 'width' + # parameter, because we want to call it on every resize. + return formatter.get_width(progress_bar=self) + + progress_controls = [ + Window( + content=_ProgressControl(self, f), + width=functools.partial(width_for_formatter, f), + ) + for f in self.formatters + ] + + self.app: Application[None] = Application( + min_redraw_interval=0.05, + layout=Layout( + HSplit( + [ + title_toolbar, + VSplit( + progress_controls, + height=lambda: D( + preferred=len(self.counters), max=len(self.counters) + ), + ), + Window(), + bottom_toolbar, + ] + ) + ), + style=self.style, + key_bindings=self.key_bindings, + refresh_interval=0.3, + color_depth=self.color_depth, + output=self.output, + input=self.input, + ) + + # Run application in different thread. + def run() -> None: + set_event_loop(self._app_loop) + try: + self.app.run() + except BaseException as e: + traceback.print_exc() + print(e) + + ctx: contextvars.Context = contextvars.copy_context() + + self._thread = threading.Thread(target=ctx.run, args=(run,)) + self._thread.start() + + # Attach WINCH signal handler in main thread. + # (Interrupt that we receive during resize events.) + self._has_sigwinch = _SIGWINCH is not None and in_main_thread() + if self._has_sigwinch: + self._previous_winch_handler = signal.getsignal(_SIGWINCH) + self._loop.add_signal_handler(_SIGWINCH, self.invalidate) + + return self + + def __exit__(self, *a: object) -> None: + # Quit UI application. + if self.app.is_running: + self.app.exit() + + # Remove WINCH handler. + if self._has_sigwinch: + self._loop.remove_signal_handler(_SIGWINCH) + signal.signal(_SIGWINCH, self._previous_winch_handler) + + if self._thread is not None: + self._thread.join() + self._app_loop.close() + + def __call__( + self, + data: Optional[Iterable[_T]] = None, + label: AnyFormattedText = "", + remove_when_done: bool = False, + total: Optional[int] = None, + ) -> "ProgressBarCounter[_T]": + """ + Start a new counter. + + :param label: Title text or description for this progress. (This can be + formatted text as well). + :param remove_when_done: When `True`, hide this progress bar. + :param total: Specify the maximum value if it can't be calculated by + calling ``len``. + """ + counter = ProgressBarCounter( + self, data, label=label, remove_when_done=remove_when_done, total=total + ) + self.counters.append(counter) + return counter + + def invalidate(self) -> None: + self._app_loop.call_soon_threadsafe(self.app.invalidate) + + +class _ProgressControl(UIControl): + """ + User control for the progress bar. + """ + + def __init__(self, progress_bar: ProgressBar, formatter: Formatter) -> None: + self.progress_bar = progress_bar + self.formatter = formatter + self._key_bindings = create_key_bindings() + + def create_content(self, width: int, height: int) -> UIContent: + items: List[StyleAndTextTuples] = [] + + for pr in self.progress_bar.counters: + try: + text = self.formatter.format(self.progress_bar, pr, width) + except BaseException: + traceback.print_exc() + text = "ERROR" + + items.append(to_formatted_text(text)) + + def get_line(i: int) -> StyleAndTextTuples: + return items[i] + + return UIContent(get_line=get_line, line_count=len(items), show_cursor=False) + + def is_focusable(self) -> bool: + return True # Make sure that the key bindings work. + + def get_key_bindings(self) -> KeyBindings: + return self._key_bindings + + +_CounterItem = TypeVar("_CounterItem", covariant=True) + + +class ProgressBarCounter(Generic[_CounterItem]): + """ + An individual counter (A progress bar can have multiple counters). + """ + + def __init__( + self, + progress_bar: ProgressBar, + data: Optional[Iterable[_CounterItem]] = None, + label: AnyFormattedText = "", + remove_when_done: bool = False, + total: Optional[int] = None, + ) -> None: + + self.start_time = datetime.datetime.now() + self.stop_time: Optional[datetime.datetime] = None + self.progress_bar = progress_bar + self.data = data + self.items_completed = 0 + self.label = label + self.remove_when_done = remove_when_done + self._done = False + self.total: Optional[int] + + if total is None: + try: + self.total = len(cast(Sized, data)) + except TypeError: + self.total = None # We don't know the total length. + else: + self.total = total + + def __iter__(self) -> Iterable[_CounterItem]: + if self.data is not None: + try: + for item in self.data: + yield item + self.item_completed() + + # Only done if we iterate to the very end. + self.done = True + finally: + # Ensure counter has stopped even if we did not iterate to the + # end (e.g. break or exceptions). + self.stopped = True + else: + raise NotImplementedError("No data defined to iterate over.") + + def item_completed(self) -> None: + """ + Start handling the next item. + + (Can be called manually in case we don't have a collection to loop through.) + """ + self.items_completed += 1 + self.progress_bar.invalidate() + + @property + def done(self) -> bool: + """Whether a counter has been completed. + + Done counter have been stopped (see stopped) and removed depending on + remove_when_done value. + + Contrast this with stopped. A stopped counter may be terminated before + 100% completion. A done counter has reached its 100% completion. + """ + return self._done + + @done.setter + def done(self, value: bool) -> None: + self._done = value + self.stopped = value + + if value and self.remove_when_done: + self.progress_bar.counters.remove(self) + + @property + def stopped(self) -> bool: + """Whether a counter has been stopped. + + Stopped counters no longer have increasing time_elapsed. This distinction is + also used to prevent the Bar formatter with unknown totals from continuing to run. + + A stopped counter (but not done) can be used to signal that a given counter has + encountered an error but allows other counters to continue + (e.g. download X of Y failed). Given how only done counters are removed + (see remove_when_done) this can help aggregate failures from a large number of + successes. + + Contrast this with done. A done counter has reached its 100% completion. + A stopped counter may be terminated before 100% completion. + """ + return self.stop_time is not None + + @stopped.setter + def stopped(self, value: bool) -> None: + if value: + # This counter has not already been stopped. + if not self.stop_time: + self.stop_time = datetime.datetime.now() + else: + # Clearing any previously set stop_time. + self.stop_time = None + + @property + def percentage(self) -> float: + if self.total is None: + return 0 + else: + return self.items_completed * 100 / max(self.total, 1) + + @property + def time_elapsed(self) -> datetime.timedelta: + """ + Return how much time has been elapsed since the start. + """ + if self.stop_time is None: + return datetime.datetime.now() - self.start_time + else: + return self.stop_time - self.start_time + + @property + def time_left(self) -> Optional[datetime.timedelta]: + """ + Timedelta representing the time left. + """ + if self.total is None or not self.percentage: + return None + elif self.done or self.stopped: + return datetime.timedelta(0) + else: + return self.time_elapsed * (100 - self.percentage) / self.percentage diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/formatters.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/formatters.py new file mode 100644 index 00000000..ad3a932d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/progress_bar/formatters.py @@ -0,0 +1,436 @@ +""" +Formatter classes for the progress bar. +Each progress bar consists of a list of these formatters. +""" +import datetime +import time +from abc import ABCMeta, abstractmethod +from typing import TYPE_CHECKING, List, Tuple + +from prompt_toolkit.formatted_text import ( + HTML, + AnyFormattedText, + StyleAndTextTuples, + to_formatted_text, +) +from prompt_toolkit.formatted_text.utils import fragment_list_width +from prompt_toolkit.layout.dimension import AnyDimension, D +from prompt_toolkit.layout.utils import explode_text_fragments +from prompt_toolkit.utils import get_cwidth + +if TYPE_CHECKING: + from .base import ProgressBar, ProgressBarCounter + +__all__ = [ + "Formatter", + "Text", + "Label", + "Percentage", + "Bar", + "Progress", + "TimeElapsed", + "TimeLeft", + "IterationsPerSecond", + "SpinningWheel", + "Rainbow", + "create_default_formatters", +] + + +class Formatter(metaclass=ABCMeta): + """ + Base class for any formatter. + """ + + @abstractmethod + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + pass + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + return D() + + +class Text(Formatter): + """ + Display plain text. + """ + + def __init__(self, text: AnyFormattedText, style: str = "") -> None: + self.text = to_formatted_text(text, style=style) + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + return self.text + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + return fragment_list_width(self.text) + + +class Label(Formatter): + """ + Display the name of the current task. + + :param width: If a `width` is given, use this width. Scroll the text if it + doesn't fit in this width. + :param suffix: String suffix to be added after the task name, e.g. ': '. + If no task name was given, no suffix will be added. + """ + + def __init__(self, width: AnyDimension = None, suffix: str = "") -> None: + self.width = width + self.suffix = suffix + + def _add_suffix(self, label: AnyFormattedText) -> StyleAndTextTuples: + label = to_formatted_text(label, style="class:label") + return label + [("", self.suffix)] + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + + label = self._add_suffix(progress.label) + cwidth = fragment_list_width(label) + + if cwidth > width: + # It doesn't fit -> scroll task name. + label = explode_text_fragments(label) + max_scroll = cwidth - width + current_scroll = int(time.time() * 3 % max_scroll) + label = label[current_scroll:] + + return label + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + if self.width: + return self.width + + all_labels = [self._add_suffix(c.label) for c in progress_bar.counters] + if all_labels: + max_widths = max(fragment_list_width(l) for l in all_labels) + return D(preferred=max_widths, max=max_widths) + else: + return D() + + +class Percentage(Formatter): + """ + Display the progress as a percentage. + """ + + template = "{percentage:>5}%" + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + + return HTML(self.template).format(percentage=round(progress.percentage, 1)) + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + return D.exact(6) + + +class Bar(Formatter): + """ + Display the progress bar itself. + """ + + template = "{start}{bar_a}{bar_b}{bar_c}{end}" + + def __init__( + self, + start: str = "[", + end: str = "]", + sym_a: str = "=", + sym_b: str = ">", + sym_c: str = " ", + unknown: str = "#", + ) -> None: + + assert len(sym_a) == 1 and get_cwidth(sym_a) == 1 + assert len(sym_c) == 1 and get_cwidth(sym_c) == 1 + + self.start = start + self.end = end + self.sym_a = sym_a + self.sym_b = sym_b + self.sym_c = sym_c + self.unknown = unknown + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + if progress.done or progress.total or progress.stopped: + sym_a, sym_b, sym_c = self.sym_a, self.sym_b, self.sym_c + + # Compute pb_a based on done, total, or stopped states. + if progress.done: + # 100% completed irrelevant of how much was actually marked as completed. + percent = 1.0 + else: + # Show percentage completed. + percent = progress.percentage / 100 + else: + # Total is unknown and bar is still running. + sym_a, sym_b, sym_c = self.sym_c, self.unknown, self.sym_c + + # Compute percent based on the time. + percent = time.time() * 20 % 100 / 100 + + # Subtract left, sym_b, and right. + width -= get_cwidth(self.start + sym_b + self.end) + + # Scale percent by width + pb_a = int(percent * width) + bar_a = sym_a * pb_a + bar_b = sym_b + bar_c = sym_c * (width - pb_a) + + return HTML(self.template).format( + start=self.start, end=self.end, bar_a=bar_a, bar_b=bar_b, bar_c=bar_c + ) + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + return D(min=9) + + +class Progress(Formatter): + """ + Display the progress as text. E.g. "8/20" + """ + + template = "{current:>3}/{total:>3}" + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + + return HTML(self.template).format( + current=progress.items_completed, total=progress.total or "?" + ) + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + all_lengths = [ + len("{0:>3}".format(c.total or "?")) for c in progress_bar.counters + ] + all_lengths.append(1) + return D.exact(max(all_lengths) * 2 + 1) + + +def _format_timedelta(timedelta: datetime.timedelta) -> str: + """ + Return hh:mm:ss, or mm:ss if the amount of hours is zero. + """ + result = "{0}".format(timedelta).split(".")[0] + if result.startswith("0:"): + result = result[2:] + return result + + +class TimeElapsed(Formatter): + """ + Display the elapsed time. + """ + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + + text = _format_timedelta(progress.time_elapsed).rjust(width) + return HTML("{time_elapsed}").format( + time_elapsed=text + ) + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + all_values = [ + len(_format_timedelta(c.time_elapsed)) for c in progress_bar.counters + ] + if all_values: + return max(all_values) + return 0 + + +class TimeLeft(Formatter): + """ + Display the time left. + """ + + template = "{time_left}" + unknown = "?:??:??" + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + + time_left = progress.time_left + if time_left is not None: + formatted_time_left = _format_timedelta(time_left) + else: + formatted_time_left = self.unknown + + return HTML(self.template).format(time_left=formatted_time_left.rjust(width)) + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + all_values = [ + len(_format_timedelta(c.time_left)) if c.time_left is not None else 7 + for c in progress_bar.counters + ] + if all_values: + return max(all_values) + return 0 + + +class IterationsPerSecond(Formatter): + """ + Display the iterations per second. + """ + + template = ( + "{iterations_per_second:.2f}" + ) + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + + value = progress.items_completed / progress.time_elapsed.total_seconds() + return HTML(self.template.format(iterations_per_second=value)) + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + all_values = [ + len("{0:.2f}".format(c.items_completed / c.time_elapsed.total_seconds())) + for c in progress_bar.counters + ] + if all_values: + return max(all_values) + return 0 + + +class SpinningWheel(Formatter): + """ + Display a spinning wheel. + """ + + characters = r"/-\|" + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + + index = int(time.time() * 3) % len(self.characters) + return HTML("{0}").format( + self.characters[index] + ) + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + return D.exact(1) + + +def _hue_to_rgb(hue: float) -> Tuple[int, int, int]: + """ + Take hue between 0 and 1, return (r, g, b). + """ + i = int(hue * 6.0) + f = (hue * 6.0) - i + + q = int(255 * (1.0 - f)) + t = int(255 * (1.0 - (1.0 - f))) + + i %= 6 + + return [ + (255, t, 0), + (q, 255, 0), + (0, 255, t), + (0, q, 255), + (t, 0, 255), + (255, 0, q), + ][i] + + +class Rainbow(Formatter): + """ + For the fun. Add rainbow colors to any of the other formatters. + """ + + colors = ["#%.2x%.2x%.2x" % _hue_to_rgb(h / 100.0) for h in range(0, 100)] + + def __init__(self, formatter: Formatter) -> None: + self.formatter = formatter + + def format( + self, + progress_bar: "ProgressBar", + progress: "ProgressBarCounter[object]", + width: int, + ) -> AnyFormattedText: + + # Get formatted text from nested formatter, and explode it in + # text/style tuples. + result = self.formatter.format(progress_bar, progress, width) + result = explode_text_fragments(to_formatted_text(result)) + + # Insert colors. + result2: StyleAndTextTuples = [] + shift = int(time.time() * 3) % len(self.colors) + + for i, (style, text, *_) in enumerate(result): + result2.append( + (style + " " + self.colors[(i + shift) % len(self.colors)], text) + ) + return result2 + + def get_width(self, progress_bar: "ProgressBar") -> AnyDimension: + return self.formatter.get_width(progress_bar) + + +def create_default_formatters() -> List[Formatter]: + """ + Return the list of default formatters. + """ + return [ + Label(), + Text(" "), + Percentage(), + Text(" "), + Bar(), + Text(" "), + Progress(), + Text(" "), + Text("eta [", style="class:time-left"), + TimeLeft(), + Text("]", style="class:time-left"), + Text(" "), + ] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/prompt.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/prompt.py new file mode 100644 index 00000000..aa787420 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/prompt.py @@ -0,0 +1,1467 @@ +""" +Line editing functionality. +--------------------------- + +This provides a UI for a line input, similar to GNU Readline, libedit and +linenoise. + +Either call the `prompt` function for every line input. Or create an instance +of the :class:`.PromptSession` class and call the `prompt` method from that +class. In the second case, we'll have a 'session' that keeps all the state like +the history in between several calls. + +There is a lot of overlap between the arguments taken by the `prompt` function +and the `PromptSession` (like `completer`, `style`, etcetera). There we have +the freedom to decide which settings we want for the whole 'session', and which +we want for an individual `prompt`. + +Example:: + + # Simple `prompt` call. + result = prompt('Say something: ') + + # Using a 'session'. + s = PromptSession() + result = s.prompt('Say something: ') +""" +from asyncio import get_event_loop +from enum import Enum +from functools import partial +from typing import ( + TYPE_CHECKING, + Callable, + Generic, + List, + Optional, + Tuple, + TypeVar, + Union, + cast, +) + +from prompt_toolkit.application import Application +from prompt_toolkit.application.current import get_app +from prompt_toolkit.auto_suggest import AutoSuggest, DynamicAutoSuggest +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.clipboard import Clipboard, DynamicClipboard, InMemoryClipboard +from prompt_toolkit.completion import Completer, DynamicCompleter, ThreadedCompleter +from prompt_toolkit.document import Document +from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode +from prompt_toolkit.filters import ( + Condition, + FilterOrBool, + has_arg, + has_focus, + is_done, + is_true, + renderer_height_is_known, + to_filter, +) +from prompt_toolkit.formatted_text import ( + AnyFormattedText, + StyleAndTextTuples, + fragment_list_to_text, + merge_formatted_text, + to_formatted_text, +) +from prompt_toolkit.history import History, InMemoryHistory +from prompt_toolkit.input.base import Input +from prompt_toolkit.key_binding.bindings.auto_suggest import load_auto_suggest_bindings +from prompt_toolkit.key_binding.bindings.completion import ( + display_completions_like_readline, +) +from prompt_toolkit.key_binding.bindings.open_in_editor import ( + load_open_in_editor_bindings, +) +from prompt_toolkit.key_binding.key_bindings import ( + ConditionalKeyBindings, + DynamicKeyBindings, + KeyBindings, + KeyBindingsBase, + merge_key_bindings, +) +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout import Float, FloatContainer, HSplit, Window +from prompt_toolkit.layout.containers import ConditionalContainer, WindowAlign +from prompt_toolkit.layout.controls import ( + BufferControl, + FormattedTextControl, + SearchBufferControl, +) +from prompt_toolkit.layout.dimension import Dimension +from prompt_toolkit.layout.layout import Layout +from prompt_toolkit.layout.menus import CompletionsMenu, MultiColumnCompletionsMenu +from prompt_toolkit.layout.processors import ( + AfterInput, + AppendAutoSuggestion, + ConditionalProcessor, + DisplayMultipleCursors, + DynamicProcessor, + HighlightIncrementalSearchProcessor, + HighlightSelectionProcessor, + PasswordProcessor, + Processor, + ReverseSearchProcessor, + merge_processors, +) +from prompt_toolkit.layout.utils import explode_text_fragments +from prompt_toolkit.lexers import DynamicLexer, Lexer +from prompt_toolkit.output import ColorDepth, DummyOutput, Output +from prompt_toolkit.styles import ( + BaseStyle, + ConditionalStyleTransformation, + DynamicStyle, + DynamicStyleTransformation, + StyleTransformation, + SwapLightAndDarkStyleTransformation, + merge_style_transformations, +) +from prompt_toolkit.utils import ( + get_cwidth, + is_dumb_terminal, + suspend_to_background_supported, + to_str, +) +from prompt_toolkit.validation import DynamicValidator, Validator +from prompt_toolkit.widgets.toolbars import ( + SearchToolbar, + SystemToolbar, + ValidationToolbar, +) + +if TYPE_CHECKING: + from prompt_toolkit.formatted_text.base import MagicFormattedText + +__all__ = [ + "PromptSession", + "prompt", + "confirm", + "create_confirm_session", # Used by '_display_completions_like_readline'. + "CompleteStyle", +] + +_StyleAndTextTuplesCallable = Callable[[], StyleAndTextTuples] +E = KeyPressEvent + + +def _split_multiline_prompt( + get_prompt_text: _StyleAndTextTuplesCallable, +) -> Tuple[ + Callable[[], bool], _StyleAndTextTuplesCallable, _StyleAndTextTuplesCallable +]: + """ + Take a `get_prompt_text` function and return three new functions instead. + One that tells whether this prompt consists of multiple lines; one that + returns the fragments to be shown on the lines above the input; and another + one with the fragments to be shown at the first line of the input. + """ + + def has_before_fragments() -> bool: + for fragment, char, *_ in get_prompt_text(): + if "\n" in char: + return True + return False + + def before() -> StyleAndTextTuples: + result: StyleAndTextTuples = [] + found_nl = False + for fragment, char, *_ in reversed(explode_text_fragments(get_prompt_text())): + if found_nl: + result.insert(0, (fragment, char)) + elif char == "\n": + found_nl = True + return result + + def first_input_line() -> StyleAndTextTuples: + result: StyleAndTextTuples = [] + for fragment, char, *_ in reversed(explode_text_fragments(get_prompt_text())): + if char == "\n": + break + else: + result.insert(0, (fragment, char)) + return result + + return has_before_fragments, before, first_input_line + + +class _RPrompt(Window): + """ + The prompt that is displayed on the right side of the Window. + """ + + def __init__(self, text: AnyFormattedText) -> None: + super().__init__( + FormattedTextControl(text=text), + align=WindowAlign.RIGHT, + style="class:rprompt", + ) + + +class CompleteStyle(str, Enum): + """ + How to display autocompletions for the prompt. + """ + + value: str + + COLUMN = "COLUMN" + MULTI_COLUMN = "MULTI_COLUMN" + READLINE_LIKE = "READLINE_LIKE" + + +# Formatted text for the continuation prompt. It's the same like other +# formatted text, except that if it's a callable, it takes three arguments. +PromptContinuationText = Union[ + str, + "MagicFormattedText", + StyleAndTextTuples, + # (prompt_width, line_number, wrap_count) -> AnyFormattedText. + Callable[[int, int, int], AnyFormattedText], +] + +_T = TypeVar("_T") + + +class PromptSession(Generic[_T]): + """ + PromptSession for a prompt application, which can be used as a GNU Readline + replacement. + + This is a wrapper around a lot of ``prompt_toolkit`` functionality and can + be a replacement for `raw_input`. + + All parameters that expect "formatted text" can take either just plain text + (a unicode object), a list of ``(style_str, text)`` tuples or an HTML object. + + Example usage:: + + s = PromptSession(message='>') + text = s.prompt() + + :param message: Plain text or formatted text to be shown before the prompt. + This can also be a callable that returns formatted text. + :param multiline: `bool` or :class:`~prompt_toolkit.filters.Filter`. + When True, prefer a layout that is more adapted for multiline input. + Text after newlines is automatically indented, and search/arg input is + shown below the input, instead of replacing the prompt. + :param wrap_lines: `bool` or :class:`~prompt_toolkit.filters.Filter`. + When True (the default), automatically wrap long lines instead of + scrolling horizontally. + :param is_password: Show asterisks instead of the actual typed characters. + :param editing_mode: ``EditingMode.VI`` or ``EditingMode.EMACS``. + :param vi_mode: `bool`, if True, Identical to ``editing_mode=EditingMode.VI``. + :param complete_while_typing: `bool` or + :class:`~prompt_toolkit.filters.Filter`. Enable autocompletion while + typing. + :param validate_while_typing: `bool` or + :class:`~prompt_toolkit.filters.Filter`. Enable input validation while + typing. + :param enable_history_search: `bool` or + :class:`~prompt_toolkit.filters.Filter`. Enable up-arrow parting + string matching. + :param search_ignore_case: + :class:`~prompt_toolkit.filters.Filter`. Search case insensitive. + :param lexer: :class:`~prompt_toolkit.lexers.Lexer` to be used for the + syntax highlighting. + :param validator: :class:`~prompt_toolkit.validation.Validator` instance + for input validation. + :param completer: :class:`~prompt_toolkit.completion.Completer` instance + for input completion. + :param complete_in_thread: `bool` or + :class:`~prompt_toolkit.filters.Filter`. Run the completer code in a + background thread in order to avoid blocking the user interface. + For ``CompleteStyle.READLINE_LIKE``, this setting has no effect. There + we always run the completions in the main thread. + :param reserve_space_for_menu: Space to be reserved for displaying the menu. + (0 means that no space needs to be reserved.) + :param auto_suggest: :class:`~prompt_toolkit.auto_suggest.AutoSuggest` + instance for input suggestions. + :param style: :class:`.Style` instance for the color scheme. + :param include_default_pygments_style: `bool` or + :class:`~prompt_toolkit.filters.Filter`. Tell whether the default + styling for Pygments lexers has to be included. By default, this is + true, but it is recommended to be disabled if another Pygments style is + passed as the `style` argument, otherwise, two Pygments styles will be + merged. + :param style_transformation: + :class:`~prompt_toolkit.style.StyleTransformation` instance. + :param swap_light_and_dark_colors: `bool` or + :class:`~prompt_toolkit.filters.Filter`. When enabled, apply + :class:`~prompt_toolkit.style.SwapLightAndDarkStyleTransformation`. + This is useful for switching between dark and light terminal + backgrounds. + :param enable_system_prompt: `bool` or + :class:`~prompt_toolkit.filters.Filter`. Pressing Meta+'!' will show + a system prompt. + :param enable_suspend: `bool` or :class:`~prompt_toolkit.filters.Filter`. + Enable Control-Z style suspension. + :param enable_open_in_editor: `bool` or + :class:`~prompt_toolkit.filters.Filter`. Pressing 'v' in Vi mode or + C-X C-E in emacs mode will open an external editor. + :param history: :class:`~prompt_toolkit.history.History` instance. + :param clipboard: :class:`~prompt_toolkit.clipboard.Clipboard` instance. + (e.g. :class:`~prompt_toolkit.clipboard.InMemoryClipboard`) + :param rprompt: Text or formatted text to be displayed on the right side. + This can also be a callable that returns (formatted) text. + :param bottom_toolbar: Formatted text or callable which is supposed to + return formatted text. + :param prompt_continuation: Text that needs to be displayed for a multiline + prompt continuation. This can either be formatted text or a callable + that takes a `prompt_width`, `line_number` and `wrap_count` as input + and returns formatted text. When this is `None` (the default), then + `prompt_width` spaces will be used. + :param complete_style: ``CompleteStyle.COLUMN``, + ``CompleteStyle.MULTI_COLUMN`` or ``CompleteStyle.READLINE_LIKE``. + :param mouse_support: `bool` or :class:`~prompt_toolkit.filters.Filter` + to enable mouse support. + :param placeholder: Text to be displayed when no input has been given + yet. Unlike the `default` parameter, this won't be returned as part of + the output ever. This can be formatted text or a callable that returns + formatted text. + :param refresh_interval: (number; in seconds) When given, refresh the UI + every so many seconds. + :param input: `Input` object. (Note that the preferred way to change the + input/output is by creating an `AppSession`.) + :param output: `Output` object. + """ + + _fields = ( + "message", + "lexer", + "completer", + "complete_in_thread", + "is_password", + "editing_mode", + "key_bindings", + "is_password", + "bottom_toolbar", + "style", + "style_transformation", + "swap_light_and_dark_colors", + "color_depth", + "include_default_pygments_style", + "rprompt", + "multiline", + "prompt_continuation", + "wrap_lines", + "enable_history_search", + "search_ignore_case", + "complete_while_typing", + "validate_while_typing", + "complete_style", + "mouse_support", + "auto_suggest", + "clipboard", + "validator", + "refresh_interval", + "input_processors", + "placeholder", + "enable_system_prompt", + "enable_suspend", + "enable_open_in_editor", + "reserve_space_for_menu", + "tempfile_suffix", + "tempfile", + ) + + def __init__( + self, + message: AnyFormattedText = "", + *, + multiline: FilterOrBool = False, + wrap_lines: FilterOrBool = True, + is_password: FilterOrBool = False, + vi_mode: bool = False, + editing_mode: EditingMode = EditingMode.EMACS, + complete_while_typing: FilterOrBool = True, + validate_while_typing: FilterOrBool = True, + enable_history_search: FilterOrBool = False, + search_ignore_case: FilterOrBool = False, + lexer: Optional[Lexer] = None, + enable_system_prompt: FilterOrBool = False, + enable_suspend: FilterOrBool = False, + enable_open_in_editor: FilterOrBool = False, + validator: Optional[Validator] = None, + completer: Optional[Completer] = None, + complete_in_thread: bool = False, + reserve_space_for_menu: int = 8, + complete_style: CompleteStyle = CompleteStyle.COLUMN, + auto_suggest: Optional[AutoSuggest] = None, + style: Optional[BaseStyle] = None, + style_transformation: Optional[StyleTransformation] = None, + swap_light_and_dark_colors: FilterOrBool = False, + color_depth: Optional[ColorDepth] = None, + include_default_pygments_style: FilterOrBool = True, + history: Optional[History] = None, + clipboard: Optional[Clipboard] = None, + prompt_continuation: Optional[PromptContinuationText] = None, + rprompt: AnyFormattedText = None, + bottom_toolbar: AnyFormattedText = None, + mouse_support: FilterOrBool = False, + input_processors: Optional[List[Processor]] = None, + placeholder: Optional[AnyFormattedText] = None, + key_bindings: Optional[KeyBindingsBase] = None, + erase_when_done: bool = False, + tempfile_suffix: Optional[Union[str, Callable[[], str]]] = ".txt", + tempfile: Optional[Union[str, Callable[[], str]]] = None, + refresh_interval: float = 0, + input: Optional[Input] = None, + output: Optional[Output] = None, + ) -> None: + + history = history or InMemoryHistory() + clipboard = clipboard or InMemoryClipboard() + + # Ensure backwards-compatibility, when `vi_mode` is passed. + if vi_mode: + editing_mode = EditingMode.VI + + # Store all settings in this class. + self._input = input + self._output = output + + # Store attributes. + # (All except 'editing_mode'.) + self.message = message + self.lexer = lexer + self.completer = completer + self.complete_in_thread = complete_in_thread + self.is_password = is_password + self.key_bindings = key_bindings + self.bottom_toolbar = bottom_toolbar + self.style = style + self.style_transformation = style_transformation + self.swap_light_and_dark_colors = swap_light_and_dark_colors + self.color_depth = color_depth + self.include_default_pygments_style = include_default_pygments_style + self.rprompt = rprompt + self.multiline = multiline + self.prompt_continuation = prompt_continuation + self.wrap_lines = wrap_lines + self.enable_history_search = enable_history_search + self.search_ignore_case = search_ignore_case + self.complete_while_typing = complete_while_typing + self.validate_while_typing = validate_while_typing + self.complete_style = complete_style + self.mouse_support = mouse_support + self.auto_suggest = auto_suggest + self.clipboard = clipboard + self.validator = validator + self.refresh_interval = refresh_interval + self.input_processors = input_processors + self.placeholder = placeholder + self.enable_system_prompt = enable_system_prompt + self.enable_suspend = enable_suspend + self.enable_open_in_editor = enable_open_in_editor + self.reserve_space_for_menu = reserve_space_for_menu + self.tempfile_suffix = tempfile_suffix + self.tempfile = tempfile + + # Create buffers, layout and Application. + self.history = history + self.default_buffer = self._create_default_buffer() + self.search_buffer = self._create_search_buffer() + self.layout = self._create_layout() + self.app = self._create_application(editing_mode, erase_when_done) + + def _dyncond(self, attr_name: str) -> Condition: + """ + Dynamically take this setting from this 'PromptSession' class. + `attr_name` represents an attribute name of this class. Its value + can either be a boolean or a `Filter`. + + This returns something that can be used as either a `Filter` + or `Filter`. + """ + + @Condition + def dynamic() -> bool: + value = cast(FilterOrBool, getattr(self, attr_name)) + return to_filter(value)() + + return dynamic + + def _create_default_buffer(self) -> Buffer: + """ + Create and return the default input buffer. + """ + dyncond = self._dyncond + + # Create buffers list. + def accept(buff: Buffer) -> bool: + """Accept the content of the default buffer. This is called when + the validation succeeds.""" + cast(Application[str], get_app()).exit(result=buff.document.text) + return True # Keep text, we call 'reset' later on. + + return Buffer( + name=DEFAULT_BUFFER, + # Make sure that complete_while_typing is disabled when + # enable_history_search is enabled. (First convert to Filter, + # to avoid doing bitwise operations on bool objects.) + complete_while_typing=Condition( + lambda: is_true(self.complete_while_typing) + and not is_true(self.enable_history_search) + and not self.complete_style == CompleteStyle.READLINE_LIKE + ), + validate_while_typing=dyncond("validate_while_typing"), + enable_history_search=dyncond("enable_history_search"), + validator=DynamicValidator(lambda: self.validator), + completer=DynamicCompleter( + lambda: ThreadedCompleter(self.completer) + if self.complete_in_thread and self.completer + else self.completer + ), + history=self.history, + auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), + accept_handler=accept, + tempfile_suffix=lambda: to_str(self.tempfile_suffix or ""), + tempfile=lambda: to_str(self.tempfile or ""), + ) + + def _create_search_buffer(self) -> Buffer: + return Buffer(name=SEARCH_BUFFER) + + def _create_layout(self) -> Layout: + """ + Create `Layout` for this prompt. + """ + dyncond = self._dyncond + + # Create functions that will dynamically split the prompt. (If we have + # a multiline prompt.) + ( + has_before_fragments, + get_prompt_text_1, + get_prompt_text_2, + ) = _split_multiline_prompt(self._get_prompt) + + default_buffer = self.default_buffer + search_buffer = self.search_buffer + + # Create processors list. + @Condition + def display_placeholder() -> bool: + return self.placeholder is not None and self.default_buffer.text == "" + + all_input_processors = [ + HighlightIncrementalSearchProcessor(), + HighlightSelectionProcessor(), + ConditionalProcessor( + AppendAutoSuggestion(), has_focus(default_buffer) & ~is_done + ), + ConditionalProcessor(PasswordProcessor(), dyncond("is_password")), + DisplayMultipleCursors(), + # Users can insert processors here. + DynamicProcessor(lambda: merge_processors(self.input_processors or [])), + ConditionalProcessor( + AfterInput(lambda: self.placeholder), + filter=display_placeholder, + ), + ] + + # Create bottom toolbars. + bottom_toolbar = ConditionalContainer( + Window( + FormattedTextControl( + lambda: self.bottom_toolbar, style="class:bottom-toolbar.text" + ), + style="class:bottom-toolbar", + dont_extend_height=True, + height=Dimension(min=1), + ), + filter=~is_done + & renderer_height_is_known + & Condition(lambda: self.bottom_toolbar is not None), + ) + + search_toolbar = SearchToolbar( + search_buffer, ignore_case=dyncond("search_ignore_case") + ) + + search_buffer_control = SearchBufferControl( + buffer=search_buffer, + input_processors=[ReverseSearchProcessor()], + ignore_case=dyncond("search_ignore_case"), + ) + + system_toolbar = SystemToolbar( + enable_global_bindings=dyncond("enable_system_prompt") + ) + + def get_search_buffer_control() -> SearchBufferControl: + " Return the UIControl to be focused when searching start. " + if is_true(self.multiline): + return search_toolbar.control + else: + return search_buffer_control + + default_buffer_control = BufferControl( + buffer=default_buffer, + search_buffer_control=get_search_buffer_control, + input_processors=all_input_processors, + include_default_input_processors=False, + lexer=DynamicLexer(lambda: self.lexer), + preview_search=True, + ) + + default_buffer_window = Window( + default_buffer_control, + height=self._get_default_buffer_control_height, + get_line_prefix=partial( + self._get_line_prefix, get_prompt_text_2=get_prompt_text_2 + ), + wrap_lines=dyncond("wrap_lines"), + ) + + @Condition + def multi_column_complete_style() -> bool: + return self.complete_style == CompleteStyle.MULTI_COLUMN + + # Build the layout. + layout = HSplit( + [ + # The main input, with completion menus floating on top of it. + FloatContainer( + HSplit( + [ + ConditionalContainer( + Window( + FormattedTextControl(get_prompt_text_1), + dont_extend_height=True, + ), + Condition(has_before_fragments), + ), + ConditionalContainer( + default_buffer_window, + Condition( + lambda: get_app().layout.current_control + != search_buffer_control + ), + ), + ConditionalContainer( + Window(search_buffer_control), + Condition( + lambda: get_app().layout.current_control + == search_buffer_control + ), + ), + ] + ), + [ + # Completion menus. + # NOTE: Especially the multi-column menu needs to be + # transparent, because the shape is not always + # rectangular due to the meta-text below the menu. + Float( + xcursor=True, + ycursor=True, + transparent=True, + content=CompletionsMenu( + max_height=16, + scroll_offset=1, + extra_filter=has_focus(default_buffer) + & ~multi_column_complete_style, + ), + ), + Float( + xcursor=True, + ycursor=True, + transparent=True, + content=MultiColumnCompletionsMenu( + show_meta=True, + extra_filter=has_focus(default_buffer) + & multi_column_complete_style, + ), + ), + # The right prompt. + Float( + right=0, + bottom=0, + hide_when_covering_content=True, + content=_RPrompt(lambda: self.rprompt), + ), + ], + ), + ConditionalContainer(ValidationToolbar(), filter=~is_done), + ConditionalContainer( + system_toolbar, dyncond("enable_system_prompt") & ~is_done + ), + # In multiline mode, we use two toolbars for 'arg' and 'search'. + ConditionalContainer( + Window(FormattedTextControl(self._get_arg_text), height=1), + dyncond("multiline") & has_arg, + ), + ConditionalContainer(search_toolbar, dyncond("multiline") & ~is_done), + bottom_toolbar, + ] + ) + + return Layout(layout, default_buffer_window) + + def _create_application( + self, editing_mode: EditingMode, erase_when_done: bool + ) -> Application[_T]: + """ + Create the `Application` object. + """ + dyncond = self._dyncond + + # Default key bindings. + auto_suggest_bindings = load_auto_suggest_bindings() + open_in_editor_bindings = load_open_in_editor_bindings() + prompt_bindings = self._create_prompt_bindings() + + # Create application + application: Application[_T] = Application( + layout=self.layout, + style=DynamicStyle(lambda: self.style), + style_transformation=merge_style_transformations( + [ + DynamicStyleTransformation(lambda: self.style_transformation), + ConditionalStyleTransformation( + SwapLightAndDarkStyleTransformation(), + dyncond("swap_light_and_dark_colors"), + ), + ] + ), + include_default_pygments_style=dyncond("include_default_pygments_style"), + clipboard=DynamicClipboard(lambda: self.clipboard), + key_bindings=merge_key_bindings( + [ + merge_key_bindings( + [ + auto_suggest_bindings, + ConditionalKeyBindings( + open_in_editor_bindings, + dyncond("enable_open_in_editor") + & has_focus(DEFAULT_BUFFER), + ), + prompt_bindings, + ] + ), + DynamicKeyBindings(lambda: self.key_bindings), + ] + ), + mouse_support=dyncond("mouse_support"), + editing_mode=editing_mode, + erase_when_done=erase_when_done, + reverse_vi_search_direction=True, + color_depth=lambda: self.color_depth, + refresh_interval=self.refresh_interval, + input=self._input, + output=self._output, + ) + + # During render time, make sure that we focus the right search control + # (if we are searching). - This could be useful if people make the + # 'multiline' property dynamic. + """ + def on_render(app): + multiline = is_true(self.multiline) + current_control = app.layout.current_control + + if multiline: + if current_control == search_buffer_control: + app.layout.current_control = search_toolbar.control + app.invalidate() + else: + if current_control == search_toolbar.control: + app.layout.current_control = search_buffer_control + app.invalidate() + + app.on_render += on_render + """ + + return application + + def _create_prompt_bindings(self) -> KeyBindings: + """ + Create the KeyBindings for a prompt application. + """ + kb = KeyBindings() + handle = kb.add + default_focused = has_focus(DEFAULT_BUFFER) + + @Condition + def do_accept() -> bool: + return not is_true(self.multiline) and self.app.layout.has_focus( + DEFAULT_BUFFER + ) + + @handle("enter", filter=do_accept & default_focused) + def _accept_input(event: E) -> None: + " Accept input when enter has been pressed. " + self.default_buffer.validate_and_handle() + + @Condition + def readline_complete_style() -> bool: + return self.complete_style == CompleteStyle.READLINE_LIKE + + @handle("tab", filter=readline_complete_style & default_focused) + def _complete_like_readline(event: E) -> None: + " Display completions (like Readline). " + display_completions_like_readline(event) + + @handle("c-c", filter=default_focused) + def _keyboard_interrupt(event: E) -> None: + " Abort when Control-C has been pressed. " + event.app.exit(exception=KeyboardInterrupt, style="class:aborting") + + @Condition + def ctrl_d_condition() -> bool: + """Ctrl-D binding is only active when the default buffer is selected + and empty.""" + app = get_app() + return ( + app.current_buffer.name == DEFAULT_BUFFER + and not app.current_buffer.text + ) + + @handle("c-d", filter=ctrl_d_condition & default_focused) + def _eof(event: E) -> None: + " Exit when Control-D has been pressed. " + event.app.exit(exception=EOFError, style="class:exiting") + + suspend_supported = Condition(suspend_to_background_supported) + + @Condition + def enable_suspend() -> bool: + return to_filter(self.enable_suspend)() + + @handle("c-z", filter=suspend_supported & enable_suspend) + def _suspend(event: E) -> None: + """ + Suspend process to background. + """ + event.app.suspend_to_background() + + return kb + + def prompt( + self, + # When any of these arguments are passed, this value is overwritten + # in this PromptSession. + message: Optional[AnyFormattedText] = None, + # `message` should go first, because people call it as + # positional argument. + *, + editing_mode: Optional[EditingMode] = None, + refresh_interval: Optional[float] = None, + vi_mode: Optional[bool] = None, + lexer: Optional[Lexer] = None, + completer: Optional[Completer] = None, + complete_in_thread: Optional[bool] = None, + is_password: Optional[bool] = None, + key_bindings: Optional[KeyBindingsBase] = None, + bottom_toolbar: Optional[AnyFormattedText] = None, + style: Optional[BaseStyle] = None, + color_depth: Optional[ColorDepth] = None, + include_default_pygments_style: Optional[FilterOrBool] = None, + style_transformation: Optional[StyleTransformation] = None, + swap_light_and_dark_colors: Optional[FilterOrBool] = None, + rprompt: Optional[AnyFormattedText] = None, + multiline: Optional[FilterOrBool] = None, + prompt_continuation: Optional[PromptContinuationText] = None, + wrap_lines: Optional[FilterOrBool] = None, + enable_history_search: Optional[FilterOrBool] = None, + search_ignore_case: Optional[FilterOrBool] = None, + complete_while_typing: Optional[FilterOrBool] = None, + validate_while_typing: Optional[FilterOrBool] = None, + complete_style: Optional[CompleteStyle] = None, + auto_suggest: Optional[AutoSuggest] = None, + validator: Optional[Validator] = None, + clipboard: Optional[Clipboard] = None, + mouse_support: Optional[FilterOrBool] = None, + input_processors: Optional[List[Processor]] = None, + placeholder: Optional[AnyFormattedText] = None, + reserve_space_for_menu: Optional[int] = None, + enable_system_prompt: Optional[FilterOrBool] = None, + enable_suspend: Optional[FilterOrBool] = None, + enable_open_in_editor: Optional[FilterOrBool] = None, + tempfile_suffix: Optional[Union[str, Callable[[], str]]] = None, + tempfile: Optional[Union[str, Callable[[], str]]] = None, + # Following arguments are specific to the current `prompt()` call. + default: Union[str, Document] = "", + accept_default: bool = False, + pre_run: Optional[Callable[[], None]] = None, + set_exception_handler: bool = True, + ) -> _T: + """ + Display the prompt. + + The first set of arguments is a subset of the :class:`~.PromptSession` + class itself. For these, passing in ``None`` will keep the current + values that are active in the session. Passing in a value will set the + attribute for the session, which means that it applies to the current, + but also to the next prompts. + + Note that in order to erase a ``Completer``, ``Validator`` or + ``AutoSuggest``, you can't use ``None``. Instead pass in a + ``DummyCompleter``, ``DummyValidator`` or ``DummyAutoSuggest`` instance + respectively. For a ``Lexer`` you can pass in an empty ``SimpleLexer``. + + Additional arguments, specific for this prompt: + + :param default: The default input text to be shown. (This can be edited + by the user). + :param accept_default: When `True`, automatically accept the default + value without allowing the user to edit the input. + :param pre_run: Callable, called at the start of `Application.run`. + + This method will raise ``KeyboardInterrupt`` when control-c has been + pressed (for abort) and ``EOFError`` when control-d has been pressed + (for exit). + """ + # NOTE: We used to create a backup of the PromptSession attributes and + # restore them after exiting the prompt. This code has been + # removed, because it was confusing and didn't really serve a use + # case. (People were changing `Application.editing_mode` + # dynamically and surprised that it was reset after every call.) + + # NOTE 2: YES, this is a lot of repeation below... + # However, it is a very convenient for a user to accept all + # these parameters in this `prompt` method as well. We could + # use `locals()` and `setattr` to avoid the repetition, but + # then we loose the advantage of mypy and pyflakes to be able + # to verify the code. + if message is not None: + self.message = message + if editing_mode is not None: + self.editing_mode = editing_mode + if refresh_interval is not None: + self.refresh_interval = refresh_interval + if vi_mode: + self.editing_mode = EditingMode.VI + if lexer is not None: + self.lexer = lexer + if completer is not None: + self.completer = completer + if complete_in_thread is not None: + self.complete_in_thread = complete_in_thread + if is_password is not None: + self.is_password = is_password + if key_bindings is not None: + self.key_bindings = key_bindings + if bottom_toolbar is not None: + self.bottom_toolbar = bottom_toolbar + if style is not None: + self.style = style + if color_depth is not None: + self.color_depth = color_depth + if include_default_pygments_style is not None: + self.include_default_pygments_style = include_default_pygments_style + if style_transformation is not None: + self.style_transformation = style_transformation + if swap_light_and_dark_colors is not None: + self.swap_light_and_dark_colors = swap_light_and_dark_colors + if rprompt is not None: + self.rprompt = rprompt + if multiline is not None: + self.multiline = multiline + if prompt_continuation is not None: + self.prompt_continuation = prompt_continuation + if wrap_lines is not None: + self.wrap_lines = wrap_lines + if enable_history_search is not None: + self.enable_history_search = enable_history_search + if search_ignore_case is not None: + self.search_ignore_case = search_ignore_case + if complete_while_typing is not None: + self.complete_while_typing = complete_while_typing + if validate_while_typing is not None: + self.validate_while_typing = validate_while_typing + if complete_style is not None: + self.complete_style = complete_style + if auto_suggest is not None: + self.auto_suggest = auto_suggest + if validator is not None: + self.validator = validator + if clipboard is not None: + self.clipboard = clipboard + if mouse_support is not None: + self.mouse_support = mouse_support + if input_processors is not None: + self.input_processors = input_processors + if placeholder is not None: + self.placeholder = placeholder + if reserve_space_for_menu is not None: + self.reserve_space_for_menu = reserve_space_for_menu + if enable_system_prompt is not None: + self.enable_system_prompt = enable_system_prompt + if enable_suspend is not None: + self.enable_suspend = enable_suspend + if enable_open_in_editor is not None: + self.enable_open_in_editor = enable_open_in_editor + if tempfile_suffix is not None: + self.tempfile_suffix = tempfile_suffix + if tempfile is not None: + self.tempfile = tempfile + + self._add_pre_run_callables(pre_run, accept_default) + self.default_buffer.reset( + default if isinstance(default, Document) else Document(default) + ) + self.app.refresh_interval = self.refresh_interval # This is not reactive. + + # If we are using the default output, and have a dumb terminal. Use the + # dumb prompt. + if self._output is None and is_dumb_terminal(): + return get_event_loop().run_until_complete(self._dumb_prompt(self.message)) + + return self.app.run(set_exception_handler=set_exception_handler) + + async def _dumb_prompt(self, message: AnyFormattedText = "") -> _T: + """ + Prompt function for dumb terminals. + + Dumb terminals have minimum rendering capabilities. We can only print + text to the screen. We can't use colors, and we can't do cursor + movements. The Emacs inferior shell is an example of a dumb terminal. + + We will show the prompt, and wait for the input. We still handle arrow + keys, and all custom key bindings, but we don't really render the + cursor movements. Instead we only print the typed character that's + right before the cursor. + """ + # Send prompt to output. + self.output.write(fragment_list_to_text(to_formatted_text(self.message))) + self.output.flush() + + # Key bindings for the dumb prompt: mostly the same as the full prompt. + key_bindings: KeyBindingsBase = self._create_prompt_bindings() + if self.key_bindings: + key_bindings = merge_key_bindings([self.key_bindings, key_bindings]) + + # Create and run application. + application = cast( + Application[_T], + Application( + input=self.input, + output=DummyOutput(), + layout=self.layout, + key_bindings=key_bindings, + ), + ) + + def on_text_changed(_) -> None: + self.output.write(self.default_buffer.document.text_before_cursor[-1:]) + self.output.flush() + + self.default_buffer.on_text_changed += on_text_changed + result = await application.run_async() + + # Render line ending. + self.output.write("\r\n") + self.output.flush() + + return result + + async def prompt_async( + self, + # When any of these arguments are passed, this value is overwritten + # in this PromptSession. + message: Optional[AnyFormattedText] = None, + # `message` should go first, because people call it as + # positional argument. + *, + editing_mode: Optional[EditingMode] = None, + refresh_interval: Optional[float] = None, + vi_mode: Optional[bool] = None, + lexer: Optional[Lexer] = None, + completer: Optional[Completer] = None, + complete_in_thread: Optional[bool] = None, + is_password: Optional[bool] = None, + key_bindings: Optional[KeyBindingsBase] = None, + bottom_toolbar: Optional[AnyFormattedText] = None, + style: Optional[BaseStyle] = None, + color_depth: Optional[ColorDepth] = None, + include_default_pygments_style: Optional[FilterOrBool] = None, + style_transformation: Optional[StyleTransformation] = None, + swap_light_and_dark_colors: Optional[FilterOrBool] = None, + rprompt: Optional[AnyFormattedText] = None, + multiline: Optional[FilterOrBool] = None, + prompt_continuation: Optional[PromptContinuationText] = None, + wrap_lines: Optional[FilterOrBool] = None, + enable_history_search: Optional[FilterOrBool] = None, + search_ignore_case: Optional[FilterOrBool] = None, + complete_while_typing: Optional[FilterOrBool] = None, + validate_while_typing: Optional[FilterOrBool] = None, + complete_style: Optional[CompleteStyle] = None, + auto_suggest: Optional[AutoSuggest] = None, + validator: Optional[Validator] = None, + clipboard: Optional[Clipboard] = None, + mouse_support: Optional[FilterOrBool] = None, + input_processors: Optional[List[Processor]] = None, + placeholder: Optional[AnyFormattedText] = None, + reserve_space_for_menu: Optional[int] = None, + enable_system_prompt: Optional[FilterOrBool] = None, + enable_suspend: Optional[FilterOrBool] = None, + enable_open_in_editor: Optional[FilterOrBool] = None, + tempfile_suffix: Optional[Union[str, Callable[[], str]]] = None, + tempfile: Optional[Union[str, Callable[[], str]]] = None, + # Following arguments are specific to the current `prompt()` call. + default: Union[str, Document] = "", + accept_default: bool = False, + pre_run: Optional[Callable[[], None]] = None, + set_exception_handler: bool = True, + ) -> _T: + + if message is not None: + self.message = message + if editing_mode is not None: + self.editing_mode = editing_mode + if refresh_interval is not None: + self.refresh_interval = refresh_interval + if vi_mode: + self.editing_mode = EditingMode.VI + if lexer is not None: + self.lexer = lexer + if completer is not None: + self.completer = completer + if complete_in_thread is not None: + self.complete_in_thread = complete_in_thread + if is_password is not None: + self.is_password = is_password + if key_bindings is not None: + self.key_bindings = key_bindings + if bottom_toolbar is not None: + self.bottom_toolbar = bottom_toolbar + if style is not None: + self.style = style + if color_depth is not None: + self.color_depth = color_depth + if include_default_pygments_style is not None: + self.include_default_pygments_style = include_default_pygments_style + if style_transformation is not None: + self.style_transformation = style_transformation + if swap_light_and_dark_colors is not None: + self.swap_light_and_dark_colors = swap_light_and_dark_colors + if rprompt is not None: + self.rprompt = rprompt + if multiline is not None: + self.multiline = multiline + if prompt_continuation is not None: + self.prompt_continuation = prompt_continuation + if wrap_lines is not None: + self.wrap_lines = wrap_lines + if enable_history_search is not None: + self.enable_history_search = enable_history_search + if search_ignore_case is not None: + self.search_ignore_case = search_ignore_case + if complete_while_typing is not None: + self.complete_while_typing = complete_while_typing + if validate_while_typing is not None: + self.validate_while_typing = validate_while_typing + if complete_style is not None: + self.complete_style = complete_style + if auto_suggest is not None: + self.auto_suggest = auto_suggest + if validator is not None: + self.validator = validator + if clipboard is not None: + self.clipboard = clipboard + if mouse_support is not None: + self.mouse_support = mouse_support + if input_processors is not None: + self.input_processors = input_processors + if placeholder is not None: + self.placeholder = placeholder + if reserve_space_for_menu is not None: + self.reserve_space_for_menu = reserve_space_for_menu + if enable_system_prompt is not None: + self.enable_system_prompt = enable_system_prompt + if enable_suspend is not None: + self.enable_suspend = enable_suspend + if enable_open_in_editor is not None: + self.enable_open_in_editor = enable_open_in_editor + if tempfile_suffix is not None: + self.tempfile_suffix = tempfile_suffix + if tempfile is not None: + self.tempfile = tempfile + + self._add_pre_run_callables(pre_run, accept_default) + self.default_buffer.reset( + default if isinstance(default, Document) else Document(default) + ) + self.app.refresh_interval = self.refresh_interval # This is not reactive. + + # If we are using the default output, and have a dumb terminal. Use the + # dumb prompt. + if self._output is None and is_dumb_terminal(): + return await self._dumb_prompt(self.message) + + return await self.app.run_async(set_exception_handler=set_exception_handler) + + def _add_pre_run_callables( + self, pre_run: Optional[Callable[[], None]], accept_default: bool + ) -> None: + def pre_run2() -> None: + if pre_run: + pre_run() + + if accept_default: + # Validate and handle input. We use `call_from_executor` in + # order to run it "soon" (during the next iteration of the + # event loop), instead of right now. Otherwise, it won't + # display the default value. + get_event_loop().call_soon(self.default_buffer.validate_and_handle) + + self.app.pre_run_callables.append(pre_run2) + + @property + def editing_mode(self) -> EditingMode: + return self.app.editing_mode + + @editing_mode.setter + def editing_mode(self, value: EditingMode) -> None: + self.app.editing_mode = value + + def _get_default_buffer_control_height(self) -> Dimension: + # If there is an autocompletion menu to be shown, make sure that our + # layout has at least a minimal height in order to display it. + if ( + self.completer is not None + and self.complete_style != CompleteStyle.READLINE_LIKE + ): + space = self.reserve_space_for_menu + else: + space = 0 + + if space and not get_app().is_done: + buff = self.default_buffer + + # Reserve the space, either when there are completions, or when + # `complete_while_typing` is true and we expect completions very + # soon. + if buff.complete_while_typing() or buff.complete_state is not None: + return Dimension(min=space) + + return Dimension() + + def _get_prompt(self) -> StyleAndTextTuples: + return to_formatted_text(self.message, style="class:prompt") + + def _get_continuation( + self, width: int, line_number: int, wrap_count: int + ) -> StyleAndTextTuples: + """ + Insert the prompt continuation. + + :param width: The width that was used for the prompt. (more or less can + be used.) + :param line_number: + :param wrap_count: Amount of times that the line has been wrapped. + """ + prompt_continuation = self.prompt_continuation + + if callable(prompt_continuation): + continuation: AnyFormattedText = prompt_continuation( + width, line_number, wrap_count + ) + else: + continuation = prompt_continuation + + # When the continuation prompt is not given, choose the same width as + # the actual prompt. + if continuation is None and is_true(self.multiline): + continuation = " " * width + + return to_formatted_text(continuation, style="class:prompt-continuation") + + def _get_line_prefix( + self, + line_number: int, + wrap_count: int, + get_prompt_text_2: _StyleAndTextTuplesCallable, + ) -> StyleAndTextTuples: + """ + Return whatever needs to be inserted before every line. + (the prompt, or a line continuation.) + """ + # First line: display the "arg" or the prompt. + if line_number == 0 and wrap_count == 0: + if not is_true(self.multiline) and get_app().key_processor.arg is not None: + return self._inline_arg() + else: + return get_prompt_text_2() + + # For the next lines, display the appropriate continuation. + prompt_width = get_cwidth(fragment_list_to_text(get_prompt_text_2())) + return self._get_continuation(prompt_width, line_number, wrap_count) + + def _get_arg_text(self) -> StyleAndTextTuples: + " 'arg' toolbar, for in multiline mode. " + arg = self.app.key_processor.arg + if arg is None: + # Should not happen because of the `has_arg` filter in the layout. + return [] + + if arg == "-": + arg = "-1" + + return [("class:arg-toolbar", "Repeat: "), ("class:arg-toolbar.text", arg)] + + def _inline_arg(self) -> StyleAndTextTuples: + " 'arg' prefix, for in single line mode. " + app = get_app() + if app.key_processor.arg is None: + return [] + else: + arg = app.key_processor.arg + + return [ + ("class:prompt.arg", "(arg: "), + ("class:prompt.arg.text", str(arg)), + ("class:prompt.arg", ") "), + ] + + # Expose the Input and Output objects as attributes, mainly for + # backward-compatibility. + + @property + def input(self) -> Input: + return self.app.input + + @property + def output(self) -> Output: + return self.app.output + + +def prompt( + message: Optional[AnyFormattedText] = None, + *, + history: Optional[History] = None, + editing_mode: Optional[EditingMode] = None, + refresh_interval: Optional[float] = None, + vi_mode: Optional[bool] = None, + lexer: Optional[Lexer] = None, + completer: Optional[Completer] = None, + complete_in_thread: Optional[bool] = None, + is_password: Optional[bool] = None, + key_bindings: Optional[KeyBindingsBase] = None, + bottom_toolbar: Optional[AnyFormattedText] = None, + style: Optional[BaseStyle] = None, + color_depth: Optional[ColorDepth] = None, + include_default_pygments_style: Optional[FilterOrBool] = None, + style_transformation: Optional[StyleTransformation] = None, + swap_light_and_dark_colors: Optional[FilterOrBool] = None, + rprompt: Optional[AnyFormattedText] = None, + multiline: Optional[FilterOrBool] = None, + prompt_continuation: Optional[PromptContinuationText] = None, + wrap_lines: Optional[FilterOrBool] = None, + enable_history_search: Optional[FilterOrBool] = None, + search_ignore_case: Optional[FilterOrBool] = None, + complete_while_typing: Optional[FilterOrBool] = None, + validate_while_typing: Optional[FilterOrBool] = None, + complete_style: Optional[CompleteStyle] = None, + auto_suggest: Optional[AutoSuggest] = None, + validator: Optional[Validator] = None, + clipboard: Optional[Clipboard] = None, + mouse_support: Optional[FilterOrBool] = None, + input_processors: Optional[List[Processor]] = None, + placeholder: Optional[AnyFormattedText] = None, + reserve_space_for_menu: Optional[int] = None, + enable_system_prompt: Optional[FilterOrBool] = None, + enable_suspend: Optional[FilterOrBool] = None, + enable_open_in_editor: Optional[FilterOrBool] = None, + tempfile_suffix: Optional[Union[str, Callable[[], str]]] = None, + tempfile: Optional[Union[str, Callable[[], str]]] = None, + # Following arguments are specific to the current `prompt()` call. + default: str = "", + accept_default: bool = False, + pre_run: Optional[Callable[[], None]] = None, +) -> str: + """ + The global `prompt` function. This will create a new `PromptSession` + instance for every call. + """ + # The history is the only attribute that has to be passed to the + # `PromptSession`, it can't be passed into the `prompt()` method. + session: PromptSession[str] = PromptSession(history=history) + + return session.prompt( + message, + editing_mode=editing_mode, + refresh_interval=refresh_interval, + vi_mode=vi_mode, + lexer=lexer, + completer=completer, + complete_in_thread=complete_in_thread, + is_password=is_password, + key_bindings=key_bindings, + bottom_toolbar=bottom_toolbar, + style=style, + color_depth=color_depth, + include_default_pygments_style=include_default_pygments_style, + style_transformation=style_transformation, + swap_light_and_dark_colors=swap_light_and_dark_colors, + rprompt=rprompt, + multiline=multiline, + prompt_continuation=prompt_continuation, + wrap_lines=wrap_lines, + enable_history_search=enable_history_search, + search_ignore_case=search_ignore_case, + complete_while_typing=complete_while_typing, + validate_while_typing=validate_while_typing, + complete_style=complete_style, + auto_suggest=auto_suggest, + validator=validator, + clipboard=clipboard, + mouse_support=mouse_support, + input_processors=input_processors, + placeholder=placeholder, + reserve_space_for_menu=reserve_space_for_menu, + enable_system_prompt=enable_system_prompt, + enable_suspend=enable_suspend, + enable_open_in_editor=enable_open_in_editor, + tempfile_suffix=tempfile_suffix, + tempfile=tempfile, + default=default, + accept_default=accept_default, + pre_run=pre_run, + ) + + +prompt.__doc__ = PromptSession.prompt.__doc__ + + +def create_confirm_session( + message: str, suffix: str = " (y/n) " +) -> PromptSession[bool]: + """ + Create a `PromptSession` object for the 'confirm' function. + """ + bindings = KeyBindings() + + @bindings.add("y") + @bindings.add("Y") + def yes(event: E) -> None: + session.default_buffer.text = "y" + event.app.exit(result=True) + + @bindings.add("n") + @bindings.add("N") + def no(event: E) -> None: + session.default_buffer.text = "n" + event.app.exit(result=False) + + @bindings.add(Keys.Any) + def _(event: E) -> None: + " Disallow inserting other text. " + pass + + complete_message = merge_formatted_text([message, suffix]) + session: PromptSession[bool] = PromptSession( + complete_message, key_bindings=bindings + ) + return session + + +def confirm(message: str = "Confirm?", suffix: str = " (y/n) ") -> bool: + """ + Display a confirmation prompt that returns True/False. + """ + session = create_confirm_session(message, suffix) + return session.prompt() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/utils.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/utils.py new file mode 100644 index 00000000..3cd49b81 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/shortcuts/utils.py @@ -0,0 +1,198 @@ +from asyncio import get_event_loop +from typing import TYPE_CHECKING, Any, Optional, TextIO + +from prompt_toolkit.application import Application +from prompt_toolkit.application.current import get_app_session +from prompt_toolkit.formatted_text import ( + FormattedText, + StyleAndTextTuples, + to_formatted_text, +) +from prompt_toolkit.input import DummyInput +from prompt_toolkit.layout import Layout +from prompt_toolkit.output import ColorDepth, Output +from prompt_toolkit.output.defaults import create_output +from prompt_toolkit.renderer import ( + print_formatted_text as renderer_print_formatted_text, +) +from prompt_toolkit.styles import ( + BaseStyle, + StyleTransformation, + default_pygments_style, + default_ui_style, + merge_styles, +) + +if TYPE_CHECKING: + from prompt_toolkit.layout.containers import Container + +__all__ = [ + "print_formatted_text", + "print_container", + "clear", + "set_title", + "clear_title", +] + + +def print_formatted_text( + *values: Any, + sep: str = " ", + end: str = "\n", + file: Optional[TextIO] = None, + flush: bool = False, + style: Optional[BaseStyle] = None, + output: Optional[Output] = None, + color_depth: Optional[ColorDepth] = None, + style_transformation: Optional[StyleTransformation] = None, + include_default_pygments_style: bool = True, +) -> None: + """ + :: + + print_formatted_text(*values, sep=' ', end='\\n', file=None, flush=False, style=None, output=None) + + Print text to stdout. This is supposed to be compatible with Python's print + function, but supports printing of formatted text. You can pass a + :class:`~prompt_toolkit.formatted_text.FormattedText`, + :class:`~prompt_toolkit.formatted_text.HTML` or + :class:`~prompt_toolkit.formatted_text.ANSI` object to print formatted + text. + + * Print HTML as follows:: + + print_formatted_text(HTML('Some italic text This is red!')) + + style = Style.from_dict({ + 'hello': '#ff0066', + 'world': '#884444 italic', + }) + print_formatted_text(HTML('Hello world!'), style=style) + + * Print a list of (style_str, text) tuples in the given style to the + output. E.g.:: + + style = Style.from_dict({ + 'hello': '#ff0066', + 'world': '#884444 italic', + }) + fragments = FormattedText([ + ('class:hello', 'Hello'), + ('class:world', 'World'), + ]) + print_formatted_text(fragments, style=style) + + If you want to print a list of Pygments tokens, wrap it in + :class:`~prompt_toolkit.formatted_text.PygmentsTokens` to do the + conversion. + + :param values: Any kind of printable object, or formatted string. + :param sep: String inserted between values, default a space. + :param end: String appended after the last value, default a newline. + :param style: :class:`.Style` instance for the color scheme. + :param include_default_pygments_style: `bool`. Include the default Pygments + style when set to `True` (the default). + """ + assert not (output and file) + + # Build/merge style. + styles = [default_ui_style()] + if include_default_pygments_style: + styles.append(default_pygments_style()) + if style: + styles.append(style) + + merged_style = merge_styles(styles) + + # Create Output object. + if output is None: + if file: + output = create_output(stdout=file) + else: + output = get_app_session().output + + assert isinstance(output, Output) + + # Get color depth. + color_depth = color_depth or output.get_default_color_depth() + + # Merges values. + def to_text(val: Any) -> StyleAndTextTuples: + # Normal lists which are not instances of `FormattedText` are + # considered plain text. + if isinstance(val, list) and not isinstance(val, FormattedText): + return to_formatted_text("{0}".format(val)) + return to_formatted_text(val, auto_convert=True) + + fragments = [] + for i, value in enumerate(values): + fragments.extend(to_text(value)) + + if sep and i != len(values) - 1: + fragments.extend(to_text(sep)) + + fragments.extend(to_text(end)) + + # Print output. + renderer_print_formatted_text( + output, + fragments, + merged_style, + color_depth=color_depth, + style_transformation=style_transformation, + ) + + # Flush the output stream. + if flush: + output.flush() + + +def print_container(container: "Container", file: Optional[TextIO] = None) -> None: + """ + Print any layout to the output in a non-interactive way. + + Example usage:: + + from prompt_toolkit.widgets import Frame, TextArea + print_container( + Frame(TextArea(text='Hello world!'))) + """ + if file: + output = create_output(stdout=file) + else: + output = get_app_session().output + + def exit_immediately() -> None: + # Use `call_from_executor` to exit "soon", so that we still render one + # initial time, before exiting the application. + get_event_loop().call_soon(lambda: app.exit()) + + app: Application[None] = Application( + layout=Layout(container=container), output=output, input=DummyInput() + ) + app.run(pre_run=exit_immediately) + + +def clear() -> None: + """ + Clear the screen. + """ + output = get_app_session().output + output.erase_screen() + output.cursor_goto(0, 0) + output.flush() + + +def set_title(text: str) -> None: + """ + Set the terminal title. + """ + output = get_app_session().output + output.set_title(text) + + +def clear_title() -> None: + """ + Erase the current title. + """ + set_title("") diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__init__.py new file mode 100644 index 00000000..c270ae01 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__init__.py @@ -0,0 +1,64 @@ +""" +Styling for prompt_toolkit applications. +""" +from .base import ( + ANSI_COLOR_NAMES, + DEFAULT_ATTRS, + Attrs, + BaseStyle, + DummyStyle, + DynamicStyle, +) +from .defaults import default_pygments_style, default_ui_style +from .named_colors import NAMED_COLORS +from .pygments import ( + pygments_token_to_classname, + style_from_pygments_cls, + style_from_pygments_dict, +) +from .style import Priority, Style, merge_styles, parse_color +from .style_transformation import ( + AdjustBrightnessStyleTransformation, + ConditionalStyleTransformation, + DummyStyleTransformation, + DynamicStyleTransformation, + ReverseStyleTransformation, + SetDefaultColorStyleTransformation, + StyleTransformation, + SwapLightAndDarkStyleTransformation, + merge_style_transformations, +) + +__all__ = [ + # Base. + "Attrs", + "DEFAULT_ATTRS", + "ANSI_COLOR_NAMES", + "BaseStyle", + "DummyStyle", + "DynamicStyle", + # Defaults. + "default_ui_style", + "default_pygments_style", + # Style. + "Style", + "Priority", + "merge_styles", + "parse_color", + # Style transformation. + "StyleTransformation", + "SwapLightAndDarkStyleTransformation", + "ReverseStyleTransformation", + "SetDefaultColorStyleTransformation", + "AdjustBrightnessStyleTransformation", + "DummyStyleTransformation", + "ConditionalStyleTransformation", + "DynamicStyleTransformation", + "merge_style_transformations", + # Pygments. + "style_from_pygments_cls", + "style_from_pygments_dict", + "pygments_token_to_classname", + # Named colors. + "NAMED_COLORS", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..07b272ba Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/base.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/base.cpython-38.pyc new file mode 100644 index 00000000..2d5d7a1d Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/base.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/defaults.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/defaults.cpython-38.pyc new file mode 100644 index 00000000..5c07043e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/defaults.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/named_colors.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/named_colors.cpython-38.pyc new file mode 100644 index 00000000..c908a754 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/named_colors.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/pygments.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/pygments.cpython-38.pyc new file mode 100644 index 00000000..a3cd5b82 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/pygments.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/style.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/style.cpython-38.pyc new file mode 100644 index 00000000..a8e3101e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/style.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/style_transformation.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/style_transformation.cpython-38.pyc new file mode 100644 index 00000000..f568a341 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/__pycache__/style_transformation.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/base.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/base.py new file mode 100644 index 00000000..e28fb5a4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/base.py @@ -0,0 +1,181 @@ +""" +The base classes for the styling. +""" +from abc import ABCMeta, abstractmethod, abstractproperty +from typing import Callable, Dict, Hashable, List, NamedTuple, Optional, Tuple + +__all__ = [ + "Attrs", + "DEFAULT_ATTRS", + "ANSI_COLOR_NAMES", + "ANSI_COLOR_NAMES_ALIASES", + "BaseStyle", + "DummyStyle", + "DynamicStyle", +] + + +#: Style attributes. +Attrs = NamedTuple( + "Attrs", + [ + ("color", Optional[str]), + ("bgcolor", Optional[str]), + ("bold", Optional[bool]), + ("underline", Optional[bool]), + ("italic", Optional[bool]), + ("blink", Optional[bool]), + ("reverse", Optional[bool]), + ("hidden", Optional[bool]), + ], +) + +""" +:param color: Hexadecimal string. E.g. '000000' or Ansi color name: e.g. 'ansiblue' +:param bgcolor: Hexadecimal string. E.g. 'ffffff' or Ansi color name: e.g. 'ansired' +:param bold: Boolean +:param underline: Boolean +:param italic: Boolean +:param blink: Boolean +:param reverse: Boolean +:param hidden: Boolean +""" + +#: The default `Attrs`. +DEFAULT_ATTRS = Attrs( + color="", + bgcolor="", + bold=False, + underline=False, + italic=False, + blink=False, + reverse=False, + hidden=False, +) + + +#: ``Attrs.bgcolor/fgcolor`` can be in either 'ffffff' format, or can be any of +#: the following in case we want to take colors from the 8/16 color palette. +#: Usually, in that case, the terminal application allows to configure the RGB +#: values for these names. +#: ISO 6429 colors +ANSI_COLOR_NAMES = [ + "ansidefault", + # Low intensity, dark. (One or two components 0x80, the other 0x00.) + "ansiblack", + "ansired", + "ansigreen", + "ansiyellow", + "ansiblue", + "ansimagenta", + "ansicyan", + "ansigray", + # High intensity, bright. (One or two components 0xff, the other 0x00. Not supported everywhere.) + "ansibrightblack", + "ansibrightred", + "ansibrightgreen", + "ansibrightyellow", + "ansibrightblue", + "ansibrightmagenta", + "ansibrightcyan", + "ansiwhite", +] + + +# People don't use the same ANSI color names everywhere. In prompt_toolkit 1.0 +# we used some unconventional names (which were contributed like that to +# Pygments). This is fixed now, but we still support the old names. + +# The table below maps the old aliases to the current names. +ANSI_COLOR_NAMES_ALIASES: Dict[str, str] = { + "ansidarkgray": "ansibrightblack", + "ansiteal": "ansicyan", + "ansiturquoise": "ansibrightcyan", + "ansibrown": "ansiyellow", + "ansipurple": "ansimagenta", + "ansifuchsia": "ansibrightmagenta", + "ansilightgray": "ansigray", + "ansidarkred": "ansired", + "ansidarkgreen": "ansigreen", + "ansidarkblue": "ansiblue", +} +assert set(ANSI_COLOR_NAMES_ALIASES.values()).issubset(set(ANSI_COLOR_NAMES)) +assert not (set(ANSI_COLOR_NAMES_ALIASES.keys()) & set(ANSI_COLOR_NAMES)) + + +class BaseStyle(metaclass=ABCMeta): + """ + Abstract base class for prompt_toolkit styles. + """ + + @abstractmethod + def get_attrs_for_style_str( + self, style_str: str, default: Attrs = DEFAULT_ATTRS + ) -> Attrs: + """ + Return :class:`.Attrs` for the given style string. + + :param style_str: The style string. This can contain inline styling as + well as classnames (e.g. "class:title"). + :param default: `Attrs` to be used if no styling was defined. + """ + + @abstractproperty + def style_rules(self) -> List[Tuple[str, str]]: + """ + The list of style rules, used to create this style. + (Required for `DynamicStyle` and `_MergedStyle` to work.) + """ + return [] + + @abstractmethod + def invalidation_hash(self) -> Hashable: + """ + Invalidation hash for the style. When this changes over time, the + renderer knows that something in the style changed, and that everything + has to be redrawn. + """ + + +class DummyStyle(BaseStyle): + """ + A style that doesn't style anything. + """ + + def get_attrs_for_style_str( + self, style_str: str, default: Attrs = DEFAULT_ATTRS + ) -> Attrs: + return default + + def invalidation_hash(self) -> Hashable: + return 1 # Always the same value. + + @property + def style_rules(self) -> List[Tuple[str, str]]: + return [] + + +class DynamicStyle(BaseStyle): + """ + Style class that can dynamically returns an other Style. + + :param get_style: Callable that returns a :class:`.Style` instance. + """ + + def __init__(self, get_style: Callable[[], Optional[BaseStyle]]): + self.get_style = get_style + self._dummy = DummyStyle() + + def get_attrs_for_style_str( + self, style_str: str, default: Attrs = DEFAULT_ATTRS + ) -> Attrs: + style = self.get_style() or self._dummy + + return style.get_attrs_for_style_str(style_str, default) + + def invalidation_hash(self) -> Hashable: + return (self.get_style() or self._dummy).invalidation_hash() + + @property + def style_rules(self) -> List[Tuple[str, str]]: + return (self.get_style() or self._dummy).style_rules diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/defaults.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/defaults.py new file mode 100644 index 00000000..92a0cb83 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/defaults.py @@ -0,0 +1,227 @@ +""" +The default styling. +""" +from prompt_toolkit.cache import memoized + +from .base import ANSI_COLOR_NAMES +from .named_colors import NAMED_COLORS +from .style import BaseStyle, Style, merge_styles + +__all__ = [ + "default_ui_style", + "default_pygments_style", +] + +#: Default styling. Mapping from classnames to their style definition. +PROMPT_TOOLKIT_STYLE = [ + # Highlighting of search matches in document. + ("search", "bg:ansibrightyellow ansiblack"), + ("search.current", ""), + # Incremental search. + ("incsearch", ""), + ("incsearch.current", "reverse"), + # Highlighting of select text in document. + ("selected", "reverse"), + ("cursor-column", "bg:#dddddd"), + ("cursor-line", "underline"), + ("color-column", "bg:#ccaacc"), + # Highlighting of matching brackets. + ("matching-bracket", ""), + ("matching-bracket.other", "#000000 bg:#aacccc"), + ("matching-bracket.cursor", "#ff8888 bg:#880000"), + # Styling of other cursors, in case of block editing. + ("multiple-cursors", "#000000 bg:#ccccaa"), + # Line numbers. + ("line-number", "#888888"), + ("line-number.current", "bold"), + ("tilde", "#8888ff"), + # Default prompt. + ("prompt", ""), + ("prompt.arg", "noinherit"), + ("prompt.arg.text", ""), + ("prompt.search", "noinherit"), + ("prompt.search.text", ""), + # Search toolbar. + ("search-toolbar", "bold"), + ("search-toolbar.text", "nobold"), + # System toolbar + ("system-toolbar", "bold"), + ("system-toolbar.text", "nobold"), + # "arg" toolbar. + ("arg-toolbar", "bold"), + ("arg-toolbar.text", "nobold"), + # Validation toolbar. + ("validation-toolbar", "bg:#550000 #ffffff"), + ("window-too-small", "bg:#550000 #ffffff"), + # Completions toolbar. + ("completion-toolbar", "bg:#bbbbbb #000000"), + ("completion-toolbar.arrow", "bg:#bbbbbb #000000 bold"), + ("completion-toolbar.completion", "bg:#bbbbbb #000000"), + ("completion-toolbar.completion.current", "bg:#444444 #ffffff"), + # Completions menu. + ("completion-menu", "bg:#bbbbbb #000000"), + ("completion-menu.completion", ""), + ("completion-menu.completion.current", "bg:#888888 #ffffff"), + ("completion-menu.meta.completion", "bg:#999999 #000000"), + ("completion-menu.meta.completion.current", "bg:#aaaaaa #000000"), + ("completion-menu.multi-column-meta", "bg:#aaaaaa #000000"), + # Fuzzy matches in completion menu (for FuzzyCompleter). + ("completion-menu.completion fuzzymatch.outside", "fg:#444444"), + ("completion-menu.completion fuzzymatch.inside", "bold"), + ("completion-menu.completion fuzzymatch.inside.character", "underline"), + ("completion-menu.completion.current fuzzymatch.outside", "fg:default"), + ("completion-menu.completion.current fuzzymatch.inside", "nobold"), + # Styling of readline-like completions. + ("readline-like-completions", ""), + ("readline-like-completions.completion", ""), + ("readline-like-completions.completion fuzzymatch.outside", "#888888"), + ("readline-like-completions.completion fuzzymatch.inside", ""), + ("readline-like-completions.completion fuzzymatch.inside.character", "underline"), + # Scrollbars. + ("scrollbar.background", "bg:#aaaaaa"), + ("scrollbar.button", "bg:#444444"), + ("scrollbar.arrow", "noinherit bold"), + # Start/end of scrollbars. Adding 'underline' here provides a nice little + # detail to the progress bar, but it doesn't look good on all terminals. + # ('scrollbar.start', 'underline #ffffff'), + # ('scrollbar.end', 'underline #000000'), + # Auto suggestion text. + ("auto-suggestion", "#666666"), + # Trailing whitespace and tabs. + ("trailing-whitespace", "#999999"), + ("tab", "#999999"), + # When Control-C/D has been pressed. Grayed. + ("aborting", "#888888 bg:default noreverse noitalic nounderline noblink"), + ("exiting", "#888888 bg:default noreverse noitalic nounderline noblink"), + # Entering a Vi digraph. + ("digraph", "#4444ff"), + # Control characters, like ^C, ^X. + ("control-character", "ansiblue"), + # Non-breaking space. + ("nbsp", "underline ansiyellow"), + # Default styling of HTML elements. + ("i", "italic"), + ("u", "underline"), + ("b", "bold"), + ("em", "italic"), + ("strong", "bold"), + ("hidden", "hidden"), + # It should be possible to use the style names in HTML. + # ... or .... + ("italic", "italic"), + ("underline", "underline"), + ("bold", "bold"), + ("reverse", "reverse"), + ("noitalic", "noitalic"), + ("nounderline", "nounderline"), + ("nobold", "nobold"), + ("noreverse", "noreverse"), + # Prompt bottom toolbar + ("bottom-toolbar", "reverse"), +] + + +# Style that will turn for instance the class 'red' into 'red'. +COLORS_STYLE = [(name, "fg:" + name) for name in ANSI_COLOR_NAMES] + [ + (name.lower(), "fg:" + name) for name in NAMED_COLORS +] + + +WIDGETS_STYLE = [ + # Dialog windows. + ("dialog", "bg:#4444ff"), + ("dialog.body", "bg:#ffffff #000000"), + ("dialog.body text-area", "bg:#cccccc"), + ("dialog.body text-area last-line", "underline"), + ("dialog frame.label", "#ff0000 bold"), + # Scrollbars in dialogs. + ("dialog.body scrollbar.background", ""), + ("dialog.body scrollbar.button", "bg:#000000"), + ("dialog.body scrollbar.arrow", ""), + ("dialog.body scrollbar.start", "nounderline"), + ("dialog.body scrollbar.end", "nounderline"), + # Buttons. + ("button", ""), + ("button.arrow", "bold"), + ("button.focused", "bg:#aa0000 #ffffff"), + # Menu bars. + ("menu-bar", "bg:#aaaaaa #000000"), + ("menu-bar.selected-item", "bg:#ffffff #000000"), + ("menu", "bg:#888888 #ffffff"), + ("menu.border", "#aaaaaa"), + ("menu.border shadow", "#444444"), + # Shadows. + ("dialog shadow", "bg:#000088"), + ("dialog.body shadow", "bg:#aaaaaa"), + ("progress-bar", "bg:#000088"), + ("progress-bar.used", "bg:#ff0000"), +] + + +# The default Pygments style, include this by default in case a Pygments lexer +# is used. +PYGMENTS_DEFAULT_STYLE = { + "pygments.whitespace": "#bbbbbb", + "pygments.comment": "italic #408080", + "pygments.comment.preproc": "noitalic #bc7a00", + "pygments.keyword": "bold #008000", + "pygments.keyword.pseudo": "nobold", + "pygments.keyword.type": "nobold #b00040", + "pygments.operator": "#666666", + "pygments.operator.word": "bold #aa22ff", + "pygments.name.builtin": "#008000", + "pygments.name.function": "#0000ff", + "pygments.name.class": "bold #0000ff", + "pygments.name.namespace": "bold #0000ff", + "pygments.name.exception": "bold #d2413a", + "pygments.name.variable": "#19177c", + "pygments.name.constant": "#880000", + "pygments.name.label": "#a0a000", + "pygments.name.entity": "bold #999999", + "pygments.name.attribute": "#7d9029", + "pygments.name.tag": "bold #008000", + "pygments.name.decorator": "#aa22ff", + # Note: In Pygments, Token.String is an alias for Token.Literal.String, + # and Token.Number as an alias for Token.Literal.Number. + "pygments.literal.string": "#ba2121", + "pygments.literal.string.doc": "italic", + "pygments.literal.string.interpol": "bold #bb6688", + "pygments.literal.string.escape": "bold #bb6622", + "pygments.literal.string.regex": "#bb6688", + "pygments.literal.string.symbol": "#19177c", + "pygments.literal.string.other": "#008000", + "pygments.literal.number": "#666666", + "pygments.generic.heading": "bold #000080", + "pygments.generic.subheading": "bold #800080", + "pygments.generic.deleted": "#a00000", + "pygments.generic.inserted": "#00a000", + "pygments.generic.error": "#ff0000", + "pygments.generic.emph": "italic", + "pygments.generic.strong": "bold", + "pygments.generic.prompt": "bold #000080", + "pygments.generic.output": "#888", + "pygments.generic.traceback": "#04d", + "pygments.error": "border:#ff0000", +} + + +@memoized() +def default_ui_style() -> BaseStyle: + """ + Create a default `Style` object. + """ + return merge_styles( + [ + Style(PROMPT_TOOLKIT_STYLE), + Style(COLORS_STYLE), + Style(WIDGETS_STYLE), + ] + ) + + +@memoized() +def default_pygments_style() -> Style: + """ + Create a `Style` object that contains the default Pygments style. + """ + return Style.from_dict(PYGMENTS_DEFAULT_STYLE) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/named_colors.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/named_colors.py new file mode 100644 index 00000000..37bd6de4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/named_colors.py @@ -0,0 +1,161 @@ +""" +All modern web browsers support these 140 color names. +Taken from: https://www.w3schools.com/colors/colors_names.asp +""" +from typing import Dict + +__all__ = [ + "NAMED_COLORS", +] + + +NAMED_COLORS: Dict[str, str] = { + "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", + "DarkGreen": "#006400", + "DarkGrey": "#a9a9a9", + "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", + "Green": "#008000", + "GreenYellow": "#adff2f", + "Grey": "#808080", + "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", + "LightGray": "#d3d3d3", + "LightGreen": "#90ee90", + "LightGrey": "#d3d3d3", + "LightPink": "#ffb6c1", + "LightSalmon": "#ffa07a", + "LightSeaGreen": "#20b2aa", + "LightSkyBlue": "#87cefa", + "LightSlateGray": "#778899", + "LightSlateGrey": "#778899", + "LightSteelBlue": "#b0c4de", + "LightYellow": "#ffffe0", + "Lime": "#00ff00", + "LimeGreen": "#32cd32", + "Linen": "#faf0e6", + "Magenta": "#ff00ff", + "Maroon": "#800000", + "MediumAquaMarine": "#66cdaa", + "MediumBlue": "#0000cd", + "MediumOrchid": "#ba55d3", + "MediumPurple": "#9370db", + "MediumSeaGreen": "#3cb371", + "MediumSlateBlue": "#7b68ee", + "MediumSpringGreen": "#00fa9a", + "MediumTurquoise": "#48d1cc", + "MediumVioletRed": "#c71585", + "MidnightBlue": "#191970", + "MintCream": "#f5fffa", + "MistyRose": "#ffe4e1", + "Moccasin": "#ffe4b5", + "NavajoWhite": "#ffdead", + "Navy": "#000080", + "OldLace": "#fdf5e6", + "Olive": "#808000", + "OliveDrab": "#6b8e23", + "Orange": "#ffa500", + "OrangeRed": "#ff4500", + "Orchid": "#da70d6", + "PaleGoldenRod": "#eee8aa", + "PaleGreen": "#98fb98", + "PaleTurquoise": "#afeeee", + "PaleVioletRed": "#db7093", + "PapayaWhip": "#ffefd5", + "PeachPuff": "#ffdab9", + "Peru": "#cd853f", + "Pink": "#ffc0cb", + "Plum": "#dda0dd", + "PowderBlue": "#b0e0e6", + "Purple": "#800080", + "RebeccaPurple": "#663399", + "Red": "#ff0000", + "RosyBrown": "#bc8f8f", + "RoyalBlue": "#4169e1", + "SaddleBrown": "#8b4513", + "Salmon": "#fa8072", + "SandyBrown": "#f4a460", + "SeaGreen": "#2e8b57", + "SeaShell": "#fff5ee", + "Sienna": "#a0522d", + "Silver": "#c0c0c0", + "SkyBlue": "#87ceeb", + "SlateBlue": "#6a5acd", + "SlateGray": "#708090", + "SlateGrey": "#708090", + "Snow": "#fffafa", + "SpringGreen": "#00ff7f", + "SteelBlue": "#4682b4", + "Tan": "#d2b48c", + "Teal": "#008080", + "Thistle": "#d8bfd8", + "Tomato": "#ff6347", + "Turquoise": "#40e0d0", + "Violet": "#ee82ee", + "Wheat": "#f5deb3", + "White": "#ffffff", + "WhiteSmoke": "#f5f5f5", + "Yellow": "#ffff00", + "YellowGreen": "#9acd32", +} diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/pygments.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/pygments.py new file mode 100644 index 00000000..382e5e31 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/pygments.py @@ -0,0 +1,67 @@ +""" +Adaptor for building prompt_toolkit styles, starting from a Pygments style. + +Usage:: + + from pygments.styles.tango import TangoStyle + style = style_from_pygments_cls(pygments_style_cls=TangoStyle) +""" +from typing import TYPE_CHECKING, Dict, Type + +from .style import Style + +if TYPE_CHECKING: + from pygments.style import Style as PygmentsStyle + from pygments.token import Token + + +__all__ = [ + "style_from_pygments_cls", + "style_from_pygments_dict", + "pygments_token_to_classname", +] + + +def style_from_pygments_cls(pygments_style_cls: Type["PygmentsStyle"]) -> Style: + """ + Shortcut to create a :class:`.Style` instance from a Pygments style class + and a style dictionary. + + Example:: + + from prompt_toolkit.styles.from_pygments import style_from_pygments_cls + from pygments.styles import get_style_by_name + style = style_from_pygments_cls(get_style_by_name('monokai')) + + :param pygments_style_cls: Pygments style class to start from. + """ + # Import inline. + from pygments.style import Style as PygmentsStyle + + assert issubclass(pygments_style_cls, PygmentsStyle) + + return style_from_pygments_dict(pygments_style_cls.styles) + + +def style_from_pygments_dict(pygments_dict: Dict["Token", str]) -> Style: + """ + Create a :class:`.Style` instance from a Pygments style dictionary. + (One that maps Token objects to style strings.) + """ + pygments_style = [] + + for token, style in pygments_dict.items(): + pygments_style.append((pygments_token_to_classname(token), style)) + + return Style(pygments_style) + + +def pygments_token_to_classname(token: "Token") -> str: + """ + Turn e.g. `Token.Name.Exception` into `'pygments.name.exception'`. + + (Our Pygments lexer will also turn the tokens that pygments produces in a + prompt_toolkit list of fragments that match these styling rules.) + """ + parts = ("pygments",) + token + return ".".join(parts).lower() diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/style.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/style.py new file mode 100644 index 00000000..775f43f3 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/style.py @@ -0,0 +1,398 @@ +""" +Tool for creating styles from a dictionary. +""" +import itertools +import re +import sys +from enum import Enum +from typing import Dict, Hashable, List, Set, Tuple, TypeVar + +from prompt_toolkit.cache import SimpleCache + +from .base import ( + ANSI_COLOR_NAMES, + ANSI_COLOR_NAMES_ALIASES, + DEFAULT_ATTRS, + Attrs, + BaseStyle, +) +from .named_colors import NAMED_COLORS + +__all__ = [ + "Style", + "parse_color", + "Priority", + "merge_styles", +] + +_named_colors_lowercase = {k.lower(): v.lstrip("#") for k, v in NAMED_COLORS.items()} + + +def parse_color(text: str) -> str: + """ + Parse/validate color format. + + Like in Pygments, but also support the ANSI color names. + (These will map to the colors of the 16 color palette.) + """ + # ANSI color names. + if text in ANSI_COLOR_NAMES: + return text + if text in ANSI_COLOR_NAMES_ALIASES: + return ANSI_COLOR_NAMES_ALIASES[text] + + # 140 named colors. + try: + # Replace by 'hex' value. + return _named_colors_lowercase[text.lower()] + except KeyError: + pass + + # Hex codes. + if text[0:1] == "#": + col = text[1:] + + # Keep this for backwards-compatibility (Pygments does it). + # I don't like the '#' prefix for named colors. + if col in ANSI_COLOR_NAMES: + return col + elif col in ANSI_COLOR_NAMES_ALIASES: + return ANSI_COLOR_NAMES_ALIASES[col] + + # 6 digit hex color. + elif len(col) == 6: + return col + + # 3 digit hex color. + elif len(col) == 3: + return col[0] * 2 + col[1] * 2 + col[2] * 2 + + # Default. + elif text in ("", "default"): + return text + + raise ValueError("Wrong color format %r" % text) + + +# Attributes, when they are not filled in by a style. None means that we take +# the value from the parent. +_EMPTY_ATTRS = Attrs( + color=None, + bgcolor=None, + bold=None, + underline=None, + italic=None, + blink=None, + reverse=None, + hidden=None, +) + + +def _expand_classname(classname: str) -> List[str]: + """ + Split a single class name at the `.` operator, and build a list of classes. + + E.g. 'a.b.c' becomes ['a', 'a.b', 'a.b.c'] + """ + result = [] + parts = classname.split(".") + + for i in range(1, len(parts) + 1): + result.append(".".join(parts[:i]).lower()) + + return result + + +def _parse_style_str(style_str: str) -> Attrs: + """ + Take a style string, e.g. 'bg:red #88ff00 class:title' + and return a `Attrs` instance. + """ + # Start from default Attrs. + if "noinherit" in style_str: + attrs = DEFAULT_ATTRS + else: + attrs = _EMPTY_ATTRS + + # Now update with the given attributes. + for part in style_str.split(): + if part == "noinherit": + pass + elif part == "bold": + attrs = attrs._replace(bold=True) + elif part == "nobold": + attrs = attrs._replace(bold=False) + elif part == "italic": + attrs = attrs._replace(italic=True) + elif part == "noitalic": + attrs = attrs._replace(italic=False) + elif part == "underline": + attrs = attrs._replace(underline=True) + elif part == "nounderline": + attrs = attrs._replace(underline=False) + + # prompt_toolkit extensions. Not in Pygments. + elif part == "blink": + attrs = attrs._replace(blink=True) + elif part == "noblink": + attrs = attrs._replace(blink=False) + elif part == "reverse": + attrs = attrs._replace(reverse=True) + elif part == "noreverse": + attrs = attrs._replace(reverse=False) + elif part == "hidden": + attrs = attrs._replace(hidden=True) + elif part == "nohidden": + attrs = attrs._replace(hidden=False) + + # Pygments properties that we ignore. + elif part in ("roman", "sans", "mono"): + pass + elif part.startswith("border:"): + pass + + # Ignore pieces in between square brackets. This is internal stuff. + # Like '[transparent]' or '[set-cursor-position]'. + elif part.startswith("[") and part.endswith("]"): + pass + + # Colors. + elif part.startswith("bg:"): + attrs = attrs._replace(bgcolor=parse_color(part[3:])) + elif part.startswith("fg:"): # The 'fg:' prefix is optional. + attrs = attrs._replace(color=parse_color(part[3:])) + else: + attrs = attrs._replace(color=parse_color(part)) + + return attrs + + +CLASS_NAMES_RE = re.compile(r"^[a-z0-9.\s_-]*$") # This one can't contain a comma! + + +class Priority(Enum): + """ + The priority of the rules, when a style is created from a dictionary. + + In a `Style`, rules that are defined later will always override previous + defined rules, however in a dictionary, the key order was arbitrary before + Python 3.6. This means that the style could change at random between rules. + + We have two options: + + - `DICT_KEY_ORDER`: This means, iterate through the dictionary, and take + the key/value pairs in order as they come. This is a good option if you + have Python >3.6. Rules at the end will override rules at the beginning. + - `MOST_PRECISE`: keys that are defined with most precision will get higher + priority. (More precise means: more elements.) + """ + + DICT_KEY_ORDER = "KEY_ORDER" + MOST_PRECISE = "MOST_PRECISE" + + +# In the latest python verions, we take the dictionary ordering like it is, +# In older versions, we sort by by precision. If you need to write code that +# runs on all Python versions, it's best to sort them manually, with the most +# precise rules at the bottom. +if sys.version_info >= (3, 6): + default_priority = Priority.DICT_KEY_ORDER +else: + default_priority = Priority.MOST_PRECISE + + +class Style(BaseStyle): + """ + Create a ``Style`` instance from a list of style rules. + + The `style_rules` is supposed to be a list of ('classnames', 'style') tuples. + The classnames are a whitespace separated string of class names and the + style string is just like a Pygments style definition, but with a few + additions: it supports 'reverse' and 'blink'. + + Later rules always override previous rules. + + Usage:: + + Style([ + ('title', '#ff0000 bold underline'), + ('something-else', 'reverse'), + ('class1 class2', 'reverse'), + ]) + + The ``from_dict`` classmethod is similar, but takes a dictionary as input. + """ + + def __init__(self, style_rules: List[Tuple[str, str]]) -> None: + class_names_and_attrs = [] + + # Loop through the rules in the order they were defined. + # Rules that are defined later get priority. + for class_names, style_str in style_rules: + assert CLASS_NAMES_RE.match(class_names), repr(class_names) + + # The order of the class names doesn't matter. + # (But the order of rules does matter.) + class_names_set = frozenset(class_names.lower().split()) + attrs = _parse_style_str(style_str) + + class_names_and_attrs.append((class_names_set, attrs)) + + self._style_rules = style_rules + self.class_names_and_attrs = class_names_and_attrs + + @property + def style_rules(self) -> List[Tuple[str, str]]: + return self._style_rules + + @classmethod + def from_dict( + cls, style_dict: Dict[str, str], priority: Priority = default_priority + ) -> "Style": + """ + :param style_dict: Style dictionary. + :param priority: `Priority` value. + """ + if priority == Priority.MOST_PRECISE: + + def key(item: Tuple[str, str]) -> int: + # Split on '.' and whitespace. Count elements. + return sum(len(i.split(".")) for i in item[0].split()) + + return cls(sorted(style_dict.items(), key=key)) + else: + return cls(list(style_dict.items())) + + def get_attrs_for_style_str( + self, style_str: str, default: Attrs = DEFAULT_ATTRS + ) -> Attrs: + """ + Get `Attrs` for the given style string. + """ + list_of_attrs = [default] + class_names: Set[str] = set() + + # Apply default styling. + for names, attr in self.class_names_and_attrs: + if not names: + list_of_attrs.append(attr) + + # Go from left to right through the style string. Things on the right + # take precedence. + for part in style_str.split(): + # This part represents a class. + # Do lookup of this class name in the style definition, as well + # as all class combinations that we have so far. + if part.startswith("class:"): + # Expand all class names (comma separated list). + new_class_names = [] + for p in part[6:].lower().split(","): + new_class_names.extend(_expand_classname(p)) + + for new_name in new_class_names: + # Build a set of all possible class combinations to be applied. + combos = set() + combos.add(frozenset([new_name])) + + for count in range(1, len(class_names) + 1): + for c2 in itertools.combinations(class_names, count): + combos.add(frozenset(c2 + (new_name,))) + + # Apply the styles that match these class names. + for names, attr in self.class_names_and_attrs: + if names in combos: + list_of_attrs.append(attr) + + class_names.add(new_name) + + # Process inline style. + else: + inline_attrs = _parse_style_str(part) + list_of_attrs.append(inline_attrs) + + return _merge_attrs(list_of_attrs) + + def invalidation_hash(self) -> Hashable: + return id(self.class_names_and_attrs) + + +_T = TypeVar("_T") + + +def _merge_attrs(list_of_attrs: List[Attrs]) -> Attrs: + """ + Take a list of :class:`.Attrs` instances and merge them into one. + Every `Attr` in the list can override the styling of the previous one. So, + the last one has highest priority. + """ + + def _or(*values: _T) -> _T: + " Take first not-None value, starting at the end. " + for v in values[::-1]: + if v is not None: + return v + raise ValueError # Should not happen, there's always one non-null value. + + return Attrs( + color=_or("", *[a.color for a in list_of_attrs]), + bgcolor=_or("", *[a.bgcolor for a in list_of_attrs]), + bold=_or(False, *[a.bold for a in list_of_attrs]), + underline=_or(False, *[a.underline for a in list_of_attrs]), + italic=_or(False, *[a.italic for a in list_of_attrs]), + blink=_or(False, *[a.blink for a in list_of_attrs]), + reverse=_or(False, *[a.reverse for a in list_of_attrs]), + hidden=_or(False, *[a.hidden for a in list_of_attrs]), + ) + + +def merge_styles(styles: List[BaseStyle]) -> "_MergedStyle": + """ + Merge multiple `Style` objects. + """ + styles = [s for s in styles if s is not None] + return _MergedStyle(styles) + + +class _MergedStyle(BaseStyle): + """ + Merge multiple `Style` objects into one. + This is supposed to ensure consistency: if any of the given styles changes, + then this style will be updated. + """ + + # NOTE: previously, we used an algorithm where we did not generate the + # combined style. Instead this was a proxy that called one style + # after the other, passing the outcome of the previous style as the + # default for the next one. This did not work, because that way, the + # priorities like described in the `Style` class don't work. + # 'class:aborted' was for instance never displayed in gray, because + # the next style specified a default color for any text. (The + # explicit styling of class:aborted should have taken priority, + # because it was more precise.) + def __init__(self, styles: List[BaseStyle]) -> None: + self.styles = styles + self._style: SimpleCache[Hashable, Style] = SimpleCache(maxsize=1) + + @property + def _merged_style(self) -> Style: + " The `Style` object that has the other styles merged together. " + + def get() -> Style: + return Style(self.style_rules) + + return self._style.get(self.invalidation_hash(), get) + + @property + def style_rules(self) -> List[Tuple[str, str]]: + style_rules = [] + for s in self.styles: + style_rules.extend(s.style_rules) + return style_rules + + def get_attrs_for_style_str( + self, style_str: str, default: Attrs = DEFAULT_ATTRS + ) -> Attrs: + return self._merged_style.get_attrs_for_style_str(style_str, default) + + def invalidation_hash(self) -> Hashable: + return tuple(s.invalidation_hash() for s in self.styles) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/style_transformation.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/style_transformation.py new file mode 100644 index 00000000..15b858aa --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/styles/style_transformation.py @@ -0,0 +1,375 @@ +""" +Collection of style transformations. + +Think of it as a kind of color post processing after the rendering is done. +This could be used for instance to change the contrast/saturation; swap light +and dark colors or even change certain colors for other colors. + +When the UI is rendered, these transformations can be applied right after the +style strings are turned into `Attrs` objects that represent the actual +formatting. +""" +from abc import ABCMeta, abstractmethod +from colorsys import hls_to_rgb, rgb_to_hls +from typing import Callable, Hashable, Optional, Sequence, Tuple, Union + +from prompt_toolkit.cache import memoized +from prompt_toolkit.filters import FilterOrBool, to_filter +from prompt_toolkit.utils import AnyFloat, to_float, to_str + +from .base import ANSI_COLOR_NAMES, Attrs +from .style import parse_color + +__all__ = [ + "StyleTransformation", + "SwapLightAndDarkStyleTransformation", + "ReverseStyleTransformation", + "SetDefaultColorStyleTransformation", + "AdjustBrightnessStyleTransformation", + "DummyStyleTransformation", + "ConditionalStyleTransformation", + "DynamicStyleTransformation", + "merge_style_transformations", +] + + +class StyleTransformation(metaclass=ABCMeta): + """ + Base class for any style transformation. + """ + + @abstractmethod + def transform_attrs(self, attrs: Attrs) -> Attrs: + """ + Take an `Attrs` object and return a new `Attrs` object. + + Remember that the color formats can be either "ansi..." or a 6 digit + lowercase hexadecimal color (without '#' prefix). + """ + + def invalidation_hash(self) -> Hashable: + """ + When this changes, the cache should be invalidated. + """ + return "%s-%s" % (self.__class__.__name__, id(self)) + + +class SwapLightAndDarkStyleTransformation(StyleTransformation): + """ + Turn dark colors into light colors and the other way around. + + This is meant to make color schemes that work on a dark background usable + on a light background (and the other way around). + + Notice that this doesn't swap foreground and background like "reverse" + does. It turns light green into dark green and the other way around. + Foreground and background colors are considered individually. + + Also notice that when is used somewhere and no colors are given + in particular (like what is the default for the bottom toolbar), then this + doesn't change anything. This is what makes sense, because when the + 'default' color is chosen, it's what works best for the terminal, and + reverse works good with that. + """ + + def transform_attrs(self, attrs: Attrs) -> Attrs: + """ + Return the `Attrs` used when opposite luminosity should be used. + """ + # Reverse colors. + attrs = attrs._replace(color=get_opposite_color(attrs.color)) + attrs = attrs._replace(bgcolor=get_opposite_color(attrs.bgcolor)) + + return attrs + + +class ReverseStyleTransformation(StyleTransformation): + """ + Swap the 'reverse' attribute. + + (This is still experimental.) + """ + + def transform_attrs(self, attrs: Attrs) -> Attrs: + return attrs._replace(reverse=not attrs.reverse) + + +class SetDefaultColorStyleTransformation(StyleTransformation): + """ + Set default foreground/background color for output that doesn't specify + anything. This is useful for overriding the terminal default colors. + + :param fg: Color string or callable that returns a color string for the + foreground. + :param bg: Like `fg`, but for the background. + """ + + def __init__( + self, fg: Union[str, Callable[[], str]], bg: Union[str, Callable[[], str]] + ) -> None: + + self.fg = fg + self.bg = bg + + def transform_attrs(self, attrs: Attrs) -> Attrs: + if attrs.bgcolor in ("", "default"): + attrs = attrs._replace(bgcolor=parse_color(to_str(self.bg))) + + if attrs.color in ("", "default"): + attrs = attrs._replace(color=parse_color(to_str(self.fg))) + + return attrs + + def invalidation_hash(self) -> Hashable: + return ( + "set-default-color", + to_str(self.fg), + to_str(self.bg), + ) + + +class AdjustBrightnessStyleTransformation(StyleTransformation): + """ + Adjust the brightness to improve the rendering on either dark or light + backgrounds. + + For dark backgrounds, it's best to increase `min_brightness`. For light + backgrounds it's best to decrease `max_brightness`. Usually, only one + setting is adjusted. + + This will only change the brightness for text that has a foreground color + defined, but no background color. It works best for 256 or true color + output. + + .. note:: Notice that there is no universal way to detect whether the + application is running in a light or dark terminal. As a + developer of an command line application, you'll have to make + this configurable for the user. + + :param min_brightness: Float between 0.0 and 1.0 or a callable that returns + a float. + :param max_brightness: Float between 0.0 and 1.0 or a callable that returns + a float. + """ + + def __init__( + self, min_brightness: AnyFloat = 0.0, max_brightness: AnyFloat = 1.0 + ) -> None: + + self.min_brightness = min_brightness + self.max_brightness = max_brightness + + def transform_attrs(self, attrs: Attrs) -> Attrs: + min_brightness = to_float(self.min_brightness) + max_brightness = to_float(self.max_brightness) + assert 0 <= min_brightness <= 1 + assert 0 <= max_brightness <= 1 + + # Don't do anything if the whole brightness range is acceptable. + # This also avoids turning ansi colors into RGB sequences. + if min_brightness == 0.0 and max_brightness == 1.0: + return attrs + + # If a foreground color is given without a background color. + no_background = not attrs.bgcolor or attrs.bgcolor == "default" + has_fgcolor = attrs.color and attrs.color != "ansidefault" + + if has_fgcolor and no_background: + # Calculate new RGB values. + r, g, b = self._color_to_rgb(attrs.color or "") + hue, brightness, saturation = rgb_to_hls(r, g, b) + brightness = self._interpolate_brightness( + brightness, min_brightness, max_brightness + ) + r, g, b = hls_to_rgb(hue, brightness, saturation) + new_color = "%02x%02x%02x" % (int(r * 255), int(g * 255), int(b * 255)) + + attrs = attrs._replace(color=new_color) + + return attrs + + def _color_to_rgb(self, color: str) -> Tuple[float, float, float]: + """ + Parse `style.Attrs` color into RGB tuple. + """ + # Do RGB lookup for ANSI colors. + try: + from prompt_toolkit.output.vt100 import ANSI_COLORS_TO_RGB + + r, g, b = ANSI_COLORS_TO_RGB[color] + return r / 255.0, g / 255.0, b / 255.0 + except KeyError: + pass + + # Parse RRGGBB format. + return ( + int(color[0:2], 16) / 255.0, + int(color[2:4], 16) / 255.0, + int(color[4:6], 16) / 255.0, + ) + + # NOTE: we don't have to support named colors here. They are already + # transformed into RGB values in `style.parse_color`. + + def _interpolate_brightness( + self, value: float, min_brightness: float, max_brightness: float + ) -> float: + """ + Map the brightness to the (min_brightness..max_brightness) range. + """ + return min_brightness + (max_brightness - min_brightness) * value + + def invalidation_hash(self) -> Hashable: + return ( + "adjust-brightness", + to_float(self.min_brightness), + to_float(self.max_brightness), + ) + + +class DummyStyleTransformation(StyleTransformation): + """ + Don't transform anything at all. + """ + + def transform_attrs(self, attrs: Attrs) -> Attrs: + return attrs + + def invalidation_hash(self) -> Hashable: + # Always return the same hash for these dummy instances. + return "dummy-style-transformation" + + +class DynamicStyleTransformation(StyleTransformation): + """ + StyleTransformation class that can dynamically returns any + `StyleTransformation`. + + :param get_style_transformation: Callable that returns a + :class:`.StyleTransformation` instance. + """ + + def __init__( + self, get_style_transformation: Callable[[], Optional[StyleTransformation]] + ) -> None: + + self.get_style_transformation = get_style_transformation + + def transform_attrs(self, attrs: Attrs) -> Attrs: + style_transformation = ( + self.get_style_transformation() or DummyStyleTransformation() + ) + return style_transformation.transform_attrs(attrs) + + def invalidation_hash(self) -> Hashable: + style_transformation = ( + self.get_style_transformation() or DummyStyleTransformation() + ) + return style_transformation.invalidation_hash() + + +class ConditionalStyleTransformation(StyleTransformation): + """ + Apply the style transformation depending on a condition. + """ + + def __init__( + self, style_transformation: StyleTransformation, filter: FilterOrBool + ) -> None: + + self.style_transformation = style_transformation + self.filter = to_filter(filter) + + def transform_attrs(self, attrs: Attrs) -> Attrs: + if self.filter(): + return self.style_transformation.transform_attrs(attrs) + return attrs + + def invalidation_hash(self) -> Hashable: + return (self.filter(), self.style_transformation.invalidation_hash()) + + +class _MergedStyleTransformation(StyleTransformation): + def __init__(self, style_transformations: Sequence[StyleTransformation]) -> None: + self.style_transformations = style_transformations + + def transform_attrs(self, attrs: Attrs) -> Attrs: + for transformation in self.style_transformations: + attrs = transformation.transform_attrs(attrs) + return attrs + + def invalidation_hash(self) -> Hashable: + return tuple(t.invalidation_hash() for t in self.style_transformations) + + +def merge_style_transformations( + style_transformations: Sequence[StyleTransformation], +) -> StyleTransformation: + """ + Merge multiple transformations together. + """ + return _MergedStyleTransformation(style_transformations) + + +# Dictionary that maps ANSI color names to their opposite. This is useful for +# turning color schemes that are optimized for a black background usable for a +# white background. +OPPOSITE_ANSI_COLOR_NAMES = { + "ansidefault": "ansidefault", + "ansiblack": "ansiwhite", + "ansired": "ansibrightred", + "ansigreen": "ansibrightgreen", + "ansiyellow": "ansibrightyellow", + "ansiblue": "ansibrightblue", + "ansimagenta": "ansibrightmagenta", + "ansicyan": "ansibrightcyan", + "ansigray": "ansibrightblack", + "ansiwhite": "ansiblack", + "ansibrightred": "ansired", + "ansibrightgreen": "ansigreen", + "ansibrightyellow": "ansiyellow", + "ansibrightblue": "ansiblue", + "ansibrightmagenta": "ansimagenta", + "ansibrightcyan": "ansicyan", + "ansibrightblack": "ansigray", +} +assert set(OPPOSITE_ANSI_COLOR_NAMES.keys()) == set(ANSI_COLOR_NAMES) +assert set(OPPOSITE_ANSI_COLOR_NAMES.values()) == set(ANSI_COLOR_NAMES) + + +@memoized() +def get_opposite_color(colorname: Optional[str]) -> Optional[str]: + """ + Take a color name in either 'ansi...' format or 6 digit RGB, return the + color of opposite luminosity (same hue/saturation). + + This is used for turning color schemes that work on a light background + usable on a dark background. + """ + if colorname is None: # Because color/bgcolor can be None in `Attrs`. + return None + + # Special values. + if colorname in ("", "default"): + return colorname + + # Try ANSI color names. + try: + return OPPOSITE_ANSI_COLOR_NAMES[colorname] + except KeyError: + # Try 6 digit RGB colors. + r = int(colorname[:2], 16) / 255.0 + g = int(colorname[2:4], 16) / 255.0 + b = int(colorname[4:6], 16) / 255.0 + + h, l, s = rgb_to_hls(r, g, b) + + l = 1 - l + + r, g, b = hls_to_rgb(h, l, s) + + r = int(r * 255) + g = int(g * 255) + b = int(b * 255) + + return "%02x%02x%02x" % (r, g, b) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/token.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/token.py new file mode 100644 index 00000000..76fd9c46 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/token.py @@ -0,0 +1,8 @@ +""" +""" + +__all__ = [ + "ZeroWidthEscape", +] + +ZeroWidthEscape = "[ZeroWidthEscape]" diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/utils.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/utils.py new file mode 100644 index 00000000..3cc9890c --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/utils.py @@ -0,0 +1,312 @@ +import os +import signal +import sys +import threading +from collections import deque +from typing import ( + Callable, + ContextManager, + Deque, + Dict, + Generator, + Generic, + List, + Optional, + TypeVar, + Union, +) + +from wcwidth import wcwidth + +__all__ = [ + "Event", + "DummyContext", + "get_cwidth", + "suspend_to_background_supported", + "is_conemu_ansi", + "is_windows", + "in_main_thread", + "get_term_environment_variable", + "take_using_weights", + "to_str", + "to_int", + "AnyFloat", + "to_float", + "is_dumb_terminal", +] + +_Sender = TypeVar("_Sender", covariant=True) + + +class Event(Generic[_Sender]): + """ + Simple event to which event handlers can be attached. For instance:: + + class Cls: + def __init__(self): + # Define event. The first parameter is the sender. + self.event = Event(self) + + obj = Cls() + + def handler(sender): + pass + + # Add event handler by using the += operator. + obj.event += handler + + # Fire event. + obj.event() + """ + + def __init__( + self, sender: _Sender, handler: Optional[Callable[[_Sender], None]] = None + ): + self.sender = sender + self._handlers: List[Callable[[_Sender], None]] = [] + + if handler is not None: + self += handler + + def __call__(self) -> None: + " Fire event. " + for handler in self._handlers: + handler(self.sender) + + def fire(self) -> None: + " Alias for just calling the event. " + self() + + def add_handler(self, handler: Callable[[_Sender], None]) -> None: + """ + Add another handler to this callback. + (Handler should be a callable that takes exactly one parameter: the + sender object.) + """ + # Add to list of event handlers. + self._handlers.append(handler) + + def remove_handler(self, handler: Callable[[_Sender], None]) -> None: + """ + Remove a handler from this callback. + """ + if handler in self._handlers: + self._handlers.remove(handler) + + def __iadd__(self, handler: Callable[[_Sender], None]) -> "Event[_Sender]": + """ + `event += handler` notation for adding a handler. + """ + self.add_handler(handler) + return self + + def __isub__(self, handler: Callable[[_Sender], None]) -> "Event[_Sender]": + """ + `event -= handler` notation for removing a handler. + """ + self.remove_handler(handler) + return self + + +class DummyContext(ContextManager[None]): + """ + (contextlib.nested is not available on Py3) + """ + + def __enter__(self) -> None: + pass + + def __exit__(self, *a: object) -> None: + pass + + +class _CharSizesCache(Dict[str, int]): + """ + Cache for wcwidth sizes. + """ + + LONG_STRING_MIN_LEN = 64 # Minimum string length for considering it long. + MAX_LONG_STRINGS = 16 # Maximum number of long strings to remember. + + def __init__(self) -> None: + super().__init__() + # Keep track of the "long" strings in this cache. + self._long_strings: Deque[str] = deque() + + def __missing__(self, string: str) -> int: + # Note: We use the `max(0, ...` because some non printable control + # characters, like e.g. Ctrl-underscore get a -1 wcwidth value. + # It can be possible that these characters end up in the input + # text. + result: int + if len(string) == 1: + result = max(0, wcwidth(string)) + else: + result = sum(self[c] for c in string) + + # Store in cache. + self[string] = result + + # Rotate long strings. + # (It's hard to tell what we can consider short...) + if len(string) > self.LONG_STRING_MIN_LEN: + long_strings = self._long_strings + long_strings.append(string) + + if len(long_strings) > self.MAX_LONG_STRINGS: + key_to_remove = long_strings.popleft() + if key_to_remove in self: + del self[key_to_remove] + + return result + + +_CHAR_SIZES_CACHE = _CharSizesCache() + + +def get_cwidth(string: str) -> int: + """ + Return width of a string. Wrapper around ``wcwidth``. + """ + return _CHAR_SIZES_CACHE[string] + + +def suspend_to_background_supported() -> bool: + """ + Returns `True` when the Python implementation supports + suspend-to-background. This is typically `False' on Windows systems. + """ + return hasattr(signal, "SIGTSTP") + + +def is_windows() -> bool: + """ + True when we are using Windows. + """ + return sys.platform.startswith("win") # E.g. 'win32', not 'darwin' or 'linux2' + + +def is_windows_vt100_supported() -> bool: + """ + True when we are using Windows, but VT100 escape sequences are supported. + """ + # Import needs to be inline. Windows libraries are not always available. + from prompt_toolkit.output.windows10 import is_win_vt100_enabled + + return is_windows() and is_win_vt100_enabled() + + +def is_conemu_ansi() -> bool: + """ + True when the ConEmu Windows console is used. + """ + return is_windows() and os.environ.get("ConEmuANSI", "OFF") == "ON" + + +def in_main_thread() -> bool: + """ + True when the current thread is the main thread. + """ + return threading.current_thread().__class__.__name__ == "_MainThread" + + +def get_term_environment_variable() -> str: + " Return the $TERM environment variable. " + return os.environ.get("TERM", "") + + +_T = TypeVar("_T") + + +def take_using_weights( + items: List[_T], weights: List[int] +) -> Generator[_T, None, None]: + """ + Generator that keeps yielding items from the items list, in proportion to + their weight. For instance:: + + # Getting the first 70 items from this generator should have yielded 10 + # times A, 20 times B and 40 times C, all distributed equally.. + take_using_weights(['A', 'B', 'C'], [5, 10, 20]) + + :param items: List of items to take from. + :param weights: Integers representing the weight. (Numbers have to be + integers, not floats.) + """ + assert len(items) == len(weights) + assert len(items) > 0 + + # Remove items with zero-weight. + items2 = [] + weights2 = [] + for item, w in zip(items, weights): + if w > 0: + items2.append(item) + weights2.append(w) + + items = items2 + weights = weights2 + + # Make sure that we have some items left. + if not items: + raise ValueError("Did't got any items with a positive weight.") + + # + already_taken = [0 for i in items] + item_count = len(items) + max_weight = max(weights) + + i = 0 + while True: + # Each iteration of this loop, we fill up until by (total_weight/max_weight). + adding = True + while adding: + adding = False + + for item_i, item, weight in zip(range(item_count), items, weights): + if already_taken[item_i] < i * weight / float(max_weight): + yield item + already_taken[item_i] += 1 + adding = True + + i += 1 + + +def to_str(value: Union[Callable[[], str], str]) -> str: + " Turn callable or string into string. " + if callable(value): + return to_str(value()) + else: + return str(value) + + +def to_int(value: Union[Callable[[], int], int]) -> int: + " Turn callable or int into int. " + if callable(value): + return to_int(value()) + else: + return int(value) + + +AnyFloat = Union[Callable[[], float], float] + + +def to_float(value: AnyFloat) -> float: + " Turn callable or float into float. " + if callable(value): + return to_float(value()) + else: + return float(value) + + +def is_dumb_terminal(term: Optional[str] = None) -> bool: + """ + True if this terminal type is considered "dumb". + + If so, we should fall back to the simplest possible form of line editing, + without cursor positioning and color support. + """ + if term is None: + return is_dumb_terminal(os.environ.get("TERM", "")) + + return term.lower() in ["dumb", "unknown"] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/validation.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/validation.py new file mode 100644 index 00000000..db2f3fd1 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/validation.py @@ -0,0 +1,194 @@ +""" +Input validation for a `Buffer`. +(Validators will be called before accepting input.) +""" +from abc import ABCMeta, abstractmethod +from typing import Callable, Optional + +from prompt_toolkit.eventloop import run_in_executor_with_context + +from .document import Document +from .filters import FilterOrBool, to_filter + +__all__ = [ + "ConditionalValidator", + "ValidationError", + "Validator", + "ThreadedValidator", + "DummyValidator", + "DynamicValidator", +] + + +class ValidationError(Exception): + """ + Error raised by :meth:`.Validator.validate`. + + :param cursor_position: The cursor position where the error occurred. + :param message: Text. + """ + + def __init__(self, cursor_position: int = 0, message: str = "") -> None: + super().__init__(message) + self.cursor_position = cursor_position + self.message = message + + def __repr__(self) -> str: + return "%s(cursor_position=%r, message=%r)" % ( + self.__class__.__name__, + self.cursor_position, + self.message, + ) + + +class Validator(metaclass=ABCMeta): + """ + Abstract base class for an input validator. + + A validator is typically created in one of the following two ways: + + - Either by overriding this class and implementing the `validate` method. + - Or by passing a callable to `Validator.from_callable`. + + If the validation takes some time and needs to happen in a background + thread, this can be wrapped in a :class:`.ThreadedValidator`. + """ + + @abstractmethod + def validate(self, document: Document) -> None: + """ + Validate the input. + If invalid, this should raise a :class:`.ValidationError`. + + :param document: :class:`~prompt_toolkit.document.Document` instance. + """ + pass + + async def validate_async(self, document: Document) -> None: + """ + Return a `Future` which is set when the validation is ready. + This function can be overloaded in order to provide an asynchronous + implementation. + """ + try: + self.validate(document) + except ValidationError: + raise + + @classmethod + def from_callable( + cls, + validate_func: Callable[[str], bool], + error_message: str = "Invalid input", + move_cursor_to_end: bool = False, + ) -> "Validator": + """ + Create a validator from a simple validate callable. E.g.: + + .. code:: python + + def is_valid(text): + return text in ['hello', 'world'] + Validator.from_callable(is_valid, error_message='Invalid input') + + :param validate_func: Callable that takes the input string, and returns + `True` if the input is valid input. + :param error_message: Message to be displayed if the input is invalid. + :param move_cursor_to_end: Move the cursor to the end of the input, if + the input is invalid. + """ + return _ValidatorFromCallable(validate_func, error_message, move_cursor_to_end) + + +class _ValidatorFromCallable(Validator): + """ + Validate input from a simple callable. + """ + + def __init__( + self, func: Callable[[str], bool], error_message: str, move_cursor_to_end: bool + ) -> None: + + self.func = func + self.error_message = error_message + self.move_cursor_to_end = move_cursor_to_end + + def __repr__(self) -> str: + return "Validator.from_callable(%r)" % (self.func,) + + def validate(self, document: Document) -> None: + if not self.func(document.text): + if self.move_cursor_to_end: + index = len(document.text) + else: + index = 0 + + raise ValidationError(cursor_position=index, message=self.error_message) + + +class ThreadedValidator(Validator): + """ + Wrapper that runs input validation in a thread. + (Use this to prevent the user interface from becoming unresponsive if the + input validation takes too much time.) + """ + + def __init__(self, validator: Validator) -> None: + self.validator = validator + + def validate(self, document: Document) -> None: + self.validator.validate(document) + + async def validate_async(self, document: Document) -> None: + """ + Run the `validate` function in a thread. + """ + + def run_validation_thread() -> None: + return self.validate(document) + + await run_in_executor_with_context(run_validation_thread) + + +class DummyValidator(Validator): + """ + Validator class that accepts any input. + """ + + def validate(self, document: Document) -> None: + pass # Don't raise any exception. + + +class ConditionalValidator(Validator): + """ + Validator that can be switched on/off according to + a filter. (This wraps around another validator.) + """ + + def __init__(self, validator: Validator, filter: FilterOrBool) -> None: + self.validator = validator + self.filter = to_filter(filter) + + def validate(self, document: Document) -> None: + # Call the validator only if the filter is active. + if self.filter(): + self.validator.validate(document) + + +class DynamicValidator(Validator): + """ + Validator class that can dynamically returns any Validator. + + :param get_validator: Callable that returns a :class:`.Validator` instance. + """ + + def __init__(self, get_validator: Callable[[], Optional[Validator]]) -> None: + self.get_validator = get_validator + + def validate(self, document: Document) -> None: + validator = self.get_validator() or DummyValidator() + validator.validate(document) + + async def validate_async(self, document: Document) -> None: + validator = self.get_validator() or DummyValidator() + await validator.validate_async(document) diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__init__.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__init__.py new file mode 100644 index 00000000..552d3559 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__init__.py @@ -0,0 +1,60 @@ +""" +Collection of reusable components for building full screen applications. +These are higher level abstractions on top of the `prompt_toolkit.layout` +module. + +Most of these widgets implement the ``__pt_container__`` method, which makes it +possible to embed these in the layout like any other container. +""" +from .base import ( + Box, + Button, + Checkbox, + CheckboxList, + Frame, + HorizontalLine, + Label, + ProgressBar, + RadioList, + Shadow, + TextArea, + VerticalLine, +) +from .dialogs import Dialog +from .menus import MenuContainer, MenuItem +from .toolbars import ( + ArgToolbar, + CompletionsToolbar, + FormattedTextToolbar, + SearchToolbar, + SystemToolbar, + ValidationToolbar, +) + +__all__ = [ + # Base. + "TextArea", + "Label", + "Button", + "Frame", + "Shadow", + "Box", + "VerticalLine", + "HorizontalLine", + "CheckboxList", + "RadioList", + "Checkbox", + "ProgressBar", + # Toolbars. + "ArgToolbar", + "CompletionsToolbar", + "FormattedTextToolbar", + "SearchToolbar", + "SystemToolbar", + "ValidationToolbar", + # Dialogs. + "Dialog", + # Menus. + "MenuContainer", + "MenuItem", +] diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..a6891423 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/base.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/base.cpython-38.pyc new file mode 100644 index 00000000..eb4337bf Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/base.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/dialogs.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/dialogs.cpython-38.pyc new file mode 100644 index 00000000..0512adf2 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/dialogs.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/menus.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/menus.cpython-38.pyc new file mode 100644 index 00000000..13e81bd3 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/menus.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/toolbars.cpython-38.pyc b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/toolbars.cpython-38.pyc new file mode 100644 index 00000000..51a849f2 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/__pycache__/toolbars.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/base.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/base.py new file mode 100644 index 00000000..e994b5a7 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/base.py @@ -0,0 +1,916 @@ +""" +Collection of reusable components for building full screen applications. + +All of these widgets implement the ``__pt_container__`` method, which makes +them usable in any situation where we are expecting a `prompt_toolkit` +container object. + +.. warning:: + + At this point, the API for these widgets is considered unstable, and can + potentially change between minor releases (we try not too, but no + guarantees are made yet). The public API in + `prompt_toolkit.shortcuts.dialogs` on the other hand is considered stable. +""" +from functools import partial +from typing import Callable, Generic, List, Optional, Sequence, Tuple, TypeVar, Union + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.auto_suggest import AutoSuggest, DynamicAutoSuggest +from prompt_toolkit.buffer import Buffer, BufferAcceptHandler +from prompt_toolkit.completion import Completer, DynamicCompleter +from prompt_toolkit.document import Document +from prompt_toolkit.filters import ( + Condition, + FilterOrBool, + has_focus, + is_done, + is_true, + to_filter, +) +from prompt_toolkit.formatted_text import ( + AnyFormattedText, + StyleAndTextTuples, + Template, + to_formatted_text, +) +from prompt_toolkit.formatted_text.utils import fragment_list_to_text +from prompt_toolkit.history import History +from prompt_toolkit.key_binding.key_bindings import KeyBindings +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout.containers import ( + AnyContainer, + ConditionalContainer, + Container, + DynamicContainer, + Float, + FloatContainer, + HSplit, + VSplit, + Window, + WindowAlign, +) +from prompt_toolkit.layout.controls import ( + BufferControl, + FormattedTextControl, + GetLinePrefixCallable, +) +from prompt_toolkit.layout.dimension import AnyDimension +from prompt_toolkit.layout.dimension import Dimension as D +from prompt_toolkit.layout.dimension import to_dimension +from prompt_toolkit.layout.margins import ( + ConditionalMargin, + NumberedMargin, + ScrollbarMargin, +) +from prompt_toolkit.layout.processors import ( + AppendAutoSuggestion, + BeforeInput, + ConditionalProcessor, + PasswordProcessor, + Processor, +) +from prompt_toolkit.lexers import DynamicLexer, Lexer +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.utils import get_cwidth +from prompt_toolkit.validation import DynamicValidator, Validator + +from .toolbars import SearchToolbar + +__all__ = [ + "TextArea", + "Label", + "Button", + "Frame", + "Shadow", + "Box", + "VerticalLine", + "HorizontalLine", + "RadioList", + "CheckboxList", + "Checkbox", # backward compatibility + "ProgressBar", +] + +E = KeyPressEvent + + +class Border: + " Box drawing characters. (Thin) " + HORIZONTAL = "\u2500" + VERTICAL = "\u2502" + TOP_LEFT = "\u250c" + TOP_RIGHT = "\u2510" + BOTTOM_LEFT = "\u2514" + BOTTOM_RIGHT = "\u2518" + + +class TextArea: + """ + A simple input field. + + This is a higher level abstraction on top of several other classes with + sane defaults. + + This widget does have the most common options, but it does not intend to + cover every single use case. For more configurations options, you can + always build a text area manually, using a + :class:`~prompt_toolkit.buffer.Buffer`, + :class:`~prompt_toolkit.layout.BufferControl` and + :class:`~prompt_toolkit.layout.Window`. + + Buffer attributes: + + :param text: The initial text. + :param multiline: If True, allow multiline input. + :param completer: :class:`~prompt_toolkit.completion.Completer` instance + for auto completion. + :param complete_while_typing: Boolean. + :param accept_handler: Called when `Enter` is pressed (This should be a + callable that takes a buffer as input). + :param history: :class:`~prompt_toolkit.history.History` instance. + :param auto_suggest: :class:`~prompt_toolkit.auto_suggest.AutoSuggest` + instance for input suggestions. + + BufferControl attributes: + + :param password: When `True`, display using asterisks. + :param focusable: When `True`, allow this widget to receive the focus. + :param focus_on_click: When `True`, focus after mouse click. + :param input_processors: `None` or a list of + :class:`~prompt_toolkit.layout.Processor` objects. + :param validator: `None` or a :class:`~prompt_toolkit.validation.Validator` + object. + + Window attributes: + + :param lexer: :class:`~prompt_toolkit.lexers.Lexer` instance for syntax + highlighting. + :param wrap_lines: When `True`, don't scroll horizontally, but wrap lines. + :param width: Window width. (:class:`~prompt_toolkit.layout.Dimension` object.) + :param height: Window height. (:class:`~prompt_toolkit.layout.Dimension` object.) + :param scrollbar: When `True`, display a scroll bar. + :param style: A style string. + :param dont_extend_width: When `True`, don't take up more width then the + preferred width reported by the control. + :param dont_extend_height: When `True`, don't take up more width then the + preferred height reported by the control. + :param get_line_prefix: None or a callable that returns formatted text to + be inserted before a line. It takes a line number (int) and a + wrap_count and returns formatted text. This can be used for + implementation of line continuations, things like Vim "breakindent" and + so on. + + Other attributes: + + :param search_field: An optional `SearchToolbar` object. + """ + + def __init__( + self, + text: str = "", + multiline: FilterOrBool = True, + password: FilterOrBool = False, + lexer: Optional[Lexer] = None, + auto_suggest: Optional[AutoSuggest] = None, + completer: Optional[Completer] = None, + complete_while_typing: FilterOrBool = True, + validator: Optional[Validator] = None, + accept_handler: Optional[BufferAcceptHandler] = None, + history: Optional[History] = None, + focusable: FilterOrBool = True, + focus_on_click: FilterOrBool = False, + wrap_lines: FilterOrBool = True, + read_only: FilterOrBool = False, + width: AnyDimension = None, + height: AnyDimension = None, + dont_extend_height: FilterOrBool = False, + dont_extend_width: FilterOrBool = False, + line_numbers: bool = False, + get_line_prefix: Optional[GetLinePrefixCallable] = None, + scrollbar: bool = False, + style: str = "", + search_field: Optional[SearchToolbar] = None, + preview_search: FilterOrBool = True, + prompt: AnyFormattedText = "", + input_processors: Optional[List[Processor]] = None, + ) -> None: + + if search_field is None: + search_control = None + elif isinstance(search_field, SearchToolbar): + search_control = search_field.control + + if input_processors is None: + input_processors = [] + + # Writeable attributes. + self.completer = completer + self.complete_while_typing = complete_while_typing + self.lexer = lexer + self.auto_suggest = auto_suggest + self.read_only = read_only + self.wrap_lines = wrap_lines + self.validator = validator + + self.buffer = Buffer( + document=Document(text, 0), + multiline=multiline, + read_only=Condition(lambda: is_true(self.read_only)), + completer=DynamicCompleter(lambda: self.completer), + complete_while_typing=Condition( + lambda: is_true(self.complete_while_typing) + ), + validator=DynamicValidator(lambda: self.validator), + auto_suggest=DynamicAutoSuggest(lambda: self.auto_suggest), + accept_handler=accept_handler, + history=history, + ) + + self.control = BufferControl( + buffer=self.buffer, + lexer=DynamicLexer(lambda: self.lexer), + input_processors=[ + ConditionalProcessor( + AppendAutoSuggestion(), has_focus(self.buffer) & ~is_done + ), + ConditionalProcessor( + processor=PasswordProcessor(), filter=to_filter(password) + ), + BeforeInput(prompt, style="class:text-area.prompt"), + ] + + input_processors, + search_buffer_control=search_control, + preview_search=preview_search, + focusable=focusable, + focus_on_click=focus_on_click, + ) + + if multiline: + if scrollbar: + right_margins = [ScrollbarMargin(display_arrows=True)] + else: + right_margins = [] + if line_numbers: + left_margins = [NumberedMargin()] + else: + left_margins = [] + else: + height = D.exact(1) + left_margins = [] + right_margins = [] + + style = "class:text-area " + style + + self.window = Window( + height=height, + width=width, + dont_extend_height=dont_extend_height, + dont_extend_width=dont_extend_width, + content=self.control, + style=style, + wrap_lines=Condition(lambda: is_true(self.wrap_lines)), + left_margins=left_margins, + right_margins=right_margins, + get_line_prefix=get_line_prefix, + ) + + @property + def text(self) -> str: + """ + The `Buffer` text. + """ + return self.buffer.text + + @text.setter + def text(self, value: str) -> None: + self.document = Document(value, 0) + + @property + def document(self) -> Document: + """ + The `Buffer` document (text + cursor position). + """ + return self.buffer.document + + @document.setter + def document(self, value: Document) -> None: + self.buffer.set_document(value, bypass_readonly=True) + + @property + def accept_handler(self) -> Optional[BufferAcceptHandler]: + """ + The accept handler. Called when the user accepts the input. + """ + return self.buffer.accept_handler + + @accept_handler.setter + def accept_handler(self, value: BufferAcceptHandler) -> None: + self.buffer.accept_handler = value + + def __pt_container__(self) -> Container: + return self.window + + +class Label: + """ + Widget that displays the given text. It is not editable or focusable. + + :param text: The text to be displayed. (This can be multiline. This can be + formatted text as well.) + :param style: A style string. + :param width: When given, use this width, rather than calculating it from + the text size. + """ + + def __init__( + self, + text: AnyFormattedText, + style: str = "", + width: AnyDimension = None, + dont_extend_height: bool = True, + dont_extend_width: bool = False, + ) -> None: + + self.text = text + + def get_width() -> AnyDimension: + if width is None: + text_fragments = to_formatted_text(self.text) + text = fragment_list_to_text(text_fragments) + if text: + longest_line = max(get_cwidth(line) for line in text.splitlines()) + else: + return D(preferred=0) + return D(preferred=longest_line) + else: + return width + + self.formatted_text_control = FormattedTextControl(text=lambda: self.text) + + self.window = Window( + content=self.formatted_text_control, + width=get_width, + style="class:label " + style, + dont_extend_height=dont_extend_height, + dont_extend_width=dont_extend_width, + ) + + def __pt_container__(self) -> Container: + return self.window + + +class Button: + """ + Clickable button. + + :param text: The caption for the button. + :param handler: `None` or callable. Called when the button is clicked. No + parameters are passed to this callable. Use for instance Python's + `functools.partial` to pass parameters to this callable if needed. + :param width: Width of the button. + """ + + def __init__( + self, text: str, handler: Optional[Callable[[], None]] = None, width: int = 12 + ) -> None: + + self.text = text + self.handler = handler + self.width = width + self.control = FormattedTextControl( + self._get_text_fragments, + key_bindings=self._get_key_bindings(), + focusable=True, + ) + + def get_style() -> str: + if get_app().layout.has_focus(self): + return "class:button.focused" + else: + return "class:button" + + self.window = Window( + self.control, + align=WindowAlign.CENTER, + height=1, + width=width, + style=get_style, + dont_extend_width=True, + dont_extend_height=True, + ) + + def _get_text_fragments(self) -> StyleAndTextTuples: + text = ("{:^%s}" % (self.width - 2)).format(self.text) + + def handler(mouse_event: MouseEvent) -> None: + if ( + self.handler is not None + and mouse_event.event_type == MouseEventType.MOUSE_UP + ): + self.handler() + + return [ + ("class:button.arrow", "<", handler), + ("[SetCursorPosition]", ""), + ("class:button.text", text, handler), + ("class:button.arrow", ">", handler), + ] + + def _get_key_bindings(self) -> KeyBindings: + " Key bindings for the Button. " + kb = KeyBindings() + + @kb.add(" ") + @kb.add("enter") + def _(event: E) -> None: + if self.handler is not None: + self.handler() + + return kb + + def __pt_container__(self) -> Container: + return self.window + + +class Frame: + """ + Draw a border around any container, optionally with a title text. + + Changing the title and body of the frame is possible at runtime by + assigning to the `body` and `title` attributes of this class. + + :param body: Another container object. + :param title: Text to be displayed in the top of the frame (can be formatted text). + :param style: Style string to be applied to this widget. + """ + + def __init__( + self, + body: AnyContainer, + title: AnyFormattedText = "", + style: str = "", + width: AnyDimension = None, + height: AnyDimension = None, + key_bindings: Optional[KeyBindings] = None, + modal: bool = False, + ) -> None: + + self.title = title + self.body = body + + fill = partial(Window, style="class:frame.border") + style = "class:frame " + style + + top_row_with_title = VSplit( + [ + fill(width=1, height=1, char=Border.TOP_LEFT), + fill(char=Border.HORIZONTAL), + fill(width=1, height=1, char="|"), + # Notice: we use `Template` here, because `self.title` can be an + # `HTML` object for instance. + Label( + lambda: Template(" {} ").format(self.title), + style="class:frame.label", + dont_extend_width=True, + ), + fill(width=1, height=1, char="|"), + fill(char=Border.HORIZONTAL), + fill(width=1, height=1, char=Border.TOP_RIGHT), + ], + height=1, + ) + + top_row_without_title = VSplit( + [ + fill(width=1, height=1, char=Border.TOP_LEFT), + fill(char=Border.HORIZONTAL), + fill(width=1, height=1, char=Border.TOP_RIGHT), + ], + height=1, + ) + + @Condition + def has_title() -> bool: + return bool(self.title) + + self.container = HSplit( + [ + ConditionalContainer(content=top_row_with_title, filter=has_title), + ConditionalContainer(content=top_row_without_title, filter=~has_title), + VSplit( + [ + fill(width=1, char=Border.VERTICAL), + DynamicContainer(lambda: self.body), + fill(width=1, char=Border.VERTICAL), + # Padding is required to make sure that if the content is + # too small, the right frame border is still aligned. + ], + padding=0, + ), + VSplit( + [ + fill(width=1, height=1, char=Border.BOTTOM_LEFT), + fill(char=Border.HORIZONTAL), + fill(width=1, height=1, char=Border.BOTTOM_RIGHT), + ] + ), + ], + width=width, + height=height, + style=style, + key_bindings=key_bindings, + modal=modal, + ) + + def __pt_container__(self) -> Container: + return self.container + + +class Shadow: + """ + Draw a shadow underneath/behind this container. + (This applies `class:shadow` the the cells under the shadow. The Style + should define the colors for the shadow.) + + :param body: Another container object. + """ + + def __init__(self, body: AnyContainer) -> None: + self.container = FloatContainer( + content=body, + floats=[ + Float( + bottom=-1, + height=1, + left=1, + right=-1, + transparent=True, + content=Window(style="class:shadow"), + ), + Float( + bottom=-1, + top=1, + width=1, + right=-1, + transparent=True, + content=Window(style="class:shadow"), + ), + ], + ) + + def __pt_container__(self) -> Container: + return self.container + + +class Box: + """ + Add padding around a container. + + This also makes sure that the parent can provide more space than required by + the child. This is very useful when wrapping a small element with a fixed + size into a ``VSplit`` or ``HSplit`` object. The ``HSplit`` and ``VSplit`` + try to make sure to adapt respectively the width and height, possibly + shrinking other elements. Wrapping something in a ``Box`` makes it flexible. + + :param body: Another container object. + :param padding: The margin to be used around the body. This can be + overridden by `padding_left`, padding_right`, `padding_top` and + `padding_bottom`. + :param style: A style string. + :param char: Character to be used for filling the space around the body. + (This is supposed to be a character with a terminal width of 1.) + """ + + def __init__( + self, + body: AnyContainer, + padding: AnyDimension = None, + padding_left: AnyDimension = None, + padding_right: AnyDimension = None, + padding_top: AnyDimension = None, + padding_bottom: AnyDimension = None, + width: AnyDimension = None, + height: AnyDimension = None, + style: str = "", + char: Union[None, str, Callable[[], str]] = None, + modal: bool = False, + key_bindings: Optional[KeyBindings] = None, + ) -> None: + + if padding is None: + padding = D(preferred=0) + + def get(value: AnyDimension) -> D: + if value is None: + value = padding + return to_dimension(value) + + self.padding_left = get(padding_left) + self.padding_right = get(padding_right) + self.padding_top = get(padding_top) + self.padding_bottom = get(padding_bottom) + self.body = body + + self.container = HSplit( + [ + Window(height=self.padding_top, char=char), + VSplit( + [ + Window(width=self.padding_left, char=char), + body, + Window(width=self.padding_right, char=char), + ] + ), + Window(height=self.padding_bottom, char=char), + ], + width=width, + height=height, + style=style, + modal=modal, + key_bindings=None, + ) + + def __pt_container__(self) -> Container: + return self.container + + +_T = TypeVar("_T") + + +class _DialogList(Generic[_T]): + """ + Common code for `RadioList` and `CheckboxList`. + """ + + open_character: str = "" + close_character: str = "" + container_style: str = "" + default_style: str = "" + selected_style: str = "" + checked_style: str = "" + multiple_selection: bool = False + show_scrollbar: bool = True + + def __init__(self, values: Sequence[Tuple[_T, AnyFormattedText]]) -> None: + assert len(values) > 0 + + self.values = values + # current_values will be used in multiple_selection, + # current_value will be used otherwise. + self.current_values: List[_T] = [] + self.current_value: _T = values[0][0] + self._selected_index = 0 + + # Key bindings. + kb = KeyBindings() + + @kb.add("up") + def _up(event: E) -> None: + self._selected_index = max(0, self._selected_index - 1) + + @kb.add("down") + def _down(event: E) -> None: + self._selected_index = min(len(self.values) - 1, self._selected_index + 1) + + @kb.add("pageup") + def _pageup(event: E) -> None: + w = event.app.layout.current_window + if w.render_info: + self._selected_index = max( + 0, self._selected_index - len(w.render_info.displayed_lines) + ) + + @kb.add("pagedown") + def _pagedown(event: E) -> None: + w = event.app.layout.current_window + if w.render_info: + self._selected_index = min( + len(self.values) - 1, + self._selected_index + len(w.render_info.displayed_lines), + ) + + @kb.add("enter") + @kb.add(" ") + def _click(event: E) -> None: + self._handle_enter() + + @kb.add(Keys.Any) + def _find(event: E) -> None: + # We first check values after the selected value, then all values. + values = list(self.values) + for value in values[self._selected_index + 1 :] + values: + text = fragment_list_to_text(to_formatted_text(value[1])).lower() + + if text.startswith(event.data.lower()): + self._selected_index = self.values.index(value) + return + + # Control and window. + self.control = FormattedTextControl( + self._get_text_fragments, key_bindings=kb, focusable=True + ) + + self.window = Window( + content=self.control, + style=self.container_style, + right_margins=[ + ConditionalMargin( + margin=ScrollbarMargin(display_arrows=True), + filter=Condition(lambda: self.show_scrollbar), + ), + ], + dont_extend_height=True, + ) + + def _handle_enter(self) -> None: + if self.multiple_selection: + val = self.values[self._selected_index][0] + if val in self.current_values: + self.current_values.remove(val) + else: + self.current_values.append(val) + else: + self.current_value = self.values[self._selected_index][0] + + def _get_text_fragments(self) -> StyleAndTextTuples: + def mouse_handler(mouse_event: MouseEvent) -> None: + """ + Set `_selected_index` and `current_value` according to the y + position of the mouse click event. + """ + if mouse_event.event_type == MouseEventType.MOUSE_UP: + self._selected_index = mouse_event.position.y + self._handle_enter() + + result: StyleAndTextTuples = [] + for i, value in enumerate(self.values): + if self.multiple_selection: + checked = value[0] in self.current_values + else: + checked = value[0] == self.current_value + selected = i == self._selected_index + + style = "" + if checked: + style += " " + self.checked_style + if selected: + style += " " + self.selected_style + + result.append((style, self.open_character)) + + if selected: + result.append(("[SetCursorPosition]", "")) + + if checked: + result.append((style, "*")) + else: + result.append((style, " ")) + + result.append((style, self.close_character)) + result.append((self.default_style, " ")) + result.extend(to_formatted_text(value[1], style=self.default_style)) + result.append(("", "\n")) + + # Add mouse handler to all fragments. + for i in range(len(result)): + result[i] = (result[i][0], result[i][1], mouse_handler) + + result.pop() # Remove last newline. + return result + + def __pt_container__(self) -> Container: + return self.window + + +class RadioList(_DialogList[_T]): + """ + List of radio buttons. Only one can be checked at the same time. + + :param values: List of (value, label) tuples. + """ + + open_character = "(" + close_character = ")" + container_style = "class:radio-list" + default_style = "class:radio" + selected_style = "class:radio-selected" + checked_style = "class:radio-checked" + multiple_selection = False + + +class CheckboxList(_DialogList[_T]): + """ + List of checkbox buttons. Several can be checked at the same time. + + :param values: List of (value, label) tuples. + """ + + open_character = "[" + close_character = "]" + container_style = "class:checkbox-list" + default_style = "class:checkbox" + selected_style = "class:checkbox-selected" + checked_style = "class:checkbox-checked" + multiple_selection = True + + +class Checkbox(CheckboxList[str]): + """Backward compatibility util: creates a 1-sized CheckboxList + + :param text: the text + """ + + show_scrollbar = False + + def __init__(self, text: AnyFormattedText = "", checked: bool = False) -> None: + values = [("value", text)] + CheckboxList.__init__(self, values) + self.checked = checked + + @property + def checked(self) -> bool: + return "value" in self.current_values + + @checked.setter + def checked(self, value: bool) -> None: + if value: + self.current_values = ["value"] + else: + self.current_values = [] + + +class VerticalLine(object): + """ + A simple vertical line with a width of 1. + """ + + def __init__(self) -> None: + self.window = Window( + char=Border.VERTICAL, style="class:line,vertical-line", width=1 + ) + + def __pt_container__(self) -> Container: + return self.window + + +class HorizontalLine: + """ + A simple horizontal line with a height of 1. + """ + + def __init__(self) -> None: + self.window = Window( + char=Border.HORIZONTAL, style="class:line,horizontal-line", height=1 + ) + + def __pt_container__(self) -> Container: + return self.window + + +class ProgressBar: + def __init__(self) -> None: + self._percentage = 60 + + self.label = Label("60%") + self.container = FloatContainer( + content=Window(height=1), + floats=[ + # We first draw the label, then the actual progress bar. Right + # now, this is the only way to have the colors of the progress + # bar appear on top of the label. The problem is that our label + # can't be part of any `Window` below. + Float(content=self.label, top=0, bottom=0), + Float( + left=0, + top=0, + right=0, + bottom=0, + content=VSplit( + [ + Window( + style="class:progress-bar.used", + width=lambda: D(weight=int(self._percentage)), + ), + Window( + style="class:progress-bar", + width=lambda: D(weight=int(100 - self._percentage)), + ), + ] + ), + ), + ], + ) + + @property + def percentage(self) -> int: + return self._percentage + + @percentage.setter + def percentage(self, value: int) -> None: + self._percentage = value + self.label.text = "{0}%".format(value) + + def __pt_container__(self) -> Container: + return self.container diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/dialogs.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/dialogs.py new file mode 100644 index 00000000..920582b4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/dialogs.py @@ -0,0 +1,106 @@ +""" +Collection of reusable components for building full screen applications. +""" +from typing import Optional, Sequence, Union + +from prompt_toolkit.filters import has_completions, has_focus +from prompt_toolkit.formatted_text import AnyFormattedText +from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous +from prompt_toolkit.key_binding.key_bindings import KeyBindings +from prompt_toolkit.layout.containers import ( + AnyContainer, + DynamicContainer, + HSplit, + VSplit, +) +from prompt_toolkit.layout.dimension import AnyDimension +from prompt_toolkit.layout.dimension import Dimension as D + +from .base import Box, Button, Frame, Shadow + +__all__ = [ + "Dialog", +] + + +class Dialog: + """ + Simple dialog window. This is the base for input dialogs, message dialogs + and confirmation dialogs. + + Changing the title and body of the dialog is possible at runtime by + assigning to the `body` and `title` attributes of this class. + + :param body: Child container object. + :param title: Text to be displayed in the heading of the dialog. + :param buttons: A list of `Button` widgets, displayed at the bottom. + """ + + def __init__( + self, + body: AnyContainer, + title: AnyFormattedText = "", + buttons: Optional[Sequence[Button]] = None, + modal: bool = True, + width: AnyDimension = None, + with_background: bool = False, + ) -> None: + + self.body = body + self.title = title + + buttons = buttons or [] + + # When a button is selected, handle left/right key bindings. + buttons_kb = KeyBindings() + if len(buttons) > 1: + first_selected = has_focus(buttons[0]) + last_selected = has_focus(buttons[-1]) + + buttons_kb.add("left", filter=~first_selected)(focus_previous) + buttons_kb.add("right", filter=~last_selected)(focus_next) + + frame_body: AnyContainer + if buttons: + frame_body = HSplit( + [ + # Add optional padding around the body. + Box( + body=DynamicContainer(lambda: self.body), + padding=D(preferred=1, max=1), + padding_bottom=0, + ), + # The buttons. + Box( + body=VSplit(buttons, padding=1, key_bindings=buttons_kb), + height=D(min=1, max=3, preferred=3), + ), + ] + ) + else: + frame_body = body + + # Key bindings for whole dialog. + kb = KeyBindings() + kb.add("tab", filter=~has_completions)(focus_next) + kb.add("s-tab", filter=~has_completions)(focus_previous) + + frame = Shadow( + body=Frame( + title=lambda: self.title, + body=frame_body, + style="class:dialog.body", + width=(None if with_background is None else width), + key_bindings=kb, + modal=modal, + ) + ) + + self.container: Union[Box, Shadow] + if with_background: + self.container = Box(body=frame, style="class:dialog", width=width) + else: + self.container = frame + + def __pt_container__(self) -> AnyContainer: + return self.container diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/menus.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/menus.py new file mode 100644 index 00000000..4408ec65 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/menus.py @@ -0,0 +1,360 @@ +from typing import Callable, Iterable, List, Optional, Sequence, Union + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.filters import Condition +from prompt_toolkit.formatted_text.base import OneStyleAndTextTuple, StyleAndTextTuples +from prompt_toolkit.key_binding.key_bindings import KeyBindings, KeyBindingsBase +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout.containers import ( + AnyContainer, + ConditionalContainer, + Container, + Float, + FloatContainer, + HSplit, + Window, +) +from prompt_toolkit.layout.controls import FormattedTextControl +from prompt_toolkit.mouse_events import MouseEvent, MouseEventType +from prompt_toolkit.utils import get_cwidth +from prompt_toolkit.widgets import Shadow + +from .base import Border + +__all__ = [ + "MenuContainer", + "MenuItem", +] + +E = KeyPressEvent + + +class MenuContainer: + """ + :param floats: List of extra Float objects to display. + :param menu_items: List of `MenuItem` objects. + """ + + def __init__( + self, + body: AnyContainer, + menu_items: List["MenuItem"], + floats: Optional[List[Float]] = None, + key_bindings: Optional[KeyBindingsBase] = None, + ) -> None: + + self.body = body + self.menu_items = menu_items + self.selected_menu = [0] + + # Key bindings. + kb = KeyBindings() + + @Condition + def in_main_menu() -> bool: + return len(self.selected_menu) == 1 + + @Condition + def in_sub_menu() -> bool: + return len(self.selected_menu) > 1 + + # Navigation through the main menu. + + @kb.add("left", filter=in_main_menu) + def _left(event: E) -> None: + self.selected_menu[0] = max(0, self.selected_menu[0] - 1) + + @kb.add("right", filter=in_main_menu) + def _right(event: E) -> None: + self.selected_menu[0] = min( + len(self.menu_items) - 1, self.selected_menu[0] + 1 + ) + + @kb.add("down", filter=in_main_menu) + def _down(event: E) -> None: + self.selected_menu.append(0) + + @kb.add("c-c", filter=in_main_menu) + @kb.add("c-g", filter=in_main_menu) + def _cancel(event: E) -> None: + " Leave menu. " + event.app.layout.focus_last() + + # Sub menu navigation. + + @kb.add("left", filter=in_sub_menu) + @kb.add("c-g", filter=in_sub_menu) + @kb.add("c-c", filter=in_sub_menu) + def _back(event: E) -> None: + " Go back to parent menu. " + if len(self.selected_menu) > 1: + self.selected_menu.pop() + + @kb.add("right", filter=in_sub_menu) + def _submenu(event: E) -> None: + " go into sub menu. " + if self._get_menu(len(self.selected_menu) - 1).children: + self.selected_menu.append(0) + + # If This item does not have a sub menu. Go up in the parent menu. + elif ( + len(self.selected_menu) == 2 + and self.selected_menu[0] < len(self.menu_items) - 1 + ): + self.selected_menu = [ + min(len(self.menu_items) - 1, self.selected_menu[0] + 1) + ] + if self.menu_items[self.selected_menu[0]].children: + self.selected_menu.append(0) + + @kb.add("up", filter=in_sub_menu) + def _up_in_submenu(event: E) -> None: + " Select previous (enabled) menu item or return to main menu. " + # Look for previous enabled items in this sub menu. + menu = self._get_menu(len(self.selected_menu) - 2) + index = self.selected_menu[-1] + + previous_indexes = [ + i + for i, item in enumerate(menu.children) + if i < index and not item.disabled + ] + + if previous_indexes: + self.selected_menu[-1] = previous_indexes[-1] + elif len(self.selected_menu) == 2: + # Return to main menu. + self.selected_menu.pop() + + @kb.add("down", filter=in_sub_menu) + def _down_in_submenu(event: E) -> None: + " Select next (enabled) menu item. " + menu = self._get_menu(len(self.selected_menu) - 2) + index = self.selected_menu[-1] + + next_indexes = [ + i + for i, item in enumerate(menu.children) + if i > index and not item.disabled + ] + + if next_indexes: + self.selected_menu[-1] = next_indexes[0] + + @kb.add("enter") + def _click(event: E) -> None: + " Click the selected menu item. " + item = self._get_menu(len(self.selected_menu) - 1) + if item.handler: + event.app.layout.focus_last() + item.handler() + + # Controls. + self.control = FormattedTextControl( + self._get_menu_fragments, key_bindings=kb, focusable=True, show_cursor=False + ) + + self.window = Window(height=1, content=self.control, style="class:menu-bar") + + submenu = self._submenu(0) + submenu2 = self._submenu(1) + submenu3 = self._submenu(2) + + @Condition + def has_focus() -> bool: + return get_app().layout.current_window == self.window + + self.container = FloatContainer( + content=HSplit( + [ + # The titlebar. + self.window, + # The 'body', like defined above. + body, + ] + ), + floats=[ + Float( + xcursor=True, + ycursor=True, + content=ConditionalContainer( + content=Shadow(body=submenu), filter=has_focus + ), + ), + Float( + attach_to_window=submenu, + xcursor=True, + ycursor=True, + allow_cover_cursor=True, + content=ConditionalContainer( + content=Shadow(body=submenu2), + filter=has_focus + & Condition(lambda: len(self.selected_menu) >= 1), + ), + ), + Float( + attach_to_window=submenu2, + xcursor=True, + ycursor=True, + allow_cover_cursor=True, + content=ConditionalContainer( + content=Shadow(body=submenu3), + filter=has_focus + & Condition(lambda: len(self.selected_menu) >= 2), + ), + ), + # -- + ] + + (floats or []), + key_bindings=key_bindings, + ) + + def _get_menu(self, level: int) -> "MenuItem": + menu = self.menu_items[self.selected_menu[0]] + + for i, index in enumerate(self.selected_menu[1:]): + if i < level: + try: + menu = menu.children[index] + except IndexError: + return MenuItem("debug") + + return menu + + def _get_menu_fragments(self) -> StyleAndTextTuples: + focused = get_app().layout.has_focus(self.window) + + # This is called during the rendering. When we discover that this + # widget doesn't have the focus anymore. Reset menu state. + if not focused: + self.selected_menu = [0] + + # Generate text fragments for the main menu. + def one_item(i: int, item: MenuItem) -> Iterable[OneStyleAndTextTuple]: + def mouse_handler(mouse_event: MouseEvent) -> None: + if mouse_event.event_type == MouseEventType.MOUSE_UP: + # Toggle focus. + app = get_app() + if app.layout.has_focus(self.window): + if self.selected_menu == [i]: + app.layout.focus_last() + else: + app.layout.focus(self.window) + self.selected_menu = [i] + + yield ("class:menu-bar", " ", mouse_handler) + if i == self.selected_menu[0] and focused: + yield ("[SetMenuPosition]", "", mouse_handler) + style = "class:menu-bar.selected-item" + else: + style = "class:menu-bar" + yield style, item.text, mouse_handler + + result: StyleAndTextTuples = [] + for i, item in enumerate(self.menu_items): + result.extend(one_item(i, item)) + + return result + + def _submenu(self, level: int = 0) -> Window: + def get_text_fragments() -> StyleAndTextTuples: + result: StyleAndTextTuples = [] + if level < len(self.selected_menu): + menu = self._get_menu(level) + if menu.children: + result.append(("class:menu", Border.TOP_LEFT)) + result.append(("class:menu", Border.HORIZONTAL * (menu.width + 4))) + result.append(("class:menu", Border.TOP_RIGHT)) + result.append(("", "\n")) + try: + selected_item = self.selected_menu[level + 1] + except IndexError: + selected_item = -1 + + def one_item( + i: int, item: MenuItem + ) -> Iterable[OneStyleAndTextTuple]: + def mouse_handler(mouse_event: MouseEvent) -> None: + if mouse_event.event_type == MouseEventType.MOUSE_UP: + app = get_app() + if item.handler: + app.layout.focus_last() + item.handler() + else: + self.selected_menu = self.selected_menu[ + : level + 1 + ] + [i] + + if i == selected_item: + yield ("[SetCursorPosition]", "") + style = "class:menu-bar.selected-item" + else: + style = "" + + yield ("class:menu", Border.VERTICAL) + if item.text == "-": + yield ( + style + "class:menu-border", + "{}".format(Border.HORIZONTAL * (menu.width + 3)), + mouse_handler, + ) + else: + yield ( + style, + " {}".format(item.text).ljust(menu.width + 3), + mouse_handler, + ) + + if item.children: + yield (style, ">", mouse_handler) + else: + yield (style, " ", mouse_handler) + + if i == selected_item: + yield ("[SetMenuPosition]", "") + yield ("class:menu", Border.VERTICAL) + + yield ("", "\n") + + for i, item in enumerate(menu.children): + result.extend(one_item(i, item)) + + result.append(("class:menu", Border.BOTTOM_LEFT)) + result.append(("class:menu", Border.HORIZONTAL * (menu.width + 4))) + result.append(("class:menu", Border.BOTTOM_RIGHT)) + return result + + return Window(FormattedTextControl(get_text_fragments), style="class:menu") + + @property + def floats(self) -> Optional[List[Float]]: + return self.container.floats + + def __pt_container__(self) -> Container: + return self.container + + +class MenuItem: + def __init__( + self, + text: str = "", + handler: Optional[Callable[[], None]] = None, + children: Optional[List["MenuItem"]] = None, + shortcut: Optional[Sequence[Union[Keys, str]]] = None, + disabled: bool = False, + ) -> None: + + self.text = text + self.handler = handler + self.children = children or [] + self.shortcut = shortcut + self.disabled = disabled + self.selected_item = 0 + + @property + def width(self) -> int: + if self.children: + return max(get_cwidth(c.text) for c in self.children) + else: + return 0 diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/toolbars.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/toolbars.py new file mode 100644 index 00000000..b9dd8484 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/widgets/toolbars.py @@ -0,0 +1,374 @@ +from typing import Optional + +from prompt_toolkit.application.current import get_app +from prompt_toolkit.buffer import Buffer +from prompt_toolkit.enums import SYSTEM_BUFFER +from prompt_toolkit.filters import ( + Condition, + FilterOrBool, + emacs_mode, + has_arg, + has_completions, + has_focus, + has_validation_error, + to_filter, + vi_mode, + vi_navigation_mode, +) +from prompt_toolkit.formatted_text import ( + AnyFormattedText, + StyleAndTextTuples, + fragment_list_len, + to_formatted_text, +) +from prompt_toolkit.key_binding.key_bindings import ( + ConditionalKeyBindings, + KeyBindings, + KeyBindingsBase, + merge_key_bindings, +) +from prompt_toolkit.key_binding.key_processor import KeyPressEvent +from prompt_toolkit.key_binding.vi_state import InputMode +from prompt_toolkit.keys import Keys +from prompt_toolkit.layout.containers import ConditionalContainer, Container, Window +from prompt_toolkit.layout.controls import ( + BufferControl, + FormattedTextControl, + SearchBufferControl, + UIContent, + UIControl, +) +from prompt_toolkit.layout.dimension import Dimension +from prompt_toolkit.layout.processors import BeforeInput +from prompt_toolkit.lexers import SimpleLexer +from prompt_toolkit.search import SearchDirection + +__all__ = [ + "ArgToolbar", + "CompletionsToolbar", + "FormattedTextToolbar", + "SearchToolbar", + "SystemToolbar", + "ValidationToolbar", +] + +E = KeyPressEvent + + +class FormattedTextToolbar(Window): + def __init__(self, text: AnyFormattedText, style: str = "", **kw) -> None: + # Note: The style needs to be applied to the toolbar as a whole, not + # just the `FormattedTextControl`. + super().__init__( + FormattedTextControl(text, **kw), + style=style, + dont_extend_height=True, + height=Dimension(min=1), + ) + + +class SystemToolbar: + """ + Toolbar for a system prompt. + + :param prompt: Prompt to be displayed to the user. + """ + + def __init__( + self, + prompt: AnyFormattedText = "Shell command: ", + enable_global_bindings: FilterOrBool = True, + ) -> None: + + self.prompt = prompt + self.enable_global_bindings = to_filter(enable_global_bindings) + + self.system_buffer = Buffer(name=SYSTEM_BUFFER) + + self._bindings = self._build_key_bindings() + + self.buffer_control = BufferControl( + buffer=self.system_buffer, + lexer=SimpleLexer(style="class:system-toolbar.text"), + input_processors=[ + BeforeInput(lambda: self.prompt, style="class:system-toolbar") + ], + key_bindings=self._bindings, + ) + + self.window = Window( + self.buffer_control, height=1, style="class:system-toolbar" + ) + + self.container = ConditionalContainer( + content=self.window, filter=has_focus(self.system_buffer) + ) + + def _get_display_before_text(self) -> StyleAndTextTuples: + return [ + ("class:system-toolbar", "Shell command: "), + ("class:system-toolbar.text", self.system_buffer.text), + ("", "\n"), + ] + + def _build_key_bindings(self) -> KeyBindingsBase: + focused = has_focus(self.system_buffer) + + # Emacs + emacs_bindings = KeyBindings() + handle = emacs_bindings.add + + @handle("escape", filter=focused) + @handle("c-g", filter=focused) + @handle("c-c", filter=focused) + def _cancel(event: E) -> None: + " Hide system prompt. " + self.system_buffer.reset() + event.app.layout.focus_last() + + @handle("enter", filter=focused) + def _accept(event: E) -> None: + " Run system command. " + event.app.run_system_command( + self.system_buffer.text, + display_before_text=self._get_display_before_text(), + ) + self.system_buffer.reset(append_to_history=True) + event.app.layout.focus_last() + + # Vi. + vi_bindings = KeyBindings() + handle = vi_bindings.add + + @handle("escape", filter=focused) + @handle("c-c", filter=focused) + def _cancel_vi(event: E) -> None: + " Hide system prompt. " + event.app.vi_state.input_mode = InputMode.NAVIGATION + self.system_buffer.reset() + event.app.layout.focus_last() + + @handle("enter", filter=focused) + def _accept_vi(event: E) -> None: + " Run system command. " + event.app.vi_state.input_mode = InputMode.NAVIGATION + event.app.run_system_command( + self.system_buffer.text, + display_before_text=self._get_display_before_text(), + ) + self.system_buffer.reset(append_to_history=True) + event.app.layout.focus_last() + + # Global bindings. (Listen to these bindings, even when this widget is + # not focussed.) + global_bindings = KeyBindings() + handle = global_bindings.add + + @handle(Keys.Escape, "!", filter=~focused & emacs_mode, is_global=True) + def _focus_me(event: E) -> None: + " M-'!' will focus this user control. " + event.app.layout.focus(self.window) + + @handle("!", filter=~focused & vi_mode & vi_navigation_mode, is_global=True) + def _focus_me_vi(event: E) -> None: + " Focus. " + event.app.vi_state.input_mode = InputMode.INSERT + event.app.layout.focus(self.window) + + return merge_key_bindings( + [ + ConditionalKeyBindings(emacs_bindings, emacs_mode), + ConditionalKeyBindings(vi_bindings, vi_mode), + ConditionalKeyBindings(global_bindings, self.enable_global_bindings), + ] + ) + + def __pt_container__(self) -> Container: + return self.container + + +class ArgToolbar: + def __init__(self) -> None: + def get_formatted_text() -> StyleAndTextTuples: + arg = get_app().key_processor.arg or "" + if arg == "-": + arg = "-1" + + return [ + ("class:arg-toolbar", "Repeat: "), + ("class:arg-toolbar.text", arg), + ] + + self.window = Window(FormattedTextControl(get_formatted_text), height=1) + + self.container = ConditionalContainer(content=self.window, filter=has_arg) + + def __pt_container__(self) -> Container: + return self.container + + +class SearchToolbar: + """ + :param vi_mode: Display '/' and '?' instead of I-search. + :param ignore_case: Search case insensitive. + """ + + def __init__( + self, + search_buffer: Optional[Buffer] = None, + vi_mode: bool = False, + text_if_not_searching: AnyFormattedText = "", + forward_search_prompt: AnyFormattedText = "I-search: ", + backward_search_prompt: AnyFormattedText = "I-search backward: ", + ignore_case: FilterOrBool = False, + ) -> None: + + if search_buffer is None: + search_buffer = Buffer() + + @Condition + def is_searching() -> bool: + return self.control in get_app().layout.search_links + + def get_before_input() -> AnyFormattedText: + if not is_searching(): + return text_if_not_searching + elif ( + self.control.searcher_search_state.direction == SearchDirection.BACKWARD + ): + return "?" if vi_mode else backward_search_prompt + else: + return "/" if vi_mode else forward_search_prompt + + self.search_buffer = search_buffer + + self.control = SearchBufferControl( + buffer=search_buffer, + input_processors=[ + BeforeInput(get_before_input, style="class:search-toolbar.prompt") + ], + lexer=SimpleLexer(style="class:search-toolbar.text"), + ignore_case=ignore_case, + ) + + self.container = ConditionalContainer( + content=Window(self.control, height=1, style="class:search-toolbar"), + filter=is_searching, + ) + + def __pt_container__(self) -> Container: + return self.container + + +class _CompletionsToolbarControl(UIControl): + def create_content(self, width: int, height: int) -> UIContent: + all_fragments: StyleAndTextTuples = [] + + complete_state = get_app().current_buffer.complete_state + if complete_state: + completions = complete_state.completions + index = complete_state.complete_index # Can be None! + + # Width of the completions without the left/right arrows in the margins. + content_width = width - 6 + + # Booleans indicating whether we stripped from the left/right + cut_left = False + cut_right = False + + # Create Menu content. + fragments: StyleAndTextTuples = [] + + for i, c in enumerate(completions): + # When there is no more place for the next completion + if fragment_list_len(fragments) + len(c.display_text) >= content_width: + # If the current one was not yet displayed, page to the next sequence. + if i <= (index or 0): + fragments = [] + cut_left = True + # If the current one is visible, stop here. + else: + cut_right = True + break + + fragments.extend( + to_formatted_text( + c.display_text, + style=( + "class:completion-toolbar.completion.current" + if i == index + else "class:completion-toolbar.completion" + ), + ) + ) + fragments.append(("", " ")) + + # Extend/strip until the content width. + fragments.append(("", " " * (content_width - fragment_list_len(fragments)))) + fragments = fragments[:content_width] + + # Return fragments + all_fragments.append(("", " ")) + all_fragments.append( + ("class:completion-toolbar.arrow", "<" if cut_left else " ") + ) + all_fragments.append(("", " ")) + + all_fragments.extend(fragments) + + all_fragments.append(("", " ")) + all_fragments.append( + ("class:completion-toolbar.arrow", ">" if cut_right else " ") + ) + all_fragments.append(("", " ")) + + def get_line(i: int) -> StyleAndTextTuples: + return all_fragments + + return UIContent(get_line=get_line, line_count=1) + + +class CompletionsToolbar: + def __init__(self) -> None: + self.container = ConditionalContainer( + content=Window( + _CompletionsToolbarControl(), height=1, style="class:completion-toolbar" + ), + filter=has_completions, + ) + + def __pt_container__(self) -> Container: + return self.container + + +class ValidationToolbar: + def __init__(self, show_position: bool = False) -> None: + def get_formatted_text() -> StyleAndTextTuples: + buff = get_app().current_buffer + + if buff.validation_error: + row, column = buff.document.translate_index_to_position( + buff.validation_error.cursor_position + ) + + if show_position: + text = "%s (line=%s column=%s)" % ( + buff.validation_error.message, + row + 1, + column + 1, + ) + else: + text = buff.validation_error.message + + return [("class:validation-toolbar", text)] + else: + return [] + + self.control = FormattedTextControl(get_formatted_text) + + self.container = ConditionalContainer( + content=Window(self.control, height=1), filter=has_validation_error + ) + + def __pt_container__(self) -> Container: + return self.container diff --git a/.venv/lib/python3.8/site-packages/prompt_toolkit/win32_types.py b/.venv/lib/python3.8/site-packages/prompt_toolkit/win32_types.py new file mode 100644 index 00000000..8439b75c --- /dev/null +++ b/.venv/lib/python3.8/site-packages/prompt_toolkit/win32_types.py @@ -0,0 +1,166 @@ +from ctypes import Structure, Union, c_char, c_long, c_short, c_ulong +from ctypes.wintypes import BOOL, DWORD, LPVOID, WCHAR, WORD + +# Input/Output standard device numbers. Note that these are not handle objects. +# It's the `windll.kernel32.GetStdHandle` system call that turns them into a +# real handle object. +STD_INPUT_HANDLE = c_ulong(-10) +STD_OUTPUT_HANDLE = c_ulong(-11) +STD_ERROR_HANDLE = c_ulong(-12) + + +class COORD(Structure): + """ + Struct in wincon.h + http://msdn.microsoft.com/en-us/library/windows/desktop/ms682119(v=vs.85).aspx + """ + + _fields_ = [ + ("X", c_short), # Short + ("Y", c_short), # Short + ] + + def __repr__(self) -> str: + return "%s(X=%r, Y=%r, type_x=%r, type_y=%r)" % ( + self.__class__.__name__, + self.X, + self.Y, + type(self.X), + type(self.Y), + ) + + +class UNICODE_OR_ASCII(Union): + _fields_ = [ + ("AsciiChar", c_char), + ("UnicodeChar", WCHAR), + ] + + +class KEY_EVENT_RECORD(Structure): + """ + http://msdn.microsoft.com/en-us/library/windows/desktop/ms684166(v=vs.85).aspx + """ + + _fields_ = [ + ("KeyDown", c_long), # bool + ("RepeatCount", c_short), # word + ("VirtualKeyCode", c_short), # word + ("VirtualScanCode", c_short), # word + ("uChar", UNICODE_OR_ASCII), # Unicode or ASCII. + ("ControlKeyState", c_long), # double word + ] + + +class MOUSE_EVENT_RECORD(Structure): + """ + http://msdn.microsoft.com/en-us/library/windows/desktop/ms684239(v=vs.85).aspx + """ + + _fields_ = [ + ("MousePosition", COORD), + ("ButtonState", c_long), # dword + ("ControlKeyState", c_long), # dword + ("EventFlags", c_long), # dword + ] + + +class WINDOW_BUFFER_SIZE_RECORD(Structure): + """ + http://msdn.microsoft.com/en-us/library/windows/desktop/ms687093(v=vs.85).aspx + """ + + _fields_ = [("Size", COORD)] + + +class MENU_EVENT_RECORD(Structure): + """ + http://msdn.microsoft.com/en-us/library/windows/desktop/ms684213(v=vs.85).aspx + """ + + _fields_ = [("CommandId", c_long)] # uint + + +class FOCUS_EVENT_RECORD(Structure): + """ + http://msdn.microsoft.com/en-us/library/windows/desktop/ms683149(v=vs.85).aspx + """ + + _fields_ = [("SetFocus", c_long)] # bool + + +class EVENT_RECORD(Union): + _fields_ = [ + ("KeyEvent", KEY_EVENT_RECORD), + ("MouseEvent", MOUSE_EVENT_RECORD), + ("WindowBufferSizeEvent", WINDOW_BUFFER_SIZE_RECORD), + ("MenuEvent", MENU_EVENT_RECORD), + ("FocusEvent", FOCUS_EVENT_RECORD), + ] + + +class INPUT_RECORD(Structure): + """ + http://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx + """ + + _fields_ = [("EventType", c_short), ("Event", EVENT_RECORD)] # word # Union. + + +EventTypes = { + 1: "KeyEvent", + 2: "MouseEvent", + 4: "WindowBufferSizeEvent", + 8: "MenuEvent", + 16: "FocusEvent", +} + + +class SMALL_RECT(Structure): + """struct in wincon.h.""" + + _fields_ = [ + ("Left", c_short), + ("Top", c_short), + ("Right", c_short), + ("Bottom", c_short), + ] + + +class CONSOLE_SCREEN_BUFFER_INFO(Structure): + """struct in wincon.h.""" + + _fields_ = [ + ("dwSize", COORD), + ("dwCursorPosition", COORD), + ("wAttributes", WORD), + ("srWindow", SMALL_RECT), + ("dwMaximumWindowSize", COORD), + ] + + def __str__(self) -> str: + return "(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)" % ( + self.dwSize.Y, + self.dwSize.X, + self.dwCursorPosition.Y, + self.dwCursorPosition.X, + self.wAttributes, + self.srWindow.Top, + self.srWindow.Left, + self.srWindow.Bottom, + self.srWindow.Right, + self.dwMaximumWindowSize.Y, + self.dwMaximumWindowSize.X, + ) + + +class SECURITY_ATTRIBUTES(Structure): + """ + http://msdn.microsoft.com/en-us/library/windows/desktop/aa379560(v=vs.85).aspx + """ + + _fields_ = [ + ("nLength", DWORD), + ("lpSecurityDescriptor", LPVOID), + ("bInheritHandle", BOOL), + ] diff --git a/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/INSTALLER b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/INSTALLER new file mode 100644 index 00000000..a1b589e3 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/LICENSE b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/LICENSE new file mode 100644 index 00000000..9c772742 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/LICENSE @@ -0,0 +1,16 @@ +Ptyprocess is under the ISC license, as code derived from Pexpect. + http://opensource.org/licenses/ISC + +Copyright (c) 2013-2014, Pexpect development team +Copyright (c) 2012, Noah Spurrier + +PERMISSION TO USE, COPY, MODIFY, AND/OR DISTRIBUTE THIS SOFTWARE FOR ANY PURPOSE +WITH OR WITHOUT FEE IS HEREBY GRANTED, PROVIDED THAT THE ABOVE COPYRIGHT NOTICE +AND THIS PERMISSION NOTICE APPEAR IN ALL COPIES. THE SOFTWARE IS PROVIDED +"AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT +SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 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. + diff --git a/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/METADATA b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/METADATA new file mode 100644 index 00000000..f38abfe7 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/METADATA @@ -0,0 +1,37 @@ +Metadata-Version: 2.1 +Name: ptyprocess +Version: 0.6.0 +Summary: Run a subprocess in a pseudo terminal +Home-page: https://github.com/pexpect/ptyprocess +License: UNKNOWN +Author: Thomas Kluyver +Author-email: thomas@kluyver.me.uk +Description-Content-Type: text/x-rst +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: ISC License (ISCL) +Classifier: Operating System :: POSIX +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Terminals + +Launch a subprocess in a pseudo terminal (pty), and interact with both the +process and its pty. + +Sometimes, piping stdin and stdout is not enough. There might be a password +prompt that doesn't read from stdin, output that changes when it's going to a +pipe rather than a terminal, or curses-style interfaces that rely on a terminal. +If you need to automate these things, running the process in a pseudo terminal +(pty) is the answer. + +Interface:: + + p = PtyProcessUnicode.spawn(['python']) + p.read(20) + p.write('6+6\n') + p.read(20) + diff --git a/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/RECORD b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/RECORD new file mode 100644 index 00000000..0d72db53 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/RECORD @@ -0,0 +1,13 @@ +ptyprocess-0.6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ptyprocess-0.6.0.dist-info/LICENSE,sha256=yCLThbGnMymEYkF5m-zxhpC11Edkwb7WkwC1NqQFAwo,905 +ptyprocess-0.6.0.dist-info/METADATA,sha256=E32CAJai9AnGYv75eEH1QQngK3Sg-v1JEOJ2q-FOD3Q,1312 +ptyprocess-0.6.0.dist-info/RECORD,, +ptyprocess-0.6.0.dist-info/WHEEL,sha256=Pnb1-aANNEDpiI6zZkifNVAxs3Mjlbjz3HYhduOZleI,97 +ptyprocess/__init__.py,sha256=Q7Cd31VTCkFWg4KPHqZ75ykOQ7kj5kuo-tzkqOz8suo,138 +ptyprocess/__pycache__/__init__.cpython-38.pyc,, +ptyprocess/__pycache__/_fork_pty.cpython-38.pyc,, +ptyprocess/__pycache__/ptyprocess.cpython-38.pyc,, +ptyprocess/__pycache__/util.cpython-38.pyc,, +ptyprocess/_fork_pty.py,sha256=VVvMy8c4ZpjDMiIMSg8T1BQ1g3SBexDpey_cxi0n5aw,2362 +ptyprocess/ptyprocess.py,sha256=rD_1VuW02HxwB4hPIm6cah5XGx7pHj8QaYz5OhCXwf4,31355 +ptyprocess/util.py,sha256=rQAdDRZfoOiOn6vykWth0wI6FFKAp7aJtBSdt-KBWdU,2785 diff --git a/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/WHEEL b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/WHEEL new file mode 100644 index 00000000..2d80143d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess-0.6.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: flit 1.0 +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any diff --git a/.venv/lib/python3.8/site-packages/ptyprocess/__init__.py b/.venv/lib/python3.8/site-packages/ptyprocess/__init__.py new file mode 100644 index 00000000..e633d0cd --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess/__init__.py @@ -0,0 +1,4 @@ +"""Run a subprocess in a pseudo terminal""" +from .ptyprocess import PtyProcess, PtyProcessUnicode, PtyProcessError + +__version__ = '0.6.0' diff --git a/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..bf3658a1 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/_fork_pty.cpython-38.pyc b/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/_fork_pty.cpython-38.pyc new file mode 100644 index 00000000..c41d2145 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/_fork_pty.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/ptyprocess.cpython-38.pyc b/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/ptyprocess.cpython-38.pyc new file mode 100644 index 00000000..0a76cb28 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/ptyprocess.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/util.cpython-38.pyc b/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/util.cpython-38.pyc new file mode 100644 index 00000000..1b46ed85 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/ptyprocess/__pycache__/util.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/ptyprocess/_fork_pty.py b/.venv/lib/python3.8/site-packages/ptyprocess/_fork_pty.py new file mode 100644 index 00000000..a8d05fe5 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess/_fork_pty.py @@ -0,0 +1,78 @@ +"""Substitute for the forkpty system call, to support Solaris. +""" +import os +import errno + +from pty import (STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, CHILD) +from .util import PtyProcessError + +def fork_pty(): + '''This implements a substitute for the forkpty system call. This + should be more portable than the pty.fork() function. Specifically, + this should work on Solaris. + + Modified 10.06.05 by Geoff Marshall: Implemented __fork_pty() method to + resolve the issue with Python's pty.fork() not supporting Solaris, + particularly ssh. Based on patch to posixmodule.c authored by Noah + Spurrier:: + + http://mail.python.org/pipermail/python-dev/2003-May/035281.html + + ''' + + parent_fd, child_fd = os.openpty() + if parent_fd < 0 or child_fd < 0: + raise OSError("os.openpty() failed") + + pid = os.fork() + if pid == CHILD: + # Child. + os.close(parent_fd) + pty_make_controlling_tty(child_fd) + + os.dup2(child_fd, STDIN_FILENO) + os.dup2(child_fd, STDOUT_FILENO) + os.dup2(child_fd, STDERR_FILENO) + + else: + # Parent. + os.close(child_fd) + + return pid, parent_fd + +def pty_make_controlling_tty(tty_fd): + '''This makes the pseudo-terminal the controlling tty. This should be + more portable than the pty.fork() function. Specifically, this should + work on Solaris. ''' + + child_name = os.ttyname(tty_fd) + + # Disconnect from controlling tty, if any. Raises OSError of ENXIO + # if there was no controlling tty to begin with, such as when + # executed by a cron(1) job. + try: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + os.close(fd) + except OSError as err: + if err.errno != errno.ENXIO: + raise + + os.setsid() + + # Verify we are disconnected from controlling tty by attempting to open + # it again. We expect that OSError of ENXIO should always be raised. + try: + fd = os.open("/dev/tty", os.O_RDWR | os.O_NOCTTY) + os.close(fd) + raise PtyProcessError("OSError of errno.ENXIO should be raised.") + except OSError as err: + if err.errno != errno.ENXIO: + raise + + # Verify we can open child pty. + fd = os.open(child_name, os.O_RDWR) + os.close(fd) + + # Verify we now have a controlling tty. + fd = os.open("/dev/tty", os.O_WRONLY) + os.close(fd) diff --git a/.venv/lib/python3.8/site-packages/ptyprocess/ptyprocess.py b/.venv/lib/python3.8/site-packages/ptyprocess/ptyprocess.py new file mode 100644 index 00000000..29b4e43b --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess/ptyprocess.py @@ -0,0 +1,836 @@ +import codecs +import errno +import fcntl +import io +import os +import pty +import resource +import signal +import struct +import sys +import termios +import time + +try: + import builtins # Python 3 +except ImportError: + import __builtin__ as builtins # Python 2 + +# Constants +from pty import (STDIN_FILENO, CHILD) + +from .util import which, PtyProcessError + +_platform = sys.platform.lower() + +# Solaris uses internal __fork_pty(). All others use pty.fork(). +_is_solaris = ( + _platform.startswith('solaris') or + _platform.startswith('sunos')) + +if _is_solaris: + use_native_pty_fork = False + from . import _fork_pty +else: + use_native_pty_fork = True + +PY3 = sys.version_info[0] >= 3 + +if PY3: + def _byte(i): + return bytes([i]) +else: + def _byte(i): + return chr(i) + + class FileNotFoundError(OSError): pass + class TimeoutError(OSError): pass + +_EOF, _INTR = None, None + +def _make_eof_intr(): + """Set constants _EOF and _INTR. + + This avoids doing potentially costly operations on module load. + """ + global _EOF, _INTR + if (_EOF is not None) and (_INTR is not None): + return + + # inherit EOF and INTR definitions from controlling process. + try: + from termios import VEOF, VINTR + fd = None + for name in 'stdin', 'stdout': + stream = getattr(sys, '__%s__' % name, None) + if stream is None or not hasattr(stream, 'fileno'): + continue + try: + fd = stream.fileno() + except ValueError: + continue + if fd is None: + # no fd, raise ValueError to fallback on CEOF, CINTR + raise ValueError("No stream has a fileno") + intr = ord(termios.tcgetattr(fd)[6][VINTR]) + eof = ord(termios.tcgetattr(fd)[6][VEOF]) + except (ImportError, OSError, IOError, ValueError, termios.error): + # unless the controlling process is also not a terminal, + # such as cron(1), or when stdin and stdout are both closed. + # Fall-back to using CEOF and CINTR. There + try: + from termios import CEOF, CINTR + (intr, eof) = (CINTR, CEOF) + except ImportError: + # ^C, ^D + (intr, eof) = (3, 4) + + _INTR = _byte(intr) + _EOF = _byte(eof) + +# setecho and setwinsize are pulled out here because on some platforms, we need +# to do this from the child before we exec() + +def _setecho(fd, state): + errmsg = 'setecho() may not be called on this platform (it may still be possible to enable/disable echo when spawning the child process)' + + try: + attr = termios.tcgetattr(fd) + except termios.error as err: + if err.args[0] == errno.EINVAL: + raise IOError(err.args[0], '%s: %s.' % (err.args[1], errmsg)) + raise + + if state: + attr[3] = attr[3] | termios.ECHO + else: + attr[3] = attr[3] & ~termios.ECHO + + try: + # I tried TCSADRAIN and TCSAFLUSH, but these were inconsistent and + # blocked on some platforms. TCSADRAIN would probably be ideal. + termios.tcsetattr(fd, termios.TCSANOW, attr) + except IOError as err: + if err.args[0] == errno.EINVAL: + raise IOError(err.args[0], '%s: %s.' % (err.args[1], errmsg)) + raise + +def _setwinsize(fd, rows, cols): + # Some very old platforms have a bug that causes the value for + # termios.TIOCSWINSZ to be truncated. There was a hack here to work + # around this, but it caused problems with newer platforms so has been + # removed. For details see https://github.com/pexpect/pexpect/issues/39 + TIOCSWINSZ = getattr(termios, 'TIOCSWINSZ', -2146929561) + # Note, assume ws_xpixel and ws_ypixel are zero. + s = struct.pack('HHHH', rows, cols, 0, 0) + fcntl.ioctl(fd, TIOCSWINSZ, s) + +class PtyProcess(object): + '''This class represents a process running in a pseudoterminal. + + The main constructor is the :meth:`spawn` classmethod. + ''' + string_type = bytes + if PY3: + linesep = os.linesep.encode('ascii') + crlf = '\r\n'.encode('ascii') + + @staticmethod + def write_to_stdout(b): + try: + return sys.stdout.buffer.write(b) + except AttributeError: + # If stdout has been replaced, it may not have .buffer + return sys.stdout.write(b.decode('ascii', 'replace')) + else: + linesep = os.linesep + crlf = '\r\n' + write_to_stdout = sys.stdout.write + + encoding = None + + argv = None + env = None + launch_dir = None + + def __init__(self, pid, fd): + _make_eof_intr() # Ensure _EOF and _INTR are calculated + self.pid = pid + self.fd = fd + readf = io.open(fd, 'rb', buffering=0) + writef = io.open(fd, 'wb', buffering=0, closefd=False) + self.fileobj = io.BufferedRWPair(readf, writef) + + self.terminated = False + self.closed = False + self.exitstatus = None + self.signalstatus = None + # status returned by os.waitpid + self.status = None + self.flag_eof = False + # Used by close() to give kernel time to update process status. + # Time in seconds. + self.delayafterclose = 0.1 + # Used by terminate() to give kernel time to update process status. + # Time in seconds. + self.delayafterterminate = 0.1 + + @classmethod + def spawn( + cls, argv, cwd=None, env=None, echo=True, preexec_fn=None, + dimensions=(24, 80)): + '''Start the given command in a child process in a pseudo terminal. + + This does all the fork/exec type of stuff for a pty, and returns an + instance of PtyProcess. + + If preexec_fn is supplied, it will be called with no arguments in the + child process before exec-ing the specified command. + It may, for instance, set signal handlers to SIG_DFL or SIG_IGN. + + Dimensions of the psuedoterminal used for the subprocess can be + specified as a tuple (rows, cols), or the default (24, 80) will be used. + ''' + # Note that it is difficult for this method to fail. + # You cannot detect if the child process cannot start. + # So the only way you can tell if the child process started + # or not is to try to read from the file descriptor. If you get + # EOF immediately then it means that the child is already dead. + # That may not necessarily be bad because you may have spawned a child + # that performs some task; creates no stdout output; and then dies. + + if not isinstance(argv, (list, tuple)): + raise TypeError("Expected a list or tuple for argv, got %r" % argv) + + # Shallow copy of argv so we can modify it + argv = argv[:] + command = argv[0] + + command_with_path = which(command) + if command_with_path is None: + raise FileNotFoundError('The command was not found or was not ' + + 'executable: %s.' % command) + command = command_with_path + argv[0] = command + + # [issue #119] To prevent the case where exec fails and the user is + # stuck interacting with a python child process instead of whatever + # was expected, we implement the solution from + # http://stackoverflow.com/a/3703179 to pass the exception to the + # parent process + + # [issue #119] 1. Before forking, open a pipe in the parent process. + exec_err_pipe_read, exec_err_pipe_write = os.pipe() + + if use_native_pty_fork: + pid, fd = pty.fork() + else: + # Use internal fork_pty, for Solaris + pid, fd = _fork_pty.fork_pty() + + # Some platforms must call setwinsize() and setecho() from the + # child process, and others from the master process. We do both, + # allowing IOError for either. + + if pid == CHILD: + # set window size + try: + _setwinsize(STDIN_FILENO, *dimensions) + except IOError as err: + if err.args[0] not in (errno.EINVAL, errno.ENOTTY): + raise + + # disable echo if spawn argument echo was unset + if not echo: + try: + _setecho(STDIN_FILENO, False) + except (IOError, termios.error) as err: + if err.args[0] not in (errno.EINVAL, errno.ENOTTY): + raise + + # [issue #119] 3. The child closes the reading end and sets the + # close-on-exec flag for the writing end. + os.close(exec_err_pipe_read) + fcntl.fcntl(exec_err_pipe_write, fcntl.F_SETFD, fcntl.FD_CLOEXEC) + + # Do not allow child to inherit open file descriptors from parent, + # with the exception of the exec_err_pipe_write of the pipe + # Impose ceiling on max_fd: AIX bugfix for users with unlimited + # nofiles where resource.RLIMIT_NOFILE is 2^63-1 and os.closerange() + # occasionally raises out of range error + max_fd = min(1048576, resource.getrlimit(resource.RLIMIT_NOFILE)[0]) + os.closerange(3, exec_err_pipe_write) + os.closerange(exec_err_pipe_write+1, max_fd) + + if cwd is not None: + os.chdir(cwd) + + if preexec_fn is not None: + try: + preexec_fn() + except Exception as e: + ename = type(e).__name__ + tosend = '{}:0:{}'.format(ename, str(e)) + if PY3: + tosend = tosend.encode('utf-8') + + os.write(exec_err_pipe_write, tosend) + os.close(exec_err_pipe_write) + os._exit(1) + + try: + if env is None: + os.execv(command, argv) + else: + os.execvpe(command, argv, env) + except OSError as err: + # [issue #119] 5. If exec fails, the child writes the error + # code back to the parent using the pipe, then exits. + tosend = 'OSError:{}:{}'.format(err.errno, str(err)) + if PY3: + tosend = tosend.encode('utf-8') + os.write(exec_err_pipe_write, tosend) + os.close(exec_err_pipe_write) + os._exit(os.EX_OSERR) + + # Parent + inst = cls(pid, fd) + + # Set some informational attributes + inst.argv = argv + if env is not None: + inst.env = env + if cwd is not None: + inst.launch_dir = cwd + + # [issue #119] 2. After forking, the parent closes the writing end + # of the pipe and reads from the reading end. + os.close(exec_err_pipe_write) + exec_err_data = os.read(exec_err_pipe_read, 4096) + os.close(exec_err_pipe_read) + + # [issue #119] 6. The parent reads eof (a zero-length read) if the + # child successfully performed exec, since close-on-exec made + # successful exec close the writing end of the pipe. Or, if exec + # failed, the parent reads the error code and can proceed + # accordingly. Either way, the parent blocks until the child calls + # exec. + if len(exec_err_data) != 0: + try: + errclass, errno_s, errmsg = exec_err_data.split(b':', 2) + exctype = getattr(builtins, errclass.decode('ascii'), Exception) + + exception = exctype(errmsg.decode('utf-8', 'replace')) + if exctype is OSError: + exception.errno = int(errno_s) + except: + raise Exception('Subprocess failed, got bad error data: %r' + % exec_err_data) + else: + raise exception + + try: + inst.setwinsize(*dimensions) + except IOError as err: + if err.args[0] not in (errno.EINVAL, errno.ENOTTY, errno.ENXIO): + raise + + return inst + + def __repr__(self): + clsname = type(self).__name__ + if self.argv is not None: + args = [repr(self.argv)] + if self.env is not None: + args.append("env=%r" % self.env) + if self.launch_dir is not None: + args.append("cwd=%r" % self.launch_dir) + + return "{}.spawn({})".format(clsname, ", ".join(args)) + + else: + return "{}(pid={}, fd={})".format(clsname, self.pid, self.fd) + + @staticmethod + def _coerce_send_string(s): + if not isinstance(s, bytes): + return s.encode('utf-8') + return s + + @staticmethod + def _coerce_read_string(s): + return s + + def __del__(self): + '''This makes sure that no system resources are left open. Python only + garbage collects Python objects. OS file descriptors are not Python + objects, so they must be handled explicitly. If the child file + descriptor was opened outside of this class (passed to the constructor) + then this does not close it. ''' + + if not self.closed: + # It is possible for __del__ methods to execute during the + # teardown of the Python VM itself. Thus self.close() may + # trigger an exception because os.close may be None. + try: + self.close() + # which exception, shouldn't we catch explicitly .. ? + except: + pass + + + def fileno(self): + '''This returns the file descriptor of the pty for the child. + ''' + return self.fd + + def close(self, force=True): + '''This closes the connection with the child application. Note that + calling close() more than once is valid. This emulates standard Python + behavior with files. Set force to True if you want to make sure that + the child is terminated (SIGKILL is sent if the child ignores SIGHUP + and SIGINT). ''' + if not self.closed: + self.flush() + self.fileobj.close() # Closes the file descriptor + # Give kernel time to update process status. + time.sleep(self.delayafterclose) + if self.isalive(): + if not self.terminate(force): + raise PtyProcessError('Could not terminate the child.') + self.fd = -1 + self.closed = True + #self.pid = None + + def flush(self): + '''This does nothing. It is here to support the interface for a + File-like object. ''' + + pass + + def isatty(self): + '''This returns True if the file descriptor is open and connected to a + tty(-like) device, else False. + + On SVR4-style platforms implementing streams, such as SunOS and HP-UX, + the child pty may not appear as a terminal device. This means + methods such as setecho(), setwinsize(), getwinsize() may raise an + IOError. ''' + + return os.isatty(self.fd) + + def waitnoecho(self, timeout=None): + '''This waits until the terminal ECHO flag is set False. This returns + True if the echo mode is off. This returns False if the ECHO flag was + not set False before the timeout. This can be used to detect when the + child is waiting for a password. Usually a child application will turn + off echo mode when it is waiting for the user to enter a password. For + example, instead of expecting the "password:" prompt you can wait for + the child to set ECHO off:: + + p = pexpect.spawn('ssh user@example.com') + p.waitnoecho() + p.sendline(mypassword) + + If timeout==None then this method to block until ECHO flag is False. + ''' + + if timeout is not None: + end_time = time.time() + timeout + while True: + if not self.getecho(): + return True + if timeout < 0 and timeout is not None: + return False + if timeout is not None: + timeout = end_time - time.time() + time.sleep(0.1) + + def getecho(self): + '''This returns the terminal echo mode. This returns True if echo is + on or False if echo is off. Child applications that are expecting you + to enter a password often set ECHO False. See waitnoecho(). + + Not supported on platforms where ``isatty()`` returns False. ''' + + try: + attr = termios.tcgetattr(self.fd) + except termios.error as err: + errmsg = 'getecho() may not be called on this platform' + if err.args[0] == errno.EINVAL: + raise IOError(err.args[0], '%s: %s.' % (err.args[1], errmsg)) + raise + + self.echo = bool(attr[3] & termios.ECHO) + return self.echo + + def setecho(self, state): + '''This sets the terminal echo mode on or off. Note that anything the + child sent before the echo will be lost, so you should be sure that + your input buffer is empty before you call setecho(). For example, the + following will work as expected:: + + p = pexpect.spawn('cat') # Echo is on by default. + p.sendline('1234') # We expect see this twice from the child... + p.expect(['1234']) # ... once from the tty echo... + p.expect(['1234']) # ... and again from cat itself. + p.setecho(False) # Turn off tty echo + p.sendline('abcd') # We will set this only once (echoed by cat). + p.sendline('wxyz') # We will set this only once (echoed by cat) + p.expect(['abcd']) + p.expect(['wxyz']) + + The following WILL NOT WORK because the lines sent before the setecho + will be lost:: + + p = pexpect.spawn('cat') + p.sendline('1234') + p.setecho(False) # Turn off tty echo + p.sendline('abcd') # We will set this only once (echoed by cat). + p.sendline('wxyz') # We will set this only once (echoed by cat) + p.expect(['1234']) + p.expect(['1234']) + p.expect(['abcd']) + p.expect(['wxyz']) + + + Not supported on platforms where ``isatty()`` returns False. + ''' + _setecho(self.fd, state) + + self.echo = state + + def read(self, size=1024): + """Read and return at most ``size`` bytes from the pty. + + Can block if there is nothing to read. Raises :exc:`EOFError` if the + terminal was closed. + + Unlike Pexpect's ``read_nonblocking`` method, this doesn't try to deal + with the vagaries of EOF on platforms that do strange things, like IRIX + or older Solaris systems. It handles the errno=EIO pattern used on + Linux, and the empty-string return used on BSD platforms and (seemingly) + on recent Solaris. + """ + try: + s = self.fileobj.read1(size) + except (OSError, IOError) as err: + if err.args[0] == errno.EIO: + # Linux-style EOF + self.flag_eof = True + raise EOFError('End Of File (EOF). Exception style platform.') + raise + if s == b'': + # BSD-style EOF (also appears to work on recent Solaris (OpenIndiana)) + self.flag_eof = True + raise EOFError('End Of File (EOF). Empty string style platform.') + + return s + + def readline(self): + """Read one line from the pseudoterminal, and return it as unicode. + + Can block if there is nothing to read. Raises :exc:`EOFError` if the + terminal was closed. + """ + try: + s = self.fileobj.readline() + except (OSError, IOError) as err: + if err.args[0] == errno.EIO: + # Linux-style EOF + self.flag_eof = True + raise EOFError('End Of File (EOF). Exception style platform.') + raise + if s == b'': + # BSD-style EOF (also appears to work on recent Solaris (OpenIndiana)) + self.flag_eof = True + raise EOFError('End Of File (EOF). Empty string style platform.') + + return s + + def _writeb(self, b, flush=True): + n = self.fileobj.write(b) + if flush: + self.fileobj.flush() + return n + + def write(self, s, flush=True): + """Write bytes to the pseudoterminal. + + Returns the number of bytes written. + """ + return self._writeb(s, flush=flush) + + def sendcontrol(self, char): + '''Helper method that wraps send() with mnemonic access for sending control + character to the child (such as Ctrl-C or Ctrl-D). For example, to send + Ctrl-G (ASCII 7, bell, '\a'):: + + child.sendcontrol('g') + + See also, sendintr() and sendeof(). + ''' + char = char.lower() + a = ord(char) + if 97 <= a <= 122: + a = a - ord('a') + 1 + byte = _byte(a) + return self._writeb(byte), byte + d = {'@': 0, '`': 0, + '[': 27, '{': 27, + '\\': 28, '|': 28, + ']': 29, '}': 29, + '^': 30, '~': 30, + '_': 31, + '?': 127} + if char not in d: + return 0, b'' + + byte = _byte(d[char]) + return self._writeb(byte), byte + + def sendeof(self): + '''This sends an EOF to the child. This sends a character which causes + the pending parent output buffer to be sent to the waiting child + program without waiting for end-of-line. If it is the first character + of the line, the read() in the user program returns 0, which signifies + end-of-file. This means to work as expected a sendeof() has to be + called at the beginning of a line. This method does not send a newline. + It is the responsibility of the caller to ensure the eof is sent at the + beginning of a line. ''' + + return self._writeb(_EOF), _EOF + + def sendintr(self): + '''This sends a SIGINT to the child. It does not require + the SIGINT to be the first character on a line. ''' + + return self._writeb(_INTR), _INTR + + def eof(self): + '''This returns True if the EOF exception was ever raised. + ''' + + return self.flag_eof + + def terminate(self, force=False): + '''This forces a child process to terminate. It starts nicely with + SIGHUP and SIGINT. If "force" is True then moves onto SIGKILL. This + returns True if the child was terminated. This returns False if the + child could not be terminated. ''' + + if not self.isalive(): + return True + try: + self.kill(signal.SIGHUP) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + self.kill(signal.SIGCONT) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + self.kill(signal.SIGINT) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + if force: + self.kill(signal.SIGKILL) + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + else: + return False + return False + except OSError: + # I think there are kernel timing issues that sometimes cause + # this to happen. I think isalive() reports True, but the + # process is dead to the kernel. + # Make one last attempt to see if the kernel is up to date. + time.sleep(self.delayafterterminate) + if not self.isalive(): + return True + else: + return False + + def wait(self): + '''This waits until the child exits. This is a blocking call. This will + not read any data from the child, so this will block forever if the + child has unread output and has terminated. In other words, the child + may have printed output then called exit(), but, the child is + technically still alive until its output is read by the parent. ''' + + if self.isalive(): + pid, status = os.waitpid(self.pid, 0) + else: + return self.exitstatus + self.exitstatus = os.WEXITSTATUS(status) + if os.WIFEXITED(status): + self.status = status + self.exitstatus = os.WEXITSTATUS(status) + self.signalstatus = None + self.terminated = True + elif os.WIFSIGNALED(status): + self.status = status + self.exitstatus = None + self.signalstatus = os.WTERMSIG(status) + self.terminated = True + elif os.WIFSTOPPED(status): # pragma: no cover + # You can't call wait() on a child process in the stopped state. + raise PtyProcessError('Called wait() on a stopped child ' + + 'process. This is not supported. Is some other ' + + 'process attempting job control with our child pid?') + return self.exitstatus + + def isalive(self): + '''This tests if the child process is running or not. This is + non-blocking. If the child was terminated then this will read the + exitstatus or signalstatus of the child. This returns True if the child + process appears to be running or False if not. It can take literally + SECONDS for Solaris to return the right status. ''' + + if self.terminated: + return False + + if self.flag_eof: + # This is for Linux, which requires the blocking form + # of waitpid to get the status of a defunct process. + # This is super-lame. The flag_eof would have been set + # in read_nonblocking(), so this should be safe. + waitpid_options = 0 + else: + waitpid_options = os.WNOHANG + + try: + pid, status = os.waitpid(self.pid, waitpid_options) + except OSError as e: + # No child processes + if e.errno == errno.ECHILD: + raise PtyProcessError('isalive() encountered condition ' + + 'where "terminated" is 0, but there was no child ' + + 'process. Did someone else call waitpid() ' + + 'on our process?') + else: + raise + + # I have to do this twice for Solaris. + # I can't even believe that I figured this out... + # If waitpid() returns 0 it means that no child process + # wishes to report, and the value of status is undefined. + if pid == 0: + try: + ### os.WNOHANG) # Solaris! + pid, status = os.waitpid(self.pid, waitpid_options) + except OSError as e: # pragma: no cover + # This should never happen... + if e.errno == errno.ECHILD: + raise PtyProcessError('isalive() encountered condition ' + + 'that should never happen. There was no child ' + + 'process. Did someone else call waitpid() ' + + 'on our process?') + else: + raise + + # If pid is still 0 after two calls to waitpid() then the process + # really is alive. This seems to work on all platforms, except for + # Irix which seems to require a blocking call on waitpid or select, + # so I let read_nonblocking take care of this situation + # (unfortunately, this requires waiting through the timeout). + if pid == 0: + return True + + if pid == 0: + return True + + if os.WIFEXITED(status): + self.status = status + self.exitstatus = os.WEXITSTATUS(status) + self.signalstatus = None + self.terminated = True + elif os.WIFSIGNALED(status): + self.status = status + self.exitstatus = None + self.signalstatus = os.WTERMSIG(status) + self.terminated = True + elif os.WIFSTOPPED(status): + raise PtyProcessError('isalive() encountered condition ' + + 'where child process is stopped. This is not ' + + 'supported. Is some other process attempting ' + + 'job control with our child pid?') + return False + + def kill(self, sig): + """Send the given signal to the child application. + + In keeping with UNIX tradition it has a misleading name. It does not + necessarily kill the child unless you send the right signal. See the + :mod:`signal` module for constants representing signal numbers. + """ + + # Same as os.kill, but the pid is given for you. + if self.isalive(): + os.kill(self.pid, sig) + + def getwinsize(self): + """Return the window size of the pseudoterminal as a tuple (rows, cols). + """ + TIOCGWINSZ = getattr(termios, 'TIOCGWINSZ', 1074295912) + s = struct.pack('HHHH', 0, 0, 0, 0) + x = fcntl.ioctl(self.fd, TIOCGWINSZ, s) + return struct.unpack('HHHH', x)[0:2] + + def setwinsize(self, rows, cols): + """Set the terminal window size of the child tty. + + This will cause a SIGWINCH signal to be sent to the child. This does not + change the physical window size. It changes the size reported to + TTY-aware applications like vi or curses -- applications that respond to + the SIGWINCH signal. + """ + return _setwinsize(self.fd, rows, cols) + + +class PtyProcessUnicode(PtyProcess): + """Unicode wrapper around a process running in a pseudoterminal. + + This class exposes a similar interface to :class:`PtyProcess`, but its read + methods return unicode, and its :meth:`write` accepts unicode. + """ + if PY3: + string_type = str + else: + string_type = unicode # analysis:ignore + + def __init__(self, pid, fd, encoding='utf-8', codec_errors='strict'): + super(PtyProcessUnicode, self).__init__(pid, fd) + self.encoding = encoding + self.codec_errors = codec_errors + self.decoder = codecs.getincrementaldecoder(encoding)(errors=codec_errors) + + def read(self, size=1024): + """Read at most ``size`` bytes from the pty, return them as unicode. + + Can block if there is nothing to read. Raises :exc:`EOFError` if the + terminal was closed. + + The size argument still refers to bytes, not unicode code points. + """ + b = super(PtyProcessUnicode, self).read(size) + return self.decoder.decode(b, final=False) + + def readline(self): + """Read one line from the pseudoterminal, and return it as unicode. + + Can block if there is nothing to read. Raises :exc:`EOFError` if the + terminal was closed. + """ + b = super(PtyProcessUnicode, self).readline() + return self.decoder.decode(b, final=False) + + def write(self, s): + """Write the unicode string ``s`` to the pseudoterminal. + + Returns the number of bytes written. + """ + b = s.encode(self.encoding) + return super(PtyProcessUnicode, self).write(b) diff --git a/.venv/lib/python3.8/site-packages/ptyprocess/util.py b/.venv/lib/python3.8/site-packages/ptyprocess/util.py new file mode 100644 index 00000000..aadbd62c --- /dev/null +++ b/.venv/lib/python3.8/site-packages/ptyprocess/util.py @@ -0,0 +1,71 @@ +try: + from shutil import which # Python >= 3.3 +except ImportError: + import os, sys + + # This is copied from Python 3.4.1 + def which(cmd, mode=os.F_OK | os.X_OK, path=None): + """Given a command, mode, and a PATH string, return the path which + conforms to the given mode on the PATH, or None if there is no such + file. + + `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result + of os.environ.get("PATH"), or can be overridden with a custom search + path. + + """ + # Check that a given file can be accessed with the correct mode. + # Additionally check that `file` is not a directory, as on Windows + # directories pass the os.access check. + def _access_check(fn, mode): + return (os.path.exists(fn) and os.access(fn, mode) + and not os.path.isdir(fn)) + + # If we're given a path with a directory part, look it up directly rather + # than referring to PATH directories. This includes checking relative to the + # current directory, e.g. ./script + if os.path.dirname(cmd): + if _access_check(cmd, mode): + return cmd + return None + + if path is None: + path = os.environ.get("PATH", os.defpath) + if not path: + return None + path = path.split(os.pathsep) + + if sys.platform == "win32": + # The current directory takes precedence on Windows. + if not os.curdir in path: + path.insert(0, os.curdir) + + # PATHEXT is necessary to check on Windows. + pathext = os.environ.get("PATHEXT", "").split(os.pathsep) + # See if the given file matches any of the expected path extensions. + # This will allow us to short circuit when given "python.exe". + # If it does match, only test that one, otherwise we have to try + # others. + if any(cmd.lower().endswith(ext.lower()) for ext in pathext): + files = [cmd] + else: + files = [cmd + ext for ext in pathext] + else: + # On other platforms you don't have things like PATHEXT to tell you + # what file suffixes are executable, so just pass on cmd as-is. + files = [cmd] + + seen = set() + for dir in path: + normdir = os.path.normcase(dir) + if not normdir in seen: + seen.add(normdir) + for thefile in files: + name = os.path.join(dir, thefile) + if _access_check(name, mode): + return name + return None + + +class PtyProcessError(Exception): + """Generic error class for this package.""" diff --git a/.venv/lib/python3.8/site-packages/pygments/__init__.py b/.venv/lib/python3.8/site-packages/pygments/__init__.py new file mode 100644 index 00000000..e5a27839 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/__init__.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +""" + Pygments + ~~~~~~~~ + + Pygments is a syntax highlighting package written in Python. + + It is a generic syntax highlighter for general use in all kinds of software + such as forum systems, wikis or other applications that need to prettify + source code. Highlights are: + + * a wide range of common languages and markup formats is supported + * special attention is paid to details, increasing quality by a fair amount + * support for new languages and formats are added easily + * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image + formats that PIL supports, and ANSI sequences + * it is usable as a command-line tool and as a library + * ... and it highlights even Brainfuck! + + The `Pygments master branch`_ is installable with ``easy_install Pygments==dev``. + + .. _Pygments master branch: + https://github.com/pygments/pygments/archive/master.zip#egg=Pygments-dev + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +import sys +from io import StringIO, BytesIO + +__version__ = '2.7.3' +__docformat__ = 'restructuredtext' + +__all__ = ['lex', 'format', 'highlight'] + + +def lex(code, lexer): + """ + Lex ``code`` with ``lexer`` and return an iterable of tokens. + """ + try: + return lexer.get_tokens(code) + except TypeError as err: + if (isinstance(err.args[0], str) and + ('unbound method get_tokens' in err.args[0] or + 'missing 1 required positional argument' in err.args[0])): + raise TypeError('lex() argument must be a lexer instance, ' + 'not a class') + raise + + +def format(tokens, formatter, outfile=None): # pylint: disable=redefined-builtin + """ + Format a tokenlist ``tokens`` with the formatter ``formatter``. + + If ``outfile`` is given and a valid file object (an object + with a ``write`` method), the result will be written to it, otherwise + it is returned as a string. + """ + try: + if not outfile: + realoutfile = getattr(formatter, 'encoding', None) and BytesIO() or StringIO() + formatter.format(tokens, realoutfile) + return realoutfile.getvalue() + else: + formatter.format(tokens, outfile) + except TypeError as err: + if (isinstance(err.args[0], str) and + ('unbound method format' in err.args[0] or + 'missing 1 required positional argument' in err.args[0])): + raise TypeError('format() argument must be a formatter instance, ' + 'not a class') + raise + + +def highlight(code, lexer, formatter, outfile=None): + """ + Lex ``code`` with ``lexer`` and format it with the formatter ``formatter``. + + If ``outfile`` is given and a valid file object (an object + with a ``write`` method), the result will be written to it, otherwise + it is returned as a string. + """ + return format(lex(code, lexer), formatter, outfile) + diff --git a/.venv/lib/python3.8/site-packages/pygments/__main__.py b/.venv/lib/python3.8/site-packages/pygments/__main__.py new file mode 100644 index 00000000..8d2ea9e3 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/__main__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" + pygments.__main__ + ~~~~~~~~~~~~~~~~~ + + Main entry point for ``python -m pygments``. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import sys +import pygments.cmdline + +try: + sys.exit(pygments.cmdline.main(sys.argv)) +except KeyboardInterrupt: + sys.exit(1) diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/__init__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 00000000..5a5e569e Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/__init__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/__main__.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/__main__.cpython-38.pyc new file mode 100644 index 00000000..60ae29f9 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/__main__.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/cmdline.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/cmdline.cpython-38.pyc new file mode 100644 index 00000000..4a9890af Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/cmdline.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/console.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/console.cpython-38.pyc new file mode 100644 index 00000000..b23b0f11 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/console.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/filter.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/filter.cpython-38.pyc new file mode 100644 index 00000000..3be10a49 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/filter.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/formatter.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/formatter.cpython-38.pyc new file mode 100644 index 00000000..58f98083 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/formatter.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/lexer.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/lexer.cpython-38.pyc new file mode 100644 index 00000000..ceb2409b Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/lexer.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/modeline.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/modeline.cpython-38.pyc new file mode 100644 index 00000000..86b159fb Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/modeline.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/plugin.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/plugin.cpython-38.pyc new file mode 100644 index 00000000..b34bcc1d Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/plugin.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/regexopt.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/regexopt.cpython-38.pyc new file mode 100644 index 00000000..dca6bc91 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/regexopt.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/scanner.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/scanner.cpython-38.pyc new file mode 100644 index 00000000..d690b25a Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/scanner.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/sphinxext.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/sphinxext.cpython-38.pyc new file mode 100644 index 00000000..0885983f Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/sphinxext.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/style.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/style.cpython-38.pyc new file mode 100644 index 00000000..7c4607f6 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/style.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/token.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/token.cpython-38.pyc new file mode 100644 index 00000000..04337d62 Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/token.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/unistring.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/unistring.cpython-38.pyc new file mode 100644 index 00000000..6bcb8d3d Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/unistring.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/__pycache__/util.cpython-38.pyc b/.venv/lib/python3.8/site-packages/pygments/__pycache__/util.cpython-38.pyc new file mode 100644 index 00000000..d823a6ad Binary files /dev/null and b/.venv/lib/python3.8/site-packages/pygments/__pycache__/util.cpython-38.pyc differ diff --git a/.venv/lib/python3.8/site-packages/pygments/cmdline.py b/.venv/lib/python3.8/site-packages/pygments/cmdline.py new file mode 100644 index 00000000..457af34d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/cmdline.py @@ -0,0 +1,582 @@ +# -*- coding: utf-8 -*- +""" + pygments.cmdline + ~~~~~~~~~~~~~~~~ + + Command line interface. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import os +import sys +import getopt +from textwrap import dedent + +from pygments import __version__, highlight +from pygments.util import ClassNotFound, OptionError, docstring_headline, \ + guess_decode, guess_decode_from_terminal, terminal_encoding, \ + UnclosingTextIOWrapper +from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \ + load_lexer_from_file, get_lexer_for_filename, find_lexer_class_for_filename +from pygments.lexers.special import TextLexer +from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter +from pygments.formatters import get_all_formatters, get_formatter_by_name, \ + load_formatter_from_file, get_formatter_for_filename, find_formatter_class +from pygments.formatters.terminal import TerminalFormatter +from pygments.formatters.terminal256 import Terminal256Formatter +from pygments.filters import get_all_filters, find_filter_class +from pygments.styles import get_all_styles, get_style_by_name + + +USAGE = """\ +Usage: %s [-l | -g] [-F [:]] [-f ] + [-O ] [-P ] [-s] [-v] [-x] [-o ] [] + + %s -S + + +

%(title)s

+ +''' + +DOC_HEADER_EXTERNALCSS = '''\ + + + + + %(title)s + + + + +

%(title)s

+ +''' + +DOC_FOOTER = '''\ + + +''' + + +class HtmlFormatter(Formatter): + r""" + Format tokens as HTML 4 ```` tags within a ``
`` tag, wrapped
+    in a ``
`` tag. The ``
``'s CSS class can be set by the `cssclass` + option. + + If the `linenos` option is set to ``"table"``, the ``
`` is
+    additionally wrapped inside a ```` which has one row and two
+    cells: one containing the line numbers and one containing the code.
+    Example:
+
+    .. sourcecode:: html
+
+        
+
+ + +
+
1
+            2
+
+
def foo(bar):
+              pass
+            
+
+ + (whitespace added to improve clarity). + + Wrapping can be disabled using the `nowrap` option. + + A list of lines can be specified using the `hl_lines` option to make these + lines highlighted (as of Pygments 0.11). + + With the `full` option, a complete HTML 4 document is output, including + the style definitions inside a ``$)', _handle_cssblock), + + include('keywords'), + include('inline'), + ], + 'keywords': [ + (words(( + '\\define', '\\end', 'caption', 'created', 'modified', 'tags', + 'title', 'type'), prefix=r'^', suffix=r'\b'), + Keyword), + ], + 'inline': [ + # escape + (r'\\.', Text), + # created or modified date + (r'\d{17}', Number.Integer), + # italics + (r'(\s)(//[^/]+//)((?=\W|\n))', + bygroups(Text, Generic.Emph, Text)), + # superscript + (r'(\s)(\^\^[^\^]+\^\^)', bygroups(Text, Generic.Emph)), + # subscript + (r'(\s)(,,[^,]+,,)', bygroups(Text, Generic.Emph)), + # underscore + (r'(\s)(__[^_]+__)', bygroups(Text, Generic.Strong)), + # bold + (r"(\s)(''[^']+'')((?=\W|\n))", + bygroups(Text, Generic.Strong, Text)), + # strikethrough + (r'(\s)(~~[^~]+~~)((?=\W|\n))', + bygroups(Text, Generic.Deleted, Text)), + # TiddlyWiki variables + (r'<<[^>]+>>', Name.Tag), + (r'\$\$[^$]+\$\$', Name.Tag), + (r'\$\([^)]+\)\$', Name.Tag), + # TiddlyWiki style or class + (r'^@@.*$', Name.Tag), + # HTML tags + (r']+>', Name.Tag), + # inline code + (r'`[^`]+`', String.Backtick), + # HTML escaped symbols + (r'&\S*?;', String.Regex), + # Wiki links + (r'(\[{2})([^]\|]+)(\]{2})', bygroups(Text, Name.Tag, Text)), + # External links + (r'(\[{2})([^]\|]+)(\|)([^]\|]+)(\]{2})', + bygroups(Text, Name.Tag, Text, Name.Attribute, Text)), + # Transclusion + (r'(\{{2})([^}]+)(\}{2})', bygroups(Text, Name.Tag, Text)), + # URLs + (r'(\b.?.?tps?://[^\s"]+)', bygroups(Name.Attribute)), + + # general text, must come last! + (r'[\w]+', Text), + (r'.', Text) + ], + } + + def __init__(self, **options): + self.handlecodeblocks = get_bool_opt(options, 'handlecodeblocks', True) + RegexLexer.__init__(self, **options) diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/math.py b/.venv/lib/python3.8/site-packages/pygments/lexers/math.py new file mode 100644 index 00000000..a4493f74 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/math.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.math + ~~~~~~~~~~~~~~~~~~~~ + + Just export lexers that were contained in this module. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexers.python import NumPyLexer +from pygments.lexers.matlab import MatlabLexer, MatlabSessionLexer, \ + OctaveLexer, ScilabLexer +from pygments.lexers.julia import JuliaLexer, JuliaConsoleLexer +from pygments.lexers.r import RConsoleLexer, SLexer, RdLexer +from pygments.lexers.modeling import BugsLexer, JagsLexer, StanLexer +from pygments.lexers.idl import IDLLexer +from pygments.lexers.algebra import MuPADLexer + +__all__ = [] diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/matlab.py b/.venv/lib/python3.8/site-packages/pygments/lexers/matlab.py new file mode 100644 index 00000000..72203113 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/matlab.py @@ -0,0 +1,720 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.matlab + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Matlab and related languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import Lexer, RegexLexer, bygroups, default, words, \ + do_insertions +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Generic, Whitespace + +from pygments.lexers import _scilab_builtins + +__all__ = ['MatlabLexer', 'MatlabSessionLexer', 'OctaveLexer', 'ScilabLexer'] + + +class MatlabLexer(RegexLexer): + """ + For Matlab source code. + + .. versionadded:: 0.10 + """ + name = 'Matlab' + aliases = ['matlab'] + filenames = ['*.m'] + mimetypes = ['text/matlab'] + + # + # These lists are generated automatically. + # Run the following in bash shell: + # + # for f in elfun specfun elmat; do + # echo -n "$f = " + # matlab -nojvm -r "help $f;exit;" | perl -ne \ + # 'push(@c,$1) if /^ (\w+)\s+-/; END {print q{["}.join(q{","},@c).qq{"]\n};}' + # done + # + # elfun: Elementary math functions + # specfun: Special Math functions + # elmat: Elementary matrices and matrix manipulation + # + # taken from Matlab version 9.4 (R2018a) + # + elfun = ("sin", "sind", "sinh", "asin", "asind", "asinh", "cos", "cosd", "cosh", + "acos", "acosd", "acosh", "tan", "tand", "tanh", "atan", "atand", "atan2", + "atan2d", "atanh", "sec", "secd", "sech", "asec", "asecd", "asech", "csc", "cscd", + "csch", "acsc", "acscd", "acsch", "cot", "cotd", "coth", "acot", "acotd", + "acoth", "hypot", "deg2rad", "rad2deg", "exp", "expm1", "log", "log1p", "log10", "log2", "pow2", + "realpow", "reallog", "realsqrt", "sqrt", "nthroot", "nextpow2", "abs", + "angle", "complex", "conj", "imag", "real", "unwrap", "isreal", "cplxpair", + "fix", "floor", "ceil", "round", "mod", "rem", "sign") + specfun = ("airy", "besselj", "bessely", "besselh", "besseli", "besselk", "beta", + "betainc", "betaincinv", "betaln", "ellipj", "ellipke", "erf", "erfc", "erfcx", + "erfinv", "erfcinv", "expint", "gamma", "gammainc", "gammaincinv", "gammaln", "psi", "legendre", + "cross", "dot", "factor", "isprime", "primes", "gcd", "lcm", "rat", + "rats", "perms", "nchoosek", "factorial", "cart2sph", "cart2pol", + "pol2cart", "sph2cart", "hsv2rgb", "rgb2hsv") + elmat = ("zeros", "ones", "eye", "repmat", "repelem", "linspace", "logspace", + "freqspace", "meshgrid", "accumarray", "size", "length", "ndims", "numel", + "disp", "isempty", "isequal", "isequaln", "cat", "reshape", + "diag", "blkdiag", "tril", "triu", "fliplr", "flipud", "flip", "rot90", + "find", "end", "sub2ind", "ind2sub", "bsxfun", "ndgrid", "permute", + "ipermute", "shiftdim", "circshift", "squeeze", "isscalar", "isvector", + "isrow", "iscolumn", "ismatrix", "eps", "realmax", "realmin", "intmax", "intmin", "flintmax", "pi", "i", "inf", "nan", "isnan", + "isinf", "isfinite", "j", "true", "false", "compan", "gallery", "hadamard", "hankel", + "hilb", "invhilb", "magic", "pascal", "rosser", "toeplitz", "vander", + "wilkinson") + + _operators = r'-|==|~=|<=|>=|<|>|&&|&|~|\|\|?|\.\*|\*|\+|\.\^|\.\\|\./|/|\\' + + tokens = { + 'root': [ + # line starting with '!' is sent as a system command. not sure what + # label to use... + (r'^!.*', String.Other), + (r'%\{\s*\n', Comment.Multiline, 'blockcomment'), + (r'%.*$', Comment), + (r'^\s*function\b', Keyword, 'deffunc'), + + # from 'iskeyword' on version 9.4 (R2018a): + # Check that there is no preceding dot, as keywords are valid field + # names. + (words(('break', 'case', 'catch', 'classdef', 'continue', 'else', + 'elseif', 'end', 'for', 'function', + 'global', 'if', 'otherwise', 'parfor', + 'persistent', 'return', 'spmd', 'switch', + 'try', 'while'), + prefix=r'(?. + + .. versionadded:: 0.10 + """ + name = 'Matlab session' + aliases = ['matlabsession'] + + def get_tokens_unprocessed(self, text): + mlexer = MatlabLexer(**self.options) + + curcode = '' + insertions = [] + continuation = False + + for match in line_re.finditer(text): + line = match.group() + + if line.startswith('>> '): + insertions.append((len(curcode), + [(0, Generic.Prompt, line[:3])])) + curcode += line[3:] + + elif line.startswith('>>'): + insertions.append((len(curcode), + [(0, Generic.Prompt, line[:2])])) + curcode += line[2:] + + elif line.startswith('???'): + + idx = len(curcode) + + # without is showing error on same line as before...? + # line = "\n" + line + token = (0, Generic.Traceback, line) + insertions.append((idx, [token])) + elif continuation: + # line_start is the length of the most recent prompt symbol + line_start = len(insertions[-1][-1][-1]) + # Set leading spaces with the length of the prompt to be a generic prompt + # This keeps code aligned when prompts are removed, say with some Javascript + if line.startswith(' '*line_start): + insertions.append((len(curcode), + [(0, Generic.Prompt, line[:line_start])])) + curcode += line[line_start:] + else: + curcode += line + else: + if curcode: + yield from do_insertions( + insertions, mlexer.get_tokens_unprocessed(curcode)) + curcode = '' + insertions = [] + + yield match.start(), Generic.Output, line + + # Does not allow continuation if a comment is included after the ellipses. + # Continues any line that ends with ..., even comments (lines that start with %) + if line.strip().endswith('...'): + continuation = True + else: + continuation = False + + if curcode: # or item: + yield from do_insertions( + insertions, mlexer.get_tokens_unprocessed(curcode)) + + +class OctaveLexer(RegexLexer): + """ + For GNU Octave source code. + + .. versionadded:: 1.5 + """ + name = 'Octave' + aliases = ['octave'] + filenames = ['*.m'] + mimetypes = ['text/octave'] + + # These lists are generated automatically. + # Run the following in bash shell: + # + # First dump all of the Octave manual into a plain text file: + # + # $ info octave --subnodes -o octave-manual + # + # Now grep through it: + + # for i in \ + # "Built-in Function" "Command" "Function File" \ + # "Loadable Function" "Mapping Function"; + # do + # perl -e '@name = qw('"$i"'); + # print lc($name[0]),"_kw = [\n"'; + # + # perl -n -e 'print "\"$1\",\n" if /-- '"$i"': .* (\w*) \(/;' \ + # octave-manual | sort | uniq ; + # echo "]" ; + # echo; + # done + + # taken from Octave Mercurial changeset 8cc154f45e37 (30-jan-2011) + + builtin_kw = ( + "addlistener", "addpath", "addproperty", "all", + "and", "any", "argnames", "argv", "assignin", + "atexit", "autoload", + "available_graphics_toolkits", "beep_on_error", + "bitand", "bitmax", "bitor", "bitshift", "bitxor", + "cat", "cell", "cellstr", "char", "class", "clc", + "columns", "command_line_path", + "completion_append_char", "completion_matches", + "complex", "confirm_recursive_rmdir", "cputime", + "crash_dumps_octave_core", "ctranspose", "cumprod", + "cumsum", "debug_on_error", "debug_on_interrupt", + "debug_on_warning", "default_save_options", + "dellistener", "diag", "diff", "disp", + "doc_cache_file", "do_string_escapes", "double", + "drawnow", "e", "echo_executing_commands", "eps", + "eq", "errno", "errno_list", "error", "eval", + "evalin", "exec", "exist", "exit", "eye", "false", + "fclear", "fclose", "fcntl", "fdisp", "feof", + "ferror", "feval", "fflush", "fgetl", "fgets", + "fieldnames", "file_in_loadpath", "file_in_path", + "filemarker", "filesep", "find_dir_in_path", + "fixed_point_format", "fnmatch", "fopen", "fork", + "formula", "fprintf", "fputs", "fread", "freport", + "frewind", "fscanf", "fseek", "fskipl", "ftell", + "functions", "fwrite", "ge", "genpath", "get", + "getegid", "getenv", "geteuid", "getgid", + "getpgrp", "getpid", "getppid", "getuid", "glob", + "gt", "gui_mode", "history_control", + "history_file", "history_size", + "history_timestamp_format_string", "home", + "horzcat", "hypot", "ifelse", + "ignore_function_time_stamp", "inferiorto", + "info_file", "info_program", "inline", "input", + "intmax", "intmin", "ipermute", + "is_absolute_filename", "isargout", "isbool", + "iscell", "iscellstr", "ischar", "iscomplex", + "isempty", "isfield", "isfloat", "isglobal", + "ishandle", "isieee", "isindex", "isinteger", + "islogical", "ismatrix", "ismethod", "isnull", + "isnumeric", "isobject", "isreal", + "is_rooted_relative_filename", "issorted", + "isstruct", "isvarname", "kbhit", "keyboard", + "kill", "lasterr", "lasterror", "lastwarn", + "ldivide", "le", "length", "link", "linspace", + "logical", "lstat", "lt", "make_absolute_filename", + "makeinfo_program", "max_recursion_depth", "merge", + "methods", "mfilename", "minus", "mislocked", + "mkdir", "mkfifo", "mkstemp", "mldivide", "mlock", + "mouse_wheel_zoom", "mpower", "mrdivide", "mtimes", + "munlock", "nargin", "nargout", + "native_float_format", "ndims", "ne", "nfields", + "nnz", "norm", "not", "numel", "nzmax", + "octave_config_info", "octave_core_file_limit", + "octave_core_file_name", + "octave_core_file_options", "ones", "or", + "output_max_field_width", "output_precision", + "page_output_immediately", "page_screen_output", + "path", "pathsep", "pause", "pclose", "permute", + "pi", "pipe", "plus", "popen", "power", + "print_empty_dimensions", "printf", + "print_struct_array_contents", "prod", + "program_invocation_name", "program_name", + "putenv", "puts", "pwd", "quit", "rats", "rdivide", + "readdir", "readlink", "read_readline_init_file", + "realmax", "realmin", "rehash", "rename", + "repelems", "re_read_readline_init_file", "reset", + "reshape", "resize", "restoredefaultpath", + "rethrow", "rmdir", "rmfield", "rmpath", "rows", + "save_header_format_string", "save_precision", + "saving_history", "scanf", "set", "setenv", + "shell_cmd", "sighup_dumps_octave_core", + "sigterm_dumps_octave_core", "silent_functions", + "single", "size", "size_equal", "sizemax", + "sizeof", "sleep", "source", "sparse_auto_mutate", + "split_long_rows", "sprintf", "squeeze", "sscanf", + "stat", "stderr", "stdin", "stdout", "strcmp", + "strcmpi", "string_fill_char", "strncmp", + "strncmpi", "struct", "struct_levels_to_print", + "strvcat", "subsasgn", "subsref", "sum", "sumsq", + "superiorto", "suppress_verbose_help_message", + "symlink", "system", "tic", "tilde_expand", + "times", "tmpfile", "tmpnam", "toc", "toupper", + "transpose", "true", "typeinfo", "umask", "uminus", + "uname", "undo_string_escapes", "unlink", "uplus", + "upper", "usage", "usleep", "vec", "vectorize", + "vertcat", "waitpid", "warning", "warranty", + "whos_line_format", "yes_or_no", "zeros", + "inf", "Inf", "nan", "NaN") + + command_kw = ("close", "load", "who", "whos") + + function_kw = ( + "accumarray", "accumdim", "acosd", "acotd", + "acscd", "addtodate", "allchild", "ancestor", + "anova", "arch_fit", "arch_rnd", "arch_test", + "area", "arma_rnd", "arrayfun", "ascii", "asctime", + "asecd", "asind", "assert", "atand", + "autoreg_matrix", "autumn", "axes", "axis", "bar", + "barh", "bartlett", "bartlett_test", "beep", + "betacdf", "betainv", "betapdf", "betarnd", + "bicgstab", "bicubic", "binary", "binocdf", + "binoinv", "binopdf", "binornd", "bitcmp", + "bitget", "bitset", "blackman", "blanks", + "blkdiag", "bone", "box", "brighten", "calendar", + "cast", "cauchy_cdf", "cauchy_inv", "cauchy_pdf", + "cauchy_rnd", "caxis", "celldisp", "center", "cgs", + "chisquare_test_homogeneity", + "chisquare_test_independence", "circshift", "cla", + "clabel", "clf", "clock", "cloglog", "closereq", + "colon", "colorbar", "colormap", "colperm", + "comet", "common_size", "commutation_matrix", + "compan", "compare_versions", "compass", + "computer", "cond", "condest", "contour", + "contourc", "contourf", "contrast", "conv", + "convhull", "cool", "copper", "copyfile", "cor", + "corrcoef", "cor_test", "cosd", "cotd", "cov", + "cplxpair", "cross", "cscd", "cstrcat", "csvread", + "csvwrite", "ctime", "cumtrapz", "curl", "cut", + "cylinder", "date", "datenum", "datestr", + "datetick", "datevec", "dblquad", "deal", + "deblank", "deconv", "delaunay", "delaunayn", + "delete", "demo", "detrend", "diffpara", "diffuse", + "dir", "discrete_cdf", "discrete_inv", + "discrete_pdf", "discrete_rnd", "display", + "divergence", "dlmwrite", "dos", "dsearch", + "dsearchn", "duplication_matrix", "durbinlevinson", + "ellipsoid", "empirical_cdf", "empirical_inv", + "empirical_pdf", "empirical_rnd", "eomday", + "errorbar", "etime", "etreeplot", "example", + "expcdf", "expinv", "expm", "exppdf", "exprnd", + "ezcontour", "ezcontourf", "ezmesh", "ezmeshc", + "ezplot", "ezpolar", "ezsurf", "ezsurfc", "factor", + "factorial", "fail", "fcdf", "feather", "fftconv", + "fftfilt", "fftshift", "figure", "fileattrib", + "fileparts", "fill", "findall", "findobj", + "findstr", "finv", "flag", "flipdim", "fliplr", + "flipud", "fpdf", "fplot", "fractdiff", "freqz", + "freqz_plot", "frnd", "fsolve", + "f_test_regression", "ftp", "fullfile", "fzero", + "gamcdf", "gaminv", "gampdf", "gamrnd", "gca", + "gcbf", "gcbo", "gcf", "genvarname", "geocdf", + "geoinv", "geopdf", "geornd", "getfield", "ginput", + "glpk", "gls", "gplot", "gradient", + "graphics_toolkit", "gray", "grid", "griddata", + "griddatan", "gtext", "gunzip", "gzip", "hadamard", + "hamming", "hankel", "hanning", "hggroup", + "hidden", "hilb", "hist", "histc", "hold", "hot", + "hotelling_test", "housh", "hsv", "hurst", + "hygecdf", "hygeinv", "hygepdf", "hygernd", + "idivide", "ifftshift", "image", "imagesc", + "imfinfo", "imread", "imshow", "imwrite", "index", + "info", "inpolygon", "inputname", "interpft", + "interpn", "intersect", "invhilb", "iqr", "isa", + "isdefinite", "isdir", "is_duplicate_entry", + "isequal", "isequalwithequalnans", "isfigure", + "ishermitian", "ishghandle", "is_leap_year", + "isletter", "ismac", "ismember", "ispc", "isprime", + "isprop", "isscalar", "issquare", "isstrprop", + "issymmetric", "isunix", "is_valid_file_id", + "isvector", "jet", "kendall", + "kolmogorov_smirnov_cdf", + "kolmogorov_smirnov_test", "kruskal_wallis_test", + "krylov", "kurtosis", "laplace_cdf", "laplace_inv", + "laplace_pdf", "laplace_rnd", "legend", "legendre", + "license", "line", "linkprop", "list_primes", + "loadaudio", "loadobj", "logistic_cdf", + "logistic_inv", "logistic_pdf", "logistic_rnd", + "logit", "loglog", "loglogerr", "logm", "logncdf", + "logninv", "lognpdf", "lognrnd", "logspace", + "lookfor", "ls_command", "lsqnonneg", "magic", + "mahalanobis", "manova", "matlabroot", + "mcnemar_test", "mean", "meansq", "median", "menu", + "mesh", "meshc", "meshgrid", "meshz", "mexext", + "mget", "mkpp", "mode", "moment", "movefile", + "mpoles", "mput", "namelengthmax", "nargchk", + "nargoutchk", "nbincdf", "nbininv", "nbinpdf", + "nbinrnd", "nchoosek", "ndgrid", "newplot", "news", + "nonzeros", "normcdf", "normest", "norminv", + "normpdf", "normrnd", "now", "nthroot", "null", + "ocean", "ols", "onenormest", "optimget", + "optimset", "orderfields", "orient", "orth", + "pack", "pareto", "parseparams", "pascal", "patch", + "pathdef", "pcg", "pchip", "pcolor", "pcr", + "peaks", "periodogram", "perl", "perms", "pie", + "pink", "planerot", "playaudio", "plot", + "plotmatrix", "plotyy", "poisscdf", "poissinv", + "poisspdf", "poissrnd", "polar", "poly", + "polyaffine", "polyarea", "polyderiv", "polyfit", + "polygcd", "polyint", "polyout", "polyreduce", + "polyval", "polyvalm", "postpad", "powerset", + "ppder", "ppint", "ppjumps", "ppplot", "ppval", + "pqpnonneg", "prepad", "primes", "print", + "print_usage", "prism", "probit", "qp", "qqplot", + "quadcc", "quadgk", "quadl", "quadv", "quiver", + "qzhess", "rainbow", "randi", "range", "rank", + "ranks", "rat", "reallog", "realpow", "realsqrt", + "record", "rectangle_lw", "rectangle_sw", + "rectint", "refresh", "refreshdata", + "regexptranslate", "repmat", "residue", "ribbon", + "rindex", "roots", "rose", "rosser", "rotdim", + "rref", "run", "run_count", "rundemos", "run_test", + "runtests", "saveas", "saveaudio", "saveobj", + "savepath", "scatter", "secd", "semilogx", + "semilogxerr", "semilogy", "semilogyerr", + "setaudio", "setdiff", "setfield", "setxor", + "shading", "shift", "shiftdim", "sign_test", + "sinc", "sind", "sinetone", "sinewave", "skewness", + "slice", "sombrero", "sortrows", "spaugment", + "spconvert", "spdiags", "spearman", "spectral_adf", + "spectral_xdf", "specular", "speed", "spencer", + "speye", "spfun", "sphere", "spinmap", "spline", + "spones", "sprand", "sprandn", "sprandsym", + "spring", "spstats", "spy", "sqp", "stairs", + "statistics", "std", "stdnormal_cdf", + "stdnormal_inv", "stdnormal_pdf", "stdnormal_rnd", + "stem", "stft", "strcat", "strchr", "strjust", + "strmatch", "strread", "strsplit", "strtok", + "strtrim", "strtrunc", "structfun", "studentize", + "subplot", "subsindex", "subspace", "substr", + "substruct", "summer", "surf", "surface", "surfc", + "surfl", "surfnorm", "svds", "swapbytes", + "sylvester_matrix", "symvar", "synthesis", "table", + "tand", "tar", "tcdf", "tempdir", "tempname", + "test", "text", "textread", "textscan", "tinv", + "title", "toeplitz", "tpdf", "trace", "trapz", + "treelayout", "treeplot", "triangle_lw", + "triangle_sw", "tril", "trimesh", "triplequad", + "triplot", "trisurf", "triu", "trnd", "tsearchn", + "t_test", "t_test_regression", "type", "unidcdf", + "unidinv", "unidpdf", "unidrnd", "unifcdf", + "unifinv", "unifpdf", "unifrnd", "union", "unique", + "unix", "unmkpp", "unpack", "untabify", "untar", + "unwrap", "unzip", "u_test", "validatestring", + "vander", "var", "var_test", "vech", "ver", + "version", "view", "voronoi", "voronoin", + "waitforbuttonpress", "wavread", "wavwrite", + "wblcdf", "wblinv", "wblpdf", "wblrnd", "weekday", + "welch_test", "what", "white", "whitebg", + "wienrnd", "wilcoxon_test", "wilkinson", "winter", + "xlabel", "xlim", "ylabel", "yulewalker", "zip", + "zlabel", "z_test") + + loadable_kw = ( + "airy", "amd", "balance", "besselh", "besseli", + "besselj", "besselk", "bessely", "bitpack", + "bsxfun", "builtin", "ccolamd", "cellfun", + "cellslices", "chol", "choldelete", "cholinsert", + "cholinv", "cholshift", "cholupdate", "colamd", + "colloc", "convhulln", "convn", "csymamd", + "cummax", "cummin", "daspk", "daspk_options", + "dasrt", "dasrt_options", "dassl", "dassl_options", + "dbclear", "dbdown", "dbstack", "dbstatus", + "dbstop", "dbtype", "dbup", "dbwhere", "det", + "dlmread", "dmperm", "dot", "eig", "eigs", + "endgrent", "endpwent", "etree", "fft", "fftn", + "fftw", "filter", "find", "full", "gcd", + "getgrent", "getgrgid", "getgrnam", "getpwent", + "getpwnam", "getpwuid", "getrusage", "givens", + "gmtime", "gnuplot_binary", "hess", "ifft", + "ifftn", "inv", "isdebugmode", "issparse", "kron", + "localtime", "lookup", "lsode", "lsode_options", + "lu", "luinc", "luupdate", "matrix_type", "max", + "min", "mktime", "pinv", "qr", "qrdelete", + "qrinsert", "qrshift", "qrupdate", "quad", + "quad_options", "qz", "rand", "rande", "randg", + "randn", "randp", "randperm", "rcond", "regexp", + "regexpi", "regexprep", "schur", "setgrent", + "setpwent", "sort", "spalloc", "sparse", "spparms", + "sprank", "sqrtm", "strfind", "strftime", + "strptime", "strrep", "svd", "svd_driver", "syl", + "symamd", "symbfact", "symrcm", "time", "tsearch", + "typecast", "urlread", "urlwrite") + + mapping_kw = ( + "abs", "acos", "acosh", "acot", "acoth", "acsc", + "acsch", "angle", "arg", "asec", "asech", "asin", + "asinh", "atan", "atanh", "beta", "betainc", + "betaln", "bincoeff", "cbrt", "ceil", "conj", "cos", + "cosh", "cot", "coth", "csc", "csch", "erf", "erfc", + "erfcx", "erfinv", "exp", "finite", "fix", "floor", + "fmod", "gamma", "gammainc", "gammaln", "imag", + "isalnum", "isalpha", "isascii", "iscntrl", + "isdigit", "isfinite", "isgraph", "isinf", + "islower", "isna", "isnan", "isprint", "ispunct", + "isspace", "isupper", "isxdigit", "lcm", "lgamma", + "log", "lower", "mod", "real", "rem", "round", + "roundb", "sec", "sech", "sign", "sin", "sinh", + "sqrt", "tan", "tanh", "toascii", "tolower", "xor") + + builtin_consts = ( + "EDITOR", "EXEC_PATH", "I", "IMAGE_PATH", "NA", + "OCTAVE_HOME", "OCTAVE_VERSION", "PAGER", + "PAGER_FLAGS", "SEEK_CUR", "SEEK_END", "SEEK_SET", + "SIG", "S_ISBLK", "S_ISCHR", "S_ISDIR", "S_ISFIFO", + "S_ISLNK", "S_ISREG", "S_ISSOCK", "WCONTINUE", + "WCOREDUMP", "WEXITSTATUS", "WIFCONTINUED", + "WIFEXITED", "WIFSIGNALED", "WIFSTOPPED", "WNOHANG", + "WSTOPSIG", "WTERMSIG", "WUNTRACED") + + tokens = { + 'root': [ + # We should look into multiline comments + (r'[%#].*$', Comment), + (r'^\s*function\b', Keyword, 'deffunc'), + + # from 'iskeyword' on hg changeset 8cc154f45e37 + (words(( + '__FILE__', '__LINE__', 'break', 'case', 'catch', 'classdef', 'continue', 'do', 'else', + 'elseif', 'end', 'end_try_catch', 'end_unwind_protect', 'endclassdef', + 'endevents', 'endfor', 'endfunction', 'endif', 'endmethods', 'endproperties', + 'endswitch', 'endwhile', 'events', 'for', 'function', 'get', 'global', 'if', 'methods', + 'otherwise', 'persistent', 'properties', 'return', 'set', 'static', 'switch', 'try', + 'until', 'unwind_protect', 'unwind_protect_cleanup', 'while'), suffix=r'\b'), + Keyword), + + (words(builtin_kw + command_kw + function_kw + loadable_kw + mapping_kw, + suffix=r'\b'), Name.Builtin), + + (words(builtin_consts, suffix=r'\b'), Name.Constant), + + # operators in Octave but not Matlab: + (r'-=|!=|!|/=|--', Operator), + # operators: + (r'-|==|~=|<|>|<=|>=|&&|&|~|\|\|?', Operator), + # operators in Octave but not Matlab requiring escape for re: + (r'\*=|\+=|\^=|\/=|\\=|\*\*|\+\+|\.\*\*', Operator), + # operators requiring escape for re: + (r'\.\*|\*|\+|\.\^|\.\\|\.\/|\/|\\', Operator), + + + # punctuation: + (r'[\[\](){}:@.,]', Punctuation), + (r'=|:|;', Punctuation), + + (r'"[^"]*"', String), + + (r'(\d+\.\d*|\d*\.\d+)([eEf][+-]?[0-9]+)?', Number.Float), + (r'\d+[eEf][+-]?[0-9]+', Number.Float), + (r'\d+', Number.Integer), + + # quote can be transpose, instead of string: + # (not great, but handles common cases...) + (r'(?<=[\w)\].])\'+', Operator), + (r'(?|<=|>=|&&|&|~|\|\|?', Operator), + # operators requiring escape for re: + (r'\.\*|\*|\+|\.\^|\.\\|\.\/|\/|\\', Operator), + + # punctuation: + (r'[\[\](){}@.,=:;]', Punctuation), + + (r'"[^"]*"', String), + + # quote can be transpose, instead of string: + # (not great, but handles common cases...) + (r'(?<=[\w)\].])\'+', Operator), + (r'(?', '->', '#', + # Modules + ':>', + } + + nonid_reserved = {'(', ')', '[', ']', '{', '}', ',', ';', '...', '_'} + + alphanumid_re = r"[a-zA-Z][\w']*" + symbolicid_re = r"[!%&$#+\-/:<=>?@\\~`^|*]+" + + # A character constant is a sequence of the form #s, where s is a string + # constant denoting a string of size one character. This setup just parses + # the entire string as either a String.Double or a String.Char (depending + # on the argument), even if the String.Char is an erronous + # multiple-character string. + def stringy(whatkind): + return [ + (r'[^"\\]', whatkind), + (r'\\[\\"abtnvfr]', String.Escape), + # Control-character notation is used for codes < 32, + # where \^@ == \000 + (r'\\\^[\x40-\x5e]', String.Escape), + # Docs say 'decimal digits' + (r'\\[0-9]{3}', String.Escape), + (r'\\u[0-9a-fA-F]{4}', String.Escape), + (r'\\\s+\\', String.Interpol), + (r'"', whatkind, '#pop'), + ] + + # Callbacks for distinguishing tokens and reserved words + def long_id_callback(self, match): + if match.group(1) in self.alphanumid_reserved: + token = Error + else: + token = Name.Namespace + yield match.start(1), token, match.group(1) + yield match.start(2), Punctuation, match.group(2) + + def end_id_callback(self, match): + if match.group(1) in self.alphanumid_reserved: + token = Error + elif match.group(1) in self.symbolicid_reserved: + token = Error + else: + token = Name + yield match.start(1), token, match.group(1) + + def id_callback(self, match): + str = match.group(1) + if str in self.alphanumid_reserved: + token = Keyword.Reserved + elif str in self.symbolicid_reserved: + token = Punctuation + else: + token = Name + yield match.start(1), token, str + + tokens = { + # Whitespace and comments are (almost) everywhere + 'whitespace': [ + (r'\s+', Text), + (r'\(\*', Comment.Multiline, 'comment'), + ], + + 'delimiters': [ + # This lexer treats these delimiters specially: + # Delimiters define scopes, and the scope is how the meaning of + # the `|' is resolved - is it a case/handle expression, or function + # definition by cases? (This is not how the Definition works, but + # it's how MLton behaves, see http://mlton.org/SMLNJDeviations) + (r'\(|\[|\{', Punctuation, 'main'), + (r'\)|\]|\}', Punctuation, '#pop'), + (r'\b(let|if|local)\b(?!\')', Keyword.Reserved, ('main', 'main')), + (r'\b(struct|sig|while)\b(?!\')', Keyword.Reserved, 'main'), + (r'\b(do|else|end|in|then)\b(?!\')', Keyword.Reserved, '#pop'), + ], + + 'core': [ + # Punctuation that doesn't overlap symbolic identifiers + (r'(%s)' % '|'.join(re.escape(z) for z in nonid_reserved), + Punctuation), + + # Special constants: strings, floats, numbers in decimal and hex + (r'#"', String.Char, 'char'), + (r'"', String.Double, 'string'), + (r'~?0x[0-9a-fA-F]+', Number.Hex), + (r'0wx[0-9a-fA-F]+', Number.Hex), + (r'0w\d+', Number.Integer), + (r'~?\d+\.\d+[eE]~?\d+', Number.Float), + (r'~?\d+\.\d+', Number.Float), + (r'~?\d+[eE]~?\d+', Number.Float), + (r'~?\d+', Number.Integer), + + # Labels + (r'#\s*[1-9][0-9]*', Name.Label), + (r'#\s*(%s)' % alphanumid_re, Name.Label), + (r'#\s+(%s)' % symbolicid_re, Name.Label), + # Some reserved words trigger a special, local lexer state change + (r'\b(datatype|abstype)\b(?!\')', Keyword.Reserved, 'dname'), + (r'(?=\b(exception)\b(?!\'))', Text, ('ename')), + (r'\b(functor|include|open|signature|structure)\b(?!\')', + Keyword.Reserved, 'sname'), + (r'\b(type|eqtype)\b(?!\')', Keyword.Reserved, 'tname'), + + # Regular identifiers, long and otherwise + (r'\'[\w\']*', Name.Decorator), + (r'(%s)(\.)' % alphanumid_re, long_id_callback, "dotted"), + (r'(%s)' % alphanumid_re, id_callback), + (r'(%s)' % symbolicid_re, id_callback), + ], + 'dotted': [ + (r'(%s)(\.)' % alphanumid_re, long_id_callback), + (r'(%s)' % alphanumid_re, end_id_callback, "#pop"), + (r'(%s)' % symbolicid_re, end_id_callback, "#pop"), + (r'\s+', Error), + (r'\S+', Error), + ], + + + # Main parser (prevents errors in files that have scoping errors) + 'root': [ + default('main') + ], + + # In this scope, I expect '|' to not be followed by a function name, + # and I expect 'and' to be followed by a binding site + 'main': [ + include('whitespace'), + + # Special behavior of val/and/fun + (r'\b(val|and)\b(?!\')', Keyword.Reserved, 'vname'), + (r'\b(fun)\b(?!\')', Keyword.Reserved, + ('#pop', 'main-fun', 'fname')), + + include('delimiters'), + include('core'), + (r'\S+', Error), + ], + + # In this scope, I expect '|' and 'and' to be followed by a function + 'main-fun': [ + include('whitespace'), + + (r'\s', Text), + (r'\(\*', Comment.Multiline, 'comment'), + + # Special behavior of val/and/fun + (r'\b(fun|and)\b(?!\')', Keyword.Reserved, 'fname'), + (r'\b(val)\b(?!\')', Keyword.Reserved, + ('#pop', 'main', 'vname')), + + # Special behavior of '|' and '|'-manipulating keywords + (r'\|', Punctuation, 'fname'), + (r'\b(case|handle)\b(?!\')', Keyword.Reserved, + ('#pop', 'main')), + + include('delimiters'), + include('core'), + (r'\S+', Error), + ], + + # Character and string parsers + 'char': stringy(String.Char), + 'string': stringy(String.Double), + + 'breakout': [ + (r'(?=\b(%s)\b(?!\'))' % '|'.join(alphanumid_reserved), Text, '#pop'), + ], + + # Dealing with what comes after module system keywords + 'sname': [ + include('whitespace'), + include('breakout'), + + (r'(%s)' % alphanumid_re, Name.Namespace), + default('#pop'), + ], + + # Dealing with what comes after the 'fun' (or 'and' or '|') keyword + 'fname': [ + include('whitespace'), + (r'\'[\w\']*', Name.Decorator), + (r'\(', Punctuation, 'tyvarseq'), + + (r'(%s)' % alphanumid_re, Name.Function, '#pop'), + (r'(%s)' % symbolicid_re, Name.Function, '#pop'), + + # Ignore interesting function declarations like "fun (x + y) = ..." + default('#pop'), + ], + + # Dealing with what comes after the 'val' (or 'and') keyword + 'vname': [ + include('whitespace'), + (r'\'[\w\']*', Name.Decorator), + (r'\(', Punctuation, 'tyvarseq'), + + (r'(%s)(\s*)(=(?!%s))' % (alphanumid_re, symbolicid_re), + bygroups(Name.Variable, Text, Punctuation), '#pop'), + (r'(%s)(\s*)(=(?!%s))' % (symbolicid_re, symbolicid_re), + bygroups(Name.Variable, Text, Punctuation), '#pop'), + (r'(%s)' % alphanumid_re, Name.Variable, '#pop'), + (r'(%s)' % symbolicid_re, Name.Variable, '#pop'), + + # Ignore interesting patterns like 'val (x, y)' + default('#pop'), + ], + + # Dealing with what comes after the 'type' (or 'and') keyword + 'tname': [ + include('whitespace'), + include('breakout'), + + (r'\'[\w\']*', Name.Decorator), + (r'\(', Punctuation, 'tyvarseq'), + (r'=(?!%s)' % symbolicid_re, Punctuation, ('#pop', 'typbind')), + + (r'(%s)' % alphanumid_re, Keyword.Type), + (r'(%s)' % symbolicid_re, Keyword.Type), + (r'\S+', Error, '#pop'), + ], + + # A type binding includes most identifiers + 'typbind': [ + include('whitespace'), + + (r'\b(and)\b(?!\')', Keyword.Reserved, ('#pop', 'tname')), + + include('breakout'), + include('core'), + (r'\S+', Error, '#pop'), + ], + + # Dealing with what comes after the 'datatype' (or 'and') keyword + 'dname': [ + include('whitespace'), + include('breakout'), + + (r'\'[\w\']*', Name.Decorator), + (r'\(', Punctuation, 'tyvarseq'), + (r'(=)(\s*)(datatype)', + bygroups(Punctuation, Text, Keyword.Reserved), '#pop'), + (r'=(?!%s)' % symbolicid_re, Punctuation, + ('#pop', 'datbind', 'datcon')), + + (r'(%s)' % alphanumid_re, Keyword.Type), + (r'(%s)' % symbolicid_re, Keyword.Type), + (r'\S+', Error, '#pop'), + ], + + # common case - A | B | C of int + 'datbind': [ + include('whitespace'), + + (r'\b(and)\b(?!\')', Keyword.Reserved, ('#pop', 'dname')), + (r'\b(withtype)\b(?!\')', Keyword.Reserved, ('#pop', 'tname')), + (r'\b(of)\b(?!\')', Keyword.Reserved), + + (r'(\|)(\s*)(%s)' % alphanumid_re, + bygroups(Punctuation, Text, Name.Class)), + (r'(\|)(\s+)(%s)' % symbolicid_re, + bygroups(Punctuation, Text, Name.Class)), + + include('breakout'), + include('core'), + (r'\S+', Error), + ], + + # Dealing with what comes after an exception + 'ename': [ + include('whitespace'), + + (r'(exception|and)\b(\s+)(%s)' % alphanumid_re, + bygroups(Keyword.Reserved, Text, Name.Class)), + (r'(exception|and)\b(\s*)(%s)' % symbolicid_re, + bygroups(Keyword.Reserved, Text, Name.Class)), + (r'\b(of)\b(?!\')', Keyword.Reserved), + + include('breakout'), + include('core'), + (r'\S+', Error), + ], + + 'datcon': [ + include('whitespace'), + (r'(%s)' % alphanumid_re, Name.Class, '#pop'), + (r'(%s)' % symbolicid_re, Name.Class, '#pop'), + (r'\S+', Error, '#pop'), + ], + + # Series of type variables + 'tyvarseq': [ + (r'\s', Text), + (r'\(\*', Comment.Multiline, 'comment'), + + (r'\'[\w\']*', Name.Decorator), + (alphanumid_re, Name), + (r',', Punctuation), + (r'\)', Punctuation, '#pop'), + (symbolicid_re, Name), + ], + + 'comment': [ + (r'[^(*)]', Comment.Multiline), + (r'\(\*', Comment.Multiline, '#push'), + (r'\*\)', Comment.Multiline, '#pop'), + (r'[(*)]', Comment.Multiline), + ], + } + + +class OcamlLexer(RegexLexer): + """ + For the OCaml language. + + .. versionadded:: 0.7 + """ + + name = 'OCaml' + aliases = ['ocaml'] + filenames = ['*.ml', '*.mli', '*.mll', '*.mly'] + mimetypes = ['text/x-ocaml'] + + keywords = ( + 'as', 'assert', 'begin', 'class', 'constraint', 'do', 'done', + 'downto', 'else', 'end', 'exception', 'external', 'false', + 'for', 'fun', 'function', 'functor', 'if', 'in', 'include', + 'inherit', 'initializer', 'lazy', 'let', 'match', 'method', + 'module', 'mutable', 'new', 'object', 'of', 'open', 'private', + 'raise', 'rec', 'sig', 'struct', 'then', 'to', 'true', 'try', + 'type', 'value', 'val', 'virtual', 'when', 'while', 'with', + ) + keyopts = ( + '!=', '#', '&', '&&', r'\(', r'\)', r'\*', r'\+', ',', '-', + r'-\.', '->', r'\.', r'\.\.', ':', '::', ':=', ':>', ';', ';;', '<', + '<-', '=', '>', '>]', r'>\}', r'\?', r'\?\?', r'\[', r'\[<', r'\[>', + r'\[\|', ']', '_', '`', r'\{', r'\{<', r'\|', r'\|]', r'\}', '~' + ) + + operators = r'[!$%&*+\./:<=>?@^|~-]' + word_operators = ('and', 'asr', 'land', 'lor', 'lsl', 'lxor', 'mod', 'or') + prefix_syms = r'[!?~]' + infix_syms = r'[=<>@^|&+\*/$%-]' + primitives = ('unit', 'int', 'float', 'bool', 'string', 'char', 'list', 'array') + + tokens = { + 'escape-sequence': [ + (r'\\[\\"\'ntbr]', String.Escape), + (r'\\[0-9]{3}', String.Escape), + (r'\\x[0-9a-fA-F]{2}', String.Escape), + ], + 'root': [ + (r'\s+', Text), + (r'false|true|\(\)|\[\]', Name.Builtin.Pseudo), + (r'\b([A-Z][\w\']*)(?=\s*\.)', Name.Namespace, 'dotted'), + (r'\b([A-Z][\w\']*)', Name.Class), + (r'\(\*(?![)])', Comment, 'comment'), + (r'\b(%s)\b' % '|'.join(keywords), Keyword), + (r'(%s)' % '|'.join(keyopts[::-1]), Operator), + (r'(%s|%s)?%s' % (infix_syms, prefix_syms, operators), Operator), + (r'\b(%s)\b' % '|'.join(word_operators), Operator.Word), + (r'\b(%s)\b' % '|'.join(primitives), Keyword.Type), + + (r"[^\W\d][\w']*", Name), + + (r'-?\d[\d_]*(.[\d_]*)?([eE][+\-]?\d[\d_]*)', Number.Float), + (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex), + (r'0[oO][0-7][0-7_]*', Number.Oct), + (r'0[bB][01][01_]*', Number.Bin), + (r'\d[\d_]*', Number.Integer), + + (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2}))'", + String.Char), + (r"'.'", String.Char), + (r"'", Keyword), # a stray quote is another syntax element + + (r'"', String.Double, 'string'), + + (r'[~?][a-z][\w\']*:', Name.Variable), + ], + 'comment': [ + (r'[^(*)]+', Comment), + (r'\(\*', Comment, '#push'), + (r'\*\)', Comment, '#pop'), + (r'[(*)]', Comment), + ], + 'string': [ + (r'[^\\"]+', String.Double), + include('escape-sequence'), + (r'\\\n', String.Double), + (r'"', String.Double, '#pop'), + ], + 'dotted': [ + (r'\s+', Text), + (r'\.', Punctuation), + (r'[A-Z][\w\']*(?=\s*\.)', Name.Namespace), + (r'[A-Z][\w\']*', Name.Class, '#pop'), + (r'[a-z_][\w\']*', Name, '#pop'), + default('#pop'), + ], + } + +class OpaLexer(RegexLexer): + """ + Lexer for the Opa language (http://opalang.org). + + .. versionadded:: 1.5 + """ + + name = 'Opa' + aliases = ['opa'] + filenames = ['*.opa'] + mimetypes = ['text/x-opa'] + + # most of these aren't strictly keywords + # but if you color only real keywords, you might just + # as well not color anything + keywords = ( + 'and', 'as', 'begin', 'case', 'client', 'css', 'database', 'db', 'do', + 'else', 'end', 'external', 'forall', 'function', 'if', 'import', + 'match', 'module', 'or', 'package', 'parser', 'rec', 'server', 'then', + 'type', 'val', 'with', 'xml_parser', + ) + + # matches both stuff and `stuff` + ident_re = r'(([a-zA-Z_]\w*)|(`[^`]*`))' + + op_re = r'[.=\-<>,@~%/+?*&^!]' + punc_re = r'[()\[\],;|]' # '{' and '}' are treated elsewhere + # because they are also used for inserts + + tokens = { + # copied from the caml lexer, should be adapted + 'escape-sequence': [ + (r'\\[\\"\'ntr}]', String.Escape), + (r'\\[0-9]{3}', String.Escape), + (r'\\x[0-9a-fA-F]{2}', String.Escape), + ], + + # factorizing these rules, because they are inserted many times + 'comments': [ + (r'/\*', Comment, 'nested-comment'), + (r'//.*?$', Comment), + ], + 'comments-and-spaces': [ + include('comments'), + (r'\s+', Text), + ], + + 'root': [ + include('comments-and-spaces'), + # keywords + (words(keywords, prefix=r'\b', suffix=r'\b'), Keyword), + # directives + # we could parse the actual set of directives instead of anything + # starting with @, but this is troublesome + # because it needs to be adjusted all the time + # and assuming we parse only sources that compile, it is useless + (r'@' + ident_re + r'\b', Name.Builtin.Pseudo), + + # number literals + (r'-?.[\d]+([eE][+\-]?\d+)', Number.Float), + (r'-?\d+.\d*([eE][+\-]?\d+)', Number.Float), + (r'-?\d+[eE][+\-]?\d+', Number.Float), + (r'0[xX][\da-fA-F]+', Number.Hex), + (r'0[oO][0-7]+', Number.Oct), + (r'0[bB][01]+', Number.Bin), + (r'\d+', Number.Integer), + # color literals + (r'#[\da-fA-F]{3,6}', Number.Integer), + + # string literals + (r'"', String.Double, 'string'), + # char literal, should be checked because this is the regexp from + # the caml lexer + (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2})|.)'", + String.Char), + + # this is meant to deal with embedded exprs in strings + # every time we find a '}' we pop a state so that if we were + # inside a string, we are back in the string state + # as a consequence, we must also push a state every time we find a + # '{' or else we will have errors when parsing {} for instance + (r'\{', Operator, '#push'), + (r'\}', Operator, '#pop'), + + # html literals + # this is a much more strict that the actual parser, + # since a])', String.Single, 'html-open-tag'), + + # db path + # matching the '[_]' in '/a[_]' because it is a part + # of the syntax of the db path definition + # unfortunately, i don't know how to match the ']' in + # /a[1], so this is somewhat inconsistent + (r'[@?!]?(/\w+)+(\[_\])?', Name.Variable), + # putting the same color on <- as on db path, since + # it can be used only to mean Db.write + (r'<-(?!'+op_re+r')', Name.Variable), + + # 'modules' + # although modules are not distinguished by their names as in caml + # the standard library seems to follow the convention that modules + # only area capitalized + (r'\b([A-Z]\w*)(?=\.)', Name.Namespace), + + # operators + # = has a special role because this is the only + # way to syntactic distinguish binding constructions + # unfortunately, this colors the equal in {x=2} too + (r'=(?!'+op_re+r')', Keyword), + (r'(%s)+' % op_re, Operator), + (r'(%s)+' % punc_re, Operator), + + # coercions + (r':', Operator, 'type'), + # type variables + # we need this rule because we don't parse specially type + # definitions so in "type t('a) = ...", "'a" is parsed by 'root' + ("'"+ident_re, Keyword.Type), + + # id literal, #something, or #{expr} + (r'#'+ident_re, String.Single), + (r'#(?=\{)', String.Single), + + # identifiers + # this avoids to color '2' in 'a2' as an integer + (ident_re, Text), + + # default, not sure if that is needed or not + # (r'.', Text), + ], + + # it is quite painful to have to parse types to know where they end + # this is the general rule for a type + # a type is either: + # * -> ty + # * type-with-slash + # * type-with-slash -> ty + # * type-with-slash (, type-with-slash)+ -> ty + # + # the code is pretty funky in here, but this code would roughly + # translate in caml to: + # let rec type stream = + # match stream with + # | [< "->"; stream >] -> type stream + # | [< ""; stream >] -> + # type_with_slash stream + # type_lhs_1 stream; + # and type_1 stream = ... + 'type': [ + include('comments-and-spaces'), + (r'->', Keyword.Type), + default(('#pop', 'type-lhs-1', 'type-with-slash')), + ], + + # parses all the atomic or closed constructions in the syntax of type + # expressions: record types, tuple types, type constructors, basic type + # and type variables + 'type-1': [ + include('comments-and-spaces'), + (r'\(', Keyword.Type, ('#pop', 'type-tuple')), + (r'~?\{', Keyword.Type, ('#pop', 'type-record')), + (ident_re+r'\(', Keyword.Type, ('#pop', 'type-tuple')), + (ident_re, Keyword.Type, '#pop'), + ("'"+ident_re, Keyword.Type), + # this case is not in the syntax but sometimes + # we think we are parsing types when in fact we are parsing + # some css, so we just pop the states until we get back into + # the root state + default('#pop'), + ], + + # type-with-slash is either: + # * type-1 + # * type-1 (/ type-1)+ + 'type-with-slash': [ + include('comments-and-spaces'), + default(('#pop', 'slash-type-1', 'type-1')), + ], + 'slash-type-1': [ + include('comments-and-spaces'), + ('/', Keyword.Type, ('#pop', 'type-1')), + # same remark as above + default('#pop'), + ], + + # we go in this state after having parsed a type-with-slash + # while trying to parse a type + # and at this point we must determine if we are parsing an arrow + # type (in which case we must continue parsing) or not (in which + # case we stop) + 'type-lhs-1': [ + include('comments-and-spaces'), + (r'->', Keyword.Type, ('#pop', 'type')), + (r'(?=,)', Keyword.Type, ('#pop', 'type-arrow')), + default('#pop'), + ], + 'type-arrow': [ + include('comments-and-spaces'), + # the look ahead here allows to parse f(x : int, y : float -> truc) + # correctly + (r',(?=[^:]*?->)', Keyword.Type, 'type-with-slash'), + (r'->', Keyword.Type, ('#pop', 'type')), + # same remark as above + default('#pop'), + ], + + # no need to do precise parsing for tuples and records + # because they are closed constructions, so we can simply + # find the closing delimiter + # note that this function would be not work if the source + # contained identifiers like `{)` (although it could be patched + # to support it) + 'type-tuple': [ + include('comments-and-spaces'), + (r'[^()/*]+', Keyword.Type), + (r'[/*]', Keyword.Type), + (r'\(', Keyword.Type, '#push'), + (r'\)', Keyword.Type, '#pop'), + ], + 'type-record': [ + include('comments-and-spaces'), + (r'[^{}/*]+', Keyword.Type), + (r'[/*]', Keyword.Type), + (r'\{', Keyword.Type, '#push'), + (r'\}', Keyword.Type, '#pop'), + ], + + # 'type-tuple': [ + # include('comments-and-spaces'), + # (r'\)', Keyword.Type, '#pop'), + # default(('#pop', 'type-tuple-1', 'type-1')), + # ], + # 'type-tuple-1': [ + # include('comments-and-spaces'), + # (r',?\s*\)', Keyword.Type, '#pop'), # ,) is a valid end of tuple, in (1,) + # (r',', Keyword.Type, 'type-1'), + # ], + # 'type-record':[ + # include('comments-and-spaces'), + # (r'\}', Keyword.Type, '#pop'), + # (r'~?(?:\w+|`[^`]*`)', Keyword.Type, 'type-record-field-expr'), + # ], + # 'type-record-field-expr': [ + # + # ], + + 'nested-comment': [ + (r'[^/*]+', Comment), + (r'/\*', Comment, '#push'), + (r'\*/', Comment, '#pop'), + (r'[/*]', Comment), + ], + + # the copy pasting between string and single-string + # is kinda sad. Is there a way to avoid that?? + 'string': [ + (r'[^\\"{]+', String.Double), + (r'"', String.Double, '#pop'), + (r'\{', Operator, 'root'), + include('escape-sequence'), + ], + 'single-string': [ + (r'[^\\\'{]+', String.Double), + (r'\'', String.Double, '#pop'), + (r'\{', Operator, 'root'), + include('escape-sequence'), + ], + + # all the html stuff + # can't really reuse some existing html parser + # because we must be able to parse embedded expressions + + # we are in this state after someone parsed the '<' that + # started the html literal + 'html-open-tag': [ + (r'[\w\-:]+', String.Single, ('#pop', 'html-attr')), + (r'>', String.Single, ('#pop', 'html-content')), + ], + + # we are in this state after someone parsed the ' is allowed + (r'[\w\-:]*>', String.Single, '#pop'), + ], + + # we are in this state after having parsed '', String.Single, '#pop'), + (r'>', String.Single, ('#pop', 'html-content')), + ], + + 'html-attr-value': [ + (r"'", String.Single, ('#pop', 'single-string')), + (r'"', String.Single, ('#pop', 'string')), + (r'#'+ident_re, String.Single, '#pop'), + (r'#(?=\{)', String.Single, ('#pop', 'root')), + (r'[^"\'{`=<>]+', String.Single, '#pop'), + (r'\{', Operator, ('#pop', 'root')), # this is a tail call! + ], + + # we should probably deal with '\' escapes here + 'html-content': [ + (r'', Comment, '#pop'), + (r'[^\-]+|-', Comment), + ], + } + + +class ReasonLexer(RegexLexer): + """ + For the ReasonML language (https://reasonml.github.io/). + + .. versionadded:: 2.6 + """ + + name = 'ReasonML' + aliases = ['reason', "reasonml"] + filenames = ['*.re', '*.rei'] + mimetypes = ['text/x-reasonml'] + + keywords = ( + 'as', 'assert', 'begin', 'class', 'constraint', 'do', 'done', 'downto', + 'else', 'end', 'exception', 'external', 'false', 'for', 'fun', 'esfun', + 'function', 'functor', 'if', 'in', 'include', 'inherit', 'initializer', 'lazy', + 'let', 'switch', 'module', 'pub', 'mutable', 'new', 'nonrec', 'object', 'of', + 'open', 'pri', 'rec', 'sig', 'struct', 'then', 'to', 'true', 'try', + 'type', 'val', 'virtual', 'when', 'while', 'with', + ) + keyopts = ( + '!=', '#', '&', '&&', r'\(', r'\)', r'\*', r'\+', ',', '-', + r'-\.', '=>', r'\.', r'\.\.', r'\.\.\.', ':', '::', ':=', ':>', ';', ';;', '<', + '<-', '=', '>', '>]', r'>\}', r'\?', r'\?\?', r'\[', r'\[<', r'\[>', + r'\[\|', ']', '_', '`', r'\{', r'\{<', r'\|', r'\|\|', r'\|]', r'\}', '~' + ) + + operators = r'[!$%&*+\./:<=>?@^|~-]' + word_operators = ('and', 'asr', 'land', 'lor', 'lsl', 'lsr', 'lxor', 'mod', 'or') + prefix_syms = r'[!?~]' + infix_syms = r'[=<>@^|&+\*/$%-]' + primitives = ('unit', 'int', 'float', 'bool', 'string', 'char', 'list', 'array') + + tokens = { + 'escape-sequence': [ + (r'\\[\\"\'ntbr]', String.Escape), + (r'\\[0-9]{3}', String.Escape), + (r'\\x[0-9a-fA-F]{2}', String.Escape), + ], + 'root': [ + (r'\s+', Text), + (r'false|true|\(\)|\[\]', Name.Builtin.Pseudo), + (r'\b([A-Z][\w\']*)(?=\s*\.)', Name.Namespace, 'dotted'), + (r'\b([A-Z][\w\']*)', Name.Class), + (r'//.*?\n', Comment.Single), + (r'\/\*(?!/)', Comment.Multiline, 'comment'), + (r'\b(%s)\b' % '|'.join(keywords), Keyword), + (r'(%s)' % '|'.join(keyopts[::-1]), Operator.Word), + (r'(%s|%s)?%s' % (infix_syms, prefix_syms, operators), Operator), + (r'\b(%s)\b' % '|'.join(word_operators), Operator.Word), + (r'\b(%s)\b' % '|'.join(primitives), Keyword.Type), + + (r"[^\W\d][\w']*", Name), + + (r'-?\d[\d_]*(.[\d_]*)?([eE][+\-]?\d[\d_]*)', Number.Float), + (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex), + (r'0[oO][0-7][0-7_]*', Number.Oct), + (r'0[bB][01][01_]*', Number.Bin), + (r'\d[\d_]*', Number.Integer), + + (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2}))'", + String.Char), + (r"'.'", String.Char), + (r"'", Keyword), + + (r'"', String.Double, 'string'), + + (r'[~?][a-z][\w\']*:', Name.Variable), + ], + 'comment': [ + (r'[^/*]+', Comment.Multiline), + (r'\/\*', Comment.Multiline, '#push'), + (r'\*\/', Comment.Multiline, '#pop'), + (r'\*', Comment.Multiline), + ], + 'string': [ + (r'[^\\"]+', String.Double), + include('escape-sequence'), + (r'\\\n', String.Double), + (r'"', String.Double, '#pop'), + ], + 'dotted': [ + (r'\s+', Text), + (r'\.', Punctuation), + (r'[A-Z][\w\']*(?=\s*\.)', Name.Namespace), + (r'[A-Z][\w\']*', Name.Class, '#pop'), + (r'[a-z_][\w\']*', Name, '#pop'), + default('#pop'), + ], + } + + +class FStarLexer(RegexLexer): + """ + For the F* language (https://www.fstar-lang.org/). + .. versionadded:: 2.7 + """ + + name = 'FStar' + aliases = ['fstar'] + filenames = ['*.fst', '*.fsti'] + mimetypes = ['text/x-fstar'] + + keywords = ( + 'abstract', 'attributes', 'noeq', 'unopteq', 'and' + 'begin', 'by', 'default', 'effect', 'else', 'end', 'ensures', + 'exception', 'exists', 'false', 'forall', 'fun', 'function', 'if', + 'in', 'include', 'inline', 'inline_for_extraction', 'irreducible', + 'logic', 'match', 'module', 'mutable', 'new', 'new_effect', 'noextract', + 'of', 'open', 'opaque', 'private', 'range_of', 'reifiable', + 'reify', 'reflectable', 'requires', 'set_range_of', 'sub_effect', + 'synth', 'then', 'total', 'true', 'try', 'type', 'unfold', 'unfoldable', + 'val', 'when', 'with', 'not' + ) + decl_keywords = ('let', 'rec') + assume_keywords = ('assume', 'admit', 'assert', 'calc') + keyopts = ( + r'~', r'-', r'/\\', r'\\/', r'<:', r'<@', r'\(\|', r'\|\)', r'#', r'u#', + r'&', r'\(', r'\)', r'\(\)', r',', r'~>', r'->', r'<-', r'<--', r'<==>', + r'==>', r'\.', r'\?', r'\?\.', r'\.\[', r'\.\(', r'\.\(\|', r'\.\[\|', + r'\{:pattern', r':', r'::', r':=', r';', r';;', r'=', r'%\[', r'!\{', + r'\[', r'\[@', r'\[\|', r'\|>', r'\]', r'\|\]', r'\{', r'\|', r'\}', r'\$' + ) + + operators = r'[!$%&*+\./:<=>?@^|~-]' + prefix_syms = r'[!?~]' + infix_syms = r'[=<>@^|&+\*/$%-]' + primitives = ('unit', 'int', 'float', 'bool', 'string', 'char', 'list', 'array') + + tokens = { + 'escape-sequence': [ + (r'\\[\\"\'ntbr]', String.Escape), + (r'\\[0-9]{3}', String.Escape), + (r'\\x[0-9a-fA-F]{2}', String.Escape), + ], + 'root': [ + (r'\s+', Text), + (r'false|true|False|True|\(\)|\[\]', Name.Builtin.Pseudo), + (r'\b([A-Z][\w\']*)(?=\s*\.)', Name.Namespace, 'dotted'), + (r'\b([A-Z][\w\']*)', Name.Class), + (r'\(\*(?![)])', Comment, 'comment'), + (r'^\/\/.+$', Comment), + (r'\b(%s)\b' % '|'.join(keywords), Keyword), + (r'\b(%s)\b' % '|'.join(assume_keywords), Name.Exception), + (r'\b(%s)\b' % '|'.join(decl_keywords), Keyword.Declaration), + (r'(%s)' % '|'.join(keyopts[::-1]), Operator), + (r'(%s|%s)?%s' % (infix_syms, prefix_syms, operators), Operator), + (r'\b(%s)\b' % '|'.join(primitives), Keyword.Type), + + (r"[^\W\d][\w']*", Name), + + (r'-?\d[\d_]*(.[\d_]*)?([eE][+\-]?\d[\d_]*)', Number.Float), + (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex), + (r'0[oO][0-7][0-7_]*', Number.Oct), + (r'0[bB][01][01_]*', Number.Bin), + (r'\d[\d_]*', Number.Integer), + + (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2}))'", + String.Char), + (r"'.'", String.Char), + (r"'", Keyword), # a stray quote is another syntax element + (r"\`([\w\'.]+)\`", Operator.Word), # for infix applications + (r"\`", Keyword), # for quoting + (r'"', String.Double, 'string'), + + (r'[~?][a-z][\w\']*:', Name.Variable), + ], + 'comment': [ + (r'[^(*)]+', Comment), + (r'\(\*', Comment, '#push'), + (r'\*\)', Comment, '#pop'), + (r'[(*)]', Comment), + ], + 'string': [ + (r'[^\\"]+', String.Double), + include('escape-sequence'), + (r'\\\n', String.Double), + (r'"', String.Double, '#pop'), + ], + 'dotted': [ + (r'\s+', Text), + (r'\.', Punctuation), + (r'[A-Z][\w\']*(?=\s*\.)', Name.Namespace), + (r'[A-Z][\w\']*', Name.Class, '#pop'), + (r'[a-z_][\w\']*', Name, '#pop'), + default('#pop'), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/modeling.py b/.venv/lib/python3.8/site-packages/pygments/lexers/modeling.py new file mode 100644 index 00000000..5a9071d6 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/modeling.py @@ -0,0 +1,366 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.modeling + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for modeling languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include, bygroups, using, default +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Whitespace + +from pygments.lexers.html import HtmlLexer +from pygments.lexers import _stan_builtins + +__all__ = ['ModelicaLexer', 'BugsLexer', 'JagsLexer', 'StanLexer'] + + +class ModelicaLexer(RegexLexer): + """ + For `Modelica `_ source code. + + .. versionadded:: 1.1 + """ + name = 'Modelica' + aliases = ['modelica'] + filenames = ['*.mo'] + mimetypes = ['text/x-modelica'] + + flags = re.DOTALL | re.MULTILINE + + _name = r"(?:'(?:[^\\']|\\.)+'|[a-zA-Z_]\w*)" + + tokens = { + 'whitespace': [ + (r'[\s\ufeff]+', Text), + (r'//[^\n]*\n?', Comment.Single), + (r'/\*.*?\*/', Comment.Multiline) + ], + 'root': [ + include('whitespace'), + (r'"', String.Double, 'string'), + (r'[()\[\]{},;]+', Punctuation), + (r'\.?[*^/+-]|\.|<>|[<>:=]=?', Operator), + (r'\d+(\.?\d*[eE][-+]?\d+|\.\d*)', Number.Float), + (r'\d+', Number.Integer), + (r'(abs|acos|actualStream|array|asin|assert|AssertionLevel|atan|' + r'atan2|backSample|Boolean|cardinality|cat|ceil|change|Clock|' + r'Connections|cos|cosh|cross|delay|diagonal|div|edge|exp|' + r'ExternalObject|fill|floor|getInstanceName|hold|homotopy|' + r'identity|inStream|integer|Integer|interval|inverse|isPresent|' + r'linspace|log|log10|matrix|max|min|mod|ndims|noClock|noEvent|' + r'ones|outerProduct|pre|previous|product|Real|reinit|rem|rooted|' + r'sample|scalar|semiLinear|shiftSample|sign|sin|sinh|size|skew|' + r'smooth|spatialDistribution|sqrt|StateSelect|String|subSample|' + r'sum|superSample|symmetric|tan|tanh|terminal|terminate|time|' + r'transpose|vector|zeros)\b', Name.Builtin), + (r'(algorithm|annotation|break|connect|constant|constrainedby|der|' + r'discrete|each|else|elseif|elsewhen|encapsulated|enumeration|' + r'equation|exit|expandable|extends|external|firstTick|final|flow|for|if|' + r'import|impure|in|initial|inner|input|interval|loop|nondiscrete|outer|' + r'output|parameter|partial|protected|public|pure|redeclare|' + r'replaceable|return|stream|then|when|while)\b', + Keyword.Reserved), + (r'(and|not|or)\b', Operator.Word), + (r'(block|class|connector|end|function|model|operator|package|' + r'record|type)\b', Keyword.Reserved, 'class'), + (r'(false|true)\b', Keyword.Constant), + (r'within\b', Keyword.Reserved, 'package-prefix'), + (_name, Name) + ], + 'class': [ + include('whitespace'), + (r'(function|record)\b', Keyword.Reserved), + (r'(if|for|when|while)\b', Keyword.Reserved, '#pop'), + (_name, Name.Class, '#pop'), + default('#pop') + ], + 'package-prefix': [ + include('whitespace'), + (_name, Name.Namespace, '#pop'), + default('#pop') + ], + 'string': [ + (r'"', String.Double, '#pop'), + (r'\\[\'"?\\abfnrtv]', String.Escape), + (r'(?i)<\s*html\s*>([^\\"]|\\.)+?(<\s*/\s*html\s*>|(?="))', + using(HtmlLexer)), + (r'<|\\?[^"\\<]+', String.Double) + ] + } + + +class BugsLexer(RegexLexer): + """ + Pygments Lexer for `OpenBugs `_ and WinBugs + models. + + .. versionadded:: 1.6 + """ + + name = 'BUGS' + aliases = ['bugs', 'winbugs', 'openbugs'] + filenames = ['*.bug'] + + _FUNCTIONS = ( + # Scalar functions + 'abs', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctanh', + 'cloglog', 'cos', 'cosh', 'cumulative', 'cut', 'density', 'deviance', + 'equals', 'expr', 'gammap', 'ilogit', 'icloglog', 'integral', 'log', + 'logfact', 'loggam', 'logit', 'max', 'min', 'phi', 'post.p.value', + 'pow', 'prior.p.value', 'probit', 'replicate.post', 'replicate.prior', + 'round', 'sin', 'sinh', 'solution', 'sqrt', 'step', 'tan', 'tanh', + 'trunc', + # Vector functions + 'inprod', 'interp.lin', 'inverse', 'logdet', 'mean', 'eigen.vals', + 'ode', 'prod', 'p.valueM', 'rank', 'ranked', 'replicate.postM', + 'sd', 'sort', 'sum', + # Special + 'D', 'I', 'F', 'T', 'C') + """ OpenBUGS built-in functions + + From http://www.openbugs.info/Manuals/ModelSpecification.html#ContentsAII + + This also includes + + - T, C, I : Truncation and censoring. + ``T`` and ``C`` are in OpenBUGS. ``I`` in WinBUGS. + - D : ODE + - F : Functional http://www.openbugs.info/Examples/Functionals.html + + """ + + _DISTRIBUTIONS = ('dbern', 'dbin', 'dcat', 'dnegbin', 'dpois', + 'dhyper', 'dbeta', 'dchisqr', 'ddexp', 'dexp', + 'dflat', 'dgamma', 'dgev', 'df', 'dggamma', 'dgpar', + 'dloglik', 'dlnorm', 'dlogis', 'dnorm', 'dpar', + 'dt', 'dunif', 'dweib', 'dmulti', 'ddirch', 'dmnorm', + 'dmt', 'dwish') + """ OpenBUGS built-in distributions + + Functions from + http://www.openbugs.info/Manuals/ModelSpecification.html#ContentsAI + """ + + tokens = { + 'whitespace': [ + (r"\s+", Text), + ], + 'comments': [ + # Comments + (r'#.*$', Comment.Single), + ], + 'root': [ + # Comments + include('comments'), + include('whitespace'), + # Block start + (r'(model)(\s+)(\{)', + bygroups(Keyword.Namespace, Text, Punctuation)), + # Reserved Words + (r'(for|in)(?![\w.])', Keyword.Reserved), + # Built-in Functions + (r'(%s)(?=\s*\()' + % r'|'.join(_FUNCTIONS + _DISTRIBUTIONS), + Name.Builtin), + # Regular variable names + (r'[A-Za-z][\w.]*', Name), + # Number Literals + (r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', Number), + # Punctuation + (r'\[|\]|\(|\)|:|,|;', Punctuation), + # Assignment operators + # SLexer makes these tokens Operators. + (r'<-|~', Operator), + # Infix and prefix operators + (r'\+|-|\*|/', Operator), + # Block + (r'[{}]', Punctuation), + ] + } + + def analyse_text(text): + if re.search(r"^\s*model\s*{", text, re.M): + return 0.7 + else: + return 0.0 + + +class JagsLexer(RegexLexer): + """ + Pygments Lexer for JAGS. + + .. versionadded:: 1.6 + """ + + name = 'JAGS' + aliases = ['jags'] + filenames = ['*.jag', '*.bug'] + + # JAGS + _FUNCTIONS = ( + 'abs', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctanh', + 'cos', 'cosh', 'cloglog', + 'equals', 'exp', 'icloglog', 'ifelse', 'ilogit', 'log', 'logfact', + 'loggam', 'logit', 'phi', 'pow', 'probit', 'round', 'sin', 'sinh', + 'sqrt', 'step', 'tan', 'tanh', 'trunc', 'inprod', 'interp.lin', + 'logdet', 'max', 'mean', 'min', 'prod', 'sum', 'sd', 'inverse', + 'rank', 'sort', 't', 'acos', 'acosh', 'asin', 'asinh', 'atan', + # Truncation/Censoring (should I include) + 'T', 'I') + # Distributions with density, probability and quartile functions + _DISTRIBUTIONS = tuple('[dpq]%s' % x for x in + ('bern', 'beta', 'dchiqsqr', 'ddexp', 'dexp', + 'df', 'gamma', 'gen.gamma', 'logis', 'lnorm', + 'negbin', 'nchisqr', 'norm', 'par', 'pois', 'weib')) + # Other distributions without density and probability + _OTHER_DISTRIBUTIONS = ( + 'dt', 'dunif', 'dbetabin', 'dbern', 'dbin', 'dcat', 'dhyper', + 'ddirch', 'dmnorm', 'dwish', 'dmt', 'dmulti', 'dbinom', 'dchisq', + 'dnbinom', 'dweibull', 'ddirich') + + tokens = { + 'whitespace': [ + (r"\s+", Text), + ], + 'names': [ + # Regular variable names + (r'[a-zA-Z][\w.]*\b', Name), + ], + 'comments': [ + # do not use stateful comments + (r'(?s)/\*.*?\*/', Comment.Multiline), + # Comments + (r'#.*$', Comment.Single), + ], + 'root': [ + # Comments + include('comments'), + include('whitespace'), + # Block start + (r'(model|data)(\s+)(\{)', + bygroups(Keyword.Namespace, Text, Punctuation)), + (r'var(?![\w.])', Keyword.Declaration), + # Reserved Words + (r'(for|in)(?![\w.])', Keyword.Reserved), + # Builtins + # Need to use lookahead because . is a valid char + (r'(%s)(?=\s*\()' % r'|'.join(_FUNCTIONS + + _DISTRIBUTIONS + + _OTHER_DISTRIBUTIONS), + Name.Builtin), + # Names + include('names'), + # Number Literals + (r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', Number), + (r'\[|\]|\(|\)|:|,|;', Punctuation), + # Assignment operators + (r'<-|~', Operator), + # # JAGS includes many more than OpenBUGS + (r'\+|-|\*|\/|\|\|[&]{2}|[<>=]=?|\^|%.*?%', Operator), + (r'[{}]', Punctuation), + ] + } + + def analyse_text(text): + if re.search(r'^\s*model\s*\{', text, re.M): + if re.search(r'^\s*data\s*\{', text, re.M): + return 0.9 + elif re.search(r'^\s*var', text, re.M): + return 0.9 + else: + return 0.3 + else: + return 0 + + +class StanLexer(RegexLexer): + """Pygments Lexer for Stan models. + + The Stan modeling language is specified in the *Stan Modeling Language + User's Guide and Reference Manual, v2.17.0*, + `pdf `__. + + .. versionadded:: 1.6 + """ + + name = 'Stan' + aliases = ['stan'] + filenames = ['*.stan'] + + tokens = { + 'whitespace': [ + (r"\s+", Text), + ], + 'comments': [ + (r'(?s)/\*.*?\*/', Comment.Multiline), + # Comments + (r'(//|#).*$', Comment.Single), + ], + 'root': [ + # Stan is more restrictive on strings than this regex + (r'"[^"]*"', String), + # Comments + include('comments'), + # block start + include('whitespace'), + # Block start + (r'(%s)(\s*)(\{)' % + r'|'.join(('functions', 'data', r'transformed\s+?data', + 'parameters', r'transformed\s+parameters', + 'model', r'generated\s+quantities')), + bygroups(Keyword.Namespace, Text, Punctuation)), + # target keyword + (r'target\s*\+=', Keyword), + # Reserved Words + (r'(%s)\b' % r'|'.join(_stan_builtins.KEYWORDS), Keyword), + # Truncation + (r'T(?=\s*\[)', Keyword), + # Data types + (r'(%s)\b' % r'|'.join(_stan_builtins.TYPES), Keyword.Type), + # < should be punctuation, but elsewhere I can't tell if it is in + # a range constraint + (r'(<)(\s*)(upper|lower)(\s*)(=)', + bygroups(Operator, Whitespace, Keyword, Whitespace, Punctuation)), + (r'(,)(\s*)(upper)(\s*)(=)', + bygroups(Punctuation, Whitespace, Keyword, Whitespace, Punctuation)), + # Punctuation + (r"[;,\[\]()]", Punctuation), + # Builtin + (r'(%s)(?=\s*\()' % '|'.join(_stan_builtins.FUNCTIONS), Name.Builtin), + (r'(~)(\s*)(%s)(?=\s*\()' % '|'.join(_stan_builtins.DISTRIBUTIONS), + bygroups(Operator, Whitespace, Name.Builtin)), + # Special names ending in __, like lp__ + (r'[A-Za-z]\w*__\b', Name.Builtin.Pseudo), + (r'(%s)\b' % r'|'.join(_stan_builtins.RESERVED), Keyword.Reserved), + # user-defined functions + (r'[A-Za-z]\w*(?=\s*\()]', Name.Function), + # Regular variable names + (r'[A-Za-z]\w*\b', Name), + # Real Literals + (r'[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?', Number.Float), + (r'\.[0-9]+([eE][+-]?[0-9]+)?', Number.Float), + # Integer Literals + (r'[0-9]+', Number.Integer), + # Assignment operators + (r'<-|(?:\+|-|\.?/|\.?\*|=)?=|~', Operator), + # Infix, prefix and postfix operators (and = ) + (r"\+|-|\.?\*|\.?/|\\|'|\^|!=?|<=?|>=?|\|\||&&|%|\?|:", Operator), + # Block delimiters + (r'[{}]', Punctuation), + # Distribution | + (r'\|', Punctuation) + ] + } + + def analyse_text(text): + if re.search(r'^\s*parameters\s*\{', text, re.M): + return 1.0 + else: + return 0.0 diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/modula2.py b/.venv/lib/python3.8/site-packages/pygments/lexers/modula2.py new file mode 100644 index 00000000..331b18ac --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/modula2.py @@ -0,0 +1,1580 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.modula2 + ~~~~~~~~~~~~~~~~~~~~~~~ + + Multi-Dialect Lexer for Modula-2. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include +from pygments.util import get_bool_opt, get_list_opt +from pygments.token import Text, Comment, Operator, Keyword, Name, \ + String, Number, Punctuation, Error + +__all__ = ['Modula2Lexer'] + + +# Multi-Dialect Modula-2 Lexer +class Modula2Lexer(RegexLexer): + """ + For `Modula-2 `_ source code. + + The Modula-2 lexer supports several dialects. By default, it operates in + fallback mode, recognising the *combined* literals, punctuation symbols + and operators of all supported dialects, and the *combined* reserved words + and builtins of PIM Modula-2, ISO Modula-2 and Modula-2 R10, while not + differentiating between library defined identifiers. + + To select a specific dialect, a dialect option may be passed + or a dialect tag may be embedded into a source file. + + Dialect Options: + + `m2pim` + Select PIM Modula-2 dialect. + `m2iso` + Select ISO Modula-2 dialect. + `m2r10` + Select Modula-2 R10 dialect. + `objm2` + Select Objective Modula-2 dialect. + + The PIM and ISO dialect options may be qualified with a language extension. + + Language Extensions: + + `+aglet` + Select Aglet Modula-2 extensions, available with m2iso. + `+gm2` + Select GNU Modula-2 extensions, available with m2pim. + `+p1` + Select p1 Modula-2 extensions, available with m2iso. + `+xds` + Select XDS Modula-2 extensions, available with m2iso. + + + Passing a Dialect Option via Unix Commandline Interface + + Dialect options may be passed to the lexer using the `dialect` key. + Only one such option should be passed. If multiple dialect options are + passed, the first valid option is used, any subsequent options are ignored. + + Examples: + + `$ pygmentize -O full,dialect=m2iso -f html -o /path/to/output /path/to/input` + Use ISO dialect to render input to HTML output + `$ pygmentize -O full,dialect=m2iso+p1 -f rtf -o /path/to/output /path/to/input` + Use ISO dialect with p1 extensions to render input to RTF output + + + Embedding a Dialect Option within a source file + + A dialect option may be embedded in a source file in form of a dialect + tag, a specially formatted comment that specifies a dialect option. + + Dialect Tag EBNF:: + + dialectTag : + OpeningCommentDelim Prefix dialectOption ClosingCommentDelim ; + + dialectOption : + 'm2pim' | 'm2iso' | 'm2r10' | 'objm2' | + 'm2iso+aglet' | 'm2pim+gm2' | 'm2iso+p1' | 'm2iso+xds' ; + + Prefix : '!' ; + + OpeningCommentDelim : '(*' ; + + ClosingCommentDelim : '*)' ; + + No whitespace is permitted between the tokens of a dialect tag. + + In the event that a source file contains multiple dialect tags, the first + tag that contains a valid dialect option will be used and any subsequent + dialect tags will be ignored. Ideally, a dialect tag should be placed + at the beginning of a source file. + + An embedded dialect tag overrides a dialect option set via command line. + + Examples: + + ``(*!m2r10*) DEFINITION MODULE Foobar; ...`` + Use Modula2 R10 dialect to render this source file. + ``(*!m2pim+gm2*) DEFINITION MODULE Bazbam; ...`` + Use PIM dialect with GNU extensions to render this source file. + + + Algol Publication Mode: + + In Algol publication mode, source text is rendered for publication of + algorithms in scientific papers and academic texts, following the format + of the Revised Algol-60 Language Report. It is activated by passing + one of two corresponding styles as an option: + + `algol` + render reserved words lowercase underline boldface + and builtins lowercase boldface italic + `algol_nu` + render reserved words lowercase boldface (no underlining) + and builtins lowercase boldface italic + + The lexer automatically performs the required lowercase conversion when + this mode is activated. + + Example: + + ``$ pygmentize -O full,style=algol -f latex -o /path/to/output /path/to/input`` + Render input file in Algol publication mode to LaTeX output. + + + Rendering Mode of First Class ADT Identifiers: + + The rendering of standard library first class ADT identifiers is controlled + by option flag "treat_stdlib_adts_as_builtins". + + When this option is turned on, standard library ADT identifiers are rendered + as builtins. When it is turned off, they are rendered as ordinary library + identifiers. + + `treat_stdlib_adts_as_builtins` (default: On) + + The option is useful for dialects that support ADTs as first class objects + and provide ADTs in the standard library that would otherwise be built-in. + + At present, only Modula-2 R10 supports library ADTs as first class objects + and therefore, no ADT identifiers are defined for any other dialects. + + Example: + + ``$ pygmentize -O full,dialect=m2r10,treat_stdlib_adts_as_builtins=Off ...`` + Render standard library ADTs as ordinary library types. + + .. versionadded:: 1.3 + + .. versionchanged:: 2.1 + Added multi-dialect support. + """ + name = 'Modula-2' + aliases = ['modula2', 'm2'] + filenames = ['*.def', '*.mod'] + mimetypes = ['text/x-modula2'] + + flags = re.MULTILINE | re.DOTALL + + tokens = { + 'whitespace': [ + (r'\n+', Text), # blank lines + (r'\s+', Text), # whitespace + ], + 'dialecttags': [ + # PIM Dialect Tag + (r'\(\*!m2pim\*\)', Comment.Special), + # ISO Dialect Tag + (r'\(\*!m2iso\*\)', Comment.Special), + # M2R10 Dialect Tag + (r'\(\*!m2r10\*\)', Comment.Special), + # ObjM2 Dialect Tag + (r'\(\*!objm2\*\)', Comment.Special), + # Aglet Extensions Dialect Tag + (r'\(\*!m2iso\+aglet\*\)', Comment.Special), + # GNU Extensions Dialect Tag + (r'\(\*!m2pim\+gm2\*\)', Comment.Special), + # p1 Extensions Dialect Tag + (r'\(\*!m2iso\+p1\*\)', Comment.Special), + # XDS Extensions Dialect Tag + (r'\(\*!m2iso\+xds\*\)', Comment.Special), + ], + 'identifiers': [ + (r'([a-zA-Z_$][\w$]*)', Name), + ], + 'prefixed_number_literals': [ + # + # Base-2, whole number + (r'0b[01]+(\'[01]+)*', Number.Bin), + # + # Base-16, whole number + (r'0[ux][0-9A-F]+(\'[0-9A-F]+)*', Number.Hex), + ], + 'plain_number_literals': [ + # + # Base-10, real number with exponent + (r'[0-9]+(\'[0-9]+)*' # integral part + r'\.[0-9]+(\'[0-9]+)*' # fractional part + r'[eE][+-]?[0-9]+(\'[0-9]+)*', # exponent + Number.Float), + # + # Base-10, real number without exponent + (r'[0-9]+(\'[0-9]+)*' # integral part + r'\.[0-9]+(\'[0-9]+)*', # fractional part + Number.Float), + # + # Base-10, whole number + (r'[0-9]+(\'[0-9]+)*', Number.Integer), + ], + 'suffixed_number_literals': [ + # + # Base-8, whole number + (r'[0-7]+B', Number.Oct), + # + # Base-8, character code + (r'[0-7]+C', Number.Oct), + # + # Base-16, number + (r'[0-9A-F]+H', Number.Hex), + ], + 'string_literals': [ + (r"'(\\\\|\\'|[^'])*'", String), # single quoted string + (r'"(\\\\|\\"|[^"])*"', String), # double quoted string + ], + 'digraph_operators': [ + # Dot Product Operator + (r'\*\.', Operator), + # Array Concatenation Operator + (r'\+>', Operator), # M2R10 + ObjM2 + # Inequality Operator + (r'<>', Operator), # ISO + PIM + # Less-Or-Equal, Subset + (r'<=', Operator), + # Greater-Or-Equal, Superset + (r'>=', Operator), + # Identity Operator + (r'==', Operator), # M2R10 + ObjM2 + # Type Conversion Operator + (r'::', Operator), # M2R10 + ObjM2 + # Assignment Symbol + (r':=', Operator), + # Postfix Increment Mutator + (r'\+\+', Operator), # M2R10 + ObjM2 + # Postfix Decrement Mutator + (r'--', Operator), # M2R10 + ObjM2 + ], + 'unigraph_operators': [ + # Arithmetic Operators + (r'[+-]', Operator), + (r'[*/]', Operator), + # ISO 80000-2 compliant Set Difference Operator + (r'\\', Operator), # M2R10 + ObjM2 + # Relational Operators + (r'[=#<>]', Operator), + # Dereferencing Operator + (r'\^', Operator), + # Dereferencing Operator Synonym + (r'@', Operator), # ISO + # Logical AND Operator Synonym + (r'&', Operator), # PIM + ISO + # Logical NOT Operator Synonym + (r'~', Operator), # PIM + ISO + # Smalltalk Message Prefix + (r'`', Operator), # ObjM2 + ], + 'digraph_punctuation': [ + # Range Constructor + (r'\.\.', Punctuation), + # Opening Chevron Bracket + (r'<<', Punctuation), # M2R10 + ISO + # Closing Chevron Bracket + (r'>>', Punctuation), # M2R10 + ISO + # Blueprint Punctuation + (r'->', Punctuation), # M2R10 + ISO + # Distinguish |# and # in M2 R10 + (r'\|#', Punctuation), + # Distinguish ## and # in M2 R10 + (r'##', Punctuation), + # Distinguish |* and * in M2 R10 + (r'\|\*', Punctuation), + ], + 'unigraph_punctuation': [ + # Common Punctuation + (r'[()\[\]{},.:;|]', Punctuation), + # Case Label Separator Synonym + (r'!', Punctuation), # ISO + # Blueprint Punctuation + (r'\?', Punctuation), # M2R10 + ObjM2 + ], + 'comments': [ + # Single Line Comment + (r'^//.*?\n', Comment.Single), # M2R10 + ObjM2 + # Block Comment + (r'\(\*([^$].*?)\*\)', Comment.Multiline), + # Template Block Comment + (r'/\*(.*?)\*/', Comment.Multiline), # M2R10 + ObjM2 + ], + 'pragmas': [ + # ISO Style Pragmas + (r'<\*.*?\*>', Comment.Preproc), # ISO, M2R10 + ObjM2 + # Pascal Style Pragmas + (r'\(\*\$.*?\*\)', Comment.Preproc), # PIM + ], + 'root': [ + include('whitespace'), + include('dialecttags'), + include('pragmas'), + include('comments'), + include('identifiers'), + include('suffixed_number_literals'), # PIM + ISO + include('prefixed_number_literals'), # M2R10 + ObjM2 + include('plain_number_literals'), + include('string_literals'), + include('digraph_punctuation'), + include('digraph_operators'), + include('unigraph_punctuation'), + include('unigraph_operators'), + ] + } + +# C o m m o n D a t a s e t s + + # Common Reserved Words Dataset + common_reserved_words = ( + # 37 common reserved words + 'AND', 'ARRAY', 'BEGIN', 'BY', 'CASE', 'CONST', 'DEFINITION', 'DIV', + 'DO', 'ELSE', 'ELSIF', 'END', 'EXIT', 'FOR', 'FROM', 'IF', + 'IMPLEMENTATION', 'IMPORT', 'IN', 'LOOP', 'MOD', 'MODULE', 'NOT', + 'OF', 'OR', 'POINTER', 'PROCEDURE', 'RECORD', 'REPEAT', 'RETURN', + 'SET', 'THEN', 'TO', 'TYPE', 'UNTIL', 'VAR', 'WHILE', + ) + + # Common Builtins Dataset + common_builtins = ( + # 16 common builtins + 'ABS', 'BOOLEAN', 'CARDINAL', 'CHAR', 'CHR', 'FALSE', 'INTEGER', + 'LONGINT', 'LONGREAL', 'MAX', 'MIN', 'NIL', 'ODD', 'ORD', 'REAL', + 'TRUE', + ) + + # Common Pseudo-Module Builtins Dataset + common_pseudo_builtins = ( + # 4 common pseudo builtins + 'ADDRESS', 'BYTE', 'WORD', 'ADR' + ) + +# P I M M o d u l a - 2 D a t a s e t s + + # Lexemes to Mark as Error Tokens for PIM Modula-2 + pim_lexemes_to_reject = ( + '!', '`', '@', '$', '%', '?', '\\', '==', '++', '--', '::', '*.', + '+>', '->', '<<', '>>', '|#', '##', + ) + + # PIM Modula-2 Additional Reserved Words Dataset + pim_additional_reserved_words = ( + # 3 additional reserved words + 'EXPORT', 'QUALIFIED', 'WITH', + ) + + # PIM Modula-2 Additional Builtins Dataset + pim_additional_builtins = ( + # 16 additional builtins + 'BITSET', 'CAP', 'DEC', 'DISPOSE', 'EXCL', 'FLOAT', 'HALT', 'HIGH', + 'INC', 'INCL', 'NEW', 'NIL', 'PROC', 'SIZE', 'TRUNC', 'VAL', + ) + + # PIM Modula-2 Additional Pseudo-Module Builtins Dataset + pim_additional_pseudo_builtins = ( + # 5 additional pseudo builtins + 'SYSTEM', 'PROCESS', 'TSIZE', 'NEWPROCESS', 'TRANSFER', + ) + +# I S O M o d u l a - 2 D a t a s e t s + + # Lexemes to Mark as Error Tokens for ISO Modula-2 + iso_lexemes_to_reject = ( + '`', '$', '%', '?', '\\', '==', '++', '--', '::', '*.', '+>', '->', + '<<', '>>', '|#', '##', + ) + + # ISO Modula-2 Additional Reserved Words Dataset + iso_additional_reserved_words = ( + # 9 additional reserved words (ISO 10514-1) + 'EXCEPT', 'EXPORT', 'FINALLY', 'FORWARD', 'PACKEDSET', 'QUALIFIED', + 'REM', 'RETRY', 'WITH', + # 10 additional reserved words (ISO 10514-2 & ISO 10514-3) + 'ABSTRACT', 'AS', 'CLASS', 'GUARD', 'INHERIT', 'OVERRIDE', 'READONLY', + 'REVEAL', 'TRACED', 'UNSAFEGUARDED', + ) + + # ISO Modula-2 Additional Builtins Dataset + iso_additional_builtins = ( + # 26 additional builtins (ISO 10514-1) + 'BITSET', 'CAP', 'CMPLX', 'COMPLEX', 'DEC', 'DISPOSE', 'EXCL', 'FLOAT', + 'HALT', 'HIGH', 'IM', 'INC', 'INCL', 'INT', 'INTERRUPTIBLE', 'LENGTH', + 'LFLOAT', 'LONGCOMPLEX', 'NEW', 'PROC', 'PROTECTION', 'RE', 'SIZE', + 'TRUNC', 'UNINTERRUBTIBLE', 'VAL', + # 5 additional builtins (ISO 10514-2 & ISO 10514-3) + 'CREATE', 'DESTROY', 'EMPTY', 'ISMEMBER', 'SELF', + ) + + # ISO Modula-2 Additional Pseudo-Module Builtins Dataset + iso_additional_pseudo_builtins = ( + # 14 additional builtins (SYSTEM) + 'SYSTEM', 'BITSPERLOC', 'LOCSPERBYTE', 'LOCSPERWORD', 'LOC', + 'ADDADR', 'SUBADR', 'DIFADR', 'MAKEADR', 'ADR', + 'ROTATE', 'SHIFT', 'CAST', 'TSIZE', + # 13 additional builtins (COROUTINES) + 'COROUTINES', 'ATTACH', 'COROUTINE', 'CURRENT', 'DETACH', 'HANDLER', + 'INTERRUPTSOURCE', 'IOTRANSFER', 'IsATTACHED', 'LISTEN', + 'NEWCOROUTINE', 'PROT', 'TRANSFER', + # 9 additional builtins (EXCEPTIONS) + 'EXCEPTIONS', 'AllocateSource', 'CurrentNumber', 'ExceptionNumber', + 'ExceptionSource', 'GetMessage', 'IsCurrentSource', + 'IsExceptionalExecution', 'RAISE', + # 3 additional builtins (TERMINATION) + 'TERMINATION', 'IsTerminating', 'HasHalted', + # 4 additional builtins (M2EXCEPTION) + 'M2EXCEPTION', 'M2Exceptions', 'M2Exception', 'IsM2Exception', + 'indexException', 'rangeException', 'caseSelectException', + 'invalidLocation', 'functionException', 'wholeValueException', + 'wholeDivException', 'realValueException', 'realDivException', + 'complexValueException', 'complexDivException', 'protException', + 'sysException', 'coException', 'exException', + ) + +# M o d u l a - 2 R 1 0 D a t a s e t s + + # Lexemes to Mark as Error Tokens for Modula-2 R10 + m2r10_lexemes_to_reject = ( + '!', '`', '@', '$', '%', '&', '<>', + ) + + # Modula-2 R10 reserved words in addition to the common set + m2r10_additional_reserved_words = ( + # 12 additional reserved words + 'ALIAS', 'ARGLIST', 'BLUEPRINT', 'COPY', 'GENLIB', 'INDETERMINATE', + 'NEW', 'NONE', 'OPAQUE', 'REFERENTIAL', 'RELEASE', 'RETAIN', + # 2 additional reserved words with symbolic assembly option + 'ASM', 'REG', + ) + + # Modula-2 R10 builtins in addition to the common set + m2r10_additional_builtins = ( + # 26 additional builtins + 'CARDINAL', 'COUNT', 'EMPTY', 'EXISTS', 'INSERT', 'LENGTH', 'LONGCARD', + 'OCTET', 'PTR', 'PRED', 'READ', 'READNEW', 'REMOVE', 'RETRIEVE', 'SORT', + 'STORE', 'SUBSET', 'SUCC', 'TLIMIT', 'TMAX', 'TMIN', 'TRUE', 'TSIZE', + 'UNICHAR', 'WRITE', 'WRITEF', + ) + + # Modula-2 R10 Additional Pseudo-Module Builtins Dataset + m2r10_additional_pseudo_builtins = ( + # 13 additional builtins (TPROPERTIES) + 'TPROPERTIES', 'PROPERTY', 'LITERAL', 'TPROPERTY', 'TLITERAL', + 'TBUILTIN', 'TDYN', 'TREFC', 'TNIL', 'TBASE', 'TPRECISION', + 'TMAXEXP', 'TMINEXP', + # 4 additional builtins (CONVERSION) + 'CONVERSION', 'TSXFSIZE', 'SXF', 'VAL', + # 35 additional builtins (UNSAFE) + 'UNSAFE', 'CAST', 'INTRINSIC', 'AVAIL', 'ADD', 'SUB', 'ADDC', 'SUBC', + 'FETCHADD', 'FETCHSUB', 'SHL', 'SHR', 'ASHR', 'ROTL', 'ROTR', 'ROTLC', + 'ROTRC', 'BWNOT', 'BWAND', 'BWOR', 'BWXOR', 'BWNAND', 'BWNOR', + 'SETBIT', 'TESTBIT', 'LSBIT', 'MSBIT', 'CSBITS', 'BAIL', 'HALT', + 'TODO', 'FFI', 'ADDR', 'VARGLIST', 'VARGC', + # 11 additional builtins (ATOMIC) + 'ATOMIC', 'INTRINSIC', 'AVAIL', 'SWAP', 'CAS', 'INC', 'DEC', 'BWAND', + 'BWNAND', 'BWOR', 'BWXOR', + # 7 additional builtins (COMPILER) + 'COMPILER', 'DEBUG', 'MODNAME', 'PROCNAME', 'LINENUM', 'DEFAULT', + 'HASH', + # 5 additional builtins (ASSEMBLER) + 'ASSEMBLER', 'REGISTER', 'SETREG', 'GETREG', 'CODE', + ) + +# O b j e c t i v e M o d u l a - 2 D a t a s e t s + + # Lexemes to Mark as Error Tokens for Objective Modula-2 + objm2_lexemes_to_reject = ( + '!', '$', '%', '&', '<>', + ) + + # Objective Modula-2 Extensions + # reserved words in addition to Modula-2 R10 + objm2_additional_reserved_words = ( + # 16 additional reserved words + 'BYCOPY', 'BYREF', 'CLASS', 'CONTINUE', 'CRITICAL', 'INOUT', 'METHOD', + 'ON', 'OPTIONAL', 'OUT', 'PRIVATE', 'PROTECTED', 'PROTOCOL', 'PUBLIC', + 'SUPER', 'TRY', + ) + + # Objective Modula-2 Extensions + # builtins in addition to Modula-2 R10 + objm2_additional_builtins = ( + # 3 additional builtins + 'OBJECT', 'NO', 'YES', + ) + + # Objective Modula-2 Extensions + # pseudo-module builtins in addition to Modula-2 R10 + objm2_additional_pseudo_builtins = ( + # None + ) + +# A g l e t M o d u l a - 2 D a t a s e t s + + # Aglet Extensions + # reserved words in addition to ISO Modula-2 + aglet_additional_reserved_words = ( + # None + ) + + # Aglet Extensions + # builtins in addition to ISO Modula-2 + aglet_additional_builtins = ( + # 9 additional builtins + 'BITSET8', 'BITSET16', 'BITSET32', 'CARDINAL8', 'CARDINAL16', + 'CARDINAL32', 'INTEGER8', 'INTEGER16', 'INTEGER32', + ) + + # Aglet Modula-2 Extensions + # pseudo-module builtins in addition to ISO Modula-2 + aglet_additional_pseudo_builtins = ( + # None + ) + +# G N U M o d u l a - 2 D a t a s e t s + + # GNU Extensions + # reserved words in addition to PIM Modula-2 + gm2_additional_reserved_words = ( + # 10 additional reserved words + 'ASM', '__ATTRIBUTE__', '__BUILTIN__', '__COLUMN__', '__DATE__', + '__FILE__', '__FUNCTION__', '__LINE__', '__MODULE__', 'VOLATILE', + ) + + # GNU Extensions + # builtins in addition to PIM Modula-2 + gm2_additional_builtins = ( + # 21 additional builtins + 'BITSET8', 'BITSET16', 'BITSET32', 'CARDINAL8', 'CARDINAL16', + 'CARDINAL32', 'CARDINAL64', 'COMPLEX32', 'COMPLEX64', 'COMPLEX96', + 'COMPLEX128', 'INTEGER8', 'INTEGER16', 'INTEGER32', 'INTEGER64', + 'REAL8', 'REAL16', 'REAL32', 'REAL96', 'REAL128', 'THROW', + ) + + # GNU Extensions + # pseudo-module builtins in addition to PIM Modula-2 + gm2_additional_pseudo_builtins = ( + # None + ) + +# p 1 M o d u l a - 2 D a t a s e t s + + # p1 Extensions + # reserved words in addition to ISO Modula-2 + p1_additional_reserved_words = ( + # None + ) + + # p1 Extensions + # builtins in addition to ISO Modula-2 + p1_additional_builtins = ( + # None + ) + + # p1 Modula-2 Extensions + # pseudo-module builtins in addition to ISO Modula-2 + p1_additional_pseudo_builtins = ( + # 1 additional builtin + 'BCD', + ) + +# X D S M o d u l a - 2 D a t a s e t s + + # XDS Extensions + # reserved words in addition to ISO Modula-2 + xds_additional_reserved_words = ( + # 1 additional reserved word + 'SEQ', + ) + + # XDS Extensions + # builtins in addition to ISO Modula-2 + xds_additional_builtins = ( + # 9 additional builtins + 'ASH', 'ASSERT', 'DIFFADR_TYPE', 'ENTIER', 'INDEX', 'LEN', + 'LONGCARD', 'SHORTCARD', 'SHORTINT', + ) + + # XDS Modula-2 Extensions + # pseudo-module builtins in addition to ISO Modula-2 + xds_additional_pseudo_builtins = ( + # 22 additional builtins (SYSTEM) + 'PROCESS', 'NEWPROCESS', 'BOOL8', 'BOOL16', 'BOOL32', 'CARD8', + 'CARD16', 'CARD32', 'INT8', 'INT16', 'INT32', 'REF', 'MOVE', + 'FILL', 'GET', 'PUT', 'CC', 'int', 'unsigned', 'size_t', 'void' + # 3 additional builtins (COMPILER) + 'COMPILER', 'OPTION', 'EQUATION' + ) + +# P I M S t a n d a r d L i b r a r y D a t a s e t s + + # PIM Modula-2 Standard Library Modules Dataset + pim_stdlib_module_identifiers = ( + 'Terminal', 'FileSystem', 'InOut', 'RealInOut', 'MathLib0', 'Storage', + ) + + # PIM Modula-2 Standard Library Types Dataset + pim_stdlib_type_identifiers = ( + 'Flag', 'FlagSet', 'Response', 'Command', 'Lock', 'Permission', + 'MediumType', 'File', 'FileProc', 'DirectoryProc', 'FileCommand', + 'DirectoryCommand', + ) + + # PIM Modula-2 Standard Library Procedures Dataset + pim_stdlib_proc_identifiers = ( + 'Read', 'BusyRead', 'ReadAgain', 'Write', 'WriteString', 'WriteLn', + 'Create', 'Lookup', 'Close', 'Delete', 'Rename', 'SetRead', 'SetWrite', + 'SetModify', 'SetOpen', 'Doio', 'SetPos', 'GetPos', 'Length', 'Reset', + 'Again', 'ReadWord', 'WriteWord', 'ReadChar', 'WriteChar', + 'CreateMedium', 'DeleteMedium', 'AssignName', 'DeassignName', + 'ReadMedium', 'LookupMedium', 'OpenInput', 'OpenOutput', 'CloseInput', + 'CloseOutput', 'ReadString', 'ReadInt', 'ReadCard', 'ReadWrd', + 'WriteInt', 'WriteCard', 'WriteOct', 'WriteHex', 'WriteWrd', + 'ReadReal', 'WriteReal', 'WriteFixPt', 'WriteRealOct', 'sqrt', 'exp', + 'ln', 'sin', 'cos', 'arctan', 'entier', 'ALLOCATE', 'DEALLOCATE', + ) + + # PIM Modula-2 Standard Library Variables Dataset + pim_stdlib_var_identifiers = ( + 'Done', 'termCH', 'in', 'out' + ) + + # PIM Modula-2 Standard Library Constants Dataset + pim_stdlib_const_identifiers = ( + 'EOL', + ) + +# I S O S t a n d a r d L i b r a r y D a t a s e t s + + # ISO Modula-2 Standard Library Modules Dataset + iso_stdlib_module_identifiers = ( + # TO DO + ) + + # ISO Modula-2 Standard Library Types Dataset + iso_stdlib_type_identifiers = ( + # TO DO + ) + + # ISO Modula-2 Standard Library Procedures Dataset + iso_stdlib_proc_identifiers = ( + # TO DO + ) + + # ISO Modula-2 Standard Library Variables Dataset + iso_stdlib_var_identifiers = ( + # TO DO + ) + + # ISO Modula-2 Standard Library Constants Dataset + iso_stdlib_const_identifiers = ( + # TO DO + ) + +# M 2 R 1 0 S t a n d a r d L i b r a r y D a t a s e t s + + # Modula-2 R10 Standard Library ADTs Dataset + m2r10_stdlib_adt_identifiers = ( + 'BCD', 'LONGBCD', 'BITSET', 'SHORTBITSET', 'LONGBITSET', + 'LONGLONGBITSET', 'COMPLEX', 'LONGCOMPLEX', 'SHORTCARD', 'LONGLONGCARD', + 'SHORTINT', 'LONGLONGINT', 'POSINT', 'SHORTPOSINT', 'LONGPOSINT', + 'LONGLONGPOSINT', 'BITSET8', 'BITSET16', 'BITSET32', 'BITSET64', + 'BITSET128', 'BS8', 'BS16', 'BS32', 'BS64', 'BS128', 'CARDINAL8', + 'CARDINAL16', 'CARDINAL32', 'CARDINAL64', 'CARDINAL128', 'CARD8', + 'CARD16', 'CARD32', 'CARD64', 'CARD128', 'INTEGER8', 'INTEGER16', + 'INTEGER32', 'INTEGER64', 'INTEGER128', 'INT8', 'INT16', 'INT32', + 'INT64', 'INT128', 'STRING', 'UNISTRING', + ) + + # Modula-2 R10 Standard Library Blueprints Dataset + m2r10_stdlib_blueprint_identifiers = ( + 'ProtoRoot', 'ProtoComputational', 'ProtoNumeric', 'ProtoScalar', + 'ProtoNonScalar', 'ProtoCardinal', 'ProtoInteger', 'ProtoReal', + 'ProtoComplex', 'ProtoVector', 'ProtoTuple', 'ProtoCompArray', + 'ProtoCollection', 'ProtoStaticArray', 'ProtoStaticSet', + 'ProtoStaticString', 'ProtoArray', 'ProtoString', 'ProtoSet', + 'ProtoMultiSet', 'ProtoDictionary', 'ProtoMultiDict', 'ProtoExtension', + 'ProtoIO', 'ProtoCardMath', 'ProtoIntMath', 'ProtoRealMath', + ) + + # Modula-2 R10 Standard Library Modules Dataset + m2r10_stdlib_module_identifiers = ( + 'ASCII', 'BooleanIO', 'CharIO', 'UnicharIO', 'OctetIO', + 'CardinalIO', 'LongCardIO', 'IntegerIO', 'LongIntIO', 'RealIO', + 'LongRealIO', 'BCDIO', 'LongBCDIO', 'CardMath', 'LongCardMath', + 'IntMath', 'LongIntMath', 'RealMath', 'LongRealMath', 'BCDMath', + 'LongBCDMath', 'FileIO', 'FileSystem', 'Storage', 'IOSupport', + ) + + # Modula-2 R10 Standard Library Types Dataset + m2r10_stdlib_type_identifiers = ( + 'File', 'Status', + # TO BE COMPLETED + ) + + # Modula-2 R10 Standard Library Procedures Dataset + m2r10_stdlib_proc_identifiers = ( + 'ALLOCATE', 'DEALLOCATE', 'SIZE', + # TO BE COMPLETED + ) + + # Modula-2 R10 Standard Library Variables Dataset + m2r10_stdlib_var_identifiers = ( + 'stdIn', 'stdOut', 'stdErr', + ) + + # Modula-2 R10 Standard Library Constants Dataset + m2r10_stdlib_const_identifiers = ( + 'pi', 'tau', + ) + +# D i a l e c t s + + # Dialect modes + dialects = ( + 'unknown', + 'm2pim', 'm2iso', 'm2r10', 'objm2', + 'm2iso+aglet', 'm2pim+gm2', 'm2iso+p1', 'm2iso+xds', + ) + +# D a t a b a s e s + + # Lexemes to Mark as Errors Database + lexemes_to_reject_db = { + # Lexemes to reject for unknown dialect + 'unknown': ( + # LEAVE THIS EMPTY + ), + # Lexemes to reject for PIM Modula-2 + 'm2pim': ( + pim_lexemes_to_reject, + ), + # Lexemes to reject for ISO Modula-2 + 'm2iso': ( + iso_lexemes_to_reject, + ), + # Lexemes to reject for Modula-2 R10 + 'm2r10': ( + m2r10_lexemes_to_reject, + ), + # Lexemes to reject for Objective Modula-2 + 'objm2': ( + objm2_lexemes_to_reject, + ), + # Lexemes to reject for Aglet Modula-2 + 'm2iso+aglet': ( + iso_lexemes_to_reject, + ), + # Lexemes to reject for GNU Modula-2 + 'm2pim+gm2': ( + pim_lexemes_to_reject, + ), + # Lexemes to reject for p1 Modula-2 + 'm2iso+p1': ( + iso_lexemes_to_reject, + ), + # Lexemes to reject for XDS Modula-2 + 'm2iso+xds': ( + iso_lexemes_to_reject, + ), + } + + # Reserved Words Database + reserved_words_db = { + # Reserved words for unknown dialect + 'unknown': ( + common_reserved_words, + pim_additional_reserved_words, + iso_additional_reserved_words, + m2r10_additional_reserved_words, + ), + + # Reserved words for PIM Modula-2 + 'm2pim': ( + common_reserved_words, + pim_additional_reserved_words, + ), + + # Reserved words for Modula-2 R10 + 'm2iso': ( + common_reserved_words, + iso_additional_reserved_words, + ), + + # Reserved words for ISO Modula-2 + 'm2r10': ( + common_reserved_words, + m2r10_additional_reserved_words, + ), + + # Reserved words for Objective Modula-2 + 'objm2': ( + common_reserved_words, + m2r10_additional_reserved_words, + objm2_additional_reserved_words, + ), + + # Reserved words for Aglet Modula-2 Extensions + 'm2iso+aglet': ( + common_reserved_words, + iso_additional_reserved_words, + aglet_additional_reserved_words, + ), + + # Reserved words for GNU Modula-2 Extensions + 'm2pim+gm2': ( + common_reserved_words, + pim_additional_reserved_words, + gm2_additional_reserved_words, + ), + + # Reserved words for p1 Modula-2 Extensions + 'm2iso+p1': ( + common_reserved_words, + iso_additional_reserved_words, + p1_additional_reserved_words, + ), + + # Reserved words for XDS Modula-2 Extensions + 'm2iso+xds': ( + common_reserved_words, + iso_additional_reserved_words, + xds_additional_reserved_words, + ), + } + + # Builtins Database + builtins_db = { + # Builtins for unknown dialect + 'unknown': ( + common_builtins, + pim_additional_builtins, + iso_additional_builtins, + m2r10_additional_builtins, + ), + + # Builtins for PIM Modula-2 + 'm2pim': ( + common_builtins, + pim_additional_builtins, + ), + + # Builtins for ISO Modula-2 + 'm2iso': ( + common_builtins, + iso_additional_builtins, + ), + + # Builtins for ISO Modula-2 + 'm2r10': ( + common_builtins, + m2r10_additional_builtins, + ), + + # Builtins for Objective Modula-2 + 'objm2': ( + common_builtins, + m2r10_additional_builtins, + objm2_additional_builtins, + ), + + # Builtins for Aglet Modula-2 Extensions + 'm2iso+aglet': ( + common_builtins, + iso_additional_builtins, + aglet_additional_builtins, + ), + + # Builtins for GNU Modula-2 Extensions + 'm2pim+gm2': ( + common_builtins, + pim_additional_builtins, + gm2_additional_builtins, + ), + + # Builtins for p1 Modula-2 Extensions + 'm2iso+p1': ( + common_builtins, + iso_additional_builtins, + p1_additional_builtins, + ), + + # Builtins for XDS Modula-2 Extensions + 'm2iso+xds': ( + common_builtins, + iso_additional_builtins, + xds_additional_builtins, + ), + } + + # Pseudo-Module Builtins Database + pseudo_builtins_db = { + # Builtins for unknown dialect + 'unknown': ( + common_pseudo_builtins, + pim_additional_pseudo_builtins, + iso_additional_pseudo_builtins, + m2r10_additional_pseudo_builtins, + ), + + # Builtins for PIM Modula-2 + 'm2pim': ( + common_pseudo_builtins, + pim_additional_pseudo_builtins, + ), + + # Builtins for ISO Modula-2 + 'm2iso': ( + common_pseudo_builtins, + iso_additional_pseudo_builtins, + ), + + # Builtins for ISO Modula-2 + 'm2r10': ( + common_pseudo_builtins, + m2r10_additional_pseudo_builtins, + ), + + # Builtins for Objective Modula-2 + 'objm2': ( + common_pseudo_builtins, + m2r10_additional_pseudo_builtins, + objm2_additional_pseudo_builtins, + ), + + # Builtins for Aglet Modula-2 Extensions + 'm2iso+aglet': ( + common_pseudo_builtins, + iso_additional_pseudo_builtins, + aglet_additional_pseudo_builtins, + ), + + # Builtins for GNU Modula-2 Extensions + 'm2pim+gm2': ( + common_pseudo_builtins, + pim_additional_pseudo_builtins, + gm2_additional_pseudo_builtins, + ), + + # Builtins for p1 Modula-2 Extensions + 'm2iso+p1': ( + common_pseudo_builtins, + iso_additional_pseudo_builtins, + p1_additional_pseudo_builtins, + ), + + # Builtins for XDS Modula-2 Extensions + 'm2iso+xds': ( + common_pseudo_builtins, + iso_additional_pseudo_builtins, + xds_additional_pseudo_builtins, + ), + } + + # Standard Library ADTs Database + stdlib_adts_db = { + # Empty entry for unknown dialect + 'unknown': ( + # LEAVE THIS EMPTY + ), + # Standard Library ADTs for PIM Modula-2 + 'm2pim': ( + # No first class library types + ), + + # Standard Library ADTs for ISO Modula-2 + 'm2iso': ( + # No first class library types + ), + + # Standard Library ADTs for Modula-2 R10 + 'm2r10': ( + m2r10_stdlib_adt_identifiers, + ), + + # Standard Library ADTs for Objective Modula-2 + 'objm2': ( + m2r10_stdlib_adt_identifiers, + ), + + # Standard Library ADTs for Aglet Modula-2 + 'm2iso+aglet': ( + # No first class library types + ), + + # Standard Library ADTs for GNU Modula-2 + 'm2pim+gm2': ( + # No first class library types + ), + + # Standard Library ADTs for p1 Modula-2 + 'm2iso+p1': ( + # No first class library types + ), + + # Standard Library ADTs for XDS Modula-2 + 'm2iso+xds': ( + # No first class library types + ), + } + + # Standard Library Modules Database + stdlib_modules_db = { + # Empty entry for unknown dialect + 'unknown': ( + # LEAVE THIS EMPTY + ), + # Standard Library Modules for PIM Modula-2 + 'm2pim': ( + pim_stdlib_module_identifiers, + ), + + # Standard Library Modules for ISO Modula-2 + 'm2iso': ( + iso_stdlib_module_identifiers, + ), + + # Standard Library Modules for Modula-2 R10 + 'm2r10': ( + m2r10_stdlib_blueprint_identifiers, + m2r10_stdlib_module_identifiers, + m2r10_stdlib_adt_identifiers, + ), + + # Standard Library Modules for Objective Modula-2 + 'objm2': ( + m2r10_stdlib_blueprint_identifiers, + m2r10_stdlib_module_identifiers, + ), + + # Standard Library Modules for Aglet Modula-2 + 'm2iso+aglet': ( + iso_stdlib_module_identifiers, + ), + + # Standard Library Modules for GNU Modula-2 + 'm2pim+gm2': ( + pim_stdlib_module_identifiers, + ), + + # Standard Library Modules for p1 Modula-2 + 'm2iso+p1': ( + iso_stdlib_module_identifiers, + ), + + # Standard Library Modules for XDS Modula-2 + 'm2iso+xds': ( + iso_stdlib_module_identifiers, + ), + } + + # Standard Library Types Database + stdlib_types_db = { + # Empty entry for unknown dialect + 'unknown': ( + # LEAVE THIS EMPTY + ), + # Standard Library Types for PIM Modula-2 + 'm2pim': ( + pim_stdlib_type_identifiers, + ), + + # Standard Library Types for ISO Modula-2 + 'm2iso': ( + iso_stdlib_type_identifiers, + ), + + # Standard Library Types for Modula-2 R10 + 'm2r10': ( + m2r10_stdlib_type_identifiers, + ), + + # Standard Library Types for Objective Modula-2 + 'objm2': ( + m2r10_stdlib_type_identifiers, + ), + + # Standard Library Types for Aglet Modula-2 + 'm2iso+aglet': ( + iso_stdlib_type_identifiers, + ), + + # Standard Library Types for GNU Modula-2 + 'm2pim+gm2': ( + pim_stdlib_type_identifiers, + ), + + # Standard Library Types for p1 Modula-2 + 'm2iso+p1': ( + iso_stdlib_type_identifiers, + ), + + # Standard Library Types for XDS Modula-2 + 'm2iso+xds': ( + iso_stdlib_type_identifiers, + ), + } + + # Standard Library Procedures Database + stdlib_procedures_db = { + # Empty entry for unknown dialect + 'unknown': ( + # LEAVE THIS EMPTY + ), + # Standard Library Procedures for PIM Modula-2 + 'm2pim': ( + pim_stdlib_proc_identifiers, + ), + + # Standard Library Procedures for ISO Modula-2 + 'm2iso': ( + iso_stdlib_proc_identifiers, + ), + + # Standard Library Procedures for Modula-2 R10 + 'm2r10': ( + m2r10_stdlib_proc_identifiers, + ), + + # Standard Library Procedures for Objective Modula-2 + 'objm2': ( + m2r10_stdlib_proc_identifiers, + ), + + # Standard Library Procedures for Aglet Modula-2 + 'm2iso+aglet': ( + iso_stdlib_proc_identifiers, + ), + + # Standard Library Procedures for GNU Modula-2 + 'm2pim+gm2': ( + pim_stdlib_proc_identifiers, + ), + + # Standard Library Procedures for p1 Modula-2 + 'm2iso+p1': ( + iso_stdlib_proc_identifiers, + ), + + # Standard Library Procedures for XDS Modula-2 + 'm2iso+xds': ( + iso_stdlib_proc_identifiers, + ), + } + + # Standard Library Variables Database + stdlib_variables_db = { + # Empty entry for unknown dialect + 'unknown': ( + # LEAVE THIS EMPTY + ), + # Standard Library Variables for PIM Modula-2 + 'm2pim': ( + pim_stdlib_var_identifiers, + ), + + # Standard Library Variables for ISO Modula-2 + 'm2iso': ( + iso_stdlib_var_identifiers, + ), + + # Standard Library Variables for Modula-2 R10 + 'm2r10': ( + m2r10_stdlib_var_identifiers, + ), + + # Standard Library Variables for Objective Modula-2 + 'objm2': ( + m2r10_stdlib_var_identifiers, + ), + + # Standard Library Variables for Aglet Modula-2 + 'm2iso+aglet': ( + iso_stdlib_var_identifiers, + ), + + # Standard Library Variables for GNU Modula-2 + 'm2pim+gm2': ( + pim_stdlib_var_identifiers, + ), + + # Standard Library Variables for p1 Modula-2 + 'm2iso+p1': ( + iso_stdlib_var_identifiers, + ), + + # Standard Library Variables for XDS Modula-2 + 'm2iso+xds': ( + iso_stdlib_var_identifiers, + ), + } + + # Standard Library Constants Database + stdlib_constants_db = { + # Empty entry for unknown dialect + 'unknown': ( + # LEAVE THIS EMPTY + ), + # Standard Library Constants for PIM Modula-2 + 'm2pim': ( + pim_stdlib_const_identifiers, + ), + + # Standard Library Constants for ISO Modula-2 + 'm2iso': ( + iso_stdlib_const_identifiers, + ), + + # Standard Library Constants for Modula-2 R10 + 'm2r10': ( + m2r10_stdlib_const_identifiers, + ), + + # Standard Library Constants for Objective Modula-2 + 'objm2': ( + m2r10_stdlib_const_identifiers, + ), + + # Standard Library Constants for Aglet Modula-2 + 'm2iso+aglet': ( + iso_stdlib_const_identifiers, + ), + + # Standard Library Constants for GNU Modula-2 + 'm2pim+gm2': ( + pim_stdlib_const_identifiers, + ), + + # Standard Library Constants for p1 Modula-2 + 'm2iso+p1': ( + iso_stdlib_const_identifiers, + ), + + # Standard Library Constants for XDS Modula-2 + 'm2iso+xds': ( + iso_stdlib_const_identifiers, + ), + } + +# M e t h o d s + + # initialise a lexer instance + def __init__(self, **options): + # + # check dialect options + # + dialects = get_list_opt(options, 'dialect', []) + # + for dialect_option in dialects: + if dialect_option in self.dialects[1:-1]: + # valid dialect option found + self.set_dialect(dialect_option) + break + # + # Fallback Mode (DEFAULT) + else: + # no valid dialect option + self.set_dialect('unknown') + # + self.dialect_set_by_tag = False + # + # check style options + # + styles = get_list_opt(options, 'style', []) + # + # use lowercase mode for Algol style + if 'algol' in styles or 'algol_nu' in styles: + self.algol_publication_mode = True + else: + self.algol_publication_mode = False + # + # Check option flags + # + self.treat_stdlib_adts_as_builtins = get_bool_opt( + options, 'treat_stdlib_adts_as_builtins', True) + # + # call superclass initialiser + RegexLexer.__init__(self, **options) + + # Set lexer to a specified dialect + def set_dialect(self, dialect_id): + # + # if __debug__: + # print 'entered set_dialect with arg: ', dialect_id + # + # check dialect name against known dialects + if dialect_id not in self.dialects: + dialect = 'unknown' # default + else: + dialect = dialect_id + # + # compose lexemes to reject set + lexemes_to_reject_set = set() + # add each list of reject lexemes for this dialect + for list in self.lexemes_to_reject_db[dialect]: + lexemes_to_reject_set.update(set(list)) + # + # compose reserved words set + reswords_set = set() + # add each list of reserved words for this dialect + for list in self.reserved_words_db[dialect]: + reswords_set.update(set(list)) + # + # compose builtins set + builtins_set = set() + # add each list of builtins for this dialect excluding reserved words + for list in self.builtins_db[dialect]: + builtins_set.update(set(list).difference(reswords_set)) + # + # compose pseudo-builtins set + pseudo_builtins_set = set() + # add each list of builtins for this dialect excluding reserved words + for list in self.pseudo_builtins_db[dialect]: + pseudo_builtins_set.update(set(list).difference(reswords_set)) + # + # compose ADTs set + adts_set = set() + # add each list of ADTs for this dialect excluding reserved words + for list in self.stdlib_adts_db[dialect]: + adts_set.update(set(list).difference(reswords_set)) + # + # compose modules set + modules_set = set() + # add each list of builtins for this dialect excluding builtins + for list in self.stdlib_modules_db[dialect]: + modules_set.update(set(list).difference(builtins_set)) + # + # compose types set + types_set = set() + # add each list of types for this dialect excluding builtins + for list in self.stdlib_types_db[dialect]: + types_set.update(set(list).difference(builtins_set)) + # + # compose procedures set + procedures_set = set() + # add each list of procedures for this dialect excluding builtins + for list in self.stdlib_procedures_db[dialect]: + procedures_set.update(set(list).difference(builtins_set)) + # + # compose variables set + variables_set = set() + # add each list of variables for this dialect excluding builtins + for list in self.stdlib_variables_db[dialect]: + variables_set.update(set(list).difference(builtins_set)) + # + # compose constants set + constants_set = set() + # add each list of constants for this dialect excluding builtins + for list in self.stdlib_constants_db[dialect]: + constants_set.update(set(list).difference(builtins_set)) + # + # update lexer state + self.dialect = dialect + self.lexemes_to_reject = lexemes_to_reject_set + self.reserved_words = reswords_set + self.builtins = builtins_set + self.pseudo_builtins = pseudo_builtins_set + self.adts = adts_set + self.modules = modules_set + self.types = types_set + self.procedures = procedures_set + self.variables = variables_set + self.constants = constants_set + # + # if __debug__: + # print 'exiting set_dialect' + # print ' self.dialect: ', self.dialect + # print ' self.lexemes_to_reject: ', self.lexemes_to_reject + # print ' self.reserved_words: ', self.reserved_words + # print ' self.builtins: ', self.builtins + # print ' self.pseudo_builtins: ', self.pseudo_builtins + # print ' self.adts: ', self.adts + # print ' self.modules: ', self.modules + # print ' self.types: ', self.types + # print ' self.procedures: ', self.procedures + # print ' self.variables: ', self.variables + # print ' self.types: ', self.types + # print ' self.constants: ', self.constants + + # Extracts a dialect name from a dialect tag comment string and checks + # the extracted name against known dialects. If a match is found, the + # matching name is returned, otherwise dialect id 'unknown' is returned + def get_dialect_from_dialect_tag(self, dialect_tag): + # + # if __debug__: + # print 'entered get_dialect_from_dialect_tag with arg: ', dialect_tag + # + # constants + left_tag_delim = '(*!' + right_tag_delim = '*)' + left_tag_delim_len = len(left_tag_delim) + right_tag_delim_len = len(right_tag_delim) + indicator_start = left_tag_delim_len + indicator_end = -(right_tag_delim_len) + # + # check comment string for dialect indicator + if len(dialect_tag) > (left_tag_delim_len + right_tag_delim_len) \ + and dialect_tag.startswith(left_tag_delim) \ + and dialect_tag.endswith(right_tag_delim): + # + # if __debug__: + # print 'dialect tag found' + # + # extract dialect indicator + indicator = dialect_tag[indicator_start:indicator_end] + # + # if __debug__: + # print 'extracted: ', indicator + # + # check against known dialects + for index in range(1, len(self.dialects)): + # + # if __debug__: + # print 'dialects[', index, ']: ', self.dialects[index] + # + if indicator == self.dialects[index]: + # + # if __debug__: + # print 'matching dialect found' + # + # indicator matches known dialect + return indicator + else: + # indicator does not match any dialect + return 'unknown' # default + else: + # invalid indicator string + return 'unknown' # default + + # intercept the token stream, modify token attributes and return them + def get_tokens_unprocessed(self, text): + for index, token, value in RegexLexer.get_tokens_unprocessed(self, text): + # + # check for dialect tag if dialect has not been set by tag + if not self.dialect_set_by_tag and token == Comment.Special: + indicated_dialect = self.get_dialect_from_dialect_tag(value) + if indicated_dialect != 'unknown': + # token is a dialect indicator + # reset reserved words and builtins + self.set_dialect(indicated_dialect) + self.dialect_set_by_tag = True + # + # check for reserved words, predefined and stdlib identifiers + if token is Name: + if value in self.reserved_words: + token = Keyword.Reserved + if self.algol_publication_mode: + value = value.lower() + # + elif value in self.builtins: + token = Name.Builtin + if self.algol_publication_mode: + value = value.lower() + # + elif value in self.pseudo_builtins: + token = Name.Builtin.Pseudo + if self.algol_publication_mode: + value = value.lower() + # + elif value in self.adts: + if not self.treat_stdlib_adts_as_builtins: + token = Name.Namespace + else: + token = Name.Builtin.Pseudo + if self.algol_publication_mode: + value = value.lower() + # + elif value in self.modules: + token = Name.Namespace + # + elif value in self.types: + token = Name.Class + # + elif value in self.procedures: + token = Name.Function + # + elif value in self.variables: + token = Name.Variable + # + elif value in self.constants: + token = Name.Constant + # + elif token in Number: + # + # mark prefix number literals as error for PIM and ISO dialects + if self.dialect not in ('unknown', 'm2r10', 'objm2'): + if "'" in value or value[0:2] in ('0b', '0x', '0u'): + token = Error + # + elif self.dialect in ('m2r10', 'objm2'): + # mark base-8 number literals as errors for M2 R10 and ObjM2 + if token is Number.Oct: + token = Error + # mark suffix base-16 literals as errors for M2 R10 and ObjM2 + elif token is Number.Hex and 'H' in value: + token = Error + # mark real numbers with E as errors for M2 R10 and ObjM2 + elif token is Number.Float and 'E' in value: + token = Error + # + elif token in Comment: + # + # mark single line comment as error for PIM and ISO dialects + if token is Comment.Single: + if self.dialect not in ('unknown', 'm2r10', 'objm2'): + token = Error + # + if token is Comment.Preproc: + # mark ISO pragma as error for PIM dialects + if value.startswith('<*') and \ + self.dialect.startswith('m2pim'): + token = Error + # mark PIM pragma as comment for other dialects + elif value.startswith('(*$') and \ + self.dialect != 'unknown' and \ + not self.dialect.startswith('m2pim'): + token = Comment.Multiline + # + else: # token is neither Name nor Comment + # + # mark lexemes matching the dialect's error token set as errors + if value in self.lexemes_to_reject: + token = Error + # + # substitute lexemes when in Algol mode + if self.algol_publication_mode: + if value == '#': + value = '≠' + elif value == '<=': + value = '≤' + elif value == '>=': + value = '≥' + elif value == '==': + value = '≡' + elif value == '*.': + value = '•' + + # return result + yield index, token, value + + def analyse_text(text): + """It's Pascal-like, but does not use FUNCTION -- uses PROCEDURE + instead.""" + + # Check if this looks like Pascal, if not, bail out early + if not ('(*' in text and '*)' in text and ':=' in text): + return + + result = 0 + # Procedure is in Modula2 + if re.search(r'\bPROCEDURE\b', text): + result += 0.6 + + # FUNCTION is only valid in Pascal, but not in Modula2 + if re.search(r'\bFUNCTION\b', text): + result = 0.0 + + return result diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/monte.py b/.venv/lib/python3.8/site-packages/pygments/lexers/monte.py new file mode 100644 index 00000000..311fa172 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/monte.py @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.monte + ~~~~~~~~~~~~~~~~~~~~~ + + Lexer for the Monte programming language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.token import Comment, Error, Keyword, Name, Number, Operator, \ + Punctuation, String, Whitespace +from pygments.lexer import RegexLexer, include, words + +__all__ = ['MonteLexer'] + + +# `var` handled separately +# `interface` handled separately +_declarations = ['bind', 'def', 'fn', 'object'] +_methods = ['method', 'to'] +_keywords = [ + 'as', 'break', 'catch', 'continue', 'else', 'escape', 'exit', 'exports', + 'extends', 'finally', 'for', 'guards', 'if', 'implements', 'import', + 'in', 'match', 'meta', 'pass', 'return', 'switch', 'try', 'via', 'when', + 'while', +] +_operators = [ + # Unary + '~', '!', + # Binary + '+', '-', '*', '/', '%', '**', '&', '|', '^', '<<', '>>', + # Binary augmented + '+=', '-=', '*=', '/=', '%=', '**=', '&=', '|=', '^=', '<<=', '>>=', + # Comparison + '==', '!=', '<', '<=', '>', '>=', '<=>', + # Patterns and assignment + ':=', '?', '=~', '!~', '=>', + # Calls and sends + '.', '<-', '->', +] +_escape_pattern = ( + r'(?:\\x[0-9a-fA-F]{2}|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|' + r'\\["\'\\bftnr])') +# _char = _escape_chars + [('.', String.Char)] +_identifier = r'[_a-zA-Z]\w*' + +_constants = [ + # Void constants + 'null', + # Bool constants + 'false', 'true', + # Double constants + 'Infinity', 'NaN', + # Special objects + 'M', 'Ref', 'throw', 'traceln', +] + +_guards = [ + 'Any', 'Binding', 'Bool', 'Bytes', 'Char', 'DeepFrozen', 'Double', + 'Empty', 'Int', 'List', 'Map', 'Near', 'NullOk', 'Same', 'Selfless', + 'Set', 'Str', 'SubrangeGuard', 'Transparent', 'Void', +] + +_safeScope = [ + '_accumulateList', '_accumulateMap', '_auditedBy', '_bind', + '_booleanFlow', '_comparer', '_equalizer', '_iterForever', '_loop', + '_makeBytes', '_makeDouble', '_makeFinalSlot', '_makeInt', '_makeList', + '_makeMap', '_makeMessageDesc', '_makeOrderedSpace', '_makeParamDesc', + '_makeProtocolDesc', '_makeSourceSpan', '_makeString', '_makeVarSlot', + '_makeVerbFacet', '_mapExtract', '_matchSame', '_quasiMatcher', + '_slotToBinding', '_splitList', '_suchThat', '_switchFailed', + '_validateFor', 'b__quasiParser', 'eval', 'import', 'm__quasiParser', + 'makeBrandPair', 'makeLazySlot', 'safeScope', 'simple__quasiParser', +] + + +class MonteLexer(RegexLexer): + """ + Lexer for the `Monte `_ programming language. + + .. versionadded:: 2.2 + """ + name = 'Monte' + aliases = ['monte'] + filenames = ['*.mt'] + + tokens = { + 'root': [ + # Comments + (r'#[^\n]*\n', Comment), + + # Docstrings + # Apologies for the non-greedy matcher here. + (r'/\*\*.*?\*/', String.Doc), + + # `var` declarations + (r'\bvar\b', Keyword.Declaration, 'var'), + + # `interface` declarations + (r'\binterface\b', Keyword.Declaration, 'interface'), + + # method declarations + (words(_methods, prefix='\\b', suffix='\\b'), + Keyword, 'method'), + + # All other declarations + (words(_declarations, prefix='\\b', suffix='\\b'), + Keyword.Declaration), + + # Keywords + (words(_keywords, prefix='\\b', suffix='\\b'), Keyword), + + # Literals + ('[+-]?0x[_0-9a-fA-F]+', Number.Hex), + (r'[+-]?[_0-9]+\.[_0-9]*([eE][+-]?[_0-9]+)?', Number.Float), + ('[+-]?[_0-9]+', Number.Integer), + ("'", String.Double, 'char'), + ('"', String.Double, 'string'), + + # Quasiliterals + ('`', String.Backtick, 'ql'), + + # Operators + (words(_operators), Operator), + + # Verb operators + (_identifier + '=', Operator.Word), + + # Safe scope constants + (words(_constants, prefix='\\b', suffix='\\b'), + Keyword.Pseudo), + + # Safe scope guards + (words(_guards, prefix='\\b', suffix='\\b'), Keyword.Type), + + # All other safe scope names + (words(_safeScope, prefix='\\b', suffix='\\b'), + Name.Builtin), + + # Identifiers + (_identifier, Name), + + # Punctuation + (r'\(|\)|\{|\}|\[|\]|:|,', Punctuation), + + # Whitespace + (' +', Whitespace), + + # Definite lexer errors + ('=', Error), + ], + 'char': [ + # It is definitely an error to have a char of width == 0. + ("'", Error, 'root'), + (_escape_pattern, String.Escape, 'charEnd'), + ('.', String.Char, 'charEnd'), + ], + 'charEnd': [ + ("'", String.Char, '#pop:2'), + # It is definitely an error to have a char of width > 1. + ('.', Error), + ], + # The state of things coming into an interface. + 'interface': [ + (' +', Whitespace), + (_identifier, Name.Class, '#pop'), + include('root'), + ], + # The state of things coming into a method. + 'method': [ + (' +', Whitespace), + (_identifier, Name.Function, '#pop'), + include('root'), + ], + 'string': [ + ('"', String.Double, 'root'), + (_escape_pattern, String.Escape), + (r'\n', String.Double), + ('.', String.Double), + ], + 'ql': [ + ('`', String.Backtick, 'root'), + (r'\$' + _escape_pattern, String.Escape), + (r'\$\$', String.Escape), + (r'@@', String.Escape), + (r'\$\{', String.Interpol, 'qlNest'), + (r'@\{', String.Interpol, 'qlNest'), + (r'\$' + _identifier, Name), + ('@' + _identifier, Name), + ('.', String.Backtick), + ], + 'qlNest': [ + (r'\}', String.Interpol, '#pop'), + include('root'), + ], + # The state of things immediately following `var`. + 'var': [ + (' +', Whitespace), + (_identifier, Name.Variable, '#pop'), + include('root'), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/mosel.py b/.venv/lib/python3.8/site-packages/pygments/lexers/mosel.py new file mode 100644 index 00000000..02cc5e3e --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/mosel.py @@ -0,0 +1,448 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.mosel + ~~~~~~~~~~~~~~~~~~~~~ + + Lexers for the mosel language. + http://www.fico.com/en/products/fico-xpress-optimization + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation + +__all__ = ['MoselLexer'] + +FUNCTIONS = ( + # core functions + '_', + 'abs', + 'arctan', + 'asproc', + 'assert', + 'bitflip', + 'bitneg', + 'bitset', + 'bitshift', + 'bittest', + 'bitval', + 'ceil', + 'cos', + 'create', + 'currentdate', + 'currenttime', + 'cutelt', + 'cutfirst', + 'cuthead', + 'cutlast', + 'cuttail', + 'datablock', + 'delcell', + 'exists', + 'exit', + 'exp', + 'exportprob', + 'fclose', + 'fflush', + 'finalize', + 'findfirst', + 'findlast', + 'floor', + 'fopen', + 'fselect', + 'fskipline', + 'fwrite', + 'fwrite_', + 'fwriteln', + 'fwriteln_', + 'getact', + 'getcoeff', + 'getcoeffs', + 'getdual', + 'getelt', + 'getfid', + 'getfirst', + 'getfname', + 'gethead', + 'getlast', + 'getobjval', + 'getparam', + 'getrcost', + 'getreadcnt', + 'getreverse', + 'getsize', + 'getslack', + 'getsol', + 'gettail', + 'gettype', + 'getvars', + 'isdynamic', + 'iseof', + 'isfinite', + 'ishidden', + 'isinf', + 'isnan', + 'isodd', + 'ln', + 'localsetparam', + 'log', + 'makesos1', + 'makesos2', + 'maxlist', + 'memoryuse', + 'minlist', + 'newmuid', + 'publish', + 'random', + 'read', + 'readln', + 'reset', + 'restoreparam', + 'reverse', + 'round', + 'setcoeff', + 'sethidden', + 'setioerr', + 'setmatherr', + 'setname', + 'setparam', + 'setrandseed', + 'setrange', + 'settype', + 'sin', + 'splithead', + 'splittail', + 'sqrt', + 'strfmt', + 'substr', + 'timestamp', + 'unpublish', + 'versionnum', + 'versionstr', + 'write', + 'write_', + 'writeln', + 'writeln_', + + # mosel exam mmxprs | sed -n -e "s/ [pf][a-z]* \([a-zA-Z0-9_]*\).*/'\1',/p" | sort -u + 'addcut', + 'addcuts', + 'addmipsol', + 'basisstability', + 'calcsolinfo', + 'clearmipdir', + 'clearmodcut', + 'command', + 'copysoltoinit', + 'crossoverlpsol', + 'defdelayedrows', + 'defsecurevecs', + 'delcuts', + 'dropcuts', + 'estimatemarginals', + 'fixglobal', + 'flushmsgq', + 'getbstat', + 'getcnlist', + 'getcplist', + 'getdualray', + 'getiis', + 'getiissense', + 'getiistype', + 'getinfcause', + 'getinfeas', + 'getlb', + 'getlct', + 'getleft', + 'getloadedlinctrs', + 'getloadedmpvars', + 'getname', + 'getprimalray', + 'getprobstat', + 'getrange', + 'getright', + 'getsensrng', + 'getsize', + 'getsol', + 'gettype', + 'getub', + 'getvars', + 'gety', + 'hasfeature', + 'implies', + 'indicator', + 'initglobal', + 'ishidden', + 'isiisvalid', + 'isintegral', + 'loadbasis', + 'loadcuts', + 'loadlpsol', + 'loadmipsol', + 'loadprob', + 'maximise', + 'maximize', + 'minimise', + 'minimize', + 'postsolve', + 'readbasis', + 'readdirs', + 'readsol', + 'refinemipsol', + 'rejectintsol', + 'repairinfeas', + 'repairinfeas_deprec', + 'resetbasis', + 'resetiis', + 'resetsol', + 'savebasis', + 'savemipsol', + 'savesol', + 'savestate', + 'selectsol', + 'setarchconsistency', + 'setbstat', + 'setcallback', + 'setcbcutoff', + 'setgndata', + 'sethidden', + 'setlb', + 'setmipdir', + 'setmodcut', + 'setsol', + 'setub', + 'setucbdata', + 'stopoptimise', + 'stopoptimize', + 'storecut', + 'storecuts', + 'unloadprob', + 'uselastbarsol', + 'writebasis', + 'writedirs', + 'writeprob', + 'writesol', + 'xor', + 'xprs_addctr', + 'xprs_addindic', + + # mosel exam mmsystem | sed -n -e "s/ [pf][a-z]* \([a-zA-Z0-9_]*\).*/'\1',/p" | sort -u + 'addmonths', + 'copytext', + 'cuttext', + 'deltext', + 'endswith', + 'erase', + 'expandpath', + 'fcopy', + 'fdelete', + 'findfiles', + 'findtext', + 'fmove', + 'formattext', + 'getasnumber', + 'getchar', + 'getcwd', + 'getdate', + 'getday', + 'getdaynum', + 'getdays', + 'getdirsep', + 'getdsoparam', + 'getendparse', + 'getenv', + 'getfsize', + 'getfstat', + 'getftime', + 'gethour', + 'getminute', + 'getmonth', + 'getmsec', + 'getoserrmsg', + 'getoserror', + 'getpathsep', + 'getqtype', + 'getsecond', + 'getsepchar', + 'getsize', + 'getstart', + 'getsucc', + 'getsysinfo', + 'getsysstat', + 'gettime', + 'gettmpdir', + 'gettrim', + 'getweekday', + 'getyear', + 'inserttext', + 'isvalid', + 'jointext', + 'makedir', + 'makepath', + 'newtar', + 'newzip', + 'nextfield', + 'openpipe', + 'parseextn', + 'parseint', + 'parsereal', + 'parsetext', + 'pastetext', + 'pathmatch', + 'pathsplit', + 'qsort', + 'quote', + 'readtextline', + 'regmatch', + 'regreplace', + 'removedir', + 'removefiles', + 'setchar', + 'setdate', + 'setday', + 'setdsoparam', + 'setendparse', + 'setenv', + 'sethour', + 'setminute', + 'setmonth', + 'setmsec', + 'setoserror', + 'setqtype', + 'setsecond', + 'setsepchar', + 'setstart', + 'setsucc', + 'settime', + 'settrim', + 'setyear', + 'sleep', + 'splittext', + 'startswith', + 'system', + 'tarlist', + 'textfmt', + 'tolower', + 'toupper', + 'trim', + 'untar', + 'unzip', + 'ziplist', + + # mosel exam mmjobs | sed -n -e "s/ [pf][a-z]* \([a-zA-Z0-9_]*\).*/'\1',/p" | sort -u + 'canceltimer', + 'clearaliases', + 'compile', + 'connect', + 'detach', + 'disconnect', + 'dropnextevent', + 'findxsrvs', + 'getaliases', + 'getannidents', + 'getannotations', + 'getbanner', + 'getclass', + 'getdsoprop', + 'getdsopropnum', + 'getexitcode', + 'getfromgid', + 'getfromid', + 'getfromuid', + 'getgid', + 'gethostalias', + 'getid', + 'getmodprop', + 'getmodpropnum', + 'getnextevent', + 'getnode', + 'getrmtid', + 'getstatus', + 'getsysinfo', + 'gettimer', + 'getuid', + 'getvalue', + 'isqueueempty', + 'load', + 'nullevent', + 'peeknextevent', + 'resetmodpar', + 'run', + 'send', + 'setcontrol', + 'setdefstream', + 'setgid', + 'sethostalias', + 'setmodpar', + 'settimer', + 'setuid', + 'setworkdir', + 'stop', + 'unload', + 'wait', + 'waitexpired', + 'waitfor', + 'waitforend', +) + + +class MoselLexer(RegexLexer): + """ + For the Mosel optimization language. + + .. versionadded:: 2.6 + """ + name = 'Mosel' + aliases = ['mosel'] + filenames = ['*.mos'] + + tokens = { + 'root': [ + (r'\n', Text), + (r'\s+', Text.Whitespace), + (r'!.*?\n', Comment.Single), + (r'\(!(.|\n)*?!\)', Comment.Multiline), + (words(( + 'and', 'as', 'break', 'case', 'count', 'declarations', 'do', + 'dynamic', 'elif', 'else', 'end-', 'end', 'evaluation', 'false', + 'forall', 'forward', 'from', 'function', 'hashmap', 'if', + 'imports', 'include', 'initialisations', 'initializations', 'inter', + 'max', 'min', 'model', 'namespace', 'next', 'not', 'nsgroup', + 'nssearch', 'of', 'options', 'or', 'package', 'parameters', + 'procedure', 'public', 'prod', 'record', 'repeat', 'requirements', + 'return', 'sum', 'then', 'to', 'true', 'union', 'until', 'uses', + 'version', 'while', 'with'), prefix=r'\b', suffix=r'\b'), + Keyword.Builtin), + (words(( + 'range', 'array', 'set', 'list', 'mpvar', 'mpproblem', 'linctr', + 'nlctr', 'integer', 'string', 'real', 'boolean', 'text', 'time', + 'date', 'datetime', 'returned', 'Model', 'Mosel', 'counter', + 'xmldoc', 'is_sos1', 'is_sos2', 'is_integer', 'is_binary', + 'is_continuous', 'is_free', 'is_semcont', 'is_semint', + 'is_partint'), prefix=r'\b', suffix=r'\b'), + Keyword.Type), + (r'(\+|\-|\*|/|=|<=|>=|\||\^|<|>|<>|\.\.|\.|:=|::|:|in|mod|div)', + Operator), + (r'[()\[\]{},;]+', Punctuation), + (words(FUNCTIONS, prefix=r'\b', suffix=r'\b'), Name.Function), + (r'(\d+\.(?!\.)\d*|\.(?!.)\d+)([eE][+-]?\d+)?', Number.Float), + (r'\d+([eE][+-]?\d+)?', Number.Integer), + (r'[+-]?Infinity', Number.Integer), + (r'0[xX][0-9a-fA-F]+', Number), + (r'"', String.Double, 'double_quote'), + (r'\'', String.Single, 'single_quote'), + (r'(\w+|(\.(?!\.)))', Text), + ], + 'single_quote': [ + (r'\'', String.Single, '#pop'), + (r'[^\']+', String.Single), + ], + 'double_quote': [ + (r'(\\"|\\[0-7]{1,3}\D|\\[abfnrtv]|\\\\)', String.Escape), + (r'\"', String.Double, '#pop'), + (r'[^"\\]+', String.Double), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/ncl.py b/.venv/lib/python3.8/site-packages/pygments/lexers/ncl.py new file mode 100644 index 00000000..6e094e00 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/ncl.py @@ -0,0 +1,894 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.ncl + ~~~~~~~~~~~~~~~~~~~ + + Lexers for NCAR Command Language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include, words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation + +__all__ = ['NCLLexer'] + + +class NCLLexer(RegexLexer): + """ + Lexer for NCL code. + + .. versionadded:: 2.2 + """ + name = 'NCL' + aliases = ['ncl'] + filenames = ['*.ncl'] + mimetypes = ['text/ncl'] + flags = re.MULTILINE + + tokens = { + 'root': [ + (r';.*\n', Comment), + include('strings'), + include('core'), + (r'[a-zA-Z_]\w*', Name), + include('nums'), + (r'[\s]+', Text), + ], + 'core': [ + # Statements + (words(( + 'begin', 'break', 'continue', 'create', 'defaultapp', 'do', + 'else', 'end', 'external', 'exit', 'True', 'False', 'file', 'function', + 'getvalues', 'graphic', 'group', 'if', 'list', 'load', 'local', + 'new', '_Missing', 'Missing', 'noparent', 'procedure', + 'quit', 'QUIT', 'Quit', 'record', 'return', 'setvalues', 'stop', + 'then', 'while'), prefix=r'\b', suffix=r'\s*\b'), + Keyword), + + # Data Types + (words(( + 'ubyte', 'uint', 'uint64', 'ulong', 'string', 'byte', + 'character', 'double', 'float', 'integer', 'int64', 'logical', + 'long', 'short', 'ushort', 'enumeric', 'numeric', 'snumeric'), + prefix=r'\b', suffix=r'\s*\b'), + Keyword.Type), + + # Operators + (r'[\%^*+\-/<>]', Operator), + + # punctuation: + (r'[\[\]():@$!&|.,\\{}]', Punctuation), + (r'[=:]', Punctuation), + + # Intrinsics + (words(( + 'abs', 'acos', 'addfile', 'addfiles', 'all', 'angmom_atm', 'any', + 'area_conserve_remap', 'area_hi2lores', 'area_poly_sphere', + 'asciiread', 'asciiwrite', 'asin', 'atan', 'atan2', 'attsetvalues', + 'avg', 'betainc', 'bin_avg', 'bin_sum', 'bw_bandpass_filter', + 'cancor', 'cbinread', 'cbinwrite', 'cd_calendar', 'cd_inv_calendar', + 'cdfbin_p', 'cdfbin_pr', 'cdfbin_s', 'cdfbin_xn', 'cdfchi_p', + 'cdfchi_x', 'cdfgam_p', 'cdfgam_x', 'cdfnor_p', 'cdfnor_x', + 'cdft_p', 'cdft_t', 'ceil', 'center_finite_diff', + 'center_finite_diff_n', 'cfftb', 'cfftf', 'cfftf_frq_reorder', + 'charactertodouble', 'charactertofloat', 'charactertointeger', + 'charactertolong', 'charactertoshort', 'charactertostring', + 'chartodouble', 'chartofloat', 'chartoint', 'chartointeger', + 'chartolong', 'chartoshort', 'chartostring', 'chiinv', 'clear', + 'color_index_to_rgba', 'conform', 'conform_dims', 'cos', 'cosh', + 'count_unique_values', 'covcorm', 'covcorm_xy', 'craybinnumrec', + 'craybinrecread', 'create_graphic', 'csa1', 'csa1d', 'csa1s', + 'csa1x', 'csa1xd', 'csa1xs', 'csa2', 'csa2d', 'csa2l', 'csa2ld', + 'csa2ls', 'csa2lx', 'csa2lxd', 'csa2lxs', 'csa2s', 'csa2x', + 'csa2xd', 'csa2xs', 'csa3', 'csa3d', 'csa3l', 'csa3ld', 'csa3ls', + 'csa3lx', 'csa3lxd', 'csa3lxs', 'csa3s', 'csa3x', 'csa3xd', + 'csa3xs', 'csc2s', 'csgetp', 'css2c', 'cssetp', 'cssgrid', 'csstri', + 'csvoro', 'cumsum', 'cz2ccm', 'datatondc', 'day_of_week', + 'day_of_year', 'days_in_month', 'default_fillvalue', 'delete', + 'depth_to_pres', 'destroy', 'determinant', 'dewtemp_trh', + 'dgeevx_lapack', 'dim_acumrun_n', 'dim_avg', 'dim_avg_n', + 'dim_avg_wgt', 'dim_avg_wgt_n', 'dim_cumsum', 'dim_cumsum_n', + 'dim_gamfit_n', 'dim_gbits', 'dim_max', 'dim_max_n', 'dim_median', + 'dim_median_n', 'dim_min', 'dim_min_n', 'dim_num', 'dim_num_n', + 'dim_numrun_n', 'dim_pqsort', 'dim_pqsort_n', 'dim_product', + 'dim_product_n', 'dim_rmsd', 'dim_rmsd_n', 'dim_rmvmean', + 'dim_rmvmean_n', 'dim_rmvmed', 'dim_rmvmed_n', 'dim_spi_n', + 'dim_standardize', 'dim_standardize_n', 'dim_stat4', 'dim_stat4_n', + 'dim_stddev', 'dim_stddev_n', 'dim_sum', 'dim_sum_n', 'dim_sum_wgt', + 'dim_sum_wgt_n', 'dim_variance', 'dim_variance_n', 'dimsizes', + 'doubletobyte', 'doubletochar', 'doubletocharacter', + 'doubletofloat', 'doubletoint', 'doubletointeger', 'doubletolong', + 'doubletoshort', 'dpres_hybrid_ccm', 'dpres_plevel', 'draw', + 'draw_color_palette', 'dsgetp', 'dsgrid2', 'dsgrid2d', 'dsgrid2s', + 'dsgrid3', 'dsgrid3d', 'dsgrid3s', 'dspnt2', 'dspnt2d', 'dspnt2s', + 'dspnt3', 'dspnt3d', 'dspnt3s', 'dssetp', 'dtrend', 'dtrend_msg', + 'dtrend_msg_n', 'dtrend_n', 'dtrend_quadratic', + 'dtrend_quadratic_msg_n', 'dv2uvf', 'dv2uvg', 'dz_height', + 'echo_off', 'echo_on', 'eof2data', 'eof_varimax', 'eofcor', + 'eofcor_pcmsg', 'eofcor_ts', 'eofcov', 'eofcov_pcmsg', 'eofcov_ts', + 'eofunc', 'eofunc_ts', 'eofunc_varimax', 'equiv_sample_size', 'erf', + 'erfc', 'esacr', 'esacv', 'esccr', 'esccv', 'escorc', 'escorc_n', + 'escovc', 'exit', 'exp', 'exp_tapersh', 'exp_tapersh_wgts', + 'exp_tapershC', 'ezfftb', 'ezfftb_n', 'ezfftf', 'ezfftf_n', + 'f2fosh', 'f2foshv', 'f2fsh', 'f2fshv', 'f2gsh', 'f2gshv', 'fabs', + 'fbindirread', 'fbindirwrite', 'fbinnumrec', 'fbinread', + 'fbinrecread', 'fbinrecwrite', 'fbinwrite', 'fft2db', 'fft2df', + 'fftshift', 'fileattdef', 'filechunkdimdef', 'filedimdef', + 'fileexists', 'filegrpdef', 'filevarattdef', 'filevarchunkdef', + 'filevarcompressleveldef', 'filevardef', 'filevardimsizes', + 'filwgts_lancos', 'filwgts_lanczos', 'filwgts_normal', + 'floattobyte', 'floattochar', 'floattocharacter', 'floattoint', + 'floattointeger', 'floattolong', 'floattoshort', 'floor', + 'fluxEddy', 'fo2fsh', 'fo2fshv', 'fourier_info', 'frame', 'fspan', + 'ftcurv', 'ftcurvd', 'ftcurvi', 'ftcurvp', 'ftcurvpi', 'ftcurvps', + 'ftcurvs', 'ftest', 'ftgetp', 'ftkurv', 'ftkurvd', 'ftkurvp', + 'ftkurvpd', 'ftsetp', 'ftsurf', 'g2fsh', 'g2fshv', 'g2gsh', + 'g2gshv', 'gamma', 'gammainc', 'gaus', 'gaus_lobat', + 'gaus_lobat_wgt', 'gc_aangle', 'gc_clkwise', 'gc_dangle', + 'gc_inout', 'gc_latlon', 'gc_onarc', 'gc_pnt2gc', 'gc_qarea', + 'gc_tarea', 'generate_2d_array', 'get_color_index', + 'get_color_rgba', 'get_cpu_time', 'get_isolines', 'get_ncl_version', + 'get_script_name', 'get_script_prefix_name', 'get_sphere_radius', + 'get_unique_values', 'getbitsone', 'getenv', 'getfiledimsizes', + 'getfilegrpnames', 'getfilepath', 'getfilevaratts', + 'getfilevarchunkdimsizes', 'getfilevardims', 'getfilevardimsizes', + 'getfilevarnames', 'getfilevartypes', 'getvaratts', 'getvardims', + 'gradsf', 'gradsg', 'greg2jul', 'grid2triple', 'hlsrgb', 'hsvrgb', + 'hydro', 'hyi2hyo', 'idsfft', 'igradsf', 'igradsg', 'ilapsf', + 'ilapsg', 'ilapvf', 'ilapvg', 'ind', 'ind_resolve', 'int2p', + 'int2p_n', 'integertobyte', 'integertochar', 'integertocharacter', + 'integertoshort', 'inttobyte', 'inttochar', 'inttoshort', + 'inverse_matrix', 'isatt', 'isbigendian', 'isbyte', 'ischar', + 'iscoord', 'isdefined', 'isdim', 'isdimnamed', 'isdouble', + 'isenumeric', 'isfile', 'isfilepresent', 'isfilevar', + 'isfilevaratt', 'isfilevarcoord', 'isfilevardim', 'isfloat', + 'isfunc', 'isgraphic', 'isint', 'isint64', 'isinteger', + 'isleapyear', 'islogical', 'islong', 'ismissing', 'isnan_ieee', + 'isnumeric', 'ispan', 'isproc', 'isshort', 'issnumeric', 'isstring', + 'isubyte', 'isuint', 'isuint64', 'isulong', 'isunlimited', + 'isunsigned', 'isushort', 'isvar', 'jul2greg', 'kmeans_as136', + 'kolsm2_n', 'kron_product', 'lapsf', 'lapsg', 'lapvf', 'lapvg', + 'latlon2utm', 'lclvl', 'lderuvf', 'lderuvg', 'linint1', 'linint1_n', + 'linint2', 'linint2_points', 'linmsg', 'linmsg_n', 'linrood_latwgt', + 'linrood_wgt', 'list_files', 'list_filevars', 'list_hlus', + 'list_procfuncs', 'list_vars', 'ListAppend', 'ListCount', + 'ListGetType', 'ListIndex', 'ListIndexFromName', 'ListPop', + 'ListPush', 'ListSetType', 'loadscript', 'local_max', 'local_min', + 'log', 'log10', 'longtobyte', 'longtochar', 'longtocharacter', + 'longtoint', 'longtointeger', 'longtoshort', 'lspoly', 'lspoly_n', + 'mask', 'max', 'maxind', 'min', 'minind', 'mixed_layer_depth', + 'mixhum_ptd', 'mixhum_ptrh', 'mjo_cross_coh2pha', + 'mjo_cross_segment', 'moc_globe_atl', 'monthday', 'natgrid', + 'natgridd', 'natgrids', 'ncargpath', 'ncargversion', 'ndctodata', + 'ndtooned', 'new', 'NewList', 'ngezlogo', 'nggcog', 'nggetp', + 'nglogo', 'ngsetp', 'NhlAddAnnotation', 'NhlAddData', + 'NhlAddOverlay', 'NhlAddPrimitive', 'NhlAppGetDefaultParentId', + 'NhlChangeWorkstation', 'NhlClassName', 'NhlClearWorkstation', + 'NhlDataPolygon', 'NhlDataPolyline', 'NhlDataPolymarker', + 'NhlDataToNDC', 'NhlDestroy', 'NhlDraw', 'NhlFrame', 'NhlFreeColor', + 'NhlGetBB', 'NhlGetClassResources', 'NhlGetErrorObjectId', + 'NhlGetNamedColorIndex', 'NhlGetParentId', + 'NhlGetParentWorkstation', 'NhlGetWorkspaceObjectId', + 'NhlIsAllocatedColor', 'NhlIsApp', 'NhlIsDataComm', 'NhlIsDataItem', + 'NhlIsDataSpec', 'NhlIsTransform', 'NhlIsView', 'NhlIsWorkstation', + 'NhlName', 'NhlNDCPolygon', 'NhlNDCPolyline', 'NhlNDCPolymarker', + 'NhlNDCToData', 'NhlNewColor', 'NhlNewDashPattern', 'NhlNewMarker', + 'NhlPalGetDefined', 'NhlRemoveAnnotation', 'NhlRemoveData', + 'NhlRemoveOverlay', 'NhlRemovePrimitive', 'NhlSetColor', + 'NhlSetDashPattern', 'NhlSetMarker', 'NhlUpdateData', + 'NhlUpdateWorkstation', 'nice_mnmxintvl', 'nngetaspectd', + 'nngetaspects', 'nngetp', 'nngetsloped', 'nngetslopes', 'nngetwts', + 'nngetwtsd', 'nnpnt', 'nnpntd', 'nnpntend', 'nnpntendd', + 'nnpntinit', 'nnpntinitd', 'nnpntinits', 'nnpnts', 'nnsetp', 'num', + 'obj_anal_ic', 'omega_ccm', 'onedtond', 'overlay', 'paleo_outline', + 'pdfxy_bin', 'poisson_grid_fill', 'pop_remap', 'potmp_insitu_ocn', + 'prcwater_dp', 'pres2hybrid', 'pres_hybrid_ccm', 'pres_sigma', + 'print', 'print_table', 'printFileVarSummary', 'printVarSummary', + 'product', 'pslec', 'pslhor', 'pslhyp', 'qsort', 'rand', + 'random_chi', 'random_gamma', 'random_normal', 'random_setallseed', + 'random_uniform', 'rcm2points', 'rcm2rgrid', 'rdsstoi', + 'read_colormap_file', 'reg_multlin', 'regcoef', 'regCoef_n', + 'regline', 'relhum', 'replace_ieeenan', 'reshape', 'reshape_ind', + 'rgba_to_color_index', 'rgbhls', 'rgbhsv', 'rgbyiq', 'rgrid2rcm', + 'rhomb_trunc', 'rip_cape_2d', 'rip_cape_3d', 'round', 'rtest', + 'runave', 'runave_n', 'set_default_fillvalue', 'set_sphere_radius', + 'setfileoption', 'sfvp2uvf', 'sfvp2uvg', 'shaec', 'shagc', + 'shgetnp', 'shgetp', 'shgrid', 'shorttobyte', 'shorttochar', + 'shorttocharacter', 'show_ascii', 'shsec', 'shsetp', 'shsgc', + 'shsgc_R42', 'sigma2hybrid', 'simpeq', 'simpne', 'sin', + 'sindex_yrmo', 'sinh', 'sizeof', 'sleep', 'smth9', 'snindex_yrmo', + 'solve_linsys', 'span_color_indexes', 'span_color_rgba', + 'sparse_matrix_mult', 'spcorr', 'spcorr_n', 'specx_anal', + 'specxy_anal', 'spei', 'sprintf', 'sprinti', 'sqrt', 'sqsort', + 'srand', 'stat2', 'stat4', 'stat_medrng', 'stat_trim', + 'status_exit', 'stdatmus_p2tdz', 'stdatmus_z2tdp', 'stddev', + 'str_capital', 'str_concat', 'str_fields_count', 'str_get_cols', + 'str_get_dq', 'str_get_field', 'str_get_nl', 'str_get_sq', + 'str_get_tab', 'str_index_of_substr', 'str_insert', 'str_is_blank', + 'str_join', 'str_left_strip', 'str_lower', 'str_match', + 'str_match_ic', 'str_match_ic_regex', 'str_match_ind', + 'str_match_ind_ic', 'str_match_ind_ic_regex', 'str_match_ind_regex', + 'str_match_regex', 'str_right_strip', 'str_split', + 'str_split_by_length', 'str_split_csv', 'str_squeeze', 'str_strip', + 'str_sub_str', 'str_switch', 'str_upper', 'stringtochar', + 'stringtocharacter', 'stringtodouble', 'stringtofloat', + 'stringtoint', 'stringtointeger', 'stringtolong', 'stringtoshort', + 'strlen', 'student_t', 'sum', 'svd_lapack', 'svdcov', 'svdcov_sv', + 'svdstd', 'svdstd_sv', 'system', 'systemfunc', 'tan', 'tanh', + 'taper', 'taper_n', 'tdclrs', 'tdctri', 'tdcudp', 'tdcurv', + 'tddtri', 'tdez2d', 'tdez3d', 'tdgetp', 'tdgrds', 'tdgrid', + 'tdgtrs', 'tdinit', 'tditri', 'tdlbla', 'tdlblp', 'tdlbls', + 'tdline', 'tdlndp', 'tdlnpa', 'tdlpdp', 'tdmtri', 'tdotri', + 'tdpara', 'tdplch', 'tdprpa', 'tdprpi', 'tdprpt', 'tdsetp', + 'tdsort', 'tdstri', 'tdstrs', 'tdttri', 'thornthwaite', 'tobyte', + 'tochar', 'todouble', 'tofloat', 'toint', 'toint64', 'tointeger', + 'tolong', 'toshort', 'tosigned', 'tostring', 'tostring_with_format', + 'totype', 'toubyte', 'touint', 'touint64', 'toulong', 'tounsigned', + 'toushort', 'trend_manken', 'tri_trunc', 'triple2grid', + 'triple2grid2d', 'trop_wmo', 'ttest', 'typeof', 'undef', + 'unique_string', 'update', 'ushorttoint', 'ut_calendar', + 'ut_inv_calendar', 'utm2latlon', 'uv2dv_cfd', 'uv2dvf', 'uv2dvg', + 'uv2sfvpf', 'uv2sfvpg', 'uv2vr_cfd', 'uv2vrdvf', 'uv2vrdvg', + 'uv2vrf', 'uv2vrg', 'v5d_close', 'v5d_create', 'v5d_setLowLev', + 'v5d_setUnits', 'v5d_write', 'v5d_write_var', 'variance', 'vhaec', + 'vhagc', 'vhsec', 'vhsgc', 'vibeta', 'vinth2p', 'vinth2p_ecmwf', + 'vinth2p_ecmwf_nodes', 'vinth2p_nodes', 'vintp2p_ecmwf', 'vr2uvf', + 'vr2uvg', 'vrdv2uvf', 'vrdv2uvg', 'wavelet', 'wavelet_default', + 'weibull', 'wgt_area_smooth', 'wgt_areaave', 'wgt_areaave2', + 'wgt_arearmse', 'wgt_arearmse2', 'wgt_areasum2', 'wgt_runave', + 'wgt_runave_n', 'wgt_vert_avg_beta', 'wgt_volave', 'wgt_volave_ccm', + 'wgt_volrmse', 'wgt_volrmse_ccm', 'where', 'wk_smooth121', 'wmbarb', + 'wmbarbmap', 'wmdrft', 'wmgetp', 'wmlabs', 'wmsetp', 'wmstnm', + 'wmvect', 'wmvectmap', 'wmvlbl', 'wrf_avo', 'wrf_cape_2d', + 'wrf_cape_3d', 'wrf_dbz', 'wrf_eth', 'wrf_helicity', 'wrf_ij_to_ll', + 'wrf_interp_1d', 'wrf_interp_2d_xy', 'wrf_interp_3d_z', + 'wrf_latlon_to_ij', 'wrf_ll_to_ij', 'wrf_omega', 'wrf_pvo', + 'wrf_rh', 'wrf_slp', 'wrf_smooth_2d', 'wrf_td', 'wrf_tk', + 'wrf_updraft_helicity', 'wrf_uvmet', 'wrf_virtual_temp', + 'wrf_wetbulb', 'wrf_wps_close_int', 'wrf_wps_open_int', + 'wrf_wps_rddata_int', 'wrf_wps_rdhead_int', 'wrf_wps_read_int', + 'wrf_wps_write_int', 'write_matrix', 'write_table', 'yiqrgb', + 'z2geouv', 'zonal_mpsi', 'addfiles_GetVar', 'advect_variable', + 'area_conserve_remap_Wrap', 'area_hi2lores_Wrap', + 'array_append_record', 'assignFillValue', 'byte2flt', + 'byte2flt_hdf', 'calcDayAnomTLL', 'calcMonAnomLLLT', + 'calcMonAnomLLT', 'calcMonAnomTLL', 'calcMonAnomTLLL', + 'calculate_monthly_values', 'cd_convert', 'changeCase', + 'changeCaseChar', 'clmDayTLL', 'clmDayTLLL', 'clmMon2clmDay', + 'clmMonLLLT', 'clmMonLLT', 'clmMonTLL', 'clmMonTLLL', 'closest_val', + 'copy_VarAtts', 'copy_VarCoords', 'copy_VarCoords_1', + 'copy_VarCoords_2', 'copy_VarMeta', 'copyatt', 'crossp3', + 'cshstringtolist', 'cssgrid_Wrap', 'dble2flt', 'decimalPlaces', + 'delete_VarAtts', 'dim_avg_n_Wrap', 'dim_avg_wgt_n_Wrap', + 'dim_avg_wgt_Wrap', 'dim_avg_Wrap', 'dim_cumsum_n_Wrap', + 'dim_cumsum_Wrap', 'dim_max_n_Wrap', 'dim_min_n_Wrap', + 'dim_rmsd_n_Wrap', 'dim_rmsd_Wrap', 'dim_rmvmean_n_Wrap', + 'dim_rmvmean_Wrap', 'dim_rmvmed_n_Wrap', 'dim_rmvmed_Wrap', + 'dim_standardize_n_Wrap', 'dim_standardize_Wrap', + 'dim_stddev_n_Wrap', 'dim_stddev_Wrap', 'dim_sum_n_Wrap', + 'dim_sum_wgt_n_Wrap', 'dim_sum_wgt_Wrap', 'dim_sum_Wrap', + 'dim_variance_n_Wrap', 'dim_variance_Wrap', 'dpres_plevel_Wrap', + 'dtrend_leftdim', 'dv2uvF_Wrap', 'dv2uvG_Wrap', 'eof_north', + 'eofcor_Wrap', 'eofcov_Wrap', 'eofunc_north', 'eofunc_ts_Wrap', + 'eofunc_varimax_reorder', 'eofunc_varimax_Wrap', 'eofunc_Wrap', + 'epsZero', 'f2fosh_Wrap', 'f2foshv_Wrap', 'f2fsh_Wrap', + 'f2fshv_Wrap', 'f2gsh_Wrap', 'f2gshv_Wrap', 'fbindirSwap', + 'fbinseqSwap1', 'fbinseqSwap2', 'flt2dble', 'flt2string', + 'fo2fsh_Wrap', 'fo2fshv_Wrap', 'g2fsh_Wrap', 'g2fshv_Wrap', + 'g2gsh_Wrap', 'g2gshv_Wrap', 'generate_resample_indices', + 'generate_sample_indices', 'generate_unique_indices', + 'genNormalDist', 'get1Dindex', 'get1Dindex_Collapse', + 'get1Dindex_Exclude', 'get_file_suffix', 'GetFillColor', + 'GetFillColorIndex', 'getFillValue', 'getind_latlon2d', + 'getVarDimNames', 'getVarFillValue', 'grib_stime2itime', + 'hyi2hyo_Wrap', 'ilapsF_Wrap', 'ilapsG_Wrap', 'ind_nearest_coord', + 'indStrSubset', 'int2dble', 'int2flt', 'int2p_n_Wrap', 'int2p_Wrap', + 'isMonotonic', 'isStrSubset', 'latGau', 'latGauWgt', 'latGlobeF', + 'latGlobeFo', 'latRegWgt', 'linint1_n_Wrap', 'linint1_Wrap', + 'linint2_points_Wrap', 'linint2_Wrap', 'local_max_1d', + 'local_min_1d', 'lonFlip', 'lonGlobeF', 'lonGlobeFo', 'lonPivot', + 'merge_levels_sfc', 'mod', 'month_to_annual', + 'month_to_annual_weighted', 'month_to_season', 'month_to_season12', + 'month_to_seasonN', 'monthly_total_to_daily_mean', 'nameDim', + 'natgrid_Wrap', 'NewCosWeight', 'niceLatLon2D', 'NormCosWgtGlobe', + 'numAsciiCol', 'numAsciiRow', 'numeric2int', + 'obj_anal_ic_deprecated', 'obj_anal_ic_Wrap', 'omega_ccm_driver', + 'omega_to_w', 'oneDtostring', 'pack_values', 'pattern_cor', 'pdfx', + 'pdfxy', 'pdfxy_conform', 'pot_temp', 'pot_vort_hybrid', + 'pot_vort_isobaric', 'pres2hybrid_Wrap', 'print_clock', + 'printMinMax', 'quadroots', 'rcm2points_Wrap', 'rcm2rgrid_Wrap', + 'readAsciiHead', 'readAsciiTable', 'reg_multlin_stats', + 'region_ind', 'regline_stats', 'relhum_ttd', 'replaceSingleChar', + 'RGBtoCmap', 'rgrid2rcm_Wrap', 'rho_mwjf', 'rm_single_dims', + 'rmAnnCycle1D', 'rmInsufData', 'rmMonAnnCycLLLT', 'rmMonAnnCycLLT', + 'rmMonAnnCycTLL', 'runave_n_Wrap', 'runave_Wrap', 'short2flt', + 'short2flt_hdf', 'shsgc_R42_Wrap', 'sign_f90', 'sign_matlab', + 'smth9_Wrap', 'smthClmDayTLL', 'smthClmDayTLLL', 'SqrtCosWeight', + 'stat_dispersion', 'static_stability', 'stdMonLLLT', 'stdMonLLT', + 'stdMonTLL', 'stdMonTLLL', 'symMinMaxPlt', 'table_attach_columns', + 'table_attach_rows', 'time_to_newtime', 'transpose', + 'triple2grid_Wrap', 'ut_convert', 'uv2dvF_Wrap', 'uv2dvG_Wrap', + 'uv2vrF_Wrap', 'uv2vrG_Wrap', 'vr2uvF_Wrap', 'vr2uvG_Wrap', + 'w_to_omega', 'wallClockElapseTime', 'wave_number_spc', + 'wgt_areaave_Wrap', 'wgt_runave_leftdim', 'wgt_runave_n_Wrap', + 'wgt_runave_Wrap', 'wgt_vertical_n', 'wind_component', + 'wind_direction', 'yyyyddd_to_yyyymmdd', 'yyyymm_time', + 'yyyymm_to_yyyyfrac', 'yyyymmdd_time', 'yyyymmdd_to_yyyyddd', + 'yyyymmdd_to_yyyyfrac', 'yyyymmddhh_time', 'yyyymmddhh_to_yyyyfrac', + 'zonal_mpsi_Wrap', 'zonalAve', 'calendar_decode2', 'cd_string', + 'kf_filter', 'run_cor', 'time_axis_labels', 'ut_string', + 'wrf_contour', 'wrf_map', 'wrf_map_overlay', 'wrf_map_overlays', + 'wrf_map_resources', 'wrf_map_zoom', 'wrf_overlay', 'wrf_overlays', + 'wrf_user_getvar', 'wrf_user_ij_to_ll', 'wrf_user_intrp2d', + 'wrf_user_intrp3d', 'wrf_user_latlon_to_ij', 'wrf_user_list_times', + 'wrf_user_ll_to_ij', 'wrf_user_unstagger', 'wrf_user_vert_interp', + 'wrf_vector', 'gsn_add_annotation', 'gsn_add_polygon', + 'gsn_add_polyline', 'gsn_add_polymarker', + 'gsn_add_shapefile_polygons', 'gsn_add_shapefile_polylines', + 'gsn_add_shapefile_polymarkers', 'gsn_add_text', 'gsn_attach_plots', + 'gsn_blank_plot', 'gsn_contour', 'gsn_contour_map', + 'gsn_contour_shade', 'gsn_coordinates', 'gsn_create_labelbar', + 'gsn_create_legend', 'gsn_create_text', + 'gsn_csm_attach_zonal_means', 'gsn_csm_blank_plot', + 'gsn_csm_contour', 'gsn_csm_contour_map', 'gsn_csm_contour_map_ce', + 'gsn_csm_contour_map_overlay', 'gsn_csm_contour_map_polar', + 'gsn_csm_hov', 'gsn_csm_lat_time', 'gsn_csm_map', 'gsn_csm_map_ce', + 'gsn_csm_map_polar', 'gsn_csm_pres_hgt', + 'gsn_csm_pres_hgt_streamline', 'gsn_csm_pres_hgt_vector', + 'gsn_csm_streamline', 'gsn_csm_streamline_contour_map', + 'gsn_csm_streamline_contour_map_ce', + 'gsn_csm_streamline_contour_map_polar', 'gsn_csm_streamline_map', + 'gsn_csm_streamline_map_ce', 'gsn_csm_streamline_map_polar', + 'gsn_csm_streamline_scalar', 'gsn_csm_streamline_scalar_map', + 'gsn_csm_streamline_scalar_map_ce', + 'gsn_csm_streamline_scalar_map_polar', 'gsn_csm_time_lat', + 'gsn_csm_vector', 'gsn_csm_vector_map', 'gsn_csm_vector_map_ce', + 'gsn_csm_vector_map_polar', 'gsn_csm_vector_scalar', + 'gsn_csm_vector_scalar_map', 'gsn_csm_vector_scalar_map_ce', + 'gsn_csm_vector_scalar_map_polar', 'gsn_csm_x2y', 'gsn_csm_x2y2', + 'gsn_csm_xy', 'gsn_csm_xy2', 'gsn_csm_xy3', 'gsn_csm_y', + 'gsn_define_colormap', 'gsn_draw_colormap', 'gsn_draw_named_colors', + 'gsn_histogram', 'gsn_labelbar_ndc', 'gsn_legend_ndc', 'gsn_map', + 'gsn_merge_colormaps', 'gsn_open_wks', 'gsn_panel', 'gsn_polygon', + 'gsn_polygon_ndc', 'gsn_polyline', 'gsn_polyline_ndc', + 'gsn_polymarker', 'gsn_polymarker_ndc', 'gsn_retrieve_colormap', + 'gsn_reverse_colormap', 'gsn_streamline', 'gsn_streamline_map', + 'gsn_streamline_scalar', 'gsn_streamline_scalar_map', 'gsn_table', + 'gsn_text', 'gsn_text_ndc', 'gsn_vector', 'gsn_vector_map', + 'gsn_vector_scalar', 'gsn_vector_scalar_map', 'gsn_xy', 'gsn_y', + 'hsv2rgb', 'maximize_output', 'namedcolor2rgb', 'namedcolor2rgba', + 'reset_device_coordinates', 'span_named_colors'), prefix=r'\b'), + Name.Builtin), + + # Resources + (words(( + 'amDataXF', 'amDataYF', 'amJust', 'amOn', 'amOrthogonalPosF', + 'amParallelPosF', 'amResizeNotify', 'amSide', 'amTrackData', + 'amViewId', 'amZone', 'appDefaultParent', 'appFileSuffix', + 'appResources', 'appSysDir', 'appUsrDir', 'caCopyArrays', + 'caXArray', 'caXCast', 'caXMaxV', 'caXMinV', 'caXMissingV', + 'caYArray', 'caYCast', 'caYMaxV', 'caYMinV', 'caYMissingV', + 'cnCellFillEdgeColor', 'cnCellFillMissingValEdgeColor', + 'cnConpackParams', 'cnConstFEnableFill', 'cnConstFLabelAngleF', + 'cnConstFLabelBackgroundColor', 'cnConstFLabelConstantSpacingF', + 'cnConstFLabelFont', 'cnConstFLabelFontAspectF', + 'cnConstFLabelFontColor', 'cnConstFLabelFontHeightF', + 'cnConstFLabelFontQuality', 'cnConstFLabelFontThicknessF', + 'cnConstFLabelFormat', 'cnConstFLabelFuncCode', 'cnConstFLabelJust', + 'cnConstFLabelOn', 'cnConstFLabelOrthogonalPosF', + 'cnConstFLabelParallelPosF', 'cnConstFLabelPerimColor', + 'cnConstFLabelPerimOn', 'cnConstFLabelPerimSpaceF', + 'cnConstFLabelPerimThicknessF', 'cnConstFLabelSide', + 'cnConstFLabelString', 'cnConstFLabelTextDirection', + 'cnConstFLabelZone', 'cnConstFUseInfoLabelRes', + 'cnExplicitLabelBarLabelsOn', 'cnExplicitLegendLabelsOn', + 'cnExplicitLineLabelsOn', 'cnFillBackgroundColor', 'cnFillColor', + 'cnFillColors', 'cnFillDotSizeF', 'cnFillDrawOrder', 'cnFillMode', + 'cnFillOn', 'cnFillOpacityF', 'cnFillPalette', 'cnFillPattern', + 'cnFillPatterns', 'cnFillScaleF', 'cnFillScales', 'cnFixFillBleed', + 'cnGridBoundFillColor', 'cnGridBoundFillPattern', + 'cnGridBoundFillScaleF', 'cnGridBoundPerimColor', + 'cnGridBoundPerimDashPattern', 'cnGridBoundPerimOn', + 'cnGridBoundPerimThicknessF', 'cnHighLabelAngleF', + 'cnHighLabelBackgroundColor', 'cnHighLabelConstantSpacingF', + 'cnHighLabelCount', 'cnHighLabelFont', 'cnHighLabelFontAspectF', + 'cnHighLabelFontColor', 'cnHighLabelFontHeightF', + 'cnHighLabelFontQuality', 'cnHighLabelFontThicknessF', + 'cnHighLabelFormat', 'cnHighLabelFuncCode', 'cnHighLabelPerimColor', + 'cnHighLabelPerimOn', 'cnHighLabelPerimSpaceF', + 'cnHighLabelPerimThicknessF', 'cnHighLabelString', 'cnHighLabelsOn', + 'cnHighLowLabelOverlapMode', 'cnHighUseLineLabelRes', + 'cnInfoLabelAngleF', 'cnInfoLabelBackgroundColor', + 'cnInfoLabelConstantSpacingF', 'cnInfoLabelFont', + 'cnInfoLabelFontAspectF', 'cnInfoLabelFontColor', + 'cnInfoLabelFontHeightF', 'cnInfoLabelFontQuality', + 'cnInfoLabelFontThicknessF', 'cnInfoLabelFormat', + 'cnInfoLabelFuncCode', 'cnInfoLabelJust', 'cnInfoLabelOn', + 'cnInfoLabelOrthogonalPosF', 'cnInfoLabelParallelPosF', + 'cnInfoLabelPerimColor', 'cnInfoLabelPerimOn', + 'cnInfoLabelPerimSpaceF', 'cnInfoLabelPerimThicknessF', + 'cnInfoLabelSide', 'cnInfoLabelString', 'cnInfoLabelTextDirection', + 'cnInfoLabelZone', 'cnLabelBarEndLabelsOn', 'cnLabelBarEndStyle', + 'cnLabelDrawOrder', 'cnLabelMasking', 'cnLabelScaleFactorF', + 'cnLabelScaleValueF', 'cnLabelScalingMode', 'cnLegendLevelFlags', + 'cnLevelCount', 'cnLevelFlag', 'cnLevelFlags', 'cnLevelSelectionMode', + 'cnLevelSpacingF', 'cnLevels', 'cnLineColor', 'cnLineColors', + 'cnLineDashPattern', 'cnLineDashPatterns', 'cnLineDashSegLenF', + 'cnLineDrawOrder', 'cnLineLabelAngleF', 'cnLineLabelBackgroundColor', + 'cnLineLabelConstantSpacingF', 'cnLineLabelCount', + 'cnLineLabelDensityF', 'cnLineLabelFont', 'cnLineLabelFontAspectF', + 'cnLineLabelFontColor', 'cnLineLabelFontColors', + 'cnLineLabelFontHeightF', 'cnLineLabelFontQuality', + 'cnLineLabelFontThicknessF', 'cnLineLabelFormat', + 'cnLineLabelFuncCode', 'cnLineLabelInterval', 'cnLineLabelPerimColor', + 'cnLineLabelPerimOn', 'cnLineLabelPerimSpaceF', + 'cnLineLabelPerimThicknessF', 'cnLineLabelPlacementMode', + 'cnLineLabelStrings', 'cnLineLabelsOn', 'cnLinePalette', + 'cnLineThicknessF', 'cnLineThicknesses', 'cnLinesOn', + 'cnLowLabelAngleF', 'cnLowLabelBackgroundColor', + 'cnLowLabelConstantSpacingF', 'cnLowLabelCount', 'cnLowLabelFont', + 'cnLowLabelFontAspectF', 'cnLowLabelFontColor', + 'cnLowLabelFontHeightF', 'cnLowLabelFontQuality', + 'cnLowLabelFontThicknessF', 'cnLowLabelFormat', 'cnLowLabelFuncCode', + 'cnLowLabelPerimColor', 'cnLowLabelPerimOn', 'cnLowLabelPerimSpaceF', + 'cnLowLabelPerimThicknessF', 'cnLowLabelString', 'cnLowLabelsOn', + 'cnLowUseHighLabelRes', 'cnMaxDataValueFormat', 'cnMaxLevelCount', + 'cnMaxLevelValF', 'cnMaxPointDistanceF', 'cnMinLevelValF', + 'cnMissingValFillColor', 'cnMissingValFillPattern', + 'cnMissingValFillScaleF', 'cnMissingValPerimColor', + 'cnMissingValPerimDashPattern', 'cnMissingValPerimGridBoundOn', + 'cnMissingValPerimOn', 'cnMissingValPerimThicknessF', + 'cnMonoFillColor', 'cnMonoFillPattern', 'cnMonoFillScale', + 'cnMonoLevelFlag', 'cnMonoLineColor', 'cnMonoLineDashPattern', + 'cnMonoLineLabelFontColor', 'cnMonoLineThickness', 'cnNoDataLabelOn', + 'cnNoDataLabelString', 'cnOutOfRangeFillColor', + 'cnOutOfRangeFillPattern', 'cnOutOfRangeFillScaleF', + 'cnOutOfRangePerimColor', 'cnOutOfRangePerimDashPattern', + 'cnOutOfRangePerimOn', 'cnOutOfRangePerimThicknessF', + 'cnRasterCellSizeF', 'cnRasterMinCellSizeF', 'cnRasterModeOn', + 'cnRasterSampleFactorF', 'cnRasterSmoothingOn', 'cnScalarFieldData', + 'cnSmoothingDistanceF', 'cnSmoothingOn', 'cnSmoothingTensionF', + 'cnSpanFillPalette', 'cnSpanLinePalette', 'ctCopyTables', + 'ctXElementSize', 'ctXMaxV', 'ctXMinV', 'ctXMissingV', 'ctXTable', + 'ctXTableLengths', 'ctXTableType', 'ctYElementSize', 'ctYMaxV', + 'ctYMinV', 'ctYMissingV', 'ctYTable', 'ctYTableLengths', + 'ctYTableType', 'dcDelayCompute', 'errBuffer', + 'errFileName', 'errFilePtr', 'errLevel', 'errPrint', 'errUnitNumber', + 'gsClipOn', 'gsColors', 'gsEdgeColor', 'gsEdgeDashPattern', + 'gsEdgeDashSegLenF', 'gsEdgeThicknessF', 'gsEdgesOn', + 'gsFillBackgroundColor', 'gsFillColor', 'gsFillDotSizeF', + 'gsFillIndex', 'gsFillLineThicknessF', 'gsFillOpacityF', + 'gsFillScaleF', 'gsFont', 'gsFontAspectF', 'gsFontColor', + 'gsFontHeightF', 'gsFontOpacityF', 'gsFontQuality', + 'gsFontThicknessF', 'gsLineColor', 'gsLineDashPattern', + 'gsLineDashSegLenF', 'gsLineLabelConstantSpacingF', 'gsLineLabelFont', + 'gsLineLabelFontAspectF', 'gsLineLabelFontColor', + 'gsLineLabelFontHeightF', 'gsLineLabelFontQuality', + 'gsLineLabelFontThicknessF', 'gsLineLabelFuncCode', + 'gsLineLabelString', 'gsLineOpacityF', 'gsLineThicknessF', + 'gsMarkerColor', 'gsMarkerIndex', 'gsMarkerOpacityF', 'gsMarkerSizeF', + 'gsMarkerThicknessF', 'gsSegments', 'gsTextAngleF', + 'gsTextConstantSpacingF', 'gsTextDirection', 'gsTextFuncCode', + 'gsTextJustification', 'gsnAboveYRefLineBarColors', + 'gsnAboveYRefLineBarFillScales', 'gsnAboveYRefLineBarPatterns', + 'gsnAboveYRefLineColor', 'gsnAddCyclic', 'gsnAttachBorderOn', + 'gsnAttachPlotsXAxis', 'gsnBelowYRefLineBarColors', + 'gsnBelowYRefLineBarFillScales', 'gsnBelowYRefLineBarPatterns', + 'gsnBelowYRefLineColor', 'gsnBoxMargin', 'gsnCenterString', + 'gsnCenterStringFontColor', 'gsnCenterStringFontHeightF', + 'gsnCenterStringFuncCode', 'gsnCenterStringOrthogonalPosF', + 'gsnCenterStringParallelPosF', 'gsnContourLineThicknessesScale', + 'gsnContourNegLineDashPattern', 'gsnContourPosLineDashPattern', + 'gsnContourZeroLineThicknessF', 'gsnDebugWriteFileName', 'gsnDraw', + 'gsnFrame', 'gsnHistogramBarWidthPercent', 'gsnHistogramBinIntervals', + 'gsnHistogramBinMissing', 'gsnHistogramBinWidth', + 'gsnHistogramClassIntervals', 'gsnHistogramCompare', + 'gsnHistogramComputePercentages', + 'gsnHistogramComputePercentagesNoMissing', + 'gsnHistogramDiscreteBinValues', 'gsnHistogramDiscreteClassValues', + 'gsnHistogramHorizontal', 'gsnHistogramMinMaxBinsOn', + 'gsnHistogramNumberOfBins', 'gsnHistogramPercentSign', + 'gsnHistogramSelectNiceIntervals', 'gsnLeftString', + 'gsnLeftStringFontColor', 'gsnLeftStringFontHeightF', + 'gsnLeftStringFuncCode', 'gsnLeftStringOrthogonalPosF', + 'gsnLeftStringParallelPosF', 'gsnMajorLatSpacing', + 'gsnMajorLonSpacing', 'gsnMaskLambertConformal', + 'gsnMaskLambertConformalOutlineOn', 'gsnMaximize', + 'gsnMinorLatSpacing', 'gsnMinorLonSpacing', 'gsnPanelBottom', + 'gsnPanelCenter', 'gsnPanelDebug', 'gsnPanelFigureStrings', + 'gsnPanelFigureStringsBackgroundFillColor', + 'gsnPanelFigureStringsFontHeightF', 'gsnPanelFigureStringsJust', + 'gsnPanelFigureStringsPerimOn', 'gsnPanelLabelBar', 'gsnPanelLeft', + 'gsnPanelMainFont', 'gsnPanelMainFontColor', + 'gsnPanelMainFontHeightF', 'gsnPanelMainString', 'gsnPanelRight', + 'gsnPanelRowSpec', 'gsnPanelScalePlotIndex', 'gsnPanelTop', + 'gsnPanelXF', 'gsnPanelXWhiteSpacePercent', 'gsnPanelYF', + 'gsnPanelYWhiteSpacePercent', 'gsnPaperHeight', 'gsnPaperMargin', + 'gsnPaperOrientation', 'gsnPaperWidth', 'gsnPolar', + 'gsnPolarLabelDistance', 'gsnPolarLabelFont', + 'gsnPolarLabelFontHeightF', 'gsnPolarLabelSpacing', 'gsnPolarTime', + 'gsnPolarUT', 'gsnRightString', 'gsnRightStringFontColor', + 'gsnRightStringFontHeightF', 'gsnRightStringFuncCode', + 'gsnRightStringOrthogonalPosF', 'gsnRightStringParallelPosF', + 'gsnScalarContour', 'gsnScale', 'gsnShape', 'gsnSpreadColorEnd', + 'gsnSpreadColorStart', 'gsnSpreadColors', 'gsnStringFont', + 'gsnStringFontColor', 'gsnStringFontHeightF', 'gsnStringFuncCode', + 'gsnTickMarksOn', 'gsnXAxisIrregular2Linear', 'gsnXAxisIrregular2Log', + 'gsnXRefLine', 'gsnXRefLineColor', 'gsnXRefLineDashPattern', + 'gsnXRefLineThicknessF', 'gsnXYAboveFillColors', 'gsnXYBarChart', + 'gsnXYBarChartBarWidth', 'gsnXYBarChartColors', + 'gsnXYBarChartColors2', 'gsnXYBarChartFillDotSizeF', + 'gsnXYBarChartFillLineThicknessF', 'gsnXYBarChartFillOpacityF', + 'gsnXYBarChartFillScaleF', 'gsnXYBarChartOutlineOnly', + 'gsnXYBarChartOutlineThicknessF', 'gsnXYBarChartPatterns', + 'gsnXYBarChartPatterns2', 'gsnXYBelowFillColors', 'gsnXYFillColors', + 'gsnXYFillOpacities', 'gsnXYLeftFillColors', 'gsnXYRightFillColors', + 'gsnYAxisIrregular2Linear', 'gsnYAxisIrregular2Log', 'gsnYRefLine', + 'gsnYRefLineColor', 'gsnYRefLineColors', 'gsnYRefLineDashPattern', + 'gsnYRefLineDashPatterns', 'gsnYRefLineThicknessF', + 'gsnYRefLineThicknesses', 'gsnZonalMean', 'gsnZonalMeanXMaxF', + 'gsnZonalMeanXMinF', 'gsnZonalMeanYRefLine', 'lbAutoManage', + 'lbBottomMarginF', 'lbBoxCount', 'lbBoxEndCapStyle', 'lbBoxFractions', + 'lbBoxLineColor', 'lbBoxLineDashPattern', 'lbBoxLineDashSegLenF', + 'lbBoxLineThicknessF', 'lbBoxLinesOn', 'lbBoxMajorExtentF', + 'lbBoxMinorExtentF', 'lbBoxSeparatorLinesOn', 'lbBoxSizing', + 'lbFillBackground', 'lbFillColor', 'lbFillColors', 'lbFillDotSizeF', + 'lbFillLineThicknessF', 'lbFillPattern', 'lbFillPatterns', + 'lbFillScaleF', 'lbFillScales', 'lbJustification', 'lbLabelAlignment', + 'lbLabelAngleF', 'lbLabelAutoStride', 'lbLabelBarOn', + 'lbLabelConstantSpacingF', 'lbLabelDirection', 'lbLabelFont', + 'lbLabelFontAspectF', 'lbLabelFontColor', 'lbLabelFontHeightF', + 'lbLabelFontQuality', 'lbLabelFontThicknessF', 'lbLabelFuncCode', + 'lbLabelJust', 'lbLabelOffsetF', 'lbLabelPosition', 'lbLabelStride', + 'lbLabelStrings', 'lbLabelsOn', 'lbLeftMarginF', 'lbMaxLabelLenF', + 'lbMinLabelSpacingF', 'lbMonoFillColor', 'lbMonoFillPattern', + 'lbMonoFillScale', 'lbOrientation', 'lbPerimColor', + 'lbPerimDashPattern', 'lbPerimDashSegLenF', 'lbPerimFill', + 'lbPerimFillColor', 'lbPerimOn', 'lbPerimThicknessF', + 'lbRasterFillOn', 'lbRightMarginF', 'lbTitleAngleF', + 'lbTitleConstantSpacingF', 'lbTitleDirection', 'lbTitleExtentF', + 'lbTitleFont', 'lbTitleFontAspectF', 'lbTitleFontColor', + 'lbTitleFontHeightF', 'lbTitleFontQuality', 'lbTitleFontThicknessF', + 'lbTitleFuncCode', 'lbTitleJust', 'lbTitleOffsetF', 'lbTitleOn', + 'lbTitlePosition', 'lbTitleString', 'lbTopMarginF', 'lgAutoManage', + 'lgBottomMarginF', 'lgBoxBackground', 'lgBoxLineColor', + 'lgBoxLineDashPattern', 'lgBoxLineDashSegLenF', 'lgBoxLineThicknessF', + 'lgBoxLinesOn', 'lgBoxMajorExtentF', 'lgBoxMinorExtentF', + 'lgDashIndex', 'lgDashIndexes', 'lgItemCount', 'lgItemOrder', + 'lgItemPlacement', 'lgItemPositions', 'lgItemType', 'lgItemTypes', + 'lgJustification', 'lgLabelAlignment', 'lgLabelAngleF', + 'lgLabelAutoStride', 'lgLabelConstantSpacingF', 'lgLabelDirection', + 'lgLabelFont', 'lgLabelFontAspectF', 'lgLabelFontColor', + 'lgLabelFontHeightF', 'lgLabelFontQuality', 'lgLabelFontThicknessF', + 'lgLabelFuncCode', 'lgLabelJust', 'lgLabelOffsetF', 'lgLabelPosition', + 'lgLabelStride', 'lgLabelStrings', 'lgLabelsOn', 'lgLeftMarginF', + 'lgLegendOn', 'lgLineColor', 'lgLineColors', 'lgLineDashSegLenF', + 'lgLineDashSegLens', 'lgLineLabelConstantSpacingF', 'lgLineLabelFont', + 'lgLineLabelFontAspectF', 'lgLineLabelFontColor', + 'lgLineLabelFontColors', 'lgLineLabelFontHeightF', + 'lgLineLabelFontHeights', 'lgLineLabelFontQuality', + 'lgLineLabelFontThicknessF', 'lgLineLabelFuncCode', + 'lgLineLabelStrings', 'lgLineLabelsOn', 'lgLineThicknessF', + 'lgLineThicknesses', 'lgMarkerColor', 'lgMarkerColors', + 'lgMarkerIndex', 'lgMarkerIndexes', 'lgMarkerSizeF', 'lgMarkerSizes', + 'lgMarkerThicknessF', 'lgMarkerThicknesses', 'lgMonoDashIndex', + 'lgMonoItemType', 'lgMonoLineColor', 'lgMonoLineDashSegLen', + 'lgMonoLineLabelFontColor', 'lgMonoLineLabelFontHeight', + 'lgMonoLineThickness', 'lgMonoMarkerColor', 'lgMonoMarkerIndex', + 'lgMonoMarkerSize', 'lgMonoMarkerThickness', 'lgOrientation', + 'lgPerimColor', 'lgPerimDashPattern', 'lgPerimDashSegLenF', + 'lgPerimFill', 'lgPerimFillColor', 'lgPerimOn', 'lgPerimThicknessF', + 'lgRightMarginF', 'lgTitleAngleF', 'lgTitleConstantSpacingF', + 'lgTitleDirection', 'lgTitleExtentF', 'lgTitleFont', + 'lgTitleFontAspectF', 'lgTitleFontColor', 'lgTitleFontHeightF', + 'lgTitleFontQuality', 'lgTitleFontThicknessF', 'lgTitleFuncCode', + 'lgTitleJust', 'lgTitleOffsetF', 'lgTitleOn', 'lgTitlePosition', + 'lgTitleString', 'lgTopMarginF', 'mpAreaGroupCount', + 'mpAreaMaskingOn', 'mpAreaNames', 'mpAreaTypes', 'mpBottomAngleF', + 'mpBottomMapPosF', 'mpBottomNDCF', 'mpBottomNPCF', + 'mpBottomPointLatF', 'mpBottomPointLonF', 'mpBottomWindowF', + 'mpCenterLatF', 'mpCenterLonF', 'mpCenterRotF', 'mpCountyLineColor', + 'mpCountyLineDashPattern', 'mpCountyLineDashSegLenF', + 'mpCountyLineThicknessF', 'mpDataBaseVersion', 'mpDataResolution', + 'mpDataSetName', 'mpDefaultFillColor', 'mpDefaultFillPattern', + 'mpDefaultFillScaleF', 'mpDynamicAreaGroups', 'mpEllipticalBoundary', + 'mpFillAreaSpecifiers', 'mpFillBoundarySets', 'mpFillColor', + 'mpFillColors', 'mpFillColors-default', 'mpFillDotSizeF', + 'mpFillDrawOrder', 'mpFillOn', 'mpFillPatternBackground', + 'mpFillPattern', 'mpFillPatterns', 'mpFillPatterns-default', + 'mpFillScaleF', 'mpFillScales', 'mpFillScales-default', + 'mpFixedAreaGroups', 'mpGeophysicalLineColor', + 'mpGeophysicalLineDashPattern', 'mpGeophysicalLineDashSegLenF', + 'mpGeophysicalLineThicknessF', 'mpGreatCircleLinesOn', + 'mpGridAndLimbDrawOrder', 'mpGridAndLimbOn', 'mpGridLatSpacingF', + 'mpGridLineColor', 'mpGridLineDashPattern', 'mpGridLineDashSegLenF', + 'mpGridLineThicknessF', 'mpGridLonSpacingF', 'mpGridMaskMode', + 'mpGridMaxLatF', 'mpGridPolarLonSpacingF', 'mpGridSpacingF', + 'mpInlandWaterFillColor', 'mpInlandWaterFillPattern', + 'mpInlandWaterFillScaleF', 'mpLabelDrawOrder', 'mpLabelFontColor', + 'mpLabelFontHeightF', 'mpLabelsOn', 'mpLambertMeridianF', + 'mpLambertParallel1F', 'mpLambertParallel2F', 'mpLandFillColor', + 'mpLandFillPattern', 'mpLandFillScaleF', 'mpLeftAngleF', + 'mpLeftCornerLatF', 'mpLeftCornerLonF', 'mpLeftMapPosF', + 'mpLeftNDCF', 'mpLeftNPCF', 'mpLeftPointLatF', + 'mpLeftPointLonF', 'mpLeftWindowF', 'mpLimbLineColor', + 'mpLimbLineDashPattern', 'mpLimbLineDashSegLenF', + 'mpLimbLineThicknessF', 'mpLimitMode', 'mpMaskAreaSpecifiers', + 'mpMaskOutlineSpecifiers', 'mpMaxLatF', 'mpMaxLonF', + 'mpMinLatF', 'mpMinLonF', 'mpMonoFillColor', 'mpMonoFillPattern', + 'mpMonoFillScale', 'mpNationalLineColor', 'mpNationalLineDashPattern', + 'mpNationalLineThicknessF', 'mpOceanFillColor', 'mpOceanFillPattern', + 'mpOceanFillScaleF', 'mpOutlineBoundarySets', 'mpOutlineDrawOrder', + 'mpOutlineMaskingOn', 'mpOutlineOn', 'mpOutlineSpecifiers', + 'mpPerimDrawOrder', 'mpPerimLineColor', 'mpPerimLineDashPattern', + 'mpPerimLineDashSegLenF', 'mpPerimLineThicknessF', 'mpPerimOn', + 'mpPolyMode', 'mpProjection', 'mpProvincialLineColor', + 'mpProvincialLineDashPattern', 'mpProvincialLineDashSegLenF', + 'mpProvincialLineThicknessF', 'mpRelativeCenterLat', + 'mpRelativeCenterLon', 'mpRightAngleF', 'mpRightCornerLatF', + 'mpRightCornerLonF', 'mpRightMapPosF', 'mpRightNDCF', + 'mpRightNPCF', 'mpRightPointLatF', 'mpRightPointLonF', + 'mpRightWindowF', 'mpSatelliteAngle1F', 'mpSatelliteAngle2F', + 'mpSatelliteDistF', 'mpShapeMode', 'mpSpecifiedFillColors', + 'mpSpecifiedFillDirectIndexing', 'mpSpecifiedFillPatterns', + 'mpSpecifiedFillPriority', 'mpSpecifiedFillScales', + 'mpTopAngleF', 'mpTopMapPosF', 'mpTopNDCF', 'mpTopNPCF', + 'mpTopPointLatF', 'mpTopPointLonF', 'mpTopWindowF', + 'mpUSStateLineColor', 'mpUSStateLineDashPattern', + 'mpUSStateLineDashSegLenF', 'mpUSStateLineThicknessF', + 'pmAnnoManagers', 'pmAnnoViews', 'pmLabelBarDisplayMode', + 'pmLabelBarHeightF', 'pmLabelBarKeepAspect', 'pmLabelBarOrthogonalPosF', + 'pmLabelBarParallelPosF', 'pmLabelBarSide', 'pmLabelBarWidthF', + 'pmLabelBarZone', 'pmLegendDisplayMode', 'pmLegendHeightF', + 'pmLegendKeepAspect', 'pmLegendOrthogonalPosF', + 'pmLegendParallelPosF', 'pmLegendSide', 'pmLegendWidthF', + 'pmLegendZone', 'pmOverlaySequenceIds', 'pmTickMarkDisplayMode', + 'pmTickMarkZone', 'pmTitleDisplayMode', 'pmTitleZone', + 'prGraphicStyle', 'prPolyType', 'prXArray', 'prYArray', + 'sfCopyData', 'sfDataArray', 'sfDataMaxV', 'sfDataMinV', + 'sfElementNodes', 'sfExchangeDimensions', 'sfFirstNodeIndex', + 'sfMissingValueV', 'sfXArray', 'sfXCActualEndF', 'sfXCActualStartF', + 'sfXCEndIndex', 'sfXCEndSubsetV', 'sfXCEndV', 'sfXCStartIndex', + 'sfXCStartSubsetV', 'sfXCStartV', 'sfXCStride', 'sfXCellBounds', + 'sfYArray', 'sfYCActualEndF', 'sfYCActualStartF', 'sfYCEndIndex', + 'sfYCEndSubsetV', 'sfYCEndV', 'sfYCStartIndex', 'sfYCStartSubsetV', + 'sfYCStartV', 'sfYCStride', 'sfYCellBounds', 'stArrowLengthF', + 'stArrowStride', 'stCrossoverCheckCount', + 'stExplicitLabelBarLabelsOn', 'stLabelBarEndLabelsOn', + 'stLabelFormat', 'stLengthCheckCount', 'stLevelColors', + 'stLevelCount', 'stLevelPalette', 'stLevelSelectionMode', + 'stLevelSpacingF', 'stLevels', 'stLineColor', 'stLineOpacityF', + 'stLineStartStride', 'stLineThicknessF', 'stMapDirection', + 'stMaxLevelCount', 'stMaxLevelValF', 'stMinArrowSpacingF', + 'stMinDistanceF', 'stMinLevelValF', 'stMinLineSpacingF', + 'stMinStepFactorF', 'stMonoLineColor', 'stNoDataLabelOn', + 'stNoDataLabelString', 'stScalarFieldData', 'stScalarMissingValColor', + 'stSpanLevelPalette', 'stStepSizeF', 'stStreamlineDrawOrder', + 'stUseScalarArray', 'stVectorFieldData', 'stZeroFLabelAngleF', + 'stZeroFLabelBackgroundColor', 'stZeroFLabelConstantSpacingF', + 'stZeroFLabelFont', 'stZeroFLabelFontAspectF', + 'stZeroFLabelFontColor', 'stZeroFLabelFontHeightF', + 'stZeroFLabelFontQuality', 'stZeroFLabelFontThicknessF', + 'stZeroFLabelFuncCode', 'stZeroFLabelJust', 'stZeroFLabelOn', + 'stZeroFLabelOrthogonalPosF', 'stZeroFLabelParallelPosF', + 'stZeroFLabelPerimColor', 'stZeroFLabelPerimOn', + 'stZeroFLabelPerimSpaceF', 'stZeroFLabelPerimThicknessF', + 'stZeroFLabelSide', 'stZeroFLabelString', 'stZeroFLabelTextDirection', + 'stZeroFLabelZone', 'tfDoNDCOverlay', 'tfPlotManagerOn', + 'tfPolyDrawList', 'tfPolyDrawOrder', 'tiDeltaF', 'tiMainAngleF', + 'tiMainConstantSpacingF', 'tiMainDirection', 'tiMainFont', + 'tiMainFontAspectF', 'tiMainFontColor', 'tiMainFontHeightF', + 'tiMainFontQuality', 'tiMainFontThicknessF', 'tiMainFuncCode', + 'tiMainJust', 'tiMainOffsetXF', 'tiMainOffsetYF', 'tiMainOn', + 'tiMainPosition', 'tiMainSide', 'tiMainString', 'tiUseMainAttributes', + 'tiXAxisAngleF', 'tiXAxisConstantSpacingF', 'tiXAxisDirection', + 'tiXAxisFont', 'tiXAxisFontAspectF', 'tiXAxisFontColor', + 'tiXAxisFontHeightF', 'tiXAxisFontQuality', 'tiXAxisFontThicknessF', + 'tiXAxisFuncCode', 'tiXAxisJust', 'tiXAxisOffsetXF', + 'tiXAxisOffsetYF', 'tiXAxisOn', 'tiXAxisPosition', 'tiXAxisSide', + 'tiXAxisString', 'tiYAxisAngleF', 'tiYAxisConstantSpacingF', + 'tiYAxisDirection', 'tiYAxisFont', 'tiYAxisFontAspectF', + 'tiYAxisFontColor', 'tiYAxisFontHeightF', 'tiYAxisFontQuality', + 'tiYAxisFontThicknessF', 'tiYAxisFuncCode', 'tiYAxisJust', + 'tiYAxisOffsetXF', 'tiYAxisOffsetYF', 'tiYAxisOn', 'tiYAxisPosition', + 'tiYAxisSide', 'tiYAxisString', 'tmBorderLineColor', + 'tmBorderThicknessF', 'tmEqualizeXYSizes', 'tmLabelAutoStride', + 'tmSciNoteCutoff', 'tmXBAutoPrecision', 'tmXBBorderOn', + 'tmXBDataLeftF', 'tmXBDataRightF', 'tmXBFormat', 'tmXBIrrTensionF', + 'tmXBIrregularPoints', 'tmXBLabelAngleF', 'tmXBLabelConstantSpacingF', + 'tmXBLabelDeltaF', 'tmXBLabelDirection', 'tmXBLabelFont', + 'tmXBLabelFontAspectF', 'tmXBLabelFontColor', 'tmXBLabelFontHeightF', + 'tmXBLabelFontQuality', 'tmXBLabelFontThicknessF', + 'tmXBLabelFuncCode', 'tmXBLabelJust', 'tmXBLabelStride', 'tmXBLabels', + 'tmXBLabelsOn', 'tmXBMajorLengthF', 'tmXBMajorLineColor', + 'tmXBMajorOutwardLengthF', 'tmXBMajorThicknessF', 'tmXBMaxLabelLenF', + 'tmXBMaxTicks', 'tmXBMinLabelSpacingF', 'tmXBMinorLengthF', + 'tmXBMinorLineColor', 'tmXBMinorOn', 'tmXBMinorOutwardLengthF', + 'tmXBMinorPerMajor', 'tmXBMinorThicknessF', 'tmXBMinorValues', + 'tmXBMode', 'tmXBOn', 'tmXBPrecision', 'tmXBStyle', 'tmXBTickEndF', + 'tmXBTickSpacingF', 'tmXBTickStartF', 'tmXBValues', 'tmXMajorGrid', + 'tmXMajorGridLineColor', 'tmXMajorGridLineDashPattern', + 'tmXMajorGridThicknessF', 'tmXMinorGrid', 'tmXMinorGridLineColor', + 'tmXMinorGridLineDashPattern', 'tmXMinorGridThicknessF', + 'tmXTAutoPrecision', 'tmXTBorderOn', 'tmXTDataLeftF', + 'tmXTDataRightF', 'tmXTFormat', 'tmXTIrrTensionF', + 'tmXTIrregularPoints', 'tmXTLabelAngleF', 'tmXTLabelConstantSpacingF', + 'tmXTLabelDeltaF', 'tmXTLabelDirection', 'tmXTLabelFont', + 'tmXTLabelFontAspectF', 'tmXTLabelFontColor', 'tmXTLabelFontHeightF', + 'tmXTLabelFontQuality', 'tmXTLabelFontThicknessF', + 'tmXTLabelFuncCode', 'tmXTLabelJust', 'tmXTLabelStride', 'tmXTLabels', + 'tmXTLabelsOn', 'tmXTMajorLengthF', 'tmXTMajorLineColor', + 'tmXTMajorOutwardLengthF', 'tmXTMajorThicknessF', 'tmXTMaxLabelLenF', + 'tmXTMaxTicks', 'tmXTMinLabelSpacingF', 'tmXTMinorLengthF', + 'tmXTMinorLineColor', 'tmXTMinorOn', 'tmXTMinorOutwardLengthF', + 'tmXTMinorPerMajor', 'tmXTMinorThicknessF', 'tmXTMinorValues', + 'tmXTMode', 'tmXTOn', 'tmXTPrecision', 'tmXTStyle', 'tmXTTickEndF', + 'tmXTTickSpacingF', 'tmXTTickStartF', 'tmXTValues', 'tmXUseBottom', + 'tmYLAutoPrecision', 'tmYLBorderOn', 'tmYLDataBottomF', + 'tmYLDataTopF', 'tmYLFormat', 'tmYLIrrTensionF', + 'tmYLIrregularPoints', 'tmYLLabelAngleF', 'tmYLLabelConstantSpacingF', + 'tmYLLabelDeltaF', 'tmYLLabelDirection', 'tmYLLabelFont', + 'tmYLLabelFontAspectF', 'tmYLLabelFontColor', 'tmYLLabelFontHeightF', + 'tmYLLabelFontQuality', 'tmYLLabelFontThicknessF', + 'tmYLLabelFuncCode', 'tmYLLabelJust', 'tmYLLabelStride', 'tmYLLabels', + 'tmYLLabelsOn', 'tmYLMajorLengthF', 'tmYLMajorLineColor', + 'tmYLMajorOutwardLengthF', 'tmYLMajorThicknessF', 'tmYLMaxLabelLenF', + 'tmYLMaxTicks', 'tmYLMinLabelSpacingF', 'tmYLMinorLengthF', + 'tmYLMinorLineColor', 'tmYLMinorOn', 'tmYLMinorOutwardLengthF', + 'tmYLMinorPerMajor', 'tmYLMinorThicknessF', 'tmYLMinorValues', + 'tmYLMode', 'tmYLOn', 'tmYLPrecision', 'tmYLStyle', 'tmYLTickEndF', + 'tmYLTickSpacingF', 'tmYLTickStartF', 'tmYLValues', 'tmYMajorGrid', + 'tmYMajorGridLineColor', 'tmYMajorGridLineDashPattern', + 'tmYMajorGridThicknessF', 'tmYMinorGrid', 'tmYMinorGridLineColor', + 'tmYMinorGridLineDashPattern', 'tmYMinorGridThicknessF', + 'tmYRAutoPrecision', 'tmYRBorderOn', 'tmYRDataBottomF', + 'tmYRDataTopF', 'tmYRFormat', 'tmYRIrrTensionF', + 'tmYRIrregularPoints', 'tmYRLabelAngleF', 'tmYRLabelConstantSpacingF', + 'tmYRLabelDeltaF', 'tmYRLabelDirection', 'tmYRLabelFont', + 'tmYRLabelFontAspectF', 'tmYRLabelFontColor', 'tmYRLabelFontHeightF', + 'tmYRLabelFontQuality', 'tmYRLabelFontThicknessF', + 'tmYRLabelFuncCode', 'tmYRLabelJust', 'tmYRLabelStride', 'tmYRLabels', + 'tmYRLabelsOn', 'tmYRMajorLengthF', 'tmYRMajorLineColor', + 'tmYRMajorOutwardLengthF', 'tmYRMajorThicknessF', 'tmYRMaxLabelLenF', + 'tmYRMaxTicks', 'tmYRMinLabelSpacingF', 'tmYRMinorLengthF', + 'tmYRMinorLineColor', 'tmYRMinorOn', 'tmYRMinorOutwardLengthF', + 'tmYRMinorPerMajor', 'tmYRMinorThicknessF', 'tmYRMinorValues', + 'tmYRMode', 'tmYROn', 'tmYRPrecision', 'tmYRStyle', 'tmYRTickEndF', + 'tmYRTickSpacingF', 'tmYRTickStartF', 'tmYRValues', 'tmYUseLeft', + 'trGridType', 'trLineInterpolationOn', + 'trXAxisType', 'trXCoordPoints', 'trXInterPoints', 'trXLog', + 'trXMaxF', 'trXMinF', 'trXReverse', 'trXSamples', 'trXTensionF', + 'trYAxisType', 'trYCoordPoints', 'trYInterPoints', 'trYLog', + 'trYMaxF', 'trYMinF', 'trYReverse', 'trYSamples', 'trYTensionF', + 'txAngleF', 'txBackgroundFillColor', 'txConstantSpacingF', 'txDirection', + 'txFont', 'HLU-Fonts', 'txFontAspectF', 'txFontColor', + 'txFontHeightF', 'txFontOpacityF', 'txFontQuality', + 'txFontThicknessF', 'txFuncCode', 'txJust', 'txPerimColor', + 'txPerimDashLengthF', 'txPerimDashPattern', 'txPerimOn', + 'txPerimSpaceF', 'txPerimThicknessF', 'txPosXF', 'txPosYF', + 'txString', 'vcExplicitLabelBarLabelsOn', 'vcFillArrowEdgeColor', + 'vcFillArrowEdgeThicknessF', 'vcFillArrowFillColor', + 'vcFillArrowHeadInteriorXF', 'vcFillArrowHeadMinFracXF', + 'vcFillArrowHeadMinFracYF', 'vcFillArrowHeadXF', 'vcFillArrowHeadYF', + 'vcFillArrowMinFracWidthF', 'vcFillArrowWidthF', 'vcFillArrowsOn', + 'vcFillOverEdge', 'vcGlyphOpacityF', 'vcGlyphStyle', + 'vcLabelBarEndLabelsOn', 'vcLabelFontColor', 'vcLabelFontHeightF', + 'vcLabelsOn', 'vcLabelsUseVectorColor', 'vcLevelColors', + 'vcLevelCount', 'vcLevelPalette', 'vcLevelSelectionMode', + 'vcLevelSpacingF', 'vcLevels', 'vcLineArrowColor', + 'vcLineArrowHeadMaxSizeF', 'vcLineArrowHeadMinSizeF', + 'vcLineArrowThicknessF', 'vcMagnitudeFormat', + 'vcMagnitudeScaleFactorF', 'vcMagnitudeScaleValueF', + 'vcMagnitudeScalingMode', 'vcMapDirection', 'vcMaxLevelCount', + 'vcMaxLevelValF', 'vcMaxMagnitudeF', 'vcMinAnnoAngleF', + 'vcMinAnnoArrowAngleF', 'vcMinAnnoArrowEdgeColor', + 'vcMinAnnoArrowFillColor', 'vcMinAnnoArrowLineColor', + 'vcMinAnnoArrowMinOffsetF', 'vcMinAnnoArrowSpaceF', + 'vcMinAnnoArrowUseVecColor', 'vcMinAnnoBackgroundColor', + 'vcMinAnnoConstantSpacingF', 'vcMinAnnoExplicitMagnitudeF', + 'vcMinAnnoFont', 'vcMinAnnoFontAspectF', 'vcMinAnnoFontColor', + 'vcMinAnnoFontHeightF', 'vcMinAnnoFontQuality', + 'vcMinAnnoFontThicknessF', 'vcMinAnnoFuncCode', 'vcMinAnnoJust', + 'vcMinAnnoOn', 'vcMinAnnoOrientation', 'vcMinAnnoOrthogonalPosF', + 'vcMinAnnoParallelPosF', 'vcMinAnnoPerimColor', 'vcMinAnnoPerimOn', + 'vcMinAnnoPerimSpaceF', 'vcMinAnnoPerimThicknessF', 'vcMinAnnoSide', + 'vcMinAnnoString1', 'vcMinAnnoString1On', 'vcMinAnnoString2', + 'vcMinAnnoString2On', 'vcMinAnnoTextDirection', 'vcMinAnnoZone', + 'vcMinDistanceF', 'vcMinFracLengthF', 'vcMinLevelValF', + 'vcMinMagnitudeF', 'vcMonoFillArrowEdgeColor', + 'vcMonoFillArrowFillColor', 'vcMonoLineArrowColor', + 'vcMonoWindBarbColor', 'vcNoDataLabelOn', 'vcNoDataLabelString', + 'vcPositionMode', 'vcRefAnnoAngleF', 'vcRefAnnoArrowAngleF', + 'vcRefAnnoArrowEdgeColor', 'vcRefAnnoArrowFillColor', + 'vcRefAnnoArrowLineColor', 'vcRefAnnoArrowMinOffsetF', + 'vcRefAnnoArrowSpaceF', 'vcRefAnnoArrowUseVecColor', + 'vcRefAnnoBackgroundColor', 'vcRefAnnoConstantSpacingF', + 'vcRefAnnoExplicitMagnitudeF', 'vcRefAnnoFont', + 'vcRefAnnoFontAspectF', 'vcRefAnnoFontColor', 'vcRefAnnoFontHeightF', + 'vcRefAnnoFontQuality', 'vcRefAnnoFontThicknessF', + 'vcRefAnnoFuncCode', 'vcRefAnnoJust', 'vcRefAnnoOn', + 'vcRefAnnoOrientation', 'vcRefAnnoOrthogonalPosF', + 'vcRefAnnoParallelPosF', 'vcRefAnnoPerimColor', 'vcRefAnnoPerimOn', + 'vcRefAnnoPerimSpaceF', 'vcRefAnnoPerimThicknessF', 'vcRefAnnoSide', + 'vcRefAnnoString1', 'vcRefAnnoString1On', 'vcRefAnnoString2', + 'vcRefAnnoString2On', 'vcRefAnnoTextDirection', 'vcRefAnnoZone', + 'vcRefLengthF', 'vcRefMagnitudeF', 'vcScalarFieldData', + 'vcScalarMissingValColor', 'vcScalarValueFormat', + 'vcScalarValueScaleFactorF', 'vcScalarValueScaleValueF', + 'vcScalarValueScalingMode', 'vcSpanLevelPalette', 'vcUseRefAnnoRes', + 'vcUseScalarArray', 'vcVectorDrawOrder', 'vcVectorFieldData', + 'vcWindBarbCalmCircleSizeF', 'vcWindBarbColor', + 'vcWindBarbLineThicknessF', 'vcWindBarbScaleFactorF', + 'vcWindBarbTickAngleF', 'vcWindBarbTickLengthF', + 'vcWindBarbTickSpacingF', 'vcZeroFLabelAngleF', + 'vcZeroFLabelBackgroundColor', 'vcZeroFLabelConstantSpacingF', + 'vcZeroFLabelFont', 'vcZeroFLabelFontAspectF', + 'vcZeroFLabelFontColor', 'vcZeroFLabelFontHeightF', + 'vcZeroFLabelFontQuality', 'vcZeroFLabelFontThicknessF', + 'vcZeroFLabelFuncCode', 'vcZeroFLabelJust', 'vcZeroFLabelOn', + 'vcZeroFLabelOrthogonalPosF', 'vcZeroFLabelParallelPosF', + 'vcZeroFLabelPerimColor', 'vcZeroFLabelPerimOn', + 'vcZeroFLabelPerimSpaceF', 'vcZeroFLabelPerimThicknessF', + 'vcZeroFLabelSide', 'vcZeroFLabelString', 'vcZeroFLabelTextDirection', + 'vcZeroFLabelZone', 'vfCopyData', 'vfDataArray', + 'vfExchangeDimensions', 'vfExchangeUVData', 'vfMagMaxV', 'vfMagMinV', + 'vfMissingUValueV', 'vfMissingVValueV', 'vfPolarData', + 'vfSingleMissingValue', 'vfUDataArray', 'vfUMaxV', 'vfUMinV', + 'vfVDataArray', 'vfVMaxV', 'vfVMinV', 'vfXArray', 'vfXCActualEndF', + 'vfXCActualStartF', 'vfXCEndIndex', 'vfXCEndSubsetV', 'vfXCEndV', + 'vfXCStartIndex', 'vfXCStartSubsetV', 'vfXCStartV', 'vfXCStride', + 'vfYArray', 'vfYCActualEndF', 'vfYCActualStartF', 'vfYCEndIndex', + 'vfYCEndSubsetV', 'vfYCEndV', 'vfYCStartIndex', 'vfYCStartSubsetV', + 'vfYCStartV', 'vfYCStride', 'vpAnnoManagerId', 'vpClipOn', + 'vpHeightF', 'vpKeepAspect', 'vpOn', 'vpUseSegments', 'vpWidthF', + 'vpXF', 'vpYF', 'wkAntiAlias', 'wkBackgroundColor', 'wkBackgroundOpacityF', + 'wkColorMapLen', 'wkColorMap', 'wkColorModel', 'wkDashTableLength', + 'wkDefGraphicStyleId', 'wkDeviceLowerX', 'wkDeviceLowerY', + 'wkDeviceUpperX', 'wkDeviceUpperY', 'wkFileName', 'wkFillTableLength', + 'wkForegroundColor', 'wkFormat', 'wkFullBackground', 'wkGksWorkId', + 'wkHeight', 'wkMarkerTableLength', 'wkMetaName', 'wkOrientation', + 'wkPDFFileName', 'wkPDFFormat', 'wkPDFResolution', 'wkPSFileName', + 'wkPSFormat', 'wkPSResolution', 'wkPaperHeightF', 'wkPaperSize', + 'wkPaperWidthF', 'wkPause', 'wkTopLevelViews', 'wkViews', + 'wkVisualType', 'wkWidth', 'wkWindowId', 'wkXColorMode', 'wsCurrentSize', + 'wsMaximumSize', 'wsThresholdSize', 'xyComputeXMax', + 'xyComputeXMin', 'xyComputeYMax', 'xyComputeYMin', 'xyCoordData', + 'xyCoordDataSpec', 'xyCurveDrawOrder', 'xyDashPattern', + 'xyDashPatterns', 'xyExplicitLabels', 'xyExplicitLegendLabels', + 'xyLabelMode', 'xyLineColor', 'xyLineColors', 'xyLineDashSegLenF', + 'xyLineLabelConstantSpacingF', 'xyLineLabelFont', + 'xyLineLabelFontAspectF', 'xyLineLabelFontColor', + 'xyLineLabelFontColors', 'xyLineLabelFontHeightF', + 'xyLineLabelFontQuality', 'xyLineLabelFontThicknessF', + 'xyLineLabelFuncCode', 'xyLineThicknessF', 'xyLineThicknesses', + 'xyMarkLineMode', 'xyMarkLineModes', 'xyMarker', 'xyMarkerColor', + 'xyMarkerColors', 'xyMarkerSizeF', 'xyMarkerSizes', + 'xyMarkerThicknessF', 'xyMarkerThicknesses', 'xyMarkers', + 'xyMonoDashPattern', 'xyMonoLineColor', 'xyMonoLineLabelFontColor', + 'xyMonoLineThickness', 'xyMonoMarkLineMode', 'xyMonoMarker', + 'xyMonoMarkerColor', 'xyMonoMarkerSize', 'xyMonoMarkerThickness', + 'xyXIrrTensionF', 'xyXIrregularPoints', 'xyXStyle', 'xyYIrrTensionF', + 'xyYIrregularPoints', 'xyYStyle'), prefix=r'\b'), + Name.Builtin), + + # Booleans + (r'\.(True|False)\.', Name.Builtin), + # Comparing Operators + (r'\.(eq|ne|lt|le|gt|ge|not|and|or|xor)\.', Operator.Word), + ], + + 'strings': [ + (r'(?s)"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double), + ], + + 'nums': [ + (r'\d+(?![.e])(_[a-z]\w+)?', Number.Integer), + (r'[+-]?\d*\.\d+(e[-+]?\d+)?(_[a-z]\w+)?', Number.Float), + (r'[+-]?\d+\.\d*(e[-+]?\d+)?(_[a-z]\w+)?', Number.Float), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/nimrod.py b/.venv/lib/python3.8/site-packages/pygments/lexers/nimrod.py new file mode 100644 index 00000000..14816eba --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/nimrod.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.nimrod + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for the Nim language (formerly known as Nimrod). + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include, default +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Error + +__all__ = ['NimrodLexer'] + + +class NimrodLexer(RegexLexer): + """ + For `Nim `_ source code. + + .. versionadded:: 1.5 + """ + + name = 'Nimrod' + aliases = ['nim', 'nimrod'] + filenames = ['*.nim', '*.nimrod'] + mimetypes = ['text/x-nim'] + + flags = re.MULTILINE | re.IGNORECASE | re.UNICODE + + def underscorize(words): + newWords = [] + new = "" + for word in words: + for ch in word: + new += (ch + "_?") + newWords.append(new) + new = "" + return "|".join(newWords) + + keywords = [ + 'addr', 'and', 'as', 'asm', 'bind', 'block', 'break', 'case', + 'cast', 'concept', 'const', 'continue', 'converter', 'defer', 'discard', + 'distinct', 'div', 'do', 'elif', 'else', 'end', 'enum', 'except', + 'export', 'finally', 'for', 'func', 'if', 'in', 'yield', 'interface', + 'is', 'isnot', 'iterator', 'let', 'macro', 'method', 'mixin', 'mod', + 'not', 'notin', 'object', 'of', 'or', 'out', 'proc', 'ptr', 'raise', + 'ref', 'return', 'shl', 'shr', 'static', 'template', 'try', + 'tuple', 'type', 'using', 'when', 'while', 'xor' + ] + + keywordsPseudo = [ + 'nil', 'true', 'false' + ] + + opWords = [ + 'and', 'or', 'not', 'xor', 'shl', 'shr', 'div', 'mod', 'in', + 'notin', 'is', 'isnot' + ] + + types = [ + 'int', 'int8', 'int16', 'int32', 'int64', 'float', 'float32', 'float64', + 'bool', 'char', 'range', 'array', 'seq', 'set', 'string' + ] + + tokens = { + 'root': [ + (r'##.*$', String.Doc), + (r'#.*$', Comment), + (r'[*=><+\-/@$~&%!?|\\\[\]]', Operator), + (r'\.\.|\.|,|\[\.|\.\]|\{\.|\.\}|\(\.|\.\)|\{|\}|\(|\)|:|\^|`|;', + Punctuation), + + # Strings + (r'(?:[\w]+)"', String, 'rdqs'), + (r'"""', String, 'tdqs'), + ('"', String, 'dqs'), + + # Char + ("'", String.Char, 'chars'), + + # Keywords + (r'(%s)\b' % underscorize(opWords), Operator.Word), + (r'(p_?r_?o_?c_?\s)(?![(\[\]])', Keyword, 'funcname'), + (r'(%s)\b' % underscorize(keywords), Keyword), + (r'(%s)\b' % underscorize(['from', 'import', 'include']), + Keyword.Namespace), + (r'(v_?a_?r)\b', Keyword.Declaration), + (r'(%s)\b' % underscorize(types), Keyword.Type), + (r'(%s)\b' % underscorize(keywordsPseudo), Keyword.Pseudo), + # Identifiers + (r'\b((?![_\d])\w)(((?!_)\w)|(_(?!_)\w))*', Name), + # Numbers + (r'[0-9][0-9_]*(?=([e.]|\'f(32|64)))', + Number.Float, ('float-suffix', 'float-number')), + (r'0x[a-f0-9][a-f0-9_]*', Number.Hex, 'int-suffix'), + (r'0b[01][01_]*', Number.Bin, 'int-suffix'), + (r'0o[0-7][0-7_]*', Number.Oct, 'int-suffix'), + (r'[0-9][0-9_]*', Number.Integer, 'int-suffix'), + # Whitespace + (r'\s+', Text), + (r'.+$', Error), + ], + 'chars': [ + (r'\\([\\abcefnrtvl"\']|x[a-f0-9]{2}|[0-9]{1,3})', String.Escape), + (r"'", String.Char, '#pop'), + (r".", String.Char) + ], + 'strings': [ + (r'(?`_ source. + + .. versionadded:: 2.0 + """ + + name = 'Nit' + aliases = ['nit'] + filenames = ['*.nit'] + tokens = { + 'root': [ + (r'#.*?$', Comment.Single), + (words(( + 'package', 'module', 'import', 'class', 'abstract', 'interface', + 'universal', 'enum', 'end', 'fun', 'type', 'init', 'redef', + 'isa', 'do', 'readable', 'writable', 'var', 'intern', 'extern', + 'public', 'protected', 'private', 'intrude', 'if', 'then', + 'else', 'while', 'loop', 'for', 'in', 'and', 'or', 'not', + 'implies', 'return', 'continue', 'break', 'abort', 'assert', + 'new', 'is', 'once', 'super', 'self', 'true', 'false', 'nullable', + 'null', 'as', 'isset', 'label', '__debug__'), suffix=r'(?=[\r\n\t( ])'), + Keyword), + (r'[A-Z]\w*', Name.Class), + (r'"""(([^\'\\]|\\.)|\\r|\\n)*((\{\{?)?(""?\{\{?)*""""*)', String), # Simple long string + (r'\'\'\'(((\\.|[^\'\\])|\\r|\\n)|\'((\\.|[^\'\\])|\\r|\\n)|' + r'\'\'((\\.|[^\'\\])|\\r|\\n))*\'\'\'', String), # Simple long string alt + (r'"""(([^\'\\]|\\.)|\\r|\\n)*((""?)?(\{\{?""?)*\{\{\{\{*)', String), # Start long string + (r'\}\}\}(((\\.|[^\'\\])|\\r|\\n))*(""?)?(\{\{?""?)*\{\{\{\{*', String), # Mid long string + (r'\}\}\}(((\\.|[^\'\\])|\\r|\\n))*(\{\{?)?(""?\{\{?)*""""*', String), # End long string + (r'"(\\.|([^"}{\\]))*"', String), # Simple String + (r'"(\\.|([^"}{\\]))*\{', String), # Start string + (r'\}(\\.|([^"}{\\]))*\{', String), # Mid String + (r'\}(\\.|([^"}{\\]))*"', String), # End String + (r'(\'[^\'\\]\')|(\'\\.\')', String.Char), + (r'[0-9]+', Number.Integer), + (r'[0-9]*.[0-9]+', Number.Float), + (r'0(x|X)[0-9A-Fa-f]+', Number.Hex), + (r'[a-z]\w*', Name), + (r'_\w+', Name.Variable.Instance), + (r'==|!=|<==>|>=|>>|>|<=|<<|<|\+|-|=|/|\*|%|\+=|-=|!|@', Operator), + (r'\(|\)|\[|\]|,|\.\.\.|\.\.|\.|::|:', Punctuation), + (r'`\{[^`]*`\}', Text), # Extern blocks won't be Lexed by Nit + (r'[\r\n\t ]+', Text), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/nix.py b/.venv/lib/python3.8/site-packages/pygments/lexers/nix.py new file mode 100644 index 00000000..713348e8 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/nix.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.nix + ~~~~~~~~~~~~~~~~~~~ + + Lexers for the NixOS Nix language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Literal + +__all__ = ['NixLexer'] + + +class NixLexer(RegexLexer): + """ + For the `Nix language `_. + + .. versionadded:: 2.0 + """ + + name = 'Nix' + aliases = ['nixos', 'nix'] + filenames = ['*.nix'] + mimetypes = ['text/x-nix'] + + flags = re.MULTILINE | re.UNICODE + + keywords = ['rec', 'with', 'let', 'in', 'inherit', 'assert', 'if', + 'else', 'then', '...'] + builtins = ['import', 'abort', 'baseNameOf', 'dirOf', 'isNull', 'builtins', + 'map', 'removeAttrs', 'throw', 'toString', 'derivation'] + operators = ['++', '+', '?', '.', '!', '//', '==', + '!=', '&&', '||', '->', '='] + + punctuations = ["(", ")", "[", "]", ";", "{", "}", ":", ",", "@"] + + tokens = { + 'root': [ + # comments starting with # + (r'#.*$', Comment.Single), + + # multiline comments + (r'/\*', Comment.Multiline, 'comment'), + + # whitespace + (r'\s+', Text), + + # keywords + ('(%s)' % '|'.join(re.escape(entry) + '\\b' for entry in keywords), Keyword), + + # highlight the builtins + ('(%s)' % '|'.join(re.escape(entry) + '\\b' for entry in builtins), + Name.Builtin), + + (r'\b(true|false|null)\b', Name.Constant), + + # operators + ('(%s)' % '|'.join(re.escape(entry) for entry in operators), + Operator), + + # word operators + (r'\b(or|and)\b', Operator.Word), + + # punctuations + ('(%s)' % '|'.join(re.escape(entry) for entry in punctuations), Punctuation), + + # integers + (r'[0-9]+', Number.Integer), + + # strings + (r'"', String.Double, 'doublequote'), + (r"''", String.Single, 'singlequote'), + + # paths + (r'[\w.+-]*(\/[\w.+-]+)+', Literal), + (r'\<[\w.+-]+(\/[\w.+-]+)*\>', Literal), + + # urls + (r'[a-zA-Z][a-zA-Z0-9\+\-\.]*\:[\w%/?:@&=+$,\\.!~*\'-]+', Literal), + + # names of variables + (r'[\w-]+\s*=', String.Symbol), + (r'[a-zA-Z_][\w\'-]*', Text), + + ], + 'comment': [ + (r'[^/*]+', Comment.Multiline), + (r'/\*', Comment.Multiline, '#push'), + (r'\*/', Comment.Multiline, '#pop'), + (r'[*/]', Comment.Multiline), + ], + 'singlequote': [ + (r"'''", String.Escape), + (r"''\$\{", String.Escape), + (r"''\n", String.Escape), + (r"''\r", String.Escape), + (r"''\t", String.Escape), + (r"''", String.Single, '#pop'), + (r'\$\{', String.Interpol, 'antiquote'), + (r"[^']", String.Single), + ], + 'doublequote': [ + (r'\\', String.Escape), + (r'\\"', String.Escape), + (r'\\$\{', String.Escape), + (r'"', String.Double, '#pop'), + (r'\$\{', String.Interpol, 'antiquote'), + (r'[^"]', String.Double), + ], + 'antiquote': [ + (r"\}", String.Interpol, '#pop'), + # TODO: we should probably escape also here ''${ \${ + (r"\$\{", String.Interpol, '#push'), + include('root'), + ], + } + + def analyse_text(text): + rv = 0.0 + # TODO: let/in + if re.search(r'import.+?<[^>]+>', text): + rv += 0.4 + if re.search(r'mkDerivation\s+(\(|\{|rec)', text): + rv += 0.4 + if re.search(r'=\s+mkIf\s+', text): + rv += 0.4 + if re.search(r'\{[a-zA-Z,\s]+\}:', text): + rv += 0.1 + return rv diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/oberon.py b/.venv/lib/python3.8/site-packages/pygments/lexers/oberon.py new file mode 100644 index 00000000..8934997a --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/oberon.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.oberon + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Oberon family languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include, words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation + +__all__ = ['ComponentPascalLexer'] + + +class ComponentPascalLexer(RegexLexer): + """ + For `Component Pascal `_ source code. + + .. versionadded:: 2.1 + """ + name = 'Component Pascal' + aliases = ['componentpascal', 'cp'] + filenames = ['*.cp', '*.cps'] + mimetypes = ['text/x-component-pascal'] + + flags = re.MULTILINE | re.DOTALL + + tokens = { + 'root': [ + include('whitespace'), + include('comments'), + include('punctuation'), + include('numliterals'), + include('strings'), + include('operators'), + include('builtins'), + include('identifiers'), + ], + 'whitespace': [ + (r'\n+', Text), # blank lines + (r'\s+', Text), # whitespace + ], + 'comments': [ + (r'\(\*([^$].*?)\*\)', Comment.Multiline), + # TODO: nested comments (* (* ... *) ... (* ... *) *) not supported! + ], + 'punctuation': [ + (r'[()\[\]{},.:;|]', Punctuation), + ], + 'numliterals': [ + (r'[0-9A-F]+X\b', Number.Hex), # char code + (r'[0-9A-F]+[HL]\b', Number.Hex), # hexadecimal number + (r'[0-9]+\.[0-9]+E[+-][0-9]+', Number.Float), # real number + (r'[0-9]+\.[0-9]+', Number.Float), # real number + (r'[0-9]+', Number.Integer), # decimal whole number + ], + 'strings': [ + (r"'[^\n']*'", String), # single quoted string + (r'"[^\n"]*"', String), # double quoted string + ], + 'operators': [ + # Arithmetic Operators + (r'[+-]', Operator), + (r'[*/]', Operator), + # Relational Operators + (r'[=#<>]', Operator), + # Dereferencing Operator + (r'\^', Operator), + # Logical AND Operator + (r'&', Operator), + # Logical NOT Operator + (r'~', Operator), + # Assignment Symbol + (r':=', Operator), + # Range Constructor + (r'\.\.', Operator), + (r'\$', Operator), + ], + 'identifiers': [ + (r'([a-zA-Z_$][\w$]*)', Name), + ], + 'builtins': [ + (words(( + 'ANYPTR', 'ANYREC', 'BOOLEAN', 'BYTE', 'CHAR', 'INTEGER', 'LONGINT', + 'REAL', 'SET', 'SHORTCHAR', 'SHORTINT', 'SHORTREAL' + ), suffix=r'\b'), Keyword.Type), + (words(( + 'ABS', 'ABSTRACT', 'ARRAY', 'ASH', 'ASSERT', 'BEGIN', 'BITS', 'BY', + 'CAP', 'CASE', 'CHR', 'CLOSE', 'CONST', 'DEC', 'DIV', 'DO', 'ELSE', + 'ELSIF', 'EMPTY', 'END', 'ENTIER', 'EXCL', 'EXIT', 'EXTENSIBLE', 'FOR', + 'HALT', 'IF', 'IMPORT', 'IN', 'INC', 'INCL', 'IS', 'LEN', 'LIMITED', + 'LONG', 'LOOP', 'MAX', 'MIN', 'MOD', 'MODULE', 'NEW', 'ODD', 'OF', + 'OR', 'ORD', 'OUT', 'POINTER', 'PROCEDURE', 'RECORD', 'REPEAT', 'RETURN', + 'SHORT', 'SHORTCHAR', 'SHORTINT', 'SIZE', 'THEN', 'TYPE', 'TO', 'UNTIL', + 'VAR', 'WHILE', 'WITH' + ), suffix=r'\b'), Keyword.Reserved), + (r'(TRUE|FALSE|NIL|INF)\b', Keyword.Constant), + ] + } + + def analyse_text(text): + """The only other lexer using .cp is the C++ one, so we check if for + a few common Pascal keywords here. Those are unfortunately quite + common across various business languages as well.""" + result = 0 + if 'BEGIN' in text: + result += 0.01 + if 'END' in text: + result += 0.01 + if 'PROCEDURE' in text: + result += 0.01 + if 'END' in text: + result += 0.01 + + return result diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/objective.py b/.venv/lib/python3.8/site-packages/pygments/lexers/objective.py new file mode 100644 index 00000000..3a1c3f65 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/objective.py @@ -0,0 +1,504 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.objective + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Objective-C family languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include, bygroups, using, this, words, \ + inherit, default +from pygments.token import Text, Keyword, Name, String, Operator, \ + Number, Punctuation, Literal, Comment + +from pygments.lexers.c_cpp import CLexer, CppLexer + +__all__ = ['ObjectiveCLexer', 'ObjectiveCppLexer', 'LogosLexer', 'SwiftLexer'] + + +def objective(baselexer): + """ + Generate a subclass of baselexer that accepts the Objective-C syntax + extensions. + """ + + # Have to be careful not to accidentally match JavaDoc/Doxygen syntax here, + # since that's quite common in ordinary C/C++ files. It's OK to match + # JavaDoc/Doxygen keywords that only apply to Objective-C, mind. + # + # The upshot of this is that we CANNOT match @class or @interface + _oc_keywords = re.compile(r'@(?:end|implementation|protocol)') + + # Matches [ ? identifier ( identifier ? ] | identifier? : ) + # (note the identifier is *optional* when there is a ':'!) + _oc_message = re.compile(r'\[\s*[a-zA-Z_]\w*\s+' + r'(?:[a-zA-Z_]\w*\s*\]|' + r'(?:[a-zA-Z_]\w*)?:)') + + class GeneratedObjectiveCVariant(baselexer): + """ + Implements Objective-C syntax on top of an existing C family lexer. + """ + + tokens = { + 'statements': [ + (r'@"', String, 'string'), + (r'@(YES|NO)', Number), + (r"@'(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'", String.Char), + (r'@(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+[lL]?', Number.Float), + (r'@(\d+\.\d*|\.\d+|\d+[fF])[fF]?', Number.Float), + (r'@0x[0-9a-fA-F]+[Ll]?', Number.Hex), + (r'@0[0-7]+[Ll]?', Number.Oct), + (r'@\d+[Ll]?', Number.Integer), + (r'@\(', Literal, 'literal_number'), + (r'@\[', Literal, 'literal_array'), + (r'@\{', Literal, 'literal_dictionary'), + (words(( + '@selector', '@private', '@protected', '@public', '@encode', + '@synchronized', '@try', '@throw', '@catch', '@finally', + '@end', '@property', '@synthesize', '__bridge', '__bridge_transfer', + '__autoreleasing', '__block', '__weak', '__strong', 'weak', 'strong', + 'copy', 'retain', 'assign', 'unsafe_unretained', 'atomic', 'nonatomic', + 'readonly', 'readwrite', 'setter', 'getter', 'typeof', 'in', + 'out', 'inout', 'release', 'class', '@dynamic', '@optional', + '@required', '@autoreleasepool', '@import'), suffix=r'\b'), + Keyword), + (words(('id', 'instancetype', 'Class', 'IMP', 'SEL', 'BOOL', + 'IBOutlet', 'IBAction', 'unichar'), suffix=r'\b'), + Keyword.Type), + (r'@(true|false|YES|NO)\n', Name.Builtin), + (r'(YES|NO|nil|self|super)\b', Name.Builtin), + # Carbon types + (r'(Boolean|UInt8|SInt8|UInt16|SInt16|UInt32|SInt32)\b', Keyword.Type), + # Carbon built-ins + (r'(TRUE|FALSE)\b', Name.Builtin), + (r'(@interface|@implementation)(\s+)', bygroups(Keyword, Text), + ('#pop', 'oc_classname')), + (r'(@class|@protocol)(\s+)', bygroups(Keyword, Text), + ('#pop', 'oc_forward_classname')), + # @ can also prefix other expressions like @{...} or @(...) + (r'@', Punctuation), + inherit, + ], + 'oc_classname': [ + # interface definition that inherits + (r'([a-zA-Z$_][\w$]*)(\s*:\s*)([a-zA-Z$_][\w$]*)?(\s*)(\{)', + bygroups(Name.Class, Text, Name.Class, Text, Punctuation), + ('#pop', 'oc_ivars')), + (r'([a-zA-Z$_][\w$]*)(\s*:\s*)([a-zA-Z$_][\w$]*)?', + bygroups(Name.Class, Text, Name.Class), '#pop'), + # interface definition for a category + (r'([a-zA-Z$_][\w$]*)(\s*)(\([a-zA-Z$_][\w$]*\))(\s*)(\{)', + bygroups(Name.Class, Text, Name.Label, Text, Punctuation), + ('#pop', 'oc_ivars')), + (r'([a-zA-Z$_][\w$]*)(\s*)(\([a-zA-Z$_][\w$]*\))', + bygroups(Name.Class, Text, Name.Label), '#pop'), + # simple interface / implementation + (r'([a-zA-Z$_][\w$]*)(\s*)(\{)', + bygroups(Name.Class, Text, Punctuation), ('#pop', 'oc_ivars')), + (r'([a-zA-Z$_][\w$]*)', Name.Class, '#pop') + ], + 'oc_forward_classname': [ + (r'([a-zA-Z$_][\w$]*)(\s*,\s*)', + bygroups(Name.Class, Text), 'oc_forward_classname'), + (r'([a-zA-Z$_][\w$]*)(\s*;?)', + bygroups(Name.Class, Text), '#pop') + ], + 'oc_ivars': [ + include('whitespace'), + include('statements'), + (';', Punctuation), + (r'\{', Punctuation, '#push'), + (r'\}', Punctuation, '#pop'), + ], + 'root': [ + # methods + (r'^([-+])(\s*)' # method marker + r'(\(.*?\))?(\s*)' # return type + r'([a-zA-Z$_][\w$]*:?)', # begin of method name + bygroups(Punctuation, Text, using(this), + Text, Name.Function), + 'method'), + inherit, + ], + 'method': [ + include('whitespace'), + # TODO unsure if ellipses are allowed elsewhere, see + # discussion in Issue 789 + (r',', Punctuation), + (r'\.\.\.', Punctuation), + (r'(\(.*?\))(\s*)([a-zA-Z$_][\w$]*)', + bygroups(using(this), Text, Name.Variable)), + (r'[a-zA-Z$_][\w$]*:', Name.Function), + (';', Punctuation, '#pop'), + (r'\{', Punctuation, 'function'), + default('#pop'), + ], + 'literal_number': [ + (r'\(', Punctuation, 'literal_number_inner'), + (r'\)', Literal, '#pop'), + include('statement'), + ], + 'literal_number_inner': [ + (r'\(', Punctuation, '#push'), + (r'\)', Punctuation, '#pop'), + include('statement'), + ], + 'literal_array': [ + (r'\[', Punctuation, 'literal_array_inner'), + (r'\]', Literal, '#pop'), + include('statement'), + ], + 'literal_array_inner': [ + (r'\[', Punctuation, '#push'), + (r'\]', Punctuation, '#pop'), + include('statement'), + ], + 'literal_dictionary': [ + (r'\}', Literal, '#pop'), + include('statement'), + ], + } + + def analyse_text(text): + if _oc_keywords.search(text): + return 1.0 + elif '@"' in text: # strings + return 0.8 + elif re.search('@[0-9]+', text): + return 0.7 + elif _oc_message.search(text): + return 0.8 + return 0 + + def get_tokens_unprocessed(self, text): + from pygments.lexers._cocoa_builtins import COCOA_INTERFACES, \ + COCOA_PROTOCOLS, COCOA_PRIMITIVES + + for index, token, value in \ + baselexer.get_tokens_unprocessed(self, text): + if token is Name or token is Name.Class: + if value in COCOA_INTERFACES or value in COCOA_PROTOCOLS \ + or value in COCOA_PRIMITIVES: + token = Name.Builtin.Pseudo + + yield index, token, value + + return GeneratedObjectiveCVariant + + +class ObjectiveCLexer(objective(CLexer)): + """ + For Objective-C source code with preprocessor directives. + """ + + name = 'Objective-C' + aliases = ['objective-c', 'objectivec', 'obj-c', 'objc'] + filenames = ['*.m', '*.h'] + mimetypes = ['text/x-objective-c'] + priority = 0.05 # Lower than C + + +class ObjectiveCppLexer(objective(CppLexer)): + """ + For Objective-C++ source code with preprocessor directives. + """ + + name = 'Objective-C++' + aliases = ['objective-c++', 'objectivec++', 'obj-c++', 'objc++'] + filenames = ['*.mm', '*.hh'] + mimetypes = ['text/x-objective-c++'] + priority = 0.05 # Lower than C++ + + +class LogosLexer(ObjectiveCppLexer): + """ + For Logos + Objective-C source code with preprocessor directives. + + .. versionadded:: 1.6 + """ + + name = 'Logos' + aliases = ['logos'] + filenames = ['*.x', '*.xi', '*.xm', '*.xmi'] + mimetypes = ['text/x-logos'] + priority = 0.25 + + tokens = { + 'statements': [ + (r'(%orig|%log)\b', Keyword), + (r'(%c)\b(\()(\s*)([a-zA-Z$_][\w$]*)(\s*)(\))', + bygroups(Keyword, Punctuation, Text, Name.Class, Text, Punctuation)), + (r'(%init)\b(\()', + bygroups(Keyword, Punctuation), 'logos_init_directive'), + (r'(%init)(?=\s*;)', bygroups(Keyword)), + (r'(%hook|%group)(\s+)([a-zA-Z$_][\w$]+)', + bygroups(Keyword, Text, Name.Class), '#pop'), + (r'(%subclass)(\s+)', bygroups(Keyword, Text), + ('#pop', 'logos_classname')), + inherit, + ], + 'logos_init_directive': [ + (r'\s+', Text), + (',', Punctuation, ('logos_init_directive', '#pop')), + (r'([a-zA-Z$_][\w$]*)(\s*)(=)(\s*)([^);]*)', + bygroups(Name.Class, Text, Punctuation, Text, Text)), + (r'([a-zA-Z$_][\w$]*)', Name.Class), + (r'\)', Punctuation, '#pop'), + ], + 'logos_classname': [ + (r'([a-zA-Z$_][\w$]*)(\s*:\s*)([a-zA-Z$_][\w$]*)?', + bygroups(Name.Class, Text, Name.Class), '#pop'), + (r'([a-zA-Z$_][\w$]*)', Name.Class, '#pop') + ], + 'root': [ + (r'(%subclass)(\s+)', bygroups(Keyword, Text), + 'logos_classname'), + (r'(%hook|%group)(\s+)([a-zA-Z$_][\w$]+)', + bygroups(Keyword, Text, Name.Class)), + (r'(%config)(\s*\(\s*)(\w+)(\s*=\s*)(.*?)(\s*\)\s*)', + bygroups(Keyword, Text, Name.Variable, Text, String, Text)), + (r'(%ctor)(\s*)(\{)', bygroups(Keyword, Text, Punctuation), + 'function'), + (r'(%new)(\s*)(\()(\s*.*?\s*)(\))', + bygroups(Keyword, Text, Keyword, String, Keyword)), + (r'(\s*)(%end)(\s*)', bygroups(Text, Keyword, Text)), + inherit, + ], + } + + _logos_keywords = re.compile(r'%(?:hook|ctor|init|c\()') + + def analyse_text(text): + if LogosLexer._logos_keywords.search(text): + return 1.0 + return 0 + + +class SwiftLexer(RegexLexer): + """ + For `Swift `_ source. + + .. versionadded:: 2.0 + """ + name = 'Swift' + filenames = ['*.swift'] + aliases = ['swift'] + mimetypes = ['text/x-swift'] + + tokens = { + 'root': [ + # Whitespace and Comments + (r'\n', Text), + (r'\s+', Text), + (r'//', Comment.Single, 'comment-single'), + (r'/\*', Comment.Multiline, 'comment-multi'), + (r'#(if|elseif|else|endif|available)\b', Comment.Preproc, 'preproc'), + + # Keywords + include('keywords'), + + # Global Types + (words(( + 'Array', 'AutoreleasingUnsafeMutablePointer', 'BidirectionalReverseView', + 'Bit', 'Bool', 'CFunctionPointer', 'COpaquePointer', 'CVaListPointer', + 'Character', 'ClosedInterval', 'CollectionOfOne', 'ContiguousArray', + 'Dictionary', 'DictionaryGenerator', 'DictionaryIndex', 'Double', + 'EmptyCollection', 'EmptyGenerator', 'EnumerateGenerator', + 'EnumerateSequence', 'FilterCollectionView', + 'FilterCollectionViewIndex', 'FilterGenerator', 'FilterSequenceView', + 'Float', 'Float80', 'FloatingPointClassification', 'GeneratorOf', + 'GeneratorOfOne', 'GeneratorSequence', 'HalfOpenInterval', 'HeapBuffer', + 'HeapBufferStorage', 'ImplicitlyUnwrappedOptional', 'IndexingGenerator', + 'Int', 'Int16', 'Int32', 'Int64', 'Int8', 'LazyBidirectionalCollection', + 'LazyForwardCollection', 'LazyRandomAccessCollection', + 'LazySequence', 'MapCollectionView', 'MapSequenceGenerator', + 'MapSequenceView', 'MirrorDisposition', 'ObjectIdentifier', 'OnHeap', + 'Optional', 'PermutationGenerator', 'QuickLookObject', + 'RandomAccessReverseView', 'Range', 'RangeGenerator', 'RawByte', 'Repeat', + 'ReverseBidirectionalIndex', 'ReverseRandomAccessIndex', 'SequenceOf', + 'SinkOf', 'Slice', 'StaticString', 'StrideThrough', 'StrideThroughGenerator', + 'StrideTo', 'StrideToGenerator', 'String', 'UInt', 'UInt16', 'UInt32', + 'UInt64', 'UInt8', 'UTF16', 'UTF32', 'UTF8', 'UnicodeDecodingResult', + 'UnicodeScalar', 'Unmanaged', 'UnsafeBufferPointer', + 'UnsafeBufferPointerGenerator', 'UnsafeMutableBufferPointer', + 'UnsafeMutablePointer', 'UnsafePointer', 'Zip2', 'ZipGenerator2', + # Protocols + 'AbsoluteValuable', 'AnyObject', 'ArrayLiteralConvertible', + 'BidirectionalIndexType', 'BitwiseOperationsType', + 'BooleanLiteralConvertible', 'BooleanType', 'CVarArgType', + 'CollectionType', 'Comparable', 'DebugPrintable', + 'DictionaryLiteralConvertible', 'Equatable', + 'ExtendedGraphemeClusterLiteralConvertible', + 'ExtensibleCollectionType', 'FloatLiteralConvertible', + 'FloatingPointType', 'ForwardIndexType', 'GeneratorType', 'Hashable', + 'IntegerArithmeticType', 'IntegerLiteralConvertible', 'IntegerType', + 'IntervalType', 'MirrorType', 'MutableCollectionType', 'MutableSliceable', + 'NilLiteralConvertible', 'OutputStreamType', 'Printable', + 'RandomAccessIndexType', 'RangeReplaceableCollectionType', + 'RawOptionSetType', 'RawRepresentable', 'Reflectable', 'SequenceType', + 'SignedIntegerType', 'SignedNumberType', 'SinkType', 'Sliceable', + 'Streamable', 'Strideable', 'StringInterpolationConvertible', + 'StringLiteralConvertible', 'UnicodeCodecType', + 'UnicodeScalarLiteralConvertible', 'UnsignedIntegerType', + '_ArrayBufferType', '_BidirectionalIndexType', '_CocoaStringType', + '_CollectionType', '_Comparable', '_ExtensibleCollectionType', + '_ForwardIndexType', '_Incrementable', '_IntegerArithmeticType', + '_IntegerType', '_ObjectiveCBridgeable', '_RandomAccessIndexType', + '_RawOptionSetType', '_SequenceType', '_Sequence_Type', + '_SignedIntegerType', '_SignedNumberType', '_Sliceable', '_Strideable', + '_SwiftNSArrayRequiredOverridesType', '_SwiftNSArrayType', + '_SwiftNSCopyingType', '_SwiftNSDictionaryRequiredOverridesType', + '_SwiftNSDictionaryType', '_SwiftNSEnumeratorType', + '_SwiftNSFastEnumerationType', '_SwiftNSStringRequiredOverridesType', + '_SwiftNSStringType', '_UnsignedIntegerType', + # Variables + 'C_ARGC', 'C_ARGV', 'Process', + # Typealiases + 'Any', 'AnyClass', 'BooleanLiteralType', 'CBool', 'CChar', 'CChar16', + 'CChar32', 'CDouble', 'CFloat', 'CInt', 'CLong', 'CLongLong', 'CShort', + 'CSignedChar', 'CUnsignedInt', 'CUnsignedLong', 'CUnsignedShort', + 'CWideChar', 'ExtendedGraphemeClusterType', 'Float32', 'Float64', + 'FloatLiteralType', 'IntMax', 'IntegerLiteralType', 'StringLiteralType', + 'UIntMax', 'UWord', 'UnicodeScalarType', 'Void', 'Word', + # Foundation/Cocoa + 'NSErrorPointer', 'NSObjectProtocol', 'Selector'), suffix=r'\b'), + Name.Builtin), + # Functions + (words(( + 'abs', 'advance', 'alignof', 'alignofValue', 'assert', 'assertionFailure', + 'contains', 'count', 'countElements', 'debugPrint', 'debugPrintln', + 'distance', 'dropFirst', 'dropLast', 'dump', 'enumerate', 'equal', + 'extend', 'fatalError', 'filter', 'find', 'first', 'getVaList', 'indices', + 'insert', 'isEmpty', 'join', 'last', 'lazy', 'lexicographicalCompare', + 'map', 'max', 'maxElement', 'min', 'minElement', 'numericCast', 'overlaps', + 'partition', 'precondition', 'preconditionFailure', 'prefix', 'print', + 'println', 'reduce', 'reflect', 'removeAll', 'removeAtIndex', 'removeLast', + 'removeRange', 'reverse', 'sizeof', 'sizeofValue', 'sort', 'sorted', + 'splice', 'split', 'startsWith', 'stride', 'strideof', 'strideofValue', + 'suffix', 'swap', 'toDebugString', 'toString', 'transcode', + 'underestimateCount', 'unsafeAddressOf', 'unsafeBitCast', 'unsafeDowncast', + 'withExtendedLifetime', 'withUnsafeMutablePointer', + 'withUnsafeMutablePointers', 'withUnsafePointer', 'withUnsafePointers', + 'withVaList'), suffix=r'\b'), + Name.Builtin.Pseudo), + + # Implicit Block Variables + (r'\$\d+', Name.Variable), + + # Binary Literal + (r'0b[01_]+', Number.Bin), + # Octal Literal + (r'0o[0-7_]+', Number.Oct), + # Hexadecimal Literal + (r'0x[0-9a-fA-F_]+', Number.Hex), + # Decimal Literal + (r'[0-9][0-9_]*(\.[0-9_]+[eE][+\-]?[0-9_]+|' + r'\.[0-9_]*|[eE][+\-]?[0-9_]+)', Number.Float), + (r'[0-9][0-9_]*', Number.Integer), + # String Literal + (r'"', String, 'string'), + + # Operators and Punctuation + (r'[(){}\[\].,:;=@#`?]|->|[<&?](?=\w)|(?<=\w)[>!?]', Punctuation), + (r'[/=\-+!*%<>&|^?~]+', Operator), + + # Identifier + (r'[a-zA-Z_]\w*', Name) + ], + 'keywords': [ + (words(( + 'as', 'break', 'case', 'catch', 'continue', 'default', 'defer', + 'do', 'else', 'fallthrough', 'for', 'guard', 'if', 'in', 'is', + 'repeat', 'return', '#selector', 'switch', 'throw', 'try', + 'where', 'while'), suffix=r'\b'), + Keyword), + (r'@availability\([^)]+\)', Keyword.Reserved), + (words(( + 'associativity', 'convenience', 'dynamic', 'didSet', 'final', + 'get', 'indirect', 'infix', 'inout', 'lazy', 'left', 'mutating', + 'none', 'nonmutating', 'optional', 'override', 'postfix', + 'precedence', 'prefix', 'Protocol', 'required', 'rethrows', + 'right', 'set', 'throws', 'Type', 'unowned', 'weak', 'willSet', + '@availability', '@autoclosure', '@noreturn', + '@NSApplicationMain', '@NSCopying', '@NSManaged', '@objc', + '@UIApplicationMain', '@IBAction', '@IBDesignable', + '@IBInspectable', '@IBOutlet'), suffix=r'\b'), + Keyword.Reserved), + (r'(as|dynamicType|false|is|nil|self|Self|super|true|__COLUMN__' + r'|__FILE__|__FUNCTION__|__LINE__|_' + r'|#(?:file|line|column|function))\b', Keyword.Constant), + (r'import\b', Keyword.Declaration, 'module'), + (r'(class|enum|extension|struct|protocol)(\s+)([a-zA-Z_]\w*)', + bygroups(Keyword.Declaration, Text, Name.Class)), + (r'(func)(\s+)([a-zA-Z_]\w*)', + bygroups(Keyword.Declaration, Text, Name.Function)), + (r'(var|let)(\s+)([a-zA-Z_]\w*)', bygroups(Keyword.Declaration, + Text, Name.Variable)), + (words(( + 'class', 'deinit', 'enum', 'extension', 'func', 'import', 'init', + 'internal', 'let', 'operator', 'private', 'protocol', 'public', + 'static', 'struct', 'subscript', 'typealias', 'var'), suffix=r'\b'), + Keyword.Declaration) + ], + 'comment': [ + (r':param: [a-zA-Z_]\w*|:returns?:|(FIXME|MARK|TODO):', + Comment.Special) + ], + + # Nested + 'comment-single': [ + (r'\n', Text, '#pop'), + include('comment'), + (r'[^\n]', Comment.Single) + ], + 'comment-multi': [ + include('comment'), + (r'[^*/]', Comment.Multiline), + (r'/\*', Comment.Multiline, '#push'), + (r'\*/', Comment.Multiline, '#pop'), + (r'[*/]', Comment.Multiline) + ], + 'module': [ + (r'\n', Text, '#pop'), + (r'[a-zA-Z_]\w*', Name.Class), + include('root') + ], + 'preproc': [ + (r'\n', Text, '#pop'), + include('keywords'), + (r'[A-Za-z]\w*', Comment.Preproc), + include('root') + ], + 'string': [ + (r'\\\(', String.Interpol, 'string-intp'), + (r'"', String, '#pop'), + (r"""\\['"\\nrt]|\\x[0-9a-fA-F]{2}|\\[0-7]{1,3}""" + r"""|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}""", String.Escape), + (r'[^\\"]+', String), + (r'\\', String) + ], + 'string-intp': [ + (r'\(', String.Interpol, '#push'), + (r'\)', String.Interpol, '#pop'), + include('root') + ] + } + + def get_tokens_unprocessed(self, text): + from pygments.lexers._cocoa_builtins import COCOA_INTERFACES, \ + COCOA_PROTOCOLS, COCOA_PRIMITIVES + + for index, token, value in \ + RegexLexer.get_tokens_unprocessed(self, text): + if token is Name or token is Name.Class: + if value in COCOA_INTERFACES or value in COCOA_PROTOCOLS \ + or value in COCOA_PRIMITIVES: + token = Name.Builtin.Pseudo + + yield index, token, value diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/ooc.py b/.venv/lib/python3.8/site-packages/pygments/lexers/ooc.py new file mode 100644 index 00000000..e0e0f249 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/ooc.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.ooc + ~~~~~~~~~~~~~~~~~~~ + + Lexers for the Ooc language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, bygroups, words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation + +__all__ = ['OocLexer'] + + +class OocLexer(RegexLexer): + """ + For `Ooc `_ source code + + .. versionadded:: 1.2 + """ + name = 'Ooc' + aliases = ['ooc'] + filenames = ['*.ooc'] + mimetypes = ['text/x-ooc'] + + tokens = { + 'root': [ + (words(( + 'class', 'interface', 'implement', 'abstract', 'extends', 'from', + 'this', 'super', 'new', 'const', 'final', 'static', 'import', + 'use', 'extern', 'inline', 'proto', 'break', 'continue', + 'fallthrough', 'operator', 'if', 'else', 'for', 'while', 'do', + 'switch', 'case', 'as', 'in', 'version', 'return', 'true', + 'false', 'null'), prefix=r'\b', suffix=r'\b'), + Keyword), + (r'include\b', Keyword, 'include'), + (r'(cover)([ \t]+)(from)([ \t]+)(\w+[*@]?)', + bygroups(Keyword, Text, Keyword, Text, Name.Class)), + (r'(func)((?:[ \t]|\\\n)+)(~[a-z_]\w*)', + bygroups(Keyword, Text, Name.Function)), + (r'\bfunc\b', Keyword), + # Note: %= and ^= not listed on http://ooc-lang.org/syntax + (r'//.*', Comment), + (r'(?s)/\*.*?\*/', Comment.Multiline), + (r'(==?|\+=?|-[=>]?|\*=?|/=?|:=|!=?|%=?|\?|>{1,3}=?|<{1,3}=?|\.\.|' + r'&&?|\|\|?|\^=?)', Operator), + (r'(\.)([ \t]*)([a-z]\w*)', bygroups(Operator, Text, + Name.Function)), + (r'[A-Z][A-Z0-9_]+', Name.Constant), + (r'[A-Z]\w*([@*]|\[[ \t]*\])?', Name.Class), + + (r'([a-z]\w*(?:~[a-z]\w*)?)((?:[ \t]|\\\n)*)(?=\()', + bygroups(Name.Function, Text)), + (r'[a-z]\w*', Name.Variable), + + # : introduces types + (r'[:(){}\[\];,]', Punctuation), + + (r'0x[0-9a-fA-F]+', Number.Hex), + (r'0c[0-9]+', Number.Oct), + (r'0b[01]+', Number.Bin), + (r'[0-9_]\.[0-9_]*(?!\.)', Number.Float), + (r'[0-9_]+', Number.Decimal), + + (r'"(?:\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\"])*"', + String.Double), + (r"'(?:\\.|\\[0-9]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'", + String.Char), + (r'@', Punctuation), # pointer dereference + (r'\.', Punctuation), # imports or chain operator + + (r'\\[ \t\n]', Text), + (r'[ \t]+', Text), + ], + 'include': [ + (r'[\w/]+', Name), + (r',', Punctuation), + (r'[ \t]', Text), + (r'[;\n]', Text, '#pop'), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/other.py b/.venv/lib/python3.8/site-packages/pygments/lexers/other.py new file mode 100644 index 00000000..8cdedcfc --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/other.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.other + ~~~~~~~~~~~~~~~~~~~~~ + + Just export lexer classes previously contained in this module. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexers.sql import SqlLexer, MySqlLexer, SqliteConsoleLexer +from pygments.lexers.shell import BashLexer, BashSessionLexer, BatchLexer, \ + TcshLexer +from pygments.lexers.robotframework import RobotFrameworkLexer +from pygments.lexers.testing import GherkinLexer +from pygments.lexers.esoteric import BrainfuckLexer, BefungeLexer, RedcodeLexer +from pygments.lexers.prolog import LogtalkLexer +from pygments.lexers.snobol import SnobolLexer +from pygments.lexers.rebol import RebolLexer +from pygments.lexers.configs import KconfigLexer, Cfengine3Lexer +from pygments.lexers.modeling import ModelicaLexer +from pygments.lexers.scripting import AppleScriptLexer, MOOCodeLexer, \ + HybrisLexer +from pygments.lexers.graphics import PostScriptLexer, GnuplotLexer, \ + AsymptoteLexer, PovrayLexer +from pygments.lexers.business import ABAPLexer, OpenEdgeLexer, \ + GoodDataCLLexer, MaqlLexer +from pygments.lexers.automation import AutoItLexer, AutohotkeyLexer +from pygments.lexers.dsls import ProtoBufLexer, BroLexer, PuppetLexer, \ + MscgenLexer, VGLLexer +from pygments.lexers.basic import CbmBasicV2Lexer +from pygments.lexers.pawn import SourcePawnLexer, PawnLexer +from pygments.lexers.ecl import ECLLexer +from pygments.lexers.urbi import UrbiscriptLexer +from pygments.lexers.smalltalk import SmalltalkLexer, NewspeakLexer +from pygments.lexers.installers import NSISLexer, RPMSpecLexer +from pygments.lexers.textedit import AwkLexer +from pygments.lexers.smv import NuSMVLexer + +__all__ = [] diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/parasail.py b/.venv/lib/python3.8/site-packages/pygments/lexers/parasail.py new file mode 100644 index 00000000..1b626b08 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/parasail.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.parasail + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for ParaSail. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Literal + +__all__ = ['ParaSailLexer'] + + +class ParaSailLexer(RegexLexer): + """ + For `ParaSail `_ source code. + + .. versionadded:: 2.1 + """ + + name = 'ParaSail' + aliases = ['parasail'] + filenames = ['*.psi', '*.psl'] + mimetypes = ['text/x-parasail'] + + flags = re.MULTILINE + + tokens = { + 'root': [ + (r'[^\S\n]+', Text), + (r'//.*?\n', Comment.Single), + (r'\b(and|or|xor)=', Operator.Word), + (r'\b(and(\s+then)?|or(\s+else)?|xor|rem|mod|' + r'(is|not)\s+null)\b', + Operator.Word), + # Keywords + (r'\b(abs|abstract|all|block|class|concurrent|const|continue|' + r'each|end|exit|extends|exports|forward|func|global|implements|' + r'import|in|interface|is|lambda|locked|new|not|null|of|op|' + r'optional|private|queued|ref|return|reverse|separate|some|' + r'type|until|var|with|' + # Control flow + r'if|then|else|elsif|case|for|while|loop)\b', + Keyword.Reserved), + (r'(abstract\s+)?(interface|class|op|func|type)', + Keyword.Declaration), + # Literals + (r'"[^"]*"', String), + (r'\\[\'ntrf"0]', String.Escape), + (r'#[a-zA-Z]\w*', Literal), # Enumeration + include('numbers'), + (r"'[^']'", String.Char), + (r'[a-zA-Z]\w*', Name), + # Operators and Punctuation + (r'(<==|==>|<=>|\*\*=|<\|=|<<=|>>=|==|!=|=\?|<=|>=|' + r'\*\*|<<|>>|=>|:=|\+=|-=|\*=|\|=|\||/=|\+|-|\*|/|' + r'\.\.|<\.\.|\.\.<|<\.\.<)', + Operator), + (r'(<|>|\[|\]|\(|\)|\||:|;|,|.|\{|\}|->)', + Punctuation), + (r'\n+', Text), + ], + 'numbers': [ + (r'\d[0-9_]*#[0-9a-fA-F][0-9a-fA-F_]*#', Number.Hex), # any base + (r'0[xX][0-9a-fA-F][0-9a-fA-F_]*', Number.Hex), # C-like hex + (r'0[bB][01][01_]*', Number.Bin), # C-like bin + (r'\d[0-9_]*\.\d[0-9_]*[eE][+-]\d[0-9_]*', # float exp + Number.Float), + (r'\d[0-9_]*\.\d[0-9_]*', Number.Float), # float + (r'\d[0-9_]*', Number.Integer), # integer + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/parsers.py b/.venv/lib/python3.8/site-packages/pygments/lexers/parsers.py new file mode 100644 index 00000000..13a3a83c --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/parsers.py @@ -0,0 +1,800 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.parsers + ~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for parser generators. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, DelegatingLexer, \ + include, bygroups, using +from pygments.token import Punctuation, Other, Text, Comment, Operator, \ + Keyword, Name, String, Number, Whitespace +from pygments.lexers.jvm import JavaLexer +from pygments.lexers.c_cpp import CLexer, CppLexer +from pygments.lexers.objective import ObjectiveCLexer +from pygments.lexers.d import DLexer +from pygments.lexers.dotnet import CSharpLexer +from pygments.lexers.ruby import RubyLexer +from pygments.lexers.python import PythonLexer +from pygments.lexers.perl import PerlLexer + +__all__ = ['RagelLexer', 'RagelEmbeddedLexer', 'RagelCLexer', 'RagelDLexer', + 'RagelCppLexer', 'RagelObjectiveCLexer', 'RagelRubyLexer', + 'RagelJavaLexer', 'AntlrLexer', 'AntlrPythonLexer', + 'AntlrPerlLexer', 'AntlrRubyLexer', 'AntlrCppLexer', + 'AntlrCSharpLexer', 'AntlrObjectiveCLexer', + 'AntlrJavaLexer', 'AntlrActionScriptLexer', + 'TreetopLexer', 'EbnfLexer'] + + +class RagelLexer(RegexLexer): + """ + A pure `Ragel `_ lexer. Use this for + fragments of Ragel. For ``.rl`` files, use RagelEmbeddedLexer instead + (or one of the language-specific subclasses). + + .. versionadded:: 1.1 + """ + + name = 'Ragel' + aliases = ['ragel'] + filenames = [] + + tokens = { + 'whitespace': [ + (r'\s+', Whitespace) + ], + 'comments': [ + (r'\#.*$', Comment), + ], + 'keywords': [ + (r'(access|action|alphtype)\b', Keyword), + (r'(getkey|write|machine|include)\b', Keyword), + (r'(any|ascii|extend|alpha|digit|alnum|lower|upper)\b', Keyword), + (r'(xdigit|cntrl|graph|print|punct|space|zlen|empty)\b', Keyword) + ], + 'numbers': [ + (r'0x[0-9A-Fa-f]+', Number.Hex), + (r'[+-]?[0-9]+', Number.Integer), + ], + 'literals': [ + (r'"(\\\\|\\"|[^"])*"', String), # double quote string + (r"'(\\\\|\\'|[^'])*'", String), # single quote string + (r'\[(\\\\|\\\]|[^\]])*\]', String), # square bracket literals + (r'/(?!\*)(\\\\|\\/|[^/])*/', String.Regex), # regular expressions + ], + 'identifiers': [ + (r'[a-zA-Z_]\w*', Name.Variable), + ], + 'operators': [ + (r',', Operator), # Join + (r'\||&|--?', Operator), # Union, Intersection and Subtraction + (r'\.|<:|:>>?', Operator), # Concatention + (r':', Operator), # Label + (r'->', Operator), # Epsilon Transition + (r'(>|\$|%|<|@|<>)(/|eof\b)', Operator), # EOF Actions + (r'(>|\$|%|<|@|<>)(!|err\b)', Operator), # Global Error Actions + (r'(>|\$|%|<|@|<>)(\^|lerr\b)', Operator), # Local Error Actions + (r'(>|\$|%|<|@|<>)(~|to\b)', Operator), # To-State Actions + (r'(>|\$|%|<|@|<>)(\*|from\b)', Operator), # From-State Actions + (r'>|@|\$|%', Operator), # Transition Actions and Priorities + (r'\*|\?|\+|\{[0-9]*,[0-9]*\}', Operator), # Repetition + (r'!|\^', Operator), # Negation + (r'\(|\)', Operator), # Grouping + ], + 'root': [ + include('literals'), + include('whitespace'), + include('comments'), + include('keywords'), + include('numbers'), + include('identifiers'), + include('operators'), + (r'\{', Punctuation, 'host'), + (r'=', Operator), + (r';', Punctuation), + ], + 'host': [ + (r'(' + r'|'.join(( # keep host code in largest possible chunks + r'[^{}\'"/#]+', # exclude unsafe characters + r'[^\\]\\[{}]', # allow escaped { or } + + # strings and comments may safely contain unsafe characters + r'"(\\\\|\\"|[^"])*"', # double quote string + r"'(\\\\|\\'|[^'])*'", # single quote string + r'//.*$\n?', # single line comment + r'/\*(.|\n)*?\*/', # multi-line javadoc-style comment + r'\#.*$\n?', # ruby comment + + # regular expression: There's no reason for it to start + # with a * and this stops confusion with comments. + r'/(?!\*)(\\\\|\\/|[^/])*/', + + # / is safe now that we've handled regex and javadoc comments + r'/', + )) + r')+', Other), + + (r'\{', Punctuation, '#push'), + (r'\}', Punctuation, '#pop'), + ], + } + + +class RagelEmbeddedLexer(RegexLexer): + """ + A lexer for `Ragel`_ embedded in a host language file. + + This will only highlight Ragel statements. If you want host language + highlighting then call the language-specific Ragel lexer. + + .. versionadded:: 1.1 + """ + + name = 'Embedded Ragel' + aliases = ['ragel-em'] + filenames = ['*.rl'] + + tokens = { + 'root': [ + (r'(' + r'|'.join(( # keep host code in largest possible chunks + r'[^%\'"/#]+', # exclude unsafe characters + r'%(?=[^%]|$)', # a single % sign is okay, just not 2 of them + + # strings and comments may safely contain unsafe characters + r'"(\\\\|\\"|[^"])*"', # double quote string + r"'(\\\\|\\'|[^'])*'", # single quote string + r'/\*(.|\n)*?\*/', # multi-line javadoc-style comment + r'//.*$\n?', # single line comment + r'\#.*$\n?', # ruby/ragel comment + r'/(?!\*)(\\\\|\\/|[^/])*/', # regular expression + + # / is safe now that we've handled regex and javadoc comments + r'/', + )) + r')+', Other), + + # Single Line FSM. + # Please don't put a quoted newline in a single line FSM. + # That's just mean. It will break this. + (r'(%%)(?![{%])(.*)($|;)(\n?)', bygroups(Punctuation, + using(RagelLexer), + Punctuation, Text)), + + # Multi Line FSM. + (r'(%%%%|%%)\{', Punctuation, 'multi-line-fsm'), + ], + 'multi-line-fsm': [ + (r'(' + r'|'.join(( # keep ragel code in largest possible chunks. + r'(' + r'|'.join(( + r'[^}\'"\[/#]', # exclude unsafe characters + r'\}(?=[^%]|$)', # } is okay as long as it's not followed by % + r'\}%(?=[^%]|$)', # ...well, one %'s okay, just not two... + r'[^\\]\\[{}]', # ...and } is okay if it's escaped + + # allow / if it's preceded with one of these symbols + # (ragel EOF actions) + r'(>|\$|%|<|@|<>)/', + + # specifically allow regex followed immediately by * + # so it doesn't get mistaken for a comment + r'/(?!\*)(\\\\|\\/|[^/])*/\*', + + # allow / as long as it's not followed by another / or by a * + r'/(?=[^/*]|$)', + + # We want to match as many of these as we can in one block. + # Not sure if we need the + sign here, + # does it help performance? + )) + r')+', + + # strings and comments may safely contain unsafe characters + r'"(\\\\|\\"|[^"])*"', # double quote string + r"'(\\\\|\\'|[^'])*'", # single quote string + r"\[(\\\\|\\\]|[^\]])*\]", # square bracket literal + r'/\*(.|\n)*?\*/', # multi-line javadoc-style comment + r'//.*$\n?', # single line comment + r'\#.*$\n?', # ruby/ragel comment + )) + r')+', using(RagelLexer)), + + (r'\}%%', Punctuation, '#pop'), + ] + } + + def analyse_text(text): + return '@LANG: indep' in text + + +class RagelRubyLexer(DelegatingLexer): + """ + A lexer for `Ragel`_ in a Ruby host file. + + .. versionadded:: 1.1 + """ + + name = 'Ragel in Ruby Host' + aliases = ['ragel-ruby', 'ragel-rb'] + filenames = ['*.rl'] + + def __init__(self, **options): + super().__init__(RubyLexer, RagelEmbeddedLexer, **options) + + def analyse_text(text): + return '@LANG: ruby' in text + + +class RagelCLexer(DelegatingLexer): + """ + A lexer for `Ragel`_ in a C host file. + + .. versionadded:: 1.1 + """ + + name = 'Ragel in C Host' + aliases = ['ragel-c'] + filenames = ['*.rl'] + + def __init__(self, **options): + super().__init__(CLexer, RagelEmbeddedLexer, **options) + + def analyse_text(text): + return '@LANG: c' in text + + +class RagelDLexer(DelegatingLexer): + """ + A lexer for `Ragel`_ in a D host file. + + .. versionadded:: 1.1 + """ + + name = 'Ragel in D Host' + aliases = ['ragel-d'] + filenames = ['*.rl'] + + def __init__(self, **options): + super().__init__(DLexer, RagelEmbeddedLexer, **options) + + def analyse_text(text): + return '@LANG: d' in text + + +class RagelCppLexer(DelegatingLexer): + """ + A lexer for `Ragel`_ in a CPP host file. + + .. versionadded:: 1.1 + """ + + name = 'Ragel in CPP Host' + aliases = ['ragel-cpp'] + filenames = ['*.rl'] + + def __init__(self, **options): + super().__init__(CppLexer, RagelEmbeddedLexer, **options) + + def analyse_text(text): + return '@LANG: c++' in text + + +class RagelObjectiveCLexer(DelegatingLexer): + """ + A lexer for `Ragel`_ in an Objective C host file. + + .. versionadded:: 1.1 + """ + + name = 'Ragel in Objective C Host' + aliases = ['ragel-objc'] + filenames = ['*.rl'] + + def __init__(self, **options): + super().__init__(ObjectiveCLexer, RagelEmbeddedLexer, **options) + + def analyse_text(text): + return '@LANG: objc' in text + + +class RagelJavaLexer(DelegatingLexer): + """ + A lexer for `Ragel`_ in a Java host file. + + .. versionadded:: 1.1 + """ + + name = 'Ragel in Java Host' + aliases = ['ragel-java'] + filenames = ['*.rl'] + + def __init__(self, **options): + super().__init__(JavaLexer, RagelEmbeddedLexer, **options) + + def analyse_text(text): + return '@LANG: java' in text + + +class AntlrLexer(RegexLexer): + """ + Generic `ANTLR`_ Lexer. + Should not be called directly, instead + use DelegatingLexer for your target language. + + .. versionadded:: 1.1 + + .. _ANTLR: http://www.antlr.org/ + """ + + name = 'ANTLR' + aliases = ['antlr'] + filenames = [] + + _id = r'[A-Za-z]\w*' + _TOKEN_REF = r'[A-Z]\w*' + _RULE_REF = r'[a-z]\w*' + _STRING_LITERAL = r'\'(?:\\\\|\\\'|[^\']*)\'' + _INT = r'[0-9]+' + + tokens = { + 'whitespace': [ + (r'\s+', Whitespace), + ], + 'comments': [ + (r'//.*$', Comment), + (r'/\*(.|\n)*?\*/', Comment), + ], + 'root': [ + include('whitespace'), + include('comments'), + + (r'(lexer|parser|tree)?(\s*)(grammar\b)(\s*)(' + _id + ')(;)', + bygroups(Keyword, Whitespace, Keyword, Whitespace, Name.Class, + Punctuation)), + # optionsSpec + (r'options\b', Keyword, 'options'), + # tokensSpec + (r'tokens\b', Keyword, 'tokens'), + # attrScope + (r'(scope)(\s*)(' + _id + r')(\s*)(\{)', + bygroups(Keyword, Whitespace, Name.Variable, Whitespace, + Punctuation), 'action'), + # exception + (r'(catch|finally)\b', Keyword, 'exception'), + # action + (r'(@' + _id + r')(\s*)(::)?(\s*)(' + _id + r')(\s*)(\{)', + bygroups(Name.Label, Whitespace, Punctuation, Whitespace, + Name.Label, Whitespace, Punctuation), 'action'), + # rule + (r'((?:protected|private|public|fragment)\b)?(\s*)(' + _id + ')(!)?', + bygroups(Keyword, Whitespace, Name.Label, Punctuation), + ('rule-alts', 'rule-prelims')), + ], + 'exception': [ + (r'\n', Whitespace, '#pop'), + (r'\s', Whitespace), + include('comments'), + + (r'\[', Punctuation, 'nested-arg-action'), + (r'\{', Punctuation, 'action'), + ], + 'rule-prelims': [ + include('whitespace'), + include('comments'), + + (r'returns\b', Keyword), + (r'\[', Punctuation, 'nested-arg-action'), + (r'\{', Punctuation, 'action'), + # throwsSpec + (r'(throws)(\s+)(' + _id + ')', + bygroups(Keyword, Whitespace, Name.Label)), + (r'(,)(\s*)(' + _id + ')', + bygroups(Punctuation, Whitespace, Name.Label)), # Additional throws + # optionsSpec + (r'options\b', Keyword, 'options'), + # ruleScopeSpec - scope followed by target language code or name of action + # TODO finish implementing other possibilities for scope + # L173 ANTLRv3.g from ANTLR book + (r'(scope)(\s+)(\{)', bygroups(Keyword, Whitespace, Punctuation), + 'action'), + (r'(scope)(\s+)(' + _id + r')(\s*)(;)', + bygroups(Keyword, Whitespace, Name.Label, Whitespace, Punctuation)), + # ruleAction + (r'(@' + _id + r')(\s*)(\{)', + bygroups(Name.Label, Whitespace, Punctuation), 'action'), + # finished prelims, go to rule alts! + (r':', Punctuation, '#pop') + ], + 'rule-alts': [ + include('whitespace'), + include('comments'), + + # These might need to go in a separate 'block' state triggered by ( + (r'options\b', Keyword, 'options'), + (r':', Punctuation), + + # literals + (r"'(\\\\|\\'|[^'])*'", String), + (r'"(\\\\|\\"|[^"])*"', String), + (r'<<([^>]|>[^>])>>', String), + # identifiers + # Tokens start with capital letter. + (r'\$?[A-Z_]\w*', Name.Constant), + # Rules start with small letter. + (r'\$?[a-z_]\w*', Name.Variable), + # operators + (r'(\+|\||->|=>|=|\(|\)|\.\.|\.|\?|\*|\^|!|\#|~)', Operator), + (r',', Punctuation), + (r'\[', Punctuation, 'nested-arg-action'), + (r'\{', Punctuation, 'action'), + (r';', Punctuation, '#pop') + ], + 'tokens': [ + include('whitespace'), + include('comments'), + (r'\{', Punctuation), + (r'(' + _TOKEN_REF + r')(\s*)(=)?(\s*)(' + _STRING_LITERAL + + r')?(\s*)(;)', + bygroups(Name.Label, Whitespace, Punctuation, Whitespace, + String, Whitespace, Punctuation)), + (r'\}', Punctuation, '#pop'), + ], + 'options': [ + include('whitespace'), + include('comments'), + (r'\{', Punctuation), + (r'(' + _id + r')(\s*)(=)(\s*)(' + + '|'.join((_id, _STRING_LITERAL, _INT, r'\*')) + r')(\s*)(;)', + bygroups(Name.Variable, Whitespace, Punctuation, Whitespace, + Text, Whitespace, Punctuation)), + (r'\}', Punctuation, '#pop'), + ], + 'action': [ + (r'(' + r'|'.join(( # keep host code in largest possible chunks + r'[^${}\'"/\\]+', # exclude unsafe characters + + # strings and comments may safely contain unsafe characters + r'"(\\\\|\\"|[^"])*"', # double quote string + r"'(\\\\|\\'|[^'])*'", # single quote string + r'//.*$\n?', # single line comment + r'/\*(.|\n)*?\*/', # multi-line javadoc-style comment + + # regular expression: There's no reason for it to start + # with a * and this stops confusion with comments. + r'/(?!\*)(\\\\|\\/|[^/])*/', + + # backslashes are okay, as long as we are not backslashing a % + r'\\(?!%)', + + # Now that we've handled regex and javadoc comments + # it's safe to let / through. + r'/', + )) + r')+', Other), + (r'(\\)(%)', bygroups(Punctuation, Other)), + (r'(\$[a-zA-Z]+)(\.?)(text|value)?', + bygroups(Name.Variable, Punctuation, Name.Property)), + (r'\{', Punctuation, '#push'), + (r'\}', Punctuation, '#pop'), + ], + 'nested-arg-action': [ + (r'(' + r'|'.join(( # keep host code in largest possible chunks. + r'[^$\[\]\'"/]+', # exclude unsafe characters + + # strings and comments may safely contain unsafe characters + r'"(\\\\|\\"|[^"])*"', # double quote string + r"'(\\\\|\\'|[^'])*'", # single quote string + r'//.*$\n?', # single line comment + r'/\*(.|\n)*?\*/', # multi-line javadoc-style comment + + # regular expression: There's no reason for it to start + # with a * and this stops confusion with comments. + r'/(?!\*)(\\\\|\\/|[^/])*/', + + # Now that we've handled regex and javadoc comments + # it's safe to let / through. + r'/', + )) + r')+', Other), + + + (r'\[', Punctuation, '#push'), + (r'\]', Punctuation, '#pop'), + (r'(\$[a-zA-Z]+)(\.?)(text|value)?', + bygroups(Name.Variable, Punctuation, Name.Property)), + (r'(\\\\|\\\]|\\\[|[^\[\]])+', Other), + ] + } + + def analyse_text(text): + return re.search(r'^\s*grammar\s+[a-zA-Z0-9]+\s*;', text, re.M) + + +# http://www.antlr.org/wiki/display/ANTLR3/Code+Generation+Targets + +class AntlrCppLexer(DelegatingLexer): + """ + `ANTLR`_ with CPP Target + + .. versionadded:: 1.1 + """ + + name = 'ANTLR With CPP Target' + aliases = ['antlr-cpp'] + filenames = ['*.G', '*.g'] + + def __init__(self, **options): + super().__init__(CppLexer, AntlrLexer, **options) + + def analyse_text(text): + return AntlrLexer.analyse_text(text) and \ + re.search(r'^\s*language\s*=\s*C\s*;', text, re.M) + + +class AntlrObjectiveCLexer(DelegatingLexer): + """ + `ANTLR`_ with Objective-C Target + + .. versionadded:: 1.1 + """ + + name = 'ANTLR With ObjectiveC Target' + aliases = ['antlr-objc'] + filenames = ['*.G', '*.g'] + + def __init__(self, **options): + super().__init__(ObjectiveCLexer, AntlrLexer, **options) + + def analyse_text(text): + return AntlrLexer.analyse_text(text) and \ + re.search(r'^\s*language\s*=\s*ObjC\s*;', text) + + +class AntlrCSharpLexer(DelegatingLexer): + """ + `ANTLR`_ with C# Target + + .. versionadded:: 1.1 + """ + + name = 'ANTLR With C# Target' + aliases = ['antlr-csharp', 'antlr-c#'] + filenames = ['*.G', '*.g'] + + def __init__(self, **options): + super().__init__(CSharpLexer, AntlrLexer, **options) + + def analyse_text(text): + return AntlrLexer.analyse_text(text) and \ + re.search(r'^\s*language\s*=\s*CSharp2\s*;', text, re.M) + + +class AntlrPythonLexer(DelegatingLexer): + """ + `ANTLR`_ with Python Target + + .. versionadded:: 1.1 + """ + + name = 'ANTLR With Python Target' + aliases = ['antlr-python'] + filenames = ['*.G', '*.g'] + + def __init__(self, **options): + super().__init__(PythonLexer, AntlrLexer, **options) + + def analyse_text(text): + return AntlrLexer.analyse_text(text) and \ + re.search(r'^\s*language\s*=\s*Python\s*;', text, re.M) + + +class AntlrJavaLexer(DelegatingLexer): + """ + `ANTLR`_ with Java Target + + .. versionadded:: 1. + """ + + name = 'ANTLR With Java Target' + aliases = ['antlr-java'] + filenames = ['*.G', '*.g'] + + def __init__(self, **options): + super().__init__(JavaLexer, AntlrLexer, **options) + + def analyse_text(text): + # Antlr language is Java by default + return AntlrLexer.analyse_text(text) and 0.9 + + +class AntlrRubyLexer(DelegatingLexer): + """ + `ANTLR`_ with Ruby Target + + .. versionadded:: 1.1 + """ + + name = 'ANTLR With Ruby Target' + aliases = ['antlr-ruby', 'antlr-rb'] + filenames = ['*.G', '*.g'] + + def __init__(self, **options): + super().__init__(RubyLexer, AntlrLexer, **options) + + def analyse_text(text): + return AntlrLexer.analyse_text(text) and \ + re.search(r'^\s*language\s*=\s*Ruby\s*;', text, re.M) + + +class AntlrPerlLexer(DelegatingLexer): + """ + `ANTLR`_ with Perl Target + + .. versionadded:: 1.1 + """ + + name = 'ANTLR With Perl Target' + aliases = ['antlr-perl'] + filenames = ['*.G', '*.g'] + + def __init__(self, **options): + super().__init__(PerlLexer, AntlrLexer, **options) + + def analyse_text(text): + return AntlrLexer.analyse_text(text) and \ + re.search(r'^\s*language\s*=\s*Perl5\s*;', text, re.M) + + +class AntlrActionScriptLexer(DelegatingLexer): + """ + `ANTLR`_ with ActionScript Target + + .. versionadded:: 1.1 + """ + + name = 'ANTLR With ActionScript Target' + aliases = ['antlr-as', 'antlr-actionscript'] + filenames = ['*.G', '*.g'] + + def __init__(self, **options): + from pygments.lexers.actionscript import ActionScriptLexer + super().__init__(ActionScriptLexer, AntlrLexer, **options) + + def analyse_text(text): + return AntlrLexer.analyse_text(text) and \ + re.search(r'^\s*language\s*=\s*ActionScript\s*;', text, re.M) + + +class TreetopBaseLexer(RegexLexer): + """ + A base lexer for `Treetop `_ grammars. + Not for direct use; use TreetopLexer instead. + + .. versionadded:: 1.6 + """ + + tokens = { + 'root': [ + include('space'), + (r'require[ \t]+[^\n\r]+[\n\r]', Other), + (r'module\b', Keyword.Namespace, 'module'), + (r'grammar\b', Keyword, 'grammar'), + ], + 'module': [ + include('space'), + include('end'), + (r'module\b', Keyword, '#push'), + (r'grammar\b', Keyword, 'grammar'), + (r'[A-Z]\w*(?:::[A-Z]\w*)*', Name.Namespace), + ], + 'grammar': [ + include('space'), + include('end'), + (r'rule\b', Keyword, 'rule'), + (r'include\b', Keyword, 'include'), + (r'[A-Z]\w*', Name), + ], + 'include': [ + include('space'), + (r'[A-Z]\w*(?:::[A-Z]\w*)*', Name.Class, '#pop'), + ], + 'rule': [ + include('space'), + include('end'), + (r'"(\\\\|\\"|[^"])*"', String.Double), + (r"'(\\\\|\\'|[^'])*'", String.Single), + (r'([A-Za-z_]\w*)(:)', bygroups(Name.Label, Punctuation)), + (r'[A-Za-z_]\w*', Name), + (r'[()]', Punctuation), + (r'[?+*/&!~]', Operator), + (r'\[(?:\\.|\[:\^?[a-z]+:\]|[^\\\]])+\]', String.Regex), + (r'([0-9]*)(\.\.)([0-9]*)', + bygroups(Number.Integer, Operator, Number.Integer)), + (r'(<)([^>]+)(>)', bygroups(Punctuation, Name.Class, Punctuation)), + (r'\{', Punctuation, 'inline_module'), + (r'\.', String.Regex), + ], + 'inline_module': [ + (r'\{', Other, 'ruby'), + (r'\}', Punctuation, '#pop'), + (r'[^{}]+', Other), + ], + 'ruby': [ + (r'\{', Other, '#push'), + (r'\}', Other, '#pop'), + (r'[^{}]+', Other), + ], + 'space': [ + (r'[ \t\n\r]+', Whitespace), + (r'#[^\n]*', Comment.Single), + ], + 'end': [ + (r'end\b', Keyword, '#pop'), + ], + } + + +class TreetopLexer(DelegatingLexer): + """ + A lexer for `Treetop `_ grammars. + + .. versionadded:: 1.6 + """ + + name = 'Treetop' + aliases = ['treetop'] + filenames = ['*.treetop', '*.tt'] + + def __init__(self, **options): + super().__init__(RubyLexer, TreetopBaseLexer, **options) + + +class EbnfLexer(RegexLexer): + """ + Lexer for `ISO/IEC 14977 EBNF + `_ + grammars. + + .. versionadded:: 2.0 + """ + + name = 'EBNF' + aliases = ['ebnf'] + filenames = ['*.ebnf'] + mimetypes = ['text/x-ebnf'] + + tokens = { + 'root': [ + include('whitespace'), + include('comment_start'), + include('identifier'), + (r'=', Operator, 'production'), + ], + 'production': [ + include('whitespace'), + include('comment_start'), + include('identifier'), + (r'"[^"]*"', String.Double), + (r"'[^']*'", String.Single), + (r'(\?[^?]*\?)', Name.Entity), + (r'[\[\]{}(),|]', Punctuation), + (r'-', Operator), + (r';', Punctuation, '#pop'), + (r'\.', Punctuation, '#pop'), + ], + 'whitespace': [ + (r'\s+', Text), + ], + 'comment_start': [ + (r'\(\*', Comment.Multiline, 'comment'), + ], + 'comment': [ + (r'[^*)]', Comment.Multiline), + include('comment_start'), + (r'\*\)', Comment.Multiline, '#pop'), + (r'[*)]', Comment.Multiline), + ], + 'identifier': [ + (r'([a-zA-Z][\w \-]*)', Keyword), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/pascal.py b/.venv/lib/python3.8/site-packages/pygments/lexers/pascal.py new file mode 100644 index 00000000..6756e234 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/pascal.py @@ -0,0 +1,644 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.pascal + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Pascal family languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import Lexer, RegexLexer, include, bygroups, words, \ + using, this, default +from pygments.util import get_bool_opt, get_list_opt +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Error +from pygments.scanner import Scanner + +# compatibility import +from pygments.lexers.modula2 import Modula2Lexer + +__all__ = ['DelphiLexer', 'AdaLexer'] + + +class DelphiLexer(Lexer): + """ + For `Delphi `_ (Borland Object Pascal), + Turbo Pascal and Free Pascal source code. + + Additional options accepted: + + `turbopascal` + Highlight Turbo Pascal specific keywords (default: ``True``). + `delphi` + Highlight Borland Delphi specific keywords (default: ``True``). + `freepascal` + Highlight Free Pascal specific keywords (default: ``True``). + `units` + A list of units that should be considered builtin, supported are + ``System``, ``SysUtils``, ``Classes`` and ``Math``. + Default is to consider all of them builtin. + """ + name = 'Delphi' + aliases = ['delphi', 'pas', 'pascal', 'objectpascal'] + filenames = ['*.pas', '*.dpr'] + mimetypes = ['text/x-pascal'] + + TURBO_PASCAL_KEYWORDS = ( + 'absolute', 'and', 'array', 'asm', 'begin', 'break', 'case', + 'const', 'constructor', 'continue', 'destructor', 'div', 'do', + 'downto', 'else', 'end', 'file', 'for', 'function', 'goto', + 'if', 'implementation', 'in', 'inherited', 'inline', 'interface', + 'label', 'mod', 'nil', 'not', 'object', 'of', 'on', 'operator', + 'or', 'packed', 'procedure', 'program', 'record', 'reintroduce', + 'repeat', 'self', 'set', 'shl', 'shr', 'string', 'then', 'to', + 'type', 'unit', 'until', 'uses', 'var', 'while', 'with', 'xor' + ) + + DELPHI_KEYWORDS = ( + 'as', 'class', 'except', 'exports', 'finalization', 'finally', + 'initialization', 'is', 'library', 'on', 'property', 'raise', + 'threadvar', 'try' + ) + + FREE_PASCAL_KEYWORDS = ( + 'dispose', 'exit', 'false', 'new', 'true' + ) + + BLOCK_KEYWORDS = { + 'begin', 'class', 'const', 'constructor', 'destructor', 'end', + 'finalization', 'function', 'implementation', 'initialization', + 'label', 'library', 'operator', 'procedure', 'program', 'property', + 'record', 'threadvar', 'type', 'unit', 'uses', 'var' + } + + FUNCTION_MODIFIERS = { + 'alias', 'cdecl', 'export', 'inline', 'interrupt', 'nostackframe', + 'pascal', 'register', 'safecall', 'softfloat', 'stdcall', + 'varargs', 'name', 'dynamic', 'near', 'virtual', 'external', + 'override', 'assembler' + } + + # XXX: those aren't global. but currently we know no way for defining + # them just for the type context. + DIRECTIVES = { + 'absolute', 'abstract', 'assembler', 'cppdecl', 'default', 'far', + 'far16', 'forward', 'index', 'oldfpccall', 'private', 'protected', + 'published', 'public' + } + + BUILTIN_TYPES = { + 'ansichar', 'ansistring', 'bool', 'boolean', 'byte', 'bytebool', + 'cardinal', 'char', 'comp', 'currency', 'double', 'dword', + 'extended', 'int64', 'integer', 'iunknown', 'longbool', 'longint', + 'longword', 'pansichar', 'pansistring', 'pbool', 'pboolean', + 'pbyte', 'pbytearray', 'pcardinal', 'pchar', 'pcomp', 'pcurrency', + 'pdate', 'pdatetime', 'pdouble', 'pdword', 'pextended', 'phandle', + 'pint64', 'pinteger', 'plongint', 'plongword', 'pointer', + 'ppointer', 'pshortint', 'pshortstring', 'psingle', 'psmallint', + 'pstring', 'pvariant', 'pwidechar', 'pwidestring', 'pword', + 'pwordarray', 'pwordbool', 'real', 'real48', 'shortint', + 'shortstring', 'single', 'smallint', 'string', 'tclass', 'tdate', + 'tdatetime', 'textfile', 'thandle', 'tobject', 'ttime', 'variant', + 'widechar', 'widestring', 'word', 'wordbool' + } + + BUILTIN_UNITS = { + 'System': ( + 'abs', 'acquireexceptionobject', 'addr', 'ansitoutf8', + 'append', 'arctan', 'assert', 'assigned', 'assignfile', + 'beginthread', 'blockread', 'blockwrite', 'break', 'chdir', + 'chr', 'close', 'closefile', 'comptocurrency', 'comptodouble', + 'concat', 'continue', 'copy', 'cos', 'dec', 'delete', + 'dispose', 'doubletocomp', 'endthread', 'enummodules', + 'enumresourcemodules', 'eof', 'eoln', 'erase', 'exceptaddr', + 'exceptobject', 'exclude', 'exit', 'exp', 'filepos', 'filesize', + 'fillchar', 'finalize', 'findclasshinstance', 'findhinstance', + 'findresourcehinstance', 'flush', 'frac', 'freemem', + 'get8087cw', 'getdir', 'getlasterror', 'getmem', + 'getmemorymanager', 'getmodulefilename', 'getvariantmanager', + 'halt', 'hi', 'high', 'inc', 'include', 'initialize', 'insert', + 'int', 'ioresult', 'ismemorymanagerset', 'isvariantmanagerset', + 'length', 'ln', 'lo', 'low', 'mkdir', 'move', 'new', 'odd', + 'olestrtostring', 'olestrtostrvar', 'ord', 'paramcount', + 'paramstr', 'pi', 'pos', 'pred', 'ptr', 'pucs4chars', 'random', + 'randomize', 'read', 'readln', 'reallocmem', + 'releaseexceptionobject', 'rename', 'reset', 'rewrite', 'rmdir', + 'round', 'runerror', 'seek', 'seekeof', 'seekeoln', + 'set8087cw', 'setlength', 'setlinebreakstyle', + 'setmemorymanager', 'setstring', 'settextbuf', + 'setvariantmanager', 'sin', 'sizeof', 'slice', 'sqr', 'sqrt', + 'str', 'stringofchar', 'stringtoolestr', 'stringtowidechar', + 'succ', 'swap', 'trunc', 'truncate', 'typeinfo', + 'ucs4stringtowidestring', 'unicodetoutf8', 'uniquestring', + 'upcase', 'utf8decode', 'utf8encode', 'utf8toansi', + 'utf8tounicode', 'val', 'vararrayredim', 'varclear', + 'widecharlentostring', 'widecharlentostrvar', + 'widechartostring', 'widechartostrvar', + 'widestringtoucs4string', 'write', 'writeln' + ), + 'SysUtils': ( + 'abort', 'addexitproc', 'addterminateproc', 'adjustlinebreaks', + 'allocmem', 'ansicomparefilename', 'ansicomparestr', + 'ansicomparetext', 'ansidequotedstr', 'ansiextractquotedstr', + 'ansilastchar', 'ansilowercase', 'ansilowercasefilename', + 'ansipos', 'ansiquotedstr', 'ansisamestr', 'ansisametext', + 'ansistrcomp', 'ansistricomp', 'ansistrlastchar', 'ansistrlcomp', + 'ansistrlicomp', 'ansistrlower', 'ansistrpos', 'ansistrrscan', + 'ansistrscan', 'ansistrupper', 'ansiuppercase', + 'ansiuppercasefilename', 'appendstr', 'assignstr', 'beep', + 'booltostr', 'bytetocharindex', 'bytetocharlen', 'bytetype', + 'callterminateprocs', 'changefileext', 'charlength', + 'chartobyteindex', 'chartobytelen', 'comparemem', 'comparestr', + 'comparetext', 'createdir', 'createguid', 'currentyear', + 'currtostr', 'currtostrf', 'date', 'datetimetofiledate', + 'datetimetostr', 'datetimetostring', 'datetimetosystemtime', + 'datetimetotimestamp', 'datetostr', 'dayofweek', 'decodedate', + 'decodedatefully', 'decodetime', 'deletefile', 'directoryexists', + 'diskfree', 'disksize', 'disposestr', 'encodedate', 'encodetime', + 'exceptionerrormessage', 'excludetrailingbackslash', + 'excludetrailingpathdelimiter', 'expandfilename', + 'expandfilenamecase', 'expanduncfilename', 'extractfiledir', + 'extractfiledrive', 'extractfileext', 'extractfilename', + 'extractfilepath', 'extractrelativepath', 'extractshortpathname', + 'fileage', 'fileclose', 'filecreate', 'filedatetodatetime', + 'fileexists', 'filegetattr', 'filegetdate', 'fileisreadonly', + 'fileopen', 'fileread', 'filesearch', 'fileseek', 'filesetattr', + 'filesetdate', 'filesetreadonly', 'filewrite', 'finalizepackage', + 'findclose', 'findcmdlineswitch', 'findfirst', 'findnext', + 'floattocurr', 'floattodatetime', 'floattodecimal', 'floattostr', + 'floattostrf', 'floattotext', 'floattotextfmt', 'fmtloadstr', + 'fmtstr', 'forcedirectories', 'format', 'formatbuf', 'formatcurr', + 'formatdatetime', 'formatfloat', 'freeandnil', 'getcurrentdir', + 'getenvironmentvariable', 'getfileversion', 'getformatsettings', + 'getlocaleformatsettings', 'getmodulename', 'getpackagedescription', + 'getpackageinfo', 'gettime', 'guidtostring', 'incamonth', + 'includetrailingbackslash', 'includetrailingpathdelimiter', + 'incmonth', 'initializepackage', 'interlockeddecrement', + 'interlockedexchange', 'interlockedexchangeadd', + 'interlockedincrement', 'inttohex', 'inttostr', 'isdelimiter', + 'isequalguid', 'isleapyear', 'ispathdelimiter', 'isvalidident', + 'languages', 'lastdelimiter', 'loadpackage', 'loadstr', + 'lowercase', 'msecstotimestamp', 'newstr', 'nextcharindex', 'now', + 'outofmemoryerror', 'quotedstr', 'raiselastoserror', + 'raiselastwin32error', 'removedir', 'renamefile', 'replacedate', + 'replacetime', 'safeloadlibrary', 'samefilename', 'sametext', + 'setcurrentdir', 'showexception', 'sleep', 'stralloc', 'strbufsize', + 'strbytetype', 'strcat', 'strcharlength', 'strcomp', 'strcopy', + 'strdispose', 'strecopy', 'strend', 'strfmt', 'stricomp', + 'stringreplace', 'stringtoguid', 'strlcat', 'strlcomp', 'strlcopy', + 'strlen', 'strlfmt', 'strlicomp', 'strlower', 'strmove', 'strnew', + 'strnextchar', 'strpas', 'strpcopy', 'strplcopy', 'strpos', + 'strrscan', 'strscan', 'strtobool', 'strtobooldef', 'strtocurr', + 'strtocurrdef', 'strtodate', 'strtodatedef', 'strtodatetime', + 'strtodatetimedef', 'strtofloat', 'strtofloatdef', 'strtoint', + 'strtoint64', 'strtoint64def', 'strtointdef', 'strtotime', + 'strtotimedef', 'strupper', 'supports', 'syserrormessage', + 'systemtimetodatetime', 'texttofloat', 'time', 'timestamptodatetime', + 'timestamptomsecs', 'timetostr', 'trim', 'trimleft', 'trimright', + 'tryencodedate', 'tryencodetime', 'tryfloattocurr', 'tryfloattodatetime', + 'trystrtobool', 'trystrtocurr', 'trystrtodate', 'trystrtodatetime', + 'trystrtofloat', 'trystrtoint', 'trystrtoint64', 'trystrtotime', + 'unloadpackage', 'uppercase', 'widecomparestr', 'widecomparetext', + 'widefmtstr', 'wideformat', 'wideformatbuf', 'widelowercase', + 'widesamestr', 'widesametext', 'wideuppercase', 'win32check', + 'wraptext' + ), + 'Classes': ( + 'activateclassgroup', 'allocatehwnd', 'bintohex', 'checksynchronize', + 'collectionsequal', 'countgenerations', 'deallocatehwnd', 'equalrect', + 'extractstrings', 'findclass', 'findglobalcomponent', 'getclass', + 'groupdescendantswith', 'hextobin', 'identtoint', + 'initinheritedcomponent', 'inttoident', 'invalidpoint', + 'isuniqueglobalcomponentname', 'linestart', 'objectbinarytotext', + 'objectresourcetotext', 'objecttexttobinary', 'objecttexttoresource', + 'pointsequal', 'readcomponentres', 'readcomponentresex', + 'readcomponentresfile', 'rect', 'registerclass', 'registerclassalias', + 'registerclasses', 'registercomponents', 'registerintegerconsts', + 'registernoicon', 'registernonactivex', 'smallpoint', 'startclassgroup', + 'teststreamformat', 'unregisterclass', 'unregisterclasses', + 'unregisterintegerconsts', 'unregistermoduleclasses', + 'writecomponentresfile' + ), + 'Math': ( + 'arccos', 'arccosh', 'arccot', 'arccoth', 'arccsc', 'arccsch', 'arcsec', + 'arcsech', 'arcsin', 'arcsinh', 'arctan2', 'arctanh', 'ceil', + 'comparevalue', 'cosecant', 'cosh', 'cot', 'cotan', 'coth', 'csc', + 'csch', 'cycletodeg', 'cycletograd', 'cycletorad', 'degtocycle', + 'degtograd', 'degtorad', 'divmod', 'doubledecliningbalance', + 'ensurerange', 'floor', 'frexp', 'futurevalue', 'getexceptionmask', + 'getprecisionmode', 'getroundmode', 'gradtocycle', 'gradtodeg', + 'gradtorad', 'hypot', 'inrange', 'interestpayment', 'interestrate', + 'internalrateofreturn', 'intpower', 'isinfinite', 'isnan', 'iszero', + 'ldexp', 'lnxp1', 'log10', 'log2', 'logn', 'max', 'maxintvalue', + 'maxvalue', 'mean', 'meanandstddev', 'min', 'minintvalue', 'minvalue', + 'momentskewkurtosis', 'netpresentvalue', 'norm', 'numberofperiods', + 'payment', 'periodpayment', 'poly', 'popnstddev', 'popnvariance', + 'power', 'presentvalue', 'radtocycle', 'radtodeg', 'radtograd', + 'randg', 'randomrange', 'roundto', 'samevalue', 'sec', 'secant', + 'sech', 'setexceptionmask', 'setprecisionmode', 'setroundmode', + 'sign', 'simpleroundto', 'sincos', 'sinh', 'slndepreciation', 'stddev', + 'sum', 'sumint', 'sumofsquares', 'sumsandsquares', 'syddepreciation', + 'tan', 'tanh', 'totalvariance', 'variance' + ) + } + + ASM_REGISTERS = { + 'ah', 'al', 'ax', 'bh', 'bl', 'bp', 'bx', 'ch', 'cl', 'cr0', + 'cr1', 'cr2', 'cr3', 'cr4', 'cs', 'cx', 'dh', 'di', 'dl', 'dr0', + 'dr1', 'dr2', 'dr3', 'dr4', 'dr5', 'dr6', 'dr7', 'ds', 'dx', + 'eax', 'ebp', 'ebx', 'ecx', 'edi', 'edx', 'es', 'esi', 'esp', + 'fs', 'gs', 'mm0', 'mm1', 'mm2', 'mm3', 'mm4', 'mm5', 'mm6', + 'mm7', 'si', 'sp', 'ss', 'st0', 'st1', 'st2', 'st3', 'st4', 'st5', + 'st6', 'st7', 'xmm0', 'xmm1', 'xmm2', 'xmm3', 'xmm4', 'xmm5', + 'xmm6', 'xmm7' + } + + ASM_INSTRUCTIONS = { + 'aaa', 'aad', 'aam', 'aas', 'adc', 'add', 'and', 'arpl', 'bound', + 'bsf', 'bsr', 'bswap', 'bt', 'btc', 'btr', 'bts', 'call', 'cbw', + 'cdq', 'clc', 'cld', 'cli', 'clts', 'cmc', 'cmova', 'cmovae', + 'cmovb', 'cmovbe', 'cmovc', 'cmovcxz', 'cmove', 'cmovg', + 'cmovge', 'cmovl', 'cmovle', 'cmovna', 'cmovnae', 'cmovnb', + 'cmovnbe', 'cmovnc', 'cmovne', 'cmovng', 'cmovnge', 'cmovnl', + 'cmovnle', 'cmovno', 'cmovnp', 'cmovns', 'cmovnz', 'cmovo', + 'cmovp', 'cmovpe', 'cmovpo', 'cmovs', 'cmovz', 'cmp', 'cmpsb', + 'cmpsd', 'cmpsw', 'cmpxchg', 'cmpxchg486', 'cmpxchg8b', 'cpuid', + 'cwd', 'cwde', 'daa', 'das', 'dec', 'div', 'emms', 'enter', 'hlt', + 'ibts', 'icebp', 'idiv', 'imul', 'in', 'inc', 'insb', 'insd', + 'insw', 'int', 'int01', 'int03', 'int1', 'int3', 'into', 'invd', + 'invlpg', 'iret', 'iretd', 'iretw', 'ja', 'jae', 'jb', 'jbe', + 'jc', 'jcxz', 'jcxz', 'je', 'jecxz', 'jg', 'jge', 'jl', 'jle', + 'jmp', 'jna', 'jnae', 'jnb', 'jnbe', 'jnc', 'jne', 'jng', 'jnge', + 'jnl', 'jnle', 'jno', 'jnp', 'jns', 'jnz', 'jo', 'jp', 'jpe', + 'jpo', 'js', 'jz', 'lahf', 'lar', 'lcall', 'lds', 'lea', 'leave', + 'les', 'lfs', 'lgdt', 'lgs', 'lidt', 'ljmp', 'lldt', 'lmsw', + 'loadall', 'loadall286', 'lock', 'lodsb', 'lodsd', 'lodsw', + 'loop', 'loope', 'loopne', 'loopnz', 'loopz', 'lsl', 'lss', 'ltr', + 'mov', 'movd', 'movq', 'movsb', 'movsd', 'movsw', 'movsx', + 'movzx', 'mul', 'neg', 'nop', 'not', 'or', 'out', 'outsb', 'outsd', + 'outsw', 'pop', 'popa', 'popad', 'popaw', 'popf', 'popfd', 'popfw', + 'push', 'pusha', 'pushad', 'pushaw', 'pushf', 'pushfd', 'pushfw', + 'rcl', 'rcr', 'rdmsr', 'rdpmc', 'rdshr', 'rdtsc', 'rep', 'repe', + 'repne', 'repnz', 'repz', 'ret', 'retf', 'retn', 'rol', 'ror', + 'rsdc', 'rsldt', 'rsm', 'sahf', 'sal', 'salc', 'sar', 'sbb', + 'scasb', 'scasd', 'scasw', 'seta', 'setae', 'setb', 'setbe', + 'setc', 'setcxz', 'sete', 'setg', 'setge', 'setl', 'setle', + 'setna', 'setnae', 'setnb', 'setnbe', 'setnc', 'setne', 'setng', + 'setnge', 'setnl', 'setnle', 'setno', 'setnp', 'setns', 'setnz', + 'seto', 'setp', 'setpe', 'setpo', 'sets', 'setz', 'sgdt', 'shl', + 'shld', 'shr', 'shrd', 'sidt', 'sldt', 'smi', 'smint', 'smintold', + 'smsw', 'stc', 'std', 'sti', 'stosb', 'stosd', 'stosw', 'str', + 'sub', 'svdc', 'svldt', 'svts', 'syscall', 'sysenter', 'sysexit', + 'sysret', 'test', 'ud1', 'ud2', 'umov', 'verr', 'verw', 'wait', + 'wbinvd', 'wrmsr', 'wrshr', 'xadd', 'xbts', 'xchg', 'xlat', + 'xlatb', 'xor' + } + + def __init__(self, **options): + Lexer.__init__(self, **options) + self.keywords = set() + if get_bool_opt(options, 'turbopascal', True): + self.keywords.update(self.TURBO_PASCAL_KEYWORDS) + if get_bool_opt(options, 'delphi', True): + self.keywords.update(self.DELPHI_KEYWORDS) + if get_bool_opt(options, 'freepascal', True): + self.keywords.update(self.FREE_PASCAL_KEYWORDS) + self.builtins = set() + for unit in get_list_opt(options, 'units', list(self.BUILTIN_UNITS)): + self.builtins.update(self.BUILTIN_UNITS[unit]) + + def get_tokens_unprocessed(self, text): + scanner = Scanner(text, re.DOTALL | re.MULTILINE | re.IGNORECASE) + stack = ['initial'] + in_function_block = False + in_property_block = False + was_dot = False + next_token_is_function = False + next_token_is_property = False + collect_labels = False + block_labels = set() + brace_balance = [0, 0] + + while not scanner.eos: + token = Error + + if stack[-1] == 'initial': + if scanner.scan(r'\s+'): + token = Text + elif scanner.scan(r'\{.*?\}|\(\*.*?\*\)'): + if scanner.match.startswith('$'): + token = Comment.Preproc + else: + token = Comment.Multiline + elif scanner.scan(r'//.*?$'): + token = Comment.Single + elif scanner.scan(r'[-+*\/=<>:;,.@\^]'): + token = Operator + # stop label highlighting on next ";" + if collect_labels and scanner.match == ';': + collect_labels = False + elif scanner.scan(r'[\(\)\[\]]+'): + token = Punctuation + # abort function naming ``foo = Function(...)`` + next_token_is_function = False + # if we are in a function block we count the open + # braces because ootherwise it's impossible to + # determine the end of the modifier context + if in_function_block or in_property_block: + if scanner.match == '(': + brace_balance[0] += 1 + elif scanner.match == ')': + brace_balance[0] -= 1 + elif scanner.match == '[': + brace_balance[1] += 1 + elif scanner.match == ']': + brace_balance[1] -= 1 + elif scanner.scan(r'[A-Za-z_][A-Za-z_0-9]*'): + lowercase_name = scanner.match.lower() + if lowercase_name == 'result': + token = Name.Builtin.Pseudo + elif lowercase_name in self.keywords: + token = Keyword + # if we are in a special block and a + # block ending keyword occours (and the parenthesis + # is balanced) we end the current block context + if (in_function_block or in_property_block) and \ + lowercase_name in self.BLOCK_KEYWORDS and \ + brace_balance[0] <= 0 and \ + brace_balance[1] <= 0: + in_function_block = False + in_property_block = False + brace_balance = [0, 0] + block_labels = set() + if lowercase_name in ('label', 'goto'): + collect_labels = True + elif lowercase_name == 'asm': + stack.append('asm') + elif lowercase_name == 'property': + in_property_block = True + next_token_is_property = True + elif lowercase_name in ('procedure', 'operator', + 'function', 'constructor', + 'destructor'): + in_function_block = True + next_token_is_function = True + # we are in a function block and the current name + # is in the set of registered modifiers. highlight + # it as pseudo keyword + elif in_function_block and \ + lowercase_name in self.FUNCTION_MODIFIERS: + token = Keyword.Pseudo + # if we are in a property highlight some more + # modifiers + elif in_property_block and \ + lowercase_name in ('read', 'write'): + token = Keyword.Pseudo + next_token_is_function = True + # if the last iteration set next_token_is_function + # to true we now want this name highlighted as + # function. so do that and reset the state + elif next_token_is_function: + # Look if the next token is a dot. If yes it's + # not a function, but a class name and the + # part after the dot a function name + if scanner.test(r'\s*\.\s*'): + token = Name.Class + # it's not a dot, our job is done + else: + token = Name.Function + next_token_is_function = False + # same for properties + elif next_token_is_property: + token = Name.Property + next_token_is_property = False + # Highlight this token as label and add it + # to the list of known labels + elif collect_labels: + token = Name.Label + block_labels.add(scanner.match.lower()) + # name is in list of known labels + elif lowercase_name in block_labels: + token = Name.Label + elif lowercase_name in self.BUILTIN_TYPES: + token = Keyword.Type + elif lowercase_name in self.DIRECTIVES: + token = Keyword.Pseudo + # builtins are just builtins if the token + # before isn't a dot + elif not was_dot and lowercase_name in self.builtins: + token = Name.Builtin + else: + token = Name + elif scanner.scan(r"'"): + token = String + stack.append('string') + elif scanner.scan(r'\#(\d+|\$[0-9A-Fa-f]+)'): + token = String.Char + elif scanner.scan(r'\$[0-9A-Fa-f]+'): + token = Number.Hex + elif scanner.scan(r'\d+(?![eE]|\.[^.])'): + token = Number.Integer + elif scanner.scan(r'\d+(\.\d+([eE][+-]?\d+)?|[eE][+-]?\d+)'): + token = Number.Float + else: + # if the stack depth is deeper than once, pop + if len(stack) > 1: + stack.pop() + scanner.get_char() + + elif stack[-1] == 'string': + if scanner.scan(r"''"): + token = String.Escape + elif scanner.scan(r"'"): + token = String + stack.pop() + elif scanner.scan(r"[^']*"): + token = String + else: + scanner.get_char() + stack.pop() + + elif stack[-1] == 'asm': + if scanner.scan(r'\s+'): + token = Text + elif scanner.scan(r'end'): + token = Keyword + stack.pop() + elif scanner.scan(r'\{.*?\}|\(\*.*?\*\)'): + if scanner.match.startswith('$'): + token = Comment.Preproc + else: + token = Comment.Multiline + elif scanner.scan(r'//.*?$'): + token = Comment.Single + elif scanner.scan(r"'"): + token = String + stack.append('string') + elif scanner.scan(r'@@[A-Za-z_][A-Za-z_0-9]*'): + token = Name.Label + elif scanner.scan(r'[A-Za-z_][A-Za-z_0-9]*'): + lowercase_name = scanner.match.lower() + if lowercase_name in self.ASM_INSTRUCTIONS: + token = Keyword + elif lowercase_name in self.ASM_REGISTERS: + token = Name.Builtin + else: + token = Name + elif scanner.scan(r'[-+*\/=<>:;,.@\^]+'): + token = Operator + elif scanner.scan(r'[\(\)\[\]]+'): + token = Punctuation + elif scanner.scan(r'\$[0-9A-Fa-f]+'): + token = Number.Hex + elif scanner.scan(r'\d+(?![eE]|\.[^.])'): + token = Number.Integer + elif scanner.scan(r'\d+(\.\d+([eE][+-]?\d+)?|[eE][+-]?\d+)'): + token = Number.Float + else: + scanner.get_char() + stack.pop() + + # save the dot!!!11 + if scanner.match.strip(): + was_dot = scanner.match == '.' + yield scanner.start_pos, token, scanner.match or '' + + +class AdaLexer(RegexLexer): + """ + For Ada source code. + + .. versionadded:: 1.3 + """ + + name = 'Ada' + aliases = ['ada', 'ada95', 'ada2005'] + filenames = ['*.adb', '*.ads', '*.ada'] + mimetypes = ['text/x-ada'] + + flags = re.MULTILINE | re.IGNORECASE + + tokens = { + 'root': [ + (r'[^\S\n]+', Text), + (r'--.*?\n', Comment.Single), + (r'[^\S\n]+', Text), + (r'function|procedure|entry', Keyword.Declaration, 'subprogram'), + (r'(subtype|type)(\s+)(\w+)', + bygroups(Keyword.Declaration, Text, Keyword.Type), 'type_def'), + (r'task|protected', Keyword.Declaration), + (r'(subtype)(\s+)', bygroups(Keyword.Declaration, Text)), + (r'(end)(\s+)', bygroups(Keyword.Reserved, Text), 'end'), + (r'(pragma)(\s+)(\w+)', bygroups(Keyword.Reserved, Text, + Comment.Preproc)), + (r'(true|false|null)\b', Keyword.Constant), + (words(( + 'Address', 'Byte', 'Boolean', 'Character', 'Controlled', 'Count', + 'Cursor', 'Duration', 'File_Mode', 'File_Type', 'Float', 'Generator', + 'Integer', 'Long_Float', 'Long_Integer', 'Long_Long_Float', + 'Long_Long_Integer', 'Natural', 'Positive', 'Reference_Type', + 'Short_Float', 'Short_Integer', 'Short_Short_Float', + 'Short_Short_Integer', 'String', 'Wide_Character', 'Wide_String'), + suffix=r'\b'), + Keyword.Type), + (r'(and(\s+then)?|in|mod|not|or(\s+else)|rem)\b', Operator.Word), + (r'generic|private', Keyword.Declaration), + (r'package', Keyword.Declaration, 'package'), + (r'array\b', Keyword.Reserved, 'array_def'), + (r'(with|use)(\s+)', bygroups(Keyword.Namespace, Text), 'import'), + (r'(\w+)(\s*)(:)(\s*)(constant)', + bygroups(Name.Constant, Text, Punctuation, Text, + Keyword.Reserved)), + (r'<<\w+>>', Name.Label), + (r'(\w+)(\s*)(:)(\s*)(declare|begin|loop|for|while)', + bygroups(Name.Label, Text, Punctuation, Text, Keyword.Reserved)), + (words(( + 'abort', 'abs', 'abstract', 'accept', 'access', 'aliased', 'all', + 'array', 'at', 'begin', 'body', 'case', 'constant', 'declare', + 'delay', 'delta', 'digits', 'do', 'else', 'elsif', 'end', 'entry', + 'exception', 'exit', 'interface', 'for', 'goto', 'if', 'is', 'limited', + 'loop', 'new', 'null', 'of', 'or', 'others', 'out', 'overriding', + 'pragma', 'protected', 'raise', 'range', 'record', 'renames', 'requeue', + 'return', 'reverse', 'select', 'separate', 'some', 'subtype', + 'synchronized', 'task', 'tagged', 'terminate', 'then', 'type', 'until', + 'when', 'while', 'xor'), prefix=r'\b', suffix=r'\b'), + Keyword.Reserved), + (r'"[^"]*"', String), + include('attribute'), + include('numbers'), + (r"'[^']'", String.Character), + (r'(\w+)(\s*|[(,])', bygroups(Name, using(this))), + (r"(<>|=>|:=|[()|:;,.'])", Punctuation), + (r'[*<>+=/&-]', Operator), + (r'\n+', Text), + ], + 'numbers': [ + (r'[0-9_]+#[0-9a-f_\.]+#', Number.Hex), + (r'[0-9_]+\.[0-9_]*', Number.Float), + (r'[0-9_]+', Number.Integer), + ], + 'attribute': [ + (r"(')(\w+)", bygroups(Punctuation, Name.Attribute)), + ], + 'subprogram': [ + (r'\(', Punctuation, ('#pop', 'formal_part')), + (r';', Punctuation, '#pop'), + (r'is\b', Keyword.Reserved, '#pop'), + (r'"[^"]+"|\w+', Name.Function), + include('root'), + ], + 'end': [ + ('(if|case|record|loop|select)', Keyword.Reserved), + (r'"[^"]+"|[\w.]+', Name.Function), + (r'\s+', Text), + (';', Punctuation, '#pop'), + ], + 'type_def': [ + (r';', Punctuation, '#pop'), + (r'\(', Punctuation, 'formal_part'), + (r'with|and|use', Keyword.Reserved), + (r'array\b', Keyword.Reserved, ('#pop', 'array_def')), + (r'record\b', Keyword.Reserved, ('record_def')), + (r'(null record)(;)', bygroups(Keyword.Reserved, Punctuation), '#pop'), + include('root'), + ], + 'array_def': [ + (r';', Punctuation, '#pop'), + (r'(\w+)(\s+)(range)', bygroups(Keyword.Type, Text, Keyword.Reserved)), + include('root'), + ], + 'record_def': [ + (r'end record', Keyword.Reserved, '#pop'), + include('root'), + ], + 'import': [ + (r'[\w.]+', Name.Namespace, '#pop'), + default('#pop'), + ], + 'formal_part': [ + (r'\)', Punctuation, '#pop'), + (r'\w+', Name.Variable), + (r',|:[^=]', Punctuation), + (r'(in|not|null|out|access)\b', Keyword.Reserved), + include('root'), + ], + 'package': [ + ('body', Keyword.Declaration), + (r'is\s+new|renames', Keyword.Reserved), + ('is', Keyword.Reserved, '#pop'), + (';', Punctuation, '#pop'), + (r'\(', Punctuation, 'package_instantiation'), + (r'([\w.]+)', Name.Class), + include('root'), + ], + 'package_instantiation': [ + (r'("[^"]+"|\w+)(\s+)(=>)', bygroups(Name.Variable, Text, Punctuation)), + (r'[\w.\'"]', Text), + (r'\)', Punctuation, '#pop'), + include('root'), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/pawn.py b/.venv/lib/python3.8/site-packages/pygments/lexers/pawn.py new file mode 100644 index 00000000..bc06d0d5 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/pawn.py @@ -0,0 +1,205 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.pawn + ~~~~~~~~~~~~~~~~~~~~ + + Lexers for the Pawn languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Error +from pygments.util import get_bool_opt + +__all__ = ['SourcePawnLexer', 'PawnLexer'] + + +class SourcePawnLexer(RegexLexer): + """ + For SourcePawn source code with preprocessor directives. + + .. versionadded:: 1.6 + """ + name = 'SourcePawn' + aliases = ['sp'] + filenames = ['*.sp'] + mimetypes = ['text/x-sourcepawn'] + + #: optional Comment or Whitespace + _ws = r'(?:\s|//.*?\n|/\*.*?\*/)+' + #: only one /* */ style comment + _ws1 = r'\s*(?:/[*].*?[*]/\s*)*' + + tokens = { + 'root': [ + # preprocessor directives: without whitespace + (r'^#if\s+0', Comment.Preproc, 'if0'), + ('^#', Comment.Preproc, 'macro'), + # or with whitespace + ('^' + _ws1 + r'#if\s+0', Comment.Preproc, 'if0'), + ('^' + _ws1 + '#', Comment.Preproc, 'macro'), + (r'\n', Text), + (r'\s+', Text), + (r'\\\n', Text), # line continuation + (r'/(\\\n)?/(\n|(.|\n)*?[^\\]\n)', Comment.Single), + (r'/(\\\n)?\*(.|\n)*?\*(\\\n)?/', Comment.Multiline), + (r'[{}]', Punctuation), + (r'L?"', String, 'string'), + (r"L?'(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'", String.Char), + (r'(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+[LlUu]*', Number.Float), + (r'(\d+\.\d*|\.\d+|\d+[fF])[fF]?', Number.Float), + (r'0x[0-9a-fA-F]+[LlUu]*', Number.Hex), + (r'0[0-7]+[LlUu]*', Number.Oct), + (r'\d+[LlUu]*', Number.Integer), + (r'\*/', Error), + (r'[~!%^&*+=|?:<>/-]', Operator), + (r'[()\[\],.;]', Punctuation), + (r'(case|const|continue|native|' + r'default|else|enum|for|if|new|operator|' + r'public|return|sizeof|static|decl|struct|switch)\b', Keyword), + (r'(bool|Float)\b', Keyword.Type), + (r'(true|false)\b', Keyword.Constant), + (r'[a-zA-Z_]\w*', Name), + ], + 'string': [ + (r'"', String, '#pop'), + (r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})', String.Escape), + (r'[^\\"\n]+', String), # all other characters + (r'\\\n', String), # line continuation + (r'\\', String), # stray backslash + ], + 'macro': [ + (r'[^/\n]+', Comment.Preproc), + (r'/\*(.|\n)*?\*/', Comment.Multiline), + (r'//.*?\n', Comment.Single, '#pop'), + (r'/', Comment.Preproc), + (r'(?<=\\)\n', Comment.Preproc), + (r'\n', Comment.Preproc, '#pop'), + ], + 'if0': [ + (r'^\s*#if.*?(?/-]', Operator), + (r'[()\[\],.;]', Punctuation), + (r'(switch|case|default|const|new|static|char|continue|break|' + r'if|else|for|while|do|operator|enum|' + r'public|return|sizeof|tagof|state|goto)\b', Keyword), + (r'(bool|Float)\b', Keyword.Type), + (r'(true|false)\b', Keyword.Constant), + (r'[a-zA-Z_]\w*', Name), + ], + 'string': [ + (r'"', String, '#pop'), + (r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})', String.Escape), + (r'[^\\"\n]+', String), # all other characters + (r'\\\n', String), # line continuation + (r'\\', String), # stray backslash + ], + 'macro': [ + (r'[^/\n]+', Comment.Preproc), + (r'/\*(.|\n)*?\*/', Comment.Multiline), + (r'//.*?\n', Comment.Single, '#pop'), + (r'/', Comment.Preproc), + (r'(?<=\\)\n', Comment.Preproc), + (r'\n', Comment.Preproc, '#pop'), + ], + 'if0': [ + (r'^\s*#if.*?(?`_ source code. + """ + + name = 'Perl' + aliases = ['perl', 'pl'] + filenames = ['*.pl', '*.pm', '*.t', '*.perl'] + mimetypes = ['text/x-perl', 'application/x-perl'] + + flags = re.DOTALL | re.MULTILINE + # TODO: give this to a perl guy who knows how to parse perl... + tokens = { + 'balanced-regex': [ + (r'/(\\\\|\\[^\\]|[^\\/])*/[egimosx]*', String.Regex, '#pop'), + (r'!(\\\\|\\[^\\]|[^\\!])*![egimosx]*', String.Regex, '#pop'), + (r'\\(\\\\|[^\\])*\\[egimosx]*', String.Regex, '#pop'), + (r'\{(\\\\|\\[^\\]|[^\\}])*\}[egimosx]*', String.Regex, '#pop'), + (r'<(\\\\|\\[^\\]|[^\\>])*>[egimosx]*', String.Regex, '#pop'), + (r'\[(\\\\|\\[^\\]|[^\\\]])*\][egimosx]*', String.Regex, '#pop'), + (r'\((\\\\|\\[^\\]|[^\\)])*\)[egimosx]*', String.Regex, '#pop'), + (r'@(\\\\|\\[^\\]|[^\\@])*@[egimosx]*', String.Regex, '#pop'), + (r'%(\\\\|\\[^\\]|[^\\%])*%[egimosx]*', String.Regex, '#pop'), + (r'\$(\\\\|\\[^\\]|[^\\$])*\$[egimosx]*', String.Regex, '#pop'), + ], + 'root': [ + (r'\A\#!.+?$', Comment.Hashbang), + (r'\#.*?$', Comment.Single), + (r'^=[a-zA-Z0-9]+\s+.*?\n=cut', Comment.Multiline), + (words(( + 'case', 'continue', 'do', 'else', 'elsif', 'for', 'foreach', + 'if', 'last', 'my', 'next', 'our', 'redo', 'reset', 'then', + 'unless', 'until', 'while', 'print', 'new', 'BEGIN', + 'CHECK', 'INIT', 'END', 'return'), suffix=r'\b'), + Keyword), + (r'(format)(\s+)(\w+)(\s*)(=)(\s*\n)', + bygroups(Keyword, Text, Name, Text, Punctuation, Text), 'format'), + (r'(eq|lt|gt|le|ge|ne|not|and|or|cmp)\b', Operator.Word), + # common delimiters + (r's/(\\\\|\\[^\\]|[^\\/])*/(\\\\|\\[^\\]|[^\\/])*/[egimosx]*', + String.Regex), + (r's!(\\\\|\\!|[^!])*!(\\\\|\\!|[^!])*![egimosx]*', String.Regex), + (r's\\(\\\\|[^\\])*\\(\\\\|[^\\])*\\[egimosx]*', String.Regex), + (r's@(\\\\|\\[^\\]|[^\\@])*@(\\\\|\\[^\\]|[^\\@])*@[egimosx]*', + String.Regex), + (r's%(\\\\|\\[^\\]|[^\\%])*%(\\\\|\\[^\\]|[^\\%])*%[egimosx]*', + String.Regex), + # balanced delimiters + (r's\{(\\\\|\\[^\\]|[^\\}])*\}\s*', String.Regex, 'balanced-regex'), + (r's<(\\\\|\\[^\\]|[^\\>])*>\s*', String.Regex, 'balanced-regex'), + (r's\[(\\\\|\\[^\\]|[^\\\]])*\]\s*', String.Regex, + 'balanced-regex'), + (r's\((\\\\|\\[^\\]|[^\\)])*\)\s*', String.Regex, + 'balanced-regex'), + + (r'm?/(\\\\|\\[^\\]|[^\\/\n])*/[gcimosx]*', String.Regex), + (r'm(?=[/!\\{<\[(@%$])', String.Regex, 'balanced-regex'), + (r'((?<==~)|(?<=\())\s*/(\\\\|\\[^\\]|[^\\/])*/[gcimosx]*', + String.Regex), + (r'\s+', Text), + (words(( + 'abs', 'accept', 'alarm', 'atan2', 'bind', 'binmode', 'bless', 'caller', 'chdir', + 'chmod', 'chomp', 'chop', 'chown', 'chr', 'chroot', 'close', 'closedir', 'connect', + 'continue', 'cos', 'crypt', 'dbmclose', 'dbmopen', 'defined', 'delete', 'die', + 'dump', 'each', 'endgrent', 'endhostent', 'endnetent', 'endprotoent', + 'endpwent', 'endservent', 'eof', 'eval', 'exec', 'exists', 'exit', 'exp', 'fcntl', + 'fileno', 'flock', 'fork', 'format', 'formline', 'getc', 'getgrent', 'getgrgid', + 'getgrnam', 'gethostbyaddr', 'gethostbyname', 'gethostent', 'getlogin', + 'getnetbyaddr', 'getnetbyname', 'getnetent', 'getpeername', 'getpgrp', + 'getppid', 'getpriority', 'getprotobyname', 'getprotobynumber', + 'getprotoent', 'getpwent', 'getpwnam', 'getpwuid', 'getservbyname', + 'getservbyport', 'getservent', 'getsockname', 'getsockopt', 'glob', 'gmtime', + 'goto', 'grep', 'hex', 'import', 'index', 'int', 'ioctl', 'join', 'keys', 'kill', 'last', + 'lc', 'lcfirst', 'length', 'link', 'listen', 'local', 'localtime', 'log', 'lstat', + 'map', 'mkdir', 'msgctl', 'msgget', 'msgrcv', 'msgsnd', 'my', 'next', 'oct', 'open', + 'opendir', 'ord', 'our', 'pack', 'pipe', 'pop', 'pos', 'printf', + 'prototype', 'push', 'quotemeta', 'rand', 'read', 'readdir', + 'readline', 'readlink', 'readpipe', 'recv', 'redo', 'ref', 'rename', + 'reverse', 'rewinddir', 'rindex', 'rmdir', 'scalar', 'seek', 'seekdir', + 'select', 'semctl', 'semget', 'semop', 'send', 'setgrent', 'sethostent', 'setnetent', + 'setpgrp', 'setpriority', 'setprotoent', 'setpwent', 'setservent', + 'setsockopt', 'shift', 'shmctl', 'shmget', 'shmread', 'shmwrite', 'shutdown', + 'sin', 'sleep', 'socket', 'socketpair', 'sort', 'splice', 'split', 'sprintf', 'sqrt', + 'srand', 'stat', 'study', 'substr', 'symlink', 'syscall', 'sysopen', 'sysread', + 'sysseek', 'system', 'syswrite', 'tell', 'telldir', 'tie', 'tied', 'time', 'times', 'tr', + 'truncate', 'uc', 'ucfirst', 'umask', 'undef', 'unlink', 'unpack', 'unshift', 'untie', + 'utime', 'values', 'vec', 'wait', 'waitpid', 'wantarray', 'warn', 'write'), suffix=r'\b'), + Name.Builtin), + (r'((__(DATA|DIE|WARN)__)|(STD(IN|OUT|ERR)))\b', Name.Builtin.Pseudo), + (r'(<<)([\'"]?)([a-zA-Z_]\w*)(\2;?\n.*?\n)(\3)(\n)', + bygroups(String, String, String.Delimiter, String, String.Delimiter, Text)), + (r'__END__', Comment.Preproc, 'end-part'), + (r'\$\^[ADEFHILMOPSTWX]', Name.Variable.Global), + (r"\$[\\\"\[\]'&`+*.,;=%~?@$!<>(^|/-](?!\w)", Name.Variable.Global), + (r'[$@%#]+', Name.Variable, 'varname'), + (r'0_?[0-7]+(_[0-7]+)*', Number.Oct), + (r'0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*', Number.Hex), + (r'0b[01]+(_[01]+)*', Number.Bin), + (r'(?i)(\d*(_\d*)*\.\d+(_\d*)*|\d+(_\d*)*\.\d+(_\d*)*)(e[+-]?\d+)?', + Number.Float), + (r'(?i)\d+(_\d*)*e[+-]?\d+(_\d*)*', Number.Float), + (r'\d+(_\d+)*', Number.Integer), + (r"'(\\\\|\\[^\\]|[^'\\])*'", String), + (r'"(\\\\|\\[^\\]|[^"\\])*"', String), + (r'`(\\\\|\\[^\\]|[^`\\])*`', String.Backtick), + (r'<([^\s>]+)>', String.Regex), + (r'(q|qq|qw|qr|qx)\{', String.Other, 'cb-string'), + (r'(q|qq|qw|qr|qx)\(', String.Other, 'rb-string'), + (r'(q|qq|qw|qr|qx)\[', String.Other, 'sb-string'), + (r'(q|qq|qw|qr|qx)\<', String.Other, 'lt-string'), + (r'(q|qq|qw|qr|qx)([\W_])(.|\n)*?\2', String.Other), + (r'(package)(\s+)([a-zA-Z_]\w*(?:::[a-zA-Z_]\w*)*)', + bygroups(Keyword, Text, Name.Namespace)), + (r'(use|require|no)(\s+)([a-zA-Z_]\w*(?:::[a-zA-Z_]\w*)*)', + bygroups(Keyword, Text, Name.Namespace)), + (r'(sub)(\s+)', bygroups(Keyword, Text), 'funcname'), + (words(( + 'no', 'package', 'require', 'use'), suffix=r'\b'), + Keyword), + (r'(\[\]|\*\*|::|<<|>>|>=|<=>|<=|={3}|!=|=~|' + r'!~|&&?|\|\||\.{1,3})', Operator), + (r'[-+/*%=<>&^|!\\~]=?', Operator), + (r'[()\[\]:;,<>/?{}]', Punctuation), # yes, there's no shortage + # of punctuation in Perl! + (r'(?=\w)', Name, 'name'), + ], + 'format': [ + (r'\.\n', String.Interpol, '#pop'), + (r'[^\n]*\n', String.Interpol), + ], + 'varname': [ + (r'\s+', Text), + (r'\{', Punctuation, '#pop'), # hash syntax? + (r'\)|,', Punctuation, '#pop'), # argument specifier + (r'\w+::', Name.Namespace), + (r'[\w:]+', Name.Variable, '#pop'), + ], + 'name': [ + (r'[a-zA-Z_]\w*(::[a-zA-Z_]\w*)*(::)?(?=\s*->)', Name.Namespace, '#pop'), + (r'[a-zA-Z_]\w*(::[a-zA-Z_]\w*)*::', Name.Namespace, '#pop'), + (r'[\w:]+', Name, '#pop'), + (r'[A-Z_]+(?=\W)', Name.Constant, '#pop'), + (r'(?=\W)', Text, '#pop'), + ], + 'funcname': [ + (r'[a-zA-Z_]\w*[!?]?', Name.Function), + (r'\s+', Text), + # argument declaration + (r'(\([$@%]*\))(\s*)', bygroups(Punctuation, Text)), + (r';', Punctuation, '#pop'), + (r'.*?\{', Punctuation, '#pop'), + ], + 'cb-string': [ + (r'\\[{}\\]', String.Other), + (r'\\', String.Other), + (r'\{', String.Other, 'cb-string'), + (r'\}', String.Other, '#pop'), + (r'[^{}\\]+', String.Other) + ], + 'rb-string': [ + (r'\\[()\\]', String.Other), + (r'\\', String.Other), + (r'\(', String.Other, 'rb-string'), + (r'\)', String.Other, '#pop'), + (r'[^()]+', String.Other) + ], + 'sb-string': [ + (r'\\[\[\]\\]', String.Other), + (r'\\', String.Other), + (r'\[', String.Other, 'sb-string'), + (r'\]', String.Other, '#pop'), + (r'[^\[\]]+', String.Other) + ], + 'lt-string': [ + (r'\\[<>\\]', String.Other), + (r'\\', String.Other), + (r'\<', String.Other, 'lt-string'), + (r'\>', String.Other, '#pop'), + (r'[^<>]+', String.Other) + ], + 'end-part': [ + (r'.+', Comment.Preproc, '#pop') + ] + } + + def analyse_text(text): + if shebang_matches(text, r'perl'): + return True + + result = 0 + + if re.search(r'(?:my|our)\s+[$@%(]', text): + result += 0.9 + + if ':=' in text: + # := is not valid Perl, but it appears in unicon, so we should + # become less confident if we think we found Perl with := + result /= 2 + + return result + + +class Perl6Lexer(ExtendedRegexLexer): + """ + For `Raku `_ (a.k.a. Perl 6) source code. + + .. versionadded:: 2.0 + """ + + name = 'Perl6' + aliases = ['perl6', 'pl6', 'raku'] + filenames = ['*.pl', '*.pm', '*.nqp', '*.p6', '*.6pl', '*.p6l', '*.pl6', + '*.6pm', '*.p6m', '*.pm6', '*.t', '*.raku', '*.rakumod', + '*.rakutest', '*.rakudoc'] + mimetypes = ['text/x-perl6', 'application/x-perl6'] + flags = re.MULTILINE | re.DOTALL | re.UNICODE + + PERL6_IDENTIFIER_RANGE = r"['\w:-]" + + PERL6_KEYWORDS = ( + #Phasers + 'BEGIN','CATCH','CHECK','CLOSE','CONTROL','DOC','END','ENTER','FIRST', + 'INIT','KEEP','LAST','LEAVE','NEXT','POST','PRE','QUIT','UNDO', + #Keywords + 'anon','augment','but','class','constant','default','does','else', + 'elsif','enum','for','gather','given','grammar','has','if','import', + 'is','let','loop','made','make','method','module','multi','my','need', + 'orwith','our','proceed','proto','repeat','require','return', + 'return-rw','returns','role','rule','state','sub','submethod','subset', + 'succeed','supersede','token','try','unit','unless','until','use', + 'when','while','with','without', + #Traits + 'export','native','repr','required','rw','symbol', + ) + + PERL6_BUILTINS = ( + 'ACCEPTS','abs','abs2rel','absolute','accept','accessed','acos', + 'acosec','acosech','acosh','acotan','acotanh','acquire','act','action', + 'actions','add','add_attribute','add_enum_value','add_fallback', + 'add_method','add_parent','add_private_method','add_role','add_trustee', + 'adverb','after','all','allocate','allof','allowed','alternative-names', + 'annotations','antipair','antipairs','any','anyof','app_lifetime', + 'append','arch','archname','args','arity','Array','asec','asech','asin', + 'asinh','ASSIGN-KEY','ASSIGN-POS','assuming','ast','at','atan','atan2', + 'atanh','AT-KEY','atomic-assign','atomic-dec-fetch','atomic-fetch', + 'atomic-fetch-add','atomic-fetch-dec','atomic-fetch-inc', + 'atomic-fetch-sub','atomic-inc-fetch','AT-POS','attributes','auth', + 'await','backtrace','Bag','BagHash','bail-out','base','basename', + 'base-repeating','batch','BIND-KEY','BIND-POS','bind-stderr', + 'bind-stdin','bind-stdout','bind-udp','bits','bless','block','Bool', + 'bool-only','bounds','break','Bridge','broken','BUILD','build-date', + 'bytes','cache','callframe','calling-package','CALL-ME','callsame', + 'callwith','can','cancel','candidates','cando','can-ok','canonpath', + 'caps','caption','Capture','cas','catdir','categorize','categorize-list', + 'catfile','catpath','cause','ceiling','cglobal','changed','Channel', + 'chars','chdir','child','child-name','child-typename','chmod','chomp', + 'chop','chr','chrs','chunks','cis','classify','classify-list','cleanup', + 'clone','close','closed','close-stdin','cmp-ok','code','codes','collate', + 'column','comb','combinations','command','comment','compiler','Complex', + 'compose','compose_type','composer','condition','config', + 'configure_destroy','configure_type_checking','conj','connect', + 'constraints','construct','contains','contents','copy','cos','cosec', + 'cosech','cosh','cotan','cotanh','count','count-only','cpu-cores', + 'cpu-usage','CREATE','create_type','cross','cue','curdir','curupdir','d', + 'Date','DateTime','day','daycount','day-of-month','day-of-week', + 'day-of-year','days-in-month','declaration','decode','decoder','deepmap', + 'default','defined','DEFINITE','delayed','DELETE-KEY','DELETE-POS', + 'denominator','desc','DESTROY','destroyers','devnull','diag', + 'did-you-mean','die','dies-ok','dir','dirname','dir-sep','DISTROnames', + 'do','does','does-ok','done','done-testing','duckmap','dynamic','e', + 'eager','earlier','elems','emit','enclosing','encode','encoder', + 'encoding','end','ends-with','enum_from_value','enum_value_list', + 'enum_values','enums','eof','EVAL','eval-dies-ok','EVALFILE', + 'eval-lives-ok','exception','excludes-max','excludes-min','EXISTS-KEY', + 'EXISTS-POS','exit','exitcode','exp','expected','explicitly-manage', + 'expmod','extension','f','fail','fails-like','fc','feature','file', + 'filename','find_method','find_method_qualified','finish','first','flat', + 'flatmap','flip','floor','flunk','flush','fmt','format','formatter', + 'freeze','from','from-list','from-loop','from-posix','full', + 'full-barrier','get','get_value','getc','gist','got','grab','grabpairs', + 'grep','handle','handled','handles','hardware','has_accessor','Hash', + 'head','headers','hh-mm-ss','hidden','hides','hour','how','hyper','id', + 'illegal','im','in','indent','index','indices','indir','infinite', + 'infix','infix:<+>','infix:<->','install_method_cache','Instant', + 'instead','Int','int-bounds','interval','in-timezone','invalid-str', + 'invert','invocant','IO','IO::Notification.watch-path','is_trusted', + 'is_type','isa','is-absolute','isa-ok','is-approx','is-deeply', + 'is-hidden','is-initial-thread','is-int','is-lazy','is-leap-year', + 'isNaN','isnt','is-prime','is-relative','is-routine','is-setting', + 'is-win','item','iterator','join','keep','kept','KERNELnames','key', + 'keyof','keys','kill','kv','kxxv','l','lang','last','lastcall','later', + 'lazy','lc','leading','level','like','line','lines','link','List', + 'listen','live','lives-ok','local','lock','log','log10','lookup','lsb', + 'made','MAIN','make','Map','match','max','maxpairs','merge','message', + 'method','method_table','methods','migrate','min','minmax','minpairs', + 'minute','misplaced','Mix','MixHash','mkdir','mode','modified','month', + 'move','mro','msb','multi','multiness','my','name','named','named_names', + 'narrow','nativecast','native-descriptor','nativesizeof','new','new_type', + 'new-from-daycount','new-from-pairs','next','nextcallee','next-handle', + 'nextsame','nextwith','NFC','NFD','NFKC','NFKD','nl-in','nl-out', + 'nodemap','nok','none','norm','not','note','now','nude','Num', + 'numerator','Numeric','of','offset','offset-in-hours','offset-in-minutes', + 'ok','old','on-close','one','on-switch','open','opened','operation', + 'optional','ord','ords','orig','os-error','osname','out-buffer','pack', + 'package','package-kind','package-name','packages','pair','pairs', + 'pairup','parameter','params','parent','parent-name','parents','parse', + 'parse-base','parsefile','parse-names','parts','pass','path','path-sep', + 'payload','peer-host','peer-port','periods','perl','permutations','phaser', + 'pick','pickpairs','pid','placeholder','plan','plus','polar','poll', + 'polymod','pop','pos','positional','posix','postfix','postmatch', + 'precomp-ext','precomp-target','pred','prefix','prematch','prepend', + 'print','printf','print-nl','print-to','private','private_method_table', + 'proc','produce','Promise','prompt','protect','pull-one','push', + 'push-all','push-at-least','push-exactly','push-until-lazy','put', + 'qualifier-type','quit','r','race','radix','rand','range','Rat','raw', + 're','read','readchars','readonly','ready','Real','reallocate','reals', + 'reason','rebless','receive','recv','redispatcher','redo','reduce', + 'rel2abs','relative','release','rename','repeated','replacement', + 'report','reserved','resolve','restore','result','resume','rethrow', + 'reverse','right','rindex','rmdir','role','roles_to_compose','rolish', + 'roll','rootdir','roots','rotate','rotor','round','roundrobin', + 'routine-type','run','rwx','s','samecase','samemark','samewith','say', + 'schedule-on','scheduler','scope','sec','sech','second','seek','self', + 'send','Set','set_hidden','set_name','set_package','set_rw','set_value', + 'SetHash','set-instruments','setup_finalization','shape','share','shell', + 'shift','sibling','sigil','sign','signal','signals','signature','sin', + 'sinh','sink','sink-all','skip','skip-at-least','skip-at-least-pull-one', + 'skip-one','skip-rest','sleep','sleep-timer','sleep-until','Slip','slurp', + 'slurp-rest','slurpy','snap','snapper','so','socket-host','socket-port', + 'sort','source','source-package','spawn','SPEC','splice','split', + 'splitdir','splitpath','sprintf','spurt','sqrt','squish','srand','stable', + 'start','started','starts-with','status','stderr','stdout','Str', + 'sub_signature','subbuf','subbuf-rw','subname','subparse','subst', + 'subst-mutate','substr','substr-eq','substr-rw','subtest','succ','sum', + 'Supply','symlink','t','tail','take','take-rw','tan','tanh','tap', + 'target','target-name','tc','tclc','tell','then','throttle','throw', + 'throws-like','timezone','tmpdir','to','today','todo','toggle','to-posix', + 'total','trailing','trans','tree','trim','trim-leading','trim-trailing', + 'truncate','truncated-to','trusts','try_acquire','trying','twigil','type', + 'type_captures','typename','uc','udp','uncaught_handler','unimatch', + 'uniname','uninames','uniparse','uniprop','uniprops','unique','unival', + 'univals','unlike','unlink','unlock','unpack','unpolar','unshift', + 'unwrap','updir','USAGE','use-ok','utc','val','value','values','VAR', + 'variable','verbose-config','version','VMnames','volume','vow','w','wait', + 'warn','watch','watch-path','week','weekday-of-month','week-number', + 'week-year','WHAT','when','WHERE','WHEREFORE','WHICH','WHO', + 'whole-second','WHY','wordcase','words','workaround','wrap','write', + 'write-to','x','yada','year','yield','yyyy-mm-dd','z','zip','zip-latest', + + ) + + PERL6_BUILTIN_CLASSES = ( + #Booleans + 'False','True', + #Classes + 'Any','Array','Associative','AST','atomicint','Attribute','Backtrace', + 'Backtrace::Frame','Bag','Baggy','BagHash','Blob','Block','Bool','Buf', + 'Callable','CallFrame','Cancellation','Capture','CArray','Channel','Code', + 'compiler','Complex','ComplexStr','Cool','CurrentThreadScheduler', + 'Cursor','Date','Dateish','DateTime','Distro','Duration','Encoding', + 'Exception','Failure','FatRat','Grammar','Hash','HyperWhatever','Instant', + 'Int','int16','int32','int64','int8','IntStr','IO','IO::ArgFiles', + 'IO::CatHandle','IO::Handle','IO::Notification','IO::Path', + 'IO::Path::Cygwin','IO::Path::QNX','IO::Path::Unix','IO::Path::Win32', + 'IO::Pipe','IO::Socket','IO::Socket::Async','IO::Socket::INET','IO::Spec', + 'IO::Spec::Cygwin','IO::Spec::QNX','IO::Spec::Unix','IO::Spec::Win32', + 'IO::Special','Iterable','Iterator','Junction','Kernel','Label','List', + 'Lock','Lock::Async','long','longlong','Macro','Map','Match', + 'Metamodel::AttributeContainer','Metamodel::C3MRO','Metamodel::ClassHOW', + 'Metamodel::EnumHOW','Metamodel::Finalization','Metamodel::MethodContainer', + 'Metamodel::MROBasedMethodDispatch','Metamodel::MultipleInheritance', + 'Metamodel::Naming','Metamodel::Primitives','Metamodel::PrivateMethodContainer', + 'Metamodel::RoleContainer','Metamodel::Trusting','Method','Mix','MixHash', + 'Mixy','Mu','NFC','NFD','NFKC','NFKD','Nil','Num','num32','num64', + 'Numeric','NumStr','ObjAt','Order','Pair','Parameter','Perl','Pod::Block', + 'Pod::Block::Code','Pod::Block::Comment','Pod::Block::Declarator', + 'Pod::Block::Named','Pod::Block::Para','Pod::Block::Table','Pod::Heading', + 'Pod::Item','Pointer','Positional','PositionalBindFailover','Proc', + 'Proc::Async','Promise','Proxy','PseudoStash','QuantHash','Range','Rat', + 'Rational','RatStr','Real','Regex','Routine','Scalar','Scheduler', + 'Semaphore','Seq','Set','SetHash','Setty','Signature','size_t','Slip', + 'Stash','Str','StrDistance','Stringy','Sub','Submethod','Supplier', + 'Supplier::Preserving','Supply','Systemic','Tap','Telemetry', + 'Telemetry::Instrument::Thread','Telemetry::Instrument::Usage', + 'Telemetry::Period','Telemetry::Sampler','Thread','ThreadPoolScheduler', + 'UInt','uint16','uint32','uint64','uint8','Uni','utf8','Variable', + 'Version','VM','Whatever','WhateverCode','WrapHandle' + ) + + PERL6_OPERATORS = ( + 'X', 'Z', 'after', 'also', 'and', 'andthen', 'before', 'cmp', 'div', + 'eq', 'eqv', 'extra', 'ff', 'fff', 'ge', 'gt', 'le', 'leg', 'lt', 'm', + 'mm', 'mod', 'ne', 'or', 'orelse', 'rx', 's', 'tr', 'x', 'xor', 'xx', + '++', '--', '**', '!', '+', '-', '~', '?', '|', '||', '+^', '~^', '?^', + '^', '*', '/', '%', '%%', '+&', '+<', '+>', '~&', '~<', '~>', '?&', + 'gcd', 'lcm', '+', '-', '+|', '+^', '~|', '~^', '?|', '?^', + '~', '&', '^', 'but', 'does', '<=>', '..', '..^', '^..', '^..^', + '!=', '==', '<', '<=', '>', '>=', '~~', '===', '!eqv', + '&&', '||', '^^', '//', 'min', 'max', '??', '!!', 'ff', 'fff', 'so', + 'not', '<==', '==>', '<<==', '==>>','unicmp', + ) + + # Perl 6 has a *lot* of possible bracketing characters + # this list was lifted from STD.pm6 (https://github.com/perl6/std) + PERL6_BRACKETS = { + '\u0028': '\u0029', '\u003c': '\u003e', '\u005b': '\u005d', + '\u007b': '\u007d', '\u00ab': '\u00bb', '\u0f3a': '\u0f3b', + '\u0f3c': '\u0f3d', '\u169b': '\u169c', '\u2018': '\u2019', + '\u201a': '\u2019', '\u201b': '\u2019', '\u201c': '\u201d', + '\u201e': '\u201d', '\u201f': '\u201d', '\u2039': '\u203a', + '\u2045': '\u2046', '\u207d': '\u207e', '\u208d': '\u208e', + '\u2208': '\u220b', '\u2209': '\u220c', '\u220a': '\u220d', + '\u2215': '\u29f5', '\u223c': '\u223d', '\u2243': '\u22cd', + '\u2252': '\u2253', '\u2254': '\u2255', '\u2264': '\u2265', + '\u2266': '\u2267', '\u2268': '\u2269', '\u226a': '\u226b', + '\u226e': '\u226f', '\u2270': '\u2271', '\u2272': '\u2273', + '\u2274': '\u2275', '\u2276': '\u2277', '\u2278': '\u2279', + '\u227a': '\u227b', '\u227c': '\u227d', '\u227e': '\u227f', + '\u2280': '\u2281', '\u2282': '\u2283', '\u2284': '\u2285', + '\u2286': '\u2287', '\u2288': '\u2289', '\u228a': '\u228b', + '\u228f': '\u2290', '\u2291': '\u2292', '\u2298': '\u29b8', + '\u22a2': '\u22a3', '\u22a6': '\u2ade', '\u22a8': '\u2ae4', + '\u22a9': '\u2ae3', '\u22ab': '\u2ae5', '\u22b0': '\u22b1', + '\u22b2': '\u22b3', '\u22b4': '\u22b5', '\u22b6': '\u22b7', + '\u22c9': '\u22ca', '\u22cb': '\u22cc', '\u22d0': '\u22d1', + '\u22d6': '\u22d7', '\u22d8': '\u22d9', '\u22da': '\u22db', + '\u22dc': '\u22dd', '\u22de': '\u22df', '\u22e0': '\u22e1', + '\u22e2': '\u22e3', '\u22e4': '\u22e5', '\u22e6': '\u22e7', + '\u22e8': '\u22e9', '\u22ea': '\u22eb', '\u22ec': '\u22ed', + '\u22f0': '\u22f1', '\u22f2': '\u22fa', '\u22f3': '\u22fb', + '\u22f4': '\u22fc', '\u22f6': '\u22fd', '\u22f7': '\u22fe', + '\u2308': '\u2309', '\u230a': '\u230b', '\u2329': '\u232a', + '\u23b4': '\u23b5', '\u2768': '\u2769', '\u276a': '\u276b', + '\u276c': '\u276d', '\u276e': '\u276f', '\u2770': '\u2771', + '\u2772': '\u2773', '\u2774': '\u2775', '\u27c3': '\u27c4', + '\u27c5': '\u27c6', '\u27d5': '\u27d6', '\u27dd': '\u27de', + '\u27e2': '\u27e3', '\u27e4': '\u27e5', '\u27e6': '\u27e7', + '\u27e8': '\u27e9', '\u27ea': '\u27eb', '\u2983': '\u2984', + '\u2985': '\u2986', '\u2987': '\u2988', '\u2989': '\u298a', + '\u298b': '\u298c', '\u298d': '\u298e', '\u298f': '\u2990', + '\u2991': '\u2992', '\u2993': '\u2994', '\u2995': '\u2996', + '\u2997': '\u2998', '\u29c0': '\u29c1', '\u29c4': '\u29c5', + '\u29cf': '\u29d0', '\u29d1': '\u29d2', '\u29d4': '\u29d5', + '\u29d8': '\u29d9', '\u29da': '\u29db', '\u29f8': '\u29f9', + '\u29fc': '\u29fd', '\u2a2b': '\u2a2c', '\u2a2d': '\u2a2e', + '\u2a34': '\u2a35', '\u2a3c': '\u2a3d', '\u2a64': '\u2a65', + '\u2a79': '\u2a7a', '\u2a7d': '\u2a7e', '\u2a7f': '\u2a80', + '\u2a81': '\u2a82', '\u2a83': '\u2a84', '\u2a8b': '\u2a8c', + '\u2a91': '\u2a92', '\u2a93': '\u2a94', '\u2a95': '\u2a96', + '\u2a97': '\u2a98', '\u2a99': '\u2a9a', '\u2a9b': '\u2a9c', + '\u2aa1': '\u2aa2', '\u2aa6': '\u2aa7', '\u2aa8': '\u2aa9', + '\u2aaa': '\u2aab', '\u2aac': '\u2aad', '\u2aaf': '\u2ab0', + '\u2ab3': '\u2ab4', '\u2abb': '\u2abc', '\u2abd': '\u2abe', + '\u2abf': '\u2ac0', '\u2ac1': '\u2ac2', '\u2ac3': '\u2ac4', + '\u2ac5': '\u2ac6', '\u2acd': '\u2ace', '\u2acf': '\u2ad0', + '\u2ad1': '\u2ad2', '\u2ad3': '\u2ad4', '\u2ad5': '\u2ad6', + '\u2aec': '\u2aed', '\u2af7': '\u2af8', '\u2af9': '\u2afa', + '\u2e02': '\u2e03', '\u2e04': '\u2e05', '\u2e09': '\u2e0a', + '\u2e0c': '\u2e0d', '\u2e1c': '\u2e1d', '\u2e20': '\u2e21', + '\u3008': '\u3009', '\u300a': '\u300b', '\u300c': '\u300d', + '\u300e': '\u300f', '\u3010': '\u3011', '\u3014': '\u3015', + '\u3016': '\u3017', '\u3018': '\u3019', '\u301a': '\u301b', + '\u301d': '\u301e', '\ufd3e': '\ufd3f', '\ufe17': '\ufe18', + '\ufe35': '\ufe36', '\ufe37': '\ufe38', '\ufe39': '\ufe3a', + '\ufe3b': '\ufe3c', '\ufe3d': '\ufe3e', '\ufe3f': '\ufe40', + '\ufe41': '\ufe42', '\ufe43': '\ufe44', '\ufe47': '\ufe48', + '\ufe59': '\ufe5a', '\ufe5b': '\ufe5c', '\ufe5d': '\ufe5e', + '\uff08': '\uff09', '\uff1c': '\uff1e', '\uff3b': '\uff3d', + '\uff5b': '\uff5d', '\uff5f': '\uff60', '\uff62': '\uff63', + } + + def _build_word_match(words, boundary_regex_fragment=None, prefix='', suffix=''): + if boundary_regex_fragment is None: + return r'\b(' + prefix + r'|'.join(re.escape(x) for x in words) + \ + suffix + r')\b' + else: + return r'(? 0: + next_open_pos = text.find(opening_chars, search_pos + n_chars) + next_close_pos = text.find(closing_chars, search_pos + n_chars) + + if next_close_pos == -1: + next_close_pos = len(text) + nesting_level = 0 + elif next_open_pos != -1 and next_open_pos < next_close_pos: + nesting_level += 1 + search_pos = next_open_pos + else: # next_close_pos < next_open_pos + nesting_level -= 1 + search_pos = next_close_pos + + end_pos = next_close_pos + + if end_pos < 0: # if we didn't find a closer, just highlight the + # rest of the text in this class + end_pos = len(text) + + if adverbs is not None and re.search(r':to\b', adverbs): + heredoc_terminator = text[match.start('delimiter') + n_chars:end_pos] + end_heredoc = re.search(r'^\s*' + re.escape(heredoc_terminator) + + r'\s*$', text[end_pos:], re.MULTILINE) + + if end_heredoc: + end_pos += end_heredoc.end() + else: + end_pos = len(text) + + yield match.start(), token_class, text[match.start():end_pos + n_chars] + context.pos = end_pos + n_chars + + return callback + + def opening_brace_callback(lexer, match, context): + stack = context.stack + + yield match.start(), Text, context.text[match.start():match.end()] + context.pos = match.end() + + # if we encounter an opening brace and we're one level + # below a token state, it means we need to increment + # the nesting level for braces so we know later when + # we should return to the token rules. + if len(stack) > 2 and stack[-2] == 'token': + context.perl6_token_nesting_level += 1 + + def closing_brace_callback(lexer, match, context): + stack = context.stack + + yield match.start(), Text, context.text[match.start():match.end()] + context.pos = match.end() + + # if we encounter a free closing brace and we're one level + # below a token state, it means we need to check the nesting + # level to see if we need to return to the token state. + if len(stack) > 2 and stack[-2] == 'token': + context.perl6_token_nesting_level -= 1 + if context.perl6_token_nesting_level == 0: + stack.pop() + + def embedded_perl6_callback(lexer, match, context): + context.perl6_token_nesting_level = 1 + yield match.start(), Text, context.text[match.start():match.end()] + context.pos = match.end() + context.stack.append('root') + + # If you're modifying these rules, be careful if you need to process '{' or '}' + # characters. We have special logic for processing these characters (due to the fact + # that you can nest Perl 6 code in regex blocks), so if you need to process one of + # them, make sure you also process the corresponding one! + tokens = { + 'common': [ + (r'#[`|=](?P(?P[' + ''.join(PERL6_BRACKETS) + r'])(?P=first_char)*)', + brackets_callback(Comment.Multiline)), + (r'#[^\n]*$', Comment.Single), + (r'^(\s*)=begin\s+(\w+)\b.*?^\1=end\s+\2', Comment.Multiline), + (r'^(\s*)=for.*?\n\s*?\n', Comment.Multiline), + (r'^=.*?\n\s*?\n', Comment.Multiline), + (r'(regex|token|rule)(\s*' + PERL6_IDENTIFIER_RANGE + '+:sym)', + bygroups(Keyword, Name), 'token-sym-brackets'), + (r'(regex|token|rule)(?!' + PERL6_IDENTIFIER_RANGE + r')(\s*' + PERL6_IDENTIFIER_RANGE + '+)?', + bygroups(Keyword, Name), 'pre-token'), + # deal with a special case in the Perl 6 grammar (role q { ... }) + (r'(role)(\s+)(q)(\s*)', bygroups(Keyword, Text, Name, Text)), + (_build_word_match(PERL6_KEYWORDS, PERL6_IDENTIFIER_RANGE), Keyword), + (_build_word_match(PERL6_BUILTIN_CLASSES, PERL6_IDENTIFIER_RANGE, suffix='(?::[UD])?'), + Name.Builtin), + (_build_word_match(PERL6_BUILTINS, PERL6_IDENTIFIER_RANGE), Name.Builtin), + # copied from PerlLexer + (r'[$@%&][.^:?=!~]?' + PERL6_IDENTIFIER_RANGE + '+(?:<<.*?>>|<.*?>|«.*?»)*', + Name.Variable), + (r'\$[!/](?:<<.*?>>|<.*?>|«.*?»)*', Name.Variable.Global), + (r'::\?\w+', Name.Variable.Global), + (r'[$@%&]\*' + PERL6_IDENTIFIER_RANGE + '+(?:<<.*?>>|<.*?>|«.*?»)*', + Name.Variable.Global), + (r'\$(?:<.*?>)+', Name.Variable), + (r'(?:q|qq|Q)[a-zA-Z]?\s*(?P:[\w\s:]+)?\s*(?P(?P[^0-9a-zA-Z:\s])' + r'(?P=first_char)*)', brackets_callback(String)), + # copied from PerlLexer + (r'0_?[0-7]+(_[0-7]+)*', Number.Oct), + (r'0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*', Number.Hex), + (r'0b[01]+(_[01]+)*', Number.Bin), + (r'(?i)(\d*(_\d*)*\.\d+(_\d*)*|\d+(_\d*)*\.\d+(_\d*)*)(e[+-]?\d+)?', + Number.Float), + (r'(?i)\d+(_\d*)*e[+-]?\d+(_\d*)*', Number.Float), + (r'\d+(_\d+)*', Number.Integer), + (r'(?<=~~)\s*/(?:\\\\|\\/|.)*?/', String.Regex), + (r'(?<=[=(,])\s*/(?:\\\\|\\/|.)*?/', String.Regex), + (r'm\w+(?=\()', Name), + (r'(?:m|ms|rx)\s*(?P:[\w\s:]+)?\s*(?P(?P[^\w:\s])' + r'(?P=first_char)*)', brackets_callback(String.Regex)), + (r'(?:s|ss|tr)\s*(?::[\w\s:]+)?\s*/(?:\\\\|\\/|.)*?/(?:\\\\|\\/|.)*?/', + String.Regex), + (r'<[^\s=].*?\S>', String), + (_build_word_match(PERL6_OPERATORS), Operator), + (r'\w' + PERL6_IDENTIFIER_RANGE + '*', Name), + (r"'(\\\\|\\[^\\]|[^'\\])*'", String), + (r'"(\\\\|\\[^\\]|[^"\\])*"', String), + ], + 'root': [ + include('common'), + (r'\{', opening_brace_callback), + (r'\}', closing_brace_callback), + (r'.+?', Text), + ], + 'pre-token': [ + include('common'), + (r'\{', Text, ('#pop', 'token')), + (r'.+?', Text), + ], + 'token-sym-brackets': [ + (r'(?P(?P[' + ''.join(PERL6_BRACKETS) + '])(?P=first_char)*)', + brackets_callback(Name), ('#pop', 'pre-token')), + default(('#pop', 'pre-token')), + ], + 'token': [ + (r'\}', Text, '#pop'), + (r'(?<=:)(?:my|our|state|constant|temp|let).*?;', using(this)), + # make sure that quotes in character classes aren't treated as strings + (r'<(?:[-!?+.]\s*)?\[.*?\]>', String.Regex), + # make sure that '#' characters in quotes aren't treated as comments + (r"(?my|our)\s+)?(?:module|class|role|enum|grammar)', line) + if class_decl: + if saw_perl_decl or class_decl.group('scope') is not None: + return True + rating = 0.05 + continue + break + + if ':=' in text: + # Same logic as above for PerlLexer + rating /= 2 + + return rating + + def __init__(self, **options): + super().__init__(**options) + self.encoding = options.get('encoding', 'utf-8') diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/php.py b/.venv/lib/python3.8/site-packages/pygments/lexers/php.py new file mode 100644 index 00000000..aab502e2 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/php.py @@ -0,0 +1,321 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.php + ~~~~~~~~~~~~~~~~~~~ + + Lexers for PHP and related languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import Lexer, RegexLexer, include, bygroups, default, \ + using, this, words, do_insertions +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Other, Generic +from pygments.util import get_bool_opt, get_list_opt, shebang_matches + +__all__ = ['ZephirLexer', 'PsyshConsoleLexer', 'PhpLexer'] + +line_re = re.compile('.*?\n') + + +class ZephirLexer(RegexLexer): + """ + For `Zephir language `_ source code. + + Zephir is a compiled high level language aimed + to the creation of C-extensions for PHP. + + .. versionadded:: 2.0 + """ + + name = 'Zephir' + aliases = ['zephir'] + filenames = ['*.zep'] + + zephir_keywords = ['fetch', 'echo', 'isset', 'empty'] + zephir_type = ['bit', 'bits', 'string'] + + flags = re.DOTALL | re.MULTILINE + + tokens = { + 'commentsandwhitespace': [ + (r'\s+', Text), + (r'//.*?\n', Comment.Single), + (r'/\*.*?\*/', Comment.Multiline) + ], + 'slashstartsregex': [ + include('commentsandwhitespace'), + (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/' + r'([gim]+\b|\B)', String.Regex, '#pop'), + (r'/', Operator, '#pop'), + default('#pop') + ], + 'badregex': [ + (r'\n', Text, '#pop') + ], + 'root': [ + (r'^(?=\s|/)', Text, 'slashstartsregex'), + include('commentsandwhitespace'), + (r'\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|' + r'(<<|>>>?|==?|!=?|->|[-<>+*%&|^/])=?', Operator, 'slashstartsregex'), + (r'[{(\[;,]', Punctuation, 'slashstartsregex'), + (r'[})\].]', Punctuation), + (r'(for|in|while|do|break|return|continue|switch|case|default|if|else|loop|' + r'require|inline|throw|try|catch|finally|new|delete|typeof|instanceof|void|' + r'namespace|use|extends|this|fetch|isset|unset|echo|fetch|likely|unlikely|' + r'empty)\b', Keyword, 'slashstartsregex'), + (r'(var|let|with|function)\b', Keyword.Declaration, 'slashstartsregex'), + (r'(abstract|boolean|bool|char|class|const|double|enum|export|extends|final|' + r'native|goto|implements|import|int|string|interface|long|ulong|char|uchar|' + r'float|unsigned|private|protected|public|short|static|self|throws|reverse|' + r'transient|volatile)\b', Keyword.Reserved), + (r'(true|false|null|undefined)\b', Keyword.Constant), + (r'(Array|Boolean|Date|_REQUEST|_COOKIE|_SESSION|' + r'_GET|_POST|_SERVER|this|stdClass|range|count|iterator|' + r'window)\b', Name.Builtin), + (r'[$a-zA-Z_][\w\\]*', Name.Other), + (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float), + (r'0x[0-9a-fA-F]+', Number.Hex), + (r'[0-9]+', Number.Integer), + (r'"(\\\\|\\"|[^"])*"', String.Double), + (r"'(\\\\|\\'|[^'])*'", String.Single), + ] + } + + +class PsyshConsoleLexer(Lexer): + """ + For `PsySH`_ console output, such as: + + .. sourcecode:: psysh + + >>> $greeting = function($name): string { + ... return "Hello, {$name}"; + ... }; + => Closure($name): string {#2371 …3} + >>> $greeting('World') + => "Hello, World" + + .. _PsySH: https://psysh.org/ + .. versionadded:: 2.7 + """ + name = 'PsySH console session for PHP' + aliases = ['psysh'] + + def __init__(self, **options): + options['startinline'] = True + Lexer.__init__(self, **options) + + def get_tokens_unprocessed(self, text): + phplexer = PhpLexer(**self.options) + curcode = '' + insertions = [] + for match in line_re.finditer(text): + line = match.group() + if line.startswith('>>> ') or line.startswith('... '): + insertions.append((len(curcode), + [(0, Generic.Prompt, line[:4])])) + curcode += line[4:] + elif line.rstrip() == '...': + insertions.append((len(curcode), + [(0, Generic.Prompt, '...')])) + curcode += line[3:] + else: + if curcode: + yield from do_insertions( + insertions, phplexer.get_tokens_unprocessed(curcode)) + curcode = '' + insertions = [] + yield match.start(), Generic.Output, line + if curcode: + yield from do_insertions(insertions, + phplexer.get_tokens_unprocessed(curcode)) + + +class PhpLexer(RegexLexer): + """ + For `PHP `_ source code. + For PHP embedded in HTML, use the `HtmlPhpLexer`. + + Additional options accepted: + + `startinline` + If given and ``True`` the lexer starts highlighting with + php code (i.e.: no starting ``>> from pygments.lexers._php_builtins import MODULES + >>> MODULES.keys() + ['PHP Options/Info', 'Zip', 'dba', ...] + + In fact the names of those modules match the module names from + the php documentation. + """ + + name = 'PHP' + aliases = ['php', 'php3', 'php4', 'php5'] + filenames = ['*.php', '*.php[345]', '*.inc'] + mimetypes = ['text/x-php'] + + # Note that a backslash is included in the following two patterns + # PHP uses a backslash as a namespace separator + _ident_char = r'[\\\w]|[^\x00-\x7f]' + _ident_begin = r'(?:[\\_a-z]|[^\x00-\x7f])' + _ident_end = r'(?:' + _ident_char + ')*' + _ident_inner = _ident_begin + _ident_end + + flags = re.IGNORECASE | re.DOTALL | re.MULTILINE + tokens = { + 'root': [ + (r'<\?(php)?', Comment.Preproc, 'php'), + (r'[^<]+', Other), + (r'<', Other) + ], + 'php': [ + (r'\?>', Comment.Preproc, '#pop'), + (r'(<<<)([\'"]?)(' + _ident_inner + r')(\2\n.*?\n\s*)(\3)(;?)(\n)', + bygroups(String, String, String.Delimiter, String, String.Delimiter, + Punctuation, Text)), + (r'\s+', Text), + (r'#.*?\n', Comment.Single), + (r'//.*?\n', Comment.Single), + # put the empty comment here, it is otherwise seen as + # the start of a docstring + (r'/\*\*/', Comment.Multiline), + (r'/\*\*.*?\*/', String.Doc), + (r'/\*.*?\*/', Comment.Multiline), + (r'(->|::)(\s*)(' + _ident_inner + ')', + bygroups(Operator, Text, Name.Attribute)), + (r'[~!%^&*+=|:.<>/@-]+', Operator), + (r'\?', Operator), # don't add to the charclass above! + (r'[\[\]{}();,]+', Punctuation), + (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'), + (r'(function)(\s*)(?=\()', bygroups(Keyword, Text)), + (r'(function)(\s+)(&?)(\s*)', + bygroups(Keyword, Text, Operator, Text), 'functionname'), + (r'(const)(\s+)(' + _ident_inner + ')', + bygroups(Keyword, Text, Name.Constant)), + (r'(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|' + r'eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|' + r'FALSE|print|for|require|continue|foreach|require_once|' + r'declare|return|default|static|do|switch|die|stdClass|' + r'echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|' + r'virtual|endfor|include_once|while|endforeach|global|' + r'endif|list|endswitch|new|endwhile|not|' + r'array|E_ALL|NULL|final|php_user_filter|interface|' + r'implements|public|private|protected|abstract|clone|try|' + r'catch|throw|this|use|namespace|trait|yield|' + r'finally)\b', Keyword), + (r'(true|false|null)\b', Keyword.Constant), + include('magicconstants'), + (r'\$\{\$+' + _ident_inner + r'\}', Name.Variable), + (r'\$+' + _ident_inner, Name.Variable), + (_ident_inner, Name.Other), + (r'(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?', Number.Float), + (r'\d+e[+-]?[0-9]+', Number.Float), + (r'0[0-7]+', Number.Oct), + (r'0x[a-f0-9]+', Number.Hex), + (r'\d+', Number.Integer), + (r'0b[01]+', Number.Bin), + (r"'([^'\\]*(?:\\.[^'\\]*)*)'", String.Single), + (r'`([^`\\]*(?:\\.[^`\\]*)*)`', String.Backtick), + (r'"', String.Double, 'string'), + ], + 'magicfuncs': [ + # source: http://php.net/manual/en/language.oop5.magic.php + (words(( + '__construct', '__destruct', '__call', '__callStatic', '__get', '__set', + '__isset', '__unset', '__sleep', '__wakeup', '__toString', '__invoke', + '__set_state', '__clone', '__debugInfo',), suffix=r'\b'), + Name.Function.Magic), + ], + 'magicconstants': [ + # source: http://php.net/manual/en/language.constants.predefined.php + (words(( + '__LINE__', '__FILE__', '__DIR__', '__FUNCTION__', '__CLASS__', + '__TRAIT__', '__METHOD__', '__NAMESPACE__',), + suffix=r'\b'), + Name.Constant), + ], + 'classname': [ + (_ident_inner, Name.Class, '#pop') + ], + 'functionname': [ + include('magicfuncs'), + (_ident_inner, Name.Function, '#pop'), + default('#pop') + ], + 'string': [ + (r'"', String.Double, '#pop'), + (r'[^{$"\\]+', String.Double), + (r'\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})', String.Escape), + (r'\$' + _ident_inner + r'(\[\S+?\]|->' + _ident_inner + ')?', + String.Interpol), + (r'(\{\$\{)(.*?)(\}\})', + bygroups(String.Interpol, using(this, _startinline=True), + String.Interpol)), + (r'(\{)(\$.*?)(\})', + bygroups(String.Interpol, using(this, _startinline=True), + String.Interpol)), + (r'(\$\{)(\S+)(\})', + bygroups(String.Interpol, Name.Variable, String.Interpol)), + (r'[${\\]', String.Double) + ], + } + + def __init__(self, **options): + self.funcnamehighlighting = get_bool_opt( + options, 'funcnamehighlighting', True) + self.disabledmodules = get_list_opt( + options, 'disabledmodules', ['unknown']) + self.startinline = get_bool_opt(options, 'startinline', False) + + # private option argument for the lexer itself + if '_startinline' in options: + self.startinline = options.pop('_startinline') + + # collect activated functions in a set + self._functions = set() + if self.funcnamehighlighting: + from pygments.lexers._php_builtins import MODULES + for key, value in MODULES.items(): + if key not in self.disabledmodules: + self._functions.update(value) + RegexLexer.__init__(self, **options) + + def get_tokens_unprocessed(self, text): + stack = ['root'] + if self.startinline: + stack.append('php') + for index, token, value in \ + RegexLexer.get_tokens_unprocessed(self, text, stack): + if token is Name.Other: + if value in self._functions: + yield index, Name.Builtin, value + continue + yield index, token, value + + def analyse_text(text): + if shebang_matches(text, r'php'): + return True + rv = 0.0 + if re.search(r'<\?(?!xml)', text): + rv += 0.3 + return rv diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/pointless.py b/.venv/lib/python3.8/site-packages/pygments/lexers/pointless.py new file mode 100644 index 00000000..9acc3ffb --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/pointless.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.pointless + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Pointless. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, words +from pygments.token import Comment, Error, Keyword, Name, Number, Operator, \ + Punctuation, String, Text + +__all__ = ['PointlessLexer'] + + +class PointlessLexer(RegexLexer): + """ + For `Pointless `_ source code. + + .. versionadded:: 2.7 + """ + + name = 'Pointless' + aliases = ['pointless'] + filenames = ['*.ptls'] + + ops = words([ + "+", "-", "*", "/", "**", "%", "+=", "-=", "*=", + "/=", "**=", "%=", "|>", "=", "==", "!=", "<", ">", + "<=", ">=", "=>", "$", "++", + ]) + + keywords = words([ + "if", "then", "else", "where", "with", "cond", + "case", "and", "or", "not", "in", "as", "for", + "requires", "throw", "try", "catch", "when", + "yield", "upval", + ], suffix=r'\b') + + tokens = { + 'root': [ + (r'[ \n\r]+', Text), + (r'--.*$', Comment.Single), + (r'"""', String, 'multiString'), + (r'"', String, 'string'), + (r'[\[\](){}:;,.]', Punctuation), + (ops, Operator), + (keywords, Keyword), + (r'\d+|\d*\.\d+', Number), + (r'(true|false)\b', Name.Builtin), + (r'[A-Z][a-zA-Z0-9]*\b', String.Symbol), + (r'output\b', Name.Variable.Magic), + (r'(export|import)\b', Keyword.Namespace), + (r'[a-z][a-zA-Z0-9]*\b', Name.Variable) + ], + 'multiString': [ + (r'\\.', String.Escape), + (r'"""', String, '#pop'), + (r'"', String), + (r'[^\\"]+', String), + ], + 'string': [ + (r'\\.', String.Escape), + (r'"', String, '#pop'), + (r'\n', Error), + (r'[^\\"]+', String), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/pony.py b/.venv/lib/python3.8/site-packages/pygments/lexers/pony.py new file mode 100644 index 00000000..d13338b2 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/pony.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.pony + ~~~~~~~~~~~~~~~~~~~~ + + Lexers for Pony and related languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, bygroups, words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation + +__all__ = ['PonyLexer'] + + +class PonyLexer(RegexLexer): + """ + For Pony source code. + + .. versionadded:: 2.4 + """ + + name = 'Pony' + aliases = ['pony'] + filenames = ['*.pony'] + + _caps = r'(iso|trn|ref|val|box|tag)' + + tokens = { + 'root': [ + (r'\n', Text), + (r'[^\S\n]+', Text), + (r'//.*\n', Comment.Single), + (r'/\*', Comment.Multiline, 'nested_comment'), + (r'"""(?:.|\n)*?"""', String.Doc), + (r'"', String, 'string'), + (r'\'.*\'', String.Char), + (r'=>|[]{}:().~;,|&!^?[]', Punctuation), + (words(( + 'addressof', 'and', 'as', 'consume', 'digestof', 'is', 'isnt', + 'not', 'or'), + suffix=r'\b'), + Operator.Word), + (r'!=|==|<<|>>|[-+/*%=<>]', Operator), + (words(( + 'box', 'break', 'compile_error', 'compile_intrinsic', + 'continue', 'do', 'else', 'elseif', 'embed', 'end', 'error', + 'for', 'if', 'ifdef', 'in', 'iso', 'lambda', 'let', 'match', + 'object', 'recover', 'ref', 'repeat', 'return', 'tag', 'then', + 'this', 'trn', 'try', 'until', 'use', 'var', 'val', 'where', + 'while', 'with', '#any', '#read', '#send', '#share'), + suffix=r'\b'), + Keyword), + (r'(actor|class|struct|primitive|interface|trait|type)((?:\s)+)', + bygroups(Keyword, Text), 'typename'), + (r'(new|fun|be)((?:\s)+)', bygroups(Keyword, Text), 'methodname'), + (words(( + 'I8', 'U8', 'I16', 'U16', 'I32', 'U32', 'I64', 'U64', 'I128', + 'U128', 'ILong', 'ULong', 'ISize', 'USize', 'F32', 'F64', + 'Bool', 'Pointer', 'None', 'Any', 'Array', 'String', + 'Iterator'), + suffix=r'\b'), + Name.Builtin.Type), + (r'_?[A-Z]\w*', Name.Type), + (r'(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+', Number.Float), + (r'0x[0-9a-fA-F]+', Number.Hex), + (r'\d+', Number.Integer), + (r'(true|false)\b', Name.Builtin), + (r'_\d*', Name), + (r'_?[a-z][\w\']*', Name) + ], + 'typename': [ + (_caps + r'?((?:\s)*)(_?[A-Z]\w*)', + bygroups(Keyword, Text, Name.Class), '#pop') + ], + 'methodname': [ + (_caps + r'?((?:\s)*)(_?[a-z]\w*)', + bygroups(Keyword, Text, Name.Function), '#pop') + ], + 'nested_comment': [ + (r'[^*/]+', Comment.Multiline), + (r'/\*', Comment.Multiline, '#push'), + (r'\*/', Comment.Multiline, '#pop'), + (r'[*/]', Comment.Multiline) + ], + 'string': [ + (r'"', String, '#pop'), + (r'\\"', String), + (r'[^\\"]+', String) + ] + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/praat.py b/.venv/lib/python3.8/site-packages/pygments/lexers/praat.py new file mode 100644 index 00000000..36c6d69d --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/praat.py @@ -0,0 +1,302 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.praat + ~~~~~~~~~~~~~~~~~~~~~ + + Lexer for Praat + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, words, bygroups, include +from pygments.token import Name, Text, Comment, Keyword, String, Punctuation, Number, \ + Operator + +__all__ = ['PraatLexer'] + + +class PraatLexer(RegexLexer): + """ + For `Praat `_ scripts. + + .. versionadded:: 2.1 + """ + + name = 'Praat' + aliases = ['praat'] + filenames = ['*.praat', '*.proc', '*.psc'] + + keywords = ( + 'if', 'then', 'else', 'elsif', 'elif', 'endif', 'fi', 'for', 'from', 'to', + 'endfor', 'endproc', 'while', 'endwhile', 'repeat', 'until', 'select', 'plus', + 'minus', 'demo', 'assert', 'stopwatch', 'nocheck', 'nowarn', 'noprogress', + 'editor', 'endeditor', 'clearinfo', + ) + + functions_string = ( + 'backslashTrigraphsToUnicode', 'chooseDirectory', 'chooseReadFile', + 'chooseWriteFile', 'date', 'demoKey', 'do', 'environment', 'extractLine', + 'extractWord', 'fixed', 'info', 'left', 'mid', 'percent', 'readFile', 'replace', + 'replace_regex', 'right', 'selected', 'string', 'unicodeToBackslashTrigraphs', + ) + + functions_numeric = ( + 'abs', 'appendFile', 'appendFileLine', 'appendInfo', 'appendInfoLine', 'arccos', + 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh', 'barkToHertz', + 'beginPause', 'beginSendPraat', 'besselI', 'besselK', 'beta', 'beta2', + 'binomialP', 'binomialQ', 'boolean', 'ceiling', 'chiSquareP', 'chiSquareQ', + 'choice', 'comment', 'cos', 'cosh', 'createDirectory', 'deleteFile', + 'demoClicked', 'demoClickedIn', 'demoCommandKeyPressed', + 'demoExtraControlKeyPressed', 'demoInput', 'demoKeyPressed', + 'demoOptionKeyPressed', 'demoShiftKeyPressed', 'demoShow', 'demoWaitForInput', + 'demoWindowTitle', 'demoX', 'demoY', 'differenceLimensToPhon', 'do', 'editor', + 'endPause', 'endSendPraat', 'endsWith', 'erb', 'erbToHertz', 'erf', 'erfc', + 'exitScript', 'exp', 'extractNumber', 'fileReadable', 'fisherP', 'fisherQ', + 'floor', 'gaussP', 'gaussQ', 'hertzToBark', 'hertzToErb', 'hertzToMel', + 'hertzToSemitones', 'imax', 'imin', 'incompleteBeta', 'incompleteGammaP', 'index', + 'index_regex', 'integer', 'invBinomialP', 'invBinomialQ', 'invChiSquareQ', 'invFisherQ', + 'invGaussQ', 'invSigmoid', 'invStudentQ', 'length', 'ln', 'lnBeta', 'lnGamma', + 'log10', 'log2', 'max', 'melToHertz', 'min', 'minusObject', 'natural', 'number', + 'numberOfColumns', 'numberOfRows', 'numberOfSelected', 'objectsAreIdentical', + 'option', 'optionMenu', 'pauseScript', 'phonToDifferenceLimens', 'plusObject', + 'positive', 'randomBinomial', 'randomGauss', 'randomInteger', 'randomPoisson', + 'randomUniform', 'real', 'readFile', 'removeObject', 'rindex', 'rindex_regex', + 'round', 'runScript', 'runSystem', 'runSystem_nocheck', 'selectObject', + 'selected', 'semitonesToHertz', 'sentence', 'sentencetext', 'sigmoid', 'sin', 'sinc', + 'sincpi', 'sinh', 'soundPressureToPhon', 'sqrt', 'startsWith', 'studentP', + 'studentQ', 'tan', 'tanh', 'text', 'variableExists', 'word', 'writeFile', 'writeFileLine', + 'writeInfo', 'writeInfoLine', + ) + + functions_array = ( + 'linear', 'randomGauss', 'randomInteger', 'randomUniform', 'zero', + ) + + objects = ( + 'Activation', 'AffineTransform', 'AmplitudeTier', 'Art', 'Artword', + 'Autosegment', 'BarkFilter', 'BarkSpectrogram', 'CCA', 'Categories', + 'Cepstrogram', 'Cepstrum', 'Cepstrumc', 'ChebyshevSeries', 'ClassificationTable', + 'Cochleagram', 'Collection', 'ComplexSpectrogram', 'Configuration', 'Confusion', + 'ContingencyTable', 'Corpus', 'Correlation', 'Covariance', + 'CrossCorrelationTable', 'CrossCorrelationTables', 'DTW', 'DataModeler', + 'Diagonalizer', 'Discriminant', 'Dissimilarity', 'Distance', 'Distributions', + 'DurationTier', 'EEG', 'ERP', 'ERPTier', 'EditCostsTable', 'EditDistanceTable', + 'Eigen', 'Excitation', 'Excitations', 'ExperimentMFC', 'FFNet', 'FeatureWeights', + 'FileInMemory', 'FilesInMemory', 'Formant', 'FormantFilter', 'FormantGrid', + 'FormantModeler', 'FormantPoint', 'FormantTier', 'GaussianMixture', 'HMM', + 'HMM_Observation', 'HMM_ObservationSequence', 'HMM_State', 'HMM_StateSequence', + 'Harmonicity', 'ISpline', 'Index', 'Intensity', 'IntensityTier', 'IntervalTier', + 'KNN', 'KlattGrid', 'KlattTable', 'LFCC', 'LPC', 'Label', 'LegendreSeries', + 'LinearRegression', 'LogisticRegression', 'LongSound', 'Ltas', 'MFCC', 'MSpline', + 'ManPages', 'Manipulation', 'Matrix', 'MelFilter', 'MelSpectrogram', + 'MixingMatrix', 'Movie', 'Network', 'Object', 'OTGrammar', 'OTHistory', 'OTMulti', + 'PCA', 'PairDistribution', 'ParamCurve', 'Pattern', 'Permutation', 'Photo', + 'Pitch', 'PitchModeler', 'PitchTier', 'PointProcess', 'Polygon', 'Polynomial', + 'PowerCepstrogram', 'PowerCepstrum', 'Procrustes', 'RealPoint', 'RealTier', + 'ResultsMFC', 'Roots', 'SPINET', 'SSCP', 'SVD', 'Salience', 'ScalarProduct', + 'Similarity', 'SimpleString', 'SortedSetOfString', 'Sound', 'Speaker', + 'Spectrogram', 'Spectrum', 'SpectrumTier', 'SpeechSynthesizer', 'SpellingChecker', + 'Strings', 'StringsIndex', 'Table', 'TableOfReal', 'TextGrid', 'TextInterval', + 'TextPoint', 'TextTier', 'Tier', 'Transition', 'VocalTract', 'VocalTractTier', + 'Weight', 'WordList', + ) + + variables_numeric = ( + 'macintosh', 'windows', 'unix', 'praatVersion', 'pi', 'e', 'undefined', + ) + + variables_string = ( + 'praatVersion', 'tab', 'shellDirectory', 'homeDirectory', + 'preferencesDirectory', 'newline', 'temporaryDirectory', + 'defaultDirectory', + ) + + object_attributes = ( + 'ncol', 'nrow', 'xmin', 'ymin', 'xmax', 'ymax', 'nx', 'ny', 'dx', 'dy', + ) + + tokens = { + 'root': [ + (r'(\s+)(#.*?$)', bygroups(Text, Comment.Single)), + (r'^#.*?$', Comment.Single), + (r';[^\n]*', Comment.Single), + (r'\s+', Text), + + (r'\bprocedure\b', Keyword, 'procedure_definition'), + (r'\bcall\b', Keyword, 'procedure_call'), + (r'@', Name.Function, 'procedure_call'), + + include('function_call'), + + (words(keywords, suffix=r'\b'), Keyword), + + (r'(\bform\b)(\s+)([^\n]+)', + bygroups(Keyword, Text, String), 'old_form'), + + (r'(print(?:line|tab)?|echo|exit|asserterror|pause|send(?:praat|socket)|' + r'include|execute|system(?:_nocheck)?)(\s+)', + bygroups(Keyword, Text), 'string_unquoted'), + + (r'(goto|label)(\s+)(\w+)', bygroups(Keyword, Text, Name.Label)), + + include('variable_name'), + include('number'), + + (r'"', String, 'string'), + + (words((objects), suffix=r'(?=\s+\S+\n)'), Name.Class, 'string_unquoted'), + + (r'\b[A-Z]', Keyword, 'command'), + (r'(\.{3}|[)(,])', Punctuation), + ], + 'command': [ + (r'( ?[\w()-]+ ?)', Keyword), + + include('string_interpolated'), + + (r'\.{3}', Keyword, ('#pop', 'old_arguments')), + (r':', Keyword, ('#pop', 'comma_list')), + (r'\s', Text, '#pop'), + ], + 'procedure_call': [ + (r'\s+', Text), + (r'([\w.]+)(:|\s*\()', + bygroups(Name.Function, Text), '#pop'), + (r'([\w.]+)', Name.Function, ('#pop', 'old_arguments')), + ], + 'procedure_definition': [ + (r'\s', Text), + (r'([\w.]+)(\s*?[(:])', + bygroups(Name.Function, Text), '#pop'), + (r'([\w.]+)([^\n]*)', + bygroups(Name.Function, Text), '#pop'), + ], + 'function_call': [ + (words(functions_string, suffix=r'\$(?=\s*[:(])'), Name.Function, 'function'), + (words(functions_array, suffix=r'#(?=\s*[:(])'), Name.Function, 'function'), + (words(functions_numeric, suffix=r'(?=\s*[:(])'), Name.Function, 'function'), + ], + 'function': [ + (r'\s+', Text), + (r':', Punctuation, ('#pop', 'comma_list')), + (r'\s*\(', Punctuation, ('#pop', 'comma_list')), + ], + 'comma_list': [ + (r'(\s*\n\s*)(\.{3})', bygroups(Text, Punctuation)), + + (r'(\s*[])\n])', Text, '#pop'), + + (r'\s+', Text), + (r'"', String, 'string'), + (r'\b(if|then|else|fi|endif)\b', Keyword), + + include('function_call'), + include('variable_name'), + include('operator'), + include('number'), + + (r'[()]', Text), + (r',', Punctuation), + ], + 'old_arguments': [ + (r'\n', Text, '#pop'), + + include('variable_name'), + include('operator'), + include('number'), + + (r'"', String, 'string'), + (r'[^\n]', Text), + ], + 'number': [ + (r'\n', Text, '#pop'), + (r'\b\d+(\.\d*)?([eE][-+]?\d+)?%?', Number), + ], + 'object_reference': [ + include('string_interpolated'), + (r'([a-z][a-zA-Z0-9_]*|\d+)', Name.Builtin), + + (words(object_attributes, prefix=r'\.'), Name.Builtin, '#pop'), + + (r'\$', Name.Builtin), + (r'\[', Text, '#pop'), + ], + 'variable_name': [ + include('operator'), + include('number'), + + (words(variables_string, suffix=r'\$'), Name.Variable.Global), + (words(variables_numeric, + suffix=r'(?=[^a-zA-Z0-9_."\'$#\[:(]|\s|^|$)'), + Name.Variable.Global), + + (words(objects, prefix=r'\b', suffix=r"(_)"), + bygroups(Name.Builtin, Name.Builtin), + 'object_reference'), + + (r'\.?_?[a-z][\w.]*(\$|#)?', Text), + (r'[\[\]]', Punctuation, 'comma_list'), + + include('string_interpolated'), + ], + 'operator': [ + (r'([+\/*<>=!-]=?|[&*|][&*|]?|\^|<>)', Operator), + (r'(?', Punctuation), + (r'"(?:\\x[0-9a-fA-F]+\\|\\u[0-9a-fA-F]{4}|\\U[0-9a-fA-F]{8}|' + r'\\[0-7]+\\|\\["\nabcefnrstv]|[^\\"])*"', String.Double), + (r"'(?:''|[^'])*'", String.Atom), # quoted atom + # Needs to not be followed by an atom. + # (r'=(?=\s|[a-zA-Z\[])', Operator), + (r'is\b', Operator), + (r'(<|>|=<|>=|==|=:=|=|/|//|\*|\+|-)(?=\s|[a-zA-Z0-9\[])', + Operator), + (r'(mod|div|not)\b', Operator), + (r'_', Keyword), # The don't-care variable + (r'([a-z]+)(:)', bygroups(Name.Namespace, Punctuation)), + (r'([a-z\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]' + r'[\w$\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]*)' + r'(\s*)(:-|-->)', + bygroups(Name.Function, Text, Operator)), # function defn + (r'([a-z\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]' + r'[\w$\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]*)' + r'(\s*)(\()', + bygroups(Name.Function, Text, Punctuation)), + (r'[a-z\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]' + r'[\w$\u00c0-\u1fff\u3040-\ud7ff\ue000-\uffef]*', + String.Atom), # atom, characters + # This one includes ! + (r'[#&*+\-./:<=>?@\\^~\u00a1-\u00bf\u2010-\u303f]+', + String.Atom), # atom, graphics + (r'[A-Z_]\w*', Name.Variable), + (r'\s+|[\u2000-\u200f\ufff0-\ufffe\uffef]', Text), + ], + 'nested-comment': [ + (r'\*/', Comment.Multiline, '#pop'), + (r'/\*', Comment.Multiline, '#push'), + (r'[^*/]+', Comment.Multiline), + (r'[*/]', Comment.Multiline), + ], + } + + def analyse_text(text): + return ':-' in text + + +class LogtalkLexer(RegexLexer): + """ + For `Logtalk `_ source code. + + .. versionadded:: 0.10 + """ + + name = 'Logtalk' + aliases = ['logtalk'] + filenames = ['*.lgt', '*.logtalk'] + mimetypes = ['text/x-logtalk'] + + tokens = { + 'root': [ + # Directives + (r'^\s*:-\s', Punctuation, 'directive'), + # Comments + (r'%.*?\n', Comment), + (r'/\*(.|\n)*?\*/', Comment), + # Whitespace + (r'\n', Text), + (r'\s+', Text), + # Numbers + (r"0'[\\]?.", Number), + (r'0b[01]+', Number.Bin), + (r'0o[0-7]+', Number.Oct), + (r'0x[0-9a-fA-F]+', Number.Hex), + (r'\d+\.?\d*((e|E)(\+|-)?\d+)?', Number), + # Variables + (r'([A-Z_][a-zA-Z0-9_]*)', Name.Variable), + # Event handlers + (r'(after|before)(?=[(])', Keyword), + # Message forwarding handler + (r'forward(?=[(])', Keyword), + # Execution-context methods + (r'(context|parameter|this|se(lf|nder))(?=[(])', Keyword), + # Reflection + (r'(current_predicate|predicate_property)(?=[(])', Keyword), + # DCGs and term expansion + (r'(expand_(goal|term)|(goal|term)_expansion|phrase)(?=[(])', Keyword), + # Entity + (r'(abolish|c(reate|urrent))_(object|protocol|category)(?=[(])', Keyword), + (r'(object|protocol|category)_property(?=[(])', Keyword), + # Entity relations + (r'co(mplements_object|nforms_to_protocol)(?=[(])', Keyword), + (r'extends_(object|protocol|category)(?=[(])', Keyword), + (r'imp(lements_protocol|orts_category)(?=[(])', Keyword), + (r'(instantiat|specializ)es_class(?=[(])', Keyword), + # Events + (r'(current_event|(abolish|define)_events)(?=[(])', Keyword), + # Flags + (r'(create|current|set)_logtalk_flag(?=[(])', Keyword), + # Compiling, loading, and library paths + (r'logtalk_(compile|l(ibrary_path|oad|oad_context)|make(_target_action)?)(?=[(])', Keyword), + (r'\blogtalk_make\b', Keyword), + # Database + (r'(clause|retract(all)?)(?=[(])', Keyword), + (r'a(bolish|ssert(a|z))(?=[(])', Keyword), + # Control constructs + (r'(ca(ll|tch)|throw)(?=[(])', Keyword), + (r'(fa(il|lse)|true|(instantiation|system)_error)\b', Keyword), + (r'(type|domain|existence|permission|representation|evaluation|resource|syntax)_error(?=[(])', Keyword), + # All solutions + (r'((bag|set)of|f(ind|or)all)(?=[(])', Keyword), + # Multi-threading predicates + (r'threaded(_(ca(ll|ncel)|once|ignore|exit|peek|wait|notify))?(?=[(])', Keyword), + # Engine predicates + (r'threaded_engine(_(create|destroy|self|next|next_reified|yield|post|fetch))?(?=[(])', Keyword), + # Term unification + (r'(subsumes_term|unify_with_occurs_check)(?=[(])', Keyword), + # Term creation and decomposition + (r'(functor|arg|copy_term|numbervars|term_variables)(?=[(])', Keyword), + # Evaluable functors + (r'(div|rem|m(ax|in|od)|abs|sign)(?=[(])', Keyword), + (r'float(_(integer|fractional)_part)?(?=[(])', Keyword), + (r'(floor|t(an|runcate)|round|ceiling)(?=[(])', Keyword), + # Other arithmetic functors + (r'(cos|a(cos|sin|tan|tan2)|exp|log|s(in|qrt)|xor)(?=[(])', Keyword), + # Term testing + (r'(var|atom(ic)?|integer|float|c(allable|ompound)|n(onvar|umber)|ground|acyclic_term)(?=[(])', Keyword), + # Term comparison + (r'compare(?=[(])', Keyword), + # Stream selection and control + (r'(curren|se)t_(in|out)put(?=[(])', Keyword), + (r'(open|close)(?=[(])', Keyword), + (r'flush_output(?=[(])', Keyword), + (r'(at_end_of_stream|flush_output)\b', Keyword), + (r'(stream_property|at_end_of_stream|set_stream_position)(?=[(])', Keyword), + # Character and byte input/output + (r'(nl|(get|peek|put)_(byte|c(har|ode)))(?=[(])', Keyword), + (r'\bnl\b', Keyword), + # Term input/output + (r'read(_term)?(?=[(])', Keyword), + (r'write(q|_(canonical|term))?(?=[(])', Keyword), + (r'(current_)?op(?=[(])', Keyword), + (r'(current_)?char_conversion(?=[(])', Keyword), + # Atomic term processing + (r'atom_(length|c(hars|o(ncat|des)))(?=[(])', Keyword), + (r'(char_code|sub_atom)(?=[(])', Keyword), + (r'number_c(har|ode)s(?=[(])', Keyword), + # Implementation defined hooks functions + (r'(se|curren)t_prolog_flag(?=[(])', Keyword), + (r'\bhalt\b', Keyword), + (r'halt(?=[(])', Keyword), + # Message sending operators + (r'(::|:|\^\^)', Operator), + # External call + (r'[{}]', Keyword), + # Logic and control + (r'(ignore|once)(?=[(])', Keyword), + (r'\brepeat\b', Keyword), + # Sorting + (r'(key)?sort(?=[(])', Keyword), + # Bitwise functors + (r'(>>|<<|/\\|\\\\|\\)', Operator), + # Predicate aliases + (r'\bas\b', Operator), + # Arithemtic evaluation + (r'\bis\b', Keyword), + # Arithemtic comparison + (r'(=:=|=\\=|<|=<|>=|>)', Operator), + # Term creation and decomposition + (r'=\.\.', Operator), + # Term unification + (r'(=|\\=)', Operator), + # Term comparison + (r'(==|\\==|@=<|@<|@>=|@>)', Operator), + # Evaluable functors + (r'(//|[-+*/])', Operator), + (r'\b(e|pi|div|mod|rem)\b', Operator), + # Other arithemtic functors + (r'\b\*\*\b', Operator), + # DCG rules + (r'-->', Operator), + # Control constructs + (r'([!;]|->)', Operator), + # Logic and control + (r'\\+', Operator), + # Mode operators + (r'[?@]', Operator), + # Existential quantifier + (r'\^', Operator), + # Strings + (r'"(\\\\|\\"|[^"])*"', String), + # Punctuation + (r'[()\[\],.|]', Text), + # Atoms + (r"[a-z][a-zA-Z0-9_]*", Text), + (r"'", String, 'quoted_atom'), + ], + + 'quoted_atom': [ + (r"''", String), + (r"'", String, '#pop'), + (r'\\([\\abfnrtv"\']|(x[a-fA-F0-9]+|[0-7]+)\\)', String.Escape), + (r"[^\\'\n]+", String), + (r'\\', String), + ], + + 'directive': [ + # Conditional compilation directives + (r'(el)?if(?=[(])', Keyword, 'root'), + (r'(e(lse|ndif))(?=[.])', Keyword, 'root'), + # Entity directives + (r'(category|object|protocol)(?=[(])', Keyword, 'entityrelations'), + (r'(end_(category|object|protocol))(?=[.])', Keyword, 'root'), + # Predicate scope directives + (r'(public|protected|private)(?=[(])', Keyword, 'root'), + # Other directives + (r'e(n(coding|sure_loaded)|xport)(?=[(])', Keyword, 'root'), + (r'in(clude|itialization|fo)(?=[(])', Keyword, 'root'), + (r'(built_in|dynamic|synchronized|threaded)(?=[.])', Keyword, 'root'), + (r'(alias|d(ynamic|iscontiguous)|m(eta_(non_terminal|predicate)|ode|ultifile)|s(et_(logtalk|prolog)_flag|ynchronized))(?=[(])', Keyword, 'root'), + (r'op(?=[(])', Keyword, 'root'), + (r'(c(alls|oinductive)|module|reexport|use(s|_module))(?=[(])', Keyword, 'root'), + (r'[a-z][a-zA-Z0-9_]*(?=[(])', Text, 'root'), + (r'[a-z][a-zA-Z0-9_]*(?=[.])', Text, 'root'), + ], + + 'entityrelations': [ + (r'(complements|extends|i(nstantiates|mp(lements|orts))|specializes)(?=[(])', Keyword), + # Numbers + (r"0'[\\]?.", Number), + (r'0b[01]+', Number.Bin), + (r'0o[0-7]+', Number.Oct), + (r'0x[0-9a-fA-F]+', Number.Hex), + (r'\d+\.?\d*((e|E)(\+|-)?\d+)?', Number), + # Variables + (r'([A-Z_][a-zA-Z0-9_]*)', Name.Variable), + # Atoms + (r"[a-z][a-zA-Z0-9_]*", Text), + (r"'", String, 'quoted_atom'), + # Strings + (r'"(\\\\|\\"|[^"])*"', String), + # End of entity-opening directive + (r'([)]\.)', Text, 'root'), + # Scope operator + (r'(::)', Operator), + # Punctuation + (r'[()\[\],.|]', Text), + # Comments + (r'%.*?\n', Comment), + (r'/\*(.|\n)*?\*/', Comment), + # Whitespace + (r'\n', Text), + (r'\s+', Text), + ] + } + + def analyse_text(text): + if ':- object(' in text: + return 1.0 + elif ':- protocol(' in text: + return 1.0 + elif ':- category(' in text: + return 1.0 + elif re.search(r'^:-\s[a-z]', text, re.M): + return 0.9 + else: + return 0.0 diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/promql.py b/.venv/lib/python3.8/site-packages/pygments/lexers/promql.py new file mode 100644 index 00000000..18069208 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/promql.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.promql + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for Prometheus Query Language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, bygroups, default, words +from pygments.token import ( + Comment, + Keyword, + Name, + Number, + Operator, + Punctuation, + String, + Whitespace, +) + +__all__ = ["PromQLLexer"] + + +class PromQLLexer(RegexLexer): + """ + For `PromQL `_ queries. + + For details about the grammar see: + https://github.com/prometheus/prometheus/tree/master/promql/parser + + .. versionadded: 2.7 + """ + + name = "PromQL" + aliases = ["promql"] + filenames = ["*.promql"] + + base_keywords = ( + words( + ( + "bool", + "by", + "group_left", + "group_right", + "ignoring", + "offset", + "on", + "without", + ), + suffix=r"\b", + ), + Keyword, + ) + + aggregator_keywords = ( + words( + ( + "sum", + "min", + "max", + "avg", + "group", + "stddev", + "stdvar", + "count", + "count_values", + "bottomk", + "topk", + "quantile", + ), + suffix=r"\b", + ), + Keyword, + ) + + function_keywords = ( + words( + ( + "abs", + "absent", + "absent_over_time", + "avg_over_time", + "ceil", + "changes", + "clamp_max", + "clamp_min", + "count_over_time", + "day_of_month", + "day_of_week", + "days_in_month", + "delta", + "deriv", + "exp", + "floor", + "histogram_quantile", + "holt_winters", + "hour", + "idelta", + "increase", + "irate", + "label_join", + "label_replace", + "ln", + "log10", + "log2", + "max_over_time", + "min_over_time", + "minute", + "month", + "predict_linear", + "quantile_over_time", + "rate", + "resets", + "round", + "scalar", + "sort", + "sort_desc", + "sqrt", + "stddev_over_time", + "stdvar_over_time", + "sum_over_time", + "time", + "timestamp", + "vector", + "year", + ), + suffix=r"\b", + ), + Keyword.Reserved, + ) + + tokens = { + "root": [ + (r"\n", Whitespace), + (r"\s+", Whitespace), + (r",", Punctuation), + # Keywords + base_keywords, + aggregator_keywords, + function_keywords, + # Offsets + (r"[1-9][0-9]*[smhdwy]", String), + # Numbers + (r"-?[0-9]+\.[0-9]+", Number.Float), + (r"-?[0-9]+", Number.Integer), + # Comments + (r"#.*?$", Comment.Single), + # Operators + (r"(\+|\-|\*|\/|\%|\^)", Operator), + (r"==|!=|>=|<=|<|>", Operator), + (r"and|or|unless", Operator.Word), + # Metrics + (r"[_a-zA-Z][a-zA-Z0-9_]+", Name.Variable), + # Params + (r'(["\'])(.*?)(["\'])', bygroups(Punctuation, String, Punctuation)), + # Other states + (r"\(", Operator, "function"), + (r"\)", Operator), + (r"\{", Punctuation, "labels"), + (r"\[", Punctuation, "range"), + ], + "labels": [ + (r"\}", Punctuation, "#pop"), + (r"\n", Whitespace), + (r"\s+", Whitespace), + (r",", Punctuation), + (r'([_a-zA-Z][a-zA-Z0-9_]*?)(\s*?)(=~|!=|=|~!)(\s*?)(")(.*?)(")', + bygroups(Name.Label, Whitespace, Operator, Whitespace, + Punctuation, String, Punctuation)), + ], + "range": [ + (r"\]", Punctuation, "#pop"), + (r"[1-9][0-9]*[smhdwy]", String), + ], + "function": [ + (r"\)", Operator, "#pop"), + (r"\(", Operator, "#push"), + default("#pop"), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/python.py b/.venv/lib/python3.8/site-packages/pygments/lexers/python.py new file mode 100644 index 00000000..051371f9 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/python.py @@ -0,0 +1,1151 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.python + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Python and related languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import Lexer, RegexLexer, include, bygroups, using, \ + default, words, combined, do_insertions +from pygments.util import get_bool_opt, shebang_matches +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Generic, Other, Error +from pygments import unistring as uni + +__all__ = ['PythonLexer', 'PythonConsoleLexer', 'PythonTracebackLexer', + 'Python2Lexer', 'Python2TracebackLexer', + 'CythonLexer', 'DgLexer', 'NumPyLexer'] + +line_re = re.compile('.*?\n') + + +class PythonLexer(RegexLexer): + """ + For `Python `_ source code (version 3.x). + + .. versionadded:: 0.10 + + .. versionchanged:: 2.5 + This is now the default ``PythonLexer``. It is still available as the + alias ``Python3Lexer``. + """ + + name = 'Python' + aliases = ['python', 'py', 'sage', 'python3', 'py3'] + filenames = [ + '*.py', + '*.pyw', + # Jython + '*.jy', + # Sage + '*.sage', + # SCons + '*.sc', + 'SConstruct', + 'SConscript', + # Skylark/Starlark (used by Bazel, Buck, and Pants) + '*.bzl', + 'BUCK', + 'BUILD', + 'BUILD.bazel', + 'WORKSPACE', + # Twisted Application infrastructure + '*.tac', + ] + mimetypes = ['text/x-python', 'application/x-python', + 'text/x-python3', 'application/x-python3'] + + flags = re.MULTILINE | re.UNICODE + + uni_name = "[%s][%s]*" % (uni.xid_start, uni.xid_continue) + + def innerstring_rules(ttype): + return [ + # the old style '%s' % (...) string formatting (still valid in Py3) + (r'%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?' + '[hlL]?[E-GXc-giorsaux%]', String.Interpol), + # the new style '{}'.format(...) string formatting + (r'\{' + r'((\w+)((\.\w+)|(\[[^\]]+\]))*)?' # field name + r'(\![sra])?' # conversion + r'(\:(.?[<>=\^])?[-+ ]?#?0?(\d+)?,?(\.\d+)?[E-GXb-gnosx%]?)?' + r'\}', String.Interpol), + + # backslashes, quotes and formatting signs must be parsed one at a time + (r'[^\\\'"%{\n]+', ttype), + (r'[\'"\\]', ttype), + # unhandled string formatting sign + (r'%|(\{{1,2})', ttype) + # newlines are an error (use "nl" state) + ] + + def fstring_rules(ttype): + return [ + # Assuming that a '}' is the closing brace after format specifier. + # Sadly, this means that we won't detect syntax error. But it's + # more important to parse correct syntax correctly, than to + # highlight invalid syntax. + (r'\}', String.Interpol), + (r'\{', String.Interpol, 'expr-inside-fstring'), + # backslashes, quotes and formatting signs must be parsed one at a time + (r'[^\\\'"{}\n]+', ttype), + (r'[\'"\\]', ttype), + # newlines are an error (use "nl" state) + ] + + tokens = { + 'root': [ + (r'\n', Text), + (r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")', + bygroups(Text, String.Affix, String.Doc)), + (r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')", + bygroups(Text, String.Affix, String.Doc)), + (r'\A#!.+$', Comment.Hashbang), + (r'#.*$', Comment.Single), + (r'\\\n', Text), + (r'\\', Text), + include('keywords'), + (r'(def)((?:\s|\\\s)+)', bygroups(Keyword, Text), 'funcname'), + (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Text), 'classname'), + (r'(from)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Text), + 'fromimport'), + (r'(import)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Text), + 'import'), + include('expr'), + ], + 'expr': [ + # raw f-strings + ('(?i)(rf|fr)(""")', + bygroups(String.Affix, String.Double), 'tdqf'), + ("(?i)(rf|fr)(''')", + bygroups(String.Affix, String.Single), 'tsqf'), + ('(?i)(rf|fr)(")', + bygroups(String.Affix, String.Double), 'dqf'), + ("(?i)(rf|fr)(')", + bygroups(String.Affix, String.Single), 'sqf'), + # non-raw f-strings + ('([fF])(""")', bygroups(String.Affix, String.Double), + combined('fstringescape', 'tdqf')), + ("([fF])(''')", bygroups(String.Affix, String.Single), + combined('fstringescape', 'tsqf')), + ('([fF])(")', bygroups(String.Affix, String.Double), + combined('fstringescape', 'dqf')), + ("([fF])(')", bygroups(String.Affix, String.Single), + combined('fstringescape', 'sqf')), + # raw strings + ('(?i)(rb|br|r)(""")', + bygroups(String.Affix, String.Double), 'tdqs'), + ("(?i)(rb|br|r)(''')", + bygroups(String.Affix, String.Single), 'tsqs'), + ('(?i)(rb|br|r)(")', + bygroups(String.Affix, String.Double), 'dqs'), + ("(?i)(rb|br|r)(')", + bygroups(String.Affix, String.Single), 'sqs'), + # non-raw strings + ('([uUbB]?)(""")', bygroups(String.Affix, String.Double), + combined('stringescape', 'tdqs')), + ("([uUbB]?)(''')", bygroups(String.Affix, String.Single), + combined('stringescape', 'tsqs')), + ('([uUbB]?)(")', bygroups(String.Affix, String.Double), + combined('stringescape', 'dqs')), + ("([uUbB]?)(')", bygroups(String.Affix, String.Single), + combined('stringescape', 'sqs')), + (r'[^\S\n]+', Text), + (r'!=|==|<<|>>|:=|[-~+/*%=<>&^|.]', Operator), + (r'[]{}:(),;[]', Punctuation), + (r'(in|is|and|or|not)\b', Operator.Word), + include('expr-keywords'), + include('builtins'), + include('magicfuncs'), + include('magicvars'), + include('name'), + include('numbers'), + ], + 'expr-inside-fstring': [ + (r'[{([]', Punctuation, 'expr-inside-fstring-inner'), + # without format specifier + (r'(=\s*)?' # debug (https://bugs.python.org/issue36817) + r'(\![sraf])?' # conversion + r'\}', String.Interpol, '#pop'), + # with format specifier + # we'll catch the remaining '}' in the outer scope + (r'(=\s*)?' # debug (https://bugs.python.org/issue36817) + r'(\![sraf])?' # conversion + r':', String.Interpol, '#pop'), + (r'\s+', Text), # allow new lines + include('expr'), + ], + 'expr-inside-fstring-inner': [ + (r'[{([]', Punctuation, 'expr-inside-fstring-inner'), + (r'[])}]', Punctuation, '#pop'), + (r'\s+', Text), # allow new lines + include('expr'), + ], + 'expr-keywords': [ + # Based on https://docs.python.org/3/reference/expressions.html + (words(( + 'async for', 'await', 'else', 'for', 'if', 'lambda', + 'yield', 'yield from'), suffix=r'\b'), + Keyword), + (words(('True', 'False', 'None'), suffix=r'\b'), Keyword.Constant), + ], + 'keywords': [ + (words(( + 'assert', 'async', 'await', 'break', 'continue', 'del', 'elif', + 'else', 'except', 'finally', 'for', 'global', 'if', 'lambda', + 'pass', 'raise', 'nonlocal', 'return', 'try', 'while', 'yield', + 'yield from', 'as', 'with'), suffix=r'\b'), + Keyword), + (words(('True', 'False', 'None'), suffix=r'\b'), Keyword.Constant), + ], + 'builtins': [ + (words(( + '__import__', 'abs', 'all', 'any', 'bin', 'bool', 'bytearray', + 'bytes', 'chr', 'classmethod', 'compile', 'complex', + 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'filter', + 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', + 'hash', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', + 'iter', 'len', 'list', 'locals', 'map', 'max', 'memoryview', + 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', + 'property', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', + 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', + 'type', 'vars', 'zip'), prefix=r'(?`_ source code. + + .. versionchanged:: 2.5 + This class has been renamed from ``PythonLexer``. ``PythonLexer`` now + refers to the Python 3 variant. File name patterns like ``*.py`` have + been moved to Python 3 as well. + """ + + name = 'Python 2.x' + aliases = ['python2', 'py2'] + filenames = [] # now taken over by PythonLexer (3.x) + mimetypes = ['text/x-python2', 'application/x-python2'] + + def innerstring_rules(ttype): + return [ + # the old style '%s' % (...) string formatting + (r'%(\(\w+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?' + '[hlL]?[E-GXc-giorsux%]', String.Interpol), + # backslashes, quotes and formatting signs must be parsed one at a time + (r'[^\\\'"%\n]+', ttype), + (r'[\'"\\]', ttype), + # unhandled string formatting sign + (r'%', ttype), + # newlines are an error (use "nl" state) + ] + + tokens = { + 'root': [ + (r'\n', Text), + (r'^(\s*)([rRuUbB]{,2})("""(?:.|\n)*?""")', + bygroups(Text, String.Affix, String.Doc)), + (r"^(\s*)([rRuUbB]{,2})('''(?:.|\n)*?''')", + bygroups(Text, String.Affix, String.Doc)), + (r'[^\S\n]+', Text), + (r'\A#!.+$', Comment.Hashbang), + (r'#.*$', Comment.Single), + (r'[]{}:(),;[]', Punctuation), + (r'\\\n', Text), + (r'\\', Text), + (r'(in|is|and|or|not)\b', Operator.Word), + (r'!=|==|<<|>>|[-~+/*%=<>&^|.]', Operator), + include('keywords'), + (r'(def)((?:\s|\\\s)+)', bygroups(Keyword, Text), 'funcname'), + (r'(class)((?:\s|\\\s)+)', bygroups(Keyword, Text), 'classname'), + (r'(from)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Text), + 'fromimport'), + (r'(import)((?:\s|\\\s)+)', bygroups(Keyword.Namespace, Text), + 'import'), + include('builtins'), + include('magicfuncs'), + include('magicvars'), + include('backtick'), + ('([rR]|[uUbB][rR]|[rR][uUbB])(""")', + bygroups(String.Affix, String.Double), 'tdqs'), + ("([rR]|[uUbB][rR]|[rR][uUbB])(''')", + bygroups(String.Affix, String.Single), 'tsqs'), + ('([rR]|[uUbB][rR]|[rR][uUbB])(")', + bygroups(String.Affix, String.Double), 'dqs'), + ("([rR]|[uUbB][rR]|[rR][uUbB])(')", + bygroups(String.Affix, String.Single), 'sqs'), + ('([uUbB]?)(""")', bygroups(String.Affix, String.Double), + combined('stringescape', 'tdqs')), + ("([uUbB]?)(''')", bygroups(String.Affix, String.Single), + combined('stringescape', 'tsqs')), + ('([uUbB]?)(")', bygroups(String.Affix, String.Double), + combined('stringescape', 'dqs')), + ("([uUbB]?)(')", bygroups(String.Affix, String.Single), + combined('stringescape', 'sqs')), + include('name'), + include('numbers'), + ], + 'keywords': [ + (words(( + 'assert', 'break', 'continue', 'del', 'elif', 'else', 'except', + 'exec', 'finally', 'for', 'global', 'if', 'lambda', 'pass', + 'print', 'raise', 'return', 'try', 'while', 'yield', + 'yield from', 'as', 'with'), suffix=r'\b'), + Keyword), + ], + 'builtins': [ + (words(( + '__import__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', + 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', + 'cmp', 'coerce', 'compile', 'complex', 'delattr', 'dict', 'dir', 'divmod', + 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', + 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', + 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', + 'list', 'locals', 'long', 'map', 'max', 'min', 'next', 'object', + 'oct', 'open', 'ord', 'pow', 'property', 'range', 'raw_input', 'reduce', + 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', + 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', + 'unichr', 'unicode', 'vars', 'xrange', 'zip'), + prefix=r'(?>> a = 'foo' + >>> print a + foo + >>> 1 / 0 + Traceback (most recent call last): + File "", line 1, in + ZeroDivisionError: integer division or modulo by zero + + Additional options: + + `python3` + Use Python 3 lexer for code. Default is ``True``. + + .. versionadded:: 1.0 + .. versionchanged:: 2.5 + Now defaults to ``True``. + """ + name = 'Python console session' + aliases = ['pycon'] + mimetypes = ['text/x-python-doctest'] + + def __init__(self, **options): + self.python3 = get_bool_opt(options, 'python3', True) + Lexer.__init__(self, **options) + + def get_tokens_unprocessed(self, text): + if self.python3: + pylexer = PythonLexer(**self.options) + tblexer = PythonTracebackLexer(**self.options) + else: + pylexer = Python2Lexer(**self.options) + tblexer = Python2TracebackLexer(**self.options) + + curcode = '' + insertions = [] + curtb = '' + tbindex = 0 + tb = 0 + for match in line_re.finditer(text): + line = match.group() + if line.startswith('>>> ') or line.startswith('... '): + tb = 0 + insertions.append((len(curcode), + [(0, Generic.Prompt, line[:4])])) + curcode += line[4:] + elif line.rstrip() == '...' and not tb: + # only a new >>> prompt can end an exception block + # otherwise an ellipsis in place of the traceback frames + # will be mishandled + insertions.append((len(curcode), + [(0, Generic.Prompt, '...')])) + curcode += line[3:] + else: + if curcode: + yield from do_insertions( + insertions, pylexer.get_tokens_unprocessed(curcode)) + curcode = '' + insertions = [] + if (line.startswith('Traceback (most recent call last):') or + re.match(' File "[^"]+", line \\d+\\n$', line)): + tb = 1 + curtb = line + tbindex = match.start() + elif line == 'KeyboardInterrupt\n': + yield match.start(), Name.Class, line + elif tb: + curtb += line + if not (line.startswith(' ') or line.strip() == '...'): + tb = 0 + for i, t, v in tblexer.get_tokens_unprocessed(curtb): + yield tbindex+i, t, v + curtb = '' + else: + yield match.start(), Generic.Output, line + if curcode: + yield from do_insertions(insertions, + pylexer.get_tokens_unprocessed(curcode)) + if curtb: + for i, t, v in tblexer.get_tokens_unprocessed(curtb): + yield tbindex+i, t, v + + +class PythonTracebackLexer(RegexLexer): + """ + For Python 3.x tracebacks, with support for chained exceptions. + + .. versionadded:: 1.0 + + .. versionchanged:: 2.5 + This is now the default ``PythonTracebackLexer``. It is still available + as the alias ``Python3TracebackLexer``. + """ + + name = 'Python Traceback' + aliases = ['pytb', 'py3tb'] + filenames = ['*.pytb', '*.py3tb'] + mimetypes = ['text/x-python-traceback', 'text/x-python3-traceback'] + + tokens = { + 'root': [ + (r'\n', Text), + (r'^Traceback \(most recent call last\):\n', Generic.Traceback, 'intb'), + (r'^During handling of the above exception, another ' + r'exception occurred:\n\n', Generic.Traceback), + (r'^The above exception was the direct cause of the ' + r'following exception:\n\n', Generic.Traceback), + (r'^(?= File "[^"]+", line \d+)', Generic.Traceback, 'intb'), + (r'^.*\n', Other), + ], + 'intb': [ + (r'^( File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)', + bygroups(Text, Name.Builtin, Text, Number, Text, Name, Text)), + (r'^( File )("[^"]+")(, line )(\d+)(\n)', + bygroups(Text, Name.Builtin, Text, Number, Text)), + (r'^( )(.+)(\n)', + bygroups(Text, using(PythonLexer), Text)), + (r'^([ \t]*)(\.\.\.)(\n)', + bygroups(Text, Comment, Text)), # for doctests... + (r'^([^:]+)(: )(.+)(\n)', + bygroups(Generic.Error, Text, Name, Text), '#pop'), + (r'^([a-zA-Z_][\w.]*)(:?\n)', + bygroups(Generic.Error, Text), '#pop') + ], + } + + +Python3TracebackLexer = PythonTracebackLexer + + +class Python2TracebackLexer(RegexLexer): + """ + For Python tracebacks. + + .. versionadded:: 0.7 + + .. versionchanged:: 2.5 + This class has been renamed from ``PythonTracebackLexer``. + ``PythonTracebackLexer`` now refers to the Python 3 variant. + """ + + name = 'Python 2.x Traceback' + aliases = ['py2tb'] + filenames = ['*.py2tb'] + mimetypes = ['text/x-python2-traceback'] + + tokens = { + 'root': [ + # Cover both (most recent call last) and (innermost last) + # The optional ^C allows us to catch keyboard interrupt signals. + (r'^(\^C)?(Traceback.*\n)', + bygroups(Text, Generic.Traceback), 'intb'), + # SyntaxError starts with this. + (r'^(?= File "[^"]+", line \d+)', Generic.Traceback, 'intb'), + (r'^.*\n', Other), + ], + 'intb': [ + (r'^( File )("[^"]+")(, line )(\d+)(, in )(.+)(\n)', + bygroups(Text, Name.Builtin, Text, Number, Text, Name, Text)), + (r'^( File )("[^"]+")(, line )(\d+)(\n)', + bygroups(Text, Name.Builtin, Text, Number, Text)), + (r'^( )(.+)(\n)', + bygroups(Text, using(Python2Lexer), Text)), + (r'^([ \t]*)(\.\.\.)(\n)', + bygroups(Text, Comment, Text)), # for doctests... + (r'^([^:]+)(: )(.+)(\n)', + bygroups(Generic.Error, Text, Name, Text), '#pop'), + (r'^([a-zA-Z_]\w*)(:?\n)', + bygroups(Generic.Error, Text), '#pop') + ], + } + + +class CythonLexer(RegexLexer): + """ + For Pyrex and `Cython `_ source code. + + .. versionadded:: 1.1 + """ + + name = 'Cython' + aliases = ['cython', 'pyx', 'pyrex'] + filenames = ['*.pyx', '*.pxd', '*.pxi'] + mimetypes = ['text/x-cython', 'application/x-cython'] + + tokens = { + 'root': [ + (r'\n', Text), + (r'^(\s*)("""(?:.|\n)*?""")', bygroups(Text, String.Doc)), + (r"^(\s*)('''(?:.|\n)*?''')", bygroups(Text, String.Doc)), + (r'[^\S\n]+', Text), + (r'#.*$', Comment), + (r'[]{}:(),;[]', Punctuation), + (r'\\\n', Text), + (r'\\', Text), + (r'(in|is|and|or|not)\b', Operator.Word), + (r'(<)([a-zA-Z0-9.?]+)(>)', + bygroups(Punctuation, Keyword.Type, Punctuation)), + (r'!=|==|<<|>>|[-~+/*%=<>&^|.?]', Operator), + (r'(from)(\d+)(<=)(\s+)(<)(\d+)(:)', + bygroups(Keyword, Number.Integer, Operator, Name, Operator, + Name, Punctuation)), + include('keywords'), + (r'(def|property)(\s+)', bygroups(Keyword, Text), 'funcname'), + (r'(cp?def)(\s+)', bygroups(Keyword, Text), 'cdef'), + # (should actually start a block with only cdefs) + (r'(cdef)(:)', bygroups(Keyword, Punctuation)), + (r'(class|struct)(\s+)', bygroups(Keyword, Text), 'classname'), + (r'(from)(\s+)', bygroups(Keyword, Text), 'fromimport'), + (r'(c?import)(\s+)', bygroups(Keyword, Text), 'import'), + include('builtins'), + include('backtick'), + ('(?:[rR]|[uU][rR]|[rR][uU])"""', String, 'tdqs'), + ("(?:[rR]|[uU][rR]|[rR][uU])'''", String, 'tsqs'), + ('(?:[rR]|[uU][rR]|[rR][uU])"', String, 'dqs'), + ("(?:[rR]|[uU][rR]|[rR][uU])'", String, 'sqs'), + ('[uU]?"""', String, combined('stringescape', 'tdqs')), + ("[uU]?'''", String, combined('stringescape', 'tsqs')), + ('[uU]?"', String, combined('stringescape', 'dqs')), + ("[uU]?'", String, combined('stringescape', 'sqs')), + include('name'), + include('numbers'), + ], + 'keywords': [ + (words(( + 'assert', 'async', 'await', 'break', 'by', 'continue', 'ctypedef', 'del', 'elif', + 'else', 'except', 'except?', 'exec', 'finally', 'for', 'fused', 'gil', + 'global', 'if', 'include', 'lambda', 'nogil', 'pass', 'print', + 'raise', 'return', 'try', 'while', 'yield', 'as', 'with'), suffix=r'\b'), + Keyword), + (r'(DEF|IF|ELIF|ELSE)\b', Comment.Preproc), + ], + 'builtins': [ + (words(( + '__import__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', + 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', + 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'delattr', + 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', + 'file', 'filter', 'float', 'frozenset', 'getattr', 'globals', + 'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', + 'issubclass', 'iter', 'len', 'list', 'locals', 'long', 'map', 'max', + 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'property', + 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', + 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', + 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'unsigned', + 'vars', 'xrange', 'zip'), prefix=r'(?`_, + a functional and object-oriented programming language + running on the CPython 3 VM. + + .. versionadded:: 1.6 + """ + name = 'dg' + aliases = ['dg'] + filenames = ['*.dg'] + mimetypes = ['text/x-dg'] + + tokens = { + 'root': [ + (r'\s+', Text), + (r'#.*?$', Comment.Single), + + (r'(?i)0b[01]+', Number.Bin), + (r'(?i)0o[0-7]+', Number.Oct), + (r'(?i)0x[0-9a-f]+', Number.Hex), + (r'(?i)[+-]?[0-9]+\.[0-9]+(e[+-]?[0-9]+)?j?', Number.Float), + (r'(?i)[+-]?[0-9]+e[+-]?\d+j?', Number.Float), + (r'(?i)[+-]?[0-9]+j?', Number.Integer), + + (r"(?i)(br|r?b?)'''", String, combined('stringescape', 'tsqs', 'string')), + (r'(?i)(br|r?b?)"""', String, combined('stringescape', 'tdqs', 'string')), + (r"(?i)(br|r?b?)'", String, combined('stringescape', 'sqs', 'string')), + (r'(?i)(br|r?b?)"', String, combined('stringescape', 'dqs', 'string')), + + (r"`\w+'*`", Operator), + (r'\b(and|in|is|or|where)\b', Operator.Word), + (r'[!$%&*+\-./:<-@\\^|~;,]+', Operator), + + (words(( + 'bool', 'bytearray', 'bytes', 'classmethod', 'complex', 'dict', 'dict\'', + 'float', 'frozenset', 'int', 'list', 'list\'', 'memoryview', 'object', + 'property', 'range', 'set', 'set\'', 'slice', 'staticmethod', 'str', + 'super', 'tuple', 'tuple\'', 'type'), + prefix=r'(?`_. + + Reference for implementing this: «Meta Object Facility (MOF) 2.0 + Query/View/Transformation Specification», Version 1.1 - January 2011 + (http://www.omg.org/spec/QVT/1.1/), see §8.4, «Concrete Syntax» in + particular. + + Notable tokens assignments: + + - Name.Class is assigned to the identifier following any of the following + keywords: metamodel, class, exception, primitive, enum, transformation + or library + + - Name.Function is assigned to the names of mappings and queries + + - Name.Builtin.Pseudo is assigned to the pre-defined variables 'this', + 'self' and 'result'. + """ + # With obvious borrowings & inspiration from the Java, Python and C lexers + + name = 'QVTO' + aliases = ['qvto', 'qvt'] + filenames = ['*.qvto'] + + tokens = { + 'root': [ + (r'\n', Text), + (r'[^\S\n]+', Text), + (r'(--|//)(\s*)(directive:)?(.*)$', + bygroups(Comment, Comment, Comment.Preproc, Comment)), + # Uncomment the following if you want to distinguish between + # '/*' and '/**', à la javadoc + # (r'/[*]{2}(.|\n)*?[*]/', Comment.Multiline), + (r'/[*](.|\n)*?[*]/', Comment.Multiline), + (r'\\\n', Text), + (r'(and|not|or|xor|##?)\b', Operator.Word), + (r'(:{1,2}=|[-+]=)\b', Operator.Word), + (r'(@|<<|>>)\b', Keyword), # stereotypes + (r'!=|<>|==|=|!->|->|>=|<=|[.]{3}|[+/*%=<>&|.~]', Operator), + (r'[]{}:(),;[]', Punctuation), + (r'(true|false|unlimited|null)\b', Keyword.Constant), + (r'(this|self|result)\b', Name.Builtin.Pseudo), + (r'(var)\b', Keyword.Declaration), + (r'(from|import)\b', Keyword.Namespace, 'fromimport'), + (r'(metamodel|class|exception|primitive|enum|transformation|' + r'library)(\s+)(\w+)', + bygroups(Keyword.Word, Text, Name.Class)), + (r'(exception)(\s+)(\w+)', + bygroups(Keyword.Word, Text, Name.Exception)), + (r'(main)\b', Name.Function), + (r'(mapping|helper|query)(\s+)', + bygroups(Keyword.Declaration, Text), 'operation'), + (r'(assert)(\s+)\b', bygroups(Keyword, Text), 'assert'), + (r'(Bag|Collection|Dict|OrderedSet|Sequence|Set|Tuple|List)\b', + Keyword.Type), + include('keywords'), + ('"', String, combined('stringescape', 'dqs')), + ("'", String, combined('stringescape', 'sqs')), + include('name'), + include('numbers'), + # (r'([a-zA-Z_]\w*)(::)([a-zA-Z_]\w*)', + # bygroups(Text, Text, Text)), + ], + + 'fromimport': [ + (r'(?:[ \t]|\\\n)+', Text), + (r'[a-zA-Z_][\w.]*', Name.Namespace), + default('#pop'), + ], + + 'operation': [ + (r'::', Text), + (r'(.*::)([a-zA-Z_]\w*)([ \t]*)(\()', + bygroups(Text, Name.Function, Text, Punctuation), '#pop') + ], + + 'assert': [ + (r'(warning|error|fatal)\b', Keyword, '#pop'), + default('#pop'), # all else: go back + ], + + 'keywords': [ + (words(( + 'abstract', 'access', 'any', 'assert', 'blackbox', 'break', + 'case', 'collect', 'collectNested', 'collectOne', 'collectselect', + 'collectselectOne', 'composes', 'compute', 'configuration', + 'constructor', 'continue', 'datatype', 'default', 'derived', + 'disjuncts', 'do', 'elif', 'else', 'end', 'endif', 'except', + 'exists', 'extends', 'forAll', 'forEach', 'forOne', 'from', 'if', + 'implies', 'in', 'inherits', 'init', 'inout', 'intermediate', + 'invresolve', 'invresolveIn', 'invresolveone', 'invresolveoneIn', + 'isUnique', 'iterate', 'late', 'let', 'literal', 'log', 'map', + 'merges', 'modeltype', 'new', 'object', 'one', 'ordered', 'out', + 'package', 'population', 'property', 'raise', 'readonly', + 'references', 'refines', 'reject', 'resolve', 'resolveIn', + 'resolveone', 'resolveoneIn', 'return', 'select', 'selectOne', + 'sortedBy', 'static', 'switch', 'tag', 'then', 'try', 'typedef', + 'unlimited', 'uses', 'when', 'where', 'while', 'with', 'xcollect', + 'xmap', 'xselect'), suffix=r'\b'), Keyword), + ], + + # There is no need to distinguish between String.Single and + # String.Double: 'strings' is factorised for 'dqs' and 'sqs' + 'strings': [ + (r'[^\\\'"\n]+', String), + # quotes, percents and backslashes must be parsed one at a time + (r'[\'"\\]', String), + ], + 'stringescape': [ + (r'\\([\\btnfr"\']|u[0-3][0-7]{2}|u[0-7]{1,2})', String.Escape) + ], + 'dqs': [ # double-quoted string + (r'"', String, '#pop'), + (r'\\\\|\\"', String.Escape), + include('strings') + ], + 'sqs': [ # single-quoted string + (r"'", String, '#pop'), + (r"\\\\|\\'", String.Escape), + include('strings') + ], + 'name': [ + (r'[a-zA-Z_]\w*', Name), + ], + # numbers: excerpt taken from the python lexer + 'numbers': [ + (r'(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?', Number.Float), + (r'\d+[eE][+-]?[0-9]+', Number.Float), + (r'\d+', Number.Integer) + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/r.py b/.venv/lib/python3.8/site-packages/pygments/lexers/r.py new file mode 100644 index 00000000..6d841a3a --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/r.py @@ -0,0 +1,191 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.r + ~~~~~~~~~~~~~~~~~ + + Lexers for the R/S languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import Lexer, RegexLexer, include, do_insertions, bygroups +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Generic + +__all__ = ['RConsoleLexer', 'SLexer', 'RdLexer'] + + +line_re = re.compile('.*?\n') + + +class RConsoleLexer(Lexer): + """ + For R console transcripts or R CMD BATCH output files. + """ + + name = 'RConsole' + aliases = ['rconsole', 'rout'] + filenames = ['*.Rout'] + + def get_tokens_unprocessed(self, text): + slexer = SLexer(**self.options) + + current_code_block = '' + insertions = [] + + for match in line_re.finditer(text): + line = match.group() + if line.startswith('>') or line.startswith('+'): + # Colorize the prompt as such, + # then put rest of line into current_code_block + insertions.append((len(current_code_block), + [(0, Generic.Prompt, line[:2])])) + current_code_block += line[2:] + else: + # We have reached a non-prompt line! + # If we have stored prompt lines, need to process them first. + if current_code_block: + # Weave together the prompts and highlight code. + yield from do_insertions( + insertions, slexer.get_tokens_unprocessed(current_code_block)) + # Reset vars for next code block. + current_code_block = '' + insertions = [] + # Now process the actual line itself, this is output from R. + yield match.start(), Generic.Output, line + + # If we happen to end on a code block with nothing after it, need to + # process the last code block. This is neither elegant nor DRY so + # should be changed. + if current_code_block: + yield from do_insertions( + insertions, slexer.get_tokens_unprocessed(current_code_block)) + + +class SLexer(RegexLexer): + """ + For S, S-plus, and R source code. + + .. versionadded:: 0.10 + """ + + name = 'S' + aliases = ['splus', 's', 'r'] + filenames = ['*.S', '*.R', '.Rhistory', '.Rprofile', '.Renviron'] + mimetypes = ['text/S-plus', 'text/S', 'text/x-r-source', 'text/x-r', + 'text/x-R', 'text/x-r-history', 'text/x-r-profile'] + + valid_name = r'`[^`\\]*(?:\\.[^`\\]*)*`|(?:[a-zA-Z]|\.[A-Za-z_.])[\w.]*|\.' + tokens = { + 'comments': [ + (r'#.*$', Comment.Single), + ], + 'valid_name': [ + (valid_name, Name), + ], + 'punctuation': [ + (r'\[{1,2}|\]{1,2}|\(|\)|;|,', Punctuation), + ], + 'keywords': [ + (r'(if|else|for|while|repeat|in|next|break|return|switch|function)' + r'(?![\w.])', + Keyword.Reserved), + ], + 'operators': [ + (r'<>?|-|==|<=|>=|<|>|&&?|!=|\|\|?|\?', Operator), + (r'\*|\+|\^|/|!|%[^%]*%|=|~|\$|@|:{1,3}', Operator), + ], + 'builtin_symbols': [ + (r'(NULL|NA(_(integer|real|complex|character)_)?|' + r'letters|LETTERS|Inf|TRUE|FALSE|NaN|pi|\.\.(\.|[0-9]+))' + r'(?![\w.])', + Keyword.Constant), + (r'(T|F)\b', Name.Builtin.Pseudo), + ], + 'numbers': [ + # hex number + (r'0[xX][a-fA-F0-9]+([pP][0-9]+)?[Li]?', Number.Hex), + # decimal number + (r'[+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+|\.)([eE][+-]?[0-9]+)?[Li]?', + Number), + ], + 'statements': [ + include('comments'), + # whitespaces + (r'\s+', Text), + (r'\'', String, 'string_squote'), + (r'\"', String, 'string_dquote'), + include('builtin_symbols'), + include('valid_name'), + include('numbers'), + include('keywords'), + include('punctuation'), + include('operators'), + ], + 'root': [ + # calls: + (r'(%s)\s*(?=\()' % valid_name, Name.Function), + include('statements'), + # blocks: + (r'\{|\}', Punctuation), + # (r'\{', Punctuation, 'block'), + (r'.', Text), + ], + # 'block': [ + # include('statements'), + # ('\{', Punctuation, '#push'), + # ('\}', Punctuation, '#pop') + # ], + 'string_squote': [ + (r'([^\'\\]|\\.)*\'', String, '#pop'), + ], + 'string_dquote': [ + (r'([^"\\]|\\.)*"', String, '#pop'), + ], + } + + def analyse_text(text): + if re.search(r'[a-z0-9_\])\s]<-(?!-)', text): + return 0.11 + + +class RdLexer(RegexLexer): + """ + Pygments Lexer for R documentation (Rd) files + + This is a very minimal implementation, highlighting little more + than the macros. A description of Rd syntax is found in `Writing R + Extensions `_ + and `Parsing Rd files `_. + + .. versionadded:: 1.6 + """ + name = 'Rd' + aliases = ['rd'] + filenames = ['*.Rd'] + mimetypes = ['text/x-r-doc'] + + # To account for verbatim / LaTeX-like / and R-like areas + # would require parsing. + tokens = { + 'root': [ + # catch escaped brackets and percent sign + (r'\\[\\{}%]', String.Escape), + # comments + (r'%.*$', Comment), + # special macros with no arguments + (r'\\(?:cr|l?dots|R|tab)\b', Keyword.Constant), + # macros + (r'\\[a-zA-Z]+\b', Keyword), + # special preprocessor macros + (r'^\s*#(?:ifn?def|endif).*\b', Comment.Preproc), + # non-escaped brackets + (r'[{}]', Name.Builtin), + # everything else + (r'[^\\%\n{}]+', Text), + (r'.', Text), + ] + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/rdf.py b/.venv/lib/python3.8/site-packages/pygments/lexers/rdf.py new file mode 100644 index 00000000..917807aa --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/rdf.py @@ -0,0 +1,463 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.rdf + ~~~~~~~~~~~~~~~~~~~ + + Lexers for semantic web and RDF query languages and markup. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, bygroups, default +from pygments.token import Keyword, Punctuation, String, Number, Operator, Generic, \ + Whitespace, Name, Literal, Comment, Text + +__all__ = ['SparqlLexer', 'TurtleLexer', 'ShExCLexer'] + + +class SparqlLexer(RegexLexer): + """ + Lexer for `SPARQL `_ query language. + + .. versionadded:: 2.0 + """ + name = 'SPARQL' + aliases = ['sparql'] + filenames = ['*.rq', '*.sparql'] + mimetypes = ['application/sparql-query'] + + # character group definitions :: + + PN_CHARS_BASE_GRP = ('a-zA-Z' + '\u00c0-\u00d6' + '\u00d8-\u00f6' + '\u00f8-\u02ff' + '\u0370-\u037d' + '\u037f-\u1fff' + '\u200c-\u200d' + '\u2070-\u218f' + '\u2c00-\u2fef' + '\u3001-\ud7ff' + '\uf900-\ufdcf' + '\ufdf0-\ufffd') + + PN_CHARS_U_GRP = (PN_CHARS_BASE_GRP + '_') + + PN_CHARS_GRP = (PN_CHARS_U_GRP + + r'\-' + + r'0-9' + + '\u00b7' + + '\u0300-\u036f' + + '\u203f-\u2040') + + HEX_GRP = '0-9A-Fa-f' + + PN_LOCAL_ESC_CHARS_GRP = r' _~.\-!$&"()*+,;=/?#@%' + + # terminal productions :: + + PN_CHARS_BASE = '[' + PN_CHARS_BASE_GRP + ']' + + PN_CHARS_U = '[' + PN_CHARS_U_GRP + ']' + + PN_CHARS = '[' + PN_CHARS_GRP + ']' + + HEX = '[' + HEX_GRP + ']' + + PN_LOCAL_ESC_CHARS = '[' + PN_LOCAL_ESC_CHARS_GRP + ']' + + IRIREF = r'<(?:[^<>"{}|^`\\\x00-\x20])*>' + + BLANK_NODE_LABEL = '_:[0-9' + PN_CHARS_U_GRP + '](?:[' + PN_CHARS_GRP + \ + '.]*' + PN_CHARS + ')?' + + PN_PREFIX = PN_CHARS_BASE + '(?:[' + PN_CHARS_GRP + '.]*' + PN_CHARS + ')?' + + VARNAME = '[0-9' + PN_CHARS_U_GRP + '][' + PN_CHARS_U_GRP + \ + '0-9\u00b7\u0300-\u036f\u203f-\u2040]*' + + PERCENT = '%' + HEX + HEX + + PN_LOCAL_ESC = r'\\' + PN_LOCAL_ESC_CHARS + + PLX = '(?:' + PERCENT + ')|(?:' + PN_LOCAL_ESC + ')' + + PN_LOCAL = ('(?:[' + PN_CHARS_U_GRP + ':0-9' + ']|' + PLX + ')' + + '(?:(?:[' + PN_CHARS_GRP + '.:]|' + PLX + ')*(?:[' + + PN_CHARS_GRP + ':]|' + PLX + '))?') + + EXPONENT = r'[eE][+-]?\d+' + + # Lexer token definitions :: + + tokens = { + 'root': [ + (r'\s+', Text), + # keywords :: + (r'(?i)(select|construct|describe|ask|where|filter|group\s+by|minus|' + r'distinct|reduced|from\s+named|from|order\s+by|desc|asc|limit|' + r'offset|values|bindings|load|into|clear|drop|create|add|move|copy|' + r'insert\s+data|delete\s+data|delete\s+where|with|delete|insert|' + r'using\s+named|using|graph|default|named|all|optional|service|' + r'silent|bind|undef|union|not\s+in|in|as|having|to|prefix|base)\b', Keyword), + (r'(a)\b', Keyword), + # IRIs :: + ('(' + IRIREF + ')', Name.Label), + # blank nodes :: + ('(' + BLANK_NODE_LABEL + ')', Name.Label), + # # variables :: + ('[?$]' + VARNAME, Name.Variable), + # prefixed names :: + (r'(' + PN_PREFIX + r')?(\:)(' + PN_LOCAL + r')?', + bygroups(Name.Namespace, Punctuation, Name.Tag)), + # function names :: + (r'(?i)(str|lang|langmatches|datatype|bound|iri|uri|bnode|rand|abs|' + r'ceil|floor|round|concat|strlen|ucase|lcase|encode_for_uri|' + r'contains|strstarts|strends|strbefore|strafter|year|month|day|' + r'hours|minutes|seconds|timezone|tz|now|uuid|struuid|md5|sha1|sha256|sha384|' + r'sha512|coalesce|if|strlang|strdt|sameterm|isiri|isuri|isblank|' + r'isliteral|isnumeric|regex|substr|replace|exists|not\s+exists|' + r'count|sum|min|max|avg|sample|group_concat|separator)\b', + Name.Function), + # boolean literals :: + (r'(true|false)', Keyword.Constant), + # double literals :: + (r'[+\-]?(\d+\.\d*' + EXPONENT + r'|\.?\d+' + EXPONENT + ')', Number.Float), + # decimal literals :: + (r'[+\-]?(\d+\.\d*|\.\d+)', Number.Float), + # integer literals :: + (r'[+\-]?\d+', Number.Integer), + # operators :: + (r'(\|\||&&|=|\*|\-|\+|/|!=|<=|>=|!|<|>)', Operator), + # punctuation characters :: + (r'[(){}.;,:^\[\]]', Punctuation), + # line comments :: + (r'#[^\n]*', Comment), + # strings :: + (r'"""', String, 'triple-double-quoted-string'), + (r'"', String, 'single-double-quoted-string'), + (r"'''", String, 'triple-single-quoted-string'), + (r"'", String, 'single-single-quoted-string'), + ], + 'triple-double-quoted-string': [ + (r'"""', String, 'end-of-string'), + (r'[^\\]+', String), + (r'\\', String, 'string-escape'), + ], + 'single-double-quoted-string': [ + (r'"', String, 'end-of-string'), + (r'[^"\\\n]+', String), + (r'\\', String, 'string-escape'), + ], + 'triple-single-quoted-string': [ + (r"'''", String, 'end-of-string'), + (r'[^\\]+', String), + (r'\\', String.Escape, 'string-escape'), + ], + 'single-single-quoted-string': [ + (r"'", String, 'end-of-string'), + (r"[^'\\\n]+", String), + (r'\\', String, 'string-escape'), + ], + 'string-escape': [ + (r'u' + HEX + '{4}', String.Escape, '#pop'), + (r'U' + HEX + '{8}', String.Escape, '#pop'), + (r'.', String.Escape, '#pop'), + ], + 'end-of-string': [ + (r'(@)([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)', + bygroups(Operator, Name.Function), '#pop:2'), + (r'\^\^', Operator, '#pop:2'), + default('#pop:2'), + ], + } + + +class TurtleLexer(RegexLexer): + """ + Lexer for `Turtle `_ data language. + + .. versionadded:: 2.1 + """ + name = 'Turtle' + aliases = ['turtle'] + filenames = ['*.ttl'] + mimetypes = ['text/turtle', 'application/x-turtle'] + + # character group definitions :: + PN_CHARS_BASE_GRP = ('a-zA-Z' + '\u00c0-\u00d6' + '\u00d8-\u00f6' + '\u00f8-\u02ff' + '\u0370-\u037d' + '\u037f-\u1fff' + '\u200c-\u200d' + '\u2070-\u218f' + '\u2c00-\u2fef' + '\u3001-\ud7ff' + '\uf900-\ufdcf' + '\ufdf0-\ufffd') + + PN_CHARS_U_GRP = (PN_CHARS_BASE_GRP + '_') + + PN_CHARS_GRP = (PN_CHARS_U_GRP + + r'\-' + + r'0-9' + + '\u00b7' + + '\u0300-\u036f' + + '\u203f-\u2040') + + PN_CHARS = '[' + PN_CHARS_GRP + ']' + + PN_CHARS_BASE = '[' + PN_CHARS_BASE_GRP + ']' + + PN_PREFIX = PN_CHARS_BASE + '(?:[' + PN_CHARS_GRP + '.]*' + PN_CHARS + ')?' + + HEX_GRP = '0-9A-Fa-f' + + HEX = '[' + HEX_GRP + ']' + + PERCENT = '%' + HEX + HEX + + PN_LOCAL_ESC_CHARS_GRP = r' _~.\-!$&"()*+,;=/?#@%' + + PN_LOCAL_ESC_CHARS = '[' + PN_LOCAL_ESC_CHARS_GRP + ']' + + PN_LOCAL_ESC = r'\\' + PN_LOCAL_ESC_CHARS + + PLX = '(?:' + PERCENT + ')|(?:' + PN_LOCAL_ESC + ')' + + PN_LOCAL = ('(?:[' + PN_CHARS_U_GRP + ':0-9' + ']|' + PLX + ')' + + '(?:(?:[' + PN_CHARS_GRP + '.:]|' + PLX + ')*(?:[' + + PN_CHARS_GRP + ':]|' + PLX + '))?') + + patterns = { + 'PNAME_NS': r'((?:[a-zA-Z][\w-]*)?\:)', # Simplified character range + 'IRIREF': r'(<[^<>"{}|^`\\\x00-\x20]*>)' + } + + tokens = { + 'root': [ + (r'\s+', Text), + + # Base / prefix + (r'(@base|BASE)(\s+)%(IRIREF)s(\s*)(\.?)' % patterns, + bygroups(Keyword, Whitespace, Name.Variable, Whitespace, + Punctuation)), + (r'(@prefix|PREFIX)(\s+)%(PNAME_NS)s(\s+)%(IRIREF)s(\s*)(\.?)' % patterns, + bygroups(Keyword, Whitespace, Name.Namespace, Whitespace, + Name.Variable, Whitespace, Punctuation)), + + # The shorthand predicate 'a' + (r'(?<=\s)a(?=\s)', Keyword.Type), + + # IRIREF + (r'%(IRIREF)s' % patterns, Name.Variable), + + # PrefixedName + (r'(' + PN_PREFIX + r')?(\:)(' + PN_LOCAL + r')?', + bygroups(Name.Namespace, Punctuation, Name.Tag)), + + # Comment + (r'#[^\n]+', Comment), + + (r'\b(true|false)\b', Literal), + (r'[+\-]?\d*\.\d+', Number.Float), + (r'[+\-]?\d*(:?\.\d+)?E[+\-]?\d+', Number.Float), + (r'[+\-]?\d+', Number.Integer), + (r'[\[\](){}.;,:^]', Punctuation), + + (r'"""', String, 'triple-double-quoted-string'), + (r'"', String, 'single-double-quoted-string'), + (r"'''", String, 'triple-single-quoted-string'), + (r"'", String, 'single-single-quoted-string'), + ], + 'triple-double-quoted-string': [ + (r'"""', String, 'end-of-string'), + (r'[^\\]+', String), + (r'\\', String, 'string-escape'), + ], + 'single-double-quoted-string': [ + (r'"', String, 'end-of-string'), + (r'[^"\\\n]+', String), + (r'\\', String, 'string-escape'), + ], + 'triple-single-quoted-string': [ + (r"'''", String, 'end-of-string'), + (r'[^\\]+', String), + (r'\\', String, 'string-escape'), + ], + 'single-single-quoted-string': [ + (r"'", String, 'end-of-string'), + (r"[^'\\\n]+", String), + (r'\\', String, 'string-escape'), + ], + 'string-escape': [ + (r'.', String, '#pop'), + ], + 'end-of-string': [ + (r'(@)([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)', + bygroups(Operator, Generic.Emph), '#pop:2'), + + (r'(\^\^)%(IRIREF)s' % patterns, bygroups(Operator, Generic.Emph), '#pop:2'), + + default('#pop:2'), + + ], + } + + # Turtle and Tera Term macro files share the same file extension + # but each has a recognizable and distinct syntax. + def analyse_text(text): + for t in ('@base ', 'BASE ', '@prefix ', 'PREFIX '): + if re.search(r'^\s*%s' % t, text): + return 0.80 + + +class ShExCLexer(RegexLexer): + """ + Lexer for `ShExC `_ shape expressions language syntax. + """ + name = 'ShExC' + aliases = ['shexc', 'shex'] + filenames = ['*.shex'] + mimetypes = ['text/shex'] + + # character group definitions :: + + PN_CHARS_BASE_GRP = ('a-zA-Z' + '\u00c0-\u00d6' + '\u00d8-\u00f6' + '\u00f8-\u02ff' + '\u0370-\u037d' + '\u037f-\u1fff' + '\u200c-\u200d' + '\u2070-\u218f' + '\u2c00-\u2fef' + '\u3001-\ud7ff' + '\uf900-\ufdcf' + '\ufdf0-\ufffd') + + PN_CHARS_U_GRP = (PN_CHARS_BASE_GRP + '_') + + PN_CHARS_GRP = (PN_CHARS_U_GRP + + r'\-' + + r'0-9' + + '\u00b7' + + '\u0300-\u036f' + + '\u203f-\u2040') + + HEX_GRP = '0-9A-Fa-f' + + PN_LOCAL_ESC_CHARS_GRP = r"_~.\-!$&'()*+,;=/?#@%" + + # terminal productions :: + + PN_CHARS_BASE = '[' + PN_CHARS_BASE_GRP + ']' + + PN_CHARS_U = '[' + PN_CHARS_U_GRP + ']' + + PN_CHARS = '[' + PN_CHARS_GRP + ']' + + HEX = '[' + HEX_GRP + ']' + + PN_LOCAL_ESC_CHARS = '[' + PN_LOCAL_ESC_CHARS_GRP + ']' + + UCHAR_NO_BACKSLASH = '(?:u' + HEX + '{4}|U' + HEX + '{8})' + + UCHAR = r'\\' + UCHAR_NO_BACKSLASH + + IRIREF = r'<(?:[^\x00-\x20<>"{}|^`\\]|' + UCHAR + ')*>' + + BLANK_NODE_LABEL = '_:[0-9' + PN_CHARS_U_GRP + '](?:[' + PN_CHARS_GRP + \ + '.]*' + PN_CHARS + ')?' + + PN_PREFIX = PN_CHARS_BASE + '(?:[' + PN_CHARS_GRP + '.]*' + PN_CHARS + ')?' + + PERCENT = '%' + HEX + HEX + + PN_LOCAL_ESC = r'\\' + PN_LOCAL_ESC_CHARS + + PLX = '(?:' + PERCENT + ')|(?:' + PN_LOCAL_ESC + ')' + + PN_LOCAL = ('(?:[' + PN_CHARS_U_GRP + ':0-9' + ']|' + PLX + ')' + + '(?:(?:[' + PN_CHARS_GRP + '.:]|' + PLX + ')*(?:[' + + PN_CHARS_GRP + ':]|' + PLX + '))?') + + EXPONENT = r'[eE][+-]?\d+' + + # Lexer token definitions :: + + tokens = { + 'root': [ + (r'\s+', Text), + # keywords :: + (r'(?i)(base|prefix|start|external|' + r'literal|iri|bnode|nonliteral|length|minlength|maxlength|' + r'mininclusive|minexclusive|maxinclusive|maxexclusive|' + r'totaldigits|fractiondigits|' + r'closed|extra)\b', Keyword), + (r'(a)\b', Keyword), + # IRIs :: + ('(' + IRIREF + ')', Name.Label), + # blank nodes :: + ('(' + BLANK_NODE_LABEL + ')', Name.Label), + # prefixed names :: + (r'(' + PN_PREFIX + r')?(\:)(' + PN_LOCAL + ')?', + bygroups(Name.Namespace, Punctuation, Name.Tag)), + # boolean literals :: + (r'(true|false)', Keyword.Constant), + # double literals :: + (r'[+\-]?(\d+\.\d*' + EXPONENT + r'|\.?\d+' + EXPONENT + ')', Number.Float), + # decimal literals :: + (r'[+\-]?(\d+\.\d*|\.\d+)', Number.Float), + # integer literals :: + (r'[+\-]?\d+', Number.Integer), + # operators :: + (r'[@|$&=*+?^\-~]', Operator), + # operator keywords :: + (r'(?i)(and|or|not)\b', Operator.Word), + # punctuation characters :: + (r'[(){}.;,:^\[\]]', Punctuation), + # line comments :: + (r'#[^\n]*', Comment), + # strings :: + (r'"""', String, 'triple-double-quoted-string'), + (r'"', String, 'single-double-quoted-string'), + (r"'''", String, 'triple-single-quoted-string'), + (r"'", String, 'single-single-quoted-string'), + ], + 'triple-double-quoted-string': [ + (r'"""', String, 'end-of-string'), + (r'[^\\]+', String), + (r'\\', String, 'string-escape'), + ], + 'single-double-quoted-string': [ + (r'"', String, 'end-of-string'), + (r'[^"\\\n]+', String), + (r'\\', String, 'string-escape'), + ], + 'triple-single-quoted-string': [ + (r"'''", String, 'end-of-string'), + (r'[^\\]+', String), + (r'\\', String.Escape, 'string-escape'), + ], + 'single-single-quoted-string': [ + (r"'", String, 'end-of-string'), + (r"[^'\\\n]+", String), + (r'\\', String, 'string-escape'), + ], + 'string-escape': [ + (UCHAR_NO_BACKSLASH, String.Escape, '#pop'), + (r'.', String.Escape, '#pop'), + ], + 'end-of-string': [ + (r'(@)([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)', + bygroups(Operator, Name.Function), '#pop:2'), + (r'\^\^', Operator, '#pop:2'), + default('#pop:2'), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/rebol.py b/.venv/lib/python3.8/site-packages/pygments/lexers/rebol.py new file mode 100644 index 00000000..211060b4 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/rebol.py @@ -0,0 +1,431 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.rebol + ~~~~~~~~~~~~~~~~~~~~~ + + Lexers for the REBOL and related languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, bygroups +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Generic, Whitespace + +__all__ = ['RebolLexer', 'RedLexer'] + + +class RebolLexer(RegexLexer): + """ + A `REBOL `_ lexer. + + .. versionadded:: 1.1 + """ + name = 'REBOL' + aliases = ['rebol'] + filenames = ['*.r', '*.r3', '*.reb'] + mimetypes = ['text/x-rebol'] + + flags = re.IGNORECASE | re.MULTILINE + + escape_re = r'(?:\^\([0-9a-f]{1,4}\)*)' + + def word_callback(lexer, match): + word = match.group() + + if re.match(".*:$", word): + yield match.start(), Generic.Subheading, word + elif re.match( + r'(native|alias|all|any|as-string|as-binary|bind|bound\?|case|' + r'catch|checksum|comment|debase|dehex|exclude|difference|disarm|' + r'either|else|enbase|foreach|remove-each|form|free|get|get-env|if|' + r'in|intersect|loop|minimum-of|maximum-of|mold|new-line|' + r'new-line\?|not|now|prin|print|reduce|compose|construct|repeat|' + r'reverse|save|script\?|set|shift|switch|throw|to-hex|trace|try|' + r'type\?|union|unique|unless|unprotect|unset|until|use|value\?|' + r'while|compress|decompress|secure|open|close|read|read-io|' + r'write-io|write|update|query|wait|input\?|exp|log-10|log-2|' + r'log-e|square-root|cosine|sine|tangent|arccosine|arcsine|' + r'arctangent|protect|lowercase|uppercase|entab|detab|connected\?|' + r'browse|launch|stats|get-modes|set-modes|to-local-file|' + r'to-rebol-file|encloak|decloak|create-link|do-browser|bind\?|' + r'hide|draw|show|size-text|textinfo|offset-to-caret|' + r'caret-to-offset|local-request-file|rgb-to-hsv|hsv-to-rgb|' + r'crypt-strength\?|dh-make-key|dh-generate-key|dh-compute-key|' + r'dsa-make-key|dsa-generate-key|dsa-make-signature|' + r'dsa-verify-signature|rsa-make-key|rsa-generate-key|' + r'rsa-encrypt)$', word): + yield match.start(), Name.Builtin, word + elif re.match( + r'(add|subtract|multiply|divide|remainder|power|and~|or~|xor~|' + r'minimum|maximum|negate|complement|absolute|random|head|tail|' + r'next|back|skip|at|pick|first|second|third|fourth|fifth|sixth|' + r'seventh|eighth|ninth|tenth|last|path|find|select|make|to|copy\*|' + r'insert|remove|change|poke|clear|trim|sort|min|max|abs|cp|' + r'copy)$', word): + yield match.start(), Name.Function, word + elif re.match( + r'(error|source|input|license|help|install|echo|Usage|with|func|' + r'throw-on-error|function|does|has|context|probe|\?\?|as-pair|' + r'mod|modulo|round|repend|about|set-net|append|join|rejoin|reform|' + r'remold|charset|array|replace|move|extract|forskip|forall|alter|' + r'first+|also|take|for|forever|dispatch|attempt|what-dir|' + r'change-dir|clean-path|list-dir|dirize|rename|split-path|delete|' + r'make-dir|delete-dir|in-dir|confirm|dump-obj|upgrade|what|' + r'build-tag|process-source|build-markup|decode-cgi|read-cgi|' + r'write-user|save-user|set-user-name|protect-system|parse-xml|' + r'cvs-date|cvs-version|do-boot|get-net-info|desktop|layout|' + r'scroll-para|get-face|alert|set-face|uninstall|unfocus|' + r'request-dir|center-face|do-events|net-error|decode-url|' + r'parse-header|parse-header-date|parse-email-addrs|import-email|' + r'send|build-attach-body|resend|show-popup|hide-popup|open-events|' + r'find-key-face|do-face|viewtop|confine|find-window|' + r'insert-event-func|remove-event-func|inform|dump-pane|dump-face|' + r'flag-face|deflag-face|clear-fields|read-net|vbug|path-thru|' + r'read-thru|load-thru|do-thru|launch-thru|load-image|' + r'request-download|do-face-alt|set-font|set-para|get-style|' + r'set-style|make-face|stylize|choose|hilight-text|hilight-all|' + r'unlight-text|focus|scroll-drag|clear-face|reset-face|scroll-face|' + r'resize-face|load-stock|load-stock-block|notify|request|flash|' + r'request-color|request-pass|request-text|request-list|' + r'request-date|request-file|dbug|editor|link-relative-path|' + r'emailer|parse-error)$', word): + yield match.start(), Keyword.Namespace, word + elif re.match( + r'(halt|quit|do|load|q|recycle|call|run|ask|parse|view|unview|' + r'return|exit|break)$', word): + yield match.start(), Name.Exception, word + elif re.match('REBOL$', word): + yield match.start(), Generic.Heading, word + elif re.match("to-.*", word): + yield match.start(), Keyword, word + elif re.match(r'(\+|-|\*|/|//|\*\*|and|or|xor|=\?|=|==|<>|<|>|<=|>=)$', + word): + yield match.start(), Operator, word + elif re.match(r".*\?$", word): + yield match.start(), Keyword, word + elif re.match(r".*\!$", word): + yield match.start(), Keyword.Type, word + elif re.match("'.*", word): + yield match.start(), Name.Variable.Instance, word # lit-word + elif re.match("#.*", word): + yield match.start(), Name.Label, word # issue + elif re.match("%.*", word): + yield match.start(), Name.Decorator, word # file + else: + yield match.start(), Name.Variable, word + + tokens = { + 'root': [ + (r'[^R]+', Comment), + (r'REBOL\s+\[', Generic.Strong, 'script'), + (r'R', Comment) + ], + 'script': [ + (r'\s+', Text), + (r'#"', String.Char, 'char'), + (r'#\{[0-9a-f]*\}', Number.Hex), + (r'2#\{', Number.Hex, 'bin2'), + (r'64#\{[0-9a-z+/=\s]*\}', Number.Hex), + (r'"', String, 'string'), + (r'\{', String, 'string2'), + (r';#+.*\n', Comment.Special), + (r';\*+.*\n', Comment.Preproc), + (r';.*\n', Comment), + (r'%"', Name.Decorator, 'stringFile'), + (r'%[^(^{")\s\[\]]+', Name.Decorator), + (r'[+-]?([a-z]{1,3})?\$\d+(\.\d+)?', Number.Float), # money + (r'[+-]?\d+\:\d+(\:\d+)?(\.\d+)?', String.Other), # time + (r'\d+[\-/][0-9a-z]+[\-/]\d+(\/\d+\:\d+((\:\d+)?' + r'([.\d+]?([+-]?\d+:\d+)?)?)?)?', String.Other), # date + (r'\d+(\.\d+)+\.\d+', Keyword.Constant), # tuple + (r'\d+X\d+', Keyword.Constant), # pair + (r'[+-]?\d+(\'\d+)?([.,]\d*)?E[+-]?\d+', Number.Float), + (r'[+-]?\d+(\'\d+)?[.,]\d*', Number.Float), + (r'[+-]?\d+(\'\d+)?', Number), + (r'[\[\]()]', Generic.Strong), + (r'[a-z]+[^(^{"\s:)]*://[^(^{"\s)]*', Name.Decorator), # url + (r'mailto:[^(^{"@\s)]+@[^(^{"@\s)]+', Name.Decorator), # url + (r'[^(^{"@\s)]+@[^(^{"@\s)]+', Name.Decorator), # email + (r'comment\s"', Comment, 'commentString1'), + (r'comment\s\{', Comment, 'commentString2'), + (r'comment\s\[', Comment, 'commentBlock'), + (r'comment\s[^(\s{"\[]+', Comment), + (r'/[^(^{")\s/[\]]*', Name.Attribute), + (r'([^(^{")\s/[\]]+)(?=[:({"\s/\[\]])', word_callback), + (r'<[\w:.-]*>', Name.Tag), + (r'<[^(<>\s")]+', Name.Tag, 'tag'), + (r'([^(^{")\s]+)', Text), + ], + 'string': [ + (r'[^(^")]+', String), + (escape_re, String.Escape), + (r'[(|)]+', String), + (r'\^.', String.Escape), + (r'"', String, '#pop'), + ], + 'string2': [ + (r'[^(^{})]+', String), + (escape_re, String.Escape), + (r'[(|)]+', String), + (r'\^.', String.Escape), + (r'\{', String, '#push'), + (r'\}', String, '#pop'), + ], + 'stringFile': [ + (r'[^(^")]+', Name.Decorator), + (escape_re, Name.Decorator), + (r'\^.', Name.Decorator), + (r'"', Name.Decorator, '#pop'), + ], + 'char': [ + (escape_re + '"', String.Char, '#pop'), + (r'\^."', String.Char, '#pop'), + (r'."', String.Char, '#pop'), + ], + 'tag': [ + (escape_re, Name.Tag), + (r'"', Name.Tag, 'tagString'), + (r'[^(<>\r\n")]+', Name.Tag), + (r'>', Name.Tag, '#pop'), + ], + 'tagString': [ + (r'[^(^")]+', Name.Tag), + (escape_re, Name.Tag), + (r'[(|)]+', Name.Tag), + (r'\^.', Name.Tag), + (r'"', Name.Tag, '#pop'), + ], + 'tuple': [ + (r'(\d+\.)+', Keyword.Constant), + (r'\d+', Keyword.Constant, '#pop'), + ], + 'bin2': [ + (r'\s+', Number.Hex), + (r'([01]\s*){8}', Number.Hex), + (r'\}', Number.Hex, '#pop'), + ], + 'commentString1': [ + (r'[^(^")]+', Comment), + (escape_re, Comment), + (r'[(|)]+', Comment), + (r'\^.', Comment), + (r'"', Comment, '#pop'), + ], + 'commentString2': [ + (r'[^(^{})]+', Comment), + (escape_re, Comment), + (r'[(|)]+', Comment), + (r'\^.', Comment), + (r'\{', Comment, '#push'), + (r'\}', Comment, '#pop'), + ], + 'commentBlock': [ + (r'\[', Comment, '#push'), + (r'\]', Comment, '#pop'), + (r'"', Comment, "commentString1"), + (r'\{', Comment, "commentString2"), + (r'[^(\[\]"{)]+', Comment), + ], + } + + def analyse_text(text): + """ + Check if code contains REBOL header and so it probably not R code + """ + if re.match(r'^\s*REBOL\s*\[', text, re.IGNORECASE): + # The code starts with REBOL header + return 1.0 + elif re.search(r'\s*REBOL\s*\[', text, re.IGNORECASE): + # The code contains REBOL header but also some text before it + return 0.5 + + +class RedLexer(RegexLexer): + """ + A `Red-language `_ lexer. + + .. versionadded:: 2.0 + """ + name = 'Red' + aliases = ['red', 'red/system'] + filenames = ['*.red', '*.reds'] + mimetypes = ['text/x-red', 'text/x-red-system'] + + flags = re.IGNORECASE | re.MULTILINE + + escape_re = r'(?:\^\([0-9a-f]{1,4}\)*)' + + def word_callback(lexer, match): + word = match.group() + + if re.match(".*:$", word): + yield match.start(), Generic.Subheading, word + elif re.match(r'(if|unless|either|any|all|while|until|loop|repeat|' + r'foreach|forall|func|function|does|has|switch|' + r'case|reduce|compose|get|set|print|prin|equal\?|' + r'not-equal\?|strict-equal\?|lesser\?|greater\?|lesser-or-equal\?|' + r'greater-or-equal\?|same\?|not|type\?|stats|' + r'bind|union|replace|charset|routine)$', word): + yield match.start(), Name.Builtin, word + elif re.match(r'(make|random|reflect|to|form|mold|absolute|add|divide|multiply|negate|' + r'power|remainder|round|subtract|even\?|odd\?|and~|complement|or~|xor~|' + r'append|at|back|change|clear|copy|find|head|head\?|index\?|insert|' + r'length\?|next|pick|poke|remove|reverse|select|sort|skip|swap|tail|tail\?|' + r'take|trim|create|close|delete|modify|open|open\?|query|read|rename|' + r'update|write)$', word): + yield match.start(), Name.Function, word + elif re.match(r'(yes|on|no|off|true|false|tab|cr|lf|newline|escape|slash|sp|space|null|' + r'none|crlf|dot|null-byte)$', word): + yield match.start(), Name.Builtin.Pseudo, word + elif re.match(r'(#system-global|#include|#enum|#define|#either|#if|#import|#export|' + r'#switch|#default|#get-definition)$', word): + yield match.start(), Keyword.Namespace, word + elif re.match(r'(system|halt|quit|quit-return|do|load|q|recycle|call|run|ask|parse|' + r'raise-error|return|exit|break|alias|push|pop|probe|\?\?|spec-of|body-of|' + r'quote|forever)$', word): + yield match.start(), Name.Exception, word + elif re.match(r'(action\?|block\?|char\?|datatype\?|file\?|function\?|get-path\?|zero\?|' + r'get-word\?|integer\?|issue\?|lit-path\?|lit-word\?|logic\?|native\?|' + r'op\?|paren\?|path\?|refinement\?|set-path\?|set-word\?|string\?|unset\?|' + r'any-struct\?|none\?|word\?|any-series\?)$', word): + yield match.start(), Keyword, word + elif re.match(r'(JNICALL|stdcall|cdecl|infix)$', word): + yield match.start(), Keyword.Namespace, word + elif re.match("to-.*", word): + yield match.start(), Keyword, word + elif re.match(r'(\+|-\*\*|-|\*\*|//|/|\*|and|or|xor|=\?|===|==|=|<>|<=|>=|' + r'<<<|>>>|<<|>>|<|>%)$', word): + yield match.start(), Operator, word + elif re.match(r".*\!$", word): + yield match.start(), Keyword.Type, word + elif re.match("'.*", word): + yield match.start(), Name.Variable.Instance, word # lit-word + elif re.match("#.*", word): + yield match.start(), Name.Label, word # issue + elif re.match("%.*", word): + yield match.start(), Name.Decorator, word # file + elif re.match(":.*", word): + yield match.start(), Generic.Subheading, word # get-word + else: + yield match.start(), Name.Variable, word + + tokens = { + 'root': [ + (r'[^R]+', Comment), + (r'Red/System\s+\[', Generic.Strong, 'script'), + (r'Red\s+\[', Generic.Strong, 'script'), + (r'R', Comment) + ], + 'script': [ + (r'\s+', Text), + (r'#"', String.Char, 'char'), + (r'#\{[0-9a-f\s]*\}', Number.Hex), + (r'2#\{', Number.Hex, 'bin2'), + (r'64#\{[0-9a-z+/=\s]*\}', Number.Hex), + (r'([0-9a-f]+)(h)((\s)|(?=[\[\]{}"()]))', + bygroups(Number.Hex, Name.Variable, Whitespace)), + (r'"', String, 'string'), + (r'\{', String, 'string2'), + (r';#+.*\n', Comment.Special), + (r';\*+.*\n', Comment.Preproc), + (r';.*\n', Comment), + (r'%"', Name.Decorator, 'stringFile'), + (r'%[^(^{")\s\[\]]+', Name.Decorator), + (r'[+-]?([a-z]{1,3})?\$\d+(\.\d+)?', Number.Float), # money + (r'[+-]?\d+\:\d+(\:\d+)?(\.\d+)?', String.Other), # time + (r'\d+[\-/][0-9a-z]+[\-/]\d+(/\d+:\d+((:\d+)?' + r'([\.\d+]?([+-]?\d+:\d+)?)?)?)?', String.Other), # date + (r'\d+(\.\d+)+\.\d+', Keyword.Constant), # tuple + (r'\d+X\d+', Keyword.Constant), # pair + (r'[+-]?\d+(\'\d+)?([.,]\d*)?E[+-]?\d+', Number.Float), + (r'[+-]?\d+(\'\d+)?[.,]\d*', Number.Float), + (r'[+-]?\d+(\'\d+)?', Number), + (r'[\[\]()]', Generic.Strong), + (r'[a-z]+[^(^{"\s:)]*://[^(^{"\s)]*', Name.Decorator), # url + (r'mailto:[^(^{"@\s)]+@[^(^{"@\s)]+', Name.Decorator), # url + (r'[^(^{"@\s)]+@[^(^{"@\s)]+', Name.Decorator), # email + (r'comment\s"', Comment, 'commentString1'), + (r'comment\s\{', Comment, 'commentString2'), + (r'comment\s\[', Comment, 'commentBlock'), + (r'comment\s[^(\s{"\[]+', Comment), + (r'/[^(^{^")\s/[\]]*', Name.Attribute), + (r'([^(^{^")\s/[\]]+)(?=[:({"\s/\[\]])', word_callback), + (r'<[\w:.-]*>', Name.Tag), + (r'<[^(<>\s")]+', Name.Tag, 'tag'), + (r'([^(^{")\s]+)', Text), + ], + 'string': [ + (r'[^(^")]+', String), + (escape_re, String.Escape), + (r'[(|)]+', String), + (r'\^.', String.Escape), + (r'"', String, '#pop'), + ], + 'string2': [ + (r'[^(^{})]+', String), + (escape_re, String.Escape), + (r'[(|)]+', String), + (r'\^.', String.Escape), + (r'\{', String, '#push'), + (r'\}', String, '#pop'), + ], + 'stringFile': [ + (r'[^(^")]+', Name.Decorator), + (escape_re, Name.Decorator), + (r'\^.', Name.Decorator), + (r'"', Name.Decorator, '#pop'), + ], + 'char': [ + (escape_re + '"', String.Char, '#pop'), + (r'\^."', String.Char, '#pop'), + (r'."', String.Char, '#pop'), + ], + 'tag': [ + (escape_re, Name.Tag), + (r'"', Name.Tag, 'tagString'), + (r'[^(<>\r\n")]+', Name.Tag), + (r'>', Name.Tag, '#pop'), + ], + 'tagString': [ + (r'[^(^")]+', Name.Tag), + (escape_re, Name.Tag), + (r'[(|)]+', Name.Tag), + (r'\^.', Name.Tag), + (r'"', Name.Tag, '#pop'), + ], + 'tuple': [ + (r'(\d+\.)+', Keyword.Constant), + (r'\d+', Keyword.Constant, '#pop'), + ], + 'bin2': [ + (r'\s+', Number.Hex), + (r'([01]\s*){8}', Number.Hex), + (r'\}', Number.Hex, '#pop'), + ], + 'commentString1': [ + (r'[^(^")]+', Comment), + (escape_re, Comment), + (r'[(|)]+', Comment), + (r'\^.', Comment), + (r'"', Comment, '#pop'), + ], + 'commentString2': [ + (r'[^(^{})]+', Comment), + (escape_re, Comment), + (r'[(|)]+', Comment), + (r'\^.', Comment), + (r'\{', Comment, '#push'), + (r'\}', Comment, '#pop'), + ], + 'commentBlock': [ + (r'\[', Comment, '#push'), + (r'\]', Comment, '#pop'), + (r'"', Comment, "commentString1"), + (r'\{', Comment, "commentString2"), + (r'[^(\[\]"{)]+', Comment), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/resource.py b/.venv/lib/python3.8/site-packages/pygments/lexers/resource.py new file mode 100644 index 00000000..28dff49c --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/resource.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.resource + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for resource definition files. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, bygroups, words +from pygments.token import Comment, String, Number, Operator, Text, \ + Keyword, Name + +__all__ = ['ResourceLexer'] + + +class ResourceLexer(RegexLexer): + """Lexer for `ICU Resource bundles + `_. + + .. versionadded:: 2.0 + """ + name = 'ResourceBundle' + aliases = ['resource', 'resourcebundle'] + filenames = [] + + _types = (':table', ':array', ':string', ':bin', ':import', ':intvector', + ':int', ':alias') + + flags = re.MULTILINE | re.IGNORECASE + tokens = { + 'root': [ + (r'//.*?$', Comment), + (r'"', String, 'string'), + (r'-?\d+', Number.Integer), + (r'[,{}]', Operator), + (r'([^\s{:]+)(\s*)(%s?)' % '|'.join(_types), + bygroups(Name, Text, Keyword)), + (r'\s+', Text), + (words(_types), Keyword), + ], + 'string': [ + (r'(\\x[0-9a-f]{2}|\\u[0-9a-f]{4}|\\U00[0-9a-f]{6}|' + r'\\[0-7]{1,3}|\\c.|\\[abtnvfre\'"?\\]|\\\{|[^"{\\])+', String), + (r'\{', String.Escape, 'msgname'), + (r'"', String, '#pop') + ], + 'msgname': [ + (r'([^{},]+)(\s*)', bygroups(Name, String.Escape), ('#pop', 'message')) + ], + 'message': [ + (r'\{', String.Escape, 'msgname'), + (r'\}', String.Escape, '#pop'), + (r'(,)(\s*)([a-z]+)(\s*\})', + bygroups(Operator, String.Escape, Keyword, String.Escape), '#pop'), + (r'(,)(\s*)([a-z]+)(\s*)(,)(\s*)(offset)(\s*)(:)(\s*)(-?\d+)(\s*)', + bygroups(Operator, String.Escape, Keyword, String.Escape, Operator, + String.Escape, Operator.Word, String.Escape, Operator, + String.Escape, Number.Integer, String.Escape), 'choice'), + (r'(,)(\s*)([a-z]+)(\s*)(,)(\s*)', + bygroups(Operator, String.Escape, Keyword, String.Escape, Operator, + String.Escape), 'choice'), + (r'\s+', String.Escape) + ], + 'choice': [ + (r'(=|<|>|<=|>=|!=)(-?\d+)(\s*\{)', + bygroups(Operator, Number.Integer, String.Escape), 'message'), + (r'([a-z]+)(\s*\{)', bygroups(Keyword.Type, String.Escape), 'str'), + (r'\}', String.Escape, ('#pop', '#pop')), + (r'\s+', String.Escape) + ], + 'str': [ + (r'\}', String.Escape, '#pop'), + (r'\{', String.Escape, 'msgname'), + (r'[^{}]+', String) + ] + } + + def analyse_text(text): + if text.startswith('root:table'): + return 1.0 diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/ride.py b/.venv/lib/python3.8/site-packages/pygments/lexers/ride.py new file mode 100644 index 00000000..490d1e07 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/ride.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.ride + ~~~~~~~~~~~~~~~~~~~~ + + Lexer for the Ride programming language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, words, include +from pygments.token import Comment, Keyword, Name, Number, Punctuation, String, Text + +__all__ = ['RideLexer'] + + +class RideLexer(RegexLexer): + """ + For `Ride `_ + source code. + + .. versionadded:: 2.6 + """ + + name = 'Ride' + aliases = ['ride'] + filenames = ['*.ride'] + mimetypes = ['text/x-ride'] + + validName = r'[a-zA-Z_][a-zA-Z0-9_\']*' + + builtinOps = ( + '||', '|', '>=', '>', '==', '!', + '=', '<=', '<', '::', ':+', ':', '!=', '/', + '.', '=>', '-', '+', '*', '&&', '%', '++', + ) + + globalVariablesName = ( + 'NOALG', 'MD5', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512', + 'SHA3224', 'SHA3256', 'SHA3384', 'SHA3512', 'nil', 'this', 'unit', + 'height', 'lastBlock', 'Buy', 'Sell', 'CEILING', 'FLOOR', 'DOWN', + 'HALFDOWN', 'HALFEVEN', 'HALFUP', 'UP', + ) + + typesName = ( + 'Unit', 'Int', 'Boolean', 'ByteVector', 'String', 'Address', 'Alias', + 'Transfer', 'AssetPair', 'DataEntry', 'Order', 'Transaction', + 'GenesisTransaction', 'PaymentTransaction', 'ReissueTransaction', + 'BurnTransaction', 'MassTransferTransaction', 'ExchangeTransaction', + 'TransferTransaction', 'SetAssetScriptTransaction', + 'InvokeScriptTransaction', 'IssueTransaction', 'LeaseTransaction', + 'LeaseCancelTransaction', 'CreateAliasTransaction', + 'SetScriptTransaction', 'SponsorFeeTransaction', 'DataTransaction', + 'WriteSet', 'AttachedPayment', 'ScriptTransfer', 'TransferSet', + 'ScriptResult', 'Invocation', 'Asset', 'BlockInfo', 'Issue', 'Reissue', + 'Burn', 'NoAlg', 'Md5', 'Sha1', 'Sha224', 'Sha256', 'Sha384', 'Sha512', + 'Sha3224', 'Sha3256', 'Sha3384', 'Sha3512', 'BinaryEntry', + 'BooleanEntry', 'IntegerEntry', 'StringEntry', 'List', 'Ceiling', + 'Down', 'Floor', 'HalfDown', 'HalfEven', 'HalfUp', 'Up', + ) + + functionsName = ( + 'fraction', 'size', 'toBytes', 'take', 'drop', 'takeRight', 'dropRight', + 'toString', 'isDefined', 'extract', 'throw', 'getElement', 'value', + 'cons', 'toUtf8String', 'toInt', 'indexOf', 'lastIndexOf', 'split', + 'parseInt', 'parseIntValue', 'keccak256', 'blake2b256', 'sha256', + 'sigVerify', 'toBase58String', 'fromBase58String', 'toBase64String', + 'fromBase64String', 'transactionById', 'transactionHeightById', + 'getInteger', 'getBoolean', 'getBinary', 'getString', + 'addressFromPublicKey', 'addressFromString', 'addressFromRecipient', + 'assetBalance', 'wavesBalance', 'getIntegerValue', 'getBooleanValue', + 'getBinaryValue', 'getStringValue', 'addressFromStringValue', + 'assetInfo', 'rsaVerify', 'checkMerkleProof', 'median', + 'valueOrElse', 'valueOrErrorMessage', 'contains', 'log', 'pow', + 'toBase16String', 'fromBase16String', 'blockInfoByHeight', + 'transferTransactionById', + ) + + reservedWords = words(( + 'match', 'case', 'else', 'func', 'if', + 'let', 'then', '@Callable', '@Verifier', + ), suffix=r'\b') + + tokens = { + 'root': [ + # Comments + (r'#.*', Comment.Single), + # Whitespace + (r'\s+', Text), + # Strings + (r'"', String, 'doublequote'), + (r'utf8\'', String, 'utf8quote'), + (r'base(58|64|16)\'', String, 'singlequote'), + # Keywords + (reservedWords, Keyword.Reserved), + (r'\{-#.*?#-\}', Keyword.Reserved), + (r'FOLD<\d+>', Keyword.Reserved), + # Types + (words(typesName), Keyword.Type), + # Main + # (specialName, Keyword.Reserved), + # Prefix Operators + (words(builtinOps, prefix=r'\(', suffix=r'\)'), Name.Function), + # Infix Operators + (words(builtinOps), Name.Function), + (words(globalVariablesName), Name.Function), + (words(functionsName), Name.Function), + # Numbers + include('numbers'), + # Variable Names + (validName, Name.Variable), + # Parens + (r'[,()\[\]{}]', Punctuation), + ], + + 'doublequote': [ + (r'\\u[0-9a-fA-F]{4}', String.Escape), + (r'\\[nrfvb\\"]', String.Escape), + (r'[^"]', String), + (r'"', String, '#pop'), + ], + + 'utf8quote': [ + (r'\\u[0-9a-fA-F]{4}', String.Escape), + (r'\\[nrfvb\\\']', String.Escape), + (r'[^\']', String), + (r'\'', String, '#pop'), + ], + + 'singlequote': [ + (r'[^\']', String), + (r'\'', String, '#pop'), + ], + + 'numbers': [ + (r'_?\d+', Number.Integer), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/rnc.py b/.venv/lib/python3.8/site-packages/pygments/lexers/rnc.py new file mode 100644 index 00000000..9fb8ab52 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/rnc.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.rnc + ~~~~~~~~~~~~~~~~~~~ + + Lexer for Relax-NG Compact syntax + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Punctuation + +__all__ = ['RNCCompactLexer'] + + +class RNCCompactLexer(RegexLexer): + """ + For `RelaxNG-compact `_ syntax. + + .. versionadded:: 2.2 + """ + + name = 'Relax-NG Compact' + aliases = ['rnc', 'rng-compact'] + filenames = ['*.rnc'] + + tokens = { + 'root': [ + (r'namespace\b', Keyword.Namespace), + (r'(?:default|datatypes)\b', Keyword.Declaration), + (r'##.*$', Comment.Preproc), + (r'#.*$', Comment.Single), + (r'"[^"]*"', String.Double), + # TODO single quoted strings and escape sequences outside of + # double-quoted strings + (r'(?:element|attribute|mixed)\b', Keyword.Declaration, 'variable'), + (r'(text\b|xsd:[^ ]+)', Keyword.Type, 'maybe_xsdattributes'), + (r'[,?&*=|~]|>>', Operator), + (r'[(){}]', Punctuation), + (r'.', Text), + ], + + # a variable has been declared using `element` or `attribute` + 'variable': [ + (r'[^{]+', Name.Variable), + (r'\{', Punctuation, '#pop'), + ], + + # after an xsd: declaration there may be attributes + 'maybe_xsdattributes': [ + (r'\{', Punctuation, 'xsdattributes'), + (r'\}', Punctuation, '#pop'), + (r'.', Text), + ], + + # attributes take the form { key1 = value1 key2 = value2 ... } + 'xsdattributes': [ + (r'[^ =}]', Name.Attribute), + (r'=', Operator), + (r'"[^"]*"', String.Double), + (r'\}', Punctuation, '#pop'), + (r'.', Text), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/roboconf.py b/.venv/lib/python3.8/site-packages/pygments/lexers/roboconf.py new file mode 100644 index 00000000..2419ee51 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/roboconf.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.roboconf + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Roboconf DSL. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, words, re +from pygments.token import Text, Operator, Keyword, Name, Comment + +__all__ = ['RoboconfGraphLexer', 'RoboconfInstancesLexer'] + + +class RoboconfGraphLexer(RegexLexer): + """ + Lexer for `Roboconf `_ graph files. + + .. versionadded:: 2.1 + """ + name = 'Roboconf Graph' + aliases = ['roboconf-graph'] + filenames = ['*.graph'] + + flags = re.IGNORECASE | re.MULTILINE + tokens = { + 'root': [ + # Skip white spaces + (r'\s+', Text), + + # There is one operator + (r'=', Operator), + + # Keywords + (words(('facet', 'import'), suffix=r'\s*\b', prefix=r'\b'), Keyword), + (words(( + 'installer', 'extends', 'exports', 'imports', 'facets', + 'children'), suffix=r'\s*:?', prefix=r'\b'), Name), + + # Comments + (r'#.*\n', Comment), + + # Default + (r'[^#]', Text), + (r'.*\n', Text) + ] + } + + +class RoboconfInstancesLexer(RegexLexer): + """ + Lexer for `Roboconf `_ instances files. + + .. versionadded:: 2.1 + """ + name = 'Roboconf Instances' + aliases = ['roboconf-instances'] + filenames = ['*.instances'] + + flags = re.IGNORECASE | re.MULTILINE + tokens = { + 'root': [ + + # Skip white spaces + (r'\s+', Text), + + # Keywords + (words(('instance of', 'import'), suffix=r'\s*\b', prefix=r'\b'), Keyword), + (words(('name', 'count'), suffix=r's*:?', prefix=r'\b'), Name), + (r'\s*[\w.-]+\s*:', Name), + + # Comments + (r'#.*\n', Comment), + + # Default + (r'[^#]', Text), + (r'.*\n', Text) + ] + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/robotframework.py b/.venv/lib/python3.8/site-packages/pygments/lexers/robotframework.py new file mode 100644 index 00000000..cd808292 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/robotframework.py @@ -0,0 +1,552 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.robotframework + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for Robot Framework. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +# Copyright 2012 Nokia Siemens Networks Oyj +# +# 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 re + +from pygments.lexer import Lexer +from pygments.token import Token + +__all__ = ['RobotFrameworkLexer'] + + +HEADING = Token.Generic.Heading +SETTING = Token.Keyword.Namespace +IMPORT = Token.Name.Namespace +TC_KW_NAME = Token.Generic.Subheading +KEYWORD = Token.Name.Function +ARGUMENT = Token.String +VARIABLE = Token.Name.Variable +COMMENT = Token.Comment +SEPARATOR = Token.Punctuation +SYNTAX = Token.Punctuation +GHERKIN = Token.Generic.Emph +ERROR = Token.Error + + +def normalize(string, remove=''): + string = string.lower() + for char in remove + ' ': + if char in string: + string = string.replace(char, '') + return string + + +class RobotFrameworkLexer(Lexer): + """ + For `Robot Framework `_ test data. + + Supports both space and pipe separated plain text formats. + + .. versionadded:: 1.6 + """ + name = 'RobotFramework' + aliases = ['robotframework'] + filenames = ['*.robot'] + mimetypes = ['text/x-robotframework'] + + def __init__(self, **options): + options['tabsize'] = 2 + options['encoding'] = 'UTF-8' + Lexer.__init__(self, **options) + + def get_tokens_unprocessed(self, text): + row_tokenizer = RowTokenizer() + var_tokenizer = VariableTokenizer() + index = 0 + for row in text.splitlines(): + for value, token in row_tokenizer.tokenize(row): + for value, token in var_tokenizer.tokenize(value, token): + if value: + yield index, token, str(value) + index += len(value) + + +class VariableTokenizer: + + def tokenize(self, string, token): + var = VariableSplitter(string, identifiers='$@%&') + if var.start < 0 or token in (COMMENT, ERROR): + yield string, token + return + for value, token in self._tokenize(var, string, token): + if value: + yield value, token + + def _tokenize(self, var, string, orig_token): + before = string[:var.start] + yield before, orig_token + yield var.identifier + '{', SYNTAX + yield from self.tokenize(var.base, VARIABLE) + yield '}', SYNTAX + if var.index: + yield '[', SYNTAX + yield from self.tokenize(var.index, VARIABLE) + yield ']', SYNTAX + yield from self.tokenize(string[var.end:], orig_token) + + +class RowTokenizer: + + def __init__(self): + self._table = UnknownTable() + self._splitter = RowSplitter() + testcases = TestCaseTable() + settings = SettingTable(testcases.set_default_template) + variables = VariableTable() + keywords = KeywordTable() + self._tables = {'settings': settings, 'setting': settings, + 'metadata': settings, + 'variables': variables, 'variable': variables, + 'testcases': testcases, 'testcase': testcases, + 'tasks': testcases, 'task': testcases, + 'keywords': keywords, 'keyword': keywords, + 'userkeywords': keywords, 'userkeyword': keywords} + + def tokenize(self, row): + commented = False + heading = False + for index, value in enumerate(self._splitter.split(row)): + # First value, and every second after that, is a separator. + index, separator = divmod(index-1, 2) + if value.startswith('#'): + commented = True + elif index == 0 and value.startswith('*'): + self._table = self._start_table(value) + heading = True + yield from self._tokenize(value, index, commented, + separator, heading) + self._table.end_row() + + def _start_table(self, header): + name = normalize(header, remove='*') + return self._tables.get(name, UnknownTable()) + + def _tokenize(self, value, index, commented, separator, heading): + if commented: + yield value, COMMENT + elif separator: + yield value, SEPARATOR + elif heading: + yield value, HEADING + else: + yield from self._table.tokenize(value, index) + + +class RowSplitter: + _space_splitter = re.compile('( {2,})') + _pipe_splitter = re.compile(r'((?:^| +)\|(?: +|$))') + + def split(self, row): + splitter = (row.startswith('| ') and self._split_from_pipes + or self._split_from_spaces) + yield from splitter(row) + yield '\n' + + def _split_from_spaces(self, row): + yield '' # Start with (pseudo)separator similarly as with pipes + yield from self._space_splitter.split(row) + + def _split_from_pipes(self, row): + _, separator, rest = self._pipe_splitter.split(row, 1) + yield separator + while self._pipe_splitter.search(rest): + cell, separator, rest = self._pipe_splitter.split(rest, 1) + yield cell + yield separator + yield rest + + +class Tokenizer: + _tokens = None + + def __init__(self): + self._index = 0 + + def tokenize(self, value): + values_and_tokens = self._tokenize(value, self._index) + self._index += 1 + if isinstance(values_and_tokens, type(Token)): + values_and_tokens = [(value, values_and_tokens)] + return values_and_tokens + + def _tokenize(self, value, index): + index = min(index, len(self._tokens) - 1) + return self._tokens[index] + + def _is_assign(self, value): + if value.endswith('='): + value = value[:-1].strip() + var = VariableSplitter(value, identifiers='$@&') + return var.start == 0 and var.end == len(value) + + +class Comment(Tokenizer): + _tokens = (COMMENT,) + + +class Setting(Tokenizer): + _tokens = (SETTING, ARGUMENT) + _keyword_settings = ('suitesetup', 'suiteprecondition', 'suiteteardown', + 'suitepostcondition', 'testsetup', 'tasksetup', 'testprecondition', + 'testteardown','taskteardown', 'testpostcondition', 'testtemplate', 'tasktemplate') + _import_settings = ('library', 'resource', 'variables') + _other_settings = ('documentation', 'metadata', 'forcetags', 'defaulttags', + 'testtimeout','tasktimeout') + _custom_tokenizer = None + + def __init__(self, template_setter=None): + Tokenizer.__init__(self) + self._template_setter = template_setter + + def _tokenize(self, value, index): + if index == 1 and self._template_setter: + self._template_setter(value) + if index == 0: + normalized = normalize(value) + if normalized in self._keyword_settings: + self._custom_tokenizer = KeywordCall(support_assign=False) + elif normalized in self._import_settings: + self._custom_tokenizer = ImportSetting() + elif normalized not in self._other_settings: + return ERROR + elif self._custom_tokenizer: + return self._custom_tokenizer.tokenize(value) + return Tokenizer._tokenize(self, value, index) + + +class ImportSetting(Tokenizer): + _tokens = (IMPORT, ARGUMENT) + + +class TestCaseSetting(Setting): + _keyword_settings = ('setup', 'precondition', 'teardown', 'postcondition', + 'template') + _import_settings = () + _other_settings = ('documentation', 'tags', 'timeout') + + def _tokenize(self, value, index): + if index == 0: + type = Setting._tokenize(self, value[1:-1], index) + return [('[', SYNTAX), (value[1:-1], type), (']', SYNTAX)] + return Setting._tokenize(self, value, index) + + +class KeywordSetting(TestCaseSetting): + _keyword_settings = ('teardown',) + _other_settings = ('documentation', 'arguments', 'return', 'timeout', 'tags') + + +class Variable(Tokenizer): + _tokens = (SYNTAX, ARGUMENT) + + def _tokenize(self, value, index): + if index == 0 and not self._is_assign(value): + return ERROR + return Tokenizer._tokenize(self, value, index) + + +class KeywordCall(Tokenizer): + _tokens = (KEYWORD, ARGUMENT) + + def __init__(self, support_assign=True): + Tokenizer.__init__(self) + self._keyword_found = not support_assign + self._assigns = 0 + + def _tokenize(self, value, index): + if not self._keyword_found and self._is_assign(value): + self._assigns += 1 + return SYNTAX # VariableTokenizer tokenizes this later. + if self._keyword_found: + return Tokenizer._tokenize(self, value, index - self._assigns) + self._keyword_found = True + return GherkinTokenizer().tokenize(value, KEYWORD) + + +class GherkinTokenizer: + _gherkin_prefix = re.compile('^(Given|When|Then|And) ', re.IGNORECASE) + + def tokenize(self, value, token): + match = self._gherkin_prefix.match(value) + if not match: + return [(value, token)] + end = match.end() + return [(value[:end], GHERKIN), (value[end:], token)] + + +class TemplatedKeywordCall(Tokenizer): + _tokens = (ARGUMENT,) + + +class ForLoop(Tokenizer): + + def __init__(self): + Tokenizer.__init__(self) + self._in_arguments = False + + def _tokenize(self, value, index): + token = self._in_arguments and ARGUMENT or SYNTAX + if value.upper() in ('IN', 'IN RANGE'): + self._in_arguments = True + return token + + +class _Table: + _tokenizer_class = None + + def __init__(self, prev_tokenizer=None): + self._tokenizer = self._tokenizer_class() + self._prev_tokenizer = prev_tokenizer + self._prev_values_on_row = [] + + def tokenize(self, value, index): + if self._continues(value, index): + self._tokenizer = self._prev_tokenizer + yield value, SYNTAX + else: + yield from self._tokenize(value, index) + self._prev_values_on_row.append(value) + + def _continues(self, value, index): + return value == '...' and all(self._is_empty(t) + for t in self._prev_values_on_row) + + def _is_empty(self, value): + return value in ('', '\\') + + def _tokenize(self, value, index): + return self._tokenizer.tokenize(value) + + def end_row(self): + self.__init__(prev_tokenizer=self._tokenizer) + + +class UnknownTable(_Table): + _tokenizer_class = Comment + + def _continues(self, value, index): + return False + + +class VariableTable(_Table): + _tokenizer_class = Variable + + +class SettingTable(_Table): + _tokenizer_class = Setting + + def __init__(self, template_setter, prev_tokenizer=None): + _Table.__init__(self, prev_tokenizer) + self._template_setter = template_setter + + def _tokenize(self, value, index): + if index == 0 and normalize(value) == 'testtemplate': + self._tokenizer = Setting(self._template_setter) + return _Table._tokenize(self, value, index) + + def end_row(self): + self.__init__(self._template_setter, prev_tokenizer=self._tokenizer) + + +class TestCaseTable(_Table): + _setting_class = TestCaseSetting + _test_template = None + _default_template = None + + @property + def _tokenizer_class(self): + if self._test_template or (self._default_template and + self._test_template is not False): + return TemplatedKeywordCall + return KeywordCall + + def _continues(self, value, index): + return index > 0 and _Table._continues(self, value, index) + + def _tokenize(self, value, index): + if index == 0: + if value: + self._test_template = None + return GherkinTokenizer().tokenize(value, TC_KW_NAME) + if index == 1 and self._is_setting(value): + if self._is_template(value): + self._test_template = False + self._tokenizer = self._setting_class(self.set_test_template) + else: + self._tokenizer = self._setting_class() + if index == 1 and self._is_for_loop(value): + self._tokenizer = ForLoop() + if index == 1 and self._is_empty(value): + return [(value, SYNTAX)] + return _Table._tokenize(self, value, index) + + def _is_setting(self, value): + return value.startswith('[') and value.endswith(']') + + def _is_template(self, value): + return normalize(value) == '[template]' + + def _is_for_loop(self, value): + return value.startswith(':') and normalize(value, remove=':') == 'for' + + def set_test_template(self, template): + self._test_template = self._is_template_set(template) + + def set_default_template(self, template): + self._default_template = self._is_template_set(template) + + def _is_template_set(self, template): + return normalize(template) not in ('', '\\', 'none', '${empty}') + + +class KeywordTable(TestCaseTable): + _tokenizer_class = KeywordCall + _setting_class = KeywordSetting + + def _is_template(self, value): + return False + + +# Following code copied directly from Robot Framework 2.7.5. + +class VariableSplitter: + + def __init__(self, string, identifiers): + self.identifier = None + self.base = None + self.index = None + self.start = -1 + self.end = -1 + self._identifiers = identifiers + self._may_have_internal_variables = False + try: + self._split(string) + except ValueError: + pass + else: + self._finalize() + + def get_replaced_base(self, variables): + if self._may_have_internal_variables: + return variables.replace_string(self.base) + return self.base + + def _finalize(self): + self.identifier = self._variable_chars[0] + self.base = ''.join(self._variable_chars[2:-1]) + self.end = self.start + len(self._variable_chars) + if self._has_list_or_dict_variable_index(): + self.index = ''.join(self._list_and_dict_variable_index_chars[1:-1]) + self.end += len(self._list_and_dict_variable_index_chars) + + def _has_list_or_dict_variable_index(self): + return self._list_and_dict_variable_index_chars\ + and self._list_and_dict_variable_index_chars[-1] == ']' + + def _split(self, string): + start_index, max_index = self._find_variable(string) + self.start = start_index + self._open_curly = 1 + self._state = self._variable_state + self._variable_chars = [string[start_index], '{'] + self._list_and_dict_variable_index_chars = [] + self._string = string + start_index += 2 + for index, char in enumerate(string[start_index:]): + index += start_index # Giving start to enumerate only in Py 2.6+ + try: + self._state(char, index) + except StopIteration: + return + if index == max_index and not self._scanning_list_variable_index(): + return + + def _scanning_list_variable_index(self): + return self._state in [self._waiting_list_variable_index_state, + self._list_variable_index_state] + + def _find_variable(self, string): + max_end_index = string.rfind('}') + if max_end_index == -1: + raise ValueError('No variable end found') + if self._is_escaped(string, max_end_index): + return self._find_variable(string[:max_end_index]) + start_index = self._find_start_index(string, 1, max_end_index) + if start_index == -1: + raise ValueError('No variable start found') + return start_index, max_end_index + + def _find_start_index(self, string, start, end): + index = string.find('{', start, end) - 1 + if index < 0: + return -1 + if self._start_index_is_ok(string, index): + return index + return self._find_start_index(string, index+2, end) + + def _start_index_is_ok(self, string, index): + return string[index] in self._identifiers\ + and not self._is_escaped(string, index) + + def _is_escaped(self, string, index): + escaped = False + while index > 0 and string[index-1] == '\\': + index -= 1 + escaped = not escaped + return escaped + + def _variable_state(self, char, index): + self._variable_chars.append(char) + if char == '}' and not self._is_escaped(self._string, index): + self._open_curly -= 1 + if self._open_curly == 0: + if not self._is_list_or_dict_variable(): + raise StopIteration + self._state = self._waiting_list_variable_index_state + elif char in self._identifiers: + self._state = self._internal_variable_start_state + + def _is_list_or_dict_variable(self): + return self._variable_chars[0] in ('@','&') + + def _internal_variable_start_state(self, char, index): + self._state = self._variable_state + if char == '{': + self._variable_chars.append(char) + self._open_curly += 1 + self._may_have_internal_variables = True + else: + self._variable_state(char, index) + + def _waiting_list_variable_index_state(self, char, index): + if char != '[': + raise StopIteration + self._list_and_dict_variable_index_chars.append(char) + self._state = self._list_variable_index_state + + def _list_variable_index_state(self, char, index): + self._list_and_dict_variable_index_chars.append(char) + if char == ']': + raise StopIteration diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/ruby.py b/.venv/lib/python3.8/site-packages/pygments/lexers/ruby.py new file mode 100644 index 00000000..e16cd711 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/ruby.py @@ -0,0 +1,517 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.ruby + ~~~~~~~~~~~~~~~~~~~~ + + Lexers for Ruby and related languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import Lexer, RegexLexer, ExtendedRegexLexer, include, \ + bygroups, default, LexerContext, do_insertions, words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Error, Generic +from pygments.util import shebang_matches + +__all__ = ['RubyLexer', 'RubyConsoleLexer', 'FancyLexer'] + +line_re = re.compile('.*?\n') + + +RUBY_OPERATORS = ( + '*', '**', '-', '+', '-@', '+@', '/', '%', '&', '|', '^', '`', '~', + '[]', '[]=', '<<', '>>', '<', '<>', '<=>', '>', '>=', '==', '===' +) + + +class RubyLexer(ExtendedRegexLexer): + """ + For `Ruby `_ source code. + """ + + name = 'Ruby' + aliases = ['rb', 'ruby', 'duby'] + filenames = ['*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec', + '*.rbx', '*.duby', 'Gemfile'] + mimetypes = ['text/x-ruby', 'application/x-ruby'] + + flags = re.DOTALL | re.MULTILINE + + def heredoc_callback(self, match, ctx): + # okay, this is the hardest part of parsing Ruby... + # match: 1 = <<[-~]?, 2 = quote? 3 = name 4 = quote? 5 = rest of line + + start = match.start(1) + yield start, Operator, match.group(1) # <<[-~]? + yield match.start(2), String.Heredoc, match.group(2) # quote ", ', ` + yield match.start(3), String.Delimiter, match.group(3) # heredoc name + yield match.start(4), String.Heredoc, match.group(4) # quote again + + heredocstack = ctx.__dict__.setdefault('heredocstack', []) + outermost = not bool(heredocstack) + heredocstack.append((match.group(1) in ('<<-', '<<~'), match.group(3))) + + ctx.pos = match.start(5) + ctx.end = match.end(5) + # this may find other heredocs + yield from self.get_tokens_unprocessed(context=ctx) + ctx.pos = match.end() + + if outermost: + # this is the outer heredoc again, now we can process them all + for tolerant, hdname in heredocstack: + lines = [] + for match in line_re.finditer(ctx.text, ctx.pos): + if tolerant: + check = match.group().strip() + else: + check = match.group().rstrip() + if check == hdname: + for amatch in lines: + yield amatch.start(), String.Heredoc, amatch.group() + yield match.start(), String.Delimiter, match.group() + ctx.pos = match.end() + break + else: + lines.append(match) + else: + # end of heredoc not found -- error! + for amatch in lines: + yield amatch.start(), Error, amatch.group() + ctx.end = len(ctx.text) + del heredocstack[:] + + def gen_rubystrings_rules(): + def intp_regex_callback(self, match, ctx): + yield match.start(1), String.Regex, match.group(1) # begin + nctx = LexerContext(match.group(3), 0, ['interpolated-regex']) + for i, t, v in self.get_tokens_unprocessed(context=nctx): + yield match.start(3)+i, t, v + yield match.start(4), String.Regex, match.group(4) # end[mixounse]* + ctx.pos = match.end() + + def intp_string_callback(self, match, ctx): + yield match.start(1), String.Other, match.group(1) + nctx = LexerContext(match.group(3), 0, ['interpolated-string']) + for i, t, v in self.get_tokens_unprocessed(context=nctx): + yield match.start(3)+i, t, v + yield match.start(4), String.Other, match.group(4) # end + ctx.pos = match.end() + + states = {} + states['strings'] = [ + # easy ones + (r'\:@{0,2}[a-zA-Z_]\w*[!?]?', String.Symbol), + (words(RUBY_OPERATORS, prefix=r'\:@{0,2}'), String.Symbol), + (r":'(\\\\|\\'|[^'])*'", String.Symbol), + (r':"', String.Symbol, 'simple-sym'), + (r'([a-zA-Z_]\w*)(:)(?!:)', + bygroups(String.Symbol, Punctuation)), # Since Ruby 1.9 + (r'"', String.Double, 'simple-string-double'), + (r"'", String.Single, 'simple-string-single'), + (r'(?', '<>', 'ab'): + states[name+'-intp-string'] = [ + (r'\\[\\' + bracecc + ']', String.Other), + (lbrace, String.Other, '#push'), + (rbrace, String.Other, '#pop'), + include('string-intp-escaped'), + (r'[\\#' + bracecc + ']', String.Other), + (r'[^\\#' + bracecc + ']+', String.Other), + ] + states['strings'].append((r'%[QWx]?' + lbrace, String.Other, + name+'-intp-string')) + states[name+'-string'] = [ + (r'\\[\\' + bracecc + ']', String.Other), + (lbrace, String.Other, '#push'), + (rbrace, String.Other, '#pop'), + (r'[\\#' + bracecc + ']', String.Other), + (r'[^\\#' + bracecc + ']+', String.Other), + ] + states['strings'].append((r'%[qsw]' + lbrace, String.Other, + name+'-string')) + states[name+'-regex'] = [ + (r'\\[\\' + bracecc + ']', String.Regex), + (lbrace, String.Regex, '#push'), + (rbrace + '[mixounse]*', String.Regex, '#pop'), + include('string-intp'), + (r'[\\#' + bracecc + ']', String.Regex), + (r'[^\\#' + bracecc + ']+', String.Regex), + ] + states['strings'].append((r'%r' + lbrace, String.Regex, + name+'-regex')) + + # these must come after %! + states['strings'] += [ + # %r regex + (r'(%r([\W_]))((?:\\\2|(?!\2).)*)(\2[mixounse]*)', + intp_regex_callback), + # regular fancy strings with qsw + (r'%[qsw]([\W_])((?:\\\1|(?!\1).)*)\1', String.Other), + (r'(%[QWx]([\W_]))((?:\\\2|(?!\2).)*)(\2)', + intp_string_callback), + # special forms of fancy strings after operators or + # in method calls with braces + (r'(?<=[-+/*%=<>&!^|~,(])(\s*)(%([\t ])(?:(?:\\\3|(?!\3).)*)\3)', + bygroups(Text, String.Other, None)), + # and because of fixed width lookbehinds the whole thing a + # second time for line startings... + (r'^(\s*)(%([\t ])(?:(?:\\\3|(?!\3).)*)\3)', + bygroups(Text, String.Other, None)), + # all regular fancy strings without qsw + (r'(%([^a-zA-Z0-9\s]))((?:\\\2|(?!\2).)*)(\2)', + intp_string_callback), + ] + + return states + + tokens = { + 'root': [ + (r'\A#!.+?$', Comment.Hashbang), + (r'#.*?$', Comment.Single), + (r'=begin\s.*?\n=end.*?$', Comment.Multiline), + # keywords + (words(( + 'BEGIN', 'END', 'alias', 'begin', 'break', 'case', 'defined?', + 'do', 'else', 'elsif', 'end', 'ensure', 'for', 'if', 'in', 'next', 'redo', + 'rescue', 'raise', 'retry', 'return', 'super', 'then', 'undef', + 'unless', 'until', 'when', 'while', 'yield'), suffix=r'\b'), + Keyword), + # start of function, class and module names + (r'(module)(\s+)([a-zA-Z_]\w*' + r'(?:::[a-zA-Z_]\w*)*)', + bygroups(Keyword, Text, Name.Namespace)), + (r'(def)(\s+)', bygroups(Keyword, Text), 'funcname'), + (r'def(?=[*%&^`~+-/\[<>=])', Keyword, 'funcname'), + (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'), + # special methods + (words(( + 'initialize', 'new', 'loop', 'include', 'extend', 'raise', 'attr_reader', + 'attr_writer', 'attr_accessor', 'attr', 'catch', 'throw', 'private', + 'module_function', 'public', 'protected', 'true', 'false', 'nil'), + suffix=r'\b'), + Keyword.Pseudo), + (r'(not|and|or)\b', Operator.Word), + (words(( + 'autoload', 'block_given', 'const_defined', 'eql', 'equal', 'frozen', 'include', + 'instance_of', 'is_a', 'iterator', 'kind_of', 'method_defined', 'nil', + 'private_method_defined', 'protected_method_defined', + 'public_method_defined', 'respond_to', 'tainted'), suffix=r'\?'), + Name.Builtin), + (r'(chomp|chop|exit|gsub|sub)!', Name.Builtin), + (words(( + 'Array', 'Float', 'Integer', 'String', '__id__', '__send__', 'abort', + 'ancestors', 'at_exit', 'autoload', 'binding', 'callcc', 'caller', + 'catch', 'chomp', 'chop', 'class_eval', 'class_variables', + 'clone', 'const_defined?', 'const_get', 'const_missing', 'const_set', + 'constants', 'display', 'dup', 'eval', 'exec', 'exit', 'extend', 'fail', 'fork', + 'format', 'freeze', 'getc', 'gets', 'global_variables', 'gsub', + 'hash', 'id', 'included_modules', 'inspect', 'instance_eval', + 'instance_method', 'instance_methods', + 'instance_variable_get', 'instance_variable_set', 'instance_variables', + 'lambda', 'load', 'local_variables', 'loop', + 'method', 'method_missing', 'methods', 'module_eval', 'name', + 'object_id', 'open', 'p', 'print', 'printf', 'private_class_method', + 'private_instance_methods', + 'private_methods', 'proc', 'protected_instance_methods', + 'protected_methods', 'public_class_method', + 'public_instance_methods', 'public_methods', + 'putc', 'puts', 'raise', 'rand', 'readline', 'readlines', 'require', + 'scan', 'select', 'self', 'send', 'set_trace_func', 'singleton_methods', 'sleep', + 'split', 'sprintf', 'srand', 'sub', 'syscall', 'system', 'taint', + 'test', 'throw', 'to_a', 'to_s', 'trace_var', 'trap', 'untaint', + 'untrace_var', 'warn'), prefix=r'(?~!:])|' + r'(?<=(?:\s|;)when\s)|' + r'(?<=(?:\s|;)or\s)|' + r'(?<=(?:\s|;)and\s)|' + r'(?<=\.index\s)|' + r'(?<=\.scan\s)|' + r'(?<=\.sub\s)|' + r'(?<=\.sub!\s)|' + r'(?<=\.gsub\s)|' + r'(?<=\.gsub!\s)|' + r'(?<=\.match\s)|' + r'(?<=(?:\s|;)if\s)|' + r'(?<=(?:\s|;)elsif\s)|' + r'(?<=^when\s)|' + r'(?<=^index\s)|' + r'(?<=^scan\s)|' + r'(?<=^sub\s)|' + r'(?<=^gsub\s)|' + r'(?<=^sub!\s)|' + r'(?<=^gsub!\s)|' + r'(?<=^match\s)|' + r'(?<=^if\s)|' + r'(?<=^elsif\s)' + r')(\s*)(/)', bygroups(Text, String.Regex), 'multiline-regex'), + # multiline regex (in method calls or subscripts) + (r'(?<=\(|,|\[)/', String.Regex, 'multiline-regex'), + # multiline regex (this time the funny no whitespace rule) + (r'(\s+)(/)(?![\s=])', bygroups(Text, String.Regex), + 'multiline-regex'), + # lex numbers and ignore following regular expressions which + # are division operators in fact (grrrr. i hate that. any + # better ideas?) + # since pygments 0.7 we also eat a "?" operator after numbers + # so that the char operator does not work. Chars are not allowed + # there so that you can use the ternary operator. + # stupid example: + # x>=0?n[x]:"" + (r'(0_?[0-7]+(?:_[0-7]+)*)(\s*)([/?])?', + bygroups(Number.Oct, Text, Operator)), + (r'(0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)([/?])?', + bygroups(Number.Hex, Text, Operator)), + (r'(0b[01]+(?:_[01]+)*)(\s*)([/?])?', + bygroups(Number.Bin, Text, Operator)), + (r'([\d]+(?:_\d+)*)(\s*)([/?])?', + bygroups(Number.Integer, Text, Operator)), + # Names + (r'@@[a-zA-Z_]\w*', Name.Variable.Class), + (r'@[a-zA-Z_]\w*', Name.Variable.Instance), + (r'\$\w+', Name.Variable.Global), + (r'\$[!@&`\'+~=/\\,;.<>_*$?:"^-]', Name.Variable.Global), + (r'\$-[0adFiIlpvw]', Name.Variable.Global), + (r'::', Operator), + include('strings'), + # chars + (r'\?(\\[MC]-)*' # modifiers + r'(\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})|\S)' + r'(?!\w)', + String.Char), + (r'[A-Z]\w+', Name.Constant), + # this is needed because ruby attributes can look + # like keywords (class) or like this: ` ?!? + (words(RUBY_OPERATORS, prefix=r'(\.|::)'), + bygroups(Operator, Name.Operator)), + (r'(\.|::)([a-zA-Z_]\w*[!?]?|[*%&^`~+\-/\[<>=])', + bygroups(Operator, Name)), + (r'[a-zA-Z_]\w*[!?]?', Name), + (r'(\[|\]|\*\*|<>?|>=|<=|<=>|=~|={3}|' + r'!~|&&?|\|\||\.{1,3})', Operator), + (r'[-+/*%=<>&!^|~]=?', Operator), + (r'[(){};,/?:\\]', Punctuation), + (r'\s+', Text) + ], + 'funcname': [ + (r'\(', Punctuation, 'defexpr'), + (r'(?:([a-zA-Z_]\w*)(\.))?' + r'([a-zA-Z_]\w*[!?]?|\*\*?|[-+]@?|' + r'[/%&|^`~]|\[\]=?|<<|>>|<=?>|>=?|===?)', + bygroups(Name.Class, Operator, Name.Function), '#pop'), + default('#pop') + ], + 'classname': [ + (r'\(', Punctuation, 'defexpr'), + (r'<<', Operator, '#pop'), + (r'[A-Z_]\w*', Name.Class, '#pop'), + default('#pop') + ], + 'defexpr': [ + (r'(\))(\.|::)?', bygroups(Punctuation, Operator), '#pop'), + (r'\(', Operator, '#push'), + include('root') + ], + 'in-intp': [ + (r'\{', String.Interpol, '#push'), + (r'\}', String.Interpol, '#pop'), + include('root'), + ], + 'string-intp': [ + (r'#\{', String.Interpol, 'in-intp'), + (r'#@@?[a-zA-Z_]\w*', String.Interpol), + (r'#\$[a-zA-Z_]\w*', String.Interpol) + ], + 'string-intp-escaped': [ + include('string-intp'), + (r'\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})', + String.Escape) + ], + 'interpolated-regex': [ + include('string-intp'), + (r'[\\#]', String.Regex), + (r'[^\\#]+', String.Regex), + ], + 'interpolated-string': [ + include('string-intp'), + (r'[\\#]', String.Other), + (r'[^\\#]+', String.Other), + ], + 'multiline-regex': [ + include('string-intp'), + (r'\\\\', String.Regex), + (r'\\/', String.Regex), + (r'[\\#]', String.Regex), + (r'[^\\/#]+', String.Regex), + (r'/[mixounse]*', String.Regex, '#pop'), + ], + 'end-part': [ + (r'.+', Comment.Preproc, '#pop') + ] + } + tokens.update(gen_rubystrings_rules()) + + def analyse_text(text): + return shebang_matches(text, r'ruby(1\.\d)?') + + +class RubyConsoleLexer(Lexer): + """ + For Ruby interactive console (**irb**) output like: + + .. sourcecode:: rbcon + + irb(main):001:0> a = 1 + => 1 + irb(main):002:0> puts a + 1 + => nil + """ + name = 'Ruby irb session' + aliases = ['rbcon', 'irb'] + mimetypes = ['text/x-ruby-shellsession'] + + _prompt_re = re.compile(r'irb\([a-zA-Z_]\w*\):\d{3}:\d+[>*"\'] ' + r'|>> |\?> ') + + def get_tokens_unprocessed(self, text): + rblexer = RubyLexer(**self.options) + + curcode = '' + insertions = [] + for match in line_re.finditer(text): + line = match.group() + m = self._prompt_re.match(line) + if m is not None: + end = m.end() + insertions.append((len(curcode), + [(0, Generic.Prompt, line[:end])])) + curcode += line[end:] + else: + if curcode: + yield from do_insertions( + insertions, rblexer.get_tokens_unprocessed(curcode)) + curcode = '' + insertions = [] + yield match.start(), Generic.Output, line + if curcode: + yield from do_insertions( + insertions, rblexer.get_tokens_unprocessed(curcode)) + + +class FancyLexer(RegexLexer): + """ + Pygments Lexer For `Fancy `_. + + Fancy is a self-hosted, pure object-oriented, dynamic, + class-based, concurrent general-purpose programming language + running on Rubinius, the Ruby VM. + + .. versionadded:: 1.5 + """ + name = 'Fancy' + filenames = ['*.fy', '*.fancypack'] + aliases = ['fancy', 'fy'] + mimetypes = ['text/x-fancysrc'] + + tokens = { + # copied from PerlLexer: + 'balanced-regex': [ + (r'/(\\\\|\\/|[^/])*/[egimosx]*', String.Regex, '#pop'), + (r'!(\\\\|\\!|[^!])*![egimosx]*', String.Regex, '#pop'), + (r'\\(\\\\|[^\\])*\\[egimosx]*', String.Regex, '#pop'), + (r'\{(\\\\|\\\}|[^}])*\}[egimosx]*', String.Regex, '#pop'), + (r'<(\\\\|\\>|[^>])*>[egimosx]*', String.Regex, '#pop'), + (r'\[(\\\\|\\\]|[^\]])*\][egimosx]*', String.Regex, '#pop'), + (r'\((\\\\|\\\)|[^)])*\)[egimosx]*', String.Regex, '#pop'), + (r'@(\\\\|\\@|[^@])*@[egimosx]*', String.Regex, '#pop'), + (r'%(\\\\|\\%|[^%])*%[egimosx]*', String.Regex, '#pop'), + (r'\$(\\\\|\\\$|[^$])*\$[egimosx]*', String.Regex, '#pop'), + ], + 'root': [ + (r'\s+', Text), + + # balanced delimiters (copied from PerlLexer): + (r's\{(\\\\|\\\}|[^}])*\}\s*', String.Regex, 'balanced-regex'), + (r's<(\\\\|\\>|[^>])*>\s*', String.Regex, 'balanced-regex'), + (r's\[(\\\\|\\\]|[^\]])*\]\s*', String.Regex, 'balanced-regex'), + (r's\((\\\\|\\\)|[^)])*\)\s*', String.Regex, 'balanced-regex'), + (r'm?/(\\\\|\\/|[^/\n])*/[gcimosx]*', String.Regex), + (r'm(?=[/!\\{<\[(@%$])', String.Regex, 'balanced-regex'), + + # Comments + (r'#(.*?)\n', Comment.Single), + # Symbols + (r'\'([^\'\s\[\](){}]+|\[\])', String.Symbol), + # Multi-line DoubleQuotedString + (r'"""(\\\\|\\"|[^"])*"""', String), + # DoubleQuotedString + (r'"(\\\\|\\"|[^"])*"', String), + # keywords + (r'(def|class|try|catch|finally|retry|return|return_local|match|' + r'case|->|=>)\b', Keyword), + # constants + (r'(self|super|nil|false|true)\b', Name.Constant), + (r'[(){};,/?|:\\]', Punctuation), + # names + (words(( + 'Object', 'Array', 'Hash', 'Directory', 'File', 'Class', 'String', + 'Number', 'Enumerable', 'FancyEnumerable', 'Block', 'TrueClass', + 'NilClass', 'FalseClass', 'Tuple', 'Symbol', 'Stack', 'Set', + 'FancySpec', 'Method', 'Package', 'Range'), suffix=r'\b'), + Name.Builtin), + # functions + (r'[a-zA-Z](\w|[-+?!=*/^><%])*:', Name.Function), + # operators, must be below functions + (r'[-+*/~,<>=&!?%^\[\].$]+', Operator), + (r'[A-Z]\w*', Name.Constant), + (r'@[a-zA-Z_]\w*', Name.Variable.Instance), + (r'@@[a-zA-Z_]\w*', Name.Variable.Class), + ('@@?', Operator), + (r'[a-zA-Z_]\w*', Name), + # numbers - / checks are necessary to avoid mismarking regexes, + # see comment in RubyLexer + (r'(0[oO]?[0-7]+(?:_[0-7]+)*)(\s*)([/?])?', + bygroups(Number.Oct, Text, Operator)), + (r'(0[xX][0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)([/?])?', + bygroups(Number.Hex, Text, Operator)), + (r'(0[bB][01]+(?:_[01]+)*)(\s*)([/?])?', + bygroups(Number.Bin, Text, Operator)), + (r'([\d]+(?:_\d+)*)(\s*)([/?])?', + bygroups(Number.Integer, Text, Operator)), + (r'\d+([eE][+-]?[0-9]+)|\d+\.\d+([eE][+-]?[0-9]+)?', Number.Float), + (r'\d+', Number.Integer) + ] + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/rust.py b/.venv/lib/python3.8/site-packages/pygments/lexers/rust.py new file mode 100644 index 00000000..70f4b8da --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/rust.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.rust + ~~~~~~~~~~~~~~~~~~~~ + + Lexers for the Rust language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, include, bygroups, words, default +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Whitespace + +__all__ = ['RustLexer'] + + +class RustLexer(RegexLexer): + """ + Lexer for the Rust programming language (version 1.47). + + .. versionadded:: 1.6 + """ + name = 'Rust' + filenames = ['*.rs', '*.rs.in'] + aliases = ['rust', 'rs'] + mimetypes = ['text/rust', 'text/x-rust'] + + keyword_types = (words(( + 'u8', 'u16', 'u32', 'u64', 'u128', 'i8', 'i16', 'i32', 'i64', 'i128', + 'usize', 'isize', 'f32', 'f64', 'char', 'str', 'bool', + ), suffix=r'\b'), Keyword.Type) + + builtin_funcs_types = (words(( + 'Copy', 'Send', 'Sized', 'Sync', 'Unpin', + 'Drop', 'Fn', 'FnMut', 'FnOnce', 'drop', + 'Box', 'ToOwned', 'Clone', + 'PartialEq', 'PartialOrd', 'Eq', 'Ord', + 'AsRef', 'AsMut', 'Into', 'From', 'Default', + 'Iterator', 'Extend', 'IntoIterator', 'DoubleEndedIterator', + 'ExactSizeIterator', + 'Option', 'Some', 'None', + 'Result', 'Ok', 'Err', + 'String', 'ToString', 'Vec', + ), suffix=r'\b'), Name.Builtin) + + builtin_macros = (words(( + 'asm', 'assert', 'assert_eq', 'assert_ne', 'cfg', 'column', + 'compile_error', 'concat', 'concat_idents', 'dbg', 'debug_assert', + 'debug_assert_eq', 'debug_assert_ne', 'env', 'eprint', 'eprintln', + 'file', 'format', 'format_args', 'format_args_nl', 'global_asm', + 'include', 'include_bytes', 'include_str', + 'is_aarch64_feature_detected', + 'is_arm_feature_detected', + 'is_mips64_feature_detected', + 'is_mips_feature_detected', + 'is_powerpc64_feature_detected', + 'is_powerpc_feature_detected', + 'is_x86_feature_detected', + 'line', 'llvm_asm', 'log_syntax', 'macro_rules', 'matches', + 'module_path', 'option_env', 'panic', 'print', 'println', 'stringify', + 'thread_local', 'todo', 'trace_macros', 'unimplemented', 'unreachable', + 'vec', 'write', 'writeln', + ), suffix=r'!'), Name.Function.Magic) + + tokens = { + 'root': [ + # rust allows a file to start with a shebang, but if the first line + # starts with #![ then it's not a shebang but a crate attribute. + (r'#![^[\r\n].*$', Comment.Preproc), + default('base'), + ], + 'base': [ + # Whitespace and Comments + (r'\n', Whitespace), + (r'\s+', Whitespace), + (r'//!.*?\n', String.Doc), + (r'///(\n|[^/].*?\n)', String.Doc), + (r'//(.*?)\n', Comment.Single), + (r'/\*\*(\n|[^/*])', String.Doc, 'doccomment'), + (r'/\*!', String.Doc, 'doccomment'), + (r'/\*', Comment.Multiline, 'comment'), + + # Macro parameters + (r"""\$([a-zA-Z_]\w*|\(,?|\),?|,?)""", Comment.Preproc), + # Keywords + (words(('as', 'async', 'await', 'box', 'const', 'crate', 'dyn', + 'else', 'extern', 'for', 'if', 'impl', 'in', 'loop', + 'match', 'move', 'mut', 'pub', 'ref', 'return', 'static', + 'super', 'trait', 'unsafe', 'use', 'where', 'while'), + suffix=r'\b'), Keyword), + (words(('abstract', 'become', 'do', 'final', 'macro', 'override', + 'priv', 'typeof', 'try', 'unsized', 'virtual', 'yield'), + suffix=r'\b'), Keyword.Reserved), + (r'(true|false)\b', Keyword.Constant), + (r'self\b', Name.Builtin.Pseudo), + (r'mod\b', Keyword, 'modname'), + (r'let\b', Keyword.Declaration), + (r'fn\b', Keyword, 'funcname'), + (r'(struct|enum|type|union)\b', Keyword, 'typename'), + (r'(default)(\s+)(type|fn)\b', bygroups(Keyword, Text, Keyword)), + keyword_types, + (r'[sS]elf\b', Name.Builtin.Pseudo), + # Prelude (taken from Rust's src/libstd/prelude.rs) + builtin_funcs_types, + builtin_macros, + # Path seperators, so types don't catch them. + (r'::\b', Text), + # Types in positions. + (r'(?::|->)', Text, 'typename'), + # Labels + (r'(break|continue)(\s*)(\'[A-Za-z_]\w*)?', + bygroups(Keyword, Text.Whitespace, Name.Label)), + + # Character literals + (r"""'(\\['"\\nrt]|\\x[0-7][0-9a-fA-F]|\\0""" + r"""|\\u\{[0-9a-fA-F]{1,6}\}|.)'""", + String.Char), + (r"""b'(\\['"\\nrt]|\\x[0-9a-fA-F]{2}|\\0""" + r"""|\\u\{[0-9a-fA-F]{1,6}\}|.)'""", + String.Char), + + # Binary literals + (r'0b[01_]+', Number.Bin, 'number_lit'), + # Octal literals + (r'0o[0-7_]+', Number.Oct, 'number_lit'), + # Hexadecimal literals + (r'0[xX][0-9a-fA-F_]+', Number.Hex, 'number_lit'), + # Decimal literals + (r'[0-9][0-9_]*(\.[0-9_]+[eE][+\-]?[0-9_]+|' + r'\.[0-9_]*(?!\.)|[eE][+\-]?[0-9_]+)', Number.Float, + 'number_lit'), + (r'[0-9][0-9_]*', Number.Integer, 'number_lit'), + + # String literals + (r'b"', String, 'bytestring'), + (r'"', String, 'string'), + (r'b?r(#*)".*?"\1', String), + + # Lifetime names + (r"'", Operator, 'lifetime'), + + # Operators and Punctuation + (r'\.\.=?', Operator), + (r'[{}()\[\],.;]', Punctuation), + (r'[+\-*/%&|<>^!~@=:?]', Operator), + + # Identifiers + (r'[a-zA-Z_]\w*', Name), + # Raw identifiers + (r'r#[a-zA-Z_]\w*', Name), + + # Attributes + (r'#!?\[', Comment.Preproc, 'attribute['), + ], + 'comment': [ + (r'[^*/]+', Comment.Multiline), + (r'/\*', Comment.Multiline, '#push'), + (r'\*/', Comment.Multiline, '#pop'), + (r'[*/]', Comment.Multiline), + ], + 'doccomment': [ + (r'[^*/]+', String.Doc), + (r'/\*', String.Doc, '#push'), + (r'\*/', String.Doc, '#pop'), + (r'[*/]', String.Doc), + ], + 'modname': [ + (r'\s+', Text), + (r'[a-zA-Z_]\w*', Name.Namespace, '#pop'), + default('#pop'), + ], + 'funcname': [ + (r'\s+', Text), + (r'[a-zA-Z_]\w*', Name.Function, '#pop'), + default('#pop'), + ], + 'typename': [ + (r'\s+', Text), + (r'&', Keyword.Pseudo), + (r"'", Operator, 'lifetime'), + builtin_funcs_types, + keyword_types, + (r'[a-zA-Z_]\w*', Name.Class, '#pop'), + default('#pop'), + ], + 'lifetime': [ + (r"(static|_)", Name.Builtin), + (r"[a-zA-Z_]+\w*", Name.Attribute), + default('#pop'), + ], + 'number_lit': [ + (r'[ui](8|16|32|64|size)', Keyword, '#pop'), + (r'f(32|64)', Keyword, '#pop'), + default('#pop'), + ], + 'string': [ + (r'"', String, '#pop'), + (r"""\\['"\\nrt]|\\x[0-7][0-9a-fA-F]|\\0""" + r"""|\\u\{[0-9a-fA-F]{1,6}\}""", String.Escape), + (r'[^\\"]+', String), + (r'\\', String), + ], + 'bytestring': [ + (r"""\\x[89a-fA-F][0-9a-fA-F]""", String.Escape), + include('string'), + ], + 'attribute_common': [ + (r'"', String, 'string'), + (r'\[', Comment.Preproc, 'attribute['), + (r'\(', Comment.Preproc, 'attribute('), + ], + 'attribute[': [ + include('attribute_common'), + (r'\];?', Comment.Preproc, '#pop'), + (r'[^"\]]+', Comment.Preproc), + ], + 'attribute(': [ + include('attribute_common'), + (r'\);?', Comment.Preproc, '#pop'), + (r'[^")]+', Comment.Preproc), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/sas.py b/.venv/lib/python3.8/site-packages/pygments/lexers/sas.py new file mode 100644 index 00000000..85b07adb --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/sas.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.sas + ~~~~~~~~~~~~~~~~~~~ + + Lexer for SAS. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +from pygments.lexer import RegexLexer, include, words +from pygments.token import Comment, Keyword, Name, Number, String, Text, \ + Other, Generic + +__all__ = ['SASLexer'] + + +class SASLexer(RegexLexer): + """ + For `SAS `_ files. + + .. versionadded:: 2.2 + """ + # Syntax from syntax/sas.vim by James Kidd + + name = 'SAS' + aliases = ['sas'] + filenames = ['*.SAS', '*.sas'] + mimetypes = ['text/x-sas', 'text/sas', 'application/x-sas'] + flags = re.IGNORECASE | re.MULTILINE + + builtins_macros = ( + "bquote", "nrbquote", "cmpres", "qcmpres", "compstor", "datatyp", + "display", "do", "else", "end", "eval", "global", "goto", "if", + "index", "input", "keydef", "label", "left", "length", "let", + "local", "lowcase", "macro", "mend", "nrquote", + "nrstr", "put", "qleft", "qlowcase", "qscan", + "qsubstr", "qsysfunc", "qtrim", "quote", "qupcase", "scan", + "str", "substr", "superq", "syscall", "sysevalf", "sysexec", + "sysfunc", "sysget", "syslput", "sysprod", "sysrc", "sysrput", + "then", "to", "trim", "unquote", "until", "upcase", "verify", + "while", "window" + ) + + builtins_conditionals = ( + "do", "if", "then", "else", "end", "until", "while" + ) + + builtins_statements = ( + "abort", "array", "attrib", "by", "call", "cards", "cards4", + "catname", "continue", "datalines", "datalines4", "delete", "delim", + "delimiter", "display", "dm", "drop", "endsas", "error", "file", + "filename", "footnote", "format", "goto", "in", "infile", "informat", + "input", "keep", "label", "leave", "length", "libname", "link", + "list", "lostcard", "merge", "missing", "modify", "options", "output", + "out", "page", "put", "redirect", "remove", "rename", "replace", + "retain", "return", "select", "set", "skip", "startsas", "stop", + "title", "update", "waitsas", "where", "window", "x", "systask" + ) + + builtins_sql = ( + "add", "and", "alter", "as", "cascade", "check", "create", + "delete", "describe", "distinct", "drop", "foreign", "from", + "group", "having", "index", "insert", "into", "in", "key", "like", + "message", "modify", "msgtype", "not", "null", "on", "or", + "order", "primary", "references", "reset", "restrict", "select", + "set", "table", "unique", "update", "validate", "view", "where" + ) + + builtins_functions = ( + "abs", "addr", "airy", "arcos", "arsin", "atan", "attrc", + "attrn", "band", "betainv", "blshift", "bnot", "bor", + "brshift", "bxor", "byte", "cdf", "ceil", "cexist", "cinv", + "close", "cnonct", "collate", "compbl", "compound", + "compress", "cos", "cosh", "css", "curobs", "cv", "daccdb", + "daccdbsl", "daccsl", "daccsyd", "dacctab", "dairy", "date", + "datejul", "datepart", "datetime", "day", "dclose", "depdb", + "depdbsl", "depsl", "depsyd", + "deptab", "dequote", "dhms", "dif", "digamma", + "dim", "dinfo", "dnum", "dopen", "doptname", "doptnum", + "dread", "dropnote", "dsname", "erf", "erfc", "exist", "exp", + "fappend", "fclose", "fcol", "fdelete", "fetch", "fetchobs", + "fexist", "fget", "fileexist", "filename", "fileref", + "finfo", "finv", "fipname", "fipnamel", "fipstate", "floor", + "fnonct", "fnote", "fopen", "foptname", "foptnum", "fpoint", + "fpos", "fput", "fread", "frewind", "frlen", "fsep", "fuzz", + "fwrite", "gaminv", "gamma", "getoption", "getvarc", "getvarn", + "hbound", "hms", "hosthelp", "hour", "ibessel", "index", + "indexc", "indexw", "input", "inputc", "inputn", "int", + "intck", "intnx", "intrr", "irr", "jbessel", "juldate", + "kurtosis", "lag", "lbound", "left", "length", "lgamma", + "libname", "libref", "log", "log10", "log2", "logpdf", "logpmf", + "logsdf", "lowcase", "max", "mdy", "mean", "min", "minute", + "mod", "month", "mopen", "mort", "n", "netpv", "nmiss", + "normal", "note", "npv", "open", "ordinal", "pathname", + "pdf", "peek", "peekc", "pmf", "point", "poisson", "poke", + "probbeta", "probbnml", "probchi", "probf", "probgam", + "probhypr", "probit", "probnegb", "probnorm", "probt", + "put", "putc", "putn", "qtr", "quote", "ranbin", "rancau", + "ranexp", "rangam", "range", "rank", "rannor", "ranpoi", + "rantbl", "rantri", "ranuni", "repeat", "resolve", "reverse", + "rewind", "right", "round", "saving", "scan", "sdf", "second", + "sign", "sin", "sinh", "skewness", "soundex", "spedis", + "sqrt", "std", "stderr", "stfips", "stname", "stnamel", + "substr", "sum", "symget", "sysget", "sysmsg", "sysprod", + "sysrc", "system", "tan", "tanh", "time", "timepart", "tinv", + "tnonct", "today", "translate", "tranwrd", "trigamma", + "trim", "trimn", "trunc", "uniform", "upcase", "uss", "var", + "varfmt", "varinfmt", "varlabel", "varlen", "varname", + "varnum", "varray", "varrayx", "vartype", "verify", "vformat", + "vformatd", "vformatdx", "vformatn", "vformatnx", "vformatw", + "vformatwx", "vformatx", "vinarray", "vinarrayx", "vinformat", + "vinformatd", "vinformatdx", "vinformatn", "vinformatnx", + "vinformatw", "vinformatwx", "vinformatx", "vlabel", + "vlabelx", "vlength", "vlengthx", "vname", "vnamex", "vtype", + "vtypex", "weekday", "year", "yyq", "zipfips", "zipname", + "zipnamel", "zipstate" + ) + + tokens = { + 'root': [ + include('comments'), + include('proc-data'), + include('cards-datalines'), + include('logs'), + include('general'), + (r'.', Text), + ], + # SAS is multi-line regardless, but * is ended by ; + 'comments': [ + (r'^\s*\*.*?;', Comment), + (r'/\*.*?\*/', Comment), + (r'^\s*\*(.|\n)*?;', Comment.Multiline), + (r'/[*](.|\n)*?[*]/', Comment.Multiline), + ], + # Special highlight for proc, data, quit, run + 'proc-data': [ + (r'(^|;)\s*(proc \w+|data|run|quit)[\s;]', + Keyword.Reserved), + ], + # Special highlight cards and datalines + 'cards-datalines': [ + (r'^\s*(datalines|cards)\s*;\s*$', Keyword, 'data'), + ], + 'data': [ + (r'(.|\n)*^\s*;\s*$', Other, '#pop'), + ], + # Special highlight for put NOTE|ERROR|WARNING (order matters) + 'logs': [ + (r'\n?^\s*%?put ', Keyword, 'log-messages'), + ], + 'log-messages': [ + (r'NOTE(:|-).*', Generic, '#pop'), + (r'WARNING(:|-).*', Generic.Emph, '#pop'), + (r'ERROR(:|-).*', Generic.Error, '#pop'), + include('general'), + ], + 'general': [ + include('keywords'), + include('vars-strings'), + include('special'), + include('numbers'), + ], + # Keywords, statements, functions, macros + 'keywords': [ + (words(builtins_statements, + prefix = r'\b', + suffix = r'\b'), + Keyword), + (words(builtins_sql, + prefix = r'\b', + suffix = r'\b'), + Keyword), + (words(builtins_conditionals, + prefix = r'\b', + suffix = r'\b'), + Keyword), + (words(builtins_macros, + prefix = r'%', + suffix = r'\b'), + Name.Builtin), + (words(builtins_functions, + prefix = r'\b', + suffix = r'\('), + Name.Builtin), + ], + # Strings and user-defined variables and macros (order matters) + 'vars-strings': [ + (r'&[a-z_]\w{0,31}\.?', Name.Variable), + (r'%[a-z_]\w{0,31}', Name.Function), + (r'\'', String, 'string_squote'), + (r'"', String, 'string_dquote'), + ], + 'string_squote': [ + ('\'', String, '#pop'), + (r'\\\\|\\"|\\\n', String.Escape), + # AFAIK, macro variables are not evaluated in single quotes + # (r'&', Name.Variable, 'validvar'), + (r'[^$\'\\]+', String), + (r'[$\'\\]', String), + ], + 'string_dquote': [ + (r'"', String, '#pop'), + (r'\\\\|\\"|\\\n', String.Escape), + (r'&', Name.Variable, 'validvar'), + (r'[^$&"\\]+', String), + (r'[$"\\]', String), + ], + 'validvar': [ + (r'[a-z_]\w{0,31}\.?', Name.Variable, '#pop'), + ], + # SAS numbers and special variables + 'numbers': [ + (r'\b[+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+|\.)(E[+-]?[0-9]+)?i?\b', + Number), + ], + 'special': [ + (r'(null|missing|_all_|_automatic_|_character_|_n_|' + r'_infile_|_name_|_null_|_numeric_|_user_|_webout_)', + Keyword.Constant), + ], + # 'operators': [ + # (r'(-|=|<=|>=|<|>|<>|&|!=|' + # r'\||\*|\+|\^|/|!|~|~=)', Operator) + # ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/scdoc.py b/.venv/lib/python3.8/site-packages/pygments/lexers/scdoc.py new file mode 100644 index 00000000..6acc64f2 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/scdoc.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.scdoc + ~~~~~~~~~~~~~~~~~~~~~ + + Lexer for scdoc, a simple man page generator. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include, bygroups, \ + using, this +from pygments.token import Text, Comment, Keyword, String, \ + Generic + + +__all__ = ['ScdocLexer'] + + +class ScdocLexer(RegexLexer): + """ + `scdoc` is a simple man page generator for POSIX systems written in C99. + https://git.sr.ht/~sircmpwn/scdoc + + .. versionadded:: 2.5 + """ + name = 'scdoc' + aliases = ['scdoc', 'scd'] + filenames = ['*.scd', '*.scdoc'] + flags = re.MULTILINE + + tokens = { + 'root': [ + # comment + (r'^(;.+\n)', bygroups(Comment)), + + # heading with pound prefix + (r'^(#)([^#].+\n)', bygroups(Generic.Heading, Text)), + (r'^(#{2})(.+\n)', bygroups(Generic.Subheading, Text)), + # bulleted lists + (r'^(\s*)([*-])(\s)(.+\n)', + bygroups(Text, Keyword, Text, using(this, state='inline'))), + # numbered lists + (r'^(\s*)(\.+\.)( .+\n)', + bygroups(Text, Keyword, using(this, state='inline'))), + # quote + (r'^(\s*>\s)(.+\n)', bygroups(Keyword, Generic.Emph)), + # text block + (r'^(```\n)([\w\W]*?)(^```$)', bygroups(String, Text, String)), + + include('inline'), + ], + 'inline': [ + # escape + (r'\\.', Text), + # underlines + (r'(\s)(_[^_]+_)(\W|\n)', bygroups(Text, Generic.Emph, Text)), + # bold + (r'(\s)(\*[^*]+\*)(\W|\n)', bygroups(Text, Generic.Strong, Text)), + # inline code + (r'`[^`]+`', String.Backtick), + + # general text, must come last! + (r'[^\\\s]+', Text), + (r'.', Text), + ], + } + + def analyse_text(text): + """This is very similar to markdown, save for the escape characters + needed for * and _.""" + result = 0 + + if '\\*' in text: + result += 0.01 + + if '\\_' in text: + result += 0.01 + + return result diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/scripting.py b/.venv/lib/python3.8/site-packages/pygments/lexers/scripting.py new file mode 100644 index 00000000..885fed47 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/scripting.py @@ -0,0 +1,1284 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.scripting + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexer for scripting and embedded languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, include, bygroups, default, combined, \ + words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Error, Whitespace, Other +from pygments.util import get_bool_opt, get_list_opt + +__all__ = ['LuaLexer', 'MoonScriptLexer', 'ChaiscriptLexer', 'LSLLexer', + 'AppleScriptLexer', 'RexxLexer', 'MOOCodeLexer', 'HybrisLexer', + 'EasytrieveLexer', 'JclLexer', 'MiniScriptLexer'] + + +class LuaLexer(RegexLexer): + """ + For `Lua `_ source code. + + Additional options accepted: + + `func_name_highlighting` + If given and ``True``, highlight builtin function names + (default: ``True``). + `disabled_modules` + If given, must be a list of module names whose function names + should not be highlighted. By default all modules are highlighted. + + To get a list of allowed modules have a look into the + `_lua_builtins` module: + + .. sourcecode:: pycon + + >>> from pygments.lexers._lua_builtins import MODULES + >>> MODULES.keys() + ['string', 'coroutine', 'modules', 'io', 'basic', ...] + """ + + name = 'Lua' + aliases = ['lua'] + filenames = ['*.lua', '*.wlua'] + mimetypes = ['text/x-lua', 'application/x-lua'] + + _comment_multiline = r'(?:--\[(?P=*)\[[\w\W]*?\](?P=level)\])' + _comment_single = r'(?:--.*$)' + _space = r'(?:\s+)' + _s = r'(?:%s|%s|%s)' % (_comment_multiline, _comment_single, _space) + _name = r'(?:[^\W\d]\w*)' + + tokens = { + 'root': [ + # Lua allows a file to start with a shebang. + (r'#!.*', Comment.Preproc), + default('base'), + ], + 'ws': [ + (_comment_multiline, Comment.Multiline), + (_comment_single, Comment.Single), + (_space, Text), + ], + 'base': [ + include('ws'), + + (r'(?i)0x[\da-f]*(\.[\da-f]*)?(p[+-]?\d+)?', Number.Hex), + (r'(?i)(\d*\.\d+|\d+\.\d*)(e[+-]?\d+)?', Number.Float), + (r'(?i)\d+e[+-]?\d+', Number.Float), + (r'\d+', Number.Integer), + + # multiline strings + (r'(?s)\[(=*)\[.*?\]\1\]', String), + + (r'::', Punctuation, 'label'), + (r'\.{3}', Punctuation), + (r'[=<>|~&+\-*/%#^]+|\.\.', Operator), + (r'[\[\]{}().,:;]', Punctuation), + (r'(and|or|not)\b', Operator.Word), + + ('(break|do|else|elseif|end|for|if|in|repeat|return|then|until|' + r'while)\b', Keyword.Reserved), + (r'goto\b', Keyword.Reserved, 'goto'), + (r'(local)\b', Keyword.Declaration), + (r'(true|false|nil)\b', Keyword.Constant), + + (r'(function)\b', Keyword.Reserved, 'funcname'), + + (r'[A-Za-z_]\w*(\.[A-Za-z_]\w*)?', Name), + + ("'", String.Single, combined('stringescape', 'sqs')), + ('"', String.Double, combined('stringescape', 'dqs')) + ], + + 'funcname': [ + include('ws'), + (r'[.:]', Punctuation), + (r'%s(?=%s*[.:])' % (_name, _s), Name.Class), + (_name, Name.Function, '#pop'), + # inline function + (r'\(', Punctuation, '#pop'), + ], + + 'goto': [ + include('ws'), + (_name, Name.Label, '#pop'), + ], + + 'label': [ + include('ws'), + (r'::', Punctuation, '#pop'), + (_name, Name.Label), + ], + + 'stringescape': [ + (r'\\([abfnrtv\\"\']|[\r\n]{1,2}|z\s*|x[0-9a-fA-F]{2}|\d{1,3}|' + r'u\{[0-9a-fA-F]+\})', String.Escape), + ], + + 'sqs': [ + (r"'", String.Single, '#pop'), + (r"[^\\']+", String.Single), + ], + + 'dqs': [ + (r'"', String.Double, '#pop'), + (r'[^\\"]+', String.Double), + ] + } + + def __init__(self, **options): + self.func_name_highlighting = get_bool_opt( + options, 'func_name_highlighting', True) + self.disabled_modules = get_list_opt(options, 'disabled_modules', []) + + self._functions = set() + if self.func_name_highlighting: + from pygments.lexers._lua_builtins import MODULES + for mod, func in MODULES.items(): + if mod not in self.disabled_modules: + self._functions.update(func) + RegexLexer.__init__(self, **options) + + def get_tokens_unprocessed(self, text): + for index, token, value in \ + RegexLexer.get_tokens_unprocessed(self, text): + if token is Name: + if value in self._functions: + yield index, Name.Builtin, value + continue + elif '.' in value: + a, b = value.split('.') + yield index, Name, a + yield index + len(a), Punctuation, '.' + yield index + len(a) + 1, Name, b + continue + yield index, token, value + +class MoonScriptLexer(LuaLexer): + """ + For `MoonScript `_ source code. + + .. versionadded:: 1.5 + """ + + name = "MoonScript" + aliases = ["moon", "moonscript"] + filenames = ["*.moon"] + mimetypes = ['text/x-moonscript', 'application/x-moonscript'] + + tokens = { + 'root': [ + (r'#!(.*?)$', Comment.Preproc), + default('base'), + ], + 'base': [ + ('--.*$', Comment.Single), + (r'(?i)(\d*\.\d+|\d+\.\d*)(e[+-]?\d+)?', Number.Float), + (r'(?i)\d+e[+-]?\d+', Number.Float), + (r'(?i)0x[0-9a-f]*', Number.Hex), + (r'\d+', Number.Integer), + (r'\n', Text), + (r'[^\S\n]+', Text), + (r'(?s)\[(=*)\[.*?\]\1\]', String), + (r'(->|=>)', Name.Function), + (r':[a-zA-Z_]\w*', Name.Variable), + (r'(==|!=|~=|<=|>=|\.\.\.|\.\.|[=+\-*/%^<>#!.\\:])', Operator), + (r'[;,]', Punctuation), + (r'[\[\]{}()]', Keyword.Type), + (r'[a-zA-Z_]\w*:', Name.Variable), + (words(( + 'class', 'extends', 'if', 'then', 'super', 'do', 'with', + 'import', 'export', 'while', 'elseif', 'return', 'for', 'in', + 'from', 'when', 'using', 'else', 'and', 'or', 'not', 'switch', + 'break'), suffix=r'\b'), + Keyword), + (r'(true|false|nil)\b', Keyword.Constant), + (r'(and|or|not)\b', Operator.Word), + (r'(self)\b', Name.Builtin.Pseudo), + (r'@@?([a-zA-Z_]\w*)?', Name.Variable.Class), + (r'[A-Z]\w*', Name.Class), # proper name + (r'[A-Za-z_]\w*(\.[A-Za-z_]\w*)?', Name), + ("'", String.Single, combined('stringescape', 'sqs')), + ('"', String.Double, combined('stringescape', 'dqs')) + ], + 'stringescape': [ + (r'''\\([abfnrtv\\"']|\d{1,3})''', String.Escape) + ], + 'sqs': [ + ("'", String.Single, '#pop'), + (".", String) + ], + 'dqs': [ + ('"', String.Double, '#pop'), + (".", String) + ] + } + + def get_tokens_unprocessed(self, text): + # set . as Operator instead of Punctuation + for index, token, value in LuaLexer.get_tokens_unprocessed(self, text): + if token == Punctuation and value == ".": + token = Operator + yield index, token, value + + +class ChaiscriptLexer(RegexLexer): + """ + For `ChaiScript `_ source code. + + .. versionadded:: 2.0 + """ + + name = 'ChaiScript' + aliases = ['chai', 'chaiscript'] + filenames = ['*.chai'] + mimetypes = ['text/x-chaiscript', 'application/x-chaiscript'] + + flags = re.DOTALL | re.MULTILINE + + tokens = { + 'commentsandwhitespace': [ + (r'\s+', Text), + (r'//.*?\n', Comment.Single), + (r'/\*.*?\*/', Comment.Multiline), + (r'^\#.*?\n', Comment.Single) + ], + 'slashstartsregex': [ + include('commentsandwhitespace'), + (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/' + r'([gim]+\b|\B)', String.Regex, '#pop'), + (r'(?=/)', Text, ('#pop', 'badregex')), + default('#pop') + ], + 'badregex': [ + (r'\n', Text, '#pop') + ], + 'root': [ + include('commentsandwhitespace'), + (r'\n', Text), + (r'[^\S\n]+', Text), + (r'\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|\.\.' + r'(<<|>>>?|==?|!=?|[-<>+*%&|^/])=?', Operator, 'slashstartsregex'), + (r'[{(\[;,]', Punctuation, 'slashstartsregex'), + (r'[})\].]', Punctuation), + (r'[=+\-*/]', Operator), + (r'(for|in|while|do|break|return|continue|if|else|' + r'throw|try|catch' + r')\b', Keyword, 'slashstartsregex'), + (r'(var)\b', Keyword.Declaration, 'slashstartsregex'), + (r'(attr|def|fun)\b', Keyword.Reserved), + (r'(true|false)\b', Keyword.Constant), + (r'(eval|throw)\b', Name.Builtin), + (r'`\S+`', Name.Builtin), + (r'[$a-zA-Z_]\w*', Name.Other), + (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float), + (r'0x[0-9a-fA-F]+', Number.Hex), + (r'[0-9]+', Number.Integer), + (r'"', String.Double, 'dqstring'), + (r"'(\\\\|\\'|[^'])*'", String.Single), + ], + 'dqstring': [ + (r'\$\{[^"}]+?\}', String.Interpol), + (r'\$', String.Double), + (r'\\\\', String.Double), + (r'\\"', String.Double), + (r'[^\\"$]+', String.Double), + (r'"', String.Double, '#pop'), + ], + } + + +class LSLLexer(RegexLexer): + """ + For Second Life's Linden Scripting Language source code. + + .. versionadded:: 2.0 + """ + + name = 'LSL' + aliases = ['lsl'] + filenames = ['*.lsl'] + mimetypes = ['text/x-lsl'] + + flags = re.MULTILINE + + lsl_keywords = r'\b(?:do|else|for|if|jump|return|while)\b' + lsl_types = r'\b(?:float|integer|key|list|quaternion|rotation|string|vector)\b' + lsl_states = r'\b(?:(?:state)\s+\w+|default)\b' + lsl_events = r'\b(?:state_(?:entry|exit)|touch(?:_(?:start|end))?|(?:land_)?collision(?:_(?:start|end))?|timer|listen|(?:no_)?sensor|control|(?:not_)?at_(?:rot_)?target|money|email|run_time_permissions|changed|attach|dataserver|moving_(?:start|end)|link_message|(?:on|object)_rez|remote_data|http_re(?:sponse|quest)|path_update|transaction_result)\b' + lsl_functions_builtin = r'\b(?:ll(?:ReturnObjectsBy(?:ID|Owner)|Json(?:2List|[GS]etValue|ValueType)|Sin|Cos|Tan|Atan2|Sqrt|Pow|Abs|Fabs|Frand|Floor|Ceil|Round|Vec(?:Mag|Norm|Dist)|Rot(?:Between|2(?:Euler|Fwd|Left|Up))|(?:Euler|Axes)2Rot|Whisper|(?:Region|Owner)?Say|Shout|Listen(?:Control|Remove)?|Sensor(?:Repeat|Remove)?|Detected(?:Name|Key|Owner|Type|Pos|Vel|Grab|Rot|Group|LinkNumber)|Die|Ground|Wind|(?:[GS]et)(?:AnimationOverride|MemoryLimit|PrimMediaParams|ParcelMusicURL|Object(?:Desc|Name)|PhysicsMaterial|Status|Scale|Color|Alpha|Texture|Pos|Rot|Force|Torque)|ResetAnimationOverride|(?:Scale|Offset|Rotate)Texture|(?:Rot)?Target(?:Remove)?|(?:Stop)?MoveToTarget|Apply(?:Rotational)?Impulse|Set(?:KeyframedMotion|ContentType|RegionPos|(?:Angular)?Velocity|Buoyancy|HoverHeight|ForceAndTorque|TimerEvent|ScriptState|Damage|TextureAnim|Sound(?:Queueing|Radius)|Vehicle(?:Type|(?:Float|Vector|Rotation)Param)|(?:Touch|Sit)?Text|Camera(?:Eye|At)Offset|PrimitiveParams|ClickAction|Link(?:Alpha|Color|PrimitiveParams(?:Fast)?|Texture(?:Anim)?|Camera|Media)|RemoteScriptAccessPin|PayPrice|LocalRot)|ScaleByFactor|Get(?:(?:Max|Min)ScaleFactor|ClosestNavPoint|StaticPath|SimStats|Env|PrimitiveParams|Link(?:PrimitiveParams|Number(?:OfSides)?|Key|Name|Media)|HTTPHeader|FreeURLs|Object(?:Details|PermMask|PrimCount)|Parcel(?:MaxPrims|Details|Prim(?:Count|Owners))|Attached|(?:SPMax|Free|Used)Memory|Region(?:Name|TimeDilation|FPS|Corner|AgentCount)|Root(?:Position|Rotation)|UnixTime|(?:Parcel|Region)Flags|(?:Wall|GMT)clock|SimulatorHostname|BoundingBox|GeometricCenter|Creator|NumberOf(?:Prims|NotecardLines|Sides)|Animation(?:List)?|(?:Camera|Local)(?:Pos|Rot)|Vel|Accel|Omega|Time(?:stamp|OfDay)|(?:Object|CenterOf)?Mass|MassMKS|Energy|Owner|(?:Owner)?Key|SunDirection|Texture(?:Offset|Scale|Rot)|Inventory(?:Number|Name|Key|Type|Creator|PermMask)|Permissions(?:Key)?|StartParameter|List(?:Length|EntryType)|Date|Agent(?:Size|Info|Language|List)|LandOwnerAt|NotecardLine|Script(?:Name|State))|(?:Get|Reset|GetAndReset)Time|PlaySound(?:Slave)?|LoopSound(?:Master|Slave)?|(?:Trigger|Stop|Preload)Sound|(?:(?:Get|Delete)Sub|Insert)String|To(?:Upper|Lower)|Give(?:InventoryList|Money)|RezObject|(?:Stop)?LookAt|Sleep|CollisionFilter|(?:Take|Release)Controls|DetachFromAvatar|AttachToAvatar(?:Temp)?|InstantMessage|(?:GetNext)?Email|StopHover|MinEventDelay|RotLookAt|String(?:Length|Trim)|(?:Start|Stop)Animation|TargetOmega|RequestPermissions|(?:Create|Break)Link|BreakAllLinks|(?:Give|Remove)Inventory|Water|PassTouches|Request(?:Agent|Inventory)Data|TeleportAgent(?:Home|GlobalCoords)?|ModifyLand|CollisionSound|ResetScript|MessageLinked|PushObject|PassCollisions|AxisAngle2Rot|Rot2(?:Axis|Angle)|A(?:cos|sin)|AngleBetween|AllowInventoryDrop|SubStringIndex|List2(?:CSV|Integer|Json|Float|String|Key|Vector|Rot|List(?:Strided)?)|DeleteSubList|List(?:Statistics|Sort|Randomize|(?:Insert|Find|Replace)List)|EdgeOfWorld|AdjustSoundVolume|Key2Name|TriggerSoundLimited|EjectFromLand|(?:CSV|ParseString)2List|OverMyLand|SameGroup|UnSit|Ground(?:Slope|Normal|Contour)|GroundRepel|(?:Set|Remove)VehicleFlags|(?:AvatarOn)?(?:Link)?SitTarget|Script(?:Danger|Profiler)|Dialog|VolumeDetect|ResetOtherScript|RemoteLoadScriptPin|(?:Open|Close)RemoteDataChannel|SendRemoteData|RemoteDataReply|(?:Integer|String)ToBase64|XorBase64|Log(?:10)?|Base64To(?:String|Integer)|ParseStringKeepNulls|RezAtRoot|RequestSimulatorData|ForceMouselook|(?:Load|Release|(?:E|Une)scape)URL|ParcelMedia(?:CommandList|Query)|ModPow|MapDestination|(?:RemoveFrom|AddTo|Reset)Land(?:Pass|Ban)List|(?:Set|Clear)CameraParams|HTTP(?:Request|Response)|TextBox|DetectedTouch(?:UV|Face|Pos|(?:N|Bin)ormal|ST)|(?:MD5|SHA1|DumpList2)String|Request(?:Secure)?URL|Clear(?:Prim|Link)Media|(?:Link)?ParticleSystem|(?:Get|Request)(?:Username|DisplayName)|RegionSayTo|CastRay|GenerateKey|TransferLindenDollars|ManageEstateAccess|(?:Create|Delete)Character|ExecCharacterCmd|Evade|FleeFrom|NavigateTo|PatrolPoints|Pursue|UpdateCharacter|WanderWithin))\b' + lsl_constants_float = r'\b(?:DEG_TO_RAD|PI(?:_BY_TWO)?|RAD_TO_DEG|SQRT2|TWO_PI)\b' + lsl_constants_integer = r'\b(?:JSON_APPEND|STATUS_(?:PHYSICS|ROTATE_[XYZ]|PHANTOM|SANDBOX|BLOCK_GRAB(?:_OBJECT)?|(?:DIE|RETURN)_AT_EDGE|CAST_SHADOWS|OK|MALFORMED_PARAMS|TYPE_MISMATCH|BOUNDS_ERROR|NOT_(?:FOUND|SUPPORTED)|INTERNAL_ERROR|WHITELIST_FAILED)|AGENT(?:_(?:BY_(?:LEGACY_|USER)NAME|FLYING|ATTACHMENTS|SCRIPTED|MOUSELOOK|SITTING|ON_OBJECT|AWAY|WALKING|IN_AIR|TYPING|CROUCHING|BUSY|ALWAYS_RUN|AUTOPILOT|LIST_(?:PARCEL(?:_OWNER)?|REGION)))?|CAMERA_(?:PITCH|DISTANCE|BEHINDNESS_(?:ANGLE|LAG)|(?:FOCUS|POSITION)(?:_(?:THRESHOLD|LOCKED|LAG))?|FOCUS_OFFSET|ACTIVE)|ANIM_ON|LOOP|REVERSE|PING_PONG|SMOOTH|ROTATE|SCALE|ALL_SIDES|LINK_(?:ROOT|SET|ALL_(?:OTHERS|CHILDREN)|THIS)|ACTIVE|PASSIVE|SCRIPTED|CONTROL_(?:FWD|BACK|(?:ROT_)?(?:LEFT|RIGHT)|UP|DOWN|(?:ML_)?LBUTTON)|PERMISSION_(?:RETURN_OBJECTS|DEBIT|OVERRIDE_ANIMATIONS|SILENT_ESTATE_MANAGEMENT|TAKE_CONTROLS|TRIGGER_ANIMATION|ATTACH|CHANGE_LINKS|(?:CONTROL|TRACK)_CAMERA|TELEPORT)|INVENTORY_(?:TEXTURE|SOUND|OBJECT|SCRIPT|LANDMARK|CLOTHING|NOTECARD|BODYPART|ANIMATION|GESTURE|ALL|NONE)|CHANGED_(?:INVENTORY|COLOR|SHAPE|SCALE|TEXTURE|LINK|ALLOWED_DROP|OWNER|REGION(?:_START)?|TELEPORT|MEDIA)|OBJECT_(?:(?:PHYSICS|SERVER|STREAMING)_COST|UNKNOWN_DETAIL|CHARACTER_TIME|PHANTOM|PHYSICS|TEMP_ON_REZ|NAME|DESC|POS|PRIM_EQUIVALENCE|RETURN_(?:PARCEL(?:_OWNER)?|REGION)|ROO?T|VELOCITY|OWNER|GROUP|CREATOR|ATTACHED_POINT|RENDER_WEIGHT|PATHFINDING_TYPE|(?:RUNNING|TOTAL)_SCRIPT_COUNT|SCRIPT_(?:MEMORY|TIME))|TYPE_(?:INTEGER|FLOAT|STRING|KEY|VECTOR|ROTATION|INVALID)|(?:DEBUG|PUBLIC)_CHANNEL|ATTACH_(?:AVATAR_CENTER|CHEST|HEAD|BACK|PELVIS|MOUTH|CHIN|NECK|NOSE|BELLY|[LR](?:SHOULDER|HAND|FOOT|EAR|EYE|[UL](?:ARM|LEG)|HIP)|(?:LEFT|RIGHT)_PEC|HUD_(?:CENTER_[12]|TOP_(?:RIGHT|CENTER|LEFT)|BOTTOM(?:_(?:RIGHT|LEFT))?))|LAND_(?:LEVEL|RAISE|LOWER|SMOOTH|NOISE|REVERT)|DATA_(?:ONLINE|NAME|BORN|SIM_(?:POS|STATUS|RATING)|PAYINFO)|PAYMENT_INFO_(?:ON_FILE|USED)|REMOTE_DATA_(?:CHANNEL|REQUEST|REPLY)|PSYS_(?:PART_(?:BF_(?:ZERO|ONE(?:_MINUS_(?:DEST_COLOR|SOURCE_(ALPHA|COLOR)))?|DEST_COLOR|SOURCE_(ALPHA|COLOR))|BLEND_FUNC_(DEST|SOURCE)|FLAGS|(?:START|END)_(?:COLOR|ALPHA|SCALE|GLOW)|MAX_AGE|(?:RIBBON|WIND|INTERP_(?:COLOR|SCALE)|BOUNCE|FOLLOW_(?:SRC|VELOCITY)|TARGET_(?:POS|LINEAR)|EMISSIVE)_MASK)|SRC_(?:MAX_AGE|PATTERN|ANGLE_(?:BEGIN|END)|BURST_(?:RATE|PART_COUNT|RADIUS|SPEED_(?:MIN|MAX))|ACCEL|TEXTURE|TARGET_KEY|OMEGA|PATTERN_(?:DROP|EXPLODE|ANGLE(?:_CONE(?:_EMPTY)?)?)))|VEHICLE_(?:REFERENCE_FRAME|TYPE_(?:NONE|SLED|CAR|BOAT|AIRPLANE|BALLOON)|(?:LINEAR|ANGULAR)_(?:FRICTION_TIMESCALE|MOTOR_DIRECTION)|LINEAR_MOTOR_OFFSET|HOVER_(?:HEIGHT|EFFICIENCY|TIMESCALE)|BUOYANCY|(?:LINEAR|ANGULAR)_(?:DEFLECTION_(?:EFFICIENCY|TIMESCALE)|MOTOR_(?:DECAY_)?TIMESCALE)|VERTICAL_ATTRACTION_(?:EFFICIENCY|TIMESCALE)|BANKING_(?:EFFICIENCY|MIX|TIMESCALE)|FLAG_(?:NO_DEFLECTION_UP|LIMIT_(?:ROLL_ONLY|MOTOR_UP)|HOVER_(?:(?:WATER|TERRAIN|UP)_ONLY|GLOBAL_HEIGHT)|MOUSELOOK_(?:STEER|BANK)|CAMERA_DECOUPLED))|PRIM_(?:TYPE(?:_(?:BOX|CYLINDER|PRISM|SPHERE|TORUS|TUBE|RING|SCULPT))?|HOLE_(?:DEFAULT|CIRCLE|SQUARE|TRIANGLE)|MATERIAL(?:_(?:STONE|METAL|GLASS|WOOD|FLESH|PLASTIC|RUBBER))?|SHINY_(?:NONE|LOW|MEDIUM|HIGH)|BUMP_(?:NONE|BRIGHT|DARK|WOOD|BARK|BRICKS|CHECKER|CONCRETE|TILE|STONE|DISKS|GRAVEL|BLOBS|SIDING|LARGETILE|STUCCO|SUCTION|WEAVE)|TEXGEN_(?:DEFAULT|PLANAR)|SCULPT_(?:TYPE_(?:SPHERE|TORUS|PLANE|CYLINDER|MASK)|FLAG_(?:MIRROR|INVERT))|PHYSICS(?:_(?:SHAPE_(?:CONVEX|NONE|PRIM|TYPE)))?|(?:POS|ROT)_LOCAL|SLICE|TEXT|FLEXIBLE|POINT_LIGHT|TEMP_ON_REZ|PHANTOM|POSITION|SIZE|ROTATION|TEXTURE|NAME|OMEGA|DESC|LINK_TARGET|COLOR|BUMP_SHINY|FULLBRIGHT|TEXGEN|GLOW|MEDIA_(?:ALT_IMAGE_ENABLE|CONTROLS|(?:CURRENT|HOME)_URL|AUTO_(?:LOOP|PLAY|SCALE|ZOOM)|FIRST_CLICK_INTERACT|(?:WIDTH|HEIGHT)_PIXELS|WHITELIST(?:_ENABLE)?|PERMS_(?:INTERACT|CONTROL)|PARAM_MAX|CONTROLS_(?:STANDARD|MINI)|PERM_(?:NONE|OWNER|GROUP|ANYONE)|MAX_(?:URL_LENGTH|WHITELIST_(?:SIZE|COUNT)|(?:WIDTH|HEIGHT)_PIXELS)))|MASK_(?:BASE|OWNER|GROUP|EVERYONE|NEXT)|PERM_(?:TRANSFER|MODIFY|COPY|MOVE|ALL)|PARCEL_(?:MEDIA_COMMAND_(?:STOP|PAUSE|PLAY|LOOP|TEXTURE|URL|TIME|AGENT|UNLOAD|AUTO_ALIGN|TYPE|SIZE|DESC|LOOP_SET)|FLAG_(?:ALLOW_(?:FLY|(?:GROUP_)?SCRIPTS|LANDMARK|TERRAFORM|DAMAGE|CREATE_(?:GROUP_)?OBJECTS)|USE_(?:ACCESS_(?:GROUP|LIST)|BAN_LIST|LAND_PASS_LIST)|LOCAL_SOUND_ONLY|RESTRICT_PUSHOBJECT|ALLOW_(?:GROUP|ALL)_OBJECT_ENTRY)|COUNT_(?:TOTAL|OWNER|GROUP|OTHER|SELECTED|TEMP)|DETAILS_(?:NAME|DESC|OWNER|GROUP|AREA|ID|SEE_AVATARS))|LIST_STAT_(?:MAX|MIN|MEAN|MEDIAN|STD_DEV|SUM(?:_SQUARES)?|NUM_COUNT|GEOMETRIC_MEAN|RANGE)|PAY_(?:HIDE|DEFAULT)|REGION_FLAG_(?:ALLOW_DAMAGE|FIXED_SUN|BLOCK_TERRAFORM|SANDBOX|DISABLE_(?:COLLISIONS|PHYSICS)|BLOCK_FLY|ALLOW_DIRECT_TELEPORT|RESTRICT_PUSHOBJECT)|HTTP_(?:METHOD|MIMETYPE|BODY_(?:MAXLENGTH|TRUNCATED)|CUSTOM_HEADER|PRAGMA_NO_CACHE|VERBOSE_THROTTLE|VERIFY_CERT)|STRING_(?:TRIM(?:_(?:HEAD|TAIL))?)|CLICK_ACTION_(?:NONE|TOUCH|SIT|BUY|PAY|OPEN(?:_MEDIA)?|PLAY|ZOOM)|TOUCH_INVALID_FACE|PROFILE_(?:NONE|SCRIPT_MEMORY)|RC_(?:DATA_FLAGS|DETECT_PHANTOM|GET_(?:LINK_NUM|NORMAL|ROOT_KEY)|MAX_HITS|REJECT_(?:TYPES|AGENTS|(?:NON)?PHYSICAL|LAND))|RCERR_(?:CAST_TIME_EXCEEDED|SIM_PERF_LOW|UNKNOWN)|ESTATE_ACCESS_(?:ALLOWED_(?:AGENT|GROUP)_(?:ADD|REMOVE)|BANNED_AGENT_(?:ADD|REMOVE))|DENSITY|FRICTION|RESTITUTION|GRAVITY_MULTIPLIER|KFM_(?:COMMAND|CMD_(?:PLAY|STOP|PAUSE|SET_MODE)|MODE|FORWARD|LOOP|PING_PONG|REVERSE|DATA|ROTATION|TRANSLATION)|ERR_(?:GENERIC|PARCEL_PERMISSIONS|MALFORMED_PARAMS|RUNTIME_PERMISSIONS|THROTTLED)|CHARACTER_(?:CMD_(?:(?:SMOOTH_)?STOP|JUMP)|DESIRED_(?:TURN_)?SPEED|RADIUS|STAY_WITHIN_PARCEL|LENGTH|ORIENTATION|ACCOUNT_FOR_SKIPPED_FRAMES|AVOIDANCE_MODE|TYPE(?:_(?:[A-D]|NONE))?|MAX_(?:DECEL|TURN_RADIUS|(?:ACCEL|SPEED)))|PURSUIT_(?:OFFSET|FUZZ_FACTOR|GOAL_TOLERANCE|INTERCEPT)|REQUIRE_LINE_OF_SIGHT|FORCE_DIRECT_PATH|VERTICAL|HORIZONTAL|AVOID_(?:CHARACTERS|DYNAMIC_OBSTACLES|NONE)|PU_(?:EVADE_(?:HIDDEN|SPOTTED)|FAILURE_(?:DYNAMIC_PATHFINDING_DISABLED|INVALID_(?:GOAL|START)|NO_(?:NAVMESH|VALID_DESTINATION)|OTHER|TARGET_GONE|(?:PARCEL_)?UNREACHABLE)|(?:GOAL|SLOWDOWN_DISTANCE)_REACHED)|TRAVERSAL_TYPE(?:_(?:FAST|NONE|SLOW))?|CONTENT_TYPE_(?:ATOM|FORM|HTML|JSON|LLSD|RSS|TEXT|XHTML|XML)|GCNP_(?:RADIUS|STATIC)|(?:PATROL|WANDER)_PAUSE_AT_WAYPOINTS|OPT_(?:AVATAR|CHARACTER|EXCLUSION_VOLUME|LEGACY_LINKSET|MATERIAL_VOLUME|OTHER|STATIC_OBSTACLE|WALKABLE)|SIM_STAT_PCT_CHARS_STEPPED)\b' + lsl_constants_integer_boolean = r'\b(?:FALSE|TRUE)\b' + lsl_constants_rotation = r'\b(?:ZERO_ROTATION)\b' + lsl_constants_string = r'\b(?:EOF|JSON_(?:ARRAY|DELETE|FALSE|INVALID|NULL|NUMBER|OBJECT|STRING|TRUE)|NULL_KEY|TEXTURE_(?:BLANK|DEFAULT|MEDIA|PLYWOOD|TRANSPARENT)|URL_REQUEST_(?:GRANTED|DENIED))\b' + lsl_constants_vector = r'\b(?:TOUCH_INVALID_(?:TEXCOORD|VECTOR)|ZERO_VECTOR)\b' + lsl_invalid_broken = r'\b(?:LAND_(?:LARGE|MEDIUM|SMALL)_BRUSH)\b' + lsl_invalid_deprecated = r'\b(?:ATTACH_[LR]PEC|DATA_RATING|OBJECT_ATTACHMENT_(?:GEOMETRY_BYTES|SURFACE_AREA)|PRIM_(?:CAST_SHADOWS|MATERIAL_LIGHT|TYPE_LEGACY)|PSYS_SRC_(?:INNER|OUTER)ANGLE|VEHICLE_FLAG_NO_FLY_UP|ll(?:Cloud|Make(?:Explosion|Fountain|Smoke|Fire)|RemoteDataSetRegion|Sound(?:Preload)?|XorBase64Strings(?:Correct)?))\b' + lsl_invalid_illegal = r'\b(?:event)\b' + lsl_invalid_unimplemented = r'\b(?:CHARACTER_(?:MAX_ANGULAR_(?:ACCEL|SPEED)|TURN_SPEED_MULTIPLIER)|PERMISSION_(?:CHANGE_(?:JOINTS|PERMISSIONS)|RELEASE_OWNERSHIP|REMAP_CONTROLS)|PRIM_PHYSICS_MATERIAL|PSYS_SRC_OBJ_REL_MASK|ll(?:CollisionSprite|(?:Stop)?PointAt|(?:(?:Refresh|Set)Prim)URL|(?:Take|Release)Camera|RemoteLoadScript))\b' + lsl_reserved_godmode = r'\b(?:ll(?:GodLikeRezObject|Set(?:Inventory|Object)PermMask))\b' + lsl_reserved_log = r'\b(?:print)\b' + lsl_operators = r'\+\+|\-\-|<<|>>|&&?|\|\|?|\^|~|[!%<>=*+\-/]=?' + + tokens = { + 'root': + [ + (r'//.*?\n', Comment.Single), + (r'/\*', Comment.Multiline, 'comment'), + (r'"', String.Double, 'string'), + (lsl_keywords, Keyword), + (lsl_types, Keyword.Type), + (lsl_states, Name.Class), + (lsl_events, Name.Builtin), + (lsl_functions_builtin, Name.Function), + (lsl_constants_float, Keyword.Constant), + (lsl_constants_integer, Keyword.Constant), + (lsl_constants_integer_boolean, Keyword.Constant), + (lsl_constants_rotation, Keyword.Constant), + (lsl_constants_string, Keyword.Constant), + (lsl_constants_vector, Keyword.Constant), + (lsl_invalid_broken, Error), + (lsl_invalid_deprecated, Error), + (lsl_invalid_illegal, Error), + (lsl_invalid_unimplemented, Error), + (lsl_reserved_godmode, Keyword.Reserved), + (lsl_reserved_log, Keyword.Reserved), + (r'\b([a-zA-Z_]\w*)\b', Name.Variable), + (r'(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d*', Number.Float), + (r'(\d+\.\d*|\.\d+)', Number.Float), + (r'0[xX][0-9a-fA-F]+', Number.Hex), + (r'\d+', Number.Integer), + (lsl_operators, Operator), + (r':=?', Error), + (r'[,;{}()\[\]]', Punctuation), + (r'\n+', Whitespace), + (r'\s+', Whitespace) + ], + 'comment': + [ + (r'[^*/]+', Comment.Multiline), + (r'/\*', Comment.Multiline, '#push'), + (r'\*/', Comment.Multiline, '#pop'), + (r'[*/]', Comment.Multiline) + ], + 'string': + [ + (r'\\([nt"\\])', String.Escape), + (r'"', String.Double, '#pop'), + (r'\\.', Error), + (r'[^"\\]+', String.Double), + ] + } + + +class AppleScriptLexer(RegexLexer): + """ + For `AppleScript source code + `_, + including `AppleScript Studio + `_. + Contributed by Andreas Amann . + + .. versionadded:: 1.0 + """ + + name = 'AppleScript' + aliases = ['applescript'] + filenames = ['*.applescript'] + + flags = re.MULTILINE | re.DOTALL + + Identifiers = r'[a-zA-Z]\w*' + + # XXX: use words() for all of these + Literals = ('AppleScript', 'current application', 'false', 'linefeed', + 'missing value', 'pi', 'quote', 'result', 'return', 'space', + 'tab', 'text item delimiters', 'true', 'version') + Classes = ('alias ', 'application ', 'boolean ', 'class ', 'constant ', + 'date ', 'file ', 'integer ', 'list ', 'number ', 'POSIX file ', + 'real ', 'record ', 'reference ', 'RGB color ', 'script ', + 'text ', 'unit types', '(?:Unicode )?text', 'string') + BuiltIn = ('attachment', 'attribute run', 'character', 'day', 'month', + 'paragraph', 'word', 'year') + HandlerParams = ('about', 'above', 'against', 'apart from', 'around', + 'aside from', 'at', 'below', 'beneath', 'beside', + 'between', 'for', 'given', 'instead of', 'on', 'onto', + 'out of', 'over', 'since') + Commands = ('ASCII (character|number)', 'activate', 'beep', 'choose URL', + 'choose application', 'choose color', 'choose file( name)?', + 'choose folder', 'choose from list', + 'choose remote application', 'clipboard info', + 'close( access)?', 'copy', 'count', 'current date', 'delay', + 'delete', 'display (alert|dialog)', 'do shell script', + 'duplicate', 'exists', 'get eof', 'get volume settings', + 'info for', 'launch', 'list (disks|folder)', 'load script', + 'log', 'make', 'mount volume', 'new', 'offset', + 'open( (for access|location))?', 'path to', 'print', 'quit', + 'random number', 'read', 'round', 'run( script)?', + 'say', 'scripting components', + 'set (eof|the clipboard to|volume)', 'store script', + 'summarize', 'system attribute', 'system info', + 'the clipboard', 'time to GMT', 'write', 'quoted form') + References = ('(in )?back of', '(in )?front of', '[0-9]+(st|nd|rd|th)', + 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', + 'seventh', 'eighth', 'ninth', 'tenth', 'after', 'back', + 'before', 'behind', 'every', 'front', 'index', 'last', + 'middle', 'some', 'that', 'through', 'thru', 'where', 'whose') + Operators = ("and", "or", "is equal", "equals", "(is )?equal to", "is not", + "isn't", "isn't equal( to)?", "is not equal( to)?", + "doesn't equal", "does not equal", "(is )?greater than", + "comes after", "is not less than or equal( to)?", + "isn't less than or equal( to)?", "(is )?less than", + "comes before", "is not greater than or equal( to)?", + "isn't greater than or equal( to)?", + "(is )?greater than or equal( to)?", "is not less than", + "isn't less than", "does not come before", + "doesn't come before", "(is )?less than or equal( to)?", + "is not greater than", "isn't greater than", + "does not come after", "doesn't come after", "starts? with", + "begins? with", "ends? with", "contains?", "does not contain", + "doesn't contain", "is in", "is contained by", "is not in", + "is not contained by", "isn't contained by", "div", "mod", + "not", "(a )?(ref( to)?|reference to)", "is", "does") + Control = ('considering', 'else', 'error', 'exit', 'from', 'if', + 'ignoring', 'in', 'repeat', 'tell', 'then', 'times', 'to', + 'try', 'until', 'using terms from', 'while', 'whith', + 'with timeout( of)?', 'with transaction', 'by', 'continue', + 'end', 'its?', 'me', 'my', 'return', 'of', 'as') + Declarations = ('global', 'local', 'prop(erty)?', 'set', 'get') + Reserved = ('but', 'put', 'returning', 'the') + StudioClasses = ('action cell', 'alert reply', 'application', 'box', + 'browser( cell)?', 'bundle', 'button( cell)?', 'cell', + 'clip view', 'color well', 'color-panel', + 'combo box( item)?', 'control', + 'data( (cell|column|item|row|source))?', 'default entry', + 'dialog reply', 'document', 'drag info', 'drawer', + 'event', 'font(-panel)?', 'formatter', + 'image( (cell|view))?', 'matrix', 'menu( item)?', 'item', + 'movie( view)?', 'open-panel', 'outline view', 'panel', + 'pasteboard', 'plugin', 'popup button', + 'progress indicator', 'responder', 'save-panel', + 'scroll view', 'secure text field( cell)?', 'slider', + 'sound', 'split view', 'stepper', 'tab view( item)?', + 'table( (column|header cell|header view|view))', + 'text( (field( cell)?|view))?', 'toolbar( item)?', + 'user-defaults', 'view', 'window') + StudioEvents = ('accept outline drop', 'accept table drop', 'action', + 'activated', 'alert ended', 'awake from nib', 'became key', + 'became main', 'begin editing', 'bounds changed', + 'cell value', 'cell value changed', 'change cell value', + 'change item value', 'changed', 'child of item', + 'choose menu item', 'clicked', 'clicked toolbar item', + 'closed', 'column clicked', 'column moved', + 'column resized', 'conclude drop', 'data representation', + 'deminiaturized', 'dialog ended', 'document nib name', + 'double clicked', 'drag( (entered|exited|updated))?', + 'drop', 'end editing', 'exposed', 'idle', 'item expandable', + 'item value', 'item value changed', 'items changed', + 'keyboard down', 'keyboard up', 'launched', + 'load data representation', 'miniaturized', 'mouse down', + 'mouse dragged', 'mouse entered', 'mouse exited', + 'mouse moved', 'mouse up', 'moved', + 'number of browser rows', 'number of items', + 'number of rows', 'open untitled', 'opened', 'panel ended', + 'parameters updated', 'plugin loaded', 'prepare drop', + 'prepare outline drag', 'prepare outline drop', + 'prepare table drag', 'prepare table drop', + 'read from file', 'resigned active', 'resigned key', + 'resigned main', 'resized( sub views)?', + 'right mouse down', 'right mouse dragged', + 'right mouse up', 'rows changed', 'scroll wheel', + 'selected tab view item', 'selection changed', + 'selection changing', 'should begin editing', + 'should close', 'should collapse item', + 'should end editing', 'should expand item', + 'should open( untitled)?', + 'should quit( after last window closed)?', + 'should select column', 'should select item', + 'should select row', 'should select tab view item', + 'should selection change', 'should zoom', 'shown', + 'update menu item', 'update parameters', + 'update toolbar item', 'was hidden', 'was miniaturized', + 'will become active', 'will close', 'will dismiss', + 'will display browser cell', 'will display cell', + 'will display item cell', 'will display outline cell', + 'will finish launching', 'will hide', 'will miniaturize', + 'will move', 'will open', 'will pop up', 'will quit', + 'will resign active', 'will resize( sub views)?', + 'will select tab view item', 'will show', 'will zoom', + 'write to file', 'zoomed') + StudioCommands = ('animate', 'append', 'call method', 'center', + 'close drawer', 'close panel', 'display', + 'display alert', 'display dialog', 'display panel', 'go', + 'hide', 'highlight', 'increment', 'item for', + 'load image', 'load movie', 'load nib', 'load panel', + 'load sound', 'localized string', 'lock focus', 'log', + 'open drawer', 'path for', 'pause', 'perform action', + 'play', 'register', 'resume', 'scroll', 'select( all)?', + 'show', 'size to fit', 'start', 'step back', + 'step forward', 'stop', 'synchronize', 'unlock focus', + 'update') + StudioProperties = ('accepts arrow key', 'action method', 'active', + 'alignment', 'allowed identifiers', + 'allows branch selection', 'allows column reordering', + 'allows column resizing', 'allows column selection', + 'allows customization', + 'allows editing text attributes', + 'allows empty selection', 'allows mixed state', + 'allows multiple selection', 'allows reordering', + 'allows undo', 'alpha( value)?', 'alternate image', + 'alternate increment value', 'alternate title', + 'animation delay', 'associated file name', + 'associated object', 'auto completes', 'auto display', + 'auto enables items', 'auto repeat', + 'auto resizes( outline column)?', + 'auto save expanded items', 'auto save name', + 'auto save table columns', 'auto saves configuration', + 'auto scroll', 'auto sizes all columns to fit', + 'auto sizes cells', 'background color', 'bezel state', + 'bezel style', 'bezeled', 'border rect', 'border type', + 'bordered', 'bounds( rotation)?', 'box type', + 'button returned', 'button type', + 'can choose directories', 'can choose files', + 'can draw', 'can hide', + 'cell( (background color|size|type))?', 'characters', + 'class', 'click count', 'clicked( data)? column', + 'clicked data item', 'clicked( data)? row', + 'closeable', 'collating', 'color( (mode|panel))', + 'command key down', 'configuration', + 'content(s| (size|view( margins)?))?', 'context', + 'continuous', 'control key down', 'control size', + 'control tint', 'control view', + 'controller visible', 'coordinate system', + 'copies( on scroll)?', 'corner view', 'current cell', + 'current column', 'current( field)? editor', + 'current( menu)? item', 'current row', + 'current tab view item', 'data source', + 'default identifiers', 'delta (x|y|z)', + 'destination window', 'directory', 'display mode', + 'displayed cell', 'document( (edited|rect|view))?', + 'double value', 'dragged column', 'dragged distance', + 'dragged items', 'draws( cell)? background', + 'draws grid', 'dynamically scrolls', 'echos bullets', + 'edge', 'editable', 'edited( data)? column', + 'edited data item', 'edited( data)? row', 'enabled', + 'enclosing scroll view', 'ending page', + 'error handling', 'event number', 'event type', + 'excluded from windows menu', 'executable path', + 'expanded', 'fax number', 'field editor', 'file kind', + 'file name', 'file type', 'first responder', + 'first visible column', 'flipped', 'floating', + 'font( panel)?', 'formatter', 'frameworks path', + 'frontmost', 'gave up', 'grid color', 'has data items', + 'has horizontal ruler', 'has horizontal scroller', + 'has parent data item', 'has resize indicator', + 'has shadow', 'has sub menu', 'has vertical ruler', + 'has vertical scroller', 'header cell', 'header view', + 'hidden', 'hides when deactivated', 'highlights by', + 'horizontal line scroll', 'horizontal page scroll', + 'horizontal ruler view', 'horizontally resizable', + 'icon image', 'id', 'identifier', + 'ignores multiple clicks', + 'image( (alignment|dims when disabled|frame style|scaling))?', + 'imports graphics', 'increment value', + 'indentation per level', 'indeterminate', 'index', + 'integer value', 'intercell spacing', 'item height', + 'key( (code|equivalent( modifier)?|window))?', + 'knob thickness', 'label', 'last( visible)? column', + 'leading offset', 'leaf', 'level', 'line scroll', + 'loaded', 'localized sort', 'location', 'loop mode', + 'main( (bunde|menu|window))?', 'marker follows cell', + 'matrix mode', 'maximum( content)? size', + 'maximum visible columns', + 'menu( form representation)?', 'miniaturizable', + 'miniaturized', 'minimized image', 'minimized title', + 'minimum column width', 'minimum( content)? size', + 'modal', 'modified', 'mouse down state', + 'movie( (controller|file|rect))?', 'muted', 'name', + 'needs display', 'next state', 'next text', + 'number of tick marks', 'only tick mark values', + 'opaque', 'open panel', 'option key down', + 'outline table column', 'page scroll', 'pages across', + 'pages down', 'palette label', 'pane splitter', + 'parent data item', 'parent window', 'pasteboard', + 'path( (names|separator))?', 'playing', + 'plays every frame', 'plays selection only', 'position', + 'preferred edge', 'preferred type', 'pressure', + 'previous text', 'prompt', 'properties', + 'prototype cell', 'pulls down', 'rate', + 'released when closed', 'repeated', + 'requested print time', 'required file type', + 'resizable', 'resized column', 'resource path', + 'returns records', 'reuses columns', 'rich text', + 'roll over', 'row height', 'rulers visible', + 'save panel', 'scripts path', 'scrollable', + 'selectable( identifiers)?', 'selected cell', + 'selected( data)? columns?', 'selected data items?', + 'selected( data)? rows?', 'selected item identifier', + 'selection by rect', 'send action on arrow key', + 'sends action when done editing', 'separates columns', + 'separator item', 'sequence number', 'services menu', + 'shared frameworks path', 'shared support path', + 'sheet', 'shift key down', 'shows alpha', + 'shows state by', 'size( mode)?', + 'smart insert delete enabled', 'sort case sensitivity', + 'sort column', 'sort order', 'sort type', + 'sorted( data rows)?', 'sound', 'source( mask)?', + 'spell checking enabled', 'starting page', 'state', + 'string value', 'sub menu', 'super menu', 'super view', + 'tab key traverses cells', 'tab state', 'tab type', + 'tab view', 'table view', 'tag', 'target( printer)?', + 'text color', 'text container insert', + 'text container origin', 'text returned', + 'tick mark position', 'time stamp', + 'title(d| (cell|font|height|position|rect))?', + 'tool tip', 'toolbar', 'trailing offset', 'transparent', + 'treat packages as directories', 'truncated labels', + 'types', 'unmodified characters', 'update views', + 'use sort indicator', 'user defaults', + 'uses data source', 'uses ruler', + 'uses threaded animation', + 'uses title from previous column', 'value wraps', + 'version', + 'vertical( (line scroll|page scroll|ruler view))?', + 'vertically resizable', 'view', + 'visible( document rect)?', 'volume', 'width', 'window', + 'windows menu', 'wraps', 'zoomable', 'zoomed') + + tokens = { + 'root': [ + (r'\s+', Text), + (r'¬\n', String.Escape), + (r"'s\s+", Text), # This is a possessive, consider moving + (r'(--|#).*?$', Comment), + (r'\(\*', Comment.Multiline, 'comment'), + (r'[(){}!,.:]', Punctuation), + (r'(«)([^»]+)(»)', + bygroups(Text, Name.Builtin, Text)), + (r'\b((?:considering|ignoring)\s*)' + r'(application responses|case|diacriticals|hyphens|' + r'numeric strings|punctuation|white space)', + bygroups(Keyword, Name.Builtin)), + (r'(-|\*|\+|&|≠|>=?|<=?|=|≥|≤|/|÷|\^)', Operator), + (r"\b(%s)\b" % '|'.join(Operators), Operator.Word), + (r'^(\s*(?:on|end)\s+)' + r'(%s)' % '|'.join(StudioEvents[::-1]), + bygroups(Keyword, Name.Function)), + (r'^(\s*)(in|on|script|to)(\s+)', bygroups(Text, Keyword, Text)), + (r'\b(as )(%s)\b' % '|'.join(Classes), + bygroups(Keyword, Name.Class)), + (r'\b(%s)\b' % '|'.join(Literals), Name.Constant), + (r'\b(%s)\b' % '|'.join(Commands), Name.Builtin), + (r'\b(%s)\b' % '|'.join(Control), Keyword), + (r'\b(%s)\b' % '|'.join(Declarations), Keyword), + (r'\b(%s)\b' % '|'.join(Reserved), Name.Builtin), + (r'\b(%s)s?\b' % '|'.join(BuiltIn), Name.Builtin), + (r'\b(%s)\b' % '|'.join(HandlerParams), Name.Builtin), + (r'\b(%s)\b' % '|'.join(StudioProperties), Name.Attribute), + (r'\b(%s)s?\b' % '|'.join(StudioClasses), Name.Builtin), + (r'\b(%s)\b' % '|'.join(StudioCommands), Name.Builtin), + (r'\b(%s)\b' % '|'.join(References), Name.Builtin), + (r'"(\\\\|\\"|[^"])*"', String.Double), + (r'\b(%s)\b' % Identifiers, Name.Variable), + (r'[-+]?(\d+\.\d*|\d*\.\d+)(E[-+][0-9]+)?', Number.Float), + (r'[-+]?\d+', Number.Integer), + ], + 'comment': [ + (r'\(\*', Comment.Multiline, '#push'), + (r'\*\)', Comment.Multiline, '#pop'), + ('[^*(]+', Comment.Multiline), + ('[*(]', Comment.Multiline), + ], + } + + +class RexxLexer(RegexLexer): + """ + `Rexx `_ is a scripting language available for + a wide range of different platforms with its roots found on mainframe + systems. It is popular for I/O- and data based tasks and can act as glue + language to bind different applications together. + + .. versionadded:: 2.0 + """ + name = 'Rexx' + aliases = ['rexx', 'arexx'] + filenames = ['*.rexx', '*.rex', '*.rx', '*.arexx'] + mimetypes = ['text/x-rexx'] + flags = re.IGNORECASE + + tokens = { + 'root': [ + (r'\s', Whitespace), + (r'/\*', Comment.Multiline, 'comment'), + (r'"', String, 'string_double'), + (r"'", String, 'string_single'), + (r'[0-9]+(\.[0-9]+)?(e[+-]?[0-9])?', Number), + (r'([a-z_]\w*)(\s*)(:)(\s*)(procedure)\b', + bygroups(Name.Function, Whitespace, Operator, Whitespace, + Keyword.Declaration)), + (r'([a-z_]\w*)(\s*)(:)', + bygroups(Name.Label, Whitespace, Operator)), + include('function'), + include('keyword'), + include('operator'), + (r'[a-z_]\w*', Text), + ], + 'function': [ + (words(( + 'abbrev', 'abs', 'address', 'arg', 'b2x', 'bitand', 'bitor', 'bitxor', + 'c2d', 'c2x', 'center', 'charin', 'charout', 'chars', 'compare', + 'condition', 'copies', 'd2c', 'd2x', 'datatype', 'date', 'delstr', + 'delword', 'digits', 'errortext', 'form', 'format', 'fuzz', 'insert', + 'lastpos', 'left', 'length', 'linein', 'lineout', 'lines', 'max', + 'min', 'overlay', 'pos', 'queued', 'random', 'reverse', 'right', 'sign', + 'sourceline', 'space', 'stream', 'strip', 'substr', 'subword', 'symbol', + 'time', 'trace', 'translate', 'trunc', 'value', 'verify', 'word', + 'wordindex', 'wordlength', 'wordpos', 'words', 'x2b', 'x2c', 'x2d', + 'xrange'), suffix=r'(\s*)(\()'), + bygroups(Name.Builtin, Whitespace, Operator)), + ], + 'keyword': [ + (r'(address|arg|by|call|do|drop|else|end|exit|for|forever|if|' + r'interpret|iterate|leave|nop|numeric|off|on|options|parse|' + r'pull|push|queue|return|say|select|signal|to|then|trace|until|' + r'while)\b', Keyword.Reserved), + ], + 'operator': [ + (r'(-|//|/|\(|\)|\*\*|\*|\\<<|\\<|\\==|\\=|\\>>|\\>|\\|\|\||\||' + r'&&|&|%|\+|<<=|<<|<=|<>|<|==|=|><|>=|>>=|>>|>|¬<<|¬<|¬==|¬=|' + r'¬>>|¬>|¬|\.|,)', Operator), + ], + 'string_double': [ + (r'[^"\n]+', String), + (r'""', String), + (r'"', String, '#pop'), + (r'\n', Text, '#pop'), # Stray linefeed also terminates strings. + ], + 'string_single': [ + (r'[^\'\n]', String), + (r'\'\'', String), + (r'\'', String, '#pop'), + (r'\n', Text, '#pop'), # Stray linefeed also terminates strings. + ], + 'comment': [ + (r'[^*]+', Comment.Multiline), + (r'\*/', Comment.Multiline, '#pop'), + (r'\*', Comment.Multiline), + ] + } + + _c = lambda s: re.compile(s, re.MULTILINE) + _ADDRESS_COMMAND_PATTERN = _c(r'^\s*address\s+command\b') + _ADDRESS_PATTERN = _c(r'^\s*address\s+') + _DO_WHILE_PATTERN = _c(r'^\s*do\s+while\b') + _IF_THEN_DO_PATTERN = _c(r'^\s*if\b.+\bthen\s+do\s*$') + _PROCEDURE_PATTERN = _c(r'^\s*([a-z_]\w*)(\s*)(:)(\s*)(procedure)\b') + _ELSE_DO_PATTERN = _c(r'\belse\s+do\s*$') + _PARSE_ARG_PATTERN = _c(r'^\s*parse\s+(upper\s+)?(arg|value)\b') + PATTERNS_AND_WEIGHTS = ( + (_ADDRESS_COMMAND_PATTERN, 0.2), + (_ADDRESS_PATTERN, 0.05), + (_DO_WHILE_PATTERN, 0.1), + (_ELSE_DO_PATTERN, 0.1), + (_IF_THEN_DO_PATTERN, 0.1), + (_PROCEDURE_PATTERN, 0.5), + (_PARSE_ARG_PATTERN, 0.2), + ) + + def analyse_text(text): + """ + Check for inital comment and patterns that distinguish Rexx from other + C-like languages. + """ + if re.search(r'/\*\**\s*rexx', text, re.IGNORECASE): + # Header matches MVS Rexx requirements, this is certainly a Rexx + # script. + return 1.0 + elif text.startswith('/*'): + # Header matches general Rexx requirements; the source code might + # still be any language using C comments such as C++, C# or Java. + lowerText = text.lower() + result = sum(weight + for (pattern, weight) in RexxLexer.PATTERNS_AND_WEIGHTS + if pattern.search(lowerText)) + 0.01 + return min(result, 1.0) + + +class MOOCodeLexer(RegexLexer): + """ + For `MOOCode `_ (the MOO scripting + language). + + .. versionadded:: 0.9 + """ + name = 'MOOCode' + filenames = ['*.moo'] + aliases = ['moocode', 'moo'] + mimetypes = ['text/x-moocode'] + + tokens = { + 'root': [ + # Numbers + (r'(0|[1-9][0-9_]*)', Number.Integer), + # Strings + (r'"(\\\\|\\"|[^"])*"', String), + # exceptions + (r'(E_PERM|E_DIV)', Name.Exception), + # db-refs + (r'((#[-0-9]+)|(\$\w+))', Name.Entity), + # Keywords + (r'\b(if|else|elseif|endif|for|endfor|fork|endfork|while' + r'|endwhile|break|continue|return|try' + r'|except|endtry|finally|in)\b', Keyword), + # builtins + (r'(random|length)', Name.Builtin), + # special variables + (r'(player|caller|this|args)', Name.Variable.Instance), + # skip whitespace + (r'\s+', Text), + (r'\n', Text), + # other operators + (r'([!;=,{}&|:.\[\]@()<>?]+)', Operator), + # function call + (r'(\w+)(\()', bygroups(Name.Function, Operator)), + # variables + (r'(\w+)', Text), + ] + } + + +class HybrisLexer(RegexLexer): + """ + For `Hybris `_ source code. + + .. versionadded:: 1.4 + """ + + name = 'Hybris' + aliases = ['hybris', 'hy'] + filenames = ['*.hy', '*.hyb'] + mimetypes = ['text/x-hybris', 'application/x-hybris'] + + flags = re.MULTILINE | re.DOTALL + + tokens = { + 'root': [ + # method names + (r'^(\s*(?:function|method|operator\s+)+?)' + r'([a-zA-Z_]\w*)' + r'(\s*)(\()', bygroups(Keyword, Name.Function, Text, Operator)), + (r'[^\S\n]+', Text), + (r'//.*?\n', Comment.Single), + (r'/\*.*?\*/', Comment.Multiline), + (r'@[a-zA-Z_][\w.]*', Name.Decorator), + (r'(break|case|catch|next|default|do|else|finally|for|foreach|of|' + r'unless|if|new|return|switch|me|throw|try|while)\b', Keyword), + (r'(extends|private|protected|public|static|throws|function|method|' + r'operator)\b', Keyword.Declaration), + (r'(true|false|null|__FILE__|__LINE__|__VERSION__|__LIB_PATH__|' + r'__INC_PATH__)\b', Keyword.Constant), + (r'(class|struct)(\s+)', + bygroups(Keyword.Declaration, Text), 'class'), + (r'(import|include)(\s+)', + bygroups(Keyword.Namespace, Text), 'import'), + (words(( + 'gc_collect', 'gc_mm_items', 'gc_mm_usage', 'gc_collect_threshold', + 'urlencode', 'urldecode', 'base64encode', 'base64decode', 'sha1', 'crc32', + 'sha2', 'md5', 'md5_file', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', + 'cosh', 'exp', 'fabs', 'floor', 'fmod', 'log', 'log10', 'pow', 'sin', + 'sinh', 'sqrt', 'tan', 'tanh', 'isint', 'isfloat', 'ischar', 'isstring', + 'isarray', 'ismap', 'isalias', 'typeof', 'sizeof', 'toint', 'tostring', + 'fromxml', 'toxml', 'binary', 'pack', 'load', 'eval', 'var_names', + 'var_values', 'user_functions', 'dyn_functions', 'methods', 'call', + 'call_method', 'mknod', 'mkfifo', 'mount', 'umount2', 'umount', 'ticks', + 'usleep', 'sleep', 'time', 'strtime', 'strdate', 'dllopen', 'dlllink', + 'dllcall', 'dllcall_argv', 'dllclose', 'env', 'exec', 'fork', 'getpid', + 'wait', 'popen', 'pclose', 'exit', 'kill', 'pthread_create', + 'pthread_create_argv', 'pthread_exit', 'pthread_join', 'pthread_kill', + 'smtp_send', 'http_get', 'http_post', 'http_download', 'socket', 'bind', + 'listen', 'accept', 'getsockname', 'getpeername', 'settimeout', 'connect', + 'server', 'recv', 'send', 'close', 'print', 'println', 'printf', 'input', + 'readline', 'serial_open', 'serial_fcntl', 'serial_get_attr', + 'serial_get_ispeed', 'serial_get_ospeed', 'serial_set_attr', + 'serial_set_ispeed', 'serial_set_ospeed', 'serial_write', 'serial_read', + 'serial_close', 'xml_load', 'xml_parse', 'fopen', 'fseek', 'ftell', + 'fsize', 'fread', 'fwrite', 'fgets', 'fclose', 'file', 'readdir', + 'pcre_replace', 'size', 'pop', 'unmap', 'has', 'keys', 'values', + 'length', 'find', 'substr', 'replace', 'split', 'trim', 'remove', + 'contains', 'join'), suffix=r'\b'), + Name.Builtin), + (words(( + 'MethodReference', 'Runner', 'Dll', 'Thread', 'Pipe', 'Process', + 'Runnable', 'CGI', 'ClientSocket', 'Socket', 'ServerSocket', + 'File', 'Console', 'Directory', 'Exception'), suffix=r'\b'), + Keyword.Type), + (r'"(\\\\|\\"|[^"])*"', String), + (r"'\\.'|'[^\\]'|'\\u[0-9a-f]{4}'", String.Char), + (r'(\.)([a-zA-Z_]\w*)', + bygroups(Operator, Name.Attribute)), + (r'[a-zA-Z_]\w*:', Name.Label), + (r'[a-zA-Z_$]\w*', Name), + (r'[~^*!%&\[\](){}<>|+=:;,./?\-@]+', Operator), + (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number.Float), + (r'0x[0-9a-f]+', Number.Hex), + (r'[0-9]+L?', Number.Integer), + (r'\n', Text), + ], + 'class': [ + (r'[a-zA-Z_]\w*', Name.Class, '#pop') + ], + 'import': [ + (r'[\w.]+\*?', Name.Namespace, '#pop') + ], + } + + def analyse_text(text): + """public method and private method don't seem to be quite common + elsewhere.""" + result = 0 + if re.search(r'\b(?:public|private)\s+method\b', text): + result += 0.01 + return result + + + +class EasytrieveLexer(RegexLexer): + """ + Easytrieve Plus is a programming language for extracting, filtering and + converting sequential data. Furthermore it can layout data for reports. + It is mainly used on mainframe platforms and can access several of the + mainframe's native file formats. It is somewhat comparable to awk. + + .. versionadded:: 2.1 + """ + name = 'Easytrieve' + aliases = ['easytrieve'] + filenames = ['*.ezt', '*.mac'] + mimetypes = ['text/x-easytrieve'] + flags = 0 + + # Note: We cannot use r'\b' at the start and end of keywords because + # Easytrieve Plus delimiter characters are: + # + # * space ( ) + # * apostrophe (') + # * period (.) + # * comma (,) + # * paranthesis ( and ) + # * colon (:) + # + # Additionally words end once a '*' appears, indicatins a comment. + _DELIMITERS = r' \'.,():\n' + _DELIMITERS_OR_COMENT = _DELIMITERS + '*' + _DELIMITER_PATTERN = '[' + _DELIMITERS + ']' + _DELIMITER_PATTERN_CAPTURE = '(' + _DELIMITER_PATTERN + ')' + _NON_DELIMITER_OR_COMMENT_PATTERN = '[^' + _DELIMITERS_OR_COMENT + ']' + _OPERATORS_PATTERN = '[.+\\-/=\\[\\](){}<>;,&%¬]' + _KEYWORDS = [ + 'AFTER-BREAK', 'AFTER-LINE', 'AFTER-SCREEN', 'AIM', 'AND', 'ATTR', + 'BEFORE', 'BEFORE-BREAK', 'BEFORE-LINE', 'BEFORE-SCREEN', 'BUSHU', + 'BY', 'CALL', 'CASE', 'CHECKPOINT', 'CHKP', 'CHKP-STATUS', 'CLEAR', + 'CLOSE', 'COL', 'COLOR', 'COMMIT', 'CONTROL', 'COPY', 'CURSOR', 'D', + 'DECLARE', 'DEFAULT', 'DEFINE', 'DELETE', 'DENWA', 'DISPLAY', 'DLI', + 'DO', 'DUPLICATE', 'E', 'ELSE', 'ELSE-IF', 'END', 'END-CASE', + 'END-DO', 'END-IF', 'END-PROC', 'ENDPAGE', 'ENDTABLE', 'ENTER', 'EOF', + 'EQ', 'ERROR', 'EXIT', 'EXTERNAL', 'EZLIB', 'F1', 'F10', 'F11', 'F12', + 'F13', 'F14', 'F15', 'F16', 'F17', 'F18', 'F19', 'F2', 'F20', 'F21', + 'F22', 'F23', 'F24', 'F25', 'F26', 'F27', 'F28', 'F29', 'F3', 'F30', + 'F31', 'F32', 'F33', 'F34', 'F35', 'F36', 'F4', 'F5', 'F6', 'F7', + 'F8', 'F9', 'FETCH', 'FILE-STATUS', 'FILL', 'FINAL', 'FIRST', + 'FIRST-DUP', 'FOR', 'GE', 'GET', 'GO', 'GOTO', 'GQ', 'GR', 'GT', + 'HEADING', 'HEX', 'HIGH-VALUES', 'IDD', 'IDMS', 'IF', 'IN', 'INSERT', + 'JUSTIFY', 'KANJI-DATE', 'KANJI-DATE-LONG', 'KANJI-TIME', 'KEY', + 'KEY-PRESSED', 'KOKUGO', 'KUN', 'LAST-DUP', 'LE', 'LEVEL', 'LIKE', + 'LINE', 'LINE-COUNT', 'LINE-NUMBER', 'LINK', 'LIST', 'LOW-VALUES', + 'LQ', 'LS', 'LT', 'MACRO', 'MASK', 'MATCHED', 'MEND', 'MESSAGE', + 'MOVE', 'MSTART', 'NE', 'NEWPAGE', 'NOMASK', 'NOPRINT', 'NOT', + 'NOTE', 'NOVERIFY', 'NQ', 'NULL', 'OF', 'OR', 'OTHERWISE', 'PA1', + 'PA2', 'PA3', 'PAGE-COUNT', 'PAGE-NUMBER', 'PARM-REGISTER', + 'PATH-ID', 'PATTERN', 'PERFORM', 'POINT', 'POS', 'PRIMARY', 'PRINT', + 'PROCEDURE', 'PROGRAM', 'PUT', 'READ', 'RECORD', 'RECORD-COUNT', + 'RECORD-LENGTH', 'REFRESH', 'RELEASE', 'RENUM', 'REPEAT', 'REPORT', + 'REPORT-INPUT', 'RESHOW', 'RESTART', 'RETRIEVE', 'RETURN-CODE', + 'ROLLBACK', 'ROW', 'S', 'SCREEN', 'SEARCH', 'SECONDARY', 'SELECT', + 'SEQUENCE', 'SIZE', 'SKIP', 'SOKAKU', 'SORT', 'SQL', 'STOP', 'SUM', + 'SYSDATE', 'SYSDATE-LONG', 'SYSIN', 'SYSIPT', 'SYSLST', 'SYSPRINT', + 'SYSSNAP', 'SYSTIME', 'TALLY', 'TERM-COLUMNS', 'TERM-NAME', + 'TERM-ROWS', 'TERMINATION', 'TITLE', 'TO', 'TRANSFER', 'TRC', + 'UNIQUE', 'UNTIL', 'UPDATE', 'UPPERCASE', 'USER', 'USERID', 'VALUE', + 'VERIFY', 'W', 'WHEN', 'WHILE', 'WORK', 'WRITE', 'X', 'XDM', 'XRST' + ] + + tokens = { + 'root': [ + (r'\*.*\n', Comment.Single), + (r'\n+', Whitespace), + # Macro argument + (r'&' + _NON_DELIMITER_OR_COMMENT_PATTERN + r'+\.', Name.Variable, + 'after_macro_argument'), + # Macro call + (r'%' + _NON_DELIMITER_OR_COMMENT_PATTERN + r'+', Name.Variable), + (r'(FILE|MACRO|REPORT)(\s+)', + bygroups(Keyword.Declaration, Whitespace), 'after_declaration'), + (r'(JOB|PARM)' + r'(' + _DELIMITER_PATTERN + r')', + bygroups(Keyword.Declaration, Operator)), + (words(_KEYWORDS, suffix=_DELIMITER_PATTERN_CAPTURE), + bygroups(Keyword.Reserved, Operator)), + (_OPERATORS_PATTERN, Operator), + # Procedure declaration + (r'(' + _NON_DELIMITER_OR_COMMENT_PATTERN + r'+)(\s*)(\.?)(\s*)(PROC)(\s*\n)', + bygroups(Name.Function, Whitespace, Operator, Whitespace, + Keyword.Declaration, Whitespace)), + (r'[0-9]+\.[0-9]*', Number.Float), + (r'[0-9]+', Number.Integer), + (r"'(''|[^'])*'", String), + (r'\s+', Whitespace), + # Everything else just belongs to a name + (_NON_DELIMITER_OR_COMMENT_PATTERN + r'+', Name), + ], + 'after_declaration': [ + (_NON_DELIMITER_OR_COMMENT_PATTERN + r'+', Name.Function), + default('#pop'), + ], + 'after_macro_argument': [ + (r'\*.*\n', Comment.Single, '#pop'), + (r'\s+', Whitespace, '#pop'), + (_OPERATORS_PATTERN, Operator, '#pop'), + (r"'(''|[^'])*'", String, '#pop'), + # Everything else just belongs to a name + (_NON_DELIMITER_OR_COMMENT_PATTERN + r'+', Name), + ], + } + _COMMENT_LINE_REGEX = re.compile(r'^\s*\*') + _MACRO_HEADER_REGEX = re.compile(r'^\s*MACRO') + + def analyse_text(text): + """ + Perform a structural analysis for basic Easytrieve constructs. + """ + result = 0.0 + lines = text.split('\n') + hasEndProc = False + hasHeaderComment = False + hasFile = False + hasJob = False + hasProc = False + hasParm = False + hasReport = False + + def isCommentLine(line): + return EasytrieveLexer._COMMENT_LINE_REGEX.match(lines[0]) is not None + + def isEmptyLine(line): + return not bool(line.strip()) + + # Remove possible empty lines and header comments. + while lines and (isEmptyLine(lines[0]) or isCommentLine(lines[0])): + if not isEmptyLine(lines[0]): + hasHeaderComment = True + del lines[0] + + if EasytrieveLexer._MACRO_HEADER_REGEX.match(lines[0]): + # Looks like an Easytrieve macro. + result = 0.4 + if hasHeaderComment: + result += 0.4 + else: + # Scan the source for lines starting with indicators. + for line in lines: + words = line.split() + if (len(words) >= 2): + firstWord = words[0] + if not hasReport: + if not hasJob: + if not hasFile: + if not hasParm: + if firstWord == 'PARM': + hasParm = True + if firstWord == 'FILE': + hasFile = True + if firstWord == 'JOB': + hasJob = True + elif firstWord == 'PROC': + hasProc = True + elif firstWord == 'END-PROC': + hasEndProc = True + elif firstWord == 'REPORT': + hasReport = True + + # Weight the findings. + if hasJob and (hasProc == hasEndProc): + if hasHeaderComment: + result += 0.1 + if hasParm: + if hasProc: + # Found PARM, JOB and PROC/END-PROC: + # pretty sure this is Easytrieve. + result += 0.8 + else: + # Found PARAM and JOB: probably this is Easytrieve + result += 0.5 + else: + # Found JOB and possibly other keywords: might be Easytrieve + result += 0.11 + if hasParm: + # Note: PARAM is not a proper English word, so this is + # regarded a much better indicator for Easytrieve than + # the other words. + result += 0.2 + if hasFile: + result += 0.01 + if hasReport: + result += 0.01 + assert 0.0 <= result <= 1.0 + return result + + +class JclLexer(RegexLexer): + """ + `Job Control Language (JCL) + `_ + is a scripting language used on mainframe platforms to instruct the system + on how to run a batch job or start a subsystem. It is somewhat + comparable to MS DOS batch and Unix shell scripts. + + .. versionadded:: 2.1 + """ + name = 'JCL' + aliases = ['jcl'] + filenames = ['*.jcl'] + mimetypes = ['text/x-jcl'] + flags = re.IGNORECASE + + tokens = { + 'root': [ + (r'//\*.*\n', Comment.Single), + (r'//', Keyword.Pseudo, 'statement'), + (r'/\*', Keyword.Pseudo, 'jes2_statement'), + # TODO: JES3 statement + (r'.*\n', Other) # Input text or inline code in any language. + ], + 'statement': [ + (r'\s*\n', Whitespace, '#pop'), + (r'([a-z]\w*)(\s+)(exec|job)(\s*)', + bygroups(Name.Label, Whitespace, Keyword.Reserved, Whitespace), + 'option'), + (r'[a-z]\w*', Name.Variable, 'statement_command'), + (r'\s+', Whitespace, 'statement_command'), + ], + 'statement_command': [ + (r'\s+(command|cntl|dd|endctl|endif|else|include|jcllib|' + r'output|pend|proc|set|then|xmit)\s+', Keyword.Reserved, 'option'), + include('option') + ], + 'jes2_statement': [ + (r'\s*\n', Whitespace, '#pop'), + (r'\$', Keyword, 'option'), + (r'\b(jobparam|message|netacct|notify|output|priority|route|' + r'setup|signoff|xeq|xmit)\b', Keyword, 'option'), + ], + 'option': [ + # (r'\n', Text, 'root'), + (r'\*', Name.Builtin), + (r'[\[\](){}<>;,]', Punctuation), + (r'[-+*/=&%]', Operator), + (r'[a-z_]\w*', Name), + (r'\d+\.\d*', Number.Float), + (r'\.\d+', Number.Float), + (r'\d+', Number.Integer), + (r"'", String, 'option_string'), + (r'[ \t]+', Whitespace, 'option_comment'), + (r'\.', Punctuation), + ], + 'option_string': [ + (r"(\n)(//)", bygroups(Text, Keyword.Pseudo)), + (r"''", String), + (r"[^']", String), + (r"'", String, '#pop'), + ], + 'option_comment': [ + # (r'\n', Text, 'root'), + (r'.+', Comment.Single), + ] + } + + _JOB_HEADER_PATTERN = re.compile(r'^//[a-z#$@][a-z0-9#$@]{0,7}\s+job(\s+.*)?$', + re.IGNORECASE) + + def analyse_text(text): + """ + Recognize JCL job by header. + """ + result = 0.0 + lines = text.split('\n') + if len(lines) > 0: + if JclLexer._JOB_HEADER_PATTERN.match(lines[0]): + result = 1.0 + assert 0.0 <= result <= 1.0 + return result + + +class MiniScriptLexer(RegexLexer): + """ + For `MiniScript `_ source code. + + .. versionadded:: 2.6 + """ + + name = "MiniScript" + aliases = ["ms", "miniscript"] + filenames = ["*.ms"] + mimetypes = ['text/x-minicript', 'application/x-miniscript'] + + tokens = { + 'root': [ + (r'#!(.*?)$', Comment.Preproc), + default('base'), + ], + 'base': [ + ('//.*$', Comment.Single), + (r'(?i)(\d*\.\d+|\d+\.\d*)(e[+-]?\d+)?', Number), + (r'(?i)\d+e[+-]?\d+', Number), + (r'\d+', Number), + (r'\n', Text), + (r'[^\S\n]+', Text), + (r'"', String, 'string_double'), + (r'(==|!=|<=|>=|[=+\-*/%^<>.:])', Operator), + (r'[;,\[\]{}()]', Punctuation), + (words(( + 'break', 'continue', 'else', 'end', 'for', 'function', 'if', + 'in', 'isa', 'then', 'repeat', 'return', 'while'), suffix=r'\b'), + Keyword), + (words(( + 'abs', 'acos', 'asin', 'atan', 'ceil', 'char', 'cos', 'floor', + 'log', 'round', 'rnd', 'pi', 'sign', 'sin', 'sqrt', 'str', 'tan', + 'hasIndex', 'indexOf', 'len', 'val', 'code', 'remove', 'lower', + 'upper', 'replace', 'split', 'indexes', 'values', 'join', 'sum', + 'sort', 'shuffle', 'push', 'pop', 'pull', 'range', + 'print', 'input', 'time', 'wait', 'locals', 'globals', 'outer', + 'yield'), suffix=r'\b'), + Name.Builtin), + (r'(true|false|null)\b', Keyword.Constant), + (r'(and|or|not|new)\b', Operator.Word), + (r'(self|super|__isa)\b', Name.Builtin.Pseudo), + (r'[a-zA-Z_]\w*', Name.Variable) + ], + 'string_double': [ + (r'[^"\n]+', String), + (r'""', String), + (r'"', String, '#pop'), + (r'\n', Text, '#pop'), # Stray linefeed also terminates strings. + ] + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/sgf.py b/.venv/lib/python3.8/site-packages/pygments/lexers/sgf.py new file mode 100644 index 00000000..6dfd275a --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/sgf.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.sgf + ~~~~~~~~~~~~~~~~~~~ + + Lexer for Smart Game Format (sgf) file format. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, bygroups +from pygments.token import Name, Literal, String, Text, Punctuation + +__all__ = ["SmartGameFormatLexer"] + + +class SmartGameFormatLexer(RegexLexer): + """ + Lexer for Smart Game Format (sgf) file format. + + The format is used to store game records of board games for two players + (mainly Go game). + For more information about the definition of the format, see: + https://www.red-bean.com/sgf/ + + .. versionadded:: 2.4 + """ + name = 'SmartGameFormat' + aliases = ['sgf'] + filenames = ['*.sgf'] + + tokens = { + 'root': [ + (r'[\s():;]', Punctuation), + # tokens: + (r'(A[BW]|AE|AN|AP|AR|AS|[BW]L|BM|[BW]R|[BW]S|[BW]T|CA|CH|CP|CR|' + r'DD|DM|DO|DT|EL|EV|EX|FF|FG|G[BW]|GC|GM|GN|HA|HO|ID|IP|IT|IY|KM|' + r'KO|LB|LN|LT|L|MA|MN|M|N|OB|OM|ON|OP|OT|OV|P[BW]|PC|PL|PM|RE|RG|' + r'RO|RU|SO|SC|SE|SI|SL|SO|SQ|ST|SU|SZ|T[BW]|TC|TE|TM|TR|UC|US|VW|' + r'V|[BW]|C)', + Name.Builtin), + # number: + (r'(\[)([0-9.]+)(\])', + bygroups(Punctuation, Literal.Number, Punctuation)), + # date: + (r'(\[)([0-9]{4}-[0-9]{2}-[0-9]{2})(\])', + bygroups(Punctuation, Literal.Date, Punctuation)), + # point: + (r'(\[)([a-z]{2})(\])', + bygroups(Punctuation, String, Punctuation)), + # double points: + (r'(\[)([a-z]{2})(:)([a-z]{2})(\])', + bygroups(Punctuation, String, Punctuation, String, Punctuation)), + + (r'(\[)([\w\s#()+,\-.:?]+)(\])', + bygroups(Punctuation, String, Punctuation)), + (r'(\[)(\s.*)(\])', + bygroups(Punctuation, Text, Punctuation)), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/shell.py b/.venv/lib/python3.8/site-packages/pygments/lexers/shell.py new file mode 100644 index 00000000..713d4321 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/shell.py @@ -0,0 +1,914 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.shell + ~~~~~~~~~~~~~~~~~~~~~ + + Lexers for various shells. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import Lexer, RegexLexer, do_insertions, bygroups, \ + include, default, this, using, words +from pygments.token import Punctuation, \ + Text, Comment, Operator, Keyword, Name, String, Number, Generic +from pygments.util import shebang_matches + + +__all__ = ['BashLexer', 'BashSessionLexer', 'TcshLexer', 'BatchLexer', + 'SlurmBashLexer', 'MSDOSSessionLexer', 'PowerShellLexer', + 'PowerShellSessionLexer', 'TcshSessionLexer', 'FishShellLexer', + 'ExeclineLexer'] + +line_re = re.compile('.*?\n') + + +class BashLexer(RegexLexer): + """ + Lexer for (ba|k|z|)sh shell scripts. + + .. versionadded:: 0.6 + """ + + name = 'Bash' + aliases = ['bash', 'sh', 'ksh', 'zsh', 'shell'] + filenames = ['*.sh', '*.ksh', '*.bash', '*.ebuild', '*.eclass', + '*.exheres-0', '*.exlib', '*.zsh', + '.bashrc', 'bashrc', '.bash_*', 'bash_*', 'zshrc', '.zshrc', + 'PKGBUILD'] + mimetypes = ['application/x-sh', 'application/x-shellscript', 'text/x-shellscript'] + + tokens = { + 'root': [ + include('basic'), + (r'`', String.Backtick, 'backticks'), + include('data'), + include('interp'), + ], + 'interp': [ + (r'\$\(\(', Keyword, 'math'), + (r'\$\(', Keyword, 'paren'), + (r'\$\{#?', String.Interpol, 'curly'), + (r'\$[a-zA-Z_]\w*', Name.Variable), # user variable + (r'\$(?:\d+|[#$?!_*@-])', Name.Variable), # builtin + (r'\$', Text), + ], + 'basic': [ + (r'\b(if|fi|else|while|do|done|for|then|return|function|case|' + r'select|continue|until|esac|elif)(\s*)\b', + bygroups(Keyword, Text)), + (r'\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|' + r'complete|declare|dirs|disown|echo|enable|eval|exec|exit|' + r'export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|' + r'local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|' + r'shopt|source|suspend|test|time|times|trap|true|type|typeset|' + r'ulimit|umask|unalias|unset|wait)(?=[\s)`])', + Name.Builtin), + (r'\A#!.+\n', Comment.Hashbang), + (r'#.*\n', Comment.Single), + (r'\\[\w\W]', String.Escape), + (r'(\b\w+)(\s*)(\+?=)', bygroups(Name.Variable, Text, Operator)), + (r'[\[\]{}()=]', Operator), + (r'<<<', Operator), # here-string + (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String), + (r'&&|\|\|', Operator), + ], + 'data': [ + (r'(?s)\$?"(\\.|[^"\\$])*"', String.Double), + (r'"', String.Double, 'string'), + (r"(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single), + (r"(?s)'.*?'", String.Single), + (r';', Punctuation), + (r'&', Punctuation), + (r'\|', Punctuation), + (r'\s+', Text), + (r'\d+\b', Number), + (r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text), + (r'<', Text), + ], + 'string': [ + (r'"', String.Double, '#pop'), + (r'(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+', String.Double), + include('interp'), + ], + 'curly': [ + (r'\}', String.Interpol, '#pop'), + (r':-', Keyword), + (r'\w+', Name.Variable), + (r'[^}:"\'`$\\]+', Punctuation), + (r':', Punctuation), + include('root'), + ], + 'paren': [ + (r'\)', Keyword, '#pop'), + include('root'), + ], + 'math': [ + (r'\)\)', Keyword, '#pop'), + (r'[-+*/%^|&]|\*\*|\|\|', Operator), + (r'\d+#\d+', Number), + (r'\d+#(?! )', Number), + (r'\d+', Number), + include('root'), + ], + 'backticks': [ + (r'`', String.Backtick, '#pop'), + include('root'), + ], + } + + def analyse_text(text): + if shebang_matches(text, r'(ba|z|)sh'): + return 1 + if text.startswith('$ '): + return 0.2 + + +class SlurmBashLexer(BashLexer): + """ + Lexer for (ba|k|z|)sh Slurm scripts. + + .. versionadded:: 2.4 + """ + + name = 'Slurm' + aliases = ['slurm', 'sbatch'] + filenames = ['*.sl'] + mimetypes = [] + EXTRA_KEYWORDS = {'srun'} + + def get_tokens_unprocessed(self, text): + for index, token, value in BashLexer.get_tokens_unprocessed(self, text): + if token is Text and value in self.EXTRA_KEYWORDS: + yield index, Name.Builtin, value + elif token is Comment.Single and 'SBATCH' in value: + yield index, Keyword.Pseudo, value + else: + yield index, token, value + + +class ShellSessionBaseLexer(Lexer): + """ + Base lexer for shell sessions. + + .. versionadded:: 2.1 + """ + + _venv = re.compile(r'^(\([^)]*\))(\s*)') + + def get_tokens_unprocessed(self, text): + innerlexer = self._innerLexerCls(**self.options) + + pos = 0 + curcode = '' + insertions = [] + backslash_continuation = False + + for match in line_re.finditer(text): + line = match.group() + if backslash_continuation: + curcode += line + backslash_continuation = curcode.endswith('\\\n') + continue + + venv_match = self._venv.match(line) + if venv_match: + venv = venv_match.group(1) + venv_whitespace = venv_match.group(2) + insertions.append((len(curcode), + [(0, Generic.Prompt.VirtualEnv, venv)])) + if venv_whitespace: + insertions.append((len(curcode), + [(0, Text, venv_whitespace)])) + line = line[venv_match.end():] + + m = self._ps1rgx.match(line) + if m: + # To support output lexers (say diff output), the output + # needs to be broken by prompts whenever the output lexer + # changes. + if not insertions: + pos = match.start() + + insertions.append((len(curcode), + [(0, Generic.Prompt, m.group(1))])) + curcode += m.group(2) + backslash_continuation = curcode.endswith('\\\n') + elif line.startswith(self._ps2): + insertions.append((len(curcode), + [(0, Generic.Prompt, line[:len(self._ps2)])])) + curcode += line[len(self._ps2):] + backslash_continuation = curcode.endswith('\\\n') + else: + if insertions: + toks = innerlexer.get_tokens_unprocessed(curcode) + for i, t, v in do_insertions(insertions, toks): + yield pos+i, t, v + yield match.start(), Generic.Output, line + insertions = [] + curcode = '' + if insertions: + for i, t, v in do_insertions(insertions, + innerlexer.get_tokens_unprocessed(curcode)): + yield pos+i, t, v + + +class BashSessionLexer(ShellSessionBaseLexer): + """ + Lexer for Bash shell sessions, i.e. command lines, including a + prompt, interspersed with output. + + .. versionadded:: 1.1 + """ + + name = 'Bash Session' + aliases = ['console', 'shell-session'] + filenames = ['*.sh-session', '*.shell-session'] + mimetypes = ['application/x-shell-session', 'application/x-sh-session'] + + _innerLexerCls = BashLexer + _ps1rgx = re.compile( + r'^((?:(?:\[.*?\])|(?:\(\S+\))?(?:| |sh\S*?|\w+\S+[@:]\S+(?:\s+\S+)' \ + r'?|\[\S+[@:][^\n]+\].+))\s*[$#%])(.*\n?)') + _ps2 = '>' + + +class BatchLexer(RegexLexer): + """ + Lexer for the DOS/Windows Batch file format. + + .. versionadded:: 0.7 + """ + name = 'Batchfile' + aliases = ['bat', 'batch', 'dosbatch', 'winbatch'] + filenames = ['*.bat', '*.cmd'] + mimetypes = ['application/x-dos-batch'] + + flags = re.MULTILINE | re.IGNORECASE + + _nl = r'\n\x1a' + _punct = r'&<>|' + _ws = r'\t\v\f\r ,;=\xa0' + _nlws = r'\s\x1a\xa0,;=' + _space = r'(?:(?:(?:\^[%s])?[%s])+)' % (_nl, _ws) + _keyword_terminator = (r'(?=(?:\^[%s]?)?[%s+./:[\\\]]|[%s%s(])' % + (_nl, _ws, _nl, _punct)) + _token_terminator = r'(?=\^?[%s]|[%s%s])' % (_ws, _punct, _nl) + _start_label = r'((?:(?<=^[^:])|^[^:]?)[%s]*)(:)' % _ws + _label = r'(?:(?:[^%s%s+:^]|\^[%s]?[\w\W])*)' % (_nlws, _punct, _nl) + _label_compound = r'(?:(?:[^%s%s+:^)]|\^[%s]?[^)])*)' % (_nlws, _punct, _nl) + _number = r'(?:-?(?:0[0-7]+|0x[\da-f]+|\d+)%s)' % _token_terminator + _opword = r'(?:equ|geq|gtr|leq|lss|neq)' + _string = r'(?:"[^%s"]*(?:"|(?=[%s])))' % (_nl, _nl) + _variable = (r'(?:(?:%%(?:\*|(?:~[a-z]*(?:\$[^:]+:)?)?\d|' + r'[^%%:%s]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:[^%%%s^]|' + r'\^[^%%%s])[^=%s]*=(?:[^%%%s^]|\^[^%%%s])*)?)?%%))|' + r'(?:\^?![^!:%s]+(?::(?:~(?:-?\d+)?(?:,(?:-?\d+)?)?|(?:' + r'[^!%s^]|\^[^!%s])[^=%s]*=(?:[^!%s^]|\^[^!%s])*)?)?\^?!))' % + (_nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl, _nl)) + _core_token = r'(?:(?:(?:\^[%s]?)?[^"%s%s])+)' % (_nl, _nlws, _punct) + _core_token_compound = r'(?:(?:(?:\^[%s]?)?[^"%s%s)])+)' % (_nl, _nlws, _punct) + _token = r'(?:[%s]+|%s)' % (_punct, _core_token) + _token_compound = r'(?:[%s]+|%s)' % (_punct, _core_token_compound) + _stoken = (r'(?:[%s]+|(?:%s|%s|%s)+)' % + (_punct, _string, _variable, _core_token)) + + def _make_begin_state(compound, _core_token=_core_token, + _core_token_compound=_core_token_compound, + _keyword_terminator=_keyword_terminator, + _nl=_nl, _punct=_punct, _string=_string, + _space=_space, _start_label=_start_label, + _stoken=_stoken, _token_terminator=_token_terminator, + _variable=_variable, _ws=_ws): + rest = '(?:%s|%s|[^"%%%s%s%s])*' % (_string, _variable, _nl, _punct, + ')' if compound else '') + rest_of_line = r'(?:(?:[^%s^]|\^[%s]?[\w\W])*)' % (_nl, _nl) + rest_of_line_compound = r'(?:(?:[^%s^)]|\^[%s]?[^)])*)' % (_nl, _nl) + set_space = r'((?:(?:\^[%s]?)?[^\S\n])*)' % _nl + suffix = '' + if compound: + _keyword_terminator = r'(?:(?=\))|%s)' % _keyword_terminator + _token_terminator = r'(?:(?=\))|%s)' % _token_terminator + suffix = '/compound' + return [ + ((r'\)', Punctuation, '#pop') if compound else + (r'\)((?=\()|%s)%s' % (_token_terminator, rest_of_line), + Comment.Single)), + (r'(?=%s)' % _start_label, Text, 'follow%s' % suffix), + (_space, using(this, state='text')), + include('redirect%s' % suffix), + (r'[%s]+' % _nl, Text), + (r'\(', Punctuation, 'root/compound'), + (r'@+', Punctuation), + (r'((?:for|if|rem)(?:(?=(?:\^[%s]?)?/)|(?:(?!\^)|' + r'(?<=m))(?:(?=\()|%s)))(%s?%s?(?:\^[%s]?)?/(?:\^[%s]?)?\?)' % + (_nl, _token_terminator, _space, + _core_token_compound if compound else _core_token, _nl, _nl), + bygroups(Keyword, using(this, state='text')), + 'follow%s' % suffix), + (r'(goto%s)(%s(?:\^[%s]?)?/(?:\^[%s]?)?\?%s)' % + (_keyword_terminator, rest, _nl, _nl, rest), + bygroups(Keyword, using(this, state='text')), + 'follow%s' % suffix), + (words(('assoc', 'break', 'cd', 'chdir', 'cls', 'color', 'copy', + 'date', 'del', 'dir', 'dpath', 'echo', 'endlocal', 'erase', + 'exit', 'ftype', 'keys', 'md', 'mkdir', 'mklink', 'move', + 'path', 'pause', 'popd', 'prompt', 'pushd', 'rd', 'ren', + 'rename', 'rmdir', 'setlocal', 'shift', 'start', 'time', + 'title', 'type', 'ver', 'verify', 'vol'), + suffix=_keyword_terminator), Keyword, 'follow%s' % suffix), + (r'(call)(%s?)(:)' % _space, + bygroups(Keyword, using(this, state='text'), Punctuation), + 'call%s' % suffix), + (r'call%s' % _keyword_terminator, Keyword), + (r'(for%s(?!\^))(%s)(/f%s)' % + (_token_terminator, _space, _token_terminator), + bygroups(Keyword, using(this, state='text'), Keyword), + ('for/f', 'for')), + (r'(for%s(?!\^))(%s)(/l%s)' % + (_token_terminator, _space, _token_terminator), + bygroups(Keyword, using(this, state='text'), Keyword), + ('for/l', 'for')), + (r'for%s(?!\^)' % _token_terminator, Keyword, ('for2', 'for')), + (r'(goto%s)(%s?)(:?)' % (_keyword_terminator, _space), + bygroups(Keyword, using(this, state='text'), Punctuation), + 'label%s' % suffix), + (r'(if(?:(?=\()|%s)(?!\^))(%s?)((?:/i%s)?)(%s?)((?:not%s)?)(%s?)' % + (_token_terminator, _space, _token_terminator, _space, + _token_terminator, _space), + bygroups(Keyword, using(this, state='text'), Keyword, + using(this, state='text'), Keyword, + using(this, state='text')), ('(?', 'if')), + (r'rem(((?=\()|%s)%s?%s?.*|%s%s)' % + (_token_terminator, _space, _stoken, _keyword_terminator, + rest_of_line_compound if compound else rest_of_line), + Comment.Single, 'follow%s' % suffix), + (r'(set%s)%s(/a)' % (_keyword_terminator, set_space), + bygroups(Keyword, using(this, state='text'), Keyword), + 'arithmetic%s' % suffix), + (r'(set%s)%s((?:/p)?)%s((?:(?:(?:\^[%s]?)?[^"%s%s^=%s]|' + r'\^[%s]?[^"=])+)?)((?:(?:\^[%s]?)?=)?)' % + (_keyword_terminator, set_space, set_space, _nl, _nl, _punct, + ')' if compound else '', _nl, _nl), + bygroups(Keyword, using(this, state='text'), Keyword, + using(this, state='text'), using(this, state='variable'), + Punctuation), + 'follow%s' % suffix), + default('follow%s' % suffix) + ] + + def _make_follow_state(compound, _label=_label, + _label_compound=_label_compound, _nl=_nl, + _space=_space, _start_label=_start_label, + _token=_token, _token_compound=_token_compound, + _ws=_ws): + suffix = '/compound' if compound else '' + state = [] + if compound: + state.append((r'(?=\))', Text, '#pop')) + state += [ + (r'%s([%s]*)(%s)(.*)' % + (_start_label, _ws, _label_compound if compound else _label), + bygroups(Text, Punctuation, Text, Name.Label, Comment.Single)), + include('redirect%s' % suffix), + (r'(?=[%s])' % _nl, Text, '#pop'), + (r'\|\|?|&&?', Punctuation, '#pop'), + include('text') + ] + return state + + def _make_arithmetic_state(compound, _nl=_nl, _punct=_punct, + _string=_string, _variable=_variable, + _ws=_ws, _nlws=_nlws): + op = r'=+\-*/!~' + state = [] + if compound: + state.append((r'(?=\))', Text, '#pop')) + state += [ + (r'0[0-7]+', Number.Oct), + (r'0x[\da-f]+', Number.Hex), + (r'\d+', Number.Integer), + (r'[(),]+', Punctuation), + (r'([%s]|%%|\^\^)+' % op, Operator), + (r'(%s|%s|(\^[%s]?)?[^()%s%%\^"%s%s]|\^[%s]?%s)+' % + (_string, _variable, _nl, op, _nlws, _punct, _nlws, + r'[^)]' if compound else r'[\w\W]'), + using(this, state='variable')), + (r'(?=[\x00|&])', Text, '#pop'), + include('follow') + ] + return state + + def _make_call_state(compound, _label=_label, + _label_compound=_label_compound): + state = [] + if compound: + state.append((r'(?=\))', Text, '#pop')) + state.append((r'(:?)(%s)' % (_label_compound if compound else _label), + bygroups(Punctuation, Name.Label), '#pop')) + return state + + def _make_label_state(compound, _label=_label, + _label_compound=_label_compound, _nl=_nl, + _punct=_punct, _string=_string, _variable=_variable): + state = [] + if compound: + state.append((r'(?=\))', Text, '#pop')) + state.append((r'(%s?)((?:%s|%s|\^[%s]?%s|[^"%%^%s%s%s])*)' % + (_label_compound if compound else _label, _string, + _variable, _nl, r'[^)]' if compound else r'[\w\W]', _nl, + _punct, r')' if compound else ''), + bygroups(Name.Label, Comment.Single), '#pop')) + return state + + def _make_redirect_state(compound, + _core_token_compound=_core_token_compound, + _nl=_nl, _punct=_punct, _stoken=_stoken, + _string=_string, _space=_space, + _variable=_variable, _nlws=_nlws): + stoken_compound = (r'(?:[%s]+|(?:%s|%s|%s)+)' % + (_punct, _string, _variable, _core_token_compound)) + return [ + (r'((?:(?<=[%s])\d)?)(>>?&|<&)([%s]*)(\d)' % + (_nlws, _nlws), + bygroups(Number.Integer, Punctuation, Text, Number.Integer)), + (r'((?:(?<=[%s])(?>?|<)(%s?%s)' % + (_nlws, _nl, _space, stoken_compound if compound else _stoken), + bygroups(Number.Integer, Punctuation, using(this, state='text'))) + ] + + tokens = { + 'root': _make_begin_state(False), + 'follow': _make_follow_state(False), + 'arithmetic': _make_arithmetic_state(False), + 'call': _make_call_state(False), + 'label': _make_label_state(False), + 'redirect': _make_redirect_state(False), + 'root/compound': _make_begin_state(True), + 'follow/compound': _make_follow_state(True), + 'arithmetic/compound': _make_arithmetic_state(True), + 'call/compound': _make_call_state(True), + 'label/compound': _make_label_state(True), + 'redirect/compound': _make_redirect_state(True), + 'variable-or-escape': [ + (_variable, Name.Variable), + (r'%%%%|\^[%s]?(\^!|[\w\W])' % _nl, String.Escape) + ], + 'string': [ + (r'"', String.Double, '#pop'), + (_variable, Name.Variable), + (r'\^!|%%', String.Escape), + (r'[^"%%^%s]+|[%%^]' % _nl, String.Double), + default('#pop') + ], + 'sqstring': [ + include('variable-or-escape'), + (r'[^%]+|%', String.Single) + ], + 'bqstring': [ + include('variable-or-escape'), + (r'[^%]+|%', String.Backtick) + ], + 'text': [ + (r'"', String.Double, 'string'), + include('variable-or-escape'), + (r'[^"%%^%s%s\d)]+|.' % (_nlws, _punct), Text) + ], + 'variable': [ + (r'"', String.Double, 'string'), + include('variable-or-escape'), + (r'[^"%%^%s]+|.' % _nl, Name.Variable) + ], + 'for': [ + (r'(%s)(in)(%s)(\()' % (_space, _space), + bygroups(using(this, state='text'), Keyword, + using(this, state='text'), Punctuation), '#pop'), + include('follow') + ], + 'for2': [ + (r'\)', Punctuation), + (r'(%s)(do%s)' % (_space, _token_terminator), + bygroups(using(this, state='text'), Keyword), '#pop'), + (r'[%s]+' % _nl, Text), + include('follow') + ], + 'for/f': [ + (r'(")((?:%s|[^"])*?")([%s]*)(\))' % (_variable, _nlws), + bygroups(String.Double, using(this, state='string'), Text, + Punctuation)), + (r'"', String.Double, ('#pop', 'for2', 'string')), + (r"('(?:%%%%|%s|[\w\W])*?')([%s]*)(\))" % (_variable, _nlws), + bygroups(using(this, state='sqstring'), Text, Punctuation)), + (r'(`(?:%%%%|%s|[\w\W])*?`)([%s]*)(\))' % (_variable, _nlws), + bygroups(using(this, state='bqstring'), Text, Punctuation)), + include('for2') + ], + 'for/l': [ + (r'-?\d+', Number.Integer), + include('for2') + ], + 'if': [ + (r'((?:cmdextversion|errorlevel)%s)(%s)(\d+)' % + (_token_terminator, _space), + bygroups(Keyword, using(this, state='text'), + Number.Integer), '#pop'), + (r'(defined%s)(%s)(%s)' % (_token_terminator, _space, _stoken), + bygroups(Keyword, using(this, state='text'), + using(this, state='variable')), '#pop'), + (r'(exist%s)(%s%s)' % (_token_terminator, _space, _stoken), + bygroups(Keyword, using(this, state='text')), '#pop'), + (r'(%s%s)(%s)(%s%s)' % (_number, _space, _opword, _space, _number), + bygroups(using(this, state='arithmetic'), Operator.Word, + using(this, state='arithmetic')), '#pop'), + (_stoken, using(this, state='text'), ('#pop', 'if2')), + ], + 'if2': [ + (r'(%s?)(==)(%s?%s)' % (_space, _space, _stoken), + bygroups(using(this, state='text'), Operator, + using(this, state='text')), '#pop'), + (r'(%s)(%s)(%s%s)' % (_space, _opword, _space, _stoken), + bygroups(using(this, state='text'), Operator.Word, + using(this, state='text')), '#pop') + ], + '(?': [ + (_space, using(this, state='text')), + (r'\(', Punctuation, ('#pop', 'else?', 'root/compound')), + default('#pop') + ], + 'else?': [ + (_space, using(this, state='text')), + (r'else%s' % _token_terminator, Keyword, '#pop'), + default('#pop') + ] + } + + +class MSDOSSessionLexer(ShellSessionBaseLexer): + """ + Lexer for MS DOS shell sessions, i.e. command lines, including a + prompt, interspersed with output. + + .. versionadded:: 2.1 + """ + + name = 'MSDOS Session' + aliases = ['doscon'] + filenames = [] + mimetypes = [] + + _innerLexerCls = BatchLexer + _ps1rgx = re.compile(r'^([^>]*>)(.*\n?)') + _ps2 = 'More? ' + + +class TcshLexer(RegexLexer): + """ + Lexer for tcsh scripts. + + .. versionadded:: 0.10 + """ + + name = 'Tcsh' + aliases = ['tcsh', 'csh'] + filenames = ['*.tcsh', '*.csh'] + mimetypes = ['application/x-csh'] + + tokens = { + 'root': [ + include('basic'), + (r'\$\(', Keyword, 'paren'), + (r'\$\{#?', Keyword, 'curly'), + (r'`', String.Backtick, 'backticks'), + include('data'), + ], + 'basic': [ + (r'\b(if|endif|else|while|then|foreach|case|default|' + r'continue|goto|breaksw|end|switch|endsw)\s*\b', + Keyword), + (r'\b(alias|alloc|bg|bindkey|break|builtins|bye|caller|cd|chdir|' + r'complete|dirs|echo|echotc|eval|exec|exit|fg|filetest|getxvers|' + r'glob|getspath|hashstat|history|hup|inlib|jobs|kill|' + r'limit|log|login|logout|ls-F|migrate|newgrp|nice|nohup|notify|' + r'onintr|popd|printenv|pushd|rehash|repeat|rootnode|popd|pushd|' + r'set|shift|sched|setenv|setpath|settc|setty|setxvers|shift|' + r'source|stop|suspend|source|suspend|telltc|time|' + r'umask|unalias|uncomplete|unhash|universe|unlimit|unset|unsetenv|' + r'ver|wait|warp|watchlog|where|which)\s*\b', + Name.Builtin), + (r'#.*', Comment), + (r'\\[\w\W]', String.Escape), + (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)), + (r'[\[\]{}()=]+', Operator), + (r'<<\s*(\'?)\\?(\w+)[\w\W]+?\2', String), + (r';', Punctuation), + ], + 'data': [ + (r'(?s)"(\\\\|\\[0-7]+|\\.|[^"\\])*"', String.Double), + (r"(?s)'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single), + (r'\s+', Text), + (r'[^=\s\[\]{}()$"\'`\\;#]+', Text), + (r'\d+(?= |\Z)', Number), + (r'\$#?(\w+|.)', Name.Variable), + ], + 'curly': [ + (r'\}', Keyword, '#pop'), + (r':-', Keyword), + (r'\w+', Name.Variable), + (r'[^}:"\'`$]+', Punctuation), + (r':', Punctuation), + include('root'), + ], + 'paren': [ + (r'\)', Keyword, '#pop'), + include('root'), + ], + 'backticks': [ + (r'`', String.Backtick, '#pop'), + include('root'), + ], + } + + +class TcshSessionLexer(ShellSessionBaseLexer): + """ + Lexer for Tcsh sessions, i.e. command lines, including a + prompt, interspersed with output. + + .. versionadded:: 2.1 + """ + + name = 'Tcsh Session' + aliases = ['tcshcon'] + filenames = [] + mimetypes = [] + + _innerLexerCls = TcshLexer + _ps1rgx = re.compile(r'^([^>]+>)(.*\n?)') + _ps2 = '? ' + + +class PowerShellLexer(RegexLexer): + """ + For Windows PowerShell code. + + .. versionadded:: 1.5 + """ + name = 'PowerShell' + aliases = ['powershell', 'posh', 'ps1', 'psm1'] + filenames = ['*.ps1', '*.psm1'] + mimetypes = ['text/x-powershell'] + + flags = re.DOTALL | re.IGNORECASE | re.MULTILINE + + keywords = ( + 'while validateset validaterange validatepattern validatelength ' + 'validatecount until trap switch return ref process param parameter in ' + 'if global: function foreach for finally filter end elseif else ' + 'dynamicparam do default continue cmdletbinding break begin alias \\? ' + '% #script #private #local #global mandatory parametersetname position ' + 'valuefrompipeline valuefrompipelinebypropertyname ' + 'valuefromremainingarguments helpmessage try catch throw').split() + + operators = ( + 'and as band bnot bor bxor casesensitive ccontains ceq cge cgt cle ' + 'clike clt cmatch cne cnotcontains cnotlike cnotmatch contains ' + 'creplace eq exact f file ge gt icontains ieq ige igt ile ilike ilt ' + 'imatch ine inotcontains inotlike inotmatch ireplace is isnot le like ' + 'lt match ne not notcontains notlike notmatch or regex replace ' + 'wildcard').split() + + verbs = ( + 'write where watch wait use update unregister unpublish unprotect ' + 'unlock uninstall undo unblock trace test tee take sync switch ' + 'suspend submit stop step start split sort skip show set send select ' + 'search scroll save revoke resume restore restart resolve resize ' + 'reset request repair rename remove register redo receive read push ' + 'publish protect pop ping out optimize open new move mount merge ' + 'measure lock limit join invoke install initialize import hide group ' + 'grant get format foreach find export expand exit enter enable edit ' + 'dismount disconnect disable deny debug cxnew copy convertto ' + 'convertfrom convert connect confirm compress complete compare close ' + 'clear checkpoint block backup assert approve aggregate add').split() + + aliases_ = ( + 'ac asnp cat cd cfs chdir clc clear clhy cli clp cls clv cnsn ' + 'compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo epal ' + 'epcsv epsn erase etsn exsn fc fhx fl foreach ft fw gal gbp gc gci gcm ' + 'gcs gdr ghy gi gjb gl gm gmo gp gps gpv group gsn gsnp gsv gu gv gwmi ' + 'h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp ' + 'ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv ' + 'oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo ' + 'rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc select ' + 'set shcm si sl sleep sls sort sp spjb spps spsv start sujb sv swmi tee ' + 'trcm type wget where wjb write').split() + + commenthelp = ( + 'component description example externalhelp forwardhelpcategory ' + 'forwardhelptargetname functionality inputs link ' + 'notes outputs parameter remotehelprunspace role synopsis').split() + + tokens = { + 'root': [ + # we need to count pairs of parentheses for correct highlight + # of '$(...)' blocks in strings + (r'\(', Punctuation, 'child'), + (r'\s+', Text), + (r'^(\s*#[#\s]*)(\.(?:%s))([^\n]*$)' % '|'.join(commenthelp), + bygroups(Comment, String.Doc, Comment)), + (r'#[^\n]*?$', Comment), + (r'(<|<)#', Comment.Multiline, 'multline'), + (r'@"\n', String.Heredoc, 'heredoc-double'), + (r"@'\n.*?\n'@", String.Heredoc), + # escaped syntax + (r'`[\'"$@-]', Punctuation), + (r'"', String.Double, 'string'), + (r"'([^']|'')*'", String.Single), + (r'(\$|@@|@)((global|script|private|env):)?\w+', + Name.Variable), + (r'(%s)\b' % '|'.join(keywords), Keyword), + (r'-(%s)\b' % '|'.join(operators), Operator), + (r'(%s)-[a-z_]\w*\b' % '|'.join(verbs), Name.Builtin), + (r'(%s)\s' % '|'.join(aliases_), Name.Builtin), + (r'\[[a-z_\[][\w. `,\[\]]*\]', Name.Constant), # .net [type]s + (r'-[a-z_]\w*', Name), + (r'\w+', Name), + (r'[.,;@{}\[\]$()=+*/\\&%!~?^`|<>-]|::', Punctuation), + ], + 'child': [ + (r'\)', Punctuation, '#pop'), + include('root'), + ], + 'multline': [ + (r'[^#&.]+', Comment.Multiline), + (r'#(>|>)', Comment.Multiline, '#pop'), + (r'\.(%s)' % '|'.join(commenthelp), String.Doc), + (r'[#&.]', Comment.Multiline), + ], + 'string': [ + (r"`[0abfnrtv'\"$`]", String.Escape), + (r'[^$`"]+', String.Double), + (r'\$\(', Punctuation, 'child'), + (r'""', String.Double), + (r'[`$]', String.Double), + (r'"', String.Double, '#pop'), + ], + 'heredoc-double': [ + (r'\n"@', String.Heredoc, '#pop'), + (r'\$\(', Punctuation, 'child'), + (r'[^@\n]+"]', String.Heredoc), + (r".", String.Heredoc), + ] + } + + +class PowerShellSessionLexer(ShellSessionBaseLexer): + """ + Lexer for PowerShell sessions, i.e. command lines, including a + prompt, interspersed with output. + + .. versionadded:: 2.1 + """ + + name = 'PowerShell Session' + aliases = ['ps1con'] + filenames = [] + mimetypes = [] + + _innerLexerCls = PowerShellLexer + _ps1rgx = re.compile(r'^((?:\[[^]]+\]: )?PS[^>]*> ?)(.*\n?)') + _ps2 = '>> ' + + +class FishShellLexer(RegexLexer): + """ + Lexer for Fish shell scripts. + + .. versionadded:: 2.1 + """ + + name = 'Fish' + aliases = ['fish', 'fishshell'] + filenames = ['*.fish', '*.load'] + mimetypes = ['application/x-fish'] + + tokens = { + 'root': [ + include('basic'), + include('data'), + include('interp'), + ], + 'interp': [ + (r'\$\(\(', Keyword, 'math'), + (r'\(', Keyword, 'paren'), + (r'\$#?(\w+|.)', Name.Variable), + ], + 'basic': [ + (r'\b(begin|end|if|else|while|break|for|in|return|function|block|' + r'case|continue|switch|not|and|or|set|echo|exit|pwd|true|false|' + r'cd|count|test)(\s*)\b', + bygroups(Keyword, Text)), + (r'\b(alias|bg|bind|breakpoint|builtin|command|commandline|' + r'complete|contains|dirh|dirs|emit|eval|exec|fg|fish|fish_config|' + r'fish_indent|fish_pager|fish_prompt|fish_right_prompt|' + r'fish_update_completions|fishd|funced|funcsave|functions|help|' + r'history|isatty|jobs|math|mimedb|nextd|open|popd|prevd|psub|' + r'pushd|random|read|set_color|source|status|trap|type|ulimit|' + r'umask|vared|fc|getopts|hash|kill|printf|time|wait)\s*\b(?!\.)', + Name.Builtin), + (r'#.*\n', Comment), + (r'\\[\w\W]', String.Escape), + (r'(\b\w+)(\s*)(=)', bygroups(Name.Variable, Text, Operator)), + (r'[\[\]()=]', Operator), + (r'<<-?\s*(\'?)\\?(\w+)[\w\W]+?\2', String), + ], + 'data': [ + (r'(?s)\$?"(\\\\|\\[0-7]+|\\.|[^"\\$])*"', String.Double), + (r'"', String.Double, 'string'), + (r"(?s)\$'(\\\\|\\[0-7]+|\\.|[^'\\])*'", String.Single), + (r"(?s)'.*?'", String.Single), + (r';', Punctuation), + (r'&|\||\^|<|>', Operator), + (r'\s+', Text), + (r'\d+(?= |\Z)', Number), + (r'[^=\s\[\]{}()$"\'`\\<&|;]+', Text), + ], + 'string': [ + (r'"', String.Double, '#pop'), + (r'(?s)(\\\\|\\[0-7]+|\\.|[^"\\$])+', String.Double), + include('interp'), + ], + 'paren': [ + (r'\)', Keyword, '#pop'), + include('root'), + ], + 'math': [ + (r'\)\)', Keyword, '#pop'), + (r'[-+*/%^|&]|\*\*|\|\|', Operator), + (r'\d+#\d+', Number), + (r'\d+#(?! )', Number), + (r'\d+', Number), + include('root'), + ], + } + +class ExeclineLexer(RegexLexer): + """ + Lexer for Laurent Bercot's execline language + (https://skarnet.org/software/execline). + + .. versionadded:: 2.7 + """ + + name = 'execline' + aliases = ['execline'] + filenames = ['*.exec'] + + tokens = { + 'root': [ + include('basic'), + include('data'), + include('interp') + ], + 'interp': [ + (r'\$\{', String.Interpol, 'curly'), + (r'\$[\w@#]+', Name.Variable), # user variable + (r'\$', Text), + ], + 'basic': [ + (r'\b(background|backtick|cd|define|dollarat|elgetopt|' + r'elgetpositionals|elglob|emptyenv|envfile|exec|execlineb|' + r'exit|export|fdblock|fdclose|fdmove|fdreserve|fdswap|' + r'forbacktickx|foreground|forstdin|forx|getcwd|getpid|heredoc|' + r'homeof|if|ifelse|ifte|ifthenelse|importas|loopwhilex|' + r'multidefine|multisubstitute|pipeline|piperw|posix-cd|' + r'redirfd|runblock|shift|trap|tryexec|umask|unexport|wait|' + r'withstdinas)\b', Name.Builtin), + (r'\A#!.+\n', Comment.Hashbang), + (r'#.*\n', Comment.Single), + (r'[{}]', Operator) + ], + 'data': [ + (r'(?s)"(\\.|[^"\\$])*"', String.Double), + (r'"', String.Double, 'string'), + (r'\s+', Text), + (r'[^\s{}$"\\]+', Text) + ], + 'string': [ + (r'"', String.Double, '#pop'), + (r'(?s)(\\\\|\\.|[^"\\$])+', String.Double), + include('interp'), + ], + 'curly': [ + (r'\}', String.Interpol, '#pop'), + (r'[\w#@]+', Name.Variable), + include('root') + ] + + } + + def analyse_text(text): + if shebang_matches(text, r'execlineb'): + return 1 diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/sieve.py b/.venv/lib/python3.8/site-packages/pygments/lexers/sieve.py new file mode 100644 index 00000000..9d0b16e5 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/sieve.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.sieve + ~~~~~~~~~~~~~~~~~~~~~ + + Lexer for Sieve file format. + + https://tools.ietf.org/html/rfc5228 + https://tools.ietf.org/html/rfc5173 + https://tools.ietf.org/html/rfc5229 + https://tools.ietf.org/html/rfc5230 + https://tools.ietf.org/html/rfc5232 + https://tools.ietf.org/html/rfc5235 + https://tools.ietf.org/html/rfc5429 + https://tools.ietf.org/html/rfc8580 + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, bygroups +from pygments.token import Comment, Name, Literal, String, Text, Punctuation, Keyword + +__all__ = ["SieveLexer"] + + +class SieveLexer(RegexLexer): + """ + Lexer for sieve format. + """ + name = 'Sieve' + filenames = ['*.siv', '*.sieve'] + aliases = ['sieve'] + + tokens = { + 'root': [ + (r'\s+', Text), + (r'[();,{}\[\]]', Punctuation), + # import: + (r'(?i)require', + Keyword.Namespace), + # tags: + (r'(?i)(:)(addresses|all|contains|content|create|copy|comparator|count|days|detail|domain|fcc|flags|from|handle|importance|is|localpart|length|lowerfirst|lower|matches|message|mime|options|over|percent|quotewildcard|raw|regex|specialuse|subject|text|under|upperfirst|upper|value)', + bygroups(Name.Tag, Name.Tag)), + # tokens: + (r'(?i)(address|addflag|allof|anyof|body|discard|elsif|else|envelope|ereject|exists|false|fileinto|if|hasflag|header|keep|notify_method_capability|notify|not|redirect|reject|removeflag|setflag|size|spamtest|stop|string|true|vacation|virustest)', + Name.Builtin), + (r'(?i)set', + Keyword.Declaration), + # number: + (r'([0-9.]+)([kmgKMG])?', + bygroups(Literal.Number, Literal.Number)), + # comment: + (r'#.*$', + Comment.Single), + (r'/\*.*\*/', + Comment.Multiline), + # string: + (r'"[^"]*?"', + String), + # text block: + (r'text:', + Name.Tag, 'text'), + ], + 'text': [ + (r'[^.].*?\n', String), + (r'^\.', Punctuation, "#pop"), + ] + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/slash.py b/.venv/lib/python3.8/site-packages/pygments/lexers/slash.py new file mode 100644 index 00000000..13f81077 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/slash.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.slash + ~~~~~~~~~~~~~~~~~~~~~ + + Lexer for the `Slash `_ programming + language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import ExtendedRegexLexer, bygroups, DelegatingLexer +from pygments.token import Name, Number, String, Comment, Punctuation, \ + Other, Keyword, Operator, Whitespace + +__all__ = ['SlashLexer'] + + +class SlashLanguageLexer(ExtendedRegexLexer): + _nkw = r'(?=[^a-zA-Z_0-9])' + + def move_state(new_state): + return ("#pop", new_state) + + def right_angle_bracket(lexer, match, ctx): + if len(ctx.stack) > 1 and ctx.stack[-2] == "string": + ctx.stack.pop() + yield match.start(), String.Interpol, '}' + ctx.pos = match.end() + pass + + tokens = { + "root": [ + (r"<%=", Comment.Preproc, move_state("slash")), + (r"<%!!", Comment.Preproc, move_state("slash")), + (r"<%#.*?%>", Comment.Multiline), + (r"<%", Comment.Preproc, move_state("slash")), + (r".|\n", Other), + ], + "string": [ + (r"\\", String.Escape, move_state("string_e")), + (r"\"", String, move_state("slash")), + (r"#\{", String.Interpol, "slash"), + (r'.|\n', String), + ], + "string_e": [ + (r'n', String.Escape, move_state("string")), + (r't', String.Escape, move_state("string")), + (r'r', String.Escape, move_state("string")), + (r'e', String.Escape, move_state("string")), + (r'x[a-fA-F0-9]{2}', String.Escape, move_state("string")), + (r'.', String.Escape, move_state("string")), + ], + "regexp": [ + (r'}[a-z]*', String.Regex, move_state("slash")), + (r'\\(.|\n)', String.Regex), + (r'{', String.Regex, "regexp_r"), + (r'.|\n', String.Regex), + ], + "regexp_r": [ + (r'}[a-z]*', String.Regex, "#pop"), + (r'\\(.|\n)', String.Regex), + (r'{', String.Regex, "regexp_r"), + ], + "slash": [ + (r"%>", Comment.Preproc, move_state("root")), + (r"\"", String, move_state("string")), + (r"'[a-zA-Z0-9_]+", String), + (r'%r{', String.Regex, move_state("regexp")), + (r'/\*.*?\*/', Comment.Multiline), + (r"(#|//).*?\n", Comment.Single), + (r'-?[0-9]+e[+-]?[0-9]+', Number.Float), + (r'-?[0-9]+\.[0-9]+(e[+-]?[0-9]+)?', Number.Float), + (r'-?[0-9]+', Number.Integer), + (r'nil'+_nkw, Name.Builtin), + (r'true'+_nkw, Name.Builtin), + (r'false'+_nkw, Name.Builtin), + (r'self'+_nkw, Name.Builtin), + (r'(class)(\s+)([A-Z][a-zA-Z0-9_\']*)', + bygroups(Keyword, Whitespace, Name.Class)), + (r'class'+_nkw, Keyword), + (r'extends'+_nkw, Keyword), + (r'(def)(\s+)(self)(\s*)(\.)(\s*)([a-z_][a-zA-Z0-9_\']*=?|<<|>>|==|<=>|<=|<|>=|>|\+|-(self)?|~(self)?|\*|/|%|^|&&|&|\||\[\]=?)', + bygroups(Keyword, Whitespace, Name.Builtin, Whitespace, Punctuation, Whitespace, Name.Function)), + (r'(def)(\s+)([a-z_][a-zA-Z0-9_\']*=?|<<|>>|==|<=>|<=|<|>=|>|\+|-(self)?|~(self)?|\*|/|%|^|&&|&|\||\[\]=?)', + bygroups(Keyword, Whitespace, Name.Function)), + (r'def'+_nkw, Keyword), + (r'if'+_nkw, Keyword), + (r'elsif'+_nkw, Keyword), + (r'else'+_nkw, Keyword), + (r'unless'+_nkw, Keyword), + (r'for'+_nkw, Keyword), + (r'in'+_nkw, Keyword), + (r'while'+_nkw, Keyword), + (r'until'+_nkw, Keyword), + (r'and'+_nkw, Keyword), + (r'or'+_nkw, Keyword), + (r'not'+_nkw, Keyword), + (r'lambda'+_nkw, Keyword), + (r'try'+_nkw, Keyword), + (r'catch'+_nkw, Keyword), + (r'return'+_nkw, Keyword), + (r'next'+_nkw, Keyword), + (r'last'+_nkw, Keyword), + (r'throw'+_nkw, Keyword), + (r'use'+_nkw, Keyword), + (r'switch'+_nkw, Keyword), + (r'\\', Keyword), + (r'λ', Keyword), + (r'__FILE__'+_nkw, Name.Builtin.Pseudo), + (r'__LINE__'+_nkw, Name.Builtin.Pseudo), + (r'[A-Z][a-zA-Z0-9_\']*'+_nkw, Name.Constant), + (r'[a-z_][a-zA-Z0-9_\']*'+_nkw, Name), + (r'@[a-z_][a-zA-Z0-9_\']*'+_nkw, Name.Variable.Instance), + (r'@@[a-z_][a-zA-Z0-9_\']*'+_nkw, Name.Variable.Class), + (r'\(', Punctuation), + (r'\)', Punctuation), + (r'\[', Punctuation), + (r'\]', Punctuation), + (r'\{', Punctuation), + (r'\}', right_angle_bracket), + (r';', Punctuation), + (r',', Punctuation), + (r'<<=', Operator), + (r'>>=', Operator), + (r'<<', Operator), + (r'>>', Operator), + (r'==', Operator), + (r'!=', Operator), + (r'=>', Operator), + (r'=', Operator), + (r'<=>', Operator), + (r'<=', Operator), + (r'>=', Operator), + (r'<', Operator), + (r'>', Operator), + (r'\+\+', Operator), + (r'\+=', Operator), + (r'-=', Operator), + (r'\*\*=', Operator), + (r'\*=', Operator), + (r'\*\*', Operator), + (r'\*', Operator), + (r'/=', Operator), + (r'\+', Operator), + (r'-', Operator), + (r'/', Operator), + (r'%=', Operator), + (r'%', Operator), + (r'^=', Operator), + (r'&&=', Operator), + (r'&=', Operator), + (r'&&', Operator), + (r'&', Operator), + (r'\|\|=', Operator), + (r'\|=', Operator), + (r'\|\|', Operator), + (r'\|', Operator), + (r'!', Operator), + (r'\.\.\.', Operator), + (r'\.\.', Operator), + (r'\.', Operator), + (r'::', Operator), + (r':', Operator), + (r'(\s|\n)+', Whitespace), + (r'[a-z_][a-zA-Z0-9_\']*', Name.Variable), + ], + } + + +class SlashLexer(DelegatingLexer): + """ + Lexer for the Slash programming language. + + .. versionadded:: 2.4 + """ + + name = 'Slash' + aliases = ['slash'] + filenames = ['*.sla'] + + def __init__(self, **options): + from pygments.lexers.web import HtmlLexer + super().__init__(HtmlLexer, SlashLanguageLexer, **options) diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/smalltalk.py b/.venv/lib/python3.8/site-packages/pygments/lexers/smalltalk.py new file mode 100644 index 00000000..b7df5f33 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/smalltalk.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.smalltalk + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Smalltalk and related languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, include, bygroups, default +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation + +__all__ = ['SmalltalkLexer', 'NewspeakLexer'] + + +class SmalltalkLexer(RegexLexer): + """ + For `Smalltalk `_ syntax. + Contributed by Stefan Matthias Aust. + Rewritten by Nils Winter. + + .. versionadded:: 0.10 + """ + name = 'Smalltalk' + filenames = ['*.st'] + aliases = ['smalltalk', 'squeak', 'st'] + mimetypes = ['text/x-smalltalk'] + + tokens = { + 'root': [ + (r'(<)(\w+:)(.*?)(>)', bygroups(Text, Keyword, Text, Text)), + include('squeak fileout'), + include('whitespaces'), + include('method definition'), + (r'(\|)([\w\s]*)(\|)', bygroups(Operator, Name.Variable, Operator)), + include('objects'), + (r'\^|\:=|\_', Operator), + # temporaries + (r'[\]({}.;!]', Text), + ], + 'method definition': [ + # Not perfect can't allow whitespaces at the beginning and the + # without breaking everything + (r'([a-zA-Z]+\w*:)(\s*)(\w+)', + bygroups(Name.Function, Text, Name.Variable)), + (r'^(\b[a-zA-Z]+\w*\b)(\s*)$', bygroups(Name.Function, Text)), + (r'^([-+*/\\~<>=|&!?,@%]+)(\s*)(\w+)(\s*)$', + bygroups(Name.Function, Text, Name.Variable, Text)), + ], + 'blockvariables': [ + include('whitespaces'), + (r'(:)(\s*)(\w+)', + bygroups(Operator, Text, Name.Variable)), + (r'\|', Operator, '#pop'), + default('#pop'), # else pop + ], + 'literals': [ + (r"'(''|[^'])*'", String, 'afterobject'), + (r'\$.', String.Char, 'afterobject'), + (r'#\(', String.Symbol, 'parenth'), + (r'\)', Text, 'afterobject'), + (r'(\d+r)?-?\d+(\.\d+)?(e-?\d+)?', Number, 'afterobject'), + ], + '_parenth_helper': [ + include('whitespaces'), + (r'(\d+r)?-?\d+(\.\d+)?(e-?\d+)?', Number), + (r'[-+*/\\~<>=|&#!?,@%\w:]+', String.Symbol), + # literals + (r"'(''|[^'])*'", String), + (r'\$.', String.Char), + (r'#*\(', String.Symbol, 'inner_parenth'), + ], + 'parenth': [ + # This state is a bit tricky since + # we can't just pop this state + (r'\)', String.Symbol, ('root', 'afterobject')), + include('_parenth_helper'), + ], + 'inner_parenth': [ + (r'\)', String.Symbol, '#pop'), + include('_parenth_helper'), + ], + 'whitespaces': [ + # skip whitespace and comments + (r'\s+', Text), + (r'"(""|[^"])*"', Comment), + ], + 'objects': [ + (r'\[', Text, 'blockvariables'), + (r'\]', Text, 'afterobject'), + (r'\b(self|super|true|false|nil|thisContext)\b', + Name.Builtin.Pseudo, 'afterobject'), + (r'\b[A-Z]\w*(?!:)\b', Name.Class, 'afterobject'), + (r'\b[a-z]\w*(?!:)\b', Name.Variable, 'afterobject'), + (r'#("(""|[^"])*"|[-+*/\\~<>=|&!?,@%]+|[\w:]+)', + String.Symbol, 'afterobject'), + include('literals'), + ], + 'afterobject': [ + (r'! !$', Keyword, '#pop'), # squeak chunk delimiter + include('whitespaces'), + (r'\b(ifTrue:|ifFalse:|whileTrue:|whileFalse:|timesRepeat:)', + Name.Builtin, '#pop'), + (r'\b(new\b(?!:))', Name.Builtin), + (r'\:=|\_', Operator, '#pop'), + (r'\b[a-zA-Z]+\w*:', Name.Function, '#pop'), + (r'\b[a-zA-Z]+\w*', Name.Function), + (r'\w+:?|[-+*/\\~<>=|&!?,@%]+', Name.Function, '#pop'), + (r'\.', Punctuation, '#pop'), + (r';', Punctuation), + (r'[\])}]', Text), + (r'[\[({]', Text, '#pop'), + ], + 'squeak fileout': [ + # Squeak fileout format (optional) + (r'^"(""|[^"])*"!', Keyword), + (r"^'(''|[^'])*'!", Keyword), + (r'^(!)(\w+)( commentStamp: )(.*?)( prior: .*?!\n)(.*?)(!)', + bygroups(Keyword, Name.Class, Keyword, String, Keyword, Text, Keyword)), + (r"^(!)(\w+(?: class)?)( methodsFor: )('(?:''|[^'])*')(.*?!)", + bygroups(Keyword, Name.Class, Keyword, String, Keyword)), + (r'^(\w+)( subclass: )(#\w+)' + r'(\s+instanceVariableNames: )(.*?)' + r'(\s+classVariableNames: )(.*?)' + r'(\s+poolDictionaries: )(.*?)' + r'(\s+category: )(.*?)(!)', + bygroups(Name.Class, Keyword, String.Symbol, Keyword, String, Keyword, + String, Keyword, String, Keyword, String, Keyword)), + (r'^(\w+(?: class)?)(\s+instanceVariableNames: )(.*?)(!)', + bygroups(Name.Class, Keyword, String, Keyword)), + (r'(!\n)(\].*)(! !)$', bygroups(Keyword, Text, Keyword)), + (r'! !$', Keyword), + ], + } + + +class NewspeakLexer(RegexLexer): + """ + For `Newspeak `_ syntax. + + .. versionadded:: 1.1 + """ + name = 'Newspeak' + filenames = ['*.ns2'] + aliases = ['newspeak', ] + mimetypes = ['text/x-newspeak'] + + tokens = { + 'root': [ + (r'\b(Newsqueak2)\b', Keyword.Declaration), + (r"'[^']*'", String), + (r'\b(class)(\s+)(\w+)(\s*)', + bygroups(Keyword.Declaration, Text, Name.Class, Text)), + (r'\b(mixin|self|super|private|public|protected|nil|true|false)\b', + Keyword), + (r'(\w+\:)(\s*)([a-zA-Z_]\w+)', + bygroups(Name.Function, Text, Name.Variable)), + (r'(\w+)(\s*)(=)', + bygroups(Name.Attribute, Text, Operator)), + (r'<\w+>', Comment.Special), + include('expressionstat'), + include('whitespace') + ], + + 'expressionstat': [ + (r'(\d+\.\d*|\.\d+|\d+[fF])[fF]?', Number.Float), + (r'\d+', Number.Integer), + (r':\w+', Name.Variable), + (r'(\w+)(::)', bygroups(Name.Variable, Operator)), + (r'\w+:', Name.Function), + (r'\w+', Name.Variable), + (r'\(|\)', Punctuation), + (r'\[|\]', Punctuation), + (r'\{|\}', Punctuation), + + (r'(\^|\+|\/|~|\*|<|>|=|@|%|\||&|\?|!|,|-|:)', Operator), + (r'\.|;', Punctuation), + include('whitespace'), + include('literals'), + ], + 'literals': [ + (r'\$.', String), + (r"'[^']*'", String), + (r"#'[^']*'", String.Symbol), + (r"#\w+:?", String.Symbol), + (r"#(\+|\/|~|\*|<|>|=|@|%|\||&|\?|!|,|-)+", String.Symbol) + ], + 'whitespace': [ + (r'\s+', Text), + (r'"[^"]*"', Comment) + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/smv.py b/.venv/lib/python3.8/site-packages/pygments/lexers/smv.py new file mode 100644 index 00000000..a5500d93 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/smv.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.smv + ~~~~~~~~~~~~~~~~~~~ + + Lexers for the SMV languages. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, words +from pygments.token import Comment, Keyword, Name, Number, Operator, \ + Punctuation, Text + +__all__ = ['NuSMVLexer'] + + +class NuSMVLexer(RegexLexer): + """ + Lexer for the NuSMV language. + + .. versionadded:: 2.2 + """ + + name = 'NuSMV' + aliases = ['nusmv'] + filenames = ['*.smv'] + mimetypes = [] + + tokens = { + 'root': [ + # Comments + (r'(?s)\/\-\-.*?\-\-/', Comment), + (r'--.*\n', Comment), + + # Reserved + (words(('MODULE', 'DEFINE', 'MDEFINE', 'CONSTANTS', 'VAR', 'IVAR', + 'FROZENVAR', 'INIT', 'TRANS', 'INVAR', 'SPEC', 'CTLSPEC', + 'LTLSPEC', 'PSLSPEC', 'COMPUTE', 'NAME', 'INVARSPEC', + 'FAIRNESS', 'JUSTICE', 'COMPASSION', 'ISA', 'ASSIGN', + 'CONSTRAINT', 'SIMPWFF', 'CTLWFF', 'LTLWFF', 'PSLWFF', + 'COMPWFF', 'IN', 'MIN', 'MAX', 'MIRROR', 'PRED', + 'PREDICATES'), suffix=r'(?![\w$#-])'), + Keyword.Declaration), + (r'process(?![\w$#-])', Keyword), + (words(('array', 'of', 'boolean', 'integer', 'real', 'word'), + suffix=r'(?![\w$#-])'), Keyword.Type), + (words(('case', 'esac'), suffix=r'(?![\w$#-])'), Keyword), + (words(('word1', 'bool', 'signed', 'unsigned', 'extend', 'resize', + 'sizeof', 'uwconst', 'swconst', 'init', 'self', 'count', + 'abs', 'max', 'min'), suffix=r'(?![\w$#-])'), + Name.Builtin), + (words(('EX', 'AX', 'EF', 'AF', 'EG', 'AG', 'E', 'F', 'O', 'G', + 'H', 'X', 'Y', 'Z', 'A', 'U', 'S', 'V', 'T', 'BU', 'EBF', + 'ABF', 'EBG', 'ABG', 'next', 'mod', 'union', 'in', 'xor', + 'xnor'), suffix=r'(?![\w$#-])'), + Operator.Word), + (words(('TRUE', 'FALSE'), suffix=r'(?![\w$#-])'), Keyword.Constant), + + # Names + (r'[a-zA-Z_][\w$#-]*', Name.Variable), + + # Operators + (r':=', Operator), + (r'[-&|+*/<>!=]', Operator), + + # Literals + (r'\-?\d+\b', Number.Integer), + (r'0[su][bB]\d*_[01_]+', Number.Bin), + (r'0[su][oO]\d*_[0-7_]+', Number.Oct), + (r'0[su][dD]\d*_[\d_]+', Number.Dec), + (r'0[su][hH]\d*_[\da-fA-F_]+', Number.Hex), + + # Whitespace, punctuation and the rest + (r'\s+', Text.Whitespace), + (r'[()\[\]{};?:.,]', Punctuation), + ], + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/snobol.py b/.venv/lib/python3.8/site-packages/pygments/lexers/snobol.py new file mode 100644 index 00000000..b0fdb01c --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/snobol.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.snobol + ~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for the SNOBOL language. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from pygments.lexer import RegexLexer, bygroups +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation + +__all__ = ['SnobolLexer'] + + +class SnobolLexer(RegexLexer): + """ + Lexer for the SNOBOL4 programming language. + + Recognizes the common ASCII equivalents of the original SNOBOL4 operators. + Does not require spaces around binary operators. + + .. versionadded:: 1.5 + """ + + name = "Snobol" + aliases = ["snobol"] + filenames = ['*.snobol'] + mimetypes = ['text/x-snobol'] + + tokens = { + # root state, start of line + # comments, continuation lines, and directives start in column 1 + # as do labels + 'root': [ + (r'\*.*\n', Comment), + (r'[+.] ', Punctuation, 'statement'), + (r'-.*\n', Comment), + (r'END\s*\n', Name.Label, 'heredoc'), + (r'[A-Za-z$][\w$]*', Name.Label, 'statement'), + (r'\s+', Text, 'statement'), + ], + # statement state, line after continuation or label + 'statement': [ + (r'\s*\n', Text, '#pop'), + (r'\s+', Text), + (r'(?<=[^\w.])(LT|LE|EQ|NE|GE|GT|INTEGER|IDENT|DIFFER|LGT|SIZE|' + r'REPLACE|TRIM|DUPL|REMDR|DATE|TIME|EVAL|APPLY|OPSYN|LOAD|UNLOAD|' + r'LEN|SPAN|BREAK|ANY|NOTANY|TAB|RTAB|REM|POS|RPOS|FAIL|FENCE|' + r'ABORT|ARB|ARBNO|BAL|SUCCEED|INPUT|OUTPUT|TERMINAL)(?=[^\w.])', + Name.Builtin), + (r'[A-Za-z][\w.]*', Name), + # ASCII equivalents of original operators + # | for the EBCDIC equivalent, ! likewise + # \ for EBCDIC negation + (r'\*\*|[?$.!%*/#+\-@|&\\=]', Operator), + (r'"[^"]*"', String), + (r"'[^']*'", String), + # Accept SPITBOL syntax for real numbers + # as well as Macro SNOBOL4 + (r'[0-9]+(?=[^.EeDd])', Number.Integer), + (r'[0-9]+(\.[0-9]*)?([EDed][-+]?[0-9]+)?', Number.Float), + # Goto + (r':', Punctuation, 'goto'), + (r'[()<>,;]', Punctuation), + ], + # Goto block + 'goto': [ + (r'\s*\n', Text, "#pop:2"), + (r'\s+', Text), + (r'F|S', Keyword), + (r'(\()([A-Za-z][\w.]*)(\))', + bygroups(Punctuation, Name.Label, Punctuation)) + ], + # everything after the END statement is basically one + # big heredoc. + 'heredoc': [ + (r'.*\n', String.Heredoc) + ] + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/solidity.py b/.venv/lib/python3.8/site-packages/pygments/lexers/solidity.py new file mode 100644 index 00000000..af0672ee --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/solidity.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.solidity + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Lexers for Solidity. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re + +from pygments.lexer import RegexLexer, bygroups, include, words +from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ + Number, Punctuation, Whitespace + +__all__ = ['SolidityLexer'] + + +class SolidityLexer(RegexLexer): + """ + For Solidity source code. + + .. versionadded:: 2.5 + """ + + name = 'Solidity' + aliases = ['solidity'] + filenames = ['*.sol'] + mimetypes = [] + + flags = re.MULTILINE | re.UNICODE + + datatype = ( + r'\b(address|bool|(?:(?:bytes|hash|int|string|uint)(?:8|16|24|32|40|48|56|64' + r'|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208' + r'|216|224|232|240|248|256)?))\b' + ) + + tokens = { + 'root': [ + include('whitespace'), + include('comments'), + (r'\bpragma\s+solidity\b', Keyword, 'pragma'), + (r'\b(contract)(\s+)([a-zA-Z_]\w*)', + bygroups(Keyword, Whitespace, Name.Entity)), + (datatype + r'(\s+)((?:external|public|internal|private)\s+)?' + + r'([a-zA-Z_]\w*)', + bygroups(Keyword.Type, Whitespace, Keyword, Name.Variable)), + (r'\b(enum|event|function|struct)(\s+)([a-zA-Z_]\w*)', + bygroups(Keyword.Type, Whitespace, Name.Variable)), + (r'\b(msg|block|tx)\.([A-Za-z_][a-zA-Z0-9_]*)\b', Keyword), + (words(( + 'block', 'break', 'constant', 'constructor', 'continue', + 'contract', 'do', 'else', 'external', 'false', 'for', + 'function', 'if', 'import', 'inherited', 'internal', 'is', + 'library', 'mapping', 'memory', 'modifier', 'msg', 'new', + 'payable', 'private', 'public', 'require', 'return', + 'returns', 'struct', 'suicide', 'throw', 'this', 'true', + 'tx', 'var', 'while'), prefix=r'\b', suffix=r'\b'), + Keyword.Type), + (words(('keccak256',), prefix=r'\b', suffix=r'\b'), Name.Builtin), + (datatype, Keyword.Type), + include('constants'), + (r'[a-zA-Z_]\w*', Text), + (r'[!<=>+*/-]', Operator), + (r'[.;:{}(),\[\]]', Punctuation) + ], + 'comments': [ + (r'//(\n|[\w\W]*?[^\\]\n)', Comment.Single), + (r'/(\\\n)?[*][\w\W]*?[*](\\\n)?/', Comment.Multiline), + (r'/(\\\n)?[*][\w\W]*', Comment.Multiline) + ], + 'constants': [ + (r'("(\\"|.)*?")', String.Double), + (r"('(\\'|.)*?')", String.Single), + (r'\b0[xX][0-9a-fA-F]+\b', Number.Hex), + (r'\b\d+\b', Number.Decimal), + ], + 'pragma': [ + include('whitespace'), + include('comments'), + (r'(\^|>=|<)(\s*)(\d+\.\d+\.\d+)', + bygroups(Operator, Whitespace, Keyword)), + (r';', Punctuation, '#pop') + ], + 'whitespace': [ + (r'\s+', Whitespace), + (r'\n', Whitespace) + ] + } diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/special.py b/.venv/lib/python3.8/site-packages/pygments/lexers/special.py new file mode 100644 index 00000000..84a924d9 --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/special.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.special + ~~~~~~~~~~~~~~~~~~~~~~~ + + Special lexers. + + :copyright: Copyright 2006-2020 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +from io import BytesIO + +from pygments.lexer import Lexer +from pygments.token import Token, Error, Text +from pygments.util import get_choice_opt + + +__all__ = ['TextLexer', 'RawTokenLexer'] + + +class TextLexer(Lexer): + """ + "Null" lexer, doesn't highlight anything. + """ + name = 'Text only' + aliases = ['text'] + filenames = ['*.txt'] + mimetypes = ['text/plain'] + priority = 0.01 + + def get_tokens_unprocessed(self, text): + yield 0, Text, text + + def analyse_text(text): + return TextLexer.priority + + +_ttype_cache = {} + +line_re = re.compile(b'.*?\n') + + +class RawTokenLexer(Lexer): + """ + Recreate a token stream formatted with the `RawTokenFormatter`. This + lexer raises exceptions during parsing if the token stream in the + file is malformed. + + Additional options accepted: + + `compress` + If set to ``"gz"`` or ``"bz2"``, decompress the token stream with + the given compression algorithm before lexing (default: ``""``). + """ + name = 'Raw token data' + aliases = ['raw'] + filenames = [] + mimetypes = ['application/x-pygments-tokens'] + + def __init__(self, **options): + self.compress = get_choice_opt(options, 'compress', + ['', 'none', 'gz', 'bz2'], '') + Lexer.__init__(self, **options) + + def get_tokens(self, text): + if isinstance(text, str): + # raw token stream never has any non-ASCII characters + text = text.encode('ascii') + if self.compress == 'gz': + import gzip + gzipfile = gzip.GzipFile('', 'rb', 9, BytesIO(text)) + text = gzipfile.read() + elif self.compress == 'bz2': + import bz2 + text = bz2.decompress(text) + + # do not call Lexer.get_tokens() because we do not want Unicode + # decoding to occur, and stripping is not optional. + text = text.strip(b'\n') + b'\n' + for i, t, v in self.get_tokens_unprocessed(text): + yield t, v + + def get_tokens_unprocessed(self, text): + length = 0 + for match in line_re.finditer(text): + try: + ttypestr, val = match.group().split(b'\t', 1) + except ValueError: + val = match.group().decode('ascii', 'replace') + ttype = Error + else: + ttype = _ttype_cache.get(ttypestr) + if not ttype: + ttype = Token + ttypes = ttypestr.split('.')[1:] + for ttype_ in ttypes: + if not ttype_ or not ttype_[0].isupper(): + raise ValueError('malformed token name') + ttype = getattr(ttype, ttype_) + _ttype_cache[ttypestr] = ttype + val = val[2:-2].decode('unicode-escape') + yield length, ttype, val + length += len(val) diff --git a/.venv/lib/python3.8/site-packages/pygments/lexers/sql.py b/.venv/lib/python3.8/site-packages/pygments/lexers/sql.py new file mode 100644 index 00000000..e27e0ddb --- /dev/null +++ b/.venv/lib/python3.8/site-packages/pygments/lexers/sql.py @@ -0,0 +1,837 @@ +# -*- coding: utf-8 -*- +""" + pygments.lexers.sql + ~~~~~~~~~~~~~~~~~~~ + + Lexers for various SQL dialects and related interactive sessions. + + Postgres specific lexers: + + `PostgresLexer` + A SQL lexer for the PostgreSQL dialect. Differences w.r.t. the SQL + lexer are: + + - keywords and data types list parsed from the PG docs (run the + `_postgres_builtins` module to update them); + - Content of $-strings parsed using a specific lexer, e.g. the content + of a PL/Python function is parsed using the Python lexer; + - parse PG specific constructs: E-strings, $-strings, U&-strings, + different operators and punctuation. + + `PlPgsqlLexer` + A lexer for the PL/pgSQL language. Adds a few specific construct on + top of the PG SQL lexer (such as <