Explorar el Código

完成报告生成流程

zsh hace 2 semanas
padre
commit
1732cfb840
Se han modificado 100 ficheros con 5423 adiciones y 0 borrados
  1. 247 0
      algo/py312/bin/Activate.ps1
  2. 70 0
      algo/py312/bin/activate
  3. 27 0
      algo/py312/bin/activate.csh
  4. 69 0
      algo/py312/bin/activate.fish
  5. 8 0
      algo/py312/bin/dotenv
  6. 8 0
      algo/py312/bin/fastapi
  7. 8 0
      algo/py312/bin/httpx
  8. 8 0
      algo/py312/bin/pip
  9. 8 0
      algo/py312/bin/pip3
  10. 8 0
      algo/py312/bin/pip3.12
  11. 8 0
      algo/py312/bin/py.test
  12. 8 0
      algo/py312/bin/pygmentize
  13. 8 0
      algo/py312/bin/pytest
  14. 1 0
      algo/py312/bin/python
  15. 1 0
      algo/py312/bin/python3
  16. 1 0
      algo/py312/bin/python3.12
  17. 8 0
      algo/py312/bin/uvicorn
  18. 8 0
      algo/py312/bin/watchfiles
  19. 8 0
      algo/py312/bin/websockets
  20. 1 0
      algo/py312/lib/python3.12/site-packages/__editable__.finrep_algo_agent-0.1.0.pth
  21. BIN
      algo/py312/lib/python3.12/site-packages/__pycache__/py.cpython-312.pyc
  22. BIN
      algo/py312/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc
  23. 13 0
      algo/py312/lib/python3.12/site-packages/_pytest/__init__.py
  24. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/__init__.cpython-312.pyc
  25. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/_argcomplete.cpython-312.pyc
  26. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/_version.cpython-312.pyc
  27. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/cacheprovider.cpython-312.pyc
  28. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/capture.cpython-312.pyc
  29. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/compat.cpython-312.pyc
  30. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/debugging.cpython-312.pyc
  31. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/deprecated.cpython-312.pyc
  32. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/doctest.cpython-312.pyc
  33. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/faulthandler.cpython-312.pyc
  34. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/fixtures.cpython-312.pyc
  35. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/freeze_support.cpython-312.pyc
  36. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/helpconfig.cpython-312.pyc
  37. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/hookspec.cpython-312.pyc
  38. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/junitxml.cpython-312.pyc
  39. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/legacypath.cpython-312.pyc
  40. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/logging.cpython-312.pyc
  41. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/main.cpython-312.pyc
  42. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/monkeypatch.cpython-312.pyc
  43. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/nodes.cpython-312.pyc
  44. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/outcomes.cpython-312.pyc
  45. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/pastebin.cpython-312.pyc
  46. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/pathlib.cpython-312.pyc
  47. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/pytester.cpython-312.pyc
  48. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/pytester_assertions.cpython-312.pyc
  49. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/python.cpython-312.pyc
  50. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/python_api.cpython-312.pyc
  51. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/raises.cpython-312.pyc
  52. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/recwarn.cpython-312.pyc
  53. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/reports.cpython-312.pyc
  54. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/runner.cpython-312.pyc
  55. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/scope.cpython-312.pyc
  56. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/setuponly.cpython-312.pyc
  57. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/setupplan.cpython-312.pyc
  58. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/skipping.cpython-312.pyc
  59. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/stash.cpython-312.pyc
  60. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/stepwise.cpython-312.pyc
  61. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/subtests.cpython-312.pyc
  62. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/terminal.cpython-312.pyc
  63. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/terminalprogress.cpython-312.pyc
  64. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/threadexception.cpython-312.pyc
  65. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/timing.cpython-312.pyc
  66. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/tmpdir.cpython-312.pyc
  67. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/tracemalloc.cpython-312.pyc
  68. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/unittest.cpython-312.pyc
  69. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/unraisableexception.cpython-312.pyc
  70. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/warning_types.cpython-312.pyc
  71. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/warnings.cpython-312.pyc
  72. 117 0
      algo/py312/lib/python3.12/site-packages/_pytest/_argcomplete.py
  73. 26 0
      algo/py312/lib/python3.12/site-packages/_pytest/_code/__init__.py
  74. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_code/__pycache__/__init__.cpython-312.pyc
  75. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_code/__pycache__/code.cpython-312.pyc
  76. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_code/__pycache__/source.cpython-312.pyc
  77. 1565 0
      algo/py312/lib/python3.12/site-packages/_pytest/_code/code.py
  78. 225 0
      algo/py312/lib/python3.12/site-packages/_pytest/_code/source.py
  79. 10 0
      algo/py312/lib/python3.12/site-packages/_pytest/_io/__init__.py
  80. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/__init__.cpython-312.pyc
  81. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/pprint.cpython-312.pyc
  82. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/saferepr.cpython-312.pyc
  83. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/terminalwriter.cpython-312.pyc
  84. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-312.pyc
  85. 673 0
      algo/py312/lib/python3.12/site-packages/_pytest/_io/pprint.py
  86. 130 0
      algo/py312/lib/python3.12/site-packages/_pytest/_io/saferepr.py
  87. 258 0
      algo/py312/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py
  88. 57 0
      algo/py312/lib/python3.12/site-packages/_pytest/_io/wcwidth.py
  89. 0 0
      algo/py312/lib/python3.12/site-packages/_pytest/_py/__init__.py
  90. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_py/__pycache__/__init__.cpython-312.pyc
  91. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_py/__pycache__/error.cpython-312.pyc
  92. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/_py/__pycache__/path.cpython-312.pyc
  93. 119 0
      algo/py312/lib/python3.12/site-packages/_pytest/_py/error.py
  94. 1475 0
      algo/py312/lib/python3.12/site-packages/_pytest/_py/path.py
  95. 34 0
      algo/py312/lib/python3.12/site-packages/_pytest/_version.py
  96. 208 0
      algo/py312/lib/python3.12/site-packages/_pytest/assertion/__init__.py
  97. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/assertion/__pycache__/__init__.cpython-312.pyc
  98. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc
  99. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/assertion/__pycache__/truncate.cpython-312.pyc
  100. BIN
      algo/py312/lib/python3.12/site-packages/_pytest/assertion/__pycache__/util.cpython-312.pyc

+ 247 - 0
algo/py312/bin/Activate.ps1

@@ -0,0 +1,247 @@
+<#
+.Synopsis
+Activate a Python virtual environment for the current PowerShell session.
+
+.Description
+Pushes the python executable for a virtual environment to the front of the
+$Env:PATH environment variable and sets the prompt to signify that you are
+in a Python virtual environment. Makes use of the command line switches as
+well as the `pyvenv.cfg` file values present in the virtual environment.
+
+.Parameter VenvDir
+Path to the directory that contains the virtual environment to activate. The
+default value for this is the parent of the directory that the Activate.ps1
+script is located within.
+
+.Parameter Prompt
+The prompt prefix to display when this virtual environment is activated. By
+default, this prompt is the name of the virtual environment folder (VenvDir)
+surrounded by parentheses and followed by a single space (ie. '(.venv) ').
+
+.Example
+Activate.ps1
+Activates the Python virtual environment that contains the Activate.ps1 script.
+
+.Example
+Activate.ps1 -Verbose
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and shows extra information about the activation as it executes.
+
+.Example
+Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
+Activates the Python virtual environment located in the specified location.
+
+.Example
+Activate.ps1 -Prompt "MyPython"
+Activates the Python virtual environment that contains the Activate.ps1 script,
+and prefixes the current prompt with the specified string (surrounded in
+parentheses) while the virtual environment is active.
+
+.Notes
+On Windows, it may be required to enable this Activate.ps1 script by setting the
+execution policy for the user. You can do this by issuing the following PowerShell
+command:
+
+PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
+
+For more information on Execution Policies: 
+https://go.microsoft.com/fwlink/?LinkID=135170
+
+#>
+Param(
+    [Parameter(Mandatory = $false)]
+    [String]
+    $VenvDir,
+    [Parameter(Mandatory = $false)]
+    [String]
+    $Prompt
+)
+
+<# Function declarations --------------------------------------------------- #>
+
+<#
+.Synopsis
+Remove all shell session elements added by the Activate script, including the
+addition of the virtual environment's Python executable from the beginning of
+the PATH variable.
+
+.Parameter NonDestructive
+If present, do not remove this function from the global namespace for the
+session.
+
+#>
+function global:deactivate ([switch]$NonDestructive) {
+    # Revert to original values
+
+    # The prior prompt:
+    if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
+        Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
+        Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
+    }
+
+    # The prior PYTHONHOME:
+    if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
+        Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
+        Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
+    }
+
+    # The prior PATH:
+    if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
+        Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
+        Remove-Item -Path Env:_OLD_VIRTUAL_PATH
+    }
+
+    # Just remove the VIRTUAL_ENV altogether:
+    if (Test-Path -Path Env:VIRTUAL_ENV) {
+        Remove-Item -Path env:VIRTUAL_ENV
+    }
+
+    # Just remove VIRTUAL_ENV_PROMPT altogether.
+    if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
+        Remove-Item -Path env:VIRTUAL_ENV_PROMPT
+    }
+
+    # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
+    if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
+        Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
+    }
+
+    # Leave deactivate function in the global namespace if requested:
+    if (-not $NonDestructive) {
+        Remove-Item -Path function:deactivate
+    }
+}
+
+<#
+.Description
+Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
+given folder, and returns them in a map.
+
+For each line in the pyvenv.cfg file, if that line can be parsed into exactly
+two strings separated by `=` (with any amount of whitespace surrounding the =)
+then it is considered a `key = value` line. The left hand string is the key,
+the right hand is the value.
+
+If the value starts with a `'` or a `"` then the first and last character is
+stripped from the value before being captured.
+
+.Parameter ConfigDir
+Path to the directory that contains the `pyvenv.cfg` file.
+#>
+function Get-PyVenvConfig(
+    [String]
+    $ConfigDir
+) {
+    Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
+
+    # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
+    $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
+
+    # An empty map will be returned if no config file is found.
+    $pyvenvConfig = @{ }
+
+    if ($pyvenvConfigPath) {
+
+        Write-Verbose "File exists, parse `key = value` lines"
+        $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
+
+        $pyvenvConfigContent | ForEach-Object {
+            $keyval = $PSItem -split "\s*=\s*", 2
+            if ($keyval[0] -and $keyval[1]) {
+                $val = $keyval[1]
+
+                # Remove extraneous quotations around a string value.
+                if ("'""".Contains($val.Substring(0, 1))) {
+                    $val = $val.Substring(1, $val.Length - 2)
+                }
+
+                $pyvenvConfig[$keyval[0]] = $val
+                Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
+            }
+        }
+    }
+    return $pyvenvConfig
+}
+
+
+<# Begin Activate script --------------------------------------------------- #>
+
+# Determine the containing directory of this script
+$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
+$VenvExecDir = Get-Item -Path $VenvExecPath
+
+Write-Verbose "Activation script is located in path: '$VenvExecPath'"
+Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
+Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
+
+# Set values required in priority: CmdLine, ConfigFile, Default
+# First, get the location of the virtual environment, it might not be
+# VenvExecDir if specified on the command line.
+if ($VenvDir) {
+    Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
+}
+else {
+    Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
+    $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
+    Write-Verbose "VenvDir=$VenvDir"
+}
+
+# Next, read the `pyvenv.cfg` file to determine any required value such
+# as `prompt`.
+$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
+
+# Next, set the prompt from the command line, or the config file, or
+# just use the name of the virtual environment folder.
+if ($Prompt) {
+    Write-Verbose "Prompt specified as argument, using '$Prompt'"
+}
+else {
+    Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
+    if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
+        Write-Verbose "  Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
+        $Prompt = $pyvenvCfg['prompt'];
+    }
+    else {
+        Write-Verbose "  Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
+        Write-Verbose "  Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
+        $Prompt = Split-Path -Path $venvDir -Leaf
+    }
+}
+
+Write-Verbose "Prompt = '$Prompt'"
+Write-Verbose "VenvDir='$VenvDir'"
+
+# Deactivate any currently active virtual environment, but leave the
+# deactivate function in place.
+deactivate -nondestructive
+
+# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
+# that there is an activated venv.
+$env:VIRTUAL_ENV = $VenvDir
+
+if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
+
+    Write-Verbose "Setting prompt to '$Prompt'"
+
+    # Set the prompt to include the env name
+    # Make sure _OLD_VIRTUAL_PROMPT is global
+    function global:_OLD_VIRTUAL_PROMPT { "" }
+    Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
+    New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
+
+    function global:prompt {
+        Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
+        _OLD_VIRTUAL_PROMPT
+    }
+    $env:VIRTUAL_ENV_PROMPT = $Prompt
+}
+
+# Clear PYTHONHOME
+if (Test-Path -Path Env:PYTHONHOME) {
+    Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
+    Remove-Item -Path Env:PYTHONHOME
+}
+
+# Add the venv to the PATH
+Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
+$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"

+ 70 - 0
algo/py312/bin/activate

@@ -0,0 +1,70 @@
+# This file must be used with "source bin/activate" *from bash*
+# You cannot run it directly
+
+deactivate () {
+    # reset old environment variables
+    if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
+        PATH="${_OLD_VIRTUAL_PATH:-}"
+        export PATH
+        unset _OLD_VIRTUAL_PATH
+    fi
+    if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
+        PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
+        export PYTHONHOME
+        unset _OLD_VIRTUAL_PYTHONHOME
+    fi
+
+    # Call hash to forget past commands. Without forgetting
+    # past commands the $PATH changes we made may not be respected
+    hash -r 2> /dev/null
+
+    if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
+        PS1="${_OLD_VIRTUAL_PS1:-}"
+        export PS1
+        unset _OLD_VIRTUAL_PS1
+    fi
+
+    unset VIRTUAL_ENV
+    unset VIRTUAL_ENV_PROMPT
+    if [ ! "${1:-}" = "nondestructive" ] ; then
+    # Self destruct!
+        unset -f deactivate
+    fi
+}
+
+# unset irrelevant variables
+deactivate nondestructive
+
+# on Windows, a path can contain colons and backslashes and has to be converted:
+if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
+    # transform D:\path\to\venv to /d/path/to/venv on MSYS
+    # and to /cygdrive/d/path/to/venv on Cygwin
+    export VIRTUAL_ENV=$(cygpath "/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312")
+else
+    # use the path as-is
+    export VIRTUAL_ENV="/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312"
+fi
+
+_OLD_VIRTUAL_PATH="$PATH"
+PATH="$VIRTUAL_ENV/bin:$PATH"
+export PATH
+
+# unset PYTHONHOME if set
+# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
+# could use `if (set -u; : $PYTHONHOME) ;` in bash
+if [ -n "${PYTHONHOME:-}" ] ; then
+    _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
+    unset PYTHONHOME
+fi
+
+if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
+    _OLD_VIRTUAL_PS1="${PS1:-}"
+    PS1="(py312) ${PS1:-}"
+    export PS1
+    VIRTUAL_ENV_PROMPT="(py312) "
+    export VIRTUAL_ENV_PROMPT
+fi
+
+# Call hash to forget past commands. Without forgetting
+# past commands the $PATH changes we made may not be respected
+hash -r 2> /dev/null

+ 27 - 0
algo/py312/bin/activate.csh

@@ -0,0 +1,27 @@
+# This file must be used with "source bin/activate.csh" *from csh*.
+# You cannot run it directly.
+
+# Created by Davide Di Blasi <davidedb@gmail.com>.
+# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
+
+alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+setenv VIRTUAL_ENV "/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312"
+
+set _OLD_VIRTUAL_PATH="$PATH"
+setenv PATH "$VIRTUAL_ENV/bin:$PATH"
+
+
+set _OLD_VIRTUAL_PROMPT="$prompt"
+
+if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
+    set prompt = "(py312) $prompt"
+    setenv VIRTUAL_ENV_PROMPT "(py312) "
+endif
+
+alias pydoc python -m pydoc
+
+rehash

+ 69 - 0
algo/py312/bin/activate.fish

@@ -0,0 +1,69 @@
+# This file must be used with "source <venv>/bin/activate.fish" *from fish*
+# (https://fishshell.com/). You cannot run it directly.
+
+function deactivate  -d "Exit virtual environment and return to normal shell environment"
+    # reset old environment variables
+    if test -n "$_OLD_VIRTUAL_PATH"
+        set -gx PATH $_OLD_VIRTUAL_PATH
+        set -e _OLD_VIRTUAL_PATH
+    end
+    if test -n "$_OLD_VIRTUAL_PYTHONHOME"
+        set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
+        set -e _OLD_VIRTUAL_PYTHONHOME
+    end
+
+    if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
+        set -e _OLD_FISH_PROMPT_OVERRIDE
+        # prevents error when using nested fish instances (Issue #93858)
+        if functions -q _old_fish_prompt
+            functions -e fish_prompt
+            functions -c _old_fish_prompt fish_prompt
+            functions -e _old_fish_prompt
+        end
+    end
+
+    set -e VIRTUAL_ENV
+    set -e VIRTUAL_ENV_PROMPT
+    if test "$argv[1]" != "nondestructive"
+        # Self-destruct!
+        functions -e deactivate
+    end
+end
+
+# Unset irrelevant variables.
+deactivate nondestructive
+
+set -gx VIRTUAL_ENV "/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312"
+
+set -gx _OLD_VIRTUAL_PATH $PATH
+set -gx PATH "$VIRTUAL_ENV/bin" $PATH
+
+# Unset PYTHONHOME if set.
+if set -q PYTHONHOME
+    set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
+    set -e PYTHONHOME
+end
+
+if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
+    # fish uses a function instead of an env var to generate the prompt.
+
+    # Save the current fish_prompt function as the function _old_fish_prompt.
+    functions -c fish_prompt _old_fish_prompt
+
+    # With the original prompt function renamed, we can override with our own.
+    function fish_prompt
+        # Save the return status of the last command.
+        set -l old_status $status
+
+        # Output the venv prompt; color taken from the blue of the Python logo.
+        printf "%s%s%s" (set_color 4B8BBE) "(py312) " (set_color normal)
+
+        # Restore the return status of the previous command.
+        echo "exit $old_status" | .
+        # Output the original/"old" prompt.
+        _old_fish_prompt
+    end
+
+    set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
+    set -gx VIRTUAL_ENV_PROMPT "(py312) "
+end

+ 8 - 0
algo/py312/bin/dotenv

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from dotenv.__main__ import cli
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(cli())

+ 8 - 0
algo/py312/bin/fastapi

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from fastapi.cli import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 8 - 0
algo/py312/bin/httpx

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from httpx import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 8 - 0
algo/py312/bin/pip

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/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())

+ 8 - 0
algo/py312/bin/pip3

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/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())

+ 8 - 0
algo/py312/bin/pip3.12

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/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())

+ 8 - 0
algo/py312/bin/py.test

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pytest import console_main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(console_main())

+ 8 - 0
algo/py312/bin/pygmentize

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pygments.cmdline import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 8 - 0
algo/py312/bin/pytest

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from pytest import console_main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(console_main())

+ 1 - 0
algo/py312/bin/python

@@ -0,0 +1 @@
+python3

+ 1 - 0
algo/py312/bin/python3

@@ -0,0 +1 @@
+/Users/zsh/.pyenv/versions/3.12.7/bin/python3

+ 1 - 0
algo/py312/bin/python3.12

@@ -0,0 +1 @@
+python3

+ 8 - 0
algo/py312/bin/uvicorn

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from uvicorn.main import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 8 - 0
algo/py312/bin/watchfiles

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from watchfiles.cli import cli
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(cli())

+ 8 - 0
algo/py312/bin/websockets

@@ -0,0 +1,8 @@
+#!/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/py312/bin/python3
+# -*- coding: utf-8 -*-
+import re
+import sys
+from websockets.cli import main
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
+    sys.exit(main())

+ 1 - 0
algo/py312/lib/python3.12/site-packages/__editable__.finrep_algo_agent-0.1.0.pth

@@ -0,0 +1 @@
+/Users/zsh/Documents/yusys/智能报告生成/finrep-report/algo/src

BIN
algo/py312/lib/python3.12/site-packages/__pycache__/py.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc


+ 13 - 0
algo/py312/lib/python3.12/site-packages/_pytest/__init__.py

@@ -0,0 +1,13 @@
+from __future__ import annotations
+
+
+__all__ = ["__version__", "version_tuple"]
+
+try:
+    from ._version import version as __version__
+    from ._version import version_tuple
+except ImportError:  # pragma: no cover
+    # broken installation, we don't even try
+    # unknown only works because we do poor mans version compare
+    __version__ = "unknown"
+    version_tuple = (0, 0, "unknown")

BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/__init__.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/_argcomplete.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/_version.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/cacheprovider.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/capture.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/compat.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/debugging.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/deprecated.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/doctest.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/faulthandler.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/fixtures.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/freeze_support.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/helpconfig.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/hookspec.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/junitxml.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/legacypath.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/logging.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/main.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/monkeypatch.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/nodes.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/outcomes.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/pastebin.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/pathlib.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/pytester.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/pytester_assertions.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/python.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/python_api.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/raises.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/recwarn.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/reports.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/runner.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/scope.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/setuponly.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/setupplan.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/skipping.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/stash.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/stepwise.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/subtests.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/terminal.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/terminalprogress.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/threadexception.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/timing.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/tmpdir.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/tracemalloc.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/unittest.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/unraisableexception.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/warning_types.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/__pycache__/warnings.cpython-312.pyc


+ 117 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_argcomplete.py

@@ -0,0 +1,117 @@
+"""Allow bash-completion for argparse with argcomplete if installed.
+
+Needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
+to find the magic string, so _ARGCOMPLETE env. var is never set, and
+this does not need special code).
+
+Function try_argcomplete(parser) should be called directly before
+the call to ArgumentParser.parse_args().
+
+The filescompleter is what you normally would use on the positional
+arguments specification, in order to get "dirname/" after "dirn<TAB>"
+instead of the default "dirname ":
+
+   optparser.add_argument(Config._file_or_dir, nargs='*').completer=filescompleter
+
+Other, application specific, completers should go in the file
+doing the add_argument calls as they need to be specified as .completer
+attributes as well. (If argcomplete is not installed, the function the
+attribute points to will not be used).
+
+SPEEDUP
+=======
+
+The generic argcomplete script for bash-completion
+(/etc/bash_completion.d/python-argcomplete.sh)
+uses a python program to determine startup script generated by pip.
+You can speed up completion somewhat by changing this script to include
+  # PYTHON_ARGCOMPLETE_OK
+so the python-argcomplete-check-easy-install-script does not
+need to be called to find the entry point of the code and see if that is
+marked  with PYTHON_ARGCOMPLETE_OK.
+
+INSTALL/DEBUGGING
+=================
+
+To include this support in another application that has setup.py generated
+scripts:
+
+- Add the line:
+    # PYTHON_ARGCOMPLETE_OK
+  near the top of the main python entry point.
+
+- Include in the file calling parse_args():
+    from _argcomplete import try_argcomplete, filescompleter
+  Call try_argcomplete just before parse_args(), and optionally add
+  filescompleter to the positional arguments' add_argument().
+
+If things do not work right away:
+
+- Switch on argcomplete debugging with (also helpful when doing custom
+  completers):
+    export _ARC_DEBUG=1
+
+- Run:
+    python-argcomplete-check-easy-install-script $(which appname)
+    echo $?
+  will echo 0 if the magic line has been found, 1 if not.
+
+- Sometimes it helps to find early on errors using:
+    _ARGCOMPLETE=1 _ARC_DEBUG=1 appname
+  which should throw a KeyError: 'COMPLINE' (which is properly set by the
+  global argcomplete script).
+"""
+
+from __future__ import annotations
+
+import argparse
+from glob import glob
+import os
+import sys
+from typing import Any
+
+
+class FastFilesCompleter:
+    """Fast file completer class."""
+
+    def __init__(self, directories: bool = True) -> None:
+        self.directories = directories
+
+    def __call__(self, prefix: str, **kwargs: Any) -> list[str]:
+        # Only called on non option completions.
+        if os.sep in prefix[1:]:
+            prefix_dir = len(os.path.dirname(prefix) + os.sep)
+        else:
+            prefix_dir = 0
+        completion = []
+        globbed = []
+        if "*" not in prefix and "?" not in prefix:
+            # We are on unix, otherwise no bash.
+            if not prefix or prefix[-1] == os.sep:
+                globbed.extend(glob(prefix + ".*"))
+            prefix += "*"
+        globbed.extend(glob(prefix))
+        for x in sorted(globbed):
+            if os.path.isdir(x):
+                x += "/"
+            # Append stripping the prefix (like bash, not like compgen).
+            completion.append(x[prefix_dir:])
+        return completion
+
+
+if os.environ.get("_ARGCOMPLETE"):
+    try:
+        import argcomplete.completers
+    except ImportError:
+        sys.exit(-1)
+    filescompleter: FastFilesCompleter | None = FastFilesCompleter()
+
+    def try_argcomplete(parser: argparse.ArgumentParser) -> None:
+        argcomplete.autocomplete(parser, always_complete_options=False)
+
+else:
+
+    def try_argcomplete(parser: argparse.ArgumentParser) -> None:
+        pass
+
+    filescompleter = None

+ 26 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_code/__init__.py

@@ -0,0 +1,26 @@
+"""Python inspection/code generation API."""
+
+from __future__ import annotations
+
+from .code import Code
+from .code import ExceptionInfo
+from .code import filter_traceback
+from .code import Frame
+from .code import getfslineno
+from .code import Traceback
+from .code import TracebackEntry
+from .source import getrawcode
+from .source import Source
+
+
+__all__ = [
+    "Code",
+    "ExceptionInfo",
+    "Frame",
+    "Source",
+    "Traceback",
+    "TracebackEntry",
+    "filter_traceback",
+    "getfslineno",
+    "getrawcode",
+]

BIN
algo/py312/lib/python3.12/site-packages/_pytest/_code/__pycache__/__init__.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_code/__pycache__/code.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_code/__pycache__/source.cpython-312.pyc


+ 1565 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_code/code.py

@@ -0,0 +1,1565 @@
+# mypy: allow-untyped-defs
+from __future__ import annotations
+
+import ast
+from collections.abc import Callable
+from collections.abc import Iterable
+from collections.abc import Mapping
+from collections.abc import Sequence
+import dataclasses
+import inspect
+from inspect import CO_VARARGS
+from inspect import CO_VARKEYWORDS
+from io import StringIO
+import os
+from pathlib import Path
+import re
+import sys
+from traceback import extract_tb
+from traceback import format_exception
+from traceback import format_exception_only
+from traceback import FrameSummary
+from types import CodeType
+from types import FrameType
+from types import TracebackType
+from typing import Any
+from typing import ClassVar
+from typing import Final
+from typing import final
+from typing import Generic
+from typing import Literal
+from typing import overload
+from typing import SupportsIndex
+from typing import TypeAlias
+from typing import TypeVar
+
+import pluggy
+
+import _pytest
+from _pytest._code.source import findsource
+from _pytest._code.source import getrawcode
+from _pytest._code.source import getstatementrange_ast
+from _pytest._code.source import Source
+from _pytest._io import TerminalWriter
+from _pytest._io.saferepr import safeformat
+from _pytest._io.saferepr import saferepr
+from _pytest.compat import get_real_func
+from _pytest.deprecated import check_ispytest
+from _pytest.pathlib import absolutepath
+from _pytest.pathlib import bestrelpath
+
+
+if sys.version_info < (3, 11):
+    from exceptiongroup import BaseExceptionGroup
+
+TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
+
+EXCEPTION_OR_MORE = type[BaseException] | tuple[type[BaseException], ...]
+
+
+class Code:
+    """Wrapper around Python code objects."""
+
+    __slots__ = ("raw",)
+
+    def __init__(self, obj: CodeType) -> None:
+        self.raw = obj
+
+    @classmethod
+    def from_function(cls, obj: object) -> Code:
+        return cls(getrawcode(obj))
+
+    def __eq__(self, other):
+        return self.raw == other.raw
+
+    # Ignore type because of https://github.com/python/mypy/issues/4266.
+    __hash__ = None  # type: ignore
+
+    @property
+    def firstlineno(self) -> int:
+        return self.raw.co_firstlineno - 1
+
+    @property
+    def name(self) -> str:
+        return self.raw.co_name
+
+    @property
+    def path(self) -> Path | str:
+        """Return a path object pointing to source code, or an ``str`` in
+        case of ``OSError`` / non-existing file."""
+        if not self.raw.co_filename:
+            return ""
+        try:
+            p = absolutepath(self.raw.co_filename)
+            # maybe don't try this checking
+            if not p.exists():
+                raise OSError("path check failed.")
+            return p
+        except OSError:
+            # XXX maybe try harder like the weird logic
+            # in the standard lib [linecache.updatecache] does?
+            return self.raw.co_filename
+
+    @property
+    def fullsource(self) -> Source | None:
+        """Return a _pytest._code.Source object for the full source file of the code."""
+        full, _ = findsource(self.raw)
+        return full
+
+    def source(self) -> Source:
+        """Return a _pytest._code.Source object for the code object's source only."""
+        # return source only for that part of code
+        return Source(self.raw)
+
+    def getargs(self, var: bool = False) -> tuple[str, ...]:
+        """Return a tuple with the argument names for the code object.
+
+        If 'var' is set True also return the names of the variable and
+        keyword arguments when present.
+        """
+        # Handy shortcut for getting args.
+        raw = self.raw
+        argcount = raw.co_argcount
+        if var:
+            argcount += raw.co_flags & CO_VARARGS
+            argcount += raw.co_flags & CO_VARKEYWORDS
+        return raw.co_varnames[:argcount]
+
+
+class Frame:
+    """Wrapper around a Python frame holding f_locals and f_globals
+    in which expressions can be evaluated."""
+
+    __slots__ = ("raw",)
+
+    def __init__(self, frame: FrameType) -> None:
+        self.raw = frame
+
+    @property
+    def lineno(self) -> int:
+        return self.raw.f_lineno - 1
+
+    @property
+    def f_globals(self) -> dict[str, Any]:
+        return self.raw.f_globals
+
+    @property
+    def f_locals(self) -> dict[str, Any]:
+        return self.raw.f_locals
+
+    @property
+    def code(self) -> Code:
+        return Code(self.raw.f_code)
+
+    @property
+    def statement(self) -> Source:
+        """Statement this frame is at."""
+        if self.code.fullsource is None:
+            return Source("")
+        return self.code.fullsource.getstatement(self.lineno)
+
+    def eval(self, code, **vars):
+        """Evaluate 'code' in the frame.
+
+        'vars' are optional additional local variables.
+
+        Returns the result of the evaluation.
+        """
+        f_locals = self.f_locals.copy()
+        f_locals.update(vars)
+        return eval(code, self.f_globals, f_locals)
+
+    def repr(self, object: object) -> str:
+        """Return a 'safe' (non-recursive, one-line) string repr for 'object'."""
+        return saferepr(object)
+
+    def getargs(self, var: bool = False):
+        """Return a list of tuples (name, value) for all arguments.
+
+        If 'var' is set True, also include the variable and keyword arguments
+        when present.
+        """
+        retval = []
+        for arg in self.code.getargs(var):
+            try:
+                retval.append((arg, self.f_locals[arg]))
+            except KeyError:
+                pass  # this can occur when using Psyco
+        return retval
+
+
+class TracebackEntry:
+    """A single entry in a Traceback."""
+
+    __slots__ = ("_rawentry", "_repr_style")
+
+    def __init__(
+        self,
+        rawentry: TracebackType,
+        repr_style: Literal["short", "long"] | None = None,
+    ) -> None:
+        self._rawentry: Final = rawentry
+        self._repr_style: Final = repr_style
+
+    def with_repr_style(
+        self, repr_style: Literal["short", "long"] | None
+    ) -> TracebackEntry:
+        return TracebackEntry(self._rawentry, repr_style)
+
+    @property
+    def lineno(self) -> int:
+        return self._rawentry.tb_lineno - 1
+
+    def get_python_framesummary(self) -> FrameSummary:
+        # Python's built-in traceback module implements all the nitty gritty
+        # details to get column numbers of out frames.
+        stack_summary = extract_tb(self._rawentry, limit=1)
+        return stack_summary[0]
+
+    # Column and end line numbers introduced in python 3.11
+    if sys.version_info < (3, 11):
+
+        @property
+        def end_lineno_relative(self) -> int | None:
+            return None
+
+        @property
+        def colno(self) -> int | None:
+            return None
+
+        @property
+        def end_colno(self) -> int | None:
+            return None
+    else:
+
+        @property
+        def end_lineno_relative(self) -> int | None:
+            frame_summary = self.get_python_framesummary()
+            if frame_summary.end_lineno is None:  # pragma: no cover
+                return None
+            return frame_summary.end_lineno - 1 - self.frame.code.firstlineno
+
+        @property
+        def colno(self) -> int | None:
+            """Starting byte offset of the expression in the traceback entry."""
+            return self.get_python_framesummary().colno
+
+        @property
+        def end_colno(self) -> int | None:
+            """Ending byte offset of the expression in the traceback entry."""
+            return self.get_python_framesummary().end_colno
+
+    @property
+    def frame(self) -> Frame:
+        return Frame(self._rawentry.tb_frame)
+
+    @property
+    def relline(self) -> int:
+        return self.lineno - self.frame.code.firstlineno
+
+    def __repr__(self) -> str:
+        return f"<TracebackEntry {self.frame.code.path}:{self.lineno + 1}>"
+
+    @property
+    def statement(self) -> Source:
+        """_pytest._code.Source object for the current statement."""
+        source = self.frame.code.fullsource
+        assert source is not None
+        return source.getstatement(self.lineno)
+
+    @property
+    def path(self) -> Path | str:
+        """Path to the source code."""
+        return self.frame.code.path
+
+    @property
+    def locals(self) -> dict[str, Any]:
+        """Locals of underlying frame."""
+        return self.frame.f_locals
+
+    def getfirstlinesource(self) -> int:
+        return self.frame.code.firstlineno
+
+    def getsource(
+        self, astcache: dict[str | Path, ast.AST] | None = None
+    ) -> Source | None:
+        """Return failing source code."""
+        # we use the passed in astcache to not reparse asttrees
+        # within exception info printing
+        source = self.frame.code.fullsource
+        if source is None:
+            return None
+        key = astnode = None
+        if astcache is not None:
+            key = self.frame.code.path
+            if key is not None:
+                astnode = astcache.get(key, None)
+        start = self.getfirstlinesource()
+        try:
+            astnode, _, end = getstatementrange_ast(
+                self.lineno, source, astnode=astnode
+            )
+        except SyntaxError:
+            end = self.lineno + 1
+        else:
+            if key is not None and astcache is not None:
+                astcache[key] = astnode
+        return source[start:end]
+
+    source = property(getsource)
+
+    def ishidden(self, excinfo: ExceptionInfo[BaseException] | None) -> bool:
+        """Return True if the current frame has a var __tracebackhide__
+        resolving to True.
+
+        If __tracebackhide__ is a callable, it gets called with the
+        ExceptionInfo instance and can decide whether to hide the traceback.
+
+        Mostly for internal use.
+        """
+        tbh: bool | Callable[[ExceptionInfo[BaseException] | None], bool] = False
+        for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals):
+            # in normal cases, f_locals and f_globals are dictionaries
+            # however via `exec(...)` / `eval(...)` they can be other types
+            # (even incorrect types!).
+            # as such, we suppress all exceptions while accessing __tracebackhide__
+            try:
+                tbh = maybe_ns_dct["__tracebackhide__"]
+            except Exception:
+                pass
+            else:
+                break
+        if tbh and callable(tbh):
+            return tbh(excinfo)
+        return tbh
+
+    def __str__(self) -> str:
+        name = self.frame.code.name
+        try:
+            line = str(self.statement).lstrip()
+        except KeyboardInterrupt:
+            raise
+        except BaseException:
+            line = "???"
+        # This output does not quite match Python's repr for traceback entries,
+        # but changing it to do so would break certain plugins.  See
+        # https://github.com/pytest-dev/pytest/pull/7535/ for details.
+        return f"  File '{self.path}':{self.lineno + 1} in {name}\n  {line}\n"
+
+    @property
+    def name(self) -> str:
+        """co_name of underlying code."""
+        return self.frame.code.raw.co_name
+
+
+class Traceback(list[TracebackEntry]):
+    """Traceback objects encapsulate and offer higher level access to Traceback entries."""
+
+    def __init__(
+        self,
+        tb: TracebackType | Iterable[TracebackEntry],
+    ) -> None:
+        """Initialize from given python traceback object and ExceptionInfo."""
+        if isinstance(tb, TracebackType):
+
+            def f(cur: TracebackType) -> Iterable[TracebackEntry]:
+                cur_: TracebackType | None = cur
+                while cur_ is not None:
+                    yield TracebackEntry(cur_)
+                    cur_ = cur_.tb_next
+
+            super().__init__(f(tb))
+        else:
+            super().__init__(tb)
+
+    def cut(
+        self,
+        path: os.PathLike[str] | str | None = None,
+        lineno: int | None = None,
+        firstlineno: int | None = None,
+        excludepath: os.PathLike[str] | None = None,
+    ) -> Traceback:
+        """Return a Traceback instance wrapping part of this Traceback.
+
+        By providing any combination of path, lineno and firstlineno, the
+        first frame to start the to-be-returned traceback is determined.
+
+        This allows cutting the first part of a Traceback instance e.g.
+        for formatting reasons (removing some uninteresting bits that deal
+        with handling of the exception/traceback).
+        """
+        path_ = None if path is None else os.fspath(path)
+        excludepath_ = None if excludepath is None else os.fspath(excludepath)
+        for x in self:
+            code = x.frame.code
+            codepath = code.path
+            if path is not None and str(codepath) != path_:
+                continue
+            if (
+                excludepath is not None
+                and isinstance(codepath, Path)
+                and excludepath_ in (str(p) for p in codepath.parents)  # type: ignore[operator]
+            ):
+                continue
+            if lineno is not None and x.lineno != lineno:
+                continue
+            if firstlineno is not None and x.frame.code.firstlineno != firstlineno:
+                continue
+            return Traceback(x._rawentry)
+        return self
+
+    @overload
+    def __getitem__(self, key: SupportsIndex) -> TracebackEntry: ...
+
+    @overload
+    def __getitem__(self, key: slice) -> Traceback: ...
+
+    def __getitem__(self, key: SupportsIndex | slice) -> TracebackEntry | Traceback:
+        if isinstance(key, slice):
+            return self.__class__(super().__getitem__(key))
+        else:
+            return super().__getitem__(key)
+
+    def filter(
+        self,
+        excinfo_or_fn: ExceptionInfo[BaseException] | Callable[[TracebackEntry], bool],
+        /,
+    ) -> Traceback:
+        """Return a Traceback instance with certain items removed.
+
+        If the filter is an `ExceptionInfo`, removes all the ``TracebackEntry``s
+        which are hidden (see ishidden() above).
+
+        Otherwise, the filter is a function that gets a single argument, a
+        ``TracebackEntry`` instance, and should return True when the item should
+        be added to the ``Traceback``, False when not.
+        """
+        if isinstance(excinfo_or_fn, ExceptionInfo):
+            fn = lambda x: not x.ishidden(excinfo_or_fn)  # noqa: E731
+        else:
+            fn = excinfo_or_fn
+        return Traceback(filter(fn, self))
+
+    def recursionindex(self) -> int | None:
+        """Return the index of the frame/TracebackEntry where recursion originates if
+        appropriate, None if no recursion occurred."""
+        cache: dict[tuple[Any, int, int], list[dict[str, Any]]] = {}
+        for i, entry in enumerate(self):
+            # id for the code.raw is needed to work around
+            # the strange metaprogramming in the decorator lib from pypi
+            # which generates code objects that have hash/value equality
+            # XXX needs a test
+            key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
+            values = cache.setdefault(key, [])
+            # Since Python 3.13 f_locals is a proxy, freeze it.
+            loc = dict(entry.frame.f_locals)
+            if values:
+                for otherloc in values:
+                    if otherloc == loc:
+                        return i
+            values.append(loc)
+        return None
+
+
+def stringify_exception(
+    exc: BaseException, include_subexception_msg: bool = True
+) -> str:
+    try:
+        notes = getattr(exc, "__notes__", [])
+    except KeyError:
+        # Workaround for https://github.com/python/cpython/issues/98778 on
+        # some 3.10 and 3.11 patch versions.
+        HTTPError = getattr(sys.modules.get("urllib.error", None), "HTTPError", ())
+        if sys.version_info < (3, 12) and isinstance(exc, HTTPError):
+            notes = []
+        else:  # pragma: no cover
+            # exception not related to above bug, reraise
+            raise
+    if not include_subexception_msg and isinstance(exc, BaseExceptionGroup):
+        message = exc.message
+    else:
+        message = str(exc)
+
+    return "\n".join(
+        [
+            message,
+            *notes,
+        ]
+    )
+
+
+E = TypeVar("E", bound=BaseException, covariant=True)
+
+
+@final
+@dataclasses.dataclass
+class ExceptionInfo(Generic[E]):
+    """Wraps sys.exc_info() objects and offers help for navigating the traceback."""
+
+    _assert_start_repr: ClassVar = "AssertionError('assert "
+
+    _excinfo: tuple[type[E], E, TracebackType] | None
+    _striptext: str
+    _traceback: Traceback | None
+
+    def __init__(
+        self,
+        excinfo: tuple[type[E], E, TracebackType] | None,
+        striptext: str = "",
+        traceback: Traceback | None = None,
+        *,
+        _ispytest: bool = False,
+    ) -> None:
+        check_ispytest(_ispytest)
+        self._excinfo = excinfo
+        self._striptext = striptext
+        self._traceback = traceback
+
+    @classmethod
+    def from_exception(
+        cls,
+        # Ignoring error: "Cannot use a covariant type variable as a parameter".
+        # This is OK to ignore because this class is (conceptually) readonly.
+        # See https://github.com/python/mypy/issues/7049.
+        exception: E,  # type: ignore[misc]
+        exprinfo: str | None = None,
+    ) -> ExceptionInfo[E]:
+        """Return an ExceptionInfo for an existing exception.
+
+        The exception must have a non-``None`` ``__traceback__`` attribute,
+        otherwise this function fails with an assertion error. This means that
+        the exception must have been raised, or added a traceback with the
+        :py:meth:`~BaseException.with_traceback()` method.
+
+        :param exprinfo:
+            A text string helping to determine if we should strip
+            ``AssertionError`` from the output. Defaults to the exception
+            message/``__str__()``.
+
+        .. versionadded:: 7.4
+        """
+        assert exception.__traceback__, (
+            "Exceptions passed to ExcInfo.from_exception(...)"
+            " must have a non-None __traceback__."
+        )
+        exc_info = (type(exception), exception, exception.__traceback__)
+        return cls.from_exc_info(exc_info, exprinfo)
+
+    @classmethod
+    def from_exc_info(
+        cls,
+        exc_info: tuple[type[E], E, TracebackType],
+        exprinfo: str | None = None,
+    ) -> ExceptionInfo[E]:
+        """Like :func:`from_exception`, but using old-style exc_info tuple."""
+        _striptext = ""
+        if exprinfo is None and isinstance(exc_info[1], AssertionError):
+            exprinfo = getattr(exc_info[1], "msg", None)
+            if exprinfo is None:
+                exprinfo = saferepr(exc_info[1])
+            if exprinfo and exprinfo.startswith(cls._assert_start_repr):
+                _striptext = "AssertionError: "
+
+        return cls(exc_info, _striptext, _ispytest=True)
+
+    @classmethod
+    def from_current(cls, exprinfo: str | None = None) -> ExceptionInfo[BaseException]:
+        """Return an ExceptionInfo matching the current traceback.
+
+        .. warning::
+
+            Experimental API
+
+        :param exprinfo:
+            A text string helping to determine if we should strip
+            ``AssertionError`` from the output. Defaults to the exception
+            message/``__str__()``.
+        """
+        tup = sys.exc_info()
+        assert tup[0] is not None, "no current exception"
+        assert tup[1] is not None, "no current exception"
+        assert tup[2] is not None, "no current exception"
+        exc_info = (tup[0], tup[1], tup[2])
+        return ExceptionInfo.from_exc_info(exc_info, exprinfo)
+
+    @classmethod
+    def for_later(cls) -> ExceptionInfo[E]:
+        """Return an unfilled ExceptionInfo."""
+        return cls(None, _ispytest=True)
+
+    def fill_unfilled(self, exc_info: tuple[type[E], E, TracebackType]) -> None:
+        """Fill an unfilled ExceptionInfo created with ``for_later()``."""
+        assert self._excinfo is None, "ExceptionInfo was already filled"
+        self._excinfo = exc_info
+
+    @property
+    def type(self) -> type[E]:
+        """The exception class."""
+        assert self._excinfo is not None, (
+            ".type can only be used after the context manager exits"
+        )
+        return self._excinfo[0]
+
+    @property
+    def value(self) -> E:
+        """The exception value."""
+        assert self._excinfo is not None, (
+            ".value can only be used after the context manager exits"
+        )
+        return self._excinfo[1]
+
+    @property
+    def tb(self) -> TracebackType:
+        """The exception raw traceback."""
+        assert self._excinfo is not None, (
+            ".tb can only be used after the context manager exits"
+        )
+        return self._excinfo[2]
+
+    @property
+    def typename(self) -> str:
+        """The type name of the exception."""
+        assert self._excinfo is not None, (
+            ".typename can only be used after the context manager exits"
+        )
+        return self.type.__name__
+
+    @property
+    def traceback(self) -> Traceback:
+        """The traceback."""
+        if self._traceback is None:
+            self._traceback = Traceback(self.tb)
+        return self._traceback
+
+    @traceback.setter
+    def traceback(self, value: Traceback) -> None:
+        self._traceback = value
+
+    def __repr__(self) -> str:
+        if self._excinfo is None:
+            return "<ExceptionInfo for raises contextmanager>"
+        return f"<{self.__class__.__name__} {saferepr(self._excinfo[1])} tblen={len(self.traceback)}>"
+
+    def exconly(self, tryshort: bool = False) -> str:
+        """Return the exception as a string.
+
+        When 'tryshort' resolves to True, and the exception is an
+        AssertionError, only the actual exception part of the exception
+        representation is returned (so 'AssertionError: ' is removed from
+        the beginning).
+        """
+
+        def _get_single_subexc(
+            eg: BaseExceptionGroup[BaseException],
+        ) -> BaseException | None:
+            if len(eg.exceptions) != 1:
+                return None
+            if isinstance(e := eg.exceptions[0], BaseExceptionGroup):
+                return _get_single_subexc(e)
+            return e
+
+        if (
+            tryshort
+            and isinstance(self.value, BaseExceptionGroup)
+            and (subexc := _get_single_subexc(self.value)) is not None
+        ):
+            return f"{subexc!r} [single exception in {type(self.value).__name__}]"
+
+        lines = format_exception_only(self.type, self.value)
+        text = "".join(lines)
+        text = text.rstrip()
+        if tryshort:
+            if text.startswith(self._striptext):
+                text = text[len(self._striptext) :]
+        return text
+
+    def errisinstance(self, exc: EXCEPTION_OR_MORE) -> bool:
+        """Return True if the exception is an instance of exc.
+
+        Consider using ``isinstance(excinfo.value, exc)`` instead.
+        """
+        return isinstance(self.value, exc)
+
+    def _getreprcrash(self) -> ReprFileLocation | None:
+        # Find last non-hidden traceback entry that led to the exception of the
+        # traceback, or None if all hidden.
+        for i in range(-1, -len(self.traceback) - 1, -1):
+            entry = self.traceback[i]
+            if not entry.ishidden(self):
+                path, lineno = entry.frame.code.raw.co_filename, entry.lineno
+                exconly = self.exconly(tryshort=True)
+                return ReprFileLocation(path, lineno + 1, exconly)
+        return None
+
+    def getrepr(
+        self,
+        showlocals: bool = False,
+        style: TracebackStyle = "long",
+        abspath: bool = False,
+        tbfilter: bool | Callable[[ExceptionInfo[BaseException]], Traceback] = True,
+        funcargs: bool = False,
+        truncate_locals: bool = True,
+        truncate_args: bool = True,
+        chain: bool = True,
+    ) -> ReprExceptionInfo | ExceptionChainRepr:
+        """Return str()able representation of this exception info.
+
+        :param bool showlocals:
+            Show locals per traceback entry.
+            Ignored if ``style=="native"``.
+
+        :param str style:
+            long|short|line|no|native|value traceback style.
+
+        :param bool abspath:
+            If paths should be changed to absolute or left unchanged.
+
+        :param tbfilter:
+            A filter for traceback entries.
+
+            * If false, don't hide any entries.
+            * If true, hide internal entries and entries that contain a local
+              variable ``__tracebackhide__ = True``.
+            * If a callable, delegates the filtering to the callable.
+
+            Ignored if ``style`` is ``"native"``.
+
+        :param bool funcargs:
+            Show fixtures ("funcargs" for legacy purposes) per traceback entry.
+
+        :param bool truncate_locals:
+            With ``showlocals==True``, make sure locals can be safely represented as strings.
+
+        :param bool truncate_args:
+            With ``showargs==True``, make sure args can be safely represented as strings.
+
+        :param bool chain:
+            If chained exceptions in Python 3 should be shown.
+
+        .. versionchanged:: 3.9
+
+            Added the ``chain`` parameter.
+        """
+        if style == "native":
+            return ReprExceptionInfo(
+                reprtraceback=ReprTracebackNative(
+                    format_exception(
+                        self.type,
+                        self.value,
+                        self.traceback[0]._rawentry if self.traceback else None,
+                    )
+                ),
+                reprcrash=self._getreprcrash(),
+            )
+
+        fmt = FormattedExcinfo(
+            showlocals=showlocals,
+            style=style,
+            abspath=abspath,
+            tbfilter=tbfilter,
+            funcargs=funcargs,
+            truncate_locals=truncate_locals,
+            truncate_args=truncate_args,
+            chain=chain,
+        )
+        return fmt.repr_excinfo(self)
+
+    def match(self, regexp: str | re.Pattern[str]) -> Literal[True]:
+        """Check whether the regular expression `regexp` matches the string
+        representation of the exception using :func:`python:re.search`.
+
+        If it matches `True` is returned, otherwise an `AssertionError` is raised.
+        """
+        __tracebackhide__ = True
+        value = stringify_exception(self.value)
+        msg = (
+            f"Regex pattern did not match.\n"
+            f"  Expected regex: {regexp!r}\n"
+            f"  Actual message: {value!r}"
+        )
+        if regexp == value:
+            msg += "\n Did you mean to `re.escape()` the regex?"
+        assert re.search(regexp, value), msg
+        # Return True to allow for "assert excinfo.match()".
+        return True
+
+    def _group_contains(
+        self,
+        exc_group: BaseExceptionGroup[BaseException],
+        expected_exception: EXCEPTION_OR_MORE,
+        match: str | re.Pattern[str] | None,
+        target_depth: int | None = None,
+        current_depth: int = 1,
+    ) -> bool:
+        """Return `True` if a `BaseExceptionGroup` contains a matching exception."""
+        if (target_depth is not None) and (current_depth > target_depth):
+            # already descended past the target depth
+            return False
+        for exc in exc_group.exceptions:
+            if isinstance(exc, BaseExceptionGroup):
+                if self._group_contains(
+                    exc, expected_exception, match, target_depth, current_depth + 1
+                ):
+                    return True
+            if (target_depth is not None) and (current_depth != target_depth):
+                # not at the target depth, no match
+                continue
+            if not isinstance(exc, expected_exception):
+                continue
+            if match is not None:
+                value = stringify_exception(exc)
+                if not re.search(match, value):
+                    continue
+            return True
+        return False
+
+    def group_contains(
+        self,
+        expected_exception: EXCEPTION_OR_MORE,
+        *,
+        match: str | re.Pattern[str] | None = None,
+        depth: int | None = None,
+    ) -> bool:
+        """Check whether a captured exception group contains a matching exception.
+
+        :param Type[BaseException] | Tuple[Type[BaseException]] expected_exception:
+            The expected exception type, or a tuple if one of multiple possible
+            exception types are expected.
+
+        :param str | re.Pattern[str] | None match:
+            If specified, a string containing a regular expression,
+            or a regular expression object, that is tested against the string
+            representation of the exception and its `PEP-678 <https://peps.python.org/pep-0678/>` `__notes__`
+            using :func:`re.search`.
+
+            To match a literal string that may contain :ref:`special characters
+            <re-syntax>`, the pattern can first be escaped with :func:`re.escape`.
+
+        :param Optional[int] depth:
+            If `None`, will search for a matching exception at any nesting depth.
+            If >= 1, will only match an exception if it's at the specified depth (depth = 1 being
+            the exceptions contained within the topmost exception group).
+
+        .. versionadded:: 8.0
+
+        .. warning::
+           This helper makes it easy to check for the presence of specific exceptions,
+           but it is very bad for checking that the group does *not* contain
+           *any other exceptions*.
+           You should instead consider using :class:`pytest.RaisesGroup`
+
+        """
+        msg = "Captured exception is not an instance of `BaseExceptionGroup`"
+        assert isinstance(self.value, BaseExceptionGroup), msg
+        msg = "`depth` must be >= 1 if specified"
+        assert (depth is None) or (depth >= 1), msg
+        return self._group_contains(self.value, expected_exception, match, depth)
+
+
+# Type alias for the `tbfilter` setting:
+# bool: If True, it should be filtered using Traceback.filter()
+# callable: A callable that takes an ExceptionInfo and returns the filtered traceback.
+TracebackFilter: TypeAlias = bool | Callable[[ExceptionInfo[BaseException]], Traceback]
+
+
+@dataclasses.dataclass
+class FormattedExcinfo:
+    """Presenting information about failing Functions and Generators."""
+
+    # for traceback entries
+    flow_marker: ClassVar = ">"
+    fail_marker: ClassVar = "E"
+
+    showlocals: bool = False
+    style: TracebackStyle = "long"
+    abspath: bool = True
+    tbfilter: TracebackFilter = True
+    funcargs: bool = False
+    truncate_locals: bool = True
+    truncate_args: bool = True
+    chain: bool = True
+    astcache: dict[str | Path, ast.AST] = dataclasses.field(
+        default_factory=dict, init=False, repr=False
+    )
+
+    def _getindent(self, source: Source) -> int:
+        # Figure out indent for the given source.
+        try:
+            s = str(source.getstatement(len(source) - 1))
+        except KeyboardInterrupt:
+            raise
+        except BaseException:
+            try:
+                s = str(source[-1])
+            except KeyboardInterrupt:
+                raise
+            except BaseException:
+                return 0
+        return 4 + (len(s) - len(s.lstrip()))
+
+    def _getentrysource(self, entry: TracebackEntry) -> Source | None:
+        source = entry.getsource(self.astcache)
+        if source is not None:
+            source = source.deindent()
+        return source
+
+    def repr_args(self, entry: TracebackEntry) -> ReprFuncArgs | None:
+        if self.funcargs:
+            args = []
+            for argname, argvalue in entry.frame.getargs(var=True):
+                if self.truncate_args:
+                    str_repr = saferepr(argvalue)
+                else:
+                    str_repr = saferepr(argvalue, maxsize=None)
+                args.append((argname, str_repr))
+            return ReprFuncArgs(args)
+        return None
+
+    def get_source(
+        self,
+        source: Source | None,
+        line_index: int = -1,
+        excinfo: ExceptionInfo[BaseException] | None = None,
+        short: bool = False,
+        end_line_index: int | None = None,
+        colno: int | None = None,
+        end_colno: int | None = None,
+    ) -> list[str]:
+        """Return formatted and marked up source lines."""
+        lines = []
+        if source is not None and line_index < 0:
+            line_index += len(source)
+        if source is None or line_index >= len(source.lines) or line_index < 0:
+            # `line_index` could still be outside `range(len(source.lines))` if
+            # we're processing AST with pathological position attributes.
+            source = Source("???")
+            line_index = 0
+        space_prefix = "    "
+        if short:
+            lines.append(space_prefix + source.lines[line_index].strip())
+            lines.extend(
+                self.get_highlight_arrows_for_line(
+                    raw_line=source.raw_lines[line_index],
+                    line=source.lines[line_index].strip(),
+                    lineno=line_index,
+                    end_lineno=end_line_index,
+                    colno=colno,
+                    end_colno=end_colno,
+                )
+            )
+        else:
+            for line in source.lines[:line_index]:
+                lines.append(space_prefix + line)
+            lines.append(self.flow_marker + "   " + source.lines[line_index])
+            lines.extend(
+                self.get_highlight_arrows_for_line(
+                    raw_line=source.raw_lines[line_index],
+                    line=source.lines[line_index],
+                    lineno=line_index,
+                    end_lineno=end_line_index,
+                    colno=colno,
+                    end_colno=end_colno,
+                )
+            )
+            for line in source.lines[line_index + 1 :]:
+                lines.append(space_prefix + line)
+        if excinfo is not None:
+            indent = 4 if short else self._getindent(source)
+            lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
+        return lines
+
+    def get_highlight_arrows_for_line(
+        self,
+        line: str,
+        raw_line: str,
+        lineno: int | None,
+        end_lineno: int | None,
+        colno: int | None,
+        end_colno: int | None,
+    ) -> list[str]:
+        """Return characters highlighting a source line.
+
+        Example with colno and end_colno pointing to the bar expression:
+                   "foo() + bar()"
+        returns    "        ^^^^^"
+        """
+        if lineno != end_lineno:
+            # Don't handle expressions that span multiple lines.
+            return []
+        if colno is None or end_colno is None:
+            # Can't do anything without column information.
+            return []
+
+        num_stripped_chars = len(raw_line) - len(line)
+
+        start_char_offset = _byte_offset_to_character_offset(raw_line, colno)
+        end_char_offset = _byte_offset_to_character_offset(raw_line, end_colno)
+        num_carets = end_char_offset - start_char_offset
+        # If the highlight would span the whole line, it is redundant, don't
+        # show it.
+        if num_carets >= len(line.strip()):
+            return []
+
+        highlights = "    "
+        highlights += " " * (start_char_offset - num_stripped_chars + 1)
+        highlights += "^" * num_carets
+        return [highlights]
+
+    def get_exconly(
+        self,
+        excinfo: ExceptionInfo[BaseException],
+        indent: int = 4,
+        markall: bool = False,
+    ) -> list[str]:
+        lines = []
+        indentstr = " " * indent
+        # Get the real exception information out.
+        exlines = excinfo.exconly(tryshort=True).split("\n")
+        failindent = self.fail_marker + indentstr[1:]
+        for line in exlines:
+            lines.append(failindent + line)
+            if not markall:
+                failindent = indentstr
+        return lines
+
+    def repr_locals(self, locals: Mapping[str, object]) -> ReprLocals | None:
+        if self.showlocals:
+            lines = []
+            keys = [loc for loc in locals if loc[0] != "@"]
+            keys.sort()
+            for name in keys:
+                value = locals[name]
+                if name == "__builtins__":
+                    lines.append("__builtins__ = <builtins>")
+                else:
+                    # This formatting could all be handled by the
+                    # _repr() function, which is only reprlib.Repr in
+                    # disguise, so is very configurable.
+                    if self.truncate_locals:
+                        str_repr = saferepr(value)
+                    else:
+                        str_repr = safeformat(value)
+                    # if len(str_repr) < 70 or not isinstance(value, (list, tuple, dict)):
+                    lines.append(f"{name:<10} = {str_repr}")
+                    # else:
+                    #    self._line("%-10s =\\" % (name,))
+                    #    # XXX
+                    #    pprint.pprint(value, stream=self.excinfowriter)
+            return ReprLocals(lines)
+        return None
+
+    def repr_traceback_entry(
+        self,
+        entry: TracebackEntry | None,
+        excinfo: ExceptionInfo[BaseException] | None = None,
+    ) -> ReprEntry:
+        lines: list[str] = []
+        style = (
+            entry._repr_style
+            if entry is not None and entry._repr_style is not None
+            else self.style
+        )
+        if style in ("short", "long") and entry is not None:
+            source = self._getentrysource(entry)
+            if source is None:
+                source = Source("???")
+                line_index = 0
+                end_line_index, colno, end_colno = None, None, None
+            else:
+                line_index = entry.relline
+                end_line_index = entry.end_lineno_relative
+                colno = entry.colno
+                end_colno = entry.end_colno
+            short = style == "short"
+            reprargs = self.repr_args(entry) if not short else None
+            s = self.get_source(
+                source=source,
+                line_index=line_index,
+                excinfo=excinfo,
+                short=short,
+                end_line_index=end_line_index,
+                colno=colno,
+                end_colno=end_colno,
+            )
+            lines.extend(s)
+            if short:
+                message = f"in {entry.name}"
+            else:
+                message = (excinfo and excinfo.typename) or ""
+            entry_path = entry.path
+            path = self._makepath(entry_path)
+            reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
+            localsrepr = self.repr_locals(entry.locals)
+            return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
+        elif style == "value":
+            if excinfo:
+                lines.extend(str(excinfo.value).split("\n"))
+            return ReprEntry(lines, None, None, None, style)
+        else:
+            if excinfo:
+                lines.extend(self.get_exconly(excinfo, indent=4))
+            return ReprEntry(lines, None, None, None, style)
+
+    def _makepath(self, path: Path | str) -> str:
+        if not self.abspath and isinstance(path, Path):
+            try:
+                np = bestrelpath(Path.cwd(), path)
+            except OSError:
+                return str(path)
+            if len(np) < len(str(path)):
+                return np
+        return str(path)
+
+    def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> ReprTraceback:
+        traceback = filter_excinfo_traceback(self.tbfilter, excinfo)
+
+        if isinstance(excinfo.value, RecursionError):
+            traceback, extraline = self._truncate_recursive_traceback(traceback)
+        else:
+            extraline = None
+
+        if not traceback:
+            if extraline is None:
+                extraline = "All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames."
+            entries = [self.repr_traceback_entry(None, excinfo)]
+            return ReprTraceback(entries, extraline, style=self.style)
+
+        last = traceback[-1]
+        if self.style == "value":
+            entries = [self.repr_traceback_entry(last, excinfo)]
+            return ReprTraceback(entries, None, style=self.style)
+
+        entries = [
+            self.repr_traceback_entry(entry, excinfo if last == entry else None)
+            for entry in traceback
+        ]
+        return ReprTraceback(entries, extraline, style=self.style)
+
+    def _truncate_recursive_traceback(
+        self, traceback: Traceback
+    ) -> tuple[Traceback, str | None]:
+        """Truncate the given recursive traceback trying to find the starting
+        point of the recursion.
+
+        The detection is done by going through each traceback entry and
+        finding the point in which the locals of the frame are equal to the
+        locals of a previous frame (see ``recursionindex()``).
+
+        Handle the situation where the recursion process might raise an
+        exception (for example comparing numpy arrays using equality raises a
+        TypeError), in which case we do our best to warn the user of the
+        error and show a limited traceback.
+        """
+        try:
+            recursionindex = traceback.recursionindex()
+        except Exception as e:
+            max_frames = 10
+            extraline: str | None = (
+                "!!! Recursion error detected, but an error occurred locating the origin of recursion.\n"
+                "  The following exception happened when comparing locals in the stack frame:\n"
+                f"    {type(e).__name__}: {e!s}\n"
+                f"  Displaying first and last {max_frames} stack frames out of {len(traceback)}."
+            )
+            # Type ignored because adding two instances of a List subtype
+            # currently incorrectly has type List instead of the subtype.
+            traceback = traceback[:max_frames] + traceback[-max_frames:]  # type: ignore
+        else:
+            if recursionindex is not None:
+                extraline = "!!! Recursion detected (same locals & position)"
+                traceback = traceback[: recursionindex + 1]
+            else:
+                extraline = None
+
+        return traceback, extraline
+
+    def repr_excinfo(self, excinfo: ExceptionInfo[BaseException]) -> ExceptionChainRepr:
+        repr_chain: list[tuple[ReprTraceback, ReprFileLocation | None, str | None]] = []
+        e: BaseException | None = excinfo.value
+        excinfo_: ExceptionInfo[BaseException] | None = excinfo
+        descr = None
+        seen: set[int] = set()
+        while e is not None and id(e) not in seen:
+            seen.add(id(e))
+
+            if excinfo_:
+                # Fall back to native traceback as a temporary workaround until
+                # full support for exception groups added to ExceptionInfo.
+                # See https://github.com/pytest-dev/pytest/issues/9159
+                reprtraceback: ReprTraceback | ReprTracebackNative
+                if isinstance(e, BaseExceptionGroup):
+                    # don't filter any sub-exceptions since they shouldn't have any internal frames
+                    traceback = filter_excinfo_traceback(self.tbfilter, excinfo)
+                    reprtraceback = ReprTracebackNative(
+                        format_exception(
+                            type(excinfo.value),
+                            excinfo.value,
+                            traceback[0]._rawentry,
+                        )
+                    )
+                else:
+                    reprtraceback = self.repr_traceback(excinfo_)
+                reprcrash = excinfo_._getreprcrash()
+            else:
+                # Fallback to native repr if the exception doesn't have a traceback:
+                # ExceptionInfo objects require a full traceback to work.
+                reprtraceback = ReprTracebackNative(format_exception(type(e), e, None))
+                reprcrash = None
+            repr_chain += [(reprtraceback, reprcrash, descr)]
+
+            if e.__cause__ is not None and self.chain:
+                e = e.__cause__
+                excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None
+                descr = "The above exception was the direct cause of the following exception:"
+            elif (
+                e.__context__ is not None and not e.__suppress_context__ and self.chain
+            ):
+                e = e.__context__
+                excinfo_ = ExceptionInfo.from_exception(e) if e.__traceback__ else None
+                descr = "During handling of the above exception, another exception occurred:"
+            else:
+                e = None
+        repr_chain.reverse()
+        return ExceptionChainRepr(repr_chain)
+
+
+@dataclasses.dataclass(eq=False)
+class TerminalRepr:
+    def __str__(self) -> str:
+        # FYI this is called from pytest-xdist's serialization of exception
+        # information.
+        io = StringIO()
+        tw = TerminalWriter(file=io)
+        self.toterminal(tw)
+        return io.getvalue().strip()
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__} instance at {id(self):0x}>"
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        raise NotImplementedError()
+
+
+# This class is abstract -- only subclasses are instantiated.
+@dataclasses.dataclass(eq=False)
+class ExceptionRepr(TerminalRepr):
+    # Provided by subclasses.
+    reprtraceback: ReprTraceback
+    reprcrash: ReprFileLocation | None
+    sections: list[tuple[str, str, str]] = dataclasses.field(
+        init=False, default_factory=list
+    )
+
+    def addsection(self, name: str, content: str, sep: str = "-") -> None:
+        self.sections.append((name, content, sep))
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        for name, content, sep in self.sections:
+            tw.sep(sep, name)
+            tw.line(content)
+
+
+@dataclasses.dataclass(eq=False)
+class ExceptionChainRepr(ExceptionRepr):
+    chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]]
+
+    def __init__(
+        self,
+        chain: Sequence[tuple[ReprTraceback, ReprFileLocation | None, str | None]],
+    ) -> None:
+        # reprcrash and reprtraceback of the outermost (the newest) exception
+        # in the chain.
+        super().__init__(
+            reprtraceback=chain[-1][0],
+            reprcrash=chain[-1][1],
+        )
+        self.chain = chain
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        for element in self.chain:
+            element[0].toterminal(tw)
+            if element[2] is not None:
+                tw.line("")
+                tw.line(element[2], yellow=True)
+        super().toterminal(tw)
+
+
+@dataclasses.dataclass(eq=False)
+class ReprExceptionInfo(ExceptionRepr):
+    reprtraceback: ReprTraceback
+    reprcrash: ReprFileLocation | None
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        self.reprtraceback.toterminal(tw)
+        super().toterminal(tw)
+
+
+@dataclasses.dataclass(eq=False)
+class ReprTraceback(TerminalRepr):
+    reprentries: Sequence[ReprEntry | ReprEntryNative]
+    extraline: str | None
+    style: TracebackStyle
+
+    entrysep: ClassVar = "_ "
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        # The entries might have different styles.
+        for i, entry in enumerate(self.reprentries):
+            if entry.style == "long":
+                tw.line("")
+            entry.toterminal(tw)
+            if i < len(self.reprentries) - 1:
+                next_entry = self.reprentries[i + 1]
+                if entry.style == "long" or (
+                    entry.style == "short" and next_entry.style == "long"
+                ):
+                    tw.sep(self.entrysep)
+
+        if self.extraline:
+            tw.line(self.extraline)
+
+
+class ReprTracebackNative(ReprTraceback):
+    def __init__(self, tblines: Sequence[str]) -> None:
+        self.reprentries = [ReprEntryNative(tblines)]
+        self.extraline = None
+        self.style = "native"
+
+
+@dataclasses.dataclass(eq=False)
+class ReprEntryNative(TerminalRepr):
+    lines: Sequence[str]
+
+    style: ClassVar[TracebackStyle] = "native"
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        tw.write("".join(self.lines))
+
+
+@dataclasses.dataclass(eq=False)
+class ReprEntry(TerminalRepr):
+    lines: Sequence[str]
+    reprfuncargs: ReprFuncArgs | None
+    reprlocals: ReprLocals | None
+    reprfileloc: ReprFileLocation | None
+    style: TracebackStyle
+
+    def _write_entry_lines(self, tw: TerminalWriter) -> None:
+        """Write the source code portions of a list of traceback entries with syntax highlighting.
+
+        Usually entries are lines like these:
+
+            "     x = 1"
+            ">    assert x == 2"
+            "E    assert 1 == 2"
+
+        This function takes care of rendering the "source" portions of it (the lines without
+        the "E" prefix) using syntax highlighting, taking care to not highlighting the ">"
+        character, as doing so might break line continuations.
+        """
+        if not self.lines:
+            return
+
+        if self.style == "value":
+            # Using tw.write instead of tw.line for testing purposes due to TWMock implementation;
+            # lines written with TWMock.line and TWMock._write_source cannot be distinguished
+            # from each other, whereas lines written with TWMock.write are marked with TWMock.WRITE
+            for line in self.lines:
+                tw.write(line)
+                tw.write("\n")
+            return
+
+        # separate indents and source lines that are not failures: we want to
+        # highlight the code but not the indentation, which may contain markers
+        # such as ">   assert 0"
+        fail_marker = f"{FormattedExcinfo.fail_marker}   "
+        indent_size = len(fail_marker)
+        indents: list[str] = []
+        source_lines: list[str] = []
+        failure_lines: list[str] = []
+        for index, line in enumerate(self.lines):
+            is_failure_line = line.startswith(fail_marker)
+            if is_failure_line:
+                # from this point on all lines are considered part of the failure
+                failure_lines.extend(self.lines[index:])
+                break
+            else:
+                indents.append(line[:indent_size])
+                source_lines.append(line[indent_size:])
+
+        tw._write_source(source_lines, indents)
+
+        # failure lines are always completely red and bold
+        for line in failure_lines:
+            tw.line(line, bold=True, red=True)
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        if self.style == "short":
+            if self.reprfileloc:
+                self.reprfileloc.toterminal(tw)
+            self._write_entry_lines(tw)
+            if self.reprlocals:
+                self.reprlocals.toterminal(tw, indent=" " * 8)
+            return
+
+        if self.reprfuncargs:
+            self.reprfuncargs.toterminal(tw)
+
+        self._write_entry_lines(tw)
+
+        if self.reprlocals:
+            tw.line("")
+            self.reprlocals.toterminal(tw)
+        if self.reprfileloc:
+            if self.lines:
+                tw.line("")
+            self.reprfileloc.toterminal(tw)
+
+    def __str__(self) -> str:
+        return "{}\n{}\n{}".format(
+            "\n".join(self.lines), self.reprlocals, self.reprfileloc
+        )
+
+
+@dataclasses.dataclass(eq=False)
+class ReprFileLocation(TerminalRepr):
+    path: str
+    lineno: int
+    message: str
+
+    def __post_init__(self) -> None:
+        self.path = str(self.path)
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        # Filename and lineno output for each entry, using an output format
+        # that most editors understand.
+        msg = self.message
+        i = msg.find("\n")
+        if i != -1:
+            msg = msg[:i]
+        tw.write(self.path, bold=True, red=True)
+        tw.line(f":{self.lineno}: {msg}")
+
+
+@dataclasses.dataclass(eq=False)
+class ReprLocals(TerminalRepr):
+    lines: Sequence[str]
+
+    def toterminal(self, tw: TerminalWriter, indent="") -> None:
+        for line in self.lines:
+            tw.line(indent + line)
+
+
+@dataclasses.dataclass(eq=False)
+class ReprFuncArgs(TerminalRepr):
+    args: Sequence[tuple[str, object]]
+
+    def toterminal(self, tw: TerminalWriter) -> None:
+        if self.args:
+            linesofar = ""
+            for name, value in self.args:
+                ns = f"{name} = {value}"
+                if len(ns) + len(linesofar) + 2 > tw.fullwidth:
+                    if linesofar:
+                        tw.line(linesofar)
+                    linesofar = ns
+                else:
+                    if linesofar:
+                        linesofar += ", " + ns
+                    else:
+                        linesofar = ns
+            if linesofar:
+                tw.line(linesofar)
+            tw.line("")
+
+
+def getfslineno(obj: object) -> tuple[str | Path, int]:
+    """Return source location (path, lineno) for the given object.
+
+    If the source cannot be determined return ("", -1).
+
+    The line number is 0-based.
+    """
+    # xxx let decorators etc specify a sane ordering
+    # NOTE: this used to be done in _pytest.compat.getfslineno, initially added
+    #       in 6ec13a2b9.  It ("place_as") appears to be something very custom.
+    obj = get_real_func(obj)
+    if hasattr(obj, "place_as"):
+        obj = obj.place_as
+
+    try:
+        code = Code.from_function(obj)
+    except TypeError:
+        try:
+            fn = inspect.getsourcefile(obj) or inspect.getfile(obj)  # type: ignore[arg-type]
+        except TypeError:
+            return "", -1
+
+        fspath = (fn and absolutepath(fn)) or ""
+        lineno = -1
+        if fspath:
+            try:
+                _, lineno = findsource(obj)
+            except OSError:
+                pass
+        return fspath, lineno
+
+    return code.path, code.firstlineno
+
+
+def _byte_offset_to_character_offset(str, offset):
+    """Converts a byte based offset in a string to a code-point."""
+    as_utf8 = str.encode("utf-8")
+    return len(as_utf8[:offset].decode("utf-8", errors="replace"))
+
+
+# Relative paths that we use to filter traceback entries from appearing to the user;
+# see filter_traceback.
+# note: if we need to add more paths than what we have now we should probably use a list
+# for better maintenance.
+
+_PLUGGY_DIR = Path(pluggy.__file__.rstrip("oc"))
+# pluggy is either a package or a single module depending on the version
+if _PLUGGY_DIR.name == "__init__.py":
+    _PLUGGY_DIR = _PLUGGY_DIR.parent
+_PYTEST_DIR = Path(_pytest.__file__).parent
+
+
+def filter_traceback(entry: TracebackEntry) -> bool:
+    """Return True if a TracebackEntry instance should be included in tracebacks.
+
+    We hide traceback entries of:
+
+    * dynamically generated code (no code to show up for it);
+    * internal traceback from pytest or its internal libraries, py and pluggy.
+    """
+    # entry.path might sometimes return a str object when the entry
+    # points to dynamically generated code.
+    # See https://bitbucket.org/pytest-dev/py/issues/71.
+    raw_filename = entry.frame.code.raw.co_filename
+    is_generated = "<" in raw_filename and ">" in raw_filename
+    if is_generated:
+        return False
+
+    # entry.path might point to a non-existing file, in which case it will
+    # also return a str object. See #1133.
+    p = Path(entry.path)
+
+    parents = p.parents
+    if _PLUGGY_DIR in parents:
+        return False
+    if _PYTEST_DIR in parents:
+        return False
+
+    return True
+
+
+def filter_excinfo_traceback(
+    tbfilter: TracebackFilter, excinfo: ExceptionInfo[BaseException]
+) -> Traceback:
+    """Filter the exception traceback in ``excinfo`` according to ``tbfilter``."""
+    if callable(tbfilter):
+        return tbfilter(excinfo)
+    elif tbfilter:
+        return excinfo.traceback.filter(excinfo)
+    else:
+        return excinfo.traceback

+ 225 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_code/source.py

@@ -0,0 +1,225 @@
+# mypy: allow-untyped-defs
+from __future__ import annotations
+
+import ast
+from bisect import bisect_right
+from collections.abc import Iterable
+from collections.abc import Iterator
+import inspect
+import textwrap
+import tokenize
+import types
+from typing import overload
+import warnings
+
+
+class Source:
+    """An immutable object holding a source code fragment.
+
+    When using Source(...), the source lines are deindented.
+    """
+
+    def __init__(self, obj: object = None) -> None:
+        if not obj:
+            self.lines: list[str] = []
+            self.raw_lines: list[str] = []
+        elif isinstance(obj, Source):
+            self.lines = obj.lines
+            self.raw_lines = obj.raw_lines
+        elif isinstance(obj, tuple | list):
+            self.lines = deindent(x.rstrip("\n") for x in obj)
+            self.raw_lines = list(x.rstrip("\n") for x in obj)
+        elif isinstance(obj, str):
+            self.lines = deindent(obj.split("\n"))
+            self.raw_lines = obj.split("\n")
+        else:
+            try:
+                rawcode = getrawcode(obj)
+                src = inspect.getsource(rawcode)
+            except TypeError:
+                src = inspect.getsource(obj)  # type: ignore[arg-type]
+            self.lines = deindent(src.split("\n"))
+            self.raw_lines = src.split("\n")
+
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Source):
+            return NotImplemented
+        return self.lines == other.lines
+
+    # Ignore type because of https://github.com/python/mypy/issues/4266.
+    __hash__ = None  # type: ignore
+
+    @overload
+    def __getitem__(self, key: int) -> str: ...
+
+    @overload
+    def __getitem__(self, key: slice) -> Source: ...
+
+    def __getitem__(self, key: int | slice) -> str | Source:
+        if isinstance(key, int):
+            return self.lines[key]
+        else:
+            if key.step not in (None, 1):
+                raise IndexError("cannot slice a Source with a step")
+            newsource = Source()
+            newsource.lines = self.lines[key.start : key.stop]
+            newsource.raw_lines = self.raw_lines[key.start : key.stop]
+            return newsource
+
+    def __iter__(self) -> Iterator[str]:
+        return iter(self.lines)
+
+    def __len__(self) -> int:
+        return len(self.lines)
+
+    def strip(self) -> Source:
+        """Return new Source object with trailing and leading blank lines removed."""
+        start, end = 0, len(self)
+        while start < end and not self.lines[start].strip():
+            start += 1
+        while end > start and not self.lines[end - 1].strip():
+            end -= 1
+        source = Source()
+        source.raw_lines = self.raw_lines
+        source.lines[:] = self.lines[start:end]
+        return source
+
+    def indent(self, indent: str = " " * 4) -> Source:
+        """Return a copy of the source object with all lines indented by the
+        given indent-string."""
+        newsource = Source()
+        newsource.raw_lines = self.raw_lines
+        newsource.lines = [(indent + line) for line in self.lines]
+        return newsource
+
+    def getstatement(self, lineno: int) -> Source:
+        """Return Source statement which contains the given linenumber
+        (counted from 0)."""
+        start, end = self.getstatementrange(lineno)
+        return self[start:end]
+
+    def getstatementrange(self, lineno: int) -> tuple[int, int]:
+        """Return (start, end) tuple which spans the minimal statement region
+        which containing the given lineno."""
+        if not (0 <= lineno < len(self)):
+            raise IndexError("lineno out of range")
+        _ast, start, end = getstatementrange_ast(lineno, self)
+        return start, end
+
+    def deindent(self) -> Source:
+        """Return a new Source object deindented."""
+        newsource = Source()
+        newsource.lines[:] = deindent(self.lines)
+        newsource.raw_lines = self.raw_lines
+        return newsource
+
+    def __str__(self) -> str:
+        return "\n".join(self.lines)
+
+
+#
+# helper functions
+#
+
+
+def findsource(obj) -> tuple[Source | None, int]:
+    try:
+        sourcelines, lineno = inspect.findsource(obj)
+    except Exception:
+        return None, -1
+    source = Source()
+    source.lines = [line.rstrip() for line in sourcelines]
+    source.raw_lines = sourcelines
+    return source, lineno
+
+
+def getrawcode(obj: object, trycall: bool = True) -> types.CodeType:
+    """Return code object for given function."""
+    try:
+        return obj.__code__  # type: ignore[attr-defined,no-any-return]
+    except AttributeError:
+        pass
+    if trycall:
+        call = getattr(obj, "__call__", None)
+        if call and not isinstance(obj, type):
+            return getrawcode(call, trycall=False)
+    raise TypeError(f"could not get code object for {obj!r}")
+
+
+def deindent(lines: Iterable[str]) -> list[str]:
+    return textwrap.dedent("\n".join(lines)).splitlines()
+
+
+def get_statement_startend2(lineno: int, node: ast.AST) -> tuple[int, int | None]:
+    # Flatten all statements and except handlers into one lineno-list.
+    # AST's line numbers start indexing at 1.
+    values: list[int] = []
+    for x in ast.walk(node):
+        if isinstance(x, ast.stmt | ast.ExceptHandler):
+            # The lineno points to the class/def, so need to include the decorators.
+            if isinstance(x, ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef):
+                for d in x.decorator_list:
+                    values.append(d.lineno - 1)
+            values.append(x.lineno - 1)
+            for name in ("finalbody", "orelse"):
+                val: list[ast.stmt] | None = getattr(x, name, None)
+                if val:
+                    # Treat the finally/orelse part as its own statement.
+                    values.append(val[0].lineno - 1 - 1)
+    values.sort()
+    insert_index = bisect_right(values, lineno)
+    start = values[insert_index - 1]
+    if insert_index >= len(values):
+        end = None
+    else:
+        end = values[insert_index]
+    return start, end
+
+
+def getstatementrange_ast(
+    lineno: int,
+    source: Source,
+    assertion: bool = False,
+    astnode: ast.AST | None = None,
+) -> tuple[ast.AST, int, int]:
+    if astnode is None:
+        content = str(source)
+        # See #4260:
+        # Don't produce duplicate warnings when compiling source to find AST.
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore")
+            astnode = ast.parse(content, "source", "exec")
+
+    start, end = get_statement_startend2(lineno, astnode)
+    # We need to correct the end:
+    # - ast-parsing strips comments
+    # - there might be empty lines
+    # - we might have lesser indented code blocks at the end
+    if end is None:
+        end = len(source.lines)
+
+    if end > start + 1:
+        # Make sure we don't span differently indented code blocks
+        # by using the BlockFinder helper used which inspect.getsource() uses itself.
+        block_finder = inspect.BlockFinder()
+        # If we start with an indented line, put blockfinder to "started" mode.
+        block_finder.started = (
+            bool(source.lines[start]) and source.lines[start][0].isspace()
+        )
+        it = ((x + "\n") for x in source.lines[start:end])
+        try:
+            for tok in tokenize.generate_tokens(lambda: next(it)):
+                block_finder.tokeneater(*tok)
+        except (inspect.EndOfBlock, IndentationError):
+            end = block_finder.last + start
+        except Exception:
+            pass
+
+    # The end might still point to a comment or empty line, correct it.
+    while end:
+        line = source.lines[end - 1].lstrip()
+        if line.startswith("#") or not line:
+            end -= 1
+        else:
+            break
+    return astnode, start, end

+ 10 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_io/__init__.py

@@ -0,0 +1,10 @@
+from __future__ import annotations
+
+from .terminalwriter import get_terminal_width
+from .terminalwriter import TerminalWriter
+
+
+__all__ = [
+    "TerminalWriter",
+    "get_terminal_width",
+]

BIN
algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/__init__.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/pprint.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/saferepr.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/terminalwriter.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_io/__pycache__/wcwidth.cpython-312.pyc


+ 673 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_io/pprint.py

@@ -0,0 +1,673 @@
+# mypy: allow-untyped-defs
+# This module was imported from the cpython standard library
+# (https://github.com/python/cpython/) at commit
+# c5140945c723ae6c4b7ee81ff720ac8ea4b52cfd (python3.12).
+#
+#
+#  Original Author:      Fred L. Drake, Jr.
+#                        fdrake@acm.org
+#
+#  This is a simple little module I wrote to make life easier.  I didn't
+#  see anything quite like it in the library, though I may have overlooked
+#  something.  I wrote this when I was trying to read some heavily nested
+#  tuples with fairly non-descriptive content.  This is modeled very much
+#  after Lisp/Scheme - style pretty-printing of lists.  If you find it
+#  useful, thank small children who sleep at night.
+from __future__ import annotations
+
+import collections as _collections
+from collections.abc import Callable
+from collections.abc import Iterator
+import dataclasses as _dataclasses
+from io import StringIO as _StringIO
+import re
+import types as _types
+from typing import Any
+from typing import IO
+
+
+class _safe_key:
+    """Helper function for key functions when sorting unorderable objects.
+
+    The wrapped-object will fallback to a Py2.x style comparison for
+    unorderable types (sorting first comparing the type name and then by
+    the obj ids).  Does not work recursively, so dict.items() must have
+    _safe_key applied to both the key and the value.
+
+    """
+
+    __slots__ = ["obj"]
+
+    def __init__(self, obj):
+        self.obj = obj
+
+    def __lt__(self, other):
+        try:
+            return self.obj < other.obj
+        except TypeError:
+            return (str(type(self.obj)), id(self.obj)) < (
+                str(type(other.obj)),
+                id(other.obj),
+            )
+
+
+def _safe_tuple(t):
+    """Helper function for comparing 2-tuples"""
+    return _safe_key(t[0]), _safe_key(t[1])
+
+
+class PrettyPrinter:
+    def __init__(
+        self,
+        indent: int = 4,
+        width: int = 80,
+        depth: int | None = None,
+    ) -> None:
+        """Handle pretty printing operations onto a stream using a set of
+        configured parameters.
+
+        indent
+            Number of spaces to indent for each level of nesting.
+
+        width
+            Attempted maximum number of columns in the output.
+
+        depth
+            The maximum depth to print out nested structures.
+
+        """
+        if indent < 0:
+            raise ValueError("indent must be >= 0")
+        if depth is not None and depth <= 0:
+            raise ValueError("depth must be > 0")
+        if not width:
+            raise ValueError("width must be != 0")
+        self._depth = depth
+        self._indent_per_level = indent
+        self._width = width
+
+    def pformat(self, object: Any) -> str:
+        sio = _StringIO()
+        self._format(object, sio, 0, 0, set(), 0)
+        return sio.getvalue()
+
+    def _format(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        objid = id(object)
+        if objid in context:
+            stream.write(_recursion(object))
+            return
+
+        p = self._dispatch.get(type(object).__repr__, None)
+        if p is not None:
+            context.add(objid)
+            p(self, object, stream, indent, allowance, context, level + 1)
+            context.remove(objid)
+        elif (
+            _dataclasses.is_dataclass(object)
+            and not isinstance(object, type)
+            and object.__dataclass_params__.repr  # type:ignore[attr-defined]
+            and
+            # Check dataclass has generated repr method.
+            hasattr(object.__repr__, "__wrapped__")
+            and "__create_fn__" in object.__repr__.__wrapped__.__qualname__
+        ):
+            context.add(objid)
+            self._pprint_dataclass(
+                object, stream, indent, allowance, context, level + 1
+            )
+            context.remove(objid)
+        else:
+            stream.write(self._repr(object, context, level))
+
+    def _pprint_dataclass(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        cls_name = object.__class__.__name__
+        items = [
+            (f.name, getattr(object, f.name))
+            for f in _dataclasses.fields(object)
+            if f.repr
+        ]
+        stream.write(cls_name + "(")
+        self._format_namespace_items(items, stream, indent, allowance, context, level)
+        stream.write(")")
+
+    _dispatch: dict[
+        Callable[..., str],
+        Callable[[PrettyPrinter, Any, IO[str], int, int, set[int], int], None],
+    ] = {}
+
+    def _pprint_dict(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        write = stream.write
+        write("{")
+        items = sorted(object.items(), key=_safe_tuple)
+        self._format_dict_items(items, stream, indent, allowance, context, level)
+        write("}")
+
+    _dispatch[dict.__repr__] = _pprint_dict
+
+    def _pprint_ordered_dict(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        if not len(object):
+            stream.write(repr(object))
+            return
+        cls = object.__class__
+        stream.write(cls.__name__ + "(")
+        self._pprint_dict(object, stream, indent, allowance, context, level)
+        stream.write(")")
+
+    _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict
+
+    def _pprint_list(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        stream.write("[")
+        self._format_items(object, stream, indent, allowance, context, level)
+        stream.write("]")
+
+    _dispatch[list.__repr__] = _pprint_list
+
+    def _pprint_tuple(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        stream.write("(")
+        self._format_items(object, stream, indent, allowance, context, level)
+        stream.write(")")
+
+    _dispatch[tuple.__repr__] = _pprint_tuple
+
+    def _pprint_set(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        if not len(object):
+            stream.write(repr(object))
+            return
+        typ = object.__class__
+        if typ is set:
+            stream.write("{")
+            endchar = "}"
+        else:
+            stream.write(typ.__name__ + "({")
+            endchar = "})"
+        object = sorted(object, key=_safe_key)
+        self._format_items(object, stream, indent, allowance, context, level)
+        stream.write(endchar)
+
+    _dispatch[set.__repr__] = _pprint_set
+    _dispatch[frozenset.__repr__] = _pprint_set
+
+    def _pprint_str(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        write = stream.write
+        if not len(object):
+            write(repr(object))
+            return
+        chunks = []
+        lines = object.splitlines(True)
+        if level == 1:
+            indent += 1
+            allowance += 1
+        max_width1 = max_width = self._width - indent
+        for i, line in enumerate(lines):
+            rep = repr(line)
+            if i == len(lines) - 1:
+                max_width1 -= allowance
+            if len(rep) <= max_width1:
+                chunks.append(rep)
+            else:
+                # A list of alternating (non-space, space) strings
+                parts = re.findall(r"\S*\s*", line)
+                assert parts
+                assert not parts[-1]
+                parts.pop()  # drop empty last part
+                max_width2 = max_width
+                current = ""
+                for j, part in enumerate(parts):
+                    candidate = current + part
+                    if j == len(parts) - 1 and i == len(lines) - 1:
+                        max_width2 -= allowance
+                    if len(repr(candidate)) > max_width2:
+                        if current:
+                            chunks.append(repr(current))
+                        current = part
+                    else:
+                        current = candidate
+                if current:
+                    chunks.append(repr(current))
+        if len(chunks) == 1:
+            write(rep)
+            return
+        if level == 1:
+            write("(")
+        for i, rep in enumerate(chunks):
+            if i > 0:
+                write("\n" + " " * indent)
+            write(rep)
+        if level == 1:
+            write(")")
+
+    _dispatch[str.__repr__] = _pprint_str
+
+    def _pprint_bytes(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        write = stream.write
+        if len(object) <= 4:
+            write(repr(object))
+            return
+        parens = level == 1
+        if parens:
+            indent += 1
+            allowance += 1
+            write("(")
+        delim = ""
+        for rep in _wrap_bytes_repr(object, self._width - indent, allowance):
+            write(delim)
+            write(rep)
+            if not delim:
+                delim = "\n" + " " * indent
+        if parens:
+            write(")")
+
+    _dispatch[bytes.__repr__] = _pprint_bytes
+
+    def _pprint_bytearray(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        write = stream.write
+        write("bytearray(")
+        self._pprint_bytes(
+            bytes(object), stream, indent + 10, allowance + 1, context, level + 1
+        )
+        write(")")
+
+    _dispatch[bytearray.__repr__] = _pprint_bytearray
+
+    def _pprint_mappingproxy(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        stream.write("mappingproxy(")
+        self._format(object.copy(), stream, indent, allowance, context, level)
+        stream.write(")")
+
+    _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy
+
+    def _pprint_simplenamespace(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        if type(object) is _types.SimpleNamespace:
+            # The SimpleNamespace repr is "namespace" instead of the class
+            # name, so we do the same here. For subclasses; use the class name.
+            cls_name = "namespace"
+        else:
+            cls_name = object.__class__.__name__
+        items = object.__dict__.items()
+        stream.write(cls_name + "(")
+        self._format_namespace_items(items, stream, indent, allowance, context, level)
+        stream.write(")")
+
+    _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace
+
+    def _format_dict_items(
+        self,
+        items: list[tuple[Any, Any]],
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        if not items:
+            return
+
+        write = stream.write
+        item_indent = indent + self._indent_per_level
+        delimnl = "\n" + " " * item_indent
+        for key, ent in items:
+            write(delimnl)
+            write(self._repr(key, context, level))
+            write(": ")
+            self._format(ent, stream, item_indent, 1, context, level)
+            write(",")
+
+        write("\n" + " " * indent)
+
+    def _format_namespace_items(
+        self,
+        items: list[tuple[Any, Any]],
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        if not items:
+            return
+
+        write = stream.write
+        item_indent = indent + self._indent_per_level
+        delimnl = "\n" + " " * item_indent
+        for key, ent in items:
+            write(delimnl)
+            write(key)
+            write("=")
+            if id(ent) in context:
+                # Special-case representation of recursion to match standard
+                # recursive dataclass repr.
+                write("...")
+            else:
+                self._format(
+                    ent,
+                    stream,
+                    item_indent + len(key) + 1,
+                    1,
+                    context,
+                    level,
+                )
+
+            write(",")
+
+        write("\n" + " " * indent)
+
+    def _format_items(
+        self,
+        items: list[Any],
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        if not items:
+            return
+
+        write = stream.write
+        item_indent = indent + self._indent_per_level
+        delimnl = "\n" + " " * item_indent
+
+        for item in items:
+            write(delimnl)
+            self._format(item, stream, item_indent, 1, context, level)
+            write(",")
+
+        write("\n" + " " * indent)
+
+    def _repr(self, object: Any, context: set[int], level: int) -> str:
+        return self._safe_repr(object, context.copy(), self._depth, level)
+
+    def _pprint_default_dict(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        rdf = self._repr(object.default_factory, context, level)
+        stream.write(f"{object.__class__.__name__}({rdf}, ")
+        self._pprint_dict(object, stream, indent, allowance, context, level)
+        stream.write(")")
+
+    _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict
+
+    def _pprint_counter(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        stream.write(object.__class__.__name__ + "(")
+
+        if object:
+            stream.write("{")
+            items = object.most_common()
+            self._format_dict_items(items, stream, indent, allowance, context, level)
+            stream.write("}")
+
+        stream.write(")")
+
+    _dispatch[_collections.Counter.__repr__] = _pprint_counter
+
+    def _pprint_chain_map(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])):
+            stream.write(repr(object))
+            return
+
+        stream.write(object.__class__.__name__ + "(")
+        self._format_items(object.maps, stream, indent, allowance, context, level)
+        stream.write(")")
+
+    _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map
+
+    def _pprint_deque(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        stream.write(object.__class__.__name__ + "(")
+        if object.maxlen is not None:
+            stream.write(f"maxlen={object.maxlen}, ")
+        stream.write("[")
+
+        self._format_items(object, stream, indent, allowance + 1, context, level)
+        stream.write("])")
+
+    _dispatch[_collections.deque.__repr__] = _pprint_deque
+
+    def _pprint_user_dict(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        self._format(object.data, stream, indent, allowance, context, level - 1)
+
+    _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict
+
+    def _pprint_user_list(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        self._format(object.data, stream, indent, allowance, context, level - 1)
+
+    _dispatch[_collections.UserList.__repr__] = _pprint_user_list
+
+    def _pprint_user_string(
+        self,
+        object: Any,
+        stream: IO[str],
+        indent: int,
+        allowance: int,
+        context: set[int],
+        level: int,
+    ) -> None:
+        self._format(object.data, stream, indent, allowance, context, level - 1)
+
+    _dispatch[_collections.UserString.__repr__] = _pprint_user_string
+
+    def _safe_repr(
+        self, object: Any, context: set[int], maxlevels: int | None, level: int
+    ) -> str:
+        typ = type(object)
+        if typ in _builtin_scalars:
+            return repr(object)
+
+        r = getattr(typ, "__repr__", None)
+
+        if issubclass(typ, dict) and r is dict.__repr__:
+            if not object:
+                return "{}"
+            objid = id(object)
+            if maxlevels and level >= maxlevels:
+                return "{...}"
+            if objid in context:
+                return _recursion(object)
+            context.add(objid)
+            components: list[str] = []
+            append = components.append
+            level += 1
+            for k, v in sorted(object.items(), key=_safe_tuple):
+                krepr = self._safe_repr(k, context, maxlevels, level)
+                vrepr = self._safe_repr(v, context, maxlevels, level)
+                append(f"{krepr}: {vrepr}")
+            context.remove(objid)
+            return "{{{}}}".format(", ".join(components))
+
+        if (issubclass(typ, list) and r is list.__repr__) or (
+            issubclass(typ, tuple) and r is tuple.__repr__
+        ):
+            if issubclass(typ, list):
+                if not object:
+                    return "[]"
+                format = "[%s]"
+            elif len(object) == 1:
+                format = "(%s,)"
+            else:
+                if not object:
+                    return "()"
+                format = "(%s)"
+            objid = id(object)
+            if maxlevels and level >= maxlevels:
+                return format % "..."
+            if objid in context:
+                return _recursion(object)
+            context.add(objid)
+            components = []
+            append = components.append
+            level += 1
+            for o in object:
+                orepr = self._safe_repr(o, context, maxlevels, level)
+                append(orepr)
+            context.remove(objid)
+            return format % ", ".join(components)
+
+        return repr(object)
+
+
+_builtin_scalars = frozenset(
+    {str, bytes, bytearray, float, complex, bool, type(None), int}
+)
+
+
+def _recursion(object: Any) -> str:
+    return f"<Recursion on {type(object).__name__} with id={id(object)}>"
+
+
+def _wrap_bytes_repr(object: Any, width: int, allowance: int) -> Iterator[str]:
+    current = b""
+    last = len(object) // 4 * 4
+    for i in range(0, len(object), 4):
+        part = object[i : i + 4]
+        candidate = current + part
+        if i == last:
+            width -= allowance
+        if len(repr(candidate)) > width:
+            if current:
+                yield repr(current)
+            current = part
+        else:
+            current = candidate
+    if current:
+        yield repr(current)

+ 130 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_io/saferepr.py

@@ -0,0 +1,130 @@
+from __future__ import annotations
+
+import pprint
+import reprlib
+
+
+def _try_repr_or_str(obj: object) -> str:
+    try:
+        return repr(obj)
+    except (KeyboardInterrupt, SystemExit):
+        raise
+    except BaseException:
+        return f'{type(obj).__name__}("{obj}")'
+
+
+def _format_repr_exception(exc: BaseException, obj: object) -> str:
+    try:
+        exc_info = _try_repr_or_str(exc)
+    except (KeyboardInterrupt, SystemExit):
+        raise
+    except BaseException as inner_exc:
+        exc_info = f"unpresentable exception ({_try_repr_or_str(inner_exc)})"
+    return (
+        f"<[{exc_info} raised in repr()] {type(obj).__name__} object at 0x{id(obj):x}>"
+    )
+
+
+def _ellipsize(s: str, maxsize: int) -> str:
+    if len(s) > maxsize:
+        i = max(0, (maxsize - 3) // 2)
+        j = max(0, maxsize - 3 - i)
+        return s[:i] + "..." + s[len(s) - j :]
+    return s
+
+
+class SafeRepr(reprlib.Repr):
+    """
+    repr.Repr that limits the resulting size of repr() and includes
+    information on exceptions raised during the call.
+    """
+
+    def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None:
+        """
+        :param maxsize:
+            If not None, will truncate the resulting repr to that specific size, using ellipsis
+            somewhere in the middle to hide the extra text.
+            If None, will not impose any size limits on the returning repr.
+        """
+        super().__init__()
+        # ``maxstring`` is used by the superclass, and needs to be an int; using a
+        # very large number in case maxsize is None, meaning we want to disable
+        # truncation.
+        self.maxstring = maxsize if maxsize is not None else 1_000_000_000
+        self.maxsize = maxsize
+        self.use_ascii = use_ascii
+
+    def repr(self, x: object) -> str:
+        try:
+            if self.use_ascii:
+                s = ascii(x)
+            else:
+                s = super().repr(x)
+        except (KeyboardInterrupt, SystemExit):
+            raise
+        except BaseException as exc:
+            s = _format_repr_exception(exc, x)
+        if self.maxsize is not None:
+            s = _ellipsize(s, self.maxsize)
+        return s
+
+    def repr_instance(self, x: object, level: int) -> str:
+        try:
+            s = repr(x)
+        except (KeyboardInterrupt, SystemExit):
+            raise
+        except BaseException as exc:
+            s = _format_repr_exception(exc, x)
+        if self.maxsize is not None:
+            s = _ellipsize(s, self.maxsize)
+        return s
+
+
+def safeformat(obj: object) -> str:
+    """Return a pretty printed string for the given object.
+
+    Failing __repr__ functions of user instances will be represented
+    with a short exception info.
+    """
+    try:
+        return pprint.pformat(obj)
+    except Exception as exc:
+        return _format_repr_exception(exc, obj)
+
+
+# Maximum size of overall repr of objects to display during assertion errors.
+DEFAULT_REPR_MAX_SIZE = 240
+
+
+def saferepr(
+    obj: object, maxsize: int | None = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False
+) -> str:
+    """Return a size-limited safe repr-string for the given object.
+
+    Failing __repr__ functions of user instances will be represented
+    with a short exception info and 'saferepr' generally takes
+    care to never raise exceptions itself.
+
+    This function is a wrapper around the Repr/reprlib functionality of the
+    stdlib.
+    """
+    return SafeRepr(maxsize, use_ascii).repr(obj)
+
+
+def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str:
+    """Return an unlimited-size safe repr-string for the given object.
+
+    As with saferepr, failing __repr__ functions of user instances
+    will be represented with a short exception info.
+
+    This function is a wrapper around simple repr.
+
+    Note: a cleaner solution would be to alter ``saferepr``this way
+    when maxsize=None, but that might affect some other code.
+    """
+    try:
+        if use_ascii:
+            return ascii(obj)
+        return repr(obj)
+    except Exception as exc:
+        return _format_repr_exception(exc, obj)

+ 258 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_io/terminalwriter.py

@@ -0,0 +1,258 @@
+"""Helper functions for writing to terminals and files."""
+
+from __future__ import annotations
+
+from collections.abc import Sequence
+import os
+import shutil
+import sys
+from typing import final
+from typing import Literal
+from typing import TextIO
+
+import pygments
+from pygments.formatters.terminal import TerminalFormatter
+from pygments.lexer import Lexer
+from pygments.lexers.diff import DiffLexer
+from pygments.lexers.python import PythonLexer
+
+from ..compat import assert_never
+from .wcwidth import wcswidth
+
+
+# This code was initially copied from py 1.8.1, file _io/terminalwriter.py.
+
+
+def get_terminal_width() -> int:
+    width, _ = shutil.get_terminal_size(fallback=(80, 24))
+
+    # The Windows get_terminal_size may be bogus, let's sanify a bit.
+    if width < 40:
+        width = 80
+
+    return width
+
+
+def should_do_markup(file: TextIO) -> bool:
+    if os.environ.get("PY_COLORS") == "1":
+        return True
+    if os.environ.get("PY_COLORS") == "0":
+        return False
+    if os.environ.get("NO_COLOR"):
+        return False
+    if os.environ.get("FORCE_COLOR"):
+        return True
+    return (
+        hasattr(file, "isatty") and file.isatty() and os.environ.get("TERM") != "dumb"
+    )
+
+
+@final
+class TerminalWriter:
+    _esctable = dict(
+        black=30,
+        red=31,
+        green=32,
+        yellow=33,
+        blue=34,
+        purple=35,
+        cyan=36,
+        white=37,
+        Black=40,
+        Red=41,
+        Green=42,
+        Yellow=43,
+        Blue=44,
+        Purple=45,
+        Cyan=46,
+        White=47,
+        bold=1,
+        light=2,
+        blink=5,
+        invert=7,
+    )
+
+    def __init__(self, file: TextIO | None = None) -> None:
+        if file is None:
+            file = sys.stdout
+        if hasattr(file, "isatty") and file.isatty() and sys.platform == "win32":
+            try:
+                import colorama
+            except ImportError:
+                pass
+            else:
+                file = colorama.AnsiToWin32(file).stream
+                assert file is not None
+        self._file = file
+        self.hasmarkup = should_do_markup(file)
+        self._current_line = ""
+        self._terminal_width: int | None = None
+        self.code_highlight = True
+
+    @property
+    def fullwidth(self) -> int:
+        if self._terminal_width is not None:
+            return self._terminal_width
+        return get_terminal_width()
+
+    @fullwidth.setter
+    def fullwidth(self, value: int) -> None:
+        self._terminal_width = value
+
+    @property
+    def width_of_current_line(self) -> int:
+        """Return an estimate of the width so far in the current line."""
+        return wcswidth(self._current_line)
+
+    def markup(self, text: str, **markup: bool) -> str:
+        for name in markup:
+            if name not in self._esctable:
+                raise ValueError(f"unknown markup: {name!r}")
+        if self.hasmarkup:
+            esc = [self._esctable[name] for name, on in markup.items() if on]
+            if esc:
+                text = "".join(f"\x1b[{cod}m" for cod in esc) + text + "\x1b[0m"
+        return text
+
+    def sep(
+        self,
+        sepchar: str,
+        title: str | None = None,
+        fullwidth: int | None = None,
+        **markup: bool,
+    ) -> None:
+        if fullwidth is None:
+            fullwidth = self.fullwidth
+        # The goal is to have the line be as long as possible
+        # under the condition that len(line) <= fullwidth.
+        if sys.platform == "win32":
+            # If we print in the last column on windows we are on a
+            # new line but there is no way to verify/neutralize this
+            # (we may not know the exact line width).
+            # So let's be defensive to avoid empty lines in the output.
+            fullwidth -= 1
+        if title is not None:
+            # we want 2 + 2*len(fill) + len(title) <= fullwidth
+            # i.e.    2 + 2*len(sepchar)*N + len(title) <= fullwidth
+            #         2*len(sepchar)*N <= fullwidth - len(title) - 2
+            #         N <= (fullwidth - len(title) - 2) // (2*len(sepchar))
+            N = max((fullwidth - len(title) - 2) // (2 * len(sepchar)), 1)
+            fill = sepchar * N
+            line = f"{fill} {title} {fill}"
+        else:
+            # we want len(sepchar)*N <= fullwidth
+            # i.e.    N <= fullwidth // len(sepchar)
+            line = sepchar * (fullwidth // len(sepchar))
+        # In some situations there is room for an extra sepchar at the right,
+        # in particular if we consider that with a sepchar like "_ " the
+        # trailing space is not important at the end of the line.
+        if len(line) + len(sepchar.rstrip()) <= fullwidth:
+            line += sepchar.rstrip()
+
+        self.line(line, **markup)
+
+    def write(self, msg: str, *, flush: bool = False, **markup: bool) -> None:
+        if msg:
+            current_line = msg.rsplit("\n", 1)[-1]
+            if "\n" in msg:
+                self._current_line = current_line
+            else:
+                self._current_line += current_line
+
+            msg = self.markup(msg, **markup)
+
+            self.write_raw(msg, flush=flush)
+
+    def write_raw(self, msg: str, *, flush: bool = False) -> None:
+        try:
+            self._file.write(msg)
+        except UnicodeEncodeError:
+            # Some environments don't support printing general Unicode
+            # strings, due to misconfiguration or otherwise; in that case,
+            # print the string escaped to ASCII.
+            # When the Unicode situation improves we should consider
+            # letting the error propagate instead of masking it (see #7475
+            # for one brief attempt).
+            msg = msg.encode("unicode-escape").decode("ascii")
+            self._file.write(msg)
+
+        if flush:
+            self.flush()
+
+    def line(self, s: str = "", **markup: bool) -> None:
+        self.write(s, **markup)
+        self.write("\n")
+
+    def flush(self) -> None:
+        self._file.flush()
+
+    def _write_source(self, lines: Sequence[str], indents: Sequence[str] = ()) -> None:
+        """Write lines of source code possibly highlighted.
+
+        Keeping this private for now because the API is clunky. We should discuss how
+        to evolve the terminal writer so we can have more precise color support, for example
+        being able to write part of a line in one color and the rest in another, and so on.
+        """
+        if indents and len(indents) != len(lines):
+            raise ValueError(
+                f"indents size ({len(indents)}) should have same size as lines ({len(lines)})"
+            )
+        if not indents:
+            indents = [""] * len(lines)
+        source = "\n".join(lines)
+        new_lines = self._highlight(source).splitlines()
+        # Would be better to strict=True but that fails some CI jobs.
+        for indent, new_line in zip(indents, new_lines, strict=False):
+            self.line(indent + new_line)
+
+    def _get_pygments_lexer(self, lexer: Literal["python", "diff"]) -> Lexer:
+        if lexer == "python":
+            return PythonLexer()
+        elif lexer == "diff":
+            return DiffLexer()
+        else:
+            assert_never(lexer)
+
+    def _get_pygments_formatter(self) -> TerminalFormatter:
+        from _pytest.config.exceptions import UsageError
+
+        theme = os.getenv("PYTEST_THEME")
+        theme_mode = os.getenv("PYTEST_THEME_MODE", "dark")
+
+        try:
+            return TerminalFormatter(bg=theme_mode, style=theme)
+        except pygments.util.ClassNotFound as e:
+            raise UsageError(
+                f"PYTEST_THEME environment variable has an invalid value: '{theme}'. "
+                "Hint: See available pygments styles with `pygmentize -L styles`."
+            ) from e
+        except pygments.util.OptionError as e:
+            raise UsageError(
+                f"PYTEST_THEME_MODE environment variable has an invalid value: '{theme_mode}'. "
+                "The allowed values are 'dark' (default) and 'light'."
+            ) from e
+
+    def _highlight(
+        self, source: str, lexer: Literal["diff", "python"] = "python"
+    ) -> str:
+        """Highlight the given source if we have markup support."""
+        if not source or not self.hasmarkup or not self.code_highlight:
+            return source
+
+        pygments_lexer = self._get_pygments_lexer(lexer)
+        pygments_formatter = self._get_pygments_formatter()
+
+        highlighted: str = pygments.highlight(
+            source, pygments_lexer, pygments_formatter
+        )
+        # pygments terminal formatter may add a newline when there wasn't one.
+        # We don't want this, remove.
+        if highlighted[-1] == "\n" and source[-1] != "\n":
+            highlighted = highlighted[:-1]
+
+        # Some lexers will not set the initial color explicitly
+        # which may lead to the previous color being propagated to the
+        # start of the expression, so reset first.
+        highlighted = "\x1b[0m" + highlighted
+
+        return highlighted

+ 57 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_io/wcwidth.py

@@ -0,0 +1,57 @@
+from __future__ import annotations
+
+from functools import lru_cache
+import unicodedata
+
+
+@lru_cache(100)
+def wcwidth(c: str) -> int:
+    """Determine how many columns are needed to display a character in a terminal.
+
+    Returns -1 if the character is not printable.
+    Returns 0, 1 or 2 for other characters.
+    """
+    o = ord(c)
+
+    # ASCII fast path.
+    if 0x20 <= o < 0x07F:
+        return 1
+
+    # Some Cf/Zp/Zl characters which should be zero-width.
+    if (
+        o == 0x0000
+        or 0x200B <= o <= 0x200F
+        or 0x2028 <= o <= 0x202E
+        or 0x2060 <= o <= 0x2063
+    ):
+        return 0
+
+    category = unicodedata.category(c)
+
+    # Control characters.
+    if category == "Cc":
+        return -1
+
+    # Combining characters with zero width.
+    if category in ("Me", "Mn"):
+        return 0
+
+    # Full/Wide east asian characters.
+    if unicodedata.east_asian_width(c) in ("F", "W"):
+        return 2
+
+    return 1
+
+
+def wcswidth(s: str) -> int:
+    """Determine how many columns are needed to display a string in a terminal.
+
+    Returns -1 if the string contains non-printable characters.
+    """
+    width = 0
+    for c in unicodedata.normalize("NFC", s):
+        wc = wcwidth(c)
+        if wc < 0:
+            return -1
+        width += wc
+    return width

+ 0 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_py/__init__.py


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_py/__pycache__/__init__.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_py/__pycache__/error.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/_py/__pycache__/path.cpython-312.pyc


+ 119 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_py/error.py

@@ -0,0 +1,119 @@
+"""create errno-specific classes for IO or os calls."""
+
+from __future__ import annotations
+
+from collections.abc import Callable
+import errno
+import os
+import sys
+from typing import TYPE_CHECKING
+from typing import TypeVar
+
+
+if TYPE_CHECKING:
+    from typing_extensions import ParamSpec
+
+    P = ParamSpec("P")
+
+R = TypeVar("R")
+
+
+class Error(EnvironmentError):
+    def __repr__(self) -> str:
+        return "{}.{} {!r}: {} ".format(
+            self.__class__.__module__,
+            self.__class__.__name__,
+            self.__class__.__doc__,
+            " ".join(map(str, self.args)),
+            # repr(self.args)
+        )
+
+    def __str__(self) -> str:
+        s = "[{}]: {}".format(
+            self.__class__.__doc__,
+            " ".join(map(str, self.args)),
+        )
+        return s
+
+
+_winerrnomap = {
+    2: errno.ENOENT,
+    3: errno.ENOENT,
+    17: errno.EEXIST,
+    18: errno.EXDEV,
+    13: errno.EBUSY,  # empty cd drive, but ENOMEDIUM seems unavailable
+    22: errno.ENOTDIR,
+    20: errno.ENOTDIR,
+    267: errno.ENOTDIR,
+    5: errno.EACCES,  # anything better?
+}
+
+
+class ErrorMaker:
+    """lazily provides Exception classes for each possible POSIX errno
+    (as defined per the 'errno' module).  All such instances
+    subclass EnvironmentError.
+    """
+
+    _errno2class: dict[int, type[Error]] = {}
+
+    def __getattr__(self, name: str) -> type[Error]:
+        if name[0] == "_":
+            raise AttributeError(name)
+        eno = getattr(errno, name)
+        cls = self._geterrnoclass(eno)
+        setattr(self, name, cls)
+        return cls
+
+    def _geterrnoclass(self, eno: int) -> type[Error]:
+        try:
+            return self._errno2class[eno]
+        except KeyError:
+            clsname = errno.errorcode.get(eno, f"UnknownErrno{eno}")
+            errorcls = type(
+                clsname,
+                (Error,),
+                {"__module__": "py.error", "__doc__": os.strerror(eno)},
+            )
+            self._errno2class[eno] = errorcls
+            return errorcls
+
+    def checked_call(
+        self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs
+    ) -> R:
+        """Call a function and raise an errno-exception if applicable."""
+        __tracebackhide__ = True
+        try:
+            return func(*args, **kwargs)
+        except Error:
+            raise
+        except OSError as value:
+            if not hasattr(value, "errno"):
+                raise
+            if sys.platform == "win32":
+                try:
+                    # error: Invalid index type "Optional[int]" for "dict[int, int]"; expected type "int"  [index]
+                    # OK to ignore because we catch the KeyError below.
+                    cls = self._geterrnoclass(_winerrnomap[value.errno])  # type:ignore[index]
+                except KeyError:
+                    raise value
+            else:
+                # we are not on Windows, or we got a proper OSError
+                if value.errno is None:
+                    cls = type(
+                        "UnknownErrnoNone",
+                        (Error,),
+                        {"__module__": "py.error", "__doc__": None},
+                    )
+                else:
+                    cls = self._geterrnoclass(value.errno)
+
+            raise cls(f"{func.__name__}{args!r}")
+
+
+_error_maker = ErrorMaker()
+checked_call = _error_maker.checked_call
+
+
+def __getattr__(attr: str) -> type[Error]:
+    return getattr(_error_maker, attr)  # type: ignore[no-any-return]

+ 1475 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_py/path.py

@@ -0,0 +1,1475 @@
+# mypy: allow-untyped-defs
+"""local path implementation."""
+
+from __future__ import annotations
+
+import atexit
+from collections.abc import Callable
+from contextlib import contextmanager
+import fnmatch
+import importlib.util
+import io
+import os
+from os.path import abspath
+from os.path import dirname
+from os.path import exists
+from os.path import isabs
+from os.path import isdir
+from os.path import isfile
+from os.path import islink
+from os.path import normpath
+import posixpath
+from stat import S_ISDIR
+from stat import S_ISLNK
+from stat import S_ISREG
+import sys
+from typing import Any
+from typing import cast
+from typing import Literal
+from typing import overload
+from typing import TYPE_CHECKING
+import uuid
+import warnings
+
+from . import error
+
+
+# Moved from local.py.
+iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt")
+
+
+class Checkers:
+    _depend_on_existence = "exists", "link", "dir", "file"
+
+    def __init__(self, path):
+        self.path = path
+
+    def dotfile(self):
+        return self.path.basename.startswith(".")
+
+    def ext(self, arg):
+        if not arg.startswith("."):
+            arg = "." + arg
+        return self.path.ext == arg
+
+    def basename(self, arg):
+        return self.path.basename == arg
+
+    def basestarts(self, arg):
+        return self.path.basename.startswith(arg)
+
+    def relto(self, arg):
+        return self.path.relto(arg)
+
+    def fnmatch(self, arg):
+        return self.path.fnmatch(arg)
+
+    def endswith(self, arg):
+        return str(self.path).endswith(arg)
+
+    def _evaluate(self, kw):
+        from .._code.source import getrawcode
+
+        for name, value in kw.items():
+            invert = False
+            meth = None
+            try:
+                meth = getattr(self, name)
+            except AttributeError:
+                if name[:3] == "not":
+                    invert = True
+                    try:
+                        meth = getattr(self, name[3:])
+                    except AttributeError:
+                        pass
+            if meth is None:
+                raise TypeError(f"no {name!r} checker available for {self.path!r}")
+            try:
+                if getrawcode(meth).co_argcount > 1:
+                    if (not meth(value)) ^ invert:
+                        return False
+                else:
+                    if bool(value) ^ bool(meth()) ^ invert:
+                        return False
+            except (error.ENOENT, error.ENOTDIR, error.EBUSY):
+                # EBUSY feels not entirely correct,
+                # but its kind of necessary since ENOMEDIUM
+                # is not accessible in python
+                for name in self._depend_on_existence:
+                    if name in kw:
+                        if kw.get(name):
+                            return False
+                    name = "not" + name
+                    if name in kw:
+                        if not kw.get(name):
+                            return False
+        return True
+
+    _statcache: Stat
+
+    def _stat(self) -> Stat:
+        try:
+            return self._statcache
+        except AttributeError:
+            try:
+                self._statcache = self.path.stat()
+            except error.ELOOP:
+                self._statcache = self.path.lstat()
+            return self._statcache
+
+    def dir(self):
+        return S_ISDIR(self._stat().mode)
+
+    def file(self):
+        return S_ISREG(self._stat().mode)
+
+    def exists(self):
+        return self._stat()
+
+    def link(self):
+        st = self.path.lstat()
+        return S_ISLNK(st.mode)
+
+
+class NeverRaised(Exception):
+    pass
+
+
+class Visitor:
+    def __init__(self, fil, rec, ignore, bf, sort):
+        if isinstance(fil, str):
+            fil = FNMatcher(fil)
+        if isinstance(rec, str):
+            self.rec: Callable[[LocalPath], bool] = FNMatcher(rec)
+        elif not hasattr(rec, "__call__") and rec:
+            self.rec = lambda path: True
+        else:
+            self.rec = rec
+        self.fil = fil
+        self.ignore = ignore
+        self.breadthfirst = bf
+        self.optsort = cast(Callable[[Any], Any], sorted) if sort else (lambda x: x)
+
+    def gen(self, path):
+        try:
+            entries = path.listdir()
+        except self.ignore:
+            return
+        rec = self.rec
+        dirs = self.optsort(
+            [p for p in entries if p.check(dir=1) and (rec is None or rec(p))]
+        )
+        if not self.breadthfirst:
+            for subdir in dirs:
+                yield from self.gen(subdir)
+        for p in self.optsort(entries):
+            if self.fil is None or self.fil(p):
+                yield p
+        if self.breadthfirst:
+            for subdir in dirs:
+                yield from self.gen(subdir)
+
+
+class FNMatcher:
+    def __init__(self, pattern):
+        self.pattern = pattern
+
+    def __call__(self, path):
+        pattern = self.pattern
+
+        if (
+            pattern.find(path.sep) == -1
+            and iswin32
+            and pattern.find(posixpath.sep) != -1
+        ):
+            # Running on Windows, the pattern has no Windows path separators,
+            # and the pattern has one or more Posix path separators. Replace
+            # the Posix path separators with the Windows path separator.
+            pattern = pattern.replace(posixpath.sep, path.sep)
+
+        if pattern.find(path.sep) == -1:
+            name = path.basename
+        else:
+            name = str(path)  # path.strpath # XXX svn?
+            if not os.path.isabs(pattern):
+                pattern = "*" + path.sep + pattern
+        return fnmatch.fnmatch(name, pattern)
+
+
+def map_as_list(func, iter):
+    return list(map(func, iter))
+
+
+class Stat:
+    if TYPE_CHECKING:
+
+        @property
+        def size(self) -> int: ...
+
+        @property
+        def mtime(self) -> float: ...
+
+    def __getattr__(self, name: str) -> Any:
+        return getattr(self._osstatresult, "st_" + name)
+
+    def __init__(self, path, osstatresult):
+        self.path = path
+        self._osstatresult = osstatresult
+
+    @property
+    def owner(self):
+        if iswin32:
+            raise NotImplementedError("XXX win32")
+        import pwd
+
+        entry = error.checked_call(pwd.getpwuid, self.uid)  # type:ignore[attr-defined,unused-ignore]
+        return entry[0]
+
+    @property
+    def group(self):
+        """Return group name of file."""
+        if iswin32:
+            raise NotImplementedError("XXX win32")
+        import grp
+
+        entry = error.checked_call(grp.getgrgid, self.gid)  # type:ignore[attr-defined,unused-ignore]
+        return entry[0]
+
+    def isdir(self):
+        return S_ISDIR(self._osstatresult.st_mode)
+
+    def isfile(self):
+        return S_ISREG(self._osstatresult.st_mode)
+
+    def islink(self):
+        self.path.lstat()
+        return S_ISLNK(self._osstatresult.st_mode)
+
+
+def getuserid(user):
+    import pwd
+
+    if not isinstance(user, int):
+        user = pwd.getpwnam(user)[2]  # type:ignore[attr-defined,unused-ignore]
+    return user
+
+
+def getgroupid(group):
+    import grp
+
+    if not isinstance(group, int):
+        group = grp.getgrnam(group)[2]  # type:ignore[attr-defined,unused-ignore]
+    return group
+
+
+class LocalPath:
+    """Object oriented interface to os.path and other local filesystem
+    related information.
+    """
+
+    class ImportMismatchError(ImportError):
+        """raised on pyimport() if there is a mismatch of __file__'s"""
+
+    sep = os.sep
+
+    def __init__(self, path=None, expanduser=False):
+        """Initialize and return a local Path instance.
+
+        Path can be relative to the current directory.
+        If path is None it defaults to the current working directory.
+        If expanduser is True, tilde-expansion is performed.
+        Note that Path instances always carry an absolute path.
+        Note also that passing in a local path object will simply return
+        the exact same path object. Use new() to get a new copy.
+        """
+        if path is None:
+            self.strpath = error.checked_call(os.getcwd)
+        else:
+            try:
+                path = os.fspath(path)
+            except TypeError:
+                raise ValueError(
+                    "can only pass None, Path instances "
+                    "or non-empty strings to LocalPath"
+                )
+            if expanduser:
+                path = os.path.expanduser(path)
+            self.strpath = abspath(path)
+
+    if sys.platform != "win32":
+
+        def chown(self, user, group, rec=0):
+            """Change ownership to the given user and group.
+            user and group may be specified by a number or
+            by a name.  if rec is True change ownership
+            recursively.
+            """
+            uid = getuserid(user)
+            gid = getgroupid(group)
+            if rec:
+                for x in self.visit(rec=lambda x: x.check(link=0)):
+                    if x.check(link=0):
+                        error.checked_call(os.chown, str(x), uid, gid)
+            error.checked_call(os.chown, str(self), uid, gid)
+
+        def readlink(self) -> str:
+            """Return value of a symbolic link."""
+            # https://github.com/python/mypy/issues/12278
+            return error.checked_call(os.readlink, self.strpath)  # type: ignore[arg-type,return-value,unused-ignore]
+
+        def mklinkto(self, oldname):
+            """Posix style hard link to another name."""
+            error.checked_call(os.link, str(oldname), str(self))
+
+        def mksymlinkto(self, value, absolute=1):
+            """Create a symbolic link with the given value (pointing to another name)."""
+            if absolute:
+                error.checked_call(os.symlink, str(value), self.strpath)
+            else:
+                base = self.common(value)
+                # with posix local paths '/' is always a common base
+                relsource = self.__class__(value).relto(base)
+                reldest = self.relto(base)
+                n = reldest.count(self.sep)
+                target = self.sep.join(("..",) * n + (relsource,))
+                error.checked_call(os.symlink, target, self.strpath)
+
+    def __div__(self, other):
+        return self.join(os.fspath(other))
+
+    __truediv__ = __div__  # py3k
+
+    @property
+    def basename(self):
+        """Basename part of path."""
+        return self._getbyspec("basename")[0]
+
+    @property
+    def dirname(self):
+        """Dirname part of path."""
+        return self._getbyspec("dirname")[0]
+
+    @property
+    def purebasename(self):
+        """Pure base name of the path."""
+        return self._getbyspec("purebasename")[0]
+
+    @property
+    def ext(self):
+        """Extension of the path (including the '.')."""
+        return self._getbyspec("ext")[0]
+
+    def read_binary(self):
+        """Read and return a bytestring from reading the path."""
+        with self.open("rb") as f:
+            return f.read()
+
+    def read_text(self, encoding):
+        """Read and return a Unicode string from reading the path."""
+        with self.open("r", encoding=encoding) as f:
+            return f.read()
+
+    def read(self, mode="r"):
+        """Read and return a bytestring from reading the path."""
+        with self.open(mode) as f:
+            return f.read()
+
+    def readlines(self, cr=1):
+        """Read and return a list of lines from the path. if cr is False, the
+        newline will be removed from the end of each line."""
+        mode = "r"
+
+        if not cr:
+            content = self.read(mode)
+            return content.split("\n")
+        else:
+            f = self.open(mode)
+            try:
+                return f.readlines()
+            finally:
+                f.close()
+
+    def load(self):
+        """(deprecated) return object unpickled from self.read()"""
+        f = self.open("rb")
+        try:
+            import pickle
+
+            return error.checked_call(pickle.load, f)
+        finally:
+            f.close()
+
+    def move(self, target):
+        """Move this path to target."""
+        if target.relto(self):
+            raise error.EINVAL(target, "cannot move path into a subdirectory of itself")
+        try:
+            self.rename(target)
+        except error.EXDEV:  # invalid cross-device link
+            self.copy(target)
+            self.remove()
+
+    def fnmatch(self, pattern):
+        """Return true if the basename/fullname matches the glob-'pattern'.
+
+        valid pattern characters::
+
+            *       matches everything
+            ?       matches any single character
+            [seq]   matches any character in seq
+            [!seq]  matches any char not in seq
+
+        If the pattern contains a path-separator then the full path
+        is used for pattern matching and a '*' is prepended to the
+        pattern.
+
+        if the pattern doesn't contain a path-separator the pattern
+        is only matched against the basename.
+        """
+        return FNMatcher(pattern)(self)
+
+    def relto(self, relpath):
+        """Return a string which is the relative part of the path
+        to the given 'relpath'.
+        """
+        if not isinstance(relpath, str | LocalPath):
+            raise TypeError(f"{relpath!r}: not a string or path object")
+        strrelpath = str(relpath)
+        if strrelpath and strrelpath[-1] != self.sep:
+            strrelpath += self.sep
+        # assert strrelpath[-1] == self.sep
+        # assert strrelpath[-2] != self.sep
+        strself = self.strpath
+        if sys.platform == "win32" or getattr(os, "_name", None) == "nt":
+            if os.path.normcase(strself).startswith(os.path.normcase(strrelpath)):
+                return strself[len(strrelpath) :]
+        elif strself.startswith(strrelpath):
+            return strself[len(strrelpath) :]
+        return ""
+
+    def ensure_dir(self, *args):
+        """Ensure the path joined with args is a directory."""
+        return self.ensure(*args, dir=True)
+
+    def bestrelpath(self, dest):
+        """Return a string which is a relative path from self
+        (assumed to be a directory) to dest such that
+        self.join(bestrelpath) == dest and if not such
+        path can be determined return dest.
+        """
+        try:
+            if self == dest:
+                return os.curdir
+            base = self.common(dest)
+            if not base:  # can be the case on windows
+                return str(dest)
+            self2base = self.relto(base)
+            reldest = dest.relto(base)
+            if self2base:
+                n = self2base.count(self.sep) + 1
+            else:
+                n = 0
+            lst = [os.pardir] * n
+            if reldest:
+                lst.append(reldest)
+            target = dest.sep.join(lst)
+            return target
+        except AttributeError:
+            return str(dest)
+
+    def exists(self):
+        return self.check()
+
+    def isdir(self):
+        return self.check(dir=1)
+
+    def isfile(self):
+        return self.check(file=1)
+
+    def parts(self, reverse=False):
+        """Return a root-first list of all ancestor directories
+        plus the path itself.
+        """
+        current = self
+        lst = [self]
+        while 1:
+            last = current
+            current = current.dirpath()
+            if last == current:
+                break
+            lst.append(current)
+        if not reverse:
+            lst.reverse()
+        return lst
+
+    def common(self, other):
+        """Return the common part shared with the other path
+        or None if there is no common part.
+        """
+        last = None
+        for x, y in zip(self.parts(), other.parts()):
+            if x != y:
+                return last
+            last = x
+        return last
+
+    def __add__(self, other):
+        """Return new path object with 'other' added to the basename"""
+        return self.new(basename=self.basename + str(other))
+
+    def visit(self, fil=None, rec=None, ignore=NeverRaised, bf=False, sort=False):
+        """Yields all paths below the current one
+
+        fil is a filter (glob pattern or callable), if not matching the
+        path will not be yielded, defaulting to None (everything is
+        returned)
+
+        rec is a filter (glob pattern or callable) that controls whether
+        a node is descended, defaulting to None
+
+        ignore is an Exception class that is ignoredwhen calling dirlist()
+        on any of the paths (by default, all exceptions are reported)
+
+        bf if True will cause a breadthfirst search instead of the
+        default depthfirst. Default: False
+
+        sort if True will sort entries within each directory level.
+        """
+        yield from Visitor(fil, rec, ignore, bf, sort).gen(self)
+
+    def _sortlist(self, res, sort):
+        if sort:
+            if hasattr(sort, "__call__"):
+                warnings.warn(
+                    DeprecationWarning(
+                        "listdir(sort=callable) is deprecated and breaks on python3"
+                    ),
+                    stacklevel=3,
+                )
+                res.sort(sort)
+            else:
+                res.sort()
+
+    def __fspath__(self):
+        return self.strpath
+
+    def __hash__(self):
+        s = self.strpath
+        if iswin32:
+            s = s.lower()
+        return hash(s)
+
+    def __eq__(self, other):
+        s1 = os.fspath(self)
+        try:
+            s2 = os.fspath(other)
+        except TypeError:
+            return False
+        if iswin32:
+            s1 = s1.lower()
+            try:
+                s2 = s2.lower()
+            except AttributeError:
+                return False
+        return s1 == s2
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def __lt__(self, other):
+        return os.fspath(self) < os.fspath(other)
+
+    def __gt__(self, other):
+        return os.fspath(self) > os.fspath(other)
+
+    def samefile(self, other):
+        """Return True if 'other' references the same file as 'self'."""
+        other = os.fspath(other)
+        if not isabs(other):
+            other = abspath(other)
+        if self == other:
+            return True
+        if not hasattr(os.path, "samefile"):
+            return False
+        return error.checked_call(os.path.samefile, self.strpath, other)
+
+    def remove(self, rec=1, ignore_errors=False):
+        """Remove a file or directory (or a directory tree if rec=1).
+        if ignore_errors is True, errors while removing directories will
+        be ignored.
+        """
+        if self.check(dir=1, link=0):
+            if rec:
+                # force remove of readonly files on windows
+                if iswin32:
+                    self.chmod(0o700, rec=1)
+                import shutil
+
+                error.checked_call(
+                    shutil.rmtree, self.strpath, ignore_errors=ignore_errors
+                )
+            else:
+                error.checked_call(os.rmdir, self.strpath)
+        else:
+            if iswin32:
+                self.chmod(0o700)
+            error.checked_call(os.remove, self.strpath)
+
+    def computehash(self, hashtype="md5", chunksize=524288):
+        """Return hexdigest of hashvalue for this file."""
+        try:
+            try:
+                import hashlib as mod
+            except ImportError:
+                if hashtype == "sha1":
+                    hashtype = "sha"
+                mod = __import__(hashtype)
+            hash = getattr(mod, hashtype)()
+        except (AttributeError, ImportError):
+            raise ValueError(f"Don't know how to compute {hashtype!r} hash")
+        f = self.open("rb")
+        try:
+            while 1:
+                buf = f.read(chunksize)
+                if not buf:
+                    return hash.hexdigest()
+                hash.update(buf)
+        finally:
+            f.close()
+
+    def new(self, **kw):
+        """Create a modified version of this path.
+        the following keyword arguments modify various path parts::
+
+          a:/some/path/to/a/file.ext
+          xx                           drive
+          xxxxxxxxxxxxxxxxx            dirname
+                            xxxxxxxx   basename
+                            xxxx       purebasename
+                                 xxx   ext
+        """
+        obj = object.__new__(self.__class__)
+        if not kw:
+            obj.strpath = self.strpath
+            return obj
+        drive, dirname, _basename, purebasename, ext = self._getbyspec(
+            "drive,dirname,basename,purebasename,ext"
+        )
+        if "basename" in kw:
+            if "purebasename" in kw or "ext" in kw:
+                raise ValueError(f"invalid specification {kw!r}")
+        else:
+            pb = kw.setdefault("purebasename", purebasename)
+            try:
+                ext = kw["ext"]
+            except KeyError:
+                pass
+            else:
+                if ext and not ext.startswith("."):
+                    ext = "." + ext
+            kw["basename"] = pb + ext
+
+        if "dirname" in kw and not kw["dirname"]:
+            kw["dirname"] = drive
+        else:
+            kw.setdefault("dirname", dirname)
+        kw.setdefault("sep", self.sep)
+        obj.strpath = normpath("{dirname}{sep}{basename}".format(**kw))
+        return obj
+
+    def _getbyspec(self, spec: str) -> list[str]:
+        """See new for what 'spec' can be."""
+        res = []
+        parts = self.strpath.split(self.sep)
+
+        args = filter(None, spec.split(","))
+        for name in args:
+            if name == "drive":
+                res.append(parts[0])
+            elif name == "dirname":
+                res.append(self.sep.join(parts[:-1]))
+            else:
+                basename = parts[-1]
+                if name == "basename":
+                    res.append(basename)
+                else:
+                    i = basename.rfind(".")
+                    if i == -1:
+                        purebasename, ext = basename, ""
+                    else:
+                        purebasename, ext = basename[:i], basename[i:]
+                    if name == "purebasename":
+                        res.append(purebasename)
+                    elif name == "ext":
+                        res.append(ext)
+                    else:
+                        raise ValueError(f"invalid part specification {name!r}")
+        return res
+
+    def dirpath(self, *args, **kwargs):
+        """Return the directory path joined with any given path arguments."""
+        if not kwargs:
+            path = object.__new__(self.__class__)
+            path.strpath = dirname(self.strpath)
+            if args:
+                path = path.join(*args)
+            return path
+        return self.new(basename="").join(*args, **kwargs)
+
+    def join(self, *args: os.PathLike[str], abs: bool = False) -> LocalPath:
+        """Return a new path by appending all 'args' as path
+        components.  if abs=1 is used restart from root if any
+        of the args is an absolute path.
+        """
+        sep = self.sep
+        strargs = [os.fspath(arg) for arg in args]
+        strpath = self.strpath
+        if abs:
+            newargs: list[str] = []
+            for arg in reversed(strargs):
+                if isabs(arg):
+                    strpath = arg
+                    strargs = newargs
+                    break
+                newargs.insert(0, arg)
+        # special case for when we have e.g. strpath == "/"
+        actual_sep = "" if strpath.endswith(sep) else sep
+        for arg in strargs:
+            arg = arg.strip(sep)
+            if iswin32:
+                # allow unix style paths even on windows.
+                arg = arg.strip("/")
+                arg = arg.replace("/", sep)
+            strpath = strpath + actual_sep + arg
+            actual_sep = sep
+        obj = object.__new__(self.__class__)
+        obj.strpath = normpath(strpath)
+        return obj
+
+    def open(self, mode="r", ensure=False, encoding=None):
+        """Return an opened file with the given mode.
+
+        If ensure is True, create parent directories if needed.
+        """
+        if ensure:
+            self.dirpath().ensure(dir=1)
+        if encoding:
+            return error.checked_call(
+                io.open,
+                self.strpath,
+                mode,
+                encoding=encoding,
+            )
+        return error.checked_call(open, self.strpath, mode)
+
+    def _fastjoin(self, name):
+        child = object.__new__(self.__class__)
+        child.strpath = self.strpath + self.sep + name
+        return child
+
+    def islink(self):
+        return islink(self.strpath)
+
+    def check(self, **kw):
+        """Check a path for existence and properties.
+
+        Without arguments, return True if the path exists, otherwise False.
+
+        valid checkers::
+
+            file = 1  # is a file
+            file = 0  # is not a file (may not even exist)
+            dir = 1  # is a dir
+            link = 1  # is a link
+            exists = 1  # exists
+
+        You can specify multiple checker definitions, for example::
+
+            path.check(file=1, link=1)  # a link pointing to a file
+        """
+        if not kw:
+            return exists(self.strpath)
+        if len(kw) == 1:
+            if "dir" in kw:
+                return not kw["dir"] ^ isdir(self.strpath)
+            if "file" in kw:
+                return not kw["file"] ^ isfile(self.strpath)
+        if not kw:
+            kw = {"exists": 1}
+        return Checkers(self)._evaluate(kw)
+
+    _patternchars = set("*?[" + os.sep)
+
+    def listdir(self, fil=None, sort=None):
+        """List directory contents, possibly filter by the given fil func
+        and possibly sorted.
+        """
+        if fil is None and sort is None:
+            names = error.checked_call(os.listdir, self.strpath)
+            return map_as_list(self._fastjoin, names)
+        if isinstance(fil, str):
+            if not self._patternchars.intersection(fil):
+                child = self._fastjoin(fil)
+                if exists(child.strpath):
+                    return [child]
+                return []
+            fil = FNMatcher(fil)
+        names = error.checked_call(os.listdir, self.strpath)
+        res = []
+        for name in names:
+            child = self._fastjoin(name)
+            if fil is None or fil(child):
+                res.append(child)
+        self._sortlist(res, sort)
+        return res
+
+    def size(self) -> int:
+        """Return size of the underlying file object"""
+        return self.stat().size
+
+    def mtime(self) -> float:
+        """Return last modification time of the path."""
+        return self.stat().mtime
+
+    def copy(self, target, mode=False, stat=False):
+        """Copy path to target.
+
+        If mode is True, will copy permission from path to target.
+        If stat is True, copy permission, last modification
+        time, last access time, and flags from path to target.
+        """
+        if self.check(file=1):
+            if target.check(dir=1):
+                target = target.join(self.basename)
+            assert self != target
+            copychunked(self, target)
+            if mode:
+                copymode(self.strpath, target.strpath)
+            if stat:
+                copystat(self, target)
+        else:
+
+            def rec(p):
+                return p.check(link=0)
+
+            for x in self.visit(rec=rec):
+                relpath = x.relto(self)
+                newx = target.join(relpath)
+                newx.dirpath().ensure(dir=1)
+                if x.check(link=1):
+                    newx.mksymlinkto(x.readlink())
+                    continue
+                elif x.check(file=1):
+                    copychunked(x, newx)
+                elif x.check(dir=1):
+                    newx.ensure(dir=1)
+                if mode:
+                    copymode(x.strpath, newx.strpath)
+                if stat:
+                    copystat(x, newx)
+
+    def rename(self, target):
+        """Rename this path to target."""
+        target = os.fspath(target)
+        return error.checked_call(os.rename, self.strpath, target)
+
+    def dump(self, obj, bin=1):
+        """Pickle object into path location"""
+        f = self.open("wb")
+        import pickle
+
+        try:
+            error.checked_call(pickle.dump, obj, f, bin)
+        finally:
+            f.close()
+
+    def mkdir(self, *args):
+        """Create & return the directory joined with args."""
+        p = self.join(*args)
+        error.checked_call(os.mkdir, os.fspath(p))
+        return p
+
+    def write_binary(self, data, ensure=False):
+        """Write binary data into path.   If ensure is True create
+        missing parent directories.
+        """
+        if ensure:
+            self.dirpath().ensure(dir=1)
+        with self.open("wb") as f:
+            f.write(data)
+
+    def write_text(self, data, encoding, ensure=False):
+        """Write text data into path using the specified encoding.
+        If ensure is True create missing parent directories.
+        """
+        if ensure:
+            self.dirpath().ensure(dir=1)
+        with self.open("w", encoding=encoding) as f:
+            f.write(data)
+
+    def write(self, data, mode="w", ensure=False):
+        """Write data into path.   If ensure is True create
+        missing parent directories.
+        """
+        if ensure:
+            self.dirpath().ensure(dir=1)
+        if "b" in mode:
+            if not isinstance(data, bytes):
+                raise ValueError("can only process bytes")
+        else:
+            if not isinstance(data, str):
+                if not isinstance(data, bytes):
+                    data = str(data)
+                else:
+                    data = data.decode(sys.getdefaultencoding())
+        f = self.open(mode)
+        try:
+            f.write(data)
+        finally:
+            f.close()
+
+    def _ensuredirs(self):
+        parent = self.dirpath()
+        if parent == self:
+            return self
+        if parent.check(dir=0):
+            parent._ensuredirs()
+        if self.check(dir=0):
+            try:
+                self.mkdir()
+            except error.EEXIST:
+                # race condition: file/dir created by another thread/process.
+                # complain if it is not a dir
+                if self.check(dir=0):
+                    raise
+        return self
+
+    def ensure(self, *args, **kwargs):
+        """Ensure that an args-joined path exists (by default as
+        a file). if you specify a keyword argument 'dir=True'
+        then the path is forced to be a directory path.
+        """
+        p = self.join(*args)
+        if kwargs.get("dir", 0):
+            return p._ensuredirs()
+        else:
+            p.dirpath()._ensuredirs()
+            if not p.check(file=1):
+                p.open("wb").close()
+            return p
+
+    @overload
+    def stat(self, raising: Literal[True] = ...) -> Stat: ...
+
+    @overload
+    def stat(self, raising: Literal[False]) -> Stat | None: ...
+
+    def stat(self, raising: bool = True) -> Stat | None:
+        """Return an os.stat() tuple."""
+        if raising:
+            return Stat(self, error.checked_call(os.stat, self.strpath))
+        try:
+            return Stat(self, os.stat(self.strpath))
+        except KeyboardInterrupt:
+            raise
+        except Exception:
+            return None
+
+    def lstat(self) -> Stat:
+        """Return an os.lstat() tuple."""
+        return Stat(self, error.checked_call(os.lstat, self.strpath))
+
+    def setmtime(self, mtime=None):
+        """Set modification time for the given path.  if 'mtime' is None
+        (the default) then the file's mtime is set to current time.
+
+        Note that the resolution for 'mtime' is platform dependent.
+        """
+        if mtime is None:
+            return error.checked_call(os.utime, self.strpath, mtime)
+        try:
+            return error.checked_call(os.utime, self.strpath, (-1, mtime))
+        except error.EINVAL:
+            return error.checked_call(os.utime, self.strpath, (self.atime(), mtime))
+
+    def chdir(self):
+        """Change directory to self and return old current directory"""
+        try:
+            old = self.__class__()
+        except error.ENOENT:
+            old = None
+        error.checked_call(os.chdir, self.strpath)
+        return old
+
+    @contextmanager
+    def as_cwd(self):
+        """
+        Return a context manager, which changes to the path's dir during the
+        managed "with" context.
+        On __enter__ it returns the old dir, which might be ``None``.
+        """
+        old = self.chdir()
+        try:
+            yield old
+        finally:
+            if old is not None:
+                old.chdir()
+
+    def realpath(self):
+        """Return a new path which contains no symbolic links."""
+        return self.__class__(os.path.realpath(self.strpath))
+
+    def atime(self):
+        """Return last access time of the path."""
+        return self.stat().atime
+
+    def __repr__(self):
+        return f"local({self.strpath!r})"
+
+    def __str__(self):
+        """Return string representation of the Path."""
+        return self.strpath
+
+    def chmod(self, mode, rec=0):
+        """Change permissions to the given mode. If mode is an
+        integer it directly encodes the os-specific modes.
+        if rec is True perform recursively.
+        """
+        if not isinstance(mode, int):
+            raise TypeError(f"mode {mode!r} must be an integer")
+        if rec:
+            for x in self.visit(rec=rec):
+                error.checked_call(os.chmod, str(x), mode)
+        error.checked_call(os.chmod, self.strpath, mode)
+
+    def pypkgpath(self):
+        """Return the Python package path by looking for the last
+        directory upwards which still contains an __init__.py.
+        Return None if a pkgpath cannot be determined.
+        """
+        pkgpath = None
+        for parent in self.parts(reverse=True):
+            if parent.isdir():
+                if not parent.join("__init__.py").exists():
+                    break
+                if not isimportable(parent.basename):
+                    break
+                pkgpath = parent
+        return pkgpath
+
+    def _ensuresyspath(self, ensuremode, path):
+        if ensuremode:
+            s = str(path)
+            if ensuremode == "append":
+                if s not in sys.path:
+                    sys.path.append(s)
+            else:
+                if s != sys.path[0]:
+                    sys.path.insert(0, s)
+
+    def pyimport(self, modname=None, ensuresyspath=True):
+        """Return path as an imported python module.
+
+        If modname is None, look for the containing package
+        and construct an according module name.
+        The module will be put/looked up in sys.modules.
+        if ensuresyspath is True then the root dir for importing
+        the file (taking __init__.py files into account) will
+        be prepended to sys.path if it isn't there already.
+        If ensuresyspath=="append" the root dir will be appended
+        if it isn't already contained in sys.path.
+        if ensuresyspath is False no modification of syspath happens.
+
+        Special value of ensuresyspath=="importlib" is intended
+        purely for using in pytest, it is capable only of importing
+        separate .py files outside packages, e.g. for test suite
+        without any __init__.py file. It effectively allows having
+        same-named test modules in different places and offers
+        mild opt-in via this option. Note that it works only in
+        recent versions of python.
+        """
+        if not self.check():
+            raise error.ENOENT(self)
+
+        if ensuresyspath == "importlib":
+            if modname is None:
+                modname = self.purebasename
+            spec = importlib.util.spec_from_file_location(modname, str(self))
+            if spec is None or spec.loader is None:
+                raise ImportError(f"Can't find module {modname} at location {self!s}")
+            mod = importlib.util.module_from_spec(spec)
+            spec.loader.exec_module(mod)
+            return mod
+
+        pkgpath = None
+        if modname is None:
+            pkgpath = self.pypkgpath()
+            if pkgpath is not None:
+                pkgroot = pkgpath.dirpath()
+                names = self.new(ext="").relto(pkgroot).split(self.sep)
+                if names[-1] == "__init__":
+                    names.pop()
+                modname = ".".join(names)
+            else:
+                pkgroot = self.dirpath()
+                modname = self.purebasename
+
+            self._ensuresyspath(ensuresyspath, pkgroot)
+            __import__(modname)
+            mod = sys.modules[modname]
+            if self.basename == "__init__.py":
+                return mod  # we don't check anything as we might
+                # be in a namespace package ... too icky to check
+            modfile = mod.__file__
+            assert modfile is not None
+            if modfile[-4:] in (".pyc", ".pyo"):
+                modfile = modfile[:-1]
+            elif modfile.endswith("$py.class"):
+                modfile = modfile[:-9] + ".py"
+            if modfile.endswith(os.sep + "__init__.py"):
+                if self.basename != "__init__.py":
+                    modfile = modfile[:-12]
+            try:
+                issame = self.samefile(modfile)
+            except error.ENOENT:
+                issame = False
+            if not issame:
+                ignore = os.getenv("PY_IGNORE_IMPORTMISMATCH")
+                if ignore != "1":
+                    raise self.ImportMismatchError(modname, modfile, self)
+            return mod
+        else:
+            try:
+                return sys.modules[modname]
+            except KeyError:
+                # we have a custom modname, do a pseudo-import
+                import types
+
+                mod = types.ModuleType(modname)
+                mod.__file__ = str(self)
+                sys.modules[modname] = mod
+                try:
+                    with open(str(self), "rb") as f:
+                        exec(f.read(), mod.__dict__)
+                except BaseException:
+                    del sys.modules[modname]
+                    raise
+                return mod
+
+    def sysexec(self, *argv: os.PathLike[str], **popen_opts: Any) -> str:
+        """Return stdout text from executing a system child process,
+        where the 'self' path points to executable.
+        The process is directly invoked and not through a system shell.
+        """
+        from subprocess import PIPE
+        from subprocess import Popen
+
+        popen_opts.pop("stdout", None)
+        popen_opts.pop("stderr", None)
+        proc = Popen(
+            [str(self)] + [str(arg) for arg in argv],
+            **popen_opts,
+            stdout=PIPE,
+            stderr=PIPE,
+        )
+        stdout: str | bytes
+        stdout, stderr = proc.communicate()
+        ret = proc.wait()
+        if isinstance(stdout, bytes):
+            stdout = stdout.decode(sys.getdefaultencoding())
+        if ret != 0:
+            if isinstance(stderr, bytes):
+                stderr = stderr.decode(sys.getdefaultencoding())
+            raise RuntimeError(
+                ret,
+                ret,
+                str(self),
+                stdout,
+                stderr,
+            )
+        return stdout
+
+    @classmethod
+    def sysfind(cls, name, checker=None, paths=None):
+        """Return a path object found by looking at the systems
+        underlying PATH specification. If the checker is not None
+        it will be invoked to filter matching paths.  If a binary
+        cannot be found, None is returned
+        Note: This is probably not working on plain win32 systems
+        but may work on cygwin.
+        """
+        if isabs(name):
+            p = local(name)
+            if p.check(file=1):
+                return p
+        else:
+            if paths is None:
+                if iswin32:
+                    paths = os.environ["Path"].split(";")
+                    if "" not in paths and "." not in paths:
+                        paths.append(".")
+                    try:
+                        systemroot = os.environ["SYSTEMROOT"]
+                    except KeyError:
+                        pass
+                    else:
+                        paths = [
+                            path.replace("%SystemRoot%", systemroot) for path in paths
+                        ]
+                else:
+                    paths = os.environ["PATH"].split(":")
+            tryadd = []
+            if iswin32:
+                tryadd += os.environ["PATHEXT"].split(os.pathsep)
+            tryadd.append("")
+
+            for x in paths:
+                for addext in tryadd:
+                    p = local(x).join(name, abs=True) + addext
+                    try:
+                        if p.check(file=1):
+                            if checker:
+                                if not checker(p):
+                                    continue
+                            return p
+                    except error.EACCES:
+                        pass
+        return None
+
+    @classmethod
+    def _gethomedir(cls):
+        try:
+            x = os.environ["HOME"]
+        except KeyError:
+            try:
+                x = os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"]
+            except KeyError:
+                return None
+        return cls(x)
+
+    # """
+    # special class constructors for local filesystem paths
+    # """
+    @classmethod
+    def get_temproot(cls):
+        """Return the system's temporary directory
+        (where tempfiles are usually created in)
+        """
+        import tempfile
+
+        return local(tempfile.gettempdir())
+
+    @classmethod
+    def mkdtemp(cls, rootdir=None):
+        """Return a Path object pointing to a fresh new temporary directory
+        (which we created ourselves).
+        """
+        import tempfile
+
+        if rootdir is None:
+            rootdir = cls.get_temproot()
+        path = error.checked_call(tempfile.mkdtemp, dir=str(rootdir))
+        return cls(path)
+
+    @classmethod
+    def make_numbered_dir(
+        cls, prefix="session-", rootdir=None, keep=3, lock_timeout=172800
+    ):  # two days
+        """Return unique directory with a number greater than the current
+        maximum one.  The number is assumed to start directly after prefix.
+        if keep is true directories with a number less than (maxnum-keep)
+        will be removed. If .lock files are used (lock_timeout non-zero),
+        algorithm is multi-process safe.
+        """
+        if rootdir is None:
+            rootdir = cls.get_temproot()
+
+        nprefix = prefix.lower()
+
+        def parse_num(path):
+            """Parse the number out of a path (if it matches the prefix)"""
+            nbasename = path.basename.lower()
+            if nbasename.startswith(nprefix):
+                try:
+                    return int(nbasename[len(nprefix) :])
+                except ValueError:
+                    pass
+
+        def create_lockfile(path):
+            """Exclusively create lockfile. Throws when failed"""
+            mypid = os.getpid()
+            lockfile = path.join(".lock")
+            if hasattr(lockfile, "mksymlinkto"):
+                lockfile.mksymlinkto(str(mypid))
+            else:
+                fd = error.checked_call(
+                    os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644
+                )
+                with os.fdopen(fd, "w") as f:
+                    f.write(str(mypid))
+            return lockfile
+
+        def atexit_remove_lockfile(lockfile):
+            """Ensure lockfile is removed at process exit"""
+            mypid = os.getpid()
+
+            def try_remove_lockfile():
+                # in a fork() situation, only the last process should
+                # remove the .lock, otherwise the other processes run the
+                # risk of seeing their temporary dir disappear.  For now
+                # we remove the .lock in the parent only (i.e. we assume
+                # that the children finish before the parent).
+                if os.getpid() != mypid:
+                    return
+                try:
+                    lockfile.remove()
+                except error.Error:
+                    pass
+
+            atexit.register(try_remove_lockfile)
+
+        # compute the maximum number currently in use with the prefix
+        lastmax = None
+        while True:
+            maxnum = -1
+            for path in rootdir.listdir():
+                num = parse_num(path)
+                if num is not None:
+                    maxnum = max(maxnum, num)
+
+            # make the new directory
+            try:
+                udir = rootdir.mkdir(prefix + str(maxnum + 1))
+                if lock_timeout:
+                    lockfile = create_lockfile(udir)
+                    atexit_remove_lockfile(lockfile)
+            except (error.EEXIST, error.ENOENT, error.EBUSY):
+                # race condition (1): another thread/process created the dir
+                #                     in the meantime - try again
+                # race condition (2): another thread/process spuriously acquired
+                #                     lock treating empty directory as candidate
+                #                     for removal - try again
+                # race condition (3): another thread/process tried to create the lock at
+                #                     the same time (happened in Python 3.3 on Windows)
+                # https://ci.appveyor.com/project/pytestbot/py/build/1.0.21/job/ffi85j4c0lqwsfwa
+                if lastmax == maxnum:
+                    raise
+                lastmax = maxnum
+                continue
+            break
+
+        def get_mtime(path):
+            """Read file modification time"""
+            try:
+                return path.lstat().mtime
+            except error.Error:
+                pass
+
+        garbage_prefix = prefix + "garbage-"
+
+        def is_garbage(path):
+            """Check if path denotes directory scheduled for removal"""
+            bn = path.basename
+            return bn.startswith(garbage_prefix)
+
+        # prune old directories
+        udir_time = get_mtime(udir)
+        if keep and udir_time:
+            for path in rootdir.listdir():
+                num = parse_num(path)
+                if num is not None and num <= (maxnum - keep):
+                    try:
+                        # try acquiring lock to remove directory as exclusive user
+                        if lock_timeout:
+                            create_lockfile(path)
+                    except (error.EEXIST, error.ENOENT, error.EBUSY):
+                        path_time = get_mtime(path)
+                        if not path_time:
+                            # assume directory doesn't exist now
+                            continue
+                        if abs(udir_time - path_time) < lock_timeout:
+                            # assume directory with lockfile exists
+                            # and lock timeout hasn't expired yet
+                            continue
+
+                    # path dir locked for exclusive use
+                    # and scheduled for removal to avoid another thread/process
+                    # treating it as a new directory or removal candidate
+                    garbage_path = rootdir.join(garbage_prefix + str(uuid.uuid4()))
+                    try:
+                        path.rename(garbage_path)
+                        garbage_path.remove(rec=1)
+                    except KeyboardInterrupt:
+                        raise
+                    except Exception:  # this might be error.Error, WindowsError ...
+                        pass
+                if is_garbage(path):
+                    try:
+                        path.remove(rec=1)
+                    except KeyboardInterrupt:
+                        raise
+                    except Exception:  # this might be error.Error, WindowsError ...
+                        pass
+
+        # make link...
+        try:
+            username = os.environ["USER"]  # linux, et al
+        except KeyError:
+            try:
+                username = os.environ["USERNAME"]  # windows
+            except KeyError:
+                username = "current"
+
+        src = str(udir)
+        dest = src[: src.rfind("-")] + "-" + username
+        try:
+            os.unlink(dest)
+        except OSError:
+            pass
+        try:
+            os.symlink(src, dest)
+        except (OSError, AttributeError, NotImplementedError):
+            pass
+
+        return udir
+
+
+def copymode(src, dest):
+    """Copy permission from src to dst."""
+    import shutil
+
+    shutil.copymode(src, dest)
+
+
+def copystat(src, dest):
+    """Copy permission,  last modification time,
+    last access time, and flags from src to dst."""
+    import shutil
+
+    shutil.copystat(str(src), str(dest))
+
+
+def copychunked(src, dest):
+    chunksize = 524288  # half a meg of bytes
+    fsrc = src.open("rb")
+    try:
+        fdest = dest.open("wb")
+        try:
+            while 1:
+                buf = fsrc.read(chunksize)
+                if not buf:
+                    break
+                fdest.write(buf)
+        finally:
+            fdest.close()
+    finally:
+        fsrc.close()
+
+
+def isimportable(name):
+    if name and (name[0].isalpha() or name[0] == "_"):
+        name = name.replace("_", "")
+        return not name or name.isalnum()
+
+
+local = LocalPath

+ 34 - 0
algo/py312/lib/python3.12/site-packages/_pytest/_version.py

@@ -0,0 +1,34 @@
+# file generated by setuptools-scm
+# don't change, don't track in version control
+
+__all__ = [
+    "__version__",
+    "__version_tuple__",
+    "version",
+    "version_tuple",
+    "__commit_id__",
+    "commit_id",
+]
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+    from typing import Tuple
+    from typing import Union
+
+    VERSION_TUPLE = Tuple[Union[int, str], ...]
+    COMMIT_ID = Union[str, None]
+else:
+    VERSION_TUPLE = object
+    COMMIT_ID = object
+
+version: str
+__version__: str
+__version_tuple__: VERSION_TUPLE
+version_tuple: VERSION_TUPLE
+commit_id: COMMIT_ID
+__commit_id__: COMMIT_ID
+
+__version__ = version = '9.0.2'
+__version_tuple__ = version_tuple = (9, 0, 2)
+
+__commit_id__ = commit_id = None

+ 208 - 0
algo/py312/lib/python3.12/site-packages/_pytest/assertion/__init__.py

@@ -0,0 +1,208 @@
+# mypy: allow-untyped-defs
+"""Support for presenting detailed information in failing assertions."""
+
+from __future__ import annotations
+
+from collections.abc import Generator
+import sys
+from typing import Any
+from typing import Protocol
+from typing import TYPE_CHECKING
+
+from _pytest.assertion import rewrite
+from _pytest.assertion import truncate
+from _pytest.assertion import util
+from _pytest.assertion.rewrite import assertstate_key
+from _pytest.config import Config
+from _pytest.config import hookimpl
+from _pytest.config.argparsing import Parser
+from _pytest.nodes import Item
+
+
+if TYPE_CHECKING:
+    from _pytest.main import Session
+
+
+def pytest_addoption(parser: Parser) -> None:
+    group = parser.getgroup("debugconfig")
+    group.addoption(
+        "--assert",
+        action="store",
+        dest="assertmode",
+        choices=("rewrite", "plain"),
+        default="rewrite",
+        metavar="MODE",
+        help=(
+            "Control assertion debugging tools.\n"
+            "'plain' performs no assertion debugging.\n"
+            "'rewrite' (the default) rewrites assert statements in test modules"
+            " on import to provide assert expression information."
+        ),
+    )
+    parser.addini(
+        "enable_assertion_pass_hook",
+        type="bool",
+        default=False,
+        help="Enables the pytest_assertion_pass hook. "
+        "Make sure to delete any previously generated pyc cache files.",
+    )
+
+    parser.addini(
+        "truncation_limit_lines",
+        default=None,
+        help="Set threshold of LINES after which truncation will take effect",
+    )
+    parser.addini(
+        "truncation_limit_chars",
+        default=None,
+        help=("Set threshold of CHARS after which truncation will take effect"),
+    )
+
+    Config._add_verbosity_ini(
+        parser,
+        Config.VERBOSITY_ASSERTIONS,
+        help=(
+            "Specify a verbosity level for assertions, overriding the main level. "
+            "Higher levels will provide more detailed explanation when an assertion fails."
+        ),
+    )
+
+
+def register_assert_rewrite(*names: str) -> None:
+    """Register one or more module names to be rewritten on import.
+
+    This function will make sure that this module or all modules inside
+    the package will get their assert statements rewritten.
+    Thus you should make sure to call this before the module is
+    actually imported, usually in your __init__.py if you are a plugin
+    using a package.
+
+    :param names: The module names to register.
+    """
+    for name in names:
+        if not isinstance(name, str):
+            msg = "expected module names as *args, got {0} instead"  # type: ignore[unreachable]
+            raise TypeError(msg.format(repr(names)))
+    rewrite_hook: RewriteHook
+    for hook in sys.meta_path:
+        if isinstance(hook, rewrite.AssertionRewritingHook):
+            rewrite_hook = hook
+            break
+    else:
+        rewrite_hook = DummyRewriteHook()
+    rewrite_hook.mark_rewrite(*names)
+
+
+class RewriteHook(Protocol):
+    def mark_rewrite(self, *names: str) -> None: ...
+
+
+class DummyRewriteHook:
+    """A no-op import hook for when rewriting is disabled."""
+
+    def mark_rewrite(self, *names: str) -> None:
+        pass
+
+
+class AssertionState:
+    """State for the assertion plugin."""
+
+    def __init__(self, config: Config, mode) -> None:
+        self.mode = mode
+        self.trace = config.trace.root.get("assertion")
+        self.hook: rewrite.AssertionRewritingHook | None = None
+
+
+def install_importhook(config: Config) -> rewrite.AssertionRewritingHook:
+    """Try to install the rewrite hook, raise SystemError if it fails."""
+    config.stash[assertstate_key] = AssertionState(config, "rewrite")
+    config.stash[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config)
+    sys.meta_path.insert(0, hook)
+    config.stash[assertstate_key].trace("installed rewrite import hook")
+
+    def undo() -> None:
+        hook = config.stash[assertstate_key].hook
+        if hook is not None and hook in sys.meta_path:
+            sys.meta_path.remove(hook)
+
+    config.add_cleanup(undo)
+    return hook
+
+
+def pytest_collection(session: Session) -> None:
+    # This hook is only called when test modules are collected
+    # so for example not in the managing process of pytest-xdist
+    # (which does not collect test modules).
+    assertstate = session.config.stash.get(assertstate_key, None)
+    if assertstate:
+        if assertstate.hook is not None:
+            assertstate.hook.set_session(session)
+
+
+@hookimpl(wrapper=True, tryfirst=True)
+def pytest_runtest_protocol(item: Item) -> Generator[None, object, object]:
+    """Setup the pytest_assertrepr_compare and pytest_assertion_pass hooks.
+
+    The rewrite module will use util._reprcompare if it exists to use custom
+    reporting via the pytest_assertrepr_compare hook.  This sets up this custom
+    comparison for the test.
+    """
+    ihook = item.ihook
+
+    def callbinrepr(op, left: object, right: object) -> str | None:
+        """Call the pytest_assertrepr_compare hook and prepare the result.
+
+        This uses the first result from the hook and then ensures the
+        following:
+        * Overly verbose explanations are truncated unless configured otherwise
+          (eg. if running in verbose mode).
+        * Embedded newlines are escaped to help util.format_explanation()
+          later.
+        * If the rewrite mode is used embedded %-characters are replaced
+          to protect later % formatting.
+
+        The result can be formatted by util.format_explanation() for
+        pretty printing.
+        """
+        hook_result = ihook.pytest_assertrepr_compare(
+            config=item.config, op=op, left=left, right=right
+        )
+        for new_expl in hook_result:
+            if new_expl:
+                new_expl = truncate.truncate_if_required(new_expl, item)
+                new_expl = [line.replace("\n", "\\n") for line in new_expl]
+                res = "\n~".join(new_expl)
+                if item.config.getvalue("assertmode") == "rewrite":
+                    res = res.replace("%", "%%")
+                return res
+        return None
+
+    saved_assert_hooks = util._reprcompare, util._assertion_pass
+    util._reprcompare = callbinrepr
+    util._config = item.config
+
+    if ihook.pytest_assertion_pass.get_hookimpls():
+
+        def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None:
+            ihook.pytest_assertion_pass(item=item, lineno=lineno, orig=orig, expl=expl)
+
+        util._assertion_pass = call_assertion_pass_hook
+
+    try:
+        return (yield)
+    finally:
+        util._reprcompare, util._assertion_pass = saved_assert_hooks
+        util._config = None
+
+
+def pytest_sessionfinish(session: Session) -> None:
+    assertstate = session.config.stash.get(assertstate_key, None)
+    if assertstate:
+        if assertstate.hook is not None:
+            assertstate.hook.set_session(None)
+
+
+def pytest_assertrepr_compare(
+    config: Config, op: str, left: Any, right: Any
+) -> list[str] | None:
+    return util.assertrepr_compare(config=config, op=op, left=left, right=right)

BIN
algo/py312/lib/python3.12/site-packages/_pytest/assertion/__pycache__/__init__.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/assertion/__pycache__/rewrite.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/assertion/__pycache__/truncate.cpython-312.pyc


BIN
algo/py312/lib/python3.12/site-packages/_pytest/assertion/__pycache__/util.cpython-312.pyc


Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio