forked from Raiza.dev/EliteBot
Removed elitebot dir and some other stuff
This commit is contained in:
parent
f5c3c95565
commit
2a8da606c2
1321 changed files with 3 additions and 554557 deletions
|
@ -19,4 +19,4 @@
|
|||
"Database": {
|
||||
"ConnectionString": "mysql://user:password@host:port/database"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
VERSION: 1.0.0
|
||||
Connection:
|
||||
Hostname: irc.rizon.net
|
||||
Hostname: irc.example.net
|
||||
Port: '+6697'
|
||||
Nick: EliteBot
|
||||
Ident: EliteBot
|
||||
|
|
|
@ -1,247 +0,0 @@
|
|||
<#
|
||||
.Synopsis
|
||||
Activate a Python virtual environment for the current PowerShell session.
|
||||
|
||||
.Description
|
||||
Pushes the python executable for a virtual environment to the front of the
|
||||
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||
in a Python virtual environment. Makes use of the command line switches as
|
||||
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||
|
||||
.Parameter VenvDir
|
||||
Path to the directory that contains the virtual environment to activate. The
|
||||
default value for this is the parent of the directory that the Activate.ps1
|
||||
script is located within.
|
||||
|
||||
.Parameter Prompt
|
||||
The prompt prefix to display when this virtual environment is activated. By
|
||||
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||
|
||||
.Example
|
||||
Activate.ps1
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Verbose
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and shows extra information about the activation as it executes.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||
Activates the Python virtual environment located in the specified location.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Prompt "MyPython"
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and prefixes the current prompt with the specified string (surrounded in
|
||||
parentheses) while the virtual environment is active.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
Param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$VenvDir,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$Prompt
|
||||
)
|
||||
|
||||
<# Function declarations --------------------------------------------------- #>
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Remove all shell session elements added by the Activate script, including the
|
||||
addition of the virtual environment's Python executable from the beginning of
|
||||
the PATH variable.
|
||||
|
||||
.Parameter NonDestructive
|
||||
If present, do not remove this function from the global namespace for the
|
||||
session.
|
||||
|
||||
#>
|
||||
function global:deactivate ([switch]$NonDestructive) {
|
||||
# Revert to original values
|
||||
|
||||
# The prior prompt:
|
||||
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# The prior PYTHONHOME:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
# The prior PATH:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Just remove the VIRTUAL_ENV altogether:
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV
|
||||
}
|
||||
|
||||
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||
}
|
||||
|
||||
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||
}
|
||||
|
||||
# Leave deactivate function in the global namespace if requested:
|
||||
if (-not $NonDestructive) {
|
||||
Remove-Item -Path function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Description
|
||||
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||
given folder, and returns them in a map.
|
||||
|
||||
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||
then it is considered a `key = value` line. The left hand string is the key,
|
||||
the right hand is the value.
|
||||
|
||||
If the value starts with a `'` or a `"` then the first and last character is
|
||||
stripped from the value before being captured.
|
||||
|
||||
.Parameter ConfigDir
|
||||
Path to the directory that contains the `pyvenv.cfg` file.
|
||||
#>
|
||||
function Get-PyVenvConfig(
|
||||
[String]
|
||||
$ConfigDir
|
||||
) {
|
||||
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||
|
||||
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||
|
||||
# An empty map will be returned if no config file is found.
|
||||
$pyvenvConfig = @{ }
|
||||
|
||||
if ($pyvenvConfigPath) {
|
||||
|
||||
Write-Verbose "File exists, parse `key = value` lines"
|
||||
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||
|
||||
$pyvenvConfigContent | ForEach-Object {
|
||||
$keyval = $PSItem -split "\s*=\s*", 2
|
||||
if ($keyval[0] -and $keyval[1]) {
|
||||
$val = $keyval[1]
|
||||
|
||||
# Remove extraneous quotations around a string value.
|
||||
if ("'""".Contains($val.Substring(0, 1))) {
|
||||
$val = $val.Substring(1, $val.Length - 2)
|
||||
}
|
||||
|
||||
$pyvenvConfig[$keyval[0]] = $val
|
||||
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pyvenvConfig
|
||||
}
|
||||
|
||||
|
||||
<# Begin Activate script --------------------------------------------------- #>
|
||||
|
||||
# Determine the containing directory of this script
|
||||
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||
|
||||
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||
|
||||
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||
# First, get the location of the virtual environment, it might not be
|
||||
# VenvExecDir if specified on the command line.
|
||||
if ($VenvDir) {
|
||||
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||
Write-Verbose "VenvDir=$VenvDir"
|
||||
}
|
||||
|
||||
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||
# as `prompt`.
|
||||
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||
|
||||
# Next, set the prompt from the command line, or the config file, or
|
||||
# just use the name of the virtual environment folder.
|
||||
if ($Prompt) {
|
||||
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||
$Prompt = $pyvenvCfg['prompt'];
|
||||
}
|
||||
else {
|
||||
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Prompt = '$Prompt'"
|
||||
Write-Verbose "VenvDir='$VenvDir'"
|
||||
|
||||
# Deactivate any currently active virtual environment, but leave the
|
||||
# deactivate function in place.
|
||||
deactivate -nondestructive
|
||||
|
||||
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
|
||||
# Set the prompt to include the env name
|
||||
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||
|
||||
function global:prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path -Path Env:PYTHONHOME) {
|
||||
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
Remove-Item -Path Env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
|
@ -1,69 +0,0 @@
|
|||
# This file must be used with "source bin/activate" *from bash*
|
||||
# you cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
VIRTUAL_ENV="/home/colby/Documents/stuff/EliteBot/elitebot"
|
||||
export VIRTUAL_ENV
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1="(elitebot) ${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT="(elitebot) "
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
|
@ -1,26 +0,0 @@
|
|||
# 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 "/home/colby/Documents/stuff/EliteBot/elitebot"
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH"
|
||||
setenv PATH "$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||
|
||||
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||
set prompt = "(elitebot) $prompt"
|
||||
setenv VIRTUAL_ENV_PROMPT "(elitebot) "
|
||||
endif
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
|
@ -1,69 +0,0 @@
|
|||
# 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 "/home/colby/Documents/stuff/EliteBot/elitebot"
|
||||
|
||||
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) "(elitebot) " (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 "(elitebot) "
|
||||
end
|
|
@ -1,8 +0,0 @@
|
|||
#!/home/colby/Documents/stuff/EliteBot/elitebot/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())
|
|
@ -1,8 +0,0 @@
|
|||
#!/home/colby/Documents/stuff/EliteBot/elitebot/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())
|
|
@ -1,8 +0,0 @@
|
|||
#!/home/colby/Documents/stuff/EliteBot/elitebot/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())
|
|
@ -1 +0,0 @@
|
|||
python3
|
|
@ -1 +0,0 @@
|
|||
/usr/bin/python3
|
|
@ -1 +0,0 @@
|
|||
python3
|
|
@ -1,164 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
|
||||
/* Greenlet object interface */
|
||||
|
||||
#ifndef Py_GREENLETOBJECT_H
|
||||
#define Py_GREENLETOBJECT_H
|
||||
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This is deprecated and undocumented. It does not change. */
|
||||
#define GREENLET_VERSION "1.0.0"
|
||||
|
||||
#ifndef GREENLET_MODULE
|
||||
#define implementation_ptr_t void*
|
||||
#endif
|
||||
|
||||
typedef struct _greenlet {
|
||||
PyObject_HEAD
|
||||
PyObject* weakreflist;
|
||||
PyObject* dict;
|
||||
implementation_ptr_t pimpl;
|
||||
} PyGreenlet;
|
||||
|
||||
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
|
||||
|
||||
|
||||
/* C API functions */
|
||||
|
||||
/* Total number of symbols that are exported */
|
||||
#define PyGreenlet_API_pointers 12
|
||||
|
||||
#define PyGreenlet_Type_NUM 0
|
||||
#define PyExc_GreenletError_NUM 1
|
||||
#define PyExc_GreenletExit_NUM 2
|
||||
|
||||
#define PyGreenlet_New_NUM 3
|
||||
#define PyGreenlet_GetCurrent_NUM 4
|
||||
#define PyGreenlet_Throw_NUM 5
|
||||
#define PyGreenlet_Switch_NUM 6
|
||||
#define PyGreenlet_SetParent_NUM 7
|
||||
|
||||
#define PyGreenlet_MAIN_NUM 8
|
||||
#define PyGreenlet_STARTED_NUM 9
|
||||
#define PyGreenlet_ACTIVE_NUM 10
|
||||
#define PyGreenlet_GET_PARENT_NUM 11
|
||||
|
||||
#ifndef GREENLET_MODULE
|
||||
/* This section is used by modules that uses the greenlet C API */
|
||||
static void** _PyGreenlet_API = NULL;
|
||||
|
||||
# define PyGreenlet_Type \
|
||||
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
|
||||
|
||||
# define PyExc_GreenletError \
|
||||
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
|
||||
|
||||
# define PyExc_GreenletExit \
|
||||
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_New(PyObject *args)
|
||||
*
|
||||
* greenlet.greenlet(run, parent=None)
|
||||
*/
|
||||
# define PyGreenlet_New \
|
||||
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
|
||||
_PyGreenlet_API[PyGreenlet_New_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_GetCurrent(void)
|
||||
*
|
||||
* greenlet.getcurrent()
|
||||
*/
|
||||
# define PyGreenlet_GetCurrent \
|
||||
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_Throw(
|
||||
* PyGreenlet *greenlet,
|
||||
* PyObject *typ,
|
||||
* PyObject *val,
|
||||
* PyObject *tb)
|
||||
*
|
||||
* g.throw(...)
|
||||
*/
|
||||
# define PyGreenlet_Throw \
|
||||
(*(PyObject * (*)(PyGreenlet * self, \
|
||||
PyObject * typ, \
|
||||
PyObject * val, \
|
||||
PyObject * tb)) \
|
||||
_PyGreenlet_API[PyGreenlet_Throw_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
|
||||
*
|
||||
* g.switch(*args, **kwargs)
|
||||
*/
|
||||
# define PyGreenlet_Switch \
|
||||
(*(PyObject * \
|
||||
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
|
||||
_PyGreenlet_API[PyGreenlet_Switch_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
|
||||
*
|
||||
* g.parent = new_parent
|
||||
*/
|
||||
# define PyGreenlet_SetParent \
|
||||
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
|
||||
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_GetParent(PyObject* greenlet)
|
||||
*
|
||||
* return greenlet.parent;
|
||||
*
|
||||
* This could return NULL even if there is no exception active.
|
||||
* If it does not return NULL, you are responsible for decrementing the
|
||||
* reference count.
|
||||
*/
|
||||
# define PyGreenlet_GetParent \
|
||||
(*(PyGreenlet* (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
|
||||
|
||||
/*
|
||||
* deprecated, undocumented alias.
|
||||
*/
|
||||
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
|
||||
|
||||
# define PyGreenlet_MAIN \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
|
||||
|
||||
# define PyGreenlet_STARTED \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
|
||||
|
||||
# define PyGreenlet_ACTIVE \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
|
||||
|
||||
|
||||
|
||||
|
||||
/* Macro that imports greenlet and initializes C API */
|
||||
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
|
||||
keep the older definition to be sure older code that might have a copy of
|
||||
the header still works. */
|
||||
# define PyGreenlet_Import() \
|
||||
{ \
|
||||
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
|
||||
}
|
||||
|
||||
#endif /* GREENLET_MODULE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !Py_GREENLETOBJECT_H */
|
|
@ -1 +0,0 @@
|
|||
pip
|
|
@ -1,20 +0,0 @@
|
|||
Copyright (c) 2017-2021 Ingy döt Net
|
||||
Copyright (c) 2006-2016 Kirill Simonov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,46 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: PyYAML
|
||||
Version: 6.0.1
|
||||
Summary: YAML parser and emitter for Python
|
||||
Home-page: https://pyyaml.org/
|
||||
Download-URL: https://pypi.org/project/PyYAML/
|
||||
Author: Kirill Simonov
|
||||
Author-email: xi@resolvent.net
|
||||
License: MIT
|
||||
Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues
|
||||
Project-URL: CI, https://github.com/yaml/pyyaml/actions
|
||||
Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation
|
||||
Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core
|
||||
Project-URL: Source Code, https://github.com/yaml/pyyaml
|
||||
Platform: Any
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Cython
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Text Processing :: Markup
|
||||
Requires-Python: >=3.6
|
||||
License-File: LICENSE
|
||||
|
||||
YAML is a data serialization format designed for human readability
|
||||
and interaction with scripting languages. PyYAML is a YAML parser
|
||||
and emitter for Python.
|
||||
|
||||
PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
|
||||
support, capable extension API, and sensible error messages. PyYAML
|
||||
supports standard YAML tags and provides Python-specific tags that
|
||||
allow to represent an arbitrary Python object.
|
||||
|
||||
PyYAML is applicable for a broad range of tasks from complex
|
||||
configuration files to object serialization and persistence.
|
|
@ -1,44 +0,0 @@
|
|||
PyYAML-6.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
PyYAML-6.0.1.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
|
||||
PyYAML-6.0.1.dist-info/METADATA,sha256=UNNF8-SzzwOKXVo-kV5lXUGH2_wDWMBmGxqISpp5HQk,2058
|
||||
PyYAML-6.0.1.dist-info/RECORD,,
|
||||
PyYAML-6.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
PyYAML-6.0.1.dist-info/WHEEL,sha256=8KU227XctfdX2qUwyjQUO-ciQuZtmyPUCKmeGV6Byto,152
|
||||
PyYAML-6.0.1.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11
|
||||
_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402
|
||||
_yaml/__pycache__/__init__.cpython-311.pyc,,
|
||||
yaml/__init__.py,sha256=bhl05qSeO-1ZxlSRjGrvl2m9nrXb1n9-GQatTN0Mrqc,12311
|
||||
yaml/__pycache__/__init__.cpython-311.pyc,,
|
||||
yaml/__pycache__/composer.cpython-311.pyc,,
|
||||
yaml/__pycache__/constructor.cpython-311.pyc,,
|
||||
yaml/__pycache__/cyaml.cpython-311.pyc,,
|
||||
yaml/__pycache__/dumper.cpython-311.pyc,,
|
||||
yaml/__pycache__/emitter.cpython-311.pyc,,
|
||||
yaml/__pycache__/error.cpython-311.pyc,,
|
||||
yaml/__pycache__/events.cpython-311.pyc,,
|
||||
yaml/__pycache__/loader.cpython-311.pyc,,
|
||||
yaml/__pycache__/nodes.cpython-311.pyc,,
|
||||
yaml/__pycache__/parser.cpython-311.pyc,,
|
||||
yaml/__pycache__/reader.cpython-311.pyc,,
|
||||
yaml/__pycache__/representer.cpython-311.pyc,,
|
||||
yaml/__pycache__/resolver.cpython-311.pyc,,
|
||||
yaml/__pycache__/scanner.cpython-311.pyc,,
|
||||
yaml/__pycache__/serializer.cpython-311.pyc,,
|
||||
yaml/__pycache__/tokens.cpython-311.pyc,,
|
||||
yaml/_yaml.cpython-311-x86_64-linux-gnu.so,sha256=ls52EONnCPWCytU6wojl6RE4BhAUdu8LH3XIYfgpH0k,2504120
|
||||
yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883
|
||||
yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639
|
||||
yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851
|
||||
yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837
|
||||
yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006
|
||||
yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533
|
||||
yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445
|
||||
yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061
|
||||
yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440
|
||||
yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495
|
||||
yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794
|
||||
yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190
|
||||
yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004
|
||||
yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279
|
||||
yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165
|
||||
yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573
|
|
@ -1,6 +0,0 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.40.0)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp311-cp311-manylinux_2_17_x86_64
|
||||
Tag: cp311-cp311-manylinux2014_x86_64
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
_yaml
|
||||
yaml
|
|
@ -1 +0,0 @@
|
|||
pip
|
|
@ -1,19 +0,0 @@
|
|||
Copyright 2005-2024 SQLAlchemy authors and contributors <see AUTHORS file>.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,242 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: SQLAlchemy
|
||||
Version: 2.0.27
|
||||
Summary: Database Abstraction Library
|
||||
Home-page: https://www.sqlalchemy.org
|
||||
Author: Mike Bayer
|
||||
Author-email: mike_mp@zzzcomputing.com
|
||||
License: MIT
|
||||
Project-URL: Documentation, https://docs.sqlalchemy.org
|
||||
Project-URL: Issue Tracker, https://github.com/sqlalchemy/sqlalchemy/
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Topic :: Database :: Front-Ends
|
||||
Requires-Python: >=3.7
|
||||
Description-Content-Type: text/x-rst
|
||||
License-File: LICENSE
|
||||
Requires-Dist: typing-extensions >=4.6.0
|
||||
Requires-Dist: greenlet !=0.4.17 ; platform_machine == "aarch64" or (platform_machine == "ppc64le" or (platform_machine == "x86_64" or (platform_machine == "amd64" or (platform_machine == "AMD64" or (platform_machine == "win32" or platform_machine == "WIN32")))))
|
||||
Requires-Dist: importlib-metadata ; python_version < "3.8"
|
||||
Provides-Extra: aiomysql
|
||||
Requires-Dist: greenlet !=0.4.17 ; extra == 'aiomysql'
|
||||
Requires-Dist: aiomysql >=0.2.0 ; extra == 'aiomysql'
|
||||
Provides-Extra: aioodbc
|
||||
Requires-Dist: greenlet !=0.4.17 ; extra == 'aioodbc'
|
||||
Requires-Dist: aioodbc ; extra == 'aioodbc'
|
||||
Provides-Extra: aiosqlite
|
||||
Requires-Dist: greenlet !=0.4.17 ; extra == 'aiosqlite'
|
||||
Requires-Dist: aiosqlite ; extra == 'aiosqlite'
|
||||
Requires-Dist: typing-extensions !=3.10.0.1 ; extra == 'aiosqlite'
|
||||
Provides-Extra: asyncio
|
||||
Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncio'
|
||||
Provides-Extra: asyncmy
|
||||
Requires-Dist: greenlet !=0.4.17 ; extra == 'asyncmy'
|
||||
Requires-Dist: asyncmy !=0.2.4,!=0.2.6,>=0.2.3 ; extra == 'asyncmy'
|
||||
Provides-Extra: mariadb_connector
|
||||
Requires-Dist: mariadb !=1.1.2,!=1.1.5,>=1.0.1 ; extra == 'mariadb_connector'
|
||||
Provides-Extra: mssql
|
||||
Requires-Dist: pyodbc ; extra == 'mssql'
|
||||
Provides-Extra: mssql_pymssql
|
||||
Requires-Dist: pymssql ; extra == 'mssql_pymssql'
|
||||
Provides-Extra: mssql_pyodbc
|
||||
Requires-Dist: pyodbc ; extra == 'mssql_pyodbc'
|
||||
Provides-Extra: mypy
|
||||
Requires-Dist: mypy >=0.910 ; extra == 'mypy'
|
||||
Provides-Extra: mysql
|
||||
Requires-Dist: mysqlclient >=1.4.0 ; extra == 'mysql'
|
||||
Provides-Extra: mysql_connector
|
||||
Requires-Dist: mysql-connector-python ; extra == 'mysql_connector'
|
||||
Provides-Extra: oracle
|
||||
Requires-Dist: cx-oracle >=8 ; extra == 'oracle'
|
||||
Provides-Extra: oracle_oracledb
|
||||
Requires-Dist: oracledb >=1.0.1 ; extra == 'oracle_oracledb'
|
||||
Provides-Extra: postgresql
|
||||
Requires-Dist: psycopg2 >=2.7 ; extra == 'postgresql'
|
||||
Provides-Extra: postgresql_asyncpg
|
||||
Requires-Dist: greenlet !=0.4.17 ; extra == 'postgresql_asyncpg'
|
||||
Requires-Dist: asyncpg ; extra == 'postgresql_asyncpg'
|
||||
Provides-Extra: postgresql_pg8000
|
||||
Requires-Dist: pg8000 >=1.29.1 ; extra == 'postgresql_pg8000'
|
||||
Provides-Extra: postgresql_psycopg
|
||||
Requires-Dist: psycopg >=3.0.7 ; extra == 'postgresql_psycopg'
|
||||
Provides-Extra: postgresql_psycopg2binary
|
||||
Requires-Dist: psycopg2-binary ; extra == 'postgresql_psycopg2binary'
|
||||
Provides-Extra: postgresql_psycopg2cffi
|
||||
Requires-Dist: psycopg2cffi ; extra == 'postgresql_psycopg2cffi'
|
||||
Provides-Extra: postgresql_psycopgbinary
|
||||
Requires-Dist: psycopg[binary] >=3.0.7 ; extra == 'postgresql_psycopgbinary'
|
||||
Provides-Extra: pymysql
|
||||
Requires-Dist: pymysql ; extra == 'pymysql'
|
||||
Provides-Extra: sqlcipher
|
||||
Requires-Dist: sqlcipher3-binary ; extra == 'sqlcipher'
|
||||
|
||||
SQLAlchemy
|
||||
==========
|
||||
|
||||
|PyPI| |Python| |Downloads|
|
||||
|
||||
.. |PyPI| image:: https://img.shields.io/pypi/v/sqlalchemy
|
||||
:target: https://pypi.org/project/sqlalchemy
|
||||
:alt: PyPI
|
||||
|
||||
.. |Python| image:: https://img.shields.io/pypi/pyversions/sqlalchemy
|
||||
:target: https://pypi.org/project/sqlalchemy
|
||||
:alt: PyPI - Python Version
|
||||
|
||||
.. |Downloads| image:: https://static.pepy.tech/badge/sqlalchemy/month
|
||||
:target: https://pepy.tech/project/sqlalchemy
|
||||
:alt: PyPI - Downloads
|
||||
|
||||
|
||||
The Python SQL Toolkit and Object Relational Mapper
|
||||
|
||||
Introduction
|
||||
-------------
|
||||
|
||||
SQLAlchemy is the Python SQL toolkit and Object Relational Mapper
|
||||
that gives application developers the full power and
|
||||
flexibility of SQL. SQLAlchemy provides a full suite
|
||||
of well known enterprise-level persistence patterns,
|
||||
designed for efficient and high-performing database
|
||||
access, adapted into a simple and Pythonic domain
|
||||
language.
|
||||
|
||||
Major SQLAlchemy features include:
|
||||
|
||||
* An industrial strength ORM, built
|
||||
from the core on the identity map, unit of work,
|
||||
and data mapper patterns. These patterns
|
||||
allow transparent persistence of objects
|
||||
using a declarative configuration system.
|
||||
Domain models
|
||||
can be constructed and manipulated naturally,
|
||||
and changes are synchronized with the
|
||||
current transaction automatically.
|
||||
* A relationally-oriented query system, exposing
|
||||
the full range of SQL's capabilities
|
||||
explicitly, including joins, subqueries,
|
||||
correlation, and most everything else,
|
||||
in terms of the object model.
|
||||
Writing queries with the ORM uses the same
|
||||
techniques of relational composition you use
|
||||
when writing SQL. While you can drop into
|
||||
literal SQL at any time, it's virtually never
|
||||
needed.
|
||||
* A comprehensive and flexible system
|
||||
of eager loading for related collections and objects.
|
||||
Collections are cached within a session,
|
||||
and can be loaded on individual access, all
|
||||
at once using joins, or by query per collection
|
||||
across the full result set.
|
||||
* A Core SQL construction system and DBAPI
|
||||
interaction layer. The SQLAlchemy Core is
|
||||
separate from the ORM and is a full database
|
||||
abstraction layer in its own right, and includes
|
||||
an extensible Python-based SQL expression
|
||||
language, schema metadata, connection pooling,
|
||||
type coercion, and custom types.
|
||||
* All primary and foreign key constraints are
|
||||
assumed to be composite and natural. Surrogate
|
||||
integer primary keys are of course still the
|
||||
norm, but SQLAlchemy never assumes or hardcodes
|
||||
to this model.
|
||||
* Database introspection and generation. Database
|
||||
schemas can be "reflected" in one step into
|
||||
Python structures representing database metadata;
|
||||
those same structures can then generate
|
||||
CREATE statements right back out - all within
|
||||
the Core, independent of the ORM.
|
||||
|
||||
SQLAlchemy's philosophy:
|
||||
|
||||
* SQL databases behave less and less like object
|
||||
collections the more size and performance start to
|
||||
matter; object collections behave less and less like
|
||||
tables and rows the more abstraction starts to matter.
|
||||
SQLAlchemy aims to accommodate both of these
|
||||
principles.
|
||||
* An ORM doesn't need to hide the "R". A relational
|
||||
database provides rich, set-based functionality
|
||||
that should be fully exposed. SQLAlchemy's
|
||||
ORM provides an open-ended set of patterns
|
||||
that allow a developer to construct a custom
|
||||
mediation layer between a domain model and
|
||||
a relational schema, turning the so-called
|
||||
"object relational impedance" issue into
|
||||
a distant memory.
|
||||
* The developer, in all cases, makes all decisions
|
||||
regarding the design, structure, and naming conventions
|
||||
of both the object model as well as the relational
|
||||
schema. SQLAlchemy only provides the means
|
||||
to automate the execution of these decisions.
|
||||
* With SQLAlchemy, there's no such thing as
|
||||
"the ORM generated a bad query" - you
|
||||
retain full control over the structure of
|
||||
queries, including how joins are organized,
|
||||
how subqueries and correlation is used, what
|
||||
columns are requested. Everything SQLAlchemy
|
||||
does is ultimately the result of a developer-initiated
|
||||
decision.
|
||||
* Don't use an ORM if the problem doesn't need one.
|
||||
SQLAlchemy consists of a Core and separate ORM
|
||||
component. The Core offers a full SQL expression
|
||||
language that allows Pythonic construction
|
||||
of SQL constructs that render directly to SQL
|
||||
strings for a target database, returning
|
||||
result sets that are essentially enhanced DBAPI
|
||||
cursors.
|
||||
* Transactions should be the norm. With SQLAlchemy's
|
||||
ORM, nothing goes to permanent storage until
|
||||
commit() is called. SQLAlchemy encourages applications
|
||||
to create a consistent means of delineating
|
||||
the start and end of a series of operations.
|
||||
* Never render a literal value in a SQL statement.
|
||||
Bound parameters are used to the greatest degree
|
||||
possible, allowing query optimizers to cache
|
||||
query plans effectively and making SQL injection
|
||||
attacks a non-issue.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Latest documentation is at:
|
||||
|
||||
https://www.sqlalchemy.org/docs/
|
||||
|
||||
Installation / Requirements
|
||||
---------------------------
|
||||
|
||||
Full documentation for installation is at
|
||||
`Installation <https://www.sqlalchemy.org/docs/intro.html#installation>`_.
|
||||
|
||||
Getting Help / Development / Bug reporting
|
||||
------------------------------------------
|
||||
|
||||
Please refer to the `SQLAlchemy Community Guide <https://www.sqlalchemy.org/support.html>`_.
|
||||
|
||||
Code of Conduct
|
||||
---------------
|
||||
|
||||
Above all, SQLAlchemy places great emphasis on polite, thoughtful, and
|
||||
constructive communication between users and developers.
|
||||
Please see our current Code of Conduct at
|
||||
`Code of Conduct <https://www.sqlalchemy.org/codeofconduct.html>`_.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
SQLAlchemy is distributed under the `MIT license
|
||||
<https://www.opensource.org/licenses/mit-license.php>`_.
|
||||
|
|
@ -1,530 +0,0 @@
|
|||
SQLAlchemy-2.0.27.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
SQLAlchemy-2.0.27.dist-info/LICENSE,sha256=PA9Zq4h9BB3mpOUv_j6e212VIt6Qn66abNettue-MpM,1100
|
||||
SQLAlchemy-2.0.27.dist-info/METADATA,sha256=fZGrNxgSqoY_vLjP6pXy7Ax_9Fvpy6P0SN_Qmjpaf8M,9602
|
||||
SQLAlchemy-2.0.27.dist-info/RECORD,,
|
||||
SQLAlchemy-2.0.27.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
SQLAlchemy-2.0.27.dist-info/WHEEL,sha256=AI1yqBLEPcVKWn5Ls2uPawjbqPXPFTYdQLSdN8WFCJw,152
|
||||
SQLAlchemy-2.0.27.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11
|
||||
sqlalchemy/__init__.py,sha256=s94qQVe-QqqRL9xhlih382KZikm_5rCLehCmTVeMkxo,13033
|
||||
sqlalchemy/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/__pycache__/events.cpython-311.pyc,,
|
||||
sqlalchemy/__pycache__/exc.cpython-311.pyc,,
|
||||
sqlalchemy/__pycache__/inspection.cpython-311.pyc,,
|
||||
sqlalchemy/__pycache__/log.cpython-311.pyc,,
|
||||
sqlalchemy/__pycache__/schema.cpython-311.pyc,,
|
||||
sqlalchemy/__pycache__/types.cpython-311.pyc,,
|
||||
sqlalchemy/connectors/__init__.py,sha256=PzXPqZqi3BzEnrs1eW0DcsR4lyknAzhhN9rWcQ97hb4,476
|
||||
sqlalchemy/connectors/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/connectors/__pycache__/aioodbc.cpython-311.pyc,,
|
||||
sqlalchemy/connectors/__pycache__/asyncio.cpython-311.pyc,,
|
||||
sqlalchemy/connectors/__pycache__/pyodbc.cpython-311.pyc,,
|
||||
sqlalchemy/connectors/aioodbc.py,sha256=GSTiNMO9h0qjPxgqaxDwWZ8HvhWMFNVR6MJQnN1oc40,5288
|
||||
sqlalchemy/connectors/asyncio.py,sha256=6s4hDYfuMjJ9KbJ4s7bF1fp5DmcgV77ozgZ5-bwZ0wc,5955
|
||||
sqlalchemy/connectors/pyodbc.py,sha256=t7AjyxIOnaWg3CrlUEpBs4Y5l0HFdNt3P_cSSKhbi0Y,8501
|
||||
sqlalchemy/cyextension/__init__.py,sha256=GzhhN8cjMnDTE0qerlUlpbrNmFPHQWCZ4Gk74OAxl04,244
|
||||
sqlalchemy/cyextension/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/cyextension/collections.cpython-311-x86_64-linux-gnu.so,sha256=mMzAZiy3Z3NaEHsP_Ad-IvLCkvjbOZQCRwvS9PryJF4,2019496
|
||||
sqlalchemy/cyextension/collections.pyx,sha256=L7DZ3DGKpgw2MT2ZZRRxCnrcyE5pU1NAFowWgAzQPEc,12571
|
||||
sqlalchemy/cyextension/immutabledict.cpython-311-x86_64-linux-gnu.so,sha256=61zeVLfZ5ylDwnXZE8stoYcWJ1EcqMFiZid-L6Ihpss,703720
|
||||
sqlalchemy/cyextension/immutabledict.pxd,sha256=3x3-rXG5eRQ7bBnktZ-OJ9-6ft8zToPmTDOd92iXpB0,291
|
||||
sqlalchemy/cyextension/immutabledict.pyx,sha256=KfDTYbTfebstE8xuqAtuXsHNAK0_b5q_ymUiinUe_xs,3535
|
||||
sqlalchemy/cyextension/processors.cpython-311-x86_64-linux-gnu.so,sha256=1kmZ0XQH06gEr1jQNF3PGAhFFd_5K4N4IAzyNTvce78,509544
|
||||
sqlalchemy/cyextension/processors.pyx,sha256=R1rHsGLEaGeBq5VeCydjClzYlivERIJ9B-XLOJlf2MQ,1792
|
||||
sqlalchemy/cyextension/resultproxy.cpython-311-x86_64-linux-gnu.so,sha256=EZDMIii55WxmoXp6I2QV0zAOaFYVQuglOfTZtTxUxCU,586752
|
||||
sqlalchemy/cyextension/resultproxy.pyx,sha256=eWLdyBXiBy_CLQrF5ScfWJm7X0NeelscSXedtj1zv9Q,2725
|
||||
sqlalchemy/cyextension/util.cpython-311-x86_64-linux-gnu.so,sha256=ArvZ5z3oRLSMODlTRYQ4LrPMyyBpf3SJ5py9jD_0QII,870136
|
||||
sqlalchemy/cyextension/util.pyx,sha256=B85orxa9LddLuQEaDoVSq1XmAXIbLKxrxpvuB8ogV_o,2530
|
||||
sqlalchemy/dialects/__init__.py,sha256=Kos9Gf5JZg1Vg6GWaCqEbD6e0r1jCwCmcnJIfcxDdcY,1770
|
||||
sqlalchemy/dialects/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/__pycache__/_typing.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/_typing.py,sha256=hyv0nKucX2gI8ispB1IsvaUgrEPn9zEcq9hS7kfstEw,888
|
||||
sqlalchemy/dialects/mssql/__init__.py,sha256=r5t8wFRNtBQoiUWh0WfIEWzXZW6f3D0uDt6NZTW_7Cc,1880
|
||||
sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mssql/__pycache__/aioodbc.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mssql/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mssql/__pycache__/json.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mssql/__pycache__/provision.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mssql/aioodbc.py,sha256=UQd9ecSMIML713TDnLAviuBVJle7P7i1FtqGZZePk2Y,2022
|
||||
sqlalchemy/dialects/mssql/base.py,sha256=2Tx9sC5bOd0JbweaMaqnjGOgESur7ZaaN1S66KMTwHk,133298
|
||||
sqlalchemy/dialects/mssql/information_schema.py,sha256=HswjDc6y0mPXCf_x6VyylHlBdBa4PSY6Evxmmlch700,8084
|
||||
sqlalchemy/dialects/mssql/json.py,sha256=evUACW2O62TAPq8B7QIPagz7jfc664ql9ms68JqiYzg,4816
|
||||
sqlalchemy/dialects/mssql/provision.py,sha256=RTVbgYLFAHzEnpVQDJroU8ji_10MqBTiZfyP9_-QNT4,5362
|
||||
sqlalchemy/dialects/mssql/pymssql.py,sha256=eZRLz7HGt3SdoZUjFBmA9BS43N7AhIASw7VPBPEJuG0,4038
|
||||
sqlalchemy/dialects/mssql/pyodbc.py,sha256=vwM-vBlmRwrqxOc73P0sFOrBSwn24wzc5IkEOpalbXQ,27056
|
||||
sqlalchemy/dialects/mysql/__init__.py,sha256=bxbi4hkysUK2OOVvr1F49akUj1cky27kKb07tgFzI9U,2153
|
||||
sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/dml.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/expression.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/json.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/provision.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/__pycache__/types.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/mysql/aiomysql.py,sha256=67JrSUD1BmN88k_ASk6GvrttZFQiFjDY0wBiwdllxMk,9964
|
||||
sqlalchemy/dialects/mysql/asyncmy.py,sha256=CGILIRKf_2Ut9Ng2yBlmdg62laL-ockEm6GMuN7xlKE,10033
|
||||
sqlalchemy/dialects/mysql/base.py,sha256=KA7tvRxKUw0KwHwMth2rz-NWV0xMkVbYvPoBM9wrAFw,120850
|
||||
sqlalchemy/dialects/mysql/cymysql.py,sha256=eXT1ry0w_qRxjiO24M980c-8PZ9qSsbhqBHntjEiKB0,2300
|
||||
sqlalchemy/dialects/mysql/dml.py,sha256=HXJMAvimJsqvhj3UZO4vW_6LkF5RqaKbHvklAjor7yU,7645
|
||||
sqlalchemy/dialects/mysql/enumerated.py,sha256=ipEPPQqoXfFwcywNdcLlZCEzHBtnitHRah1Gn6nItcg,8448
|
||||
sqlalchemy/dialects/mysql/expression.py,sha256=lsmQCHKwfPezUnt27d2kR6ohk4IRFCA64KBS16kx5dc,4097
|
||||
sqlalchemy/dialects/mysql/json.py,sha256=l6MEZ0qp8FgiRrIQvOMhyEJq0q6OqiEnvDTx5Cbt9uQ,2269
|
||||
sqlalchemy/dialects/mysql/mariadb.py,sha256=kTfBLioLKk4JFFst4TY_iWqPtnvvQXFHknLfm89H2N8,853
|
||||
sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=VVRwKLb6GzDmitOM4wLNvmZw6RdhnIwkLl7IZfAmUy8,8734
|
||||
sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=qiQdfLPze3QHuASAZ9iqRzD0hDW8FbKoQnfAEQCF7tM,5675
|
||||
sqlalchemy/dialects/mysql/mysqldb.py,sha256=9x_JiY4hj4tykG1ckuEGPyH4jCtsh4fgBhNukVnjUos,9658
|
||||
sqlalchemy/dialects/mysql/provision.py,sha256=4oGkClQ8jC3YLPF54sB4kCjFc8HRTwf5zl5zftAAXGo,3474
|
||||
sqlalchemy/dialects/mysql/pymysql.py,sha256=GUnSHd2M2uKjmN46Hheymtm26g7phEgwYOXrX0zLY8M,4083
|
||||
sqlalchemy/dialects/mysql/pyodbc.py,sha256=072crI4qVyPhajYvHnsfFeSrNjLFVPIjBQKo5uyz5yk,4297
|
||||
sqlalchemy/dialects/mysql/reflection.py,sha256=XXM8AGpaRTqDvuObg89Bzn_4h2ETG03viYBpWZJM3vc,22822
|
||||
sqlalchemy/dialects/mysql/reserved_words.py,sha256=Dm7FINIAkrKLoXmdu26SpE6V8LDCGyp734nmHV2tMd0,9154
|
||||
sqlalchemy/dialects/mysql/types.py,sha256=aPzx7hqqZ21aGwByEC-yWZUl6OpMvkbxwTqdN3OUGGI,24267
|
||||
sqlalchemy/dialects/oracle/__init__.py,sha256=p4-2gw7TT0bX_MoJXTGD4i8WHctYsK9kCRbkpzykBrc,1493
|
||||
sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/oracle/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/oracle/__pycache__/dictionary.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/oracle/__pycache__/oracledb.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/oracle/__pycache__/provision.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/oracle/__pycache__/types.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/oracle/base.py,sha256=-7b5iubFPxJyDRoLXlxj8rk8eBRN2_IdZlB2zzzrrbw,118246
|
||||
sqlalchemy/dialects/oracle/cx_oracle.py,sha256=t5yH4svVz7xoDSITF958blgZ01hbCUEWUKrAXwiCiAE,55566
|
||||
sqlalchemy/dialects/oracle/dictionary.py,sha256=7WMrbPkqo8ZdGjaEZyQr-5f2pajSOF1OTGb8P97z8-g,19519
|
||||
sqlalchemy/dialects/oracle/oracledb.py,sha256=UFcZwrrk0pWfAp_SKJZ1B5rIQHtNhOvuu73_JaSnTbI,9487
|
||||
sqlalchemy/dialects/oracle/provision.py,sha256=O9ZpF4OG6Cx4mMzLRfZwhs8dZjrJETWR402n9c7726A,8304
|
||||
sqlalchemy/dialects/oracle/types.py,sha256=QK3hJvWzKnnCe3oD3rItwEEIwcoBze8qGg7VFOvVlIk,8231
|
||||
sqlalchemy/dialects/postgresql/__init__.py,sha256=kwgzMhtZKDHD12HMGo5MtdKCnDdy6wLezDGZPOEoU3Q,3895
|
||||
sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/_psycopg_common.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/array.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/json.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/named_types.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/operators.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/pg_catalog.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/psycopg.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/__pycache__/types.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/postgresql/_psycopg_common.py,sha256=7TudtgsPiSB8O5kX8W8KxcNYR8t5h_UHb86b_ChL0P8,5696
|
||||
sqlalchemy/dialects/postgresql/array.py,sha256=9dJ_1WjWSBX1-MGDZtJACJ38vtRO3da7d4UId79WsnQ,13713
|
||||
sqlalchemy/dialects/postgresql/asyncpg.py,sha256=12DN8hlK-Na_bEFmQ5kXK7MRqu87ze2IMX8aDyiSddU,40183
|
||||
sqlalchemy/dialects/postgresql/base.py,sha256=ogY8rcQvT9jjYdtStxZ_nhTl8LmhB_zeJyhZIaUyMLk,176486
|
||||
sqlalchemy/dialects/postgresql/dml.py,sha256=Pc69Le6qzmUHHb1FT5zeUSD31dWm6SBgdCAGW89cs3s,11212
|
||||
sqlalchemy/dialects/postgresql/ext.py,sha256=1bZ--iNh2O9ym7l2gXZX48yP3yMO4dqb9RpYro2Mj2Q,16262
|
||||
sqlalchemy/dialects/postgresql/hstore.py,sha256=otAx-RTDfpi_tcXkMuQV0JOIXtYgevgnsikLKKOkI6U,11541
|
||||
sqlalchemy/dialects/postgresql/json.py,sha256=-ffnp85fQBOyt0Bjb7XAupmOxloUdzFZZgixUG3Wj5w,11212
|
||||
sqlalchemy/dialects/postgresql/named_types.py,sha256=SFhs9_l108errKNuAMPl761RQ2imTO9PbUAnSv-WtRg,17100
|
||||
sqlalchemy/dialects/postgresql/operators.py,sha256=NsAaWun_tL3d_be0fs9YL6T4LPKK6crnmFxxIJHgyeY,2808
|
||||
sqlalchemy/dialects/postgresql/pg8000.py,sha256=3yoekiWSF-xnaWMqG76XrYPMqerg-42TdmfsW_ivK9E,18640
|
||||
sqlalchemy/dialects/postgresql/pg_catalog.py,sha256=nAKavWTE_4cqxiDKDTdo-ivkCxxRIlzD5GO9Wl1yrG4,8884
|
||||
sqlalchemy/dialects/postgresql/provision.py,sha256=yqyx-aDFO9l2YcL9f4T5HBP_Lnt5dHsMjpuXUG8mi7A,5762
|
||||
sqlalchemy/dialects/postgresql/psycopg.py,sha256=TF53axr1EkTBAZD85JCq6wA7XTcJTzXueSz26txDbgc,22364
|
||||
sqlalchemy/dialects/postgresql/psycopg2.py,sha256=gAP3poHDUxEB6iut6sxe9PhBiOrV_iIMvnP0NUlC-Rw,31607
|
||||
sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=M7wAYSL6Pvt-4nbfacAHGyyw4XMKJ_bQZ1tc1pBtIdg,1756
|
||||
sqlalchemy/dialects/postgresql/ranges.py,sha256=6CgV7qkxEMJ9AQsiibo_XBLJYzGh-2ZxpG83sRaesVY,32949
|
||||
sqlalchemy/dialects/postgresql/types.py,sha256=Jfxqw9JaKNOq29JRWBublywgb3lLMyzx8YZI7CXpS2s,7300
|
||||
sqlalchemy/dialects/sqlite/__init__.py,sha256=lp9DIggNn349M-7IYhUA8et8--e8FRExWD2V_r1LJk4,1182
|
||||
sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/sqlite/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/sqlite/__pycache__/json.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-311.pyc,,
|
||||
sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=OMvxP2eWyqk5beF-sHhzxRmjzO4VCQp55q7NH2XPVTE,12305
|
||||
sqlalchemy/dialects/sqlite/base.py,sha256=lUtigjn7NdPBq831zQsLcBwdwRJqdgKM_tUaDrMElOE,96794
|
||||
sqlalchemy/dialects/sqlite/dml.py,sha256=9GE55WvwoktKy2fHeT-Wbc9xPHgsbh5oBfd_fckMH5Q,8443
|
||||
sqlalchemy/dialects/sqlite/json.py,sha256=Eoplbb_4dYlfrtmQaI8Xddd2suAIHA-IdbDQYM-LIhs,2777
|
||||
sqlalchemy/dialects/sqlite/provision.py,sha256=UCpmwxf4IWlrpb2eLHGbPTpCFVbdI_KAh2mKtjiLYao,5632
|
||||
sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=OL2S_05DK9kllZj6DOz7QtEl7jI7syxjW6woS725ii4,5356
|
||||
sqlalchemy/dialects/sqlite/pysqlite.py,sha256=TAOqsHIjhbUZOF_Qk7UooiekkVZNhYJNduxlGQjokeA,27900
|
||||
sqlalchemy/dialects/type_migration_guidelines.txt,sha256=-uHNdmYFGB7bzUNT6i8M5nb4j6j9YUKAtW4lcBZqsMg,8239
|
||||
sqlalchemy/engine/__init__.py,sha256=Stb2oV6l8w65JvqEo6J4qtKoApcmOpXy3AAxQud4C1o,2818
|
||||
sqlalchemy/engine/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/_py_processors.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/_py_row.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/_py_util.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/characteristics.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/create.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/cursor.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/default.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/events.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/interfaces.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/mock.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/processors.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/reflection.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/result.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/row.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/strategies.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/url.cpython-311.pyc,,
|
||||
sqlalchemy/engine/__pycache__/util.cpython-311.pyc,,
|
||||
sqlalchemy/engine/_py_processors.py,sha256=j9i_lcYYQOYJMcsDerPxI0sVFBIlX5sqoYMdMJlgWPI,3744
|
||||
sqlalchemy/engine/_py_row.py,sha256=wSqoUFzLOJ1f89kgDb6sJm9LUrF5LMFpXPcK1vUsKcs,3787
|
||||
sqlalchemy/engine/_py_util.py,sha256=f2DI3AN1kv6EplelowesCVpwS8hSXNufRkZoQmJtSH8,2484
|
||||
sqlalchemy/engine/base.py,sha256=NGD1iokXsJBw_6sBOpX4STo_05fQFd52qUl1YiJZsdU,122038
|
||||
sqlalchemy/engine/characteristics.py,sha256=Qbvt4CPrggJ3GfxHl0hOAxopjnCQy-W_pjtwLIe-Q1g,2590
|
||||
sqlalchemy/engine/create.py,sha256=5Me7rgLvmZVJM6QzoH8aBHz0lIratA2vXN8cW6kUgdY,32872
|
||||
sqlalchemy/engine/cursor.py,sha256=jSjpGM5DiwX1pwEHGx3wyqgHrgj8rwU5ZpVvMv5GaJs,74443
|
||||
sqlalchemy/engine/default.py,sha256=VSqSm-juosz-5WqZPWjgDQf8Fra27M-YsrVVcs7RwPU,84672
|
||||
sqlalchemy/engine/events.py,sha256=c0unNFFiHzTAvkUtXoJaxzMFMDwurBkHiiUhuN8qluc,37381
|
||||
sqlalchemy/engine/interfaces.py,sha256=gktNzgLjNK-KrYMU__Lk0h85SXQI8LCjDakkuLxagNE,112688
|
||||
sqlalchemy/engine/mock.py,sha256=yvpxgFmRw5G4QsHeF-ZwQGHKES-HqQOucTxFtN1uzdk,4179
|
||||
sqlalchemy/engine/processors.py,sha256=XyfINKbo-2fjN-mW55YybvFyQMOil50_kVqsunahkNs,2379
|
||||
sqlalchemy/engine/reflection.py,sha256=FlT5kPpKm7Lah50GNt5XcnlJWojTL3LD_x0SoCF9kfY,75127
|
||||
sqlalchemy/engine/result.py,sha256=j6BI4Wj2bziQNQG5OlG_Cm4KcNWY9AoYvTXVlJUU-D8,77603
|
||||
sqlalchemy/engine/row.py,sha256=9AAQo9zYDL88GcZ3bjcQTwMT-YIcuGTSMAyTfmBJ_yM,12032
|
||||
sqlalchemy/engine/strategies.py,sha256=DqFSWaXJPL-29Omot9O0aOcuGL8KmCGyOvnPGDkAJoE,442
|
||||
sqlalchemy/engine/url.py,sha256=8eWkUaIUyDExOcJ2D4xJXRcn4OY1GQJ3Q2duSX6UGAg,30784
|
||||
sqlalchemy/engine/util.py,sha256=hkEql1t19WHl6uzR55-F-Fs_VMCJ7p02KKQVNUDSXTk,5667
|
||||
sqlalchemy/event/__init__.py,sha256=KBrp622xojnC3FFquxa2JsMamwAbfkvzfv6Op0NKiYc,997
|
||||
sqlalchemy/event/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/event/__pycache__/api.cpython-311.pyc,,
|
||||
sqlalchemy/event/__pycache__/attr.cpython-311.pyc,,
|
||||
sqlalchemy/event/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/event/__pycache__/legacy.cpython-311.pyc,,
|
||||
sqlalchemy/event/__pycache__/registry.cpython-311.pyc,,
|
||||
sqlalchemy/event/api.py,sha256=BUTAZjSlzvq4Hn2v2pihP_P1yo3lvCVDczK8lV_XJ80,8227
|
||||
sqlalchemy/event/attr.py,sha256=X8QeHGK4ioSYht1vkhc11f606_mq_t91jMNIT314ubs,20751
|
||||
sqlalchemy/event/base.py,sha256=3n9FmUkcXYHHyGzfpjKDsrIUVCNST_hq4zOtrNm0_a4,14954
|
||||
sqlalchemy/event/legacy.py,sha256=teMPs00fO-4g8a_z2omcVKkYce5wj_1uvJO2n2MIeuo,8227
|
||||
sqlalchemy/event/registry.py,sha256=nfTSSyhjZZXc5wseWB4sXn-YibSc0LKX8mg17XlWmAo,10835
|
||||
sqlalchemy/events.py,sha256=k-ZD38aSPD29LYhED7CBqttp5MDVVx_YSaWC2-cu9ec,525
|
||||
sqlalchemy/exc.py,sha256=M_8-O1hd8i6gbyx-TapV400p_Lxq2QqTGMXUAO-YgCc,23976
|
||||
sqlalchemy/ext/__init__.py,sha256=S1fGKAbycnQDV01gs-JWGaFQ9GCD4QHwKcU2wnugg_o,322
|
||||
sqlalchemy/ext/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/associationproxy.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/automap.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/baked.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/compiler.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/horizontal_shard.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/hybrid.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/indexable.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/instrumentation.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/mutable.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/orderinglist.cpython-311.pyc,,
|
||||
sqlalchemy/ext/__pycache__/serializer.cpython-311.pyc,,
|
||||
sqlalchemy/ext/associationproxy.py,sha256=5O5ANHARO8jytvqBQmOu-QjNVE4Hh3tfYquqKAj5ajs,65771
|
||||
sqlalchemy/ext/asyncio/__init__.py,sha256=1OqSxEyIUn7RWLGyO12F-jAUIvk1I6DXlVy80-Gvkds,1317
|
||||
sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/ext/asyncio/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/ext/asyncio/__pycache__/engine.cpython-311.pyc,,
|
||||
sqlalchemy/ext/asyncio/__pycache__/exc.cpython-311.pyc,,
|
||||
sqlalchemy/ext/asyncio/__pycache__/result.cpython-311.pyc,,
|
||||
sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-311.pyc,,
|
||||
sqlalchemy/ext/asyncio/__pycache__/session.cpython-311.pyc,,
|
||||
sqlalchemy/ext/asyncio/base.py,sha256=fl7wxZD9KjgFiCtG3WXrYjHEvanamcsodCqq9pH9lOk,8905
|
||||
sqlalchemy/ext/asyncio/engine.py,sha256=vQRdpBnGuyzyG48ZssDZvFlcS6Y6ZXUYI0GEOQqdDxk,47941
|
||||
sqlalchemy/ext/asyncio/exc.py,sha256=8sII7VMXzs2TrhizhFQMzSfcroRtiesq8o3UwLfXSgQ,639
|
||||
sqlalchemy/ext/asyncio/result.py,sha256=ID2eh-NHW-lnNFTxbKhje8fr-tnsucUsiw_jcpGcSPc,30409
|
||||
sqlalchemy/ext/asyncio/scoping.py,sha256=BmE1UbFV_C4BXB4WngJc523DeMH-nTchNb8ORiSPYfE,52597
|
||||
sqlalchemy/ext/asyncio/session.py,sha256=Zhkrwwc4rqZJntUpzbgruQNgpuOwaRmjrBQb8ol19z0,62894
|
||||
sqlalchemy/ext/automap.py,sha256=hBlKAfZn2fgAAQh7vh4f2kClbb5ryOgV59tzVHEObQM,61389
|
||||
sqlalchemy/ext/baked.py,sha256=H6T1il7GY84BhzPFj49UECSpZh_eBuiHomA-QIsYOYQ,17807
|
||||
sqlalchemy/ext/compiler.py,sha256=ONPoxoKD2yUS9R2-oOhmPsA7efm-Bs0BXo7HE1dGlsU,20391
|
||||
sqlalchemy/ext/declarative/__init__.py,sha256=20psLdFQbbOWfpdXHZ0CTY6I1k4UqXvKemNVu1LvPOI,1818
|
||||
sqlalchemy/ext/declarative/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/ext/declarative/__pycache__/extensions.cpython-311.pyc,,
|
||||
sqlalchemy/ext/declarative/extensions.py,sha256=uCjN1GisQt54AjqYnKYzJdUjnGd2pZBW47WWdPlS7FE,19547
|
||||
sqlalchemy/ext/horizontal_shard.py,sha256=wuwAPnHymln0unSBnyx-cpX0AfESKSsypaSQTYCvzDk,16750
|
||||
sqlalchemy/ext/hybrid.py,sha256=LXph2NOtBQj6rZMi5ar-WCxkY7qaFp-o-UFIvCy-ep0,52432
|
||||
sqlalchemy/ext/indexable.py,sha256=UkTelbydKCdKelzbv3HWFFavoET9WocKaGRPGEOVfN8,11032
|
||||
sqlalchemy/ext/instrumentation.py,sha256=sg8ghDjdHSODFXh_jAmpgemnNX1rxCeeXEG3-PMdrNk,15707
|
||||
sqlalchemy/ext/mutable.py,sha256=L5ZkHBGYhMaqO75Xtyrk2DBR44RDk0g6Rz2HzHH0F8Q,37355
|
||||
sqlalchemy/ext/mypy/__init__.py,sha256=0WebDIZmqBD0OTq5JLtd_PmfF9JGxe4d4Qv3Ml3PKUg,241
|
||||
sqlalchemy/ext/mypy/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/ext/mypy/__pycache__/apply.cpython-311.pyc,,
|
||||
sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-311.pyc,,
|
||||
sqlalchemy/ext/mypy/__pycache__/infer.cpython-311.pyc,,
|
||||
sqlalchemy/ext/mypy/__pycache__/names.cpython-311.pyc,,
|
||||
sqlalchemy/ext/mypy/__pycache__/plugin.cpython-311.pyc,,
|
||||
sqlalchemy/ext/mypy/__pycache__/util.cpython-311.pyc,,
|
||||
sqlalchemy/ext/mypy/apply.py,sha256=Aek_-XA1eXihT4attxhfE43yBKtCgsxBSb--qgZKUqc,10550
|
||||
sqlalchemy/ext/mypy/decl_class.py,sha256=1vVJRII2apnLTUbc5HkJS6Z2GueaUv_eKvhbqh7Wik4,17384
|
||||
sqlalchemy/ext/mypy/infer.py,sha256=KVnmLFEVS33Al8pUKI7MJbJQu3KeveBUMl78EluBORw,19369
|
||||
sqlalchemy/ext/mypy/names.py,sha256=IQ16GLZFqKxfYxIZxkbTurBqOUYbUV-64V_DSRns1tc,10630
|
||||
sqlalchemy/ext/mypy/plugin.py,sha256=74ML8LI9xar0V86oCxnPFv5FQGEEfUzK64vOay4BKFs,9750
|
||||
sqlalchemy/ext/mypy/util.py,sha256=1zuDQG8ezmF-XhJmAQU_lcBHiD--sL-lq20clg8t4lE,9448
|
||||
sqlalchemy/ext/orderinglist.py,sha256=TGYbsGH72wEZcFNQDYDsZg9OSPuzf__P8YX8_2HtYUo,14384
|
||||
sqlalchemy/ext/serializer.py,sha256=YemanWdeMVUDweHCnQc-iMO6mVVXNo2qQ5NK0Eb2_Es,6178
|
||||
sqlalchemy/future/__init__.py,sha256=q2mw-gxk_xoxJLEvRoyMha3vO1xSRHrslcExOHZwmPA,512
|
||||
sqlalchemy/future/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/future/__pycache__/engine.cpython-311.pyc,,
|
||||
sqlalchemy/future/engine.py,sha256=AgIw6vMsef8W6tynOTkxsjd6o_OQDwGjLdbpoMD8ue8,495
|
||||
sqlalchemy/inspection.py,sha256=MF-LE358wZDUEl1IH8-Uwt2HI65EsQpQW5o5udHkZwA,5063
|
||||
sqlalchemy/log.py,sha256=8x9UR3nj0uFm6or6bQF-JWb4fYv2zOeQjG_w-0wOJFA,8607
|
||||
sqlalchemy/orm/__init__.py,sha256=ZYys5nL3RFUDCMOLFDBrRI52F6er3S1U1OY9TeORuKs,8463
|
||||
sqlalchemy/orm/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/_orm_constructors.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/_typing.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/attributes.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/bulk_persistence.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/clsregistry.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/collections.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/context.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/decl_api.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/decl_base.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/dependency.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/descriptor_props.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/dynamic.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/evaluator.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/events.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/exc.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/identity.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/instrumentation.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/interfaces.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/loading.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/mapped_collection.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/mapper.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/path_registry.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/persistence.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/properties.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/query.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/relationships.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/scoping.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/session.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/state.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/state_changes.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/strategies.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/strategy_options.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/sync.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/unitofwork.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/util.cpython-311.pyc,,
|
||||
sqlalchemy/orm/__pycache__/writeonly.cpython-311.pyc,,
|
||||
sqlalchemy/orm/_orm_constructors.py,sha256=VWY_MotbcQlECGx2uwEu3IcRkZ4RgLM_ufPad3IA9ZM,99354
|
||||
sqlalchemy/orm/_typing.py,sha256=DVBfpHmDVK4x1zxaGJPY2GoTrAsyR6uexv20Lzf1afc,4973
|
||||
sqlalchemy/orm/attributes.py,sha256=-IGg2RFjOPwAEr-boohvlZfitz7OtaXz1v8-7uG8ekw,92520
|
||||
sqlalchemy/orm/base.py,sha256=HhuarpRU-BpKSHE1LeeBaG8CpdkwwLrvTkUVRK-ofjg,27424
|
||||
sqlalchemy/orm/bulk_persistence.py,sha256=SSSR0Omv8A8BzpsOdSo4x75XICoqGpO1sUkyEWUVGso,70022
|
||||
sqlalchemy/orm/clsregistry.py,sha256=29LyYiuj0qbebOpgW6DbBPNB2ikTweFQar1byCst7I0,17958
|
||||
sqlalchemy/orm/collections.py,sha256=jpMsJGVixmrW9kfT8wevm9kpatKRqyDLcqWd7CjKPxE,52179
|
||||
sqlalchemy/orm/context.py,sha256=Wjx0d1Rkxd-wsX1mP2V2_4VbOxdNY6S_HijdXJ-TtKg,112001
|
||||
sqlalchemy/orm/decl_api.py,sha256=0gCZWM2sOXb_4OzUXfevVUisZWOUrErQTAHyaSQQL5k,63674
|
||||
sqlalchemy/orm/decl_base.py,sha256=Tq6I3Jm3bkM01mmoiHfdFXLE94YDk1ik2u2dXL1RxLc,81601
|
||||
sqlalchemy/orm/dependency.py,sha256=hgjksUWhgbmgHK5GdJdiDCBgDAIGQXIrY-Tj79tbL2k,47631
|
||||
sqlalchemy/orm/descriptor_props.py,sha256=pKtpP7H1LB_YuHRVrEYpfFZybEnUUdPwQXxduYFe2hA,37180
|
||||
sqlalchemy/orm/dynamic.py,sha256=jksBDCOsm6EBMVParcNGuMeaAv12hX4IzouKspC-HPA,9786
|
||||
sqlalchemy/orm/evaluator.py,sha256=q292K5vdpP69G7Z9y1RqI5GFAk2diUPwnsXE8De_Wgw,11925
|
||||
sqlalchemy/orm/events.py,sha256=_Ttun_bCSGgvsfg-VzAeEcsGpacf8p4c5z12JkSQkjM,127697
|
||||
sqlalchemy/orm/exc.py,sha256=w7MZkJMGGlu5J6jOFSmi9XXzc02ctnTv34jrEWpI-eM,7356
|
||||
sqlalchemy/orm/identity.py,sha256=jHdCxCpCyda_8mFOfGmN_Pr0XZdKiU-2hFZshlNxbHs,9249
|
||||
sqlalchemy/orm/instrumentation.py,sha256=M-kZmkUvHUxtf-0mCA8RIM5QmMH1hWlYR_pKMwaidjA,24321
|
||||
sqlalchemy/orm/interfaces.py,sha256=1yyppjMHcP5NPXjxfOlSeFNmc-3_T_o2upeF3KFZtc0,48378
|
||||
sqlalchemy/orm/loading.py,sha256=JN2zLnPjNnk7K9DERbyerxESCXin7m7X1XP0gfdWLOE,57537
|
||||
sqlalchemy/orm/mapped_collection.py,sha256=3cneB1dfPTLrsTvKoo9_oCY2xtq4UAHfe5WSXPyqIS4,19690
|
||||
sqlalchemy/orm/mapper.py,sha256=8SVHr7tO-DDNpNGi68usc7PLQ7mTwzkZNEJu1aMb6dQ,171059
|
||||
sqlalchemy/orm/path_registry.py,sha256=bIXllBRevK7Ic5irajYnZgl2iazJ0rKNRkhXJSlfxjY,25850
|
||||
sqlalchemy/orm/persistence.py,sha256=dzyB2JOXNwQgaCbN8kh0sEz00WFePr48qf8NWVCUZH8,61701
|
||||
sqlalchemy/orm/properties.py,sha256=81I-PIF7f7bB0qdH4BCYMWzCzRpe57yUiEIPv2tzBoA,29127
|
||||
sqlalchemy/orm/query.py,sha256=UVNWn_Rq4a8agh5UUWNeu0DJQtPceCWVpXx1-uW7A4E,117555
|
||||
sqlalchemy/orm/relationships.py,sha256=DqD3WBKpeVQ59ldh6eCxar_sIToA3tc2-bJPtp3zfpM,127709
|
||||
sqlalchemy/orm/scoping.py,sha256=gFYywLeMmd5qjFdVPzeuCX727mTaChrCv8aqn4wPke0,78677
|
||||
sqlalchemy/orm/session.py,sha256=yiKyoJBARQj4I1ZBjsIxc6ecCpt2Upjvlxruo2A5HRc,193181
|
||||
sqlalchemy/orm/state.py,sha256=mW2f1hMSNeTJ89foutOE1EqLLD6DZkrSeO-pgagZweg,37520
|
||||
sqlalchemy/orm/state_changes.py,sha256=qKYg7NxwrDkuUY3EPygAztym6oAVUFcP2wXn7QD3Mz4,6815
|
||||
sqlalchemy/orm/strategies.py,sha256=OtmMtWpCDk4ZiaM_ipzGn80sPOi6Opwj3Co4lUHpd_w,114206
|
||||
sqlalchemy/orm/strategy_options.py,sha256=RbFl-79Lrh8XIVUnZFmQ5GVvR586SG_szs3prw5DZLQ,84204
|
||||
sqlalchemy/orm/sync.py,sha256=g7iZfSge1HgxMk9SKRgUgtHEbpbZ1kP_CBqOIdTOXqc,5779
|
||||
sqlalchemy/orm/unitofwork.py,sha256=fiVaqcymbDDHRa1NjS90N9Z466nd5pkJOEi1dHO6QLY,27033
|
||||
sqlalchemy/orm/util.py,sha256=MSLKAWZNw3FzFTH094xpfrhoA2Ov5pJQinK_dU_M0zo,80351
|
||||
sqlalchemy/orm/writeonly.py,sha256=SYu2sAaHZONk2pW4PmtE871LG-O0P_bjidvKzY1H_zI,22305
|
||||
sqlalchemy/pool/__init__.py,sha256=qiDdq4r4FFAoDrK6ncugF_i6usi_X1LeJt-CuBHey0s,1804
|
||||
sqlalchemy/pool/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/pool/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/pool/__pycache__/events.cpython-311.pyc,,
|
||||
sqlalchemy/pool/__pycache__/impl.cpython-311.pyc,,
|
||||
sqlalchemy/pool/base.py,sha256=WF4az4ZKuzQGuKeSJeyexaYjmWZUvYdC6KIi8zTGodw,52236
|
||||
sqlalchemy/pool/events.py,sha256=xGjkIUZl490ZDtCHqnQF9ZCwe2Jv93eGXmnQxftB11E,13147
|
||||
sqlalchemy/pool/impl.py,sha256=2k2YMY9AepEoqGD_ClP_sUodSoa6atkt3GLPPWI49i4,17717
|
||||
sqlalchemy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
sqlalchemy/schema.py,sha256=dKiWmgHYjcKQ4TiiD6vD0UMmIsD8u0Fsor1M9AAeGUs,3194
|
||||
sqlalchemy/sql/__init__.py,sha256=UNa9EUiYWoPayf-FzNcwVgQvpsBdInPZfpJesAStN9o,5820
|
||||
sqlalchemy/sql/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/_dml_constructors.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/_elements_constructors.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/_orm_types.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/_py_util.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/_selectable_constructors.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/_typing.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/annotation.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/cache_key.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/coercions.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/compiler.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/crud.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/ddl.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/default_comparator.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/dml.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/elements.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/events.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/expression.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/functions.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/lambdas.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/naming.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/operators.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/roles.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/schema.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/selectable.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/sqltypes.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/traversals.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/type_api.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/util.cpython-311.pyc,,
|
||||
sqlalchemy/sql/__pycache__/visitors.cpython-311.pyc,,
|
||||
sqlalchemy/sql/_dml_constructors.py,sha256=YdBJex0MCVACv4q2nl_ii3uhxzwU6aDB8zAsratX5UQ,3867
|
||||
sqlalchemy/sql/_elements_constructors.py,sha256=rrxq5Nzyo7GbLaGxb8VRZxko2nW0z7SYEli8ArfMIXI,62550
|
||||
sqlalchemy/sql/_orm_types.py,sha256=T-vjcry4C1y0GToFKVxQCnmly_-Zsq4IO4SHN6bvUF4,625
|
||||
sqlalchemy/sql/_py_util.py,sha256=hiM9ePbRSGs60bAMxPFuJCIC_p9SQ1VzqXGiPchiYwE,2173
|
||||
sqlalchemy/sql/_selectable_constructors.py,sha256=wjE6HrLm9cR7bxvZXT8sFLUqT6t_J9G1XyQCnYmBDl0,18780
|
||||
sqlalchemy/sql/_typing.py,sha256=Z3tBzapYRP0sKL7IwqnzPE9b8Bbq9vQtc4iV9tvxDoU,12494
|
||||
sqlalchemy/sql/annotation.py,sha256=aqbbVz9kfbCT3_66CZ9GEirVN197Cukoqt8rq48FgkQ,18245
|
||||
sqlalchemy/sql/base.py,sha256=2MVQnIL0b8xuzp1Fcv0NAye6h_OcgYpsUgLB4sy8Ahk,73756
|
||||
sqlalchemy/sql/cache_key.py,sha256=sJhAA-2jovIteeIU7mw9hSL1liPP9Ez89CZZJFC3h6E,33548
|
||||
sqlalchemy/sql/coercions.py,sha256=1xzN_9U5BCJGgokdc5iYj5o2cMAfEEZkr1Oa9Q-JYj8,40493
|
||||
sqlalchemy/sql/compiler.py,sha256=aDD100xmz8WpBq8oe7PJ5ar8lk9emd54gEE5K2Hr76g,271187
|
||||
sqlalchemy/sql/crud.py,sha256=g9xcol2KRGjZi1qsb2-bVz8zgVy_53gfMtJcnNO2vyQ,56521
|
||||
sqlalchemy/sql/ddl.py,sha256=CIqMilCKfuQnF0lrZsQdTxgrbXqcTauKr0Ojzj77PFQ,45602
|
||||
sqlalchemy/sql/default_comparator.py,sha256=utXWsZVGEjflhFfCT4ywa6RnhORc1Rryo87Hga71Rps,16707
|
||||
sqlalchemy/sql/dml.py,sha256=pn0Lm1ofC5qVZzwGWFW73lPCiNba8OsTeemurJgwRyg,65614
|
||||
sqlalchemy/sql/elements.py,sha256=kGRUilpx-rr6TTZgZpC5b71OnxxmCgDJRF2fYjDtxh8,172025
|
||||
sqlalchemy/sql/events.py,sha256=iC_Q1Htm1Aobt5tOYxWfHHqNpoytrULORmUKcusH_-E,18290
|
||||
sqlalchemy/sql/expression.py,sha256=VMX-dLpsZYnVRJpYNDozDUgaj7iQ0HuewUKVefD57PE,7586
|
||||
sqlalchemy/sql/functions.py,sha256=MjXK0IVv45Y4n96_TMDZGJ7fwAhGHPRbFP8hOJgaplQ,63689
|
||||
sqlalchemy/sql/lambdas.py,sha256=6P__bsWsFnrD7M18FPiBXI0L0OdWZOEV25FAijT4bwo,49289
|
||||
sqlalchemy/sql/naming.py,sha256=ZHs1qSV3ou8TYmZ92uvU3sfdklUQlIz4uhe330n05SU,6858
|
||||
sqlalchemy/sql/operators.py,sha256=r4oQp4h5zTMFFOpiFNV56joIK-QIjJCobatsmaZ-724,75935
|
||||
sqlalchemy/sql/roles.py,sha256=pOsVn_OZD7mF2gJByHf24Rjopt0_Hu3dUCEOK5t4KS8,7662
|
||||
sqlalchemy/sql/schema.py,sha256=WOIBaDVdg-zahrP95CPYgY4--3OQN56DH6xm28JDF-Y,228262
|
||||
sqlalchemy/sql/selectable.py,sha256=7lxe79hZvnHyzHe1DobodI1lZ1eo8quSLZ6phw10Zj4,232848
|
||||
sqlalchemy/sql/sqltypes.py,sha256=UV46KTkgxSin48oPckPOqk3Gx0tZT1l60qXwk7SbKlo,127101
|
||||
sqlalchemy/sql/traversals.py,sha256=NFgJrVJzInO3HrnG90CklxrDXhFydZohPs2vRJkh3Bo,33589
|
||||
sqlalchemy/sql/type_api.py,sha256=5DzdVquCJomFfpfMyLYbCb66PWxjxbSRdjh6UYB1Yv4,83841
|
||||
sqlalchemy/sql/util.py,sha256=qGHQF-tPCj-m1FBerzT7weCanGcXU7dK5m-W7NHio-4,48077
|
||||
sqlalchemy/sql/visitors.py,sha256=71wdVvhhZL4nJvVwFAs6ssaW-qZgNRSmKjpAcOzF_TA,36317
|
||||
sqlalchemy/testing/__init__.py,sha256=VsrEHrORpAF5n7Vfl43YQgABh6EP1xBx_gHxs7pSXeE,3126
|
||||
sqlalchemy/testing/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/assertions.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/assertsql.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/asyncio.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/config.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/engines.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/entities.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/exclusions.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/pickleable.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/profiling.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/provision.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/requirements.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/schema.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/util.cpython-311.pyc,,
|
||||
sqlalchemy/testing/__pycache__/warnings.cpython-311.pyc,,
|
||||
sqlalchemy/testing/assertions.py,sha256=gL0rA7CCZJbcVgvWOPV91tTZTRwQc1_Ta0-ykBn83Ew,31439
|
||||
sqlalchemy/testing/assertsql.py,sha256=IgQG7l94WaiRP8nTbilJh1ZHZl125g7GPq-S5kmQZN0,16817
|
||||
sqlalchemy/testing/asyncio.py,sha256=fkdRz-E37d5OrQKw5hdjmglOTJyXGnJzaJpvNXOBLxg,3728
|
||||
sqlalchemy/testing/config.py,sha256=AqyH1qub_gDqX0BvlL-JBQe7N-t2wo8655FtwblUNOY,12090
|
||||
sqlalchemy/testing/engines.py,sha256=UnH-8--3zLlYz4IbbCPwC375Za_DC61Spz-oKulbs9Q,13347
|
||||
sqlalchemy/testing/entities.py,sha256=IphFegPKbff3Un47jY6bi7_MQXy6qkx_50jX2tHZJR4,3354
|
||||
sqlalchemy/testing/exclusions.py,sha256=T8B01hmm8WVs-EKcUOQRzabahPqblWJfOidi6bHJ6GA,12460
|
||||
sqlalchemy/testing/fixtures/__init__.py,sha256=dMClrIoxqlYIFpk2ia4RZpkbfxsS_3EBigr9QsPJ66g,1198
|
||||
sqlalchemy/testing/fixtures/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/testing/fixtures/__pycache__/base.cpython-311.pyc,,
|
||||
sqlalchemy/testing/fixtures/__pycache__/mypy.cpython-311.pyc,,
|
||||
sqlalchemy/testing/fixtures/__pycache__/orm.cpython-311.pyc,,
|
||||
sqlalchemy/testing/fixtures/__pycache__/sql.cpython-311.pyc,,
|
||||
sqlalchemy/testing/fixtures/base.py,sha256=9r_J2ksiTzClpUxW0TczICHrWR7Ny8PV8IsBz6TsGFI,12256
|
||||
sqlalchemy/testing/fixtures/mypy.py,sha256=gdxiwNFIzDlNGSOdvM3gbwDceVCC9t8oM5kKbwyhGBk,11973
|
||||
sqlalchemy/testing/fixtures/orm.py,sha256=8EFbnaBbXX_Bf4FcCzBUaAHgyVpsLGBHX16SGLqE3Fg,6095
|
||||
sqlalchemy/testing/fixtures/sql.py,sha256=MFOuYBUyPIpHJzjRCHL9vU-IT4bD6LXGGMvsp0v1FY8,15704
|
||||
sqlalchemy/testing/pickleable.py,sha256=U9mIqk-zaxq9Xfy7HErP7UrKgTov-A3QFnhZh-NiOjI,2833
|
||||
sqlalchemy/testing/plugin/__init__.py,sha256=79F--BIY_NTBzVRIlJGgAY5LNJJ3cD19XvrAo4X0W9A,247
|
||||
sqlalchemy/testing/plugin/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-311.pyc,,
|
||||
sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-311.pyc,,
|
||||
sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-311.pyc,,
|
||||
sqlalchemy/testing/plugin/bootstrap.py,sha256=oYScMbEW4pCnWlPEAq1insFruCXFQeEVBwo__i4McpU,1685
|
||||
sqlalchemy/testing/plugin/plugin_base.py,sha256=BgNzWNEmgpK4CwhyblQQKnH-7FDKVi_Uul5vw8fFjBU,21578
|
||||
sqlalchemy/testing/plugin/pytestplugin.py,sha256=Jtj073ArTcAmetv81sHmrUhlH0SblcSK4wyN8S4hmvo,27554
|
||||
sqlalchemy/testing/profiling.py,sha256=PbuPhRFbauFilUONeY3tV_Y_5lBkD7iCa8VVyH2Sk9Y,10148
|
||||
sqlalchemy/testing/provision.py,sha256=zXsw2D2Xpmw_chmYLsE1GXQqKQ-so3V8xU_joTcKan0,14619
|
||||
sqlalchemy/testing/requirements.py,sha256=N9pSj7z2wVMkBif-DQfPVa_cl9k6p9g_J5FY1OsWtrY,51817
|
||||
sqlalchemy/testing/schema.py,sha256=lr4GkGrGwagaHMuSGzWdzkMaj3HnS7dgfLLWfxt__-U,6513
|
||||
sqlalchemy/testing/suite/__init__.py,sha256=Y5DRNG0Yl1u3ypt9zVF0Z9suPZeuO_UQGLl-wRgvTjU,722
|
||||
sqlalchemy/testing/suite/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_cte.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_insert.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_results.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_select.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_types.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-311.pyc,,
|
||||
sqlalchemy/testing/suite/test_cte.py,sha256=6zBC3W2OwX1Xs-HedzchcKN2S7EaLNkgkvV_JSZ_Pq0,6451
|
||||
sqlalchemy/testing/suite/test_ddl.py,sha256=1Npkf0C_4UNxphthAGjG078n0vPEgnSIHpDu5MfokxQ,12031
|
||||
sqlalchemy/testing/suite/test_deprecations.py,sha256=BcJxZTcjYqeOAENVElCg3hVvU6fkGEW3KGBMfnW8bng,5337
|
||||
sqlalchemy/testing/suite/test_dialect.py,sha256=EH4ZQWbnGdtjmx5amZtTyhYmrkXJCvW1SQoLahoE7uk,22923
|
||||
sqlalchemy/testing/suite/test_insert.py,sha256=9azifj6-OCD7s8h_tAO1uPw100ibQv8YoKc_VA3hn3c,18824
|
||||
sqlalchemy/testing/suite/test_reflection.py,sha256=tJSbJFg5fw0sSUv3I_FPmhN7rWWeJtq3YyxmylWJUlM,106466
|
||||
sqlalchemy/testing/suite/test_results.py,sha256=NQ23m8FDVd0ub751jN4PswGoAhk5nrqvjHvpYULZXnc,15937
|
||||
sqlalchemy/testing/suite/test_rowcount.py,sha256=3KDTlRgjpQ1OVfp__1cv8Hvq4CsDKzmrhJQ_WIJWoJg,7900
|
||||
sqlalchemy/testing/suite/test_select.py,sha256=FvMFYQW9IJpDWGYZiJk46is6YrtmdSghBdTjZCG8T0Y,58574
|
||||
sqlalchemy/testing/suite/test_sequence.py,sha256=66bCoy4xo99GBSaX6Hxb88foANAykLGRz1YEKbvpfuA,9923
|
||||
sqlalchemy/testing/suite/test_types.py,sha256=rFmTOg6XuMch9L2-XthfLJRCTTwpZbMfrNss2g09gmc,65677
|
||||
sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=c3_eIxLyORuSOhNDP0jWKxPyUf3SwMFpdalxtquwqlM,6141
|
||||
sqlalchemy/testing/suite/test_update_delete.py,sha256=yTiM2unnfOK9rK8ZkqeTTU_MkT-RsKFLmdYliniZfAY,3994
|
||||
sqlalchemy/testing/util.py,sha256=BFiSp3CEX95Dr-vv4l_7ZRu5vjZi9hjjnp-JKNfuS5E,14080
|
||||
sqlalchemy/testing/warnings.py,sha256=fJ-QJUY2zY2PPxZJKv9medW-BKKbCNbA4Ns_V3YwFXM,1546
|
||||
sqlalchemy/types.py,sha256=cQFM-hFRmaf1GErun1qqgEs6QxufvzMuwKqj9tuMPpE,3168
|
||||
sqlalchemy/util/__init__.py,sha256=B3bedg-LSQEscwqgmYYU-VENUX8_zAE3q9vb7tkfJNY,8277
|
||||
sqlalchemy/util/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/_collections.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/_has_cy.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/_py_collections.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/compat.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/concurrency.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/deprecations.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/langhelpers.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/preloaded.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/queue.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/tool_support.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/topological.cpython-311.pyc,,
|
||||
sqlalchemy/util/__pycache__/typing.cpython-311.pyc,,
|
||||
sqlalchemy/util/_collections.py,sha256=NE9dGJo8UNXIMbY3l3k8AO9BdPW04DlKTYraKCinchI,20063
|
||||
sqlalchemy/util/_concurrency_py3k.py,sha256=v8VVoBfFvFHe4j8mMkVLfdUrTbV897p8RWGAm73Ue9U,8574
|
||||
sqlalchemy/util/_has_cy.py,sha256=wCQmeSjT3jaH_oxfCEtGk-1g0gbSpt5MCK5UcWdMWqk,1247
|
||||
sqlalchemy/util/_py_collections.py,sha256=U6L5AoyLdgSv7cdqB4xxQbw1rpeJjyOZVXffgxgga8I,16714
|
||||
sqlalchemy/util/compat.py,sha256=R6bpBydldtbr6h7oJePihQxFb7jKiI-YDsK465MSOzk,8714
|
||||
sqlalchemy/util/concurrency.py,sha256=mhwHm0utriD14DRqxTBWgIW7QuwdSEiLgLiJdUjiR3w,2427
|
||||
sqlalchemy/util/deprecations.py,sha256=YBwvvYhSB8LhasIZRKvg_-WNoVhPUcaYI1ZrnjDn868,11971
|
||||
sqlalchemy/util/langhelpers.py,sha256=khoFN05HjHiWY9ddeehCYxYG2u8LDzuiIKLOGLSAihU,64905
|
||||
sqlalchemy/util/preloaded.py,sha256=az7NmLJLsqs0mtM9uBkIu10-841RYDq8wOyqJ7xXvqE,5904
|
||||
sqlalchemy/util/queue.py,sha256=CaeSEaYZ57YwtmLdNdOIjT5PK_LCuwMFiO0mpp39ybM,10185
|
||||
sqlalchemy/util/tool_support.py,sha256=9braZyidaiNrZVsWtGmkSmus50-byhuYrlAqvhjcmnA,6135
|
||||
sqlalchemy/util/topological.py,sha256=N3M3Le7KzGHCmqPGg0ZBqixTDGwmFLhOZvBtc4rHL_g,3458
|
||||
sqlalchemy/util/typing.py,sha256=FqH6WjV3p-8rz68YaXktpiZrPu3kmug2-gktJxBQSnI,16641
|
|
@ -1,6 +0,0 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.42.0)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp311-cp311-manylinux_2_17_x86_64
|
||||
Tag: cp311-cp311-manylinux2014_x86_64
|
||||
|
|
@ -1 +0,0 @@
|
|||
sqlalchemy
|
|
@ -1 +0,0 @@
|
|||
pip
|
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2012, Konsta Vesterinen
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,97 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: SQLAlchemy-Utils
|
||||
Version: 0.41.1
|
||||
Summary: Various utility functions for SQLAlchemy.
|
||||
Home-page: https://github.com/kvesteri/sqlalchemy-utils
|
||||
Author: Konsta Vesterinen, Ryan Leckey, Janne Vanhala, Vesa Uimonen
|
||||
Author-email: konsta@fastmonkeys.com
|
||||
License: BSD
|
||||
Platform: any
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Requires-Python: >=3.6
|
||||
License-File: LICENSE
|
||||
Requires-Dist: SQLAlchemy (>=1.3)
|
||||
Requires-Dist: importlib-metadata ; python_version < "3.8"
|
||||
Provides-Extra: arrow
|
||||
Requires-Dist: arrow (>=0.3.4) ; extra == 'arrow'
|
||||
Provides-Extra: babel
|
||||
Requires-Dist: Babel (>=1.3) ; extra == 'babel'
|
||||
Provides-Extra: color
|
||||
Requires-Dist: colour (>=0.0.4) ; extra == 'color'
|
||||
Provides-Extra: encrypted
|
||||
Requires-Dist: cryptography (>=0.6) ; extra == 'encrypted'
|
||||
Provides-Extra: intervals
|
||||
Requires-Dist: intervals (>=0.7.1) ; extra == 'intervals'
|
||||
Provides-Extra: password
|
||||
Requires-Dist: passlib (<2.0,>=1.6) ; extra == 'password'
|
||||
Provides-Extra: pendulum
|
||||
Requires-Dist: pendulum (>=2.0.5) ; extra == 'pendulum'
|
||||
Provides-Extra: phone
|
||||
Requires-Dist: phonenumbers (>=5.9.2) ; extra == 'phone'
|
||||
Provides-Extra: test
|
||||
Requires-Dist: pytest (>=2.7.1) ; extra == 'test'
|
||||
Requires-Dist: Pygments (>=1.2) ; extra == 'test'
|
||||
Requires-Dist: Jinja2 (>=2.3) ; extra == 'test'
|
||||
Requires-Dist: docutils (>=0.10) ; extra == 'test'
|
||||
Requires-Dist: flexmock (>=0.9.7) ; extra == 'test'
|
||||
Requires-Dist: psycopg (>=3.1.8) ; extra == 'test'
|
||||
Requires-Dist: psycopg2 (>=2.5.1) ; extra == 'test'
|
||||
Requires-Dist: psycopg2cffi (>=2.8.1) ; extra == 'test'
|
||||
Requires-Dist: pg8000 (>=1.12.4) ; extra == 'test'
|
||||
Requires-Dist: pytz (>=2014.2) ; extra == 'test'
|
||||
Requires-Dist: python-dateutil (>=2.6) ; extra == 'test'
|
||||
Requires-Dist: pymysql ; extra == 'test'
|
||||
Requires-Dist: flake8 (>=2.4.0) ; extra == 'test'
|
||||
Requires-Dist: isort (>=4.2.2) ; extra == 'test'
|
||||
Requires-Dist: pyodbc ; extra == 'test'
|
||||
Requires-Dist: backports.zoneinfo ; (python_version < "3.9") and extra == 'test'
|
||||
Provides-Extra: test_all
|
||||
Requires-Dist: Babel (>=1.3) ; extra == 'test_all'
|
||||
Requires-Dist: Jinja2 (>=2.3) ; extra == 'test_all'
|
||||
Requires-Dist: Pygments (>=1.2) ; extra == 'test_all'
|
||||
Requires-Dist: arrow (>=0.3.4) ; extra == 'test_all'
|
||||
Requires-Dist: colour (>=0.0.4) ; extra == 'test_all'
|
||||
Requires-Dist: cryptography (>=0.6) ; extra == 'test_all'
|
||||
Requires-Dist: docutils (>=0.10) ; extra == 'test_all'
|
||||
Requires-Dist: flake8 (>=2.4.0) ; extra == 'test_all'
|
||||
Requires-Dist: flexmock (>=0.9.7) ; extra == 'test_all'
|
||||
Requires-Dist: furl (>=0.4.1) ; extra == 'test_all'
|
||||
Requires-Dist: intervals (>=0.7.1) ; extra == 'test_all'
|
||||
Requires-Dist: isort (>=4.2.2) ; extra == 'test_all'
|
||||
Requires-Dist: passlib (<2.0,>=1.6) ; extra == 'test_all'
|
||||
Requires-Dist: pendulum (>=2.0.5) ; extra == 'test_all'
|
||||
Requires-Dist: pg8000 (>=1.12.4) ; extra == 'test_all'
|
||||
Requires-Dist: phonenumbers (>=5.9.2) ; extra == 'test_all'
|
||||
Requires-Dist: psycopg2 (>=2.5.1) ; extra == 'test_all'
|
||||
Requires-Dist: psycopg2cffi (>=2.8.1) ; extra == 'test_all'
|
||||
Requires-Dist: psycopg (>=3.1.8) ; extra == 'test_all'
|
||||
Requires-Dist: pymysql ; extra == 'test_all'
|
||||
Requires-Dist: pyodbc ; extra == 'test_all'
|
||||
Requires-Dist: pytest (>=2.7.1) ; extra == 'test_all'
|
||||
Requires-Dist: python-dateutil ; extra == 'test_all'
|
||||
Requires-Dist: python-dateutil (>=2.6) ; extra == 'test_all'
|
||||
Requires-Dist: pytz (>=2014.2) ; extra == 'test_all'
|
||||
Requires-Dist: backports.zoneinfo ; (python_version < "3.9") and extra == 'test_all'
|
||||
Provides-Extra: timezone
|
||||
Requires-Dist: python-dateutil ; extra == 'timezone'
|
||||
Provides-Extra: url
|
||||
Requires-Dist: furl (>=0.4.1) ; extra == 'url'
|
||||
|
||||
|
||||
SQLAlchemy-Utils
|
||||
----------------
|
||||
|
||||
Various utility functions and custom data types for SQLAlchemy.
|
|
@ -1,135 +0,0 @@
|
|||
SQLAlchemy_Utils-0.41.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
SQLAlchemy_Utils-0.41.1.dist-info/LICENSE,sha256=aKpRvWCrOmo-gm2RyB2KhgP4FtG6tTWi_xi_fWmqmwo,1437
|
||||
SQLAlchemy_Utils-0.41.1.dist-info/METADATA,sha256=L2ExcVhOidADc4LVJvw1RuF3ScQx3YMnaGGbkBlN-Ok,4341
|
||||
SQLAlchemy_Utils-0.41.1.dist-info/RECORD,,
|
||||
SQLAlchemy_Utils-0.41.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
SQLAlchemy_Utils-0.41.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
||||
SQLAlchemy_Utils-0.41.1.dist-info/top_level.txt,sha256=C1ORFCU1fhRUCHEe-ZcUkfSkafW8gtZgCEAFeXZeaLc,17
|
||||
sqlalchemy_utils/__init__.py,sha256=lQkpRiVlMTX8GuH5M6FIyntqMXlB2LcWBD1zK4x-c60,2363
|
||||
sqlalchemy_utils/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/aggregates.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/asserts.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/compat.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/exceptions.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/expressions.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/generic.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/i18n.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/listeners.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/models.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/observer.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/operators.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/path.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/proxy_dict.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/query_chain.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/utils.cpython-311.pyc,,
|
||||
sqlalchemy_utils/__pycache__/view.cpython-311.pyc,,
|
||||
sqlalchemy_utils/aggregates.py,sha256=sdzDxpQDZgHmHMuEJ82vzMh8DDQEw7_vqaLBeRwn9ik,15420
|
||||
sqlalchemy_utils/asserts.py,sha256=w37lkU4hgzlLkr3GalUMkGVRFrB4E65gi8vVeDNW5tE,5376
|
||||
sqlalchemy_utils/compat.py,sha256=BYEgwN_tu-1yfjYSjVJEtK2R_WC57R7okXHoCv6aD4k,2241
|
||||
sqlalchemy_utils/exceptions.py,sha256=iZW-TQSZDtCge4pzDDpYug4tKmpEM8coaplYiPJ8UPw,229
|
||||
sqlalchemy_utils/expressions.py,sha256=ab-7JXJbuf9nNaSIEHGeUK9mkI__UpLrsAxgXYHZjCA,1610
|
||||
sqlalchemy_utils/functions/__init__.py,sha256=R6TU8WdVGFrYOcA_76rW8CH8AvZeBR_SL6pJFdHa0g8,950
|
||||
sqlalchemy_utils/functions/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy_utils/functions/__pycache__/database.cpython-311.pyc,,
|
||||
sqlalchemy_utils/functions/__pycache__/foreign_keys.cpython-311.pyc,,
|
||||
sqlalchemy_utils/functions/__pycache__/mock.cpython-311.pyc,,
|
||||
sqlalchemy_utils/functions/__pycache__/orm.cpython-311.pyc,,
|
||||
sqlalchemy_utils/functions/__pycache__/render.cpython-311.pyc,,
|
||||
sqlalchemy_utils/functions/__pycache__/sort_query.cpython-311.pyc,,
|
||||
sqlalchemy_utils/functions/database.py,sha256=eH2pTTRbCPrXFIsHcEjaH5SHugfKl-1SFQF_M7xyfcw,19986
|
||||
sqlalchemy_utils/functions/foreign_keys.py,sha256=oHVJoB037tpa-xc3eVnhJPKT6YhkTDETVgQ5RIdIchg,10440
|
||||
sqlalchemy_utils/functions/mock.py,sha256=g8PrPh7W2NFIyklSzKoL4s2cErvJXrAxMzGfvdif_48,3145
|
||||
sqlalchemy_utils/functions/orm.py,sha256=sg9PfWFefRLGeMT_B8vcVDdLmn5EJjuUj9ImW-MNGqM,24628
|
||||
sqlalchemy_utils/functions/render.py,sha256=aCAtzQicivrCMbCvaT-CdHhQvztr2yzAVFXHImzakH8,2057
|
||||
sqlalchemy_utils/functions/sort_query.py,sha256=4O2vMDAwrPA2FsOY62xTirVmhqsfaDBGkRV_ReM7F4A,2332
|
||||
sqlalchemy_utils/generic.py,sha256=LK11XwZa0gc8YEkykXVqVT48rYd6A2qCSsbc6asNJQM,6354
|
||||
sqlalchemy_utils/i18n.py,sha256=bPJziNDTzIaSdNZntlgL0MlI3frQgf8M-yvyjmS_XoQ,3905
|
||||
sqlalchemy_utils/listeners.py,sha256=p1nxzGnEWNspueH27iFG21D9_O-PX95wIWbgiNx5pHM,7805
|
||||
sqlalchemy_utils/models.py,sha256=z87KGdLmwSRGeQBzEJ4w3jj5eEL3heEZPIfQYRPYniw,2877
|
||||
sqlalchemy_utils/observer.py,sha256=11E_n4NwgRcqes0N6A1G8MKCTCGcznSvla2VkJdREPE,12220
|
||||
sqlalchemy_utils/operators.py,sha256=nItfRvJbYcreLjCkJd7t1zckfidkvoiF-tMXNVUJz04,1966
|
||||
sqlalchemy_utils/path.py,sha256=ulyUFuG5BAHIKr_ttJoa2s5NTzUKH27YYPrMLsy1Pew,4121
|
||||
sqlalchemy_utils/primitives/__init__.py,sha256=Ubo8i8HJr2TIcA94y0I822FninJyxKSmHY676wZWT14,185
|
||||
sqlalchemy_utils/primitives/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy_utils/primitives/__pycache__/country.cpython-311.pyc,,
|
||||
sqlalchemy_utils/primitives/__pycache__/currency.cpython-311.pyc,,
|
||||
sqlalchemy_utils/primitives/__pycache__/ltree.cpython-311.pyc,,
|
||||
sqlalchemy_utils/primitives/__pycache__/weekday.cpython-311.pyc,,
|
||||
sqlalchemy_utils/primitives/__pycache__/weekdays.cpython-311.pyc,,
|
||||
sqlalchemy_utils/primitives/country.py,sha256=U8eayWirlksN2ZnCTLE4PFYnLU5DSnxs51m4FU2rVFM,2740
|
||||
sqlalchemy_utils/primitives/currency.py,sha256=RdI-TIxhO90VCmzxnuOAd9ZvHkuw2XrzFJPvD5ZgfE8,2733
|
||||
sqlalchemy_utils/primitives/ltree.py,sha256=4Hs6xjZKdNVMLiVppSCA37RoidfOQZWi7FlUWPuujHA,5261
|
||||
sqlalchemy_utils/primitives/weekday.py,sha256=AMAe1km6iOKLlHghWUr8enj8J2jWNvf5Dox4LrneJJg,1259
|
||||
sqlalchemy_utils/primitives/weekdays.py,sha256=K4tIOHv6EvYR3IUwCEqKT1DEiVgsBMpOty8xRym0Kcc,1764
|
||||
sqlalchemy_utils/proxy_dict.py,sha256=t0-VwgrTcAAsUhOFCE2JdIhTy5Yv-71GebhwQC3xNIU,2369
|
||||
sqlalchemy_utils/query_chain.py,sha256=GN6pWesgac5LtPTvUSecbciR9XbfBAG6qxdQiNFA38s,3963
|
||||
sqlalchemy_utils/relationships/__init__.py,sha256=vyGb2Opfv-ZM0pw5JOpv9kdWr4_Q2foSHSp8Ho_cBik,3587
|
||||
sqlalchemy_utils/relationships/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy_utils/relationships/__pycache__/chained_join.cpython-311.pyc,,
|
||||
sqlalchemy_utils/relationships/chained_join.py,sha256=VUMZh5gwGf8qypkM-ZzNz3OtHY7mP7Hi0QoijQM6OpA,880
|
||||
sqlalchemy_utils/types/__init__.py,sha256=LUMa5FZq7dFuzatd_Dl993hPHc8lQXSnHX4QlW0wG7E,1919
|
||||
sqlalchemy_utils/types/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/arrow.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/bit.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/choice.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/color.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/country.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/currency.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/email.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/ip_address.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/json.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/locale.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/ltree.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/password.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/pg_composite.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/phone_number.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/range.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/scalar_coercible.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/scalar_list.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/timezone.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/ts_vector.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/url.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/uuid.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/__pycache__/weekdays.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/arrow.py,sha256=zpklt2Bh2rFPyjI2HBjMWWmbm83o6iiksp4yj9j1z-g,1535
|
||||
sqlalchemy_utils/types/bit.py,sha256=V9TU63GCuPJGAjlDskK15kBwi2F2y_E9On34QQAY1Zs,756
|
||||
sqlalchemy_utils/types/choice.py,sha256=3GNGs0qNgO3eR0xOlYEhbMjumTKGjMB1wAi-p5UuI5k,5882
|
||||
sqlalchemy_utils/types/color.py,sha256=X5ogyU5s21wJYFhE9v_SttyYElV1BlnNFaZ2M0kTvJk,2121
|
||||
sqlalchemy_utils/types/country.py,sha256=VQpj_5zL04u1mW6g9HKPp5pQlZoHmSXwW--wjj7DGtY,1579
|
||||
sqlalchemy_utils/types/currency.py,sha256=7jab_GdPTZvaYjvsWwh8pyMj69iU2lpUwDsJy4_3YTo,1933
|
||||
sqlalchemy_utils/types/email.py,sha256=93fleQPJrvdxZH9pjdPXtYnci96qwTIwdx3sJbvZNWk,1259
|
||||
sqlalchemy_utils/types/encrypted/__init__.py,sha256=54Yf-Bd-HekcoRTJnHc5q0YVbAlJh8XMcPctgKTJGJo,28
|
||||
sqlalchemy_utils/types/encrypted/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/encrypted/__pycache__/encrypted_type.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/encrypted/__pycache__/padding.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/encrypted/encrypted_type.py,sha256=Z7f48H-4JX1ngv2jmmfUPbYLIKoedZTCmmr73bm7plg,16646
|
||||
sqlalchemy_utils/types/encrypted/padding.py,sha256=roNnoiCelBV-N1-UpOrBHZpL9tA7pkokcPeSNUrhfA8,4483
|
||||
sqlalchemy_utils/types/enriched_datetime/__init__.py,sha256=OmaPAmpYTRsySUrf5V6YboLtq5cGMzjSkixzXg0g3fs,196
|
||||
sqlalchemy_utils/types/enriched_datetime/__pycache__/__init__.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/enriched_datetime/__pycache__/arrow_datetime.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/enriched_datetime/__pycache__/enriched_date_type.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/enriched_datetime/__pycache__/enriched_datetime_type.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/enriched_datetime/__pycache__/pendulum_date.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/enriched_datetime/__pycache__/pendulum_datetime.cpython-311.pyc,,
|
||||
sqlalchemy_utils/types/enriched_datetime/arrow_datetime.py,sha256=FsBohjRv800aIRIt-HCs_Hy7yV_MXYfS6fGzc9VdP2k,1038
|
||||
sqlalchemy_utils/types/enriched_datetime/enriched_date_type.py,sha256=ZcqeUk_2MqQR91pHvh0r4-bZmdeFnf49zVEIcSRnspQ,1367
|
||||
sqlalchemy_utils/types/enriched_datetime/enriched_datetime_type.py,sha256=N4BNnx4dqwbdltgxxm3GMNXvd744E2rSuTG-Iu2IKs8,1446
|
||||
sqlalchemy_utils/types/enriched_datetime/pendulum_date.py,sha256=4gWAowdqxXWxkci5iKG6UrtYdAQ7ZbM4S4Br4Su7clA,868
|
||||
sqlalchemy_utils/types/enriched_datetime/pendulum_datetime.py,sha256=FWGq--aTUDEOsjcmvSOYf3mpvQuwtUua4UFO2guJ8As,1451
|
||||
sqlalchemy_utils/types/ip_address.py,sha256=d0W5H9ICIchWfGi76hkwOCZVVBsUiFS5ZE3HzJoHZwU,1327
|
||||
sqlalchemy_utils/types/json.py,sha256=R_B-GkUwsqjsN0ncat9siWiTgITb-AS9LfLYZa5ilgQ,2123
|
||||
sqlalchemy_utils/types/locale.py,sha256=4wJJ--wNUz_4Zj7u1-QSSRk_1VaF9fN6AjEmq-EL7sM,1797
|
||||
sqlalchemy_utils/types/ltree.py,sha256=uS5Ry3GdXQzcXmYE57kCq_Orq22j_vIUSDtZLDi5UgA,3386
|
||||
sqlalchemy_utils/types/password.py,sha256=GxfkBcnndSy-ihmqoECAY2YsgGJUpqGYzILg3x9gSD4,7875
|
||||
sqlalchemy_utils/types/pg_composite.py,sha256=k6iPw3znq6U2fVhCUNykSTN4lrGpx5evxEwAfKcDQxA,10524
|
||||
sqlalchemy_utils/types/phone_number.py,sha256=RTUiZbpw06e8Rt_N6Smgkh-lSNa27GidXysYx3ODFQ8,6707
|
||||
sqlalchemy_utils/types/range.py,sha256=_nMDA-xL9rhYJby1gdLshZ8E9q2aR_rYd_8-3OrIT2A,12002
|
||||
sqlalchemy_utils/types/scalar_coercible.py,sha256=cIG3kQKMPy6tktOQmexFZ_iDYzWzz3ky_x4jRZ38VFU,192
|
||||
sqlalchemy_utils/types/scalar_list.py,sha256=ioi-hEIz6lC4E8ohQ_4SCTqvWYIHiNGxugzB3g1CeS8,2701
|
||||
sqlalchemy_utils/types/timezone.py,sha256=RvtXS8OVoQvLvJVKpN5YjID7FcURwMJ9Vouh-lxO2Bk,3408
|
||||
sqlalchemy_utils/types/ts_vector.py,sha256=WSOOGr8FXUIVe0HXhP-CSFAtCLDCyF07gMimfXQx8Rs,3148
|
||||
sqlalchemy_utils/types/url.py,sha256=Gwa05HTM1ZKOBVWVk4AZ2WcydaQTAbwwANO8jp-SFmQ,1525
|
||||
sqlalchemy_utils/types/uuid.py,sha256=NDkGm_NOcKyluKYT_tKgIWJX6WIk2kY8em-Md8a7Zc4,3297
|
||||
sqlalchemy_utils/types/weekdays.py,sha256=u0AEc_sNnRNFKBYBfupby-7j7iBC3CWo5S2JDVC8mDE,2129
|
||||
sqlalchemy_utils/utils.py,sha256=xFL2j2MxNiaKGFMrXOZsK8wMmqMItgtgSEgCExzsaIQ,452
|
||||
sqlalchemy_utils/view.py,sha256=JPCxu4x74qIRDzIDahNJprE7sIYCJJ9P99Z9MNjsAOU,6468
|
|
@ -1,5 +0,0 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.40.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
|
@ -1 +0,0 @@
|
|||
sqlalchemy_utils
|
|
@ -1,227 +0,0 @@
|
|||
# don't import any costly modules
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
is_pypy = '__pypy__' in sys.builtin_module_names
|
||||
|
||||
|
||||
def warn_distutils_present():
|
||||
if 'distutils' not in sys.modules:
|
||||
return
|
||||
if is_pypy and sys.version_info < (3, 7):
|
||||
# PyPy for 3.6 unconditionally imports distutils, so bypass the warning
|
||||
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
|
||||
return
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"Distutils was imported before Setuptools, but importing Setuptools "
|
||||
"also replaces the `distutils` module in `sys.modules`. This may lead "
|
||||
"to undesirable behaviors or errors. To avoid these issues, avoid "
|
||||
"using distutils directly, ensure that setuptools is installed in the "
|
||||
"traditional way (e.g. not an editable install), and/or make sure "
|
||||
"that setuptools is always imported before distutils."
|
||||
)
|
||||
|
||||
|
||||
def clear_distutils():
|
||||
if 'distutils' not in sys.modules:
|
||||
return
|
||||
import warnings
|
||||
|
||||
warnings.warn("Setuptools is replacing distutils.")
|
||||
mods = [
|
||||
name
|
||||
for name in sys.modules
|
||||
if name == "distutils" or name.startswith("distutils.")
|
||||
]
|
||||
for name in mods:
|
||||
del sys.modules[name]
|
||||
|
||||
|
||||
def enabled():
|
||||
"""
|
||||
Allow selection of distutils by environment variable.
|
||||
"""
|
||||
which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
|
||||
return which == 'local'
|
||||
|
||||
|
||||
def ensure_local_distutils():
|
||||
import importlib
|
||||
|
||||
clear_distutils()
|
||||
|
||||
# With the DistutilsMetaFinder in place,
|
||||
# perform an import to cause distutils to be
|
||||
# loaded from setuptools._distutils. Ref #2906.
|
||||
with shim():
|
||||
importlib.import_module('distutils')
|
||||
|
||||
# check that submodules load as expected
|
||||
core = importlib.import_module('distutils.core')
|
||||
assert '_distutils' in core.__file__, core.__file__
|
||||
assert 'setuptools._distutils.log' not in sys.modules
|
||||
|
||||
|
||||
def do_override():
|
||||
"""
|
||||
Ensure that the local copy of distutils is preferred over stdlib.
|
||||
|
||||
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
|
||||
for more motivation.
|
||||
"""
|
||||
if enabled():
|
||||
warn_distutils_present()
|
||||
ensure_local_distutils()
|
||||
|
||||
|
||||
class _TrivialRe:
|
||||
def __init__(self, *patterns):
|
||||
self._patterns = patterns
|
||||
|
||||
def match(self, string):
|
||||
return all(pat in string for pat in self._patterns)
|
||||
|
||||
|
||||
class DistutilsMetaFinder:
|
||||
def find_spec(self, fullname, path, target=None):
|
||||
# optimization: only consider top level modules and those
|
||||
# found in the CPython test suite.
|
||||
if path is not None and not fullname.startswith('test.'):
|
||||
return
|
||||
|
||||
method_name = 'spec_for_{fullname}'.format(**locals())
|
||||
method = getattr(self, method_name, lambda: None)
|
||||
return method()
|
||||
|
||||
def spec_for_distutils(self):
|
||||
if self.is_cpython():
|
||||
return
|
||||
|
||||
import importlib
|
||||
import importlib.abc
|
||||
import importlib.util
|
||||
|
||||
try:
|
||||
mod = importlib.import_module('setuptools._distutils')
|
||||
except Exception:
|
||||
# There are a couple of cases where setuptools._distutils
|
||||
# may not be present:
|
||||
# - An older Setuptools without a local distutils is
|
||||
# taking precedence. Ref #2957.
|
||||
# - Path manipulation during sitecustomize removes
|
||||
# setuptools from the path but only after the hook
|
||||
# has been loaded. Ref #2980.
|
||||
# In either case, fall back to stdlib behavior.
|
||||
return
|
||||
|
||||
class DistutilsLoader(importlib.abc.Loader):
|
||||
def create_module(self, spec):
|
||||
mod.__name__ = 'distutils'
|
||||
return mod
|
||||
|
||||
def exec_module(self, module):
|
||||
pass
|
||||
|
||||
return importlib.util.spec_from_loader(
|
||||
'distutils', DistutilsLoader(), origin=mod.__file__
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def is_cpython():
|
||||
"""
|
||||
Suppress supplying distutils for CPython (build and tests).
|
||||
Ref #2965 and #3007.
|
||||
"""
|
||||
return os.path.isfile('pybuilddir.txt')
|
||||
|
||||
def spec_for_pip(self):
|
||||
"""
|
||||
Ensure stdlib distutils when running under pip.
|
||||
See pypa/pip#8761 for rationale.
|
||||
"""
|
||||
if sys.version_info >= (3, 12) or self.pip_imported_during_build():
|
||||
return
|
||||
clear_distutils()
|
||||
self.spec_for_distutils = lambda: None
|
||||
|
||||
@classmethod
|
||||
def pip_imported_during_build(cls):
|
||||
"""
|
||||
Detect if pip is being imported in a build script. Ref #2355.
|
||||
"""
|
||||
import traceback
|
||||
|
||||
return any(
|
||||
cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def frame_file_is_setup(frame):
|
||||
"""
|
||||
Return True if the indicated frame suggests a setup.py file.
|
||||
"""
|
||||
# some frames may not have __file__ (#2940)
|
||||
return frame.f_globals.get('__file__', '').endswith('setup.py')
|
||||
|
||||
def spec_for_sensitive_tests(self):
|
||||
"""
|
||||
Ensure stdlib distutils when running select tests under CPython.
|
||||
|
||||
python/cpython#91169
|
||||
"""
|
||||
clear_distutils()
|
||||
self.spec_for_distutils = lambda: None
|
||||
|
||||
sensitive_tests = (
|
||||
[
|
||||
'test.test_distutils',
|
||||
'test.test_peg_generator',
|
||||
'test.test_importlib',
|
||||
]
|
||||
if sys.version_info < (3, 10)
|
||||
else [
|
||||
'test.test_distutils',
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
for name in DistutilsMetaFinder.sensitive_tests:
|
||||
setattr(
|
||||
DistutilsMetaFinder,
|
||||
f'spec_for_{name}',
|
||||
DistutilsMetaFinder.spec_for_sensitive_tests,
|
||||
)
|
||||
|
||||
|
||||
DISTUTILS_FINDER = DistutilsMetaFinder()
|
||||
|
||||
|
||||
def add_shim():
|
||||
DISTUTILS_FINDER in sys.meta_path or insert_shim()
|
||||
|
||||
|
||||
class shim:
|
||||
def __enter__(self):
|
||||
insert_shim()
|
||||
|
||||
def __exit__(self, exc, value, tb):
|
||||
_remove_shim()
|
||||
|
||||
|
||||
def insert_shim():
|
||||
sys.meta_path.insert(0, DISTUTILS_FINDER)
|
||||
|
||||
|
||||
def _remove_shim():
|
||||
try:
|
||||
sys.meta_path.remove(DISTUTILS_FINDER)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
if sys.version_info < (3, 12):
|
||||
# DistutilsMetaFinder can only be disabled in Python < 3.12 (PEP 632)
|
||||
remove_shim = _remove_shim
|
|
@ -1 +0,0 @@
|
|||
__import__('_distutils_hack').do_override()
|
|
@ -1,33 +0,0 @@
|
|||
# This is a stub package designed to roughly emulate the _yaml
|
||||
# extension module, which previously existed as a standalone module
|
||||
# and has been moved into the `yaml` package namespace.
|
||||
# It does not perfectly mimic its old counterpart, but should get
|
||||
# close enough for anyone who's relying on it even when they shouldn't.
|
||||
import yaml
|
||||
|
||||
# in some circumstances, the yaml module we imoprted may be from a different version, so we need
|
||||
# to tread carefully when poking at it here (it may not have the attributes we expect)
|
||||
if not getattr(yaml, '__with_libyaml__', False):
|
||||
from sys import version_info
|
||||
|
||||
exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError
|
||||
raise exc("No module named '_yaml'")
|
||||
else:
|
||||
from yaml._yaml import *
|
||||
import warnings
|
||||
warnings.warn(
|
||||
'The _yaml extension module is now located at yaml._yaml'
|
||||
' and its location is subject to change. To use the'
|
||||
' LibYAML-based parser and emitter, import from `yaml`:'
|
||||
' `from yaml import CLoader as Loader, CDumper as Dumper`.',
|
||||
DeprecationWarning
|
||||
)
|
||||
del warnings
|
||||
# Don't `del yaml` here because yaml is actually an existing
|
||||
# namespace member of _yaml.
|
||||
|
||||
__name__ = '_yaml'
|
||||
# If the module is top-level (i.e. not a part of any specific package)
|
||||
# then the attribute should be set to ''.
|
||||
# https://docs.python.org/3.8/library/types.html
|
||||
__package__ = ''
|
|
@ -1 +0,0 @@
|
|||
pip
|
|
@ -1,441 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: colorama
|
||||
Version: 0.4.6
|
||||
Summary: Cross-platform colored terminal text.
|
||||
Project-URL: Homepage, https://github.com/tartley/colorama
|
||||
Author-email: Jonathan Hartley <tartley@tartley.com>
|
||||
License-File: LICENSE.txt
|
||||
Keywords: ansi,color,colour,crossplatform,terminal,text,windows,xplatform
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Console
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Topic :: Terminals
|
||||
Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7
|
||||
Description-Content-Type: text/x-rst
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/colorama.svg
|
||||
:target: https://pypi.org/project/colorama/
|
||||
:alt: Latest Version
|
||||
|
||||
.. image:: https://img.shields.io/pypi/pyversions/colorama.svg
|
||||
:target: https://pypi.org/project/colorama/
|
||||
:alt: Supported Python versions
|
||||
|
||||
.. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg
|
||||
:target: https://github.com/tartley/colorama/actions/workflows/test.yml
|
||||
:alt: Build Status
|
||||
|
||||
Colorama
|
||||
========
|
||||
|
||||
Makes ANSI escape character sequences (for producing colored terminal text and
|
||||
cursor positioning) work under MS Windows.
|
||||
|
||||
.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif
|
||||
:target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD
|
||||
:alt: Donate with Paypal
|
||||
|
||||
`PyPI for releases <https://pypi.org/project/colorama/>`_ |
|
||||
`Github for source <https://github.com/tartley/colorama>`_ |
|
||||
`Colorama for enterprise on Tidelift <https://github.com/tartley/colorama/blob/master/ENTERPRISE.md>`_
|
||||
|
||||
If you find Colorama useful, please |donate| to the authors. Thank you!
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Tested on CPython 2.7, 3.7, 3.8, 3.9 and 3.10 and Pypy 2.7 and 3.8.
|
||||
|
||||
No requirements other than the standard library.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install colorama
|
||||
# or
|
||||
conda install -c anaconda colorama
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
ANSI escape character sequences have long been used to produce colored terminal
|
||||
text and cursor positioning on Unix and Macs. Colorama makes this work on
|
||||
Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which
|
||||
would appear as gobbledygook in the output), and converting them into the
|
||||
appropriate win32 calls to modify the state of the terminal. On other platforms,
|
||||
Colorama does nothing.
|
||||
|
||||
This has the upshot of providing a simple cross-platform API for printing
|
||||
colored terminal text from Python, and has the happy side-effect that existing
|
||||
applications or libraries which use ANSI sequences to produce colored output on
|
||||
Linux or Macs can now also work on Windows, simply by calling
|
||||
``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()``
|
||||
(all versions, but may have other side-effects – see below).
|
||||
|
||||
An alternative approach is to install ``ansi.sys`` on Windows machines, which
|
||||
provides the same behaviour for all applications running in terminals. Colorama
|
||||
is intended for situations where that isn't easy (e.g., maybe your app doesn't
|
||||
have an installer.)
|
||||
|
||||
Demo scripts in the source code repository print some colored text using
|
||||
ANSI sequences. Compare their output under Gnome-terminal's built in ANSI
|
||||
handling, versus on Windows Command-Prompt using Colorama:
|
||||
|
||||
.. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png
|
||||
:width: 661
|
||||
:height: 357
|
||||
:alt: ANSI sequences on Ubuntu under gnome-terminal.
|
||||
|
||||
.. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png
|
||||
:width: 668
|
||||
:height: 325
|
||||
:alt: Same ANSI sequences on Windows, using Colorama.
|
||||
|
||||
These screenshots show that, on Windows, Colorama does not support ANSI 'dim
|
||||
text'; it looks the same as 'normal text'.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Initialisation
|
||||
..............
|
||||
|
||||
If the only thing you want from Colorama is to get ANSI escapes to work on
|
||||
Windows, then run:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from colorama import just_fix_windows_console
|
||||
just_fix_windows_console()
|
||||
|
||||
If you're on a recent version of Windows 10 or better, and your stdout/stderr
|
||||
are pointing to a Windows console, then this will flip the magic configuration
|
||||
switch to enable Windows' built-in ANSI support.
|
||||
|
||||
If you're on an older version of Windows, and your stdout/stderr are pointing to
|
||||
a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a
|
||||
magic file object that intercepts ANSI escape sequences and issues the
|
||||
appropriate Win32 calls to emulate them.
|
||||
|
||||
In all other circumstances, it does nothing whatsoever. Basically the idea is
|
||||
that this makes Windows act like Unix with respect to ANSI escape handling.
|
||||
|
||||
It's safe to call this function multiple times. It's safe to call this function
|
||||
on non-Windows platforms, but it won't do anything. It's safe to call this
|
||||
function when one or both of your stdout/stderr are redirected to a file – it
|
||||
won't do anything to those streams.
|
||||
|
||||
Alternatively, you can use the older interface with more features (but also more
|
||||
potential footguns):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from colorama import init
|
||||
init()
|
||||
|
||||
This does the same thing as ``just_fix_windows_console``, except for the
|
||||
following differences:
|
||||
|
||||
- It's not safe to call ``init`` multiple times; you can end up with multiple
|
||||
layers of wrapping and broken ANSI support.
|
||||
|
||||
- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI,
|
||||
and if it thinks they don't, then it will wrap ``sys.stdout`` and
|
||||
``sys.stderr`` in a magic file object that strips out ANSI escape sequences
|
||||
before printing them. This happens on all platforms, and can be convenient if
|
||||
you want to write your code to emit ANSI escape sequences unconditionally, and
|
||||
let Colorama decide whether they should actually be output. But note that
|
||||
Colorama's heuristic is not particularly clever.
|
||||
|
||||
- ``init`` also accepts explicit keyword args to enable/disable various
|
||||
functionality – see below.
|
||||
|
||||
To stop using Colorama before your program exits, simply call ``deinit()``.
|
||||
This will restore ``stdout`` and ``stderr`` to their original values, so that
|
||||
Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is
|
||||
cheaper than calling ``init()`` again (but does the same thing).
|
||||
|
||||
Most users should depend on ``colorama >= 0.4.6``, and use
|
||||
``just_fix_windows_console``. The old ``init`` interface will be supported
|
||||
indefinitely for backwards compatibility, but we don't plan to fix any issues
|
||||
with it, also for backwards compatibility.
|
||||
|
||||
Colored Output
|
||||
..............
|
||||
|
||||
Cross-platform printing of colored text can then be done using Colorama's
|
||||
constant shorthand for ANSI escape sequences. These are deliberately
|
||||
rudimentary, see below.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from colorama import Fore, Back, Style
|
||||
print(Fore.RED + 'some red text')
|
||||
print(Back.GREEN + 'and with a green background')
|
||||
print(Style.DIM + 'and in dim text')
|
||||
print(Style.RESET_ALL)
|
||||
print('back to normal now')
|
||||
|
||||
...or simply by manually printing ANSI sequences from your own code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
print('\033[31m' + 'some red text')
|
||||
print('\033[39m') # and reset to default color
|
||||
|
||||
...or, Colorama can be used in conjunction with existing ANSI libraries
|
||||
such as the venerable `Termcolor <https://pypi.org/project/termcolor/>`_
|
||||
the fabulous `Blessings <https://pypi.org/project/blessings/>`_,
|
||||
or the incredible `_Rich <https://pypi.org/project/rich/>`_.
|
||||
|
||||
If you wish Colorama's Fore, Back and Style constants were more capable,
|
||||
then consider using one of the above highly capable libraries to generate
|
||||
colors, etc, and use Colorama just for its primary purpose: to convert
|
||||
those ANSI sequences to also work on Windows:
|
||||
|
||||
SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama.
|
||||
We are only interested in converting ANSI codes to win32 API calls, not
|
||||
shortcuts like the above to generate ANSI characters.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from colorama import just_fix_windows_console
|
||||
from termcolor import colored
|
||||
|
||||
# use Colorama to make Termcolor work on Windows too
|
||||
just_fix_windows_console()
|
||||
|
||||
# then use Termcolor for all colored text output
|
||||
print(colored('Hello, World!', 'green', 'on_red'))
|
||||
|
||||
Available formatting constants are::
|
||||
|
||||
Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
|
||||
Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET.
|
||||
Style: DIM, NORMAL, BRIGHT, RESET_ALL
|
||||
|
||||
``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will
|
||||
perform this reset automatically on program exit.
|
||||
|
||||
These are fairly well supported, but not part of the standard::
|
||||
|
||||
Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX
|
||||
Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX
|
||||
|
||||
Cursor Positioning
|
||||
..................
|
||||
|
||||
ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for
|
||||
an example of how to generate them.
|
||||
|
||||
Init Keyword Args
|
||||
.................
|
||||
|
||||
``init()`` accepts some ``**kwargs`` to override default behaviour.
|
||||
|
||||
init(autoreset=False):
|
||||
If you find yourself repeatedly sending reset sequences to turn off color
|
||||
changes at the end of every print, then ``init(autoreset=True)`` will
|
||||
automate that:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from colorama import init
|
||||
init(autoreset=True)
|
||||
print(Fore.RED + 'some red text')
|
||||
print('automatically back to default color again')
|
||||
|
||||
init(strip=None):
|
||||
Pass ``True`` or ``False`` to override whether ANSI codes should be
|
||||
stripped from the output. The default behaviour is to strip if on Windows
|
||||
or if output is redirected (not a tty).
|
||||
|
||||
init(convert=None):
|
||||
Pass ``True`` or ``False`` to override whether to convert ANSI codes in the
|
||||
output into win32 calls. The default behaviour is to convert if on Windows
|
||||
and output is to a tty (terminal).
|
||||
|
||||
init(wrap=True):
|
||||
On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr``
|
||||
with proxy objects, which override the ``.write()`` method to do their work.
|
||||
If this wrapping causes you problems, then this can be disabled by passing
|
||||
``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or
|
||||
``strip`` or ``convert`` are True.
|
||||
|
||||
When wrapping is disabled, colored printing on non-Windows platforms will
|
||||
continue to work as normal. To do cross-platform colored output, you can
|
||||
use Colorama's ``AnsiToWin32`` proxy directly:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sys
|
||||
from colorama import init, AnsiToWin32
|
||||
init(wrap=False)
|
||||
stream = AnsiToWin32(sys.stderr).stream
|
||||
|
||||
# Python 2
|
||||
print >>stream, Fore.BLUE + 'blue text on stderr'
|
||||
|
||||
# Python 3
|
||||
print(Fore.BLUE + 'blue text on stderr', file=stream)
|
||||
|
||||
Recognised ANSI Sequences
|
||||
.........................
|
||||
|
||||
ANSI sequences generally take the form::
|
||||
|
||||
ESC [ <param> ; <param> ... <command>
|
||||
|
||||
Where ``<param>`` is an integer, and ``<command>`` is a single letter. Zero or
|
||||
more params are passed to a ``<command>``. If no params are passed, it is
|
||||
generally synonymous with passing a single zero. No spaces exist in the
|
||||
sequence; they have been inserted here simply to read more easily.
|
||||
|
||||
The only ANSI sequences that Colorama converts into win32 calls are::
|
||||
|
||||
ESC [ 0 m # reset all (colors and brightness)
|
||||
ESC [ 1 m # bright
|
||||
ESC [ 2 m # dim (looks same as normal brightness)
|
||||
ESC [ 22 m # normal brightness
|
||||
|
||||
# FOREGROUND:
|
||||
ESC [ 30 m # black
|
||||
ESC [ 31 m # red
|
||||
ESC [ 32 m # green
|
||||
ESC [ 33 m # yellow
|
||||
ESC [ 34 m # blue
|
||||
ESC [ 35 m # magenta
|
||||
ESC [ 36 m # cyan
|
||||
ESC [ 37 m # white
|
||||
ESC [ 39 m # reset
|
||||
|
||||
# BACKGROUND
|
||||
ESC [ 40 m # black
|
||||
ESC [ 41 m # red
|
||||
ESC [ 42 m # green
|
||||
ESC [ 43 m # yellow
|
||||
ESC [ 44 m # blue
|
||||
ESC [ 45 m # magenta
|
||||
ESC [ 46 m # cyan
|
||||
ESC [ 47 m # white
|
||||
ESC [ 49 m # reset
|
||||
|
||||
# cursor positioning
|
||||
ESC [ y;x H # position cursor at x across, y down
|
||||
ESC [ y;x f # position cursor at x across, y down
|
||||
ESC [ n A # move cursor n lines up
|
||||
ESC [ n B # move cursor n lines down
|
||||
ESC [ n C # move cursor n characters forward
|
||||
ESC [ n D # move cursor n characters backward
|
||||
|
||||
# clear the screen
|
||||
ESC [ mode J # clear the screen
|
||||
|
||||
# clear the line
|
||||
ESC [ mode K # clear the line
|
||||
|
||||
Multiple numeric params to the ``'m'`` command can be combined into a single
|
||||
sequence::
|
||||
|
||||
ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background
|
||||
|
||||
All other ANSI sequences of the form ``ESC [ <param> ; <param> ... <command>``
|
||||
are silently stripped from the output on Windows.
|
||||
|
||||
Any other form of ANSI sequence, such as single-character codes or alternative
|
||||
initial characters, are not recognised or stripped. It would be cool to add
|
||||
them though. Let me know if it would be useful for you, via the Issues on
|
||||
GitHub.
|
||||
|
||||
Status & Known Problems
|
||||
-----------------------
|
||||
|
||||
I've personally only tested it on Windows XP (CMD, Console2), Ubuntu
|
||||
(gnome-terminal, xterm), and OS X.
|
||||
|
||||
Some valid ANSI sequences aren't recognised.
|
||||
|
||||
If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the
|
||||
explanation there of why we do not want PRs that allow Colorama to generate new
|
||||
types of ANSI codes.
|
||||
|
||||
See outstanding issues and wish-list:
|
||||
https://github.com/tartley/colorama/issues
|
||||
|
||||
If anything doesn't work for you, or doesn't do what you expected or hoped for,
|
||||
I'd love to hear about it on that issues list, would be delighted by patches,
|
||||
and would be happy to grant commit access to anyone who submits a working patch
|
||||
or two.
|
||||
|
||||
.. _README-hacking.md: README-hacking.md
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see
|
||||
LICENSE file.
|
||||
|
||||
Professional support
|
||||
--------------------
|
||||
|
||||
.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png
|
||||
:alt: Tidelift
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
.. list-table::
|
||||
:widths: 10 100
|
||||
|
||||
* - |tideliftlogo|
|
||||
- Professional support for colorama is available as part of the
|
||||
`Tidelift Subscription`_.
|
||||
Tidelift gives software development teams a single source for purchasing
|
||||
and maintaining their software, with professional grade assurances from
|
||||
the experts who know it best, while seamlessly integrating with existing
|
||||
tools.
|
||||
|
||||
.. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme
|
||||
|
||||
Thanks
|
||||
------
|
||||
|
||||
See the CHANGELOG for more thanks!
|
||||
|
||||
* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5.
|
||||
* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``,
|
||||
providing a solution to issue #7's setuptools/distutils debate,
|
||||
and other fixes.
|
||||
* User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``.
|
||||
* Matthew McCormick for politely pointing out a longstanding crash on non-Win.
|
||||
* Ben Hoyt, for a magnificent fix under 64-bit Windows.
|
||||
* Jesse at Empty Square for submitting a fix for examples in the README.
|
||||
* User 'jamessp', an observant documentation fix for cursor positioning.
|
||||
* User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7
|
||||
fix.
|
||||
* Julien Stuyck, for wisely suggesting Python3 compatible updates to README.
|
||||
* Daniel Griffith for multiple fabulous patches.
|
||||
* Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty
|
||||
output.
|
||||
* Roger Binns, for many suggestions, valuable feedback, & bug reports.
|
||||
* Tim Golden for thought and much appreciated feedback on the initial idea.
|
||||
* User 'Zearin' for updates to the README file.
|
||||
* John Szakmeister for adding support for light colors
|
||||
* Charles Merriam for adding documentation to demos
|
||||
* Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes
|
||||
* Florian Bruhin for a fix when stdout or stderr are None
|
||||
* Thomas Weininger for fixing ValueError on Windows
|
||||
* Remi Rampin for better Github integration and fixes to the README file
|
||||
* Simeon Visser for closing a file handle using 'with' and updating classifiers
|
||||
to include Python 3.3 and 3.4
|
||||
* Andy Neff for fixing RESET of LIGHT_EX colors.
|
||||
* Jonathan Hartley for the initial idea and implementation.
|
|
@ -1,32 +0,0 @@
|
|||
colorama-0.4.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
colorama-0.4.6.dist-info/METADATA,sha256=e67SnrUMOym9sz_4TjF3vxvAV4T3aF7NyqRHHH3YEMw,17158
|
||||
colorama-0.4.6.dist-info/RECORD,,
|
||||
colorama-0.4.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
colorama-0.4.6.dist-info/WHEEL,sha256=cdcF4Fbd0FPtw2EMIOwH-3rSOTUdTCeOSXRMD1iLUb8,105
|
||||
colorama-0.4.6.dist-info/licenses/LICENSE.txt,sha256=ysNcAmhuXQSlpxQL-zs25zrtSWZW6JEQLkKIhteTAxg,1491
|
||||
colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266
|
||||
colorama/__pycache__/__init__.cpython-311.pyc,,
|
||||
colorama/__pycache__/ansi.cpython-311.pyc,,
|
||||
colorama/__pycache__/ansitowin32.cpython-311.pyc,,
|
||||
colorama/__pycache__/initialise.cpython-311.pyc,,
|
||||
colorama/__pycache__/win32.cpython-311.pyc,,
|
||||
colorama/__pycache__/winterm.cpython-311.pyc,,
|
||||
colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522
|
||||
colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128
|
||||
colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325
|
||||
colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75
|
||||
colorama/tests/__pycache__/__init__.cpython-311.pyc,,
|
||||
colorama/tests/__pycache__/ansi_test.cpython-311.pyc,,
|
||||
colorama/tests/__pycache__/ansitowin32_test.cpython-311.pyc,,
|
||||
colorama/tests/__pycache__/initialise_test.cpython-311.pyc,,
|
||||
colorama/tests/__pycache__/isatty_test.cpython-311.pyc,,
|
||||
colorama/tests/__pycache__/utils.cpython-311.pyc,,
|
||||
colorama/tests/__pycache__/winterm_test.cpython-311.pyc,,
|
||||
colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839
|
||||
colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678
|
||||
colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741
|
||||
colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866
|
||||
colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079
|
||||
colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709
|
||||
colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181
|
||||
colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134
|
|
@ -1,5 +0,0 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: hatchling 1.11.1
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2010 Jonathan Hartley
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holders, nor those of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,7 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console
|
||||
from .ansi import Fore, Back, Style, Cursor
|
||||
from .ansitowin32 import AnsiToWin32
|
||||
|
||||
__version__ = '0.4.6'
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
'''
|
||||
This module generates ANSI character codes to printing colors to terminals.
|
||||
See: http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
'''
|
||||
|
||||
CSI = '\033['
|
||||
OSC = '\033]'
|
||||
BEL = '\a'
|
||||
|
||||
|
||||
def code_to_chars(code):
|
||||
return CSI + str(code) + 'm'
|
||||
|
||||
def set_title(title):
|
||||
return OSC + '2;' + title + BEL
|
||||
|
||||
def clear_screen(mode=2):
|
||||
return CSI + str(mode) + 'J'
|
||||
|
||||
def clear_line(mode=2):
|
||||
return CSI + str(mode) + 'K'
|
||||
|
||||
|
||||
class AnsiCodes(object):
|
||||
def __init__(self):
|
||||
# the subclasses declare class attributes which are numbers.
|
||||
# Upon instantiation we define instance attributes, which are the same
|
||||
# as the class attributes but wrapped with the ANSI escape sequence
|
||||
for name in dir(self):
|
||||
if not name.startswith('_'):
|
||||
value = getattr(self, name)
|
||||
setattr(self, name, code_to_chars(value))
|
||||
|
||||
|
||||
class AnsiCursor(object):
|
||||
def UP(self, n=1):
|
||||
return CSI + str(n) + 'A'
|
||||
def DOWN(self, n=1):
|
||||
return CSI + str(n) + 'B'
|
||||
def FORWARD(self, n=1):
|
||||
return CSI + str(n) + 'C'
|
||||
def BACK(self, n=1):
|
||||
return CSI + str(n) + 'D'
|
||||
def POS(self, x=1, y=1):
|
||||
return CSI + str(y) + ';' + str(x) + 'H'
|
||||
|
||||
|
||||
class AnsiFore(AnsiCodes):
|
||||
BLACK = 30
|
||||
RED = 31
|
||||
GREEN = 32
|
||||
YELLOW = 33
|
||||
BLUE = 34
|
||||
MAGENTA = 35
|
||||
CYAN = 36
|
||||
WHITE = 37
|
||||
RESET = 39
|
||||
|
||||
# These are fairly well supported, but not part of the standard.
|
||||
LIGHTBLACK_EX = 90
|
||||
LIGHTRED_EX = 91
|
||||
LIGHTGREEN_EX = 92
|
||||
LIGHTYELLOW_EX = 93
|
||||
LIGHTBLUE_EX = 94
|
||||
LIGHTMAGENTA_EX = 95
|
||||
LIGHTCYAN_EX = 96
|
||||
LIGHTWHITE_EX = 97
|
||||
|
||||
|
||||
class AnsiBack(AnsiCodes):
|
||||
BLACK = 40
|
||||
RED = 41
|
||||
GREEN = 42
|
||||
YELLOW = 43
|
||||
BLUE = 44
|
||||
MAGENTA = 45
|
||||
CYAN = 46
|
||||
WHITE = 47
|
||||
RESET = 49
|
||||
|
||||
# These are fairly well supported, but not part of the standard.
|
||||
LIGHTBLACK_EX = 100
|
||||
LIGHTRED_EX = 101
|
||||
LIGHTGREEN_EX = 102
|
||||
LIGHTYELLOW_EX = 103
|
||||
LIGHTBLUE_EX = 104
|
||||
LIGHTMAGENTA_EX = 105
|
||||
LIGHTCYAN_EX = 106
|
||||
LIGHTWHITE_EX = 107
|
||||
|
||||
|
||||
class AnsiStyle(AnsiCodes):
|
||||
BRIGHT = 1
|
||||
DIM = 2
|
||||
NORMAL = 22
|
||||
RESET_ALL = 0
|
||||
|
||||
Fore = AnsiFore()
|
||||
Back = AnsiBack()
|
||||
Style = AnsiStyle()
|
||||
Cursor = AnsiCursor()
|
|
@ -1,277 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL
|
||||
from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle
|
||||
from .win32 import windll, winapi_test
|
||||
|
||||
|
||||
winterm = None
|
||||
if windll is not None:
|
||||
winterm = WinTerm()
|
||||
|
||||
|
||||
class StreamWrapper(object):
|
||||
'''
|
||||
Wraps a stream (such as stdout), acting as a transparent proxy for all
|
||||
attribute access apart from method 'write()', which is delegated to our
|
||||
Converter instance.
|
||||
'''
|
||||
def __init__(self, wrapped, converter):
|
||||
# double-underscore everything to prevent clashes with names of
|
||||
# attributes on the wrapped stream object.
|
||||
self.__wrapped = wrapped
|
||||
self.__convertor = converter
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.__wrapped, name)
|
||||
|
||||
def __enter__(self, *args, **kwargs):
|
||||
# special method lookup bypasses __getattr__/__getattribute__, see
|
||||
# https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit
|
||||
# thus, contextlib magic methods are not proxied via __getattr__
|
||||
return self.__wrapped.__enter__(*args, **kwargs)
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
return self.__wrapped.__exit__(*args, **kwargs)
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.__dict__ = state
|
||||
|
||||
def __getstate__(self):
|
||||
return self.__dict__
|
||||
|
||||
def write(self, text):
|
||||
self.__convertor.write(text)
|
||||
|
||||
def isatty(self):
|
||||
stream = self.__wrapped
|
||||
if 'PYCHARM_HOSTED' in os.environ:
|
||||
if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__):
|
||||
return True
|
||||
try:
|
||||
stream_isatty = stream.isatty
|
||||
except AttributeError:
|
||||
return False
|
||||
else:
|
||||
return stream_isatty()
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
stream = self.__wrapped
|
||||
try:
|
||||
return stream.closed
|
||||
# AttributeError in the case that the stream doesn't support being closed
|
||||
# ValueError for the case that the stream has already been detached when atexit runs
|
||||
except (AttributeError, ValueError):
|
||||
return True
|
||||
|
||||
|
||||
class AnsiToWin32(object):
|
||||
'''
|
||||
Implements a 'write()' method which, on Windows, will strip ANSI character
|
||||
sequences from the text, and if outputting to a tty, will convert them into
|
||||
win32 function calls.
|
||||
'''
|
||||
ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer
|
||||
ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command
|
||||
|
||||
def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
|
||||
# The wrapped stream (normally sys.stdout or sys.stderr)
|
||||
self.wrapped = wrapped
|
||||
|
||||
# should we reset colors to defaults after every .write()
|
||||
self.autoreset = autoreset
|
||||
|
||||
# create the proxy wrapping our output stream
|
||||
self.stream = StreamWrapper(wrapped, self)
|
||||
|
||||
on_windows = os.name == 'nt'
|
||||
# We test if the WinAPI works, because even if we are on Windows
|
||||
# we may be using a terminal that doesn't support the WinAPI
|
||||
# (e.g. Cygwin Terminal). In this case it's up to the terminal
|
||||
# to support the ANSI codes.
|
||||
conversion_supported = on_windows and winapi_test()
|
||||
try:
|
||||
fd = wrapped.fileno()
|
||||
except Exception:
|
||||
fd = -1
|
||||
system_has_native_ansi = not on_windows or enable_vt_processing(fd)
|
||||
have_tty = not self.stream.closed and self.stream.isatty()
|
||||
need_conversion = conversion_supported and not system_has_native_ansi
|
||||
|
||||
# should we strip ANSI sequences from our output?
|
||||
if strip is None:
|
||||
strip = need_conversion or not have_tty
|
||||
self.strip = strip
|
||||
|
||||
# should we should convert ANSI sequences into win32 calls?
|
||||
if convert is None:
|
||||
convert = need_conversion and have_tty
|
||||
self.convert = convert
|
||||
|
||||
# dict of ansi codes to win32 functions and parameters
|
||||
self.win32_calls = self.get_win32_calls()
|
||||
|
||||
# are we wrapping stderr?
|
||||
self.on_stderr = self.wrapped is sys.stderr
|
||||
|
||||
def should_wrap(self):
|
||||
'''
|
||||
True if this class is actually needed. If false, then the output
|
||||
stream will not be affected, nor will win32 calls be issued, so
|
||||
wrapping stdout is not actually required. This will generally be
|
||||
False on non-Windows platforms, unless optional functionality like
|
||||
autoreset has been requested using kwargs to init()
|
||||
'''
|
||||
return self.convert or self.strip or self.autoreset
|
||||
|
||||
def get_win32_calls(self):
|
||||
if self.convert and winterm:
|
||||
return {
|
||||
AnsiStyle.RESET_ALL: (winterm.reset_all, ),
|
||||
AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
|
||||
AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
|
||||
AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
|
||||
AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
|
||||
AnsiFore.RED: (winterm.fore, WinColor.RED),
|
||||
AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
|
||||
AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW),
|
||||
AnsiFore.BLUE: (winterm.fore, WinColor.BLUE),
|
||||
AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA),
|
||||
AnsiFore.CYAN: (winterm.fore, WinColor.CYAN),
|
||||
AnsiFore.WHITE: (winterm.fore, WinColor.GREY),
|
||||
AnsiFore.RESET: (winterm.fore, ),
|
||||
AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True),
|
||||
AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True),
|
||||
AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True),
|
||||
AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True),
|
||||
AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True),
|
||||
AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True),
|
||||
AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True),
|
||||
AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True),
|
||||
AnsiBack.BLACK: (winterm.back, WinColor.BLACK),
|
||||
AnsiBack.RED: (winterm.back, WinColor.RED),
|
||||
AnsiBack.GREEN: (winterm.back, WinColor.GREEN),
|
||||
AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW),
|
||||
AnsiBack.BLUE: (winterm.back, WinColor.BLUE),
|
||||
AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA),
|
||||
AnsiBack.CYAN: (winterm.back, WinColor.CYAN),
|
||||
AnsiBack.WHITE: (winterm.back, WinColor.GREY),
|
||||
AnsiBack.RESET: (winterm.back, ),
|
||||
AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True),
|
||||
AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True),
|
||||
AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True),
|
||||
AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True),
|
||||
AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True),
|
||||
AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True),
|
||||
AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True),
|
||||
AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True),
|
||||
}
|
||||
return dict()
|
||||
|
||||
def write(self, text):
|
||||
if self.strip or self.convert:
|
||||
self.write_and_convert(text)
|
||||
else:
|
||||
self.wrapped.write(text)
|
||||
self.wrapped.flush()
|
||||
if self.autoreset:
|
||||
self.reset_all()
|
||||
|
||||
|
||||
def reset_all(self):
|
||||
if self.convert:
|
||||
self.call_win32('m', (0,))
|
||||
elif not self.strip and not self.stream.closed:
|
||||
self.wrapped.write(Style.RESET_ALL)
|
||||
|
||||
|
||||
def write_and_convert(self, text):
|
||||
'''
|
||||
Write the given text to our wrapped stream, stripping any ANSI
|
||||
sequences from the text, and optionally converting them into win32
|
||||
calls.
|
||||
'''
|
||||
cursor = 0
|
||||
text = self.convert_osc(text)
|
||||
for match in self.ANSI_CSI_RE.finditer(text):
|
||||
start, end = match.span()
|
||||
self.write_plain_text(text, cursor, start)
|
||||
self.convert_ansi(*match.groups())
|
||||
cursor = end
|
||||
self.write_plain_text(text, cursor, len(text))
|
||||
|
||||
|
||||
def write_plain_text(self, text, start, end):
|
||||
if start < end:
|
||||
self.wrapped.write(text[start:end])
|
||||
self.wrapped.flush()
|
||||
|
||||
|
||||
def convert_ansi(self, paramstring, command):
|
||||
if self.convert:
|
||||
params = self.extract_params(command, paramstring)
|
||||
self.call_win32(command, params)
|
||||
|
||||
|
||||
def extract_params(self, command, paramstring):
|
||||
if command in 'Hf':
|
||||
params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';'))
|
||||
while len(params) < 2:
|
||||
# defaults:
|
||||
params = params + (1,)
|
||||
else:
|
||||
params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0)
|
||||
if len(params) == 0:
|
||||
# defaults:
|
||||
if command in 'JKm':
|
||||
params = (0,)
|
||||
elif command in 'ABCD':
|
||||
params = (1,)
|
||||
|
||||
return params
|
||||
|
||||
|
||||
def call_win32(self, command, params):
|
||||
if command == 'm':
|
||||
for param in params:
|
||||
if param in self.win32_calls:
|
||||
func_args = self.win32_calls[param]
|
||||
func = func_args[0]
|
||||
args = func_args[1:]
|
||||
kwargs = dict(on_stderr=self.on_stderr)
|
||||
func(*args, **kwargs)
|
||||
elif command in 'J':
|
||||
winterm.erase_screen(params[0], on_stderr=self.on_stderr)
|
||||
elif command in 'K':
|
||||
winterm.erase_line(params[0], on_stderr=self.on_stderr)
|
||||
elif command in 'Hf': # cursor position - absolute
|
||||
winterm.set_cursor_position(params, on_stderr=self.on_stderr)
|
||||
elif command in 'ABCD': # cursor position - relative
|
||||
n = params[0]
|
||||
# A - up, B - down, C - forward, D - back
|
||||
x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command]
|
||||
winterm.cursor_adjust(x, y, on_stderr=self.on_stderr)
|
||||
|
||||
|
||||
def convert_osc(self, text):
|
||||
for match in self.ANSI_OSC_RE.finditer(text):
|
||||
start, end = match.span()
|
||||
text = text[:start] + text[end:]
|
||||
paramstring, command = match.groups()
|
||||
if command == BEL:
|
||||
if paramstring.count(";") == 1:
|
||||
params = paramstring.split(";")
|
||||
# 0 - change title and icon (we will only change title)
|
||||
# 1 - change icon (we don't support this)
|
||||
# 2 - change title
|
||||
if params[0] in '02':
|
||||
winterm.set_title(params[1])
|
||||
return text
|
||||
|
||||
|
||||
def flush(self):
|
||||
self.wrapped.flush()
|
|
@ -1,121 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
import atexit
|
||||
import contextlib
|
||||
import sys
|
||||
|
||||
from .ansitowin32 import AnsiToWin32
|
||||
|
||||
|
||||
def _wipe_internal_state_for_tests():
|
||||
global orig_stdout, orig_stderr
|
||||
orig_stdout = None
|
||||
orig_stderr = None
|
||||
|
||||
global wrapped_stdout, wrapped_stderr
|
||||
wrapped_stdout = None
|
||||
wrapped_stderr = None
|
||||
|
||||
global atexit_done
|
||||
atexit_done = False
|
||||
|
||||
global fixed_windows_console
|
||||
fixed_windows_console = False
|
||||
|
||||
try:
|
||||
# no-op if it wasn't registered
|
||||
atexit.unregister(reset_all)
|
||||
except AttributeError:
|
||||
# python 2: no atexit.unregister. Oh well, we did our best.
|
||||
pass
|
||||
|
||||
|
||||
def reset_all():
|
||||
if AnsiToWin32 is not None: # Issue #74: objects might become None at exit
|
||||
AnsiToWin32(orig_stdout).reset_all()
|
||||
|
||||
|
||||
def init(autoreset=False, convert=None, strip=None, wrap=True):
|
||||
|
||||
if not wrap and any([autoreset, convert, strip]):
|
||||
raise ValueError('wrap=False conflicts with any other arg=True')
|
||||
|
||||
global wrapped_stdout, wrapped_stderr
|
||||
global orig_stdout, orig_stderr
|
||||
|
||||
orig_stdout = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
|
||||
if sys.stdout is None:
|
||||
wrapped_stdout = None
|
||||
else:
|
||||
sys.stdout = wrapped_stdout = \
|
||||
wrap_stream(orig_stdout, convert, strip, autoreset, wrap)
|
||||
if sys.stderr is None:
|
||||
wrapped_stderr = None
|
||||
else:
|
||||
sys.stderr = wrapped_stderr = \
|
||||
wrap_stream(orig_stderr, convert, strip, autoreset, wrap)
|
||||
|
||||
global atexit_done
|
||||
if not atexit_done:
|
||||
atexit.register(reset_all)
|
||||
atexit_done = True
|
||||
|
||||
|
||||
def deinit():
|
||||
if orig_stdout is not None:
|
||||
sys.stdout = orig_stdout
|
||||
if orig_stderr is not None:
|
||||
sys.stderr = orig_stderr
|
||||
|
||||
|
||||
def just_fix_windows_console():
|
||||
global fixed_windows_console
|
||||
|
||||
if sys.platform != "win32":
|
||||
return
|
||||
if fixed_windows_console:
|
||||
return
|
||||
if wrapped_stdout is not None or wrapped_stderr is not None:
|
||||
# Someone already ran init() and it did stuff, so we won't second-guess them
|
||||
return
|
||||
|
||||
# On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the
|
||||
# native ANSI support in the console as a side-effect. We only need to actually
|
||||
# replace sys.stdout/stderr if we're in the old-style conversion mode.
|
||||
new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False)
|
||||
if new_stdout.convert:
|
||||
sys.stdout = new_stdout
|
||||
new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False)
|
||||
if new_stderr.convert:
|
||||
sys.stderr = new_stderr
|
||||
|
||||
fixed_windows_console = True
|
||||
|
||||
@contextlib.contextmanager
|
||||
def colorama_text(*args, **kwargs):
|
||||
init(*args, **kwargs)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
deinit()
|
||||
|
||||
|
||||
def reinit():
|
||||
if wrapped_stdout is not None:
|
||||
sys.stdout = wrapped_stdout
|
||||
if wrapped_stderr is not None:
|
||||
sys.stderr = wrapped_stderr
|
||||
|
||||
|
||||
def wrap_stream(stream, convert, strip, autoreset, wrap):
|
||||
if wrap:
|
||||
wrapper = AnsiToWin32(stream,
|
||||
convert=convert, strip=strip, autoreset=autoreset)
|
||||
if wrapper.should_wrap():
|
||||
stream = wrapper.stream
|
||||
return stream
|
||||
|
||||
|
||||
# Use this for initial setup as well, to reduce code duplication
|
||||
_wipe_internal_state_for_tests()
|
|
@ -1 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
|
@ -1,76 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
import sys
|
||||
from unittest import TestCase, main
|
||||
|
||||
from ..ansi import Back, Fore, Style
|
||||
from ..ansitowin32 import AnsiToWin32
|
||||
|
||||
stdout_orig = sys.stdout
|
||||
stderr_orig = sys.stderr
|
||||
|
||||
|
||||
class AnsiTest(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
# sanity check: stdout should be a file or StringIO object.
|
||||
# It will only be AnsiToWin32 if init() has previously wrapped it
|
||||
self.assertNotEqual(type(sys.stdout), AnsiToWin32)
|
||||
self.assertNotEqual(type(sys.stderr), AnsiToWin32)
|
||||
|
||||
def tearDown(self):
|
||||
sys.stdout = stdout_orig
|
||||
sys.stderr = stderr_orig
|
||||
|
||||
|
||||
def testForeAttributes(self):
|
||||
self.assertEqual(Fore.BLACK, '\033[30m')
|
||||
self.assertEqual(Fore.RED, '\033[31m')
|
||||
self.assertEqual(Fore.GREEN, '\033[32m')
|
||||
self.assertEqual(Fore.YELLOW, '\033[33m')
|
||||
self.assertEqual(Fore.BLUE, '\033[34m')
|
||||
self.assertEqual(Fore.MAGENTA, '\033[35m')
|
||||
self.assertEqual(Fore.CYAN, '\033[36m')
|
||||
self.assertEqual(Fore.WHITE, '\033[37m')
|
||||
self.assertEqual(Fore.RESET, '\033[39m')
|
||||
|
||||
# Check the light, extended versions.
|
||||
self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m')
|
||||
self.assertEqual(Fore.LIGHTRED_EX, '\033[91m')
|
||||
self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m')
|
||||
self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m')
|
||||
self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m')
|
||||
self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m')
|
||||
self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m')
|
||||
self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m')
|
||||
|
||||
|
||||
def testBackAttributes(self):
|
||||
self.assertEqual(Back.BLACK, '\033[40m')
|
||||
self.assertEqual(Back.RED, '\033[41m')
|
||||
self.assertEqual(Back.GREEN, '\033[42m')
|
||||
self.assertEqual(Back.YELLOW, '\033[43m')
|
||||
self.assertEqual(Back.BLUE, '\033[44m')
|
||||
self.assertEqual(Back.MAGENTA, '\033[45m')
|
||||
self.assertEqual(Back.CYAN, '\033[46m')
|
||||
self.assertEqual(Back.WHITE, '\033[47m')
|
||||
self.assertEqual(Back.RESET, '\033[49m')
|
||||
|
||||
# Check the light, extended versions.
|
||||
self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m')
|
||||
self.assertEqual(Back.LIGHTRED_EX, '\033[101m')
|
||||
self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m')
|
||||
self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m')
|
||||
self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m')
|
||||
self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m')
|
||||
self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m')
|
||||
self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m')
|
||||
|
||||
|
||||
def testStyleAttributes(self):
|
||||
self.assertEqual(Style.DIM, '\033[2m')
|
||||
self.assertEqual(Style.NORMAL, '\033[22m')
|
||||
self.assertEqual(Style.BRIGHT, '\033[1m')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,294 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
from io import StringIO, TextIOWrapper
|
||||
from unittest import TestCase, main
|
||||
try:
|
||||
from contextlib import ExitStack
|
||||
except ImportError:
|
||||
# python 2
|
||||
from contextlib2 import ExitStack
|
||||
|
||||
try:
|
||||
from unittest.mock import MagicMock, Mock, patch
|
||||
except ImportError:
|
||||
from mock import MagicMock, Mock, patch
|
||||
|
||||
from ..ansitowin32 import AnsiToWin32, StreamWrapper
|
||||
from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
from .utils import osname
|
||||
|
||||
|
||||
class StreamWrapperTest(TestCase):
|
||||
|
||||
def testIsAProxy(self):
|
||||
mockStream = Mock()
|
||||
wrapper = StreamWrapper(mockStream, None)
|
||||
self.assertTrue( wrapper.random_attr is mockStream.random_attr )
|
||||
|
||||
def testDelegatesWrite(self):
|
||||
mockStream = Mock()
|
||||
mockConverter = Mock()
|
||||
wrapper = StreamWrapper(mockStream, mockConverter)
|
||||
wrapper.write('hello')
|
||||
self.assertTrue(mockConverter.write.call_args, (('hello',), {}))
|
||||
|
||||
def testDelegatesContext(self):
|
||||
mockConverter = Mock()
|
||||
s = StringIO()
|
||||
with StreamWrapper(s, mockConverter) as fp:
|
||||
fp.write(u'hello')
|
||||
self.assertTrue(s.closed)
|
||||
|
||||
def testProxyNoContextManager(self):
|
||||
mockStream = MagicMock()
|
||||
mockStream.__enter__.side_effect = AttributeError()
|
||||
mockConverter = Mock()
|
||||
with self.assertRaises(AttributeError) as excinfo:
|
||||
with StreamWrapper(mockStream, mockConverter) as wrapper:
|
||||
wrapper.write('hello')
|
||||
|
||||
def test_closed_shouldnt_raise_on_closed_stream(self):
|
||||
stream = StringIO()
|
||||
stream.close()
|
||||
wrapper = StreamWrapper(stream, None)
|
||||
self.assertEqual(wrapper.closed, True)
|
||||
|
||||
def test_closed_shouldnt_raise_on_detached_stream(self):
|
||||
stream = TextIOWrapper(StringIO())
|
||||
stream.detach()
|
||||
wrapper = StreamWrapper(stream, None)
|
||||
self.assertEqual(wrapper.closed, True)
|
||||
|
||||
class AnsiToWin32Test(TestCase):
|
||||
|
||||
def testInit(self):
|
||||
mockStdout = Mock()
|
||||
auto = Mock()
|
||||
stream = AnsiToWin32(mockStdout, autoreset=auto)
|
||||
self.assertEqual(stream.wrapped, mockStdout)
|
||||
self.assertEqual(stream.autoreset, auto)
|
||||
|
||||
@patch('colorama.ansitowin32.winterm', None)
|
||||
@patch('colorama.ansitowin32.winapi_test', lambda *_: True)
|
||||
def testStripIsTrueOnWindows(self):
|
||||
with osname('nt'):
|
||||
mockStdout = Mock()
|
||||
stream = AnsiToWin32(mockStdout)
|
||||
self.assertTrue(stream.strip)
|
||||
|
||||
def testStripIsFalseOffWindows(self):
|
||||
with osname('posix'):
|
||||
mockStdout = Mock(closed=False)
|
||||
stream = AnsiToWin32(mockStdout)
|
||||
self.assertFalse(stream.strip)
|
||||
|
||||
def testWriteStripsAnsi(self):
|
||||
mockStdout = Mock()
|
||||
stream = AnsiToWin32(mockStdout)
|
||||
stream.wrapped = Mock()
|
||||
stream.write_and_convert = Mock()
|
||||
stream.strip = True
|
||||
|
||||
stream.write('abc')
|
||||
|
||||
self.assertFalse(stream.wrapped.write.called)
|
||||
self.assertEqual(stream.write_and_convert.call_args, (('abc',), {}))
|
||||
|
||||
def testWriteDoesNotStripAnsi(self):
|
||||
mockStdout = Mock()
|
||||
stream = AnsiToWin32(mockStdout)
|
||||
stream.wrapped = Mock()
|
||||
stream.write_and_convert = Mock()
|
||||
stream.strip = False
|
||||
stream.convert = False
|
||||
|
||||
stream.write('abc')
|
||||
|
||||
self.assertFalse(stream.write_and_convert.called)
|
||||
self.assertEqual(stream.wrapped.write.call_args, (('abc',), {}))
|
||||
|
||||
def assert_autoresets(self, convert, autoreset=True):
|
||||
stream = AnsiToWin32(Mock())
|
||||
stream.convert = convert
|
||||
stream.reset_all = Mock()
|
||||
stream.autoreset = autoreset
|
||||
stream.winterm = Mock()
|
||||
|
||||
stream.write('abc')
|
||||
|
||||
self.assertEqual(stream.reset_all.called, autoreset)
|
||||
|
||||
def testWriteAutoresets(self):
|
||||
self.assert_autoresets(convert=True)
|
||||
self.assert_autoresets(convert=False)
|
||||
self.assert_autoresets(convert=True, autoreset=False)
|
||||
self.assert_autoresets(convert=False, autoreset=False)
|
||||
|
||||
def testWriteAndConvertWritesPlainText(self):
|
||||
stream = AnsiToWin32(Mock())
|
||||
stream.write_and_convert( 'abc' )
|
||||
self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) )
|
||||
|
||||
def testWriteAndConvertStripsAllValidAnsi(self):
|
||||
stream = AnsiToWin32(Mock())
|
||||
stream.call_win32 = Mock()
|
||||
data = [
|
||||
'abc\033[mdef',
|
||||
'abc\033[0mdef',
|
||||
'abc\033[2mdef',
|
||||
'abc\033[02mdef',
|
||||
'abc\033[002mdef',
|
||||
'abc\033[40mdef',
|
||||
'abc\033[040mdef',
|
||||
'abc\033[0;1mdef',
|
||||
'abc\033[40;50mdef',
|
||||
'abc\033[50;30;40mdef',
|
||||
'abc\033[Adef',
|
||||
'abc\033[0Gdef',
|
||||
'abc\033[1;20;128Hdef',
|
||||
]
|
||||
for datum in data:
|
||||
stream.wrapped.write.reset_mock()
|
||||
stream.write_and_convert( datum )
|
||||
self.assertEqual(
|
||||
[args[0] for args in stream.wrapped.write.call_args_list],
|
||||
[ ('abc',), ('def',) ]
|
||||
)
|
||||
|
||||
def testWriteAndConvertSkipsEmptySnippets(self):
|
||||
stream = AnsiToWin32(Mock())
|
||||
stream.call_win32 = Mock()
|
||||
stream.write_and_convert( '\033[40m\033[41m' )
|
||||
self.assertFalse( stream.wrapped.write.called )
|
||||
|
||||
def testWriteAndConvertCallsWin32WithParamsAndCommand(self):
|
||||
stream = AnsiToWin32(Mock())
|
||||
stream.convert = True
|
||||
stream.call_win32 = Mock()
|
||||
stream.extract_params = Mock(return_value='params')
|
||||
data = {
|
||||
'abc\033[adef': ('a', 'params'),
|
||||
'abc\033[;;bdef': ('b', 'params'),
|
||||
'abc\033[0cdef': ('c', 'params'),
|
||||
'abc\033[;;0;;Gdef': ('G', 'params'),
|
||||
'abc\033[1;20;128Hdef': ('H', 'params'),
|
||||
}
|
||||
for datum, expected in data.items():
|
||||
stream.call_win32.reset_mock()
|
||||
stream.write_and_convert( datum )
|
||||
self.assertEqual( stream.call_win32.call_args[0], expected )
|
||||
|
||||
def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self):
|
||||
stream = StringIO()
|
||||
converter = AnsiToWin32(stream)
|
||||
stream.close()
|
||||
|
||||
converter.reset_all()
|
||||
|
||||
def test_wrap_shouldnt_raise_on_closed_orig_stdout(self):
|
||||
stream = StringIO()
|
||||
stream.close()
|
||||
with \
|
||||
patch("colorama.ansitowin32.os.name", "nt"), \
|
||||
patch("colorama.ansitowin32.winapi_test", lambda: True):
|
||||
converter = AnsiToWin32(stream)
|
||||
self.assertTrue(converter.strip)
|
||||
self.assertFalse(converter.convert)
|
||||
|
||||
def test_wrap_shouldnt_raise_on_missing_closed_attr(self):
|
||||
with \
|
||||
patch("colorama.ansitowin32.os.name", "nt"), \
|
||||
patch("colorama.ansitowin32.winapi_test", lambda: True):
|
||||
converter = AnsiToWin32(object())
|
||||
self.assertTrue(converter.strip)
|
||||
self.assertFalse(converter.convert)
|
||||
|
||||
def testExtractParams(self):
|
||||
stream = AnsiToWin32(Mock())
|
||||
data = {
|
||||
'': (0,),
|
||||
';;': (0,),
|
||||
'2': (2,),
|
||||
';;002;;': (2,),
|
||||
'0;1': (0, 1),
|
||||
';;003;;456;;': (3, 456),
|
||||
'11;22;33;44;55': (11, 22, 33, 44, 55),
|
||||
}
|
||||
for datum, expected in data.items():
|
||||
self.assertEqual(stream.extract_params('m', datum), expected)
|
||||
|
||||
def testCallWin32UsesLookup(self):
|
||||
listener = Mock()
|
||||
stream = AnsiToWin32(listener)
|
||||
stream.win32_calls = {
|
||||
1: (lambda *_, **__: listener(11),),
|
||||
2: (lambda *_, **__: listener(22),),
|
||||
3: (lambda *_, **__: listener(33),),
|
||||
}
|
||||
stream.call_win32('m', (3, 1, 99, 2))
|
||||
self.assertEqual(
|
||||
[a[0][0] for a in listener.call_args_list],
|
||||
[33, 11, 22] )
|
||||
|
||||
def test_osc_codes(self):
|
||||
mockStdout = Mock()
|
||||
stream = AnsiToWin32(mockStdout, convert=True)
|
||||
with patch('colorama.ansitowin32.winterm') as winterm:
|
||||
data = [
|
||||
'\033]0\x07', # missing arguments
|
||||
'\033]0;foo\x08', # wrong OSC command
|
||||
'\033]0;colorama_test_title\x07', # should work
|
||||
'\033]1;colorama_test_title\x07', # wrong set command
|
||||
'\033]2;colorama_test_title\x07', # should work
|
||||
'\033]' + ';' * 64 + '\x08', # see issue #247
|
||||
]
|
||||
for code in data:
|
||||
stream.write(code)
|
||||
self.assertEqual(winterm.set_title.call_count, 2)
|
||||
|
||||
def test_native_windows_ansi(self):
|
||||
with ExitStack() as stack:
|
||||
def p(a, b):
|
||||
stack.enter_context(patch(a, b, create=True))
|
||||
# Pretend to be on Windows
|
||||
p("colorama.ansitowin32.os.name", "nt")
|
||||
p("colorama.ansitowin32.winapi_test", lambda: True)
|
||||
p("colorama.win32.winapi_test", lambda: True)
|
||||
p("colorama.winterm.win32.windll", "non-None")
|
||||
p("colorama.winterm.get_osfhandle", lambda _: 1234)
|
||||
|
||||
# Pretend that our mock stream has native ANSI support
|
||||
p(
|
||||
"colorama.winterm.win32.GetConsoleMode",
|
||||
lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING,
|
||||
)
|
||||
SetConsoleMode = Mock()
|
||||
p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode)
|
||||
|
||||
stdout = Mock()
|
||||
stdout.closed = False
|
||||
stdout.isatty.return_value = True
|
||||
stdout.fileno.return_value = 1
|
||||
|
||||
# Our fake console says it has native vt support, so AnsiToWin32 should
|
||||
# enable that support and do nothing else.
|
||||
stream = AnsiToWin32(stdout)
|
||||
SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
||||
self.assertFalse(stream.strip)
|
||||
self.assertFalse(stream.convert)
|
||||
self.assertFalse(stream.should_wrap())
|
||||
|
||||
# Now let's pretend we're on an old Windows console, that doesn't have
|
||||
# native ANSI support.
|
||||
p("colorama.winterm.win32.GetConsoleMode", lambda _: 0)
|
||||
SetConsoleMode = Mock()
|
||||
p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode)
|
||||
|
||||
stream = AnsiToWin32(stdout)
|
||||
SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
||||
self.assertTrue(stream.strip)
|
||||
self.assertTrue(stream.convert)
|
||||
self.assertTrue(stream.should_wrap())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,189 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
import sys
|
||||
from unittest import TestCase, main, skipUnless
|
||||
|
||||
try:
|
||||
from unittest.mock import patch, Mock
|
||||
except ImportError:
|
||||
from mock import patch, Mock
|
||||
|
||||
from ..ansitowin32 import StreamWrapper
|
||||
from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests
|
||||
from .utils import osname, replace_by
|
||||
|
||||
orig_stdout = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
|
||||
|
||||
class InitTest(TestCase):
|
||||
|
||||
@skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty")
|
||||
def setUp(self):
|
||||
# sanity check
|
||||
self.assertNotWrapped()
|
||||
|
||||
def tearDown(self):
|
||||
_wipe_internal_state_for_tests()
|
||||
sys.stdout = orig_stdout
|
||||
sys.stderr = orig_stderr
|
||||
|
||||
def assertWrapped(self):
|
||||
self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped')
|
||||
self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped')
|
||||
self.assertTrue(isinstance(sys.stdout, StreamWrapper),
|
||||
'bad stdout wrapper')
|
||||
self.assertTrue(isinstance(sys.stderr, StreamWrapper),
|
||||
'bad stderr wrapper')
|
||||
|
||||
def assertNotWrapped(self):
|
||||
self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped')
|
||||
self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped')
|
||||
|
||||
@patch('colorama.initialise.reset_all')
|
||||
@patch('colorama.ansitowin32.winapi_test', lambda *_: True)
|
||||
@patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False)
|
||||
def testInitWrapsOnWindows(self, _):
|
||||
with osname("nt"):
|
||||
init()
|
||||
self.assertWrapped()
|
||||
|
||||
@patch('colorama.initialise.reset_all')
|
||||
@patch('colorama.ansitowin32.winapi_test', lambda *_: False)
|
||||
def testInitDoesntWrapOnEmulatedWindows(self, _):
|
||||
with osname("nt"):
|
||||
init()
|
||||
self.assertNotWrapped()
|
||||
|
||||
def testInitDoesntWrapOnNonWindows(self):
|
||||
with osname("posix"):
|
||||
init()
|
||||
self.assertNotWrapped()
|
||||
|
||||
def testInitDoesntWrapIfNone(self):
|
||||
with replace_by(None):
|
||||
init()
|
||||
# We can't use assertNotWrapped here because replace_by(None)
|
||||
# changes stdout/stderr already.
|
||||
self.assertIsNone(sys.stdout)
|
||||
self.assertIsNone(sys.stderr)
|
||||
|
||||
def testInitAutoresetOnWrapsOnAllPlatforms(self):
|
||||
with osname("posix"):
|
||||
init(autoreset=True)
|
||||
self.assertWrapped()
|
||||
|
||||
def testInitWrapOffDoesntWrapOnWindows(self):
|
||||
with osname("nt"):
|
||||
init(wrap=False)
|
||||
self.assertNotWrapped()
|
||||
|
||||
def testInitWrapOffIncompatibleWithAutoresetOn(self):
|
||||
self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False))
|
||||
|
||||
@patch('colorama.win32.SetConsoleTextAttribute')
|
||||
@patch('colorama.initialise.AnsiToWin32')
|
||||
def testAutoResetPassedOn(self, mockATW32, _):
|
||||
with osname("nt"):
|
||||
init(autoreset=True)
|
||||
self.assertEqual(len(mockATW32.call_args_list), 2)
|
||||
self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True)
|
||||
self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True)
|
||||
|
||||
@patch('colorama.initialise.AnsiToWin32')
|
||||
def testAutoResetChangeable(self, mockATW32):
|
||||
with osname("nt"):
|
||||
init()
|
||||
|
||||
init(autoreset=True)
|
||||
self.assertEqual(len(mockATW32.call_args_list), 4)
|
||||
self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True)
|
||||
self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True)
|
||||
|
||||
init()
|
||||
self.assertEqual(len(mockATW32.call_args_list), 6)
|
||||
self.assertEqual(
|
||||
mockATW32.call_args_list[4][1]['autoreset'], False)
|
||||
self.assertEqual(
|
||||
mockATW32.call_args_list[5][1]['autoreset'], False)
|
||||
|
||||
|
||||
@patch('colorama.initialise.atexit.register')
|
||||
def testAtexitRegisteredOnlyOnce(self, mockRegister):
|
||||
init()
|
||||
self.assertTrue(mockRegister.called)
|
||||
mockRegister.reset_mock()
|
||||
init()
|
||||
self.assertFalse(mockRegister.called)
|
||||
|
||||
|
||||
class JustFixWindowsConsoleTest(TestCase):
|
||||
def _reset(self):
|
||||
_wipe_internal_state_for_tests()
|
||||
sys.stdout = orig_stdout
|
||||
sys.stderr = orig_stderr
|
||||
|
||||
def tearDown(self):
|
||||
self._reset()
|
||||
|
||||
@patch("colorama.ansitowin32.winapi_test", lambda: True)
|
||||
def testJustFixWindowsConsole(self):
|
||||
if sys.platform != "win32":
|
||||
# just_fix_windows_console should be a no-op
|
||||
just_fix_windows_console()
|
||||
self.assertIs(sys.stdout, orig_stdout)
|
||||
self.assertIs(sys.stderr, orig_stderr)
|
||||
else:
|
||||
def fake_std():
|
||||
# Emulate stdout=not a tty, stderr=tty
|
||||
# to check that we handle both cases correctly
|
||||
stdout = Mock()
|
||||
stdout.closed = False
|
||||
stdout.isatty.return_value = False
|
||||
stdout.fileno.return_value = 1
|
||||
sys.stdout = stdout
|
||||
|
||||
stderr = Mock()
|
||||
stderr.closed = False
|
||||
stderr.isatty.return_value = True
|
||||
stderr.fileno.return_value = 2
|
||||
sys.stderr = stderr
|
||||
|
||||
for native_ansi in [False, True]:
|
||||
with patch(
|
||||
'colorama.ansitowin32.enable_vt_processing',
|
||||
lambda *_: native_ansi
|
||||
):
|
||||
self._reset()
|
||||
fake_std()
|
||||
|
||||
# Regular single-call test
|
||||
prev_stdout = sys.stdout
|
||||
prev_stderr = sys.stderr
|
||||
just_fix_windows_console()
|
||||
self.assertIs(sys.stdout, prev_stdout)
|
||||
if native_ansi:
|
||||
self.assertIs(sys.stderr, prev_stderr)
|
||||
else:
|
||||
self.assertIsNot(sys.stderr, prev_stderr)
|
||||
|
||||
# second call without resetting is always a no-op
|
||||
prev_stdout = sys.stdout
|
||||
prev_stderr = sys.stderr
|
||||
just_fix_windows_console()
|
||||
self.assertIs(sys.stdout, prev_stdout)
|
||||
self.assertIs(sys.stderr, prev_stderr)
|
||||
|
||||
self._reset()
|
||||
fake_std()
|
||||
|
||||
# If init() runs first, just_fix_windows_console should be a no-op
|
||||
init()
|
||||
prev_stdout = sys.stdout
|
||||
prev_stderr = sys.stderr
|
||||
just_fix_windows_console()
|
||||
self.assertIs(prev_stdout, sys.stdout)
|
||||
self.assertIs(prev_stderr, sys.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,57 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
import sys
|
||||
from unittest import TestCase, main
|
||||
|
||||
from ..ansitowin32 import StreamWrapper, AnsiToWin32
|
||||
from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY
|
||||
|
||||
|
||||
def is_a_tty(stream):
|
||||
return StreamWrapper(stream, None).isatty()
|
||||
|
||||
class IsattyTest(TestCase):
|
||||
|
||||
def test_TTY(self):
|
||||
tty = StreamTTY()
|
||||
self.assertTrue(is_a_tty(tty))
|
||||
with pycharm():
|
||||
self.assertTrue(is_a_tty(tty))
|
||||
|
||||
def test_nonTTY(self):
|
||||
non_tty = StreamNonTTY()
|
||||
self.assertFalse(is_a_tty(non_tty))
|
||||
with pycharm():
|
||||
self.assertFalse(is_a_tty(non_tty))
|
||||
|
||||
def test_withPycharm(self):
|
||||
with pycharm():
|
||||
self.assertTrue(is_a_tty(sys.stderr))
|
||||
self.assertTrue(is_a_tty(sys.stdout))
|
||||
|
||||
def test_withPycharmTTYOverride(self):
|
||||
tty = StreamTTY()
|
||||
with pycharm(), replace_by(tty):
|
||||
self.assertTrue(is_a_tty(tty))
|
||||
|
||||
def test_withPycharmNonTTYOverride(self):
|
||||
non_tty = StreamNonTTY()
|
||||
with pycharm(), replace_by(non_tty):
|
||||
self.assertFalse(is_a_tty(non_tty))
|
||||
|
||||
def test_withPycharmNoneOverride(self):
|
||||
with pycharm():
|
||||
with replace_by(None), replace_original_by(None):
|
||||
self.assertFalse(is_a_tty(None))
|
||||
self.assertFalse(is_a_tty(StreamNonTTY()))
|
||||
self.assertTrue(is_a_tty(StreamTTY()))
|
||||
|
||||
def test_withPycharmStreamWrapped(self):
|
||||
with pycharm():
|
||||
self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty())
|
||||
self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty())
|
||||
self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty())
|
||||
self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,49 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
from contextlib import contextmanager
|
||||
from io import StringIO
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class StreamTTY(StringIO):
|
||||
def isatty(self):
|
||||
return True
|
||||
|
||||
class StreamNonTTY(StringIO):
|
||||
def isatty(self):
|
||||
return False
|
||||
|
||||
@contextmanager
|
||||
def osname(name):
|
||||
orig = os.name
|
||||
os.name = name
|
||||
yield
|
||||
os.name = orig
|
||||
|
||||
@contextmanager
|
||||
def replace_by(stream):
|
||||
orig_stdout = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
sys.stdout = stream
|
||||
sys.stderr = stream
|
||||
yield
|
||||
sys.stdout = orig_stdout
|
||||
sys.stderr = orig_stderr
|
||||
|
||||
@contextmanager
|
||||
def replace_original_by(stream):
|
||||
orig_stdout = sys.__stdout__
|
||||
orig_stderr = sys.__stderr__
|
||||
sys.__stdout__ = stream
|
||||
sys.__stderr__ = stream
|
||||
yield
|
||||
sys.__stdout__ = orig_stdout
|
||||
sys.__stderr__ = orig_stderr
|
||||
|
||||
@contextmanager
|
||||
def pycharm():
|
||||
os.environ["PYCHARM_HOSTED"] = "1"
|
||||
non_tty = StreamNonTTY()
|
||||
with replace_by(non_tty), replace_original_by(non_tty):
|
||||
yield
|
||||
del os.environ["PYCHARM_HOSTED"]
|
|
@ -1,131 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
import sys
|
||||
from unittest import TestCase, main, skipUnless
|
||||
|
||||
try:
|
||||
from unittest.mock import Mock, patch
|
||||
except ImportError:
|
||||
from mock import Mock, patch
|
||||
|
||||
from ..winterm import WinColor, WinStyle, WinTerm
|
||||
|
||||
|
||||
class WinTermTest(TestCase):
|
||||
|
||||
@patch('colorama.winterm.win32')
|
||||
def testInit(self, mockWin32):
|
||||
mockAttr = Mock()
|
||||
mockAttr.wAttributes = 7 + 6 * 16 + 8
|
||||
mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr
|
||||
term = WinTerm()
|
||||
self.assertEqual(term._fore, 7)
|
||||
self.assertEqual(term._back, 6)
|
||||
self.assertEqual(term._style, 8)
|
||||
|
||||
@skipUnless(sys.platform.startswith("win"), "requires Windows")
|
||||
def testGetAttrs(self):
|
||||
term = WinTerm()
|
||||
|
||||
term._fore = 0
|
||||
term._back = 0
|
||||
term._style = 0
|
||||
self.assertEqual(term.get_attrs(), 0)
|
||||
|
||||
term._fore = WinColor.YELLOW
|
||||
self.assertEqual(term.get_attrs(), WinColor.YELLOW)
|
||||
|
||||
term._back = WinColor.MAGENTA
|
||||
self.assertEqual(
|
||||
term.get_attrs(),
|
||||
WinColor.YELLOW + WinColor.MAGENTA * 16)
|
||||
|
||||
term._style = WinStyle.BRIGHT
|
||||
self.assertEqual(
|
||||
term.get_attrs(),
|
||||
WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT)
|
||||
|
||||
@patch('colorama.winterm.win32')
|
||||
def testResetAll(self, mockWin32):
|
||||
mockAttr = Mock()
|
||||
mockAttr.wAttributes = 1 + 2 * 16 + 8
|
||||
mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr
|
||||
term = WinTerm()
|
||||
|
||||
term.set_console = Mock()
|
||||
term._fore = -1
|
||||
term._back = -1
|
||||
term._style = -1
|
||||
|
||||
term.reset_all()
|
||||
|
||||
self.assertEqual(term._fore, 1)
|
||||
self.assertEqual(term._back, 2)
|
||||
self.assertEqual(term._style, 8)
|
||||
self.assertEqual(term.set_console.called, True)
|
||||
|
||||
@skipUnless(sys.platform.startswith("win"), "requires Windows")
|
||||
def testFore(self):
|
||||
term = WinTerm()
|
||||
term.set_console = Mock()
|
||||
term._fore = 0
|
||||
|
||||
term.fore(5)
|
||||
|
||||
self.assertEqual(term._fore, 5)
|
||||
self.assertEqual(term.set_console.called, True)
|
||||
|
||||
@skipUnless(sys.platform.startswith("win"), "requires Windows")
|
||||
def testBack(self):
|
||||
term = WinTerm()
|
||||
term.set_console = Mock()
|
||||
term._back = 0
|
||||
|
||||
term.back(5)
|
||||
|
||||
self.assertEqual(term._back, 5)
|
||||
self.assertEqual(term.set_console.called, True)
|
||||
|
||||
@skipUnless(sys.platform.startswith("win"), "requires Windows")
|
||||
def testStyle(self):
|
||||
term = WinTerm()
|
||||
term.set_console = Mock()
|
||||
term._style = 0
|
||||
|
||||
term.style(22)
|
||||
|
||||
self.assertEqual(term._style, 22)
|
||||
self.assertEqual(term.set_console.called, True)
|
||||
|
||||
@patch('colorama.winterm.win32')
|
||||
def testSetConsole(self, mockWin32):
|
||||
mockAttr = Mock()
|
||||
mockAttr.wAttributes = 0
|
||||
mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr
|
||||
term = WinTerm()
|
||||
term.windll = Mock()
|
||||
|
||||
term.set_console()
|
||||
|
||||
self.assertEqual(
|
||||
mockWin32.SetConsoleTextAttribute.call_args,
|
||||
((mockWin32.STDOUT, term.get_attrs()), {})
|
||||
)
|
||||
|
||||
@patch('colorama.winterm.win32')
|
||||
def testSetConsoleOnStderr(self, mockWin32):
|
||||
mockAttr = Mock()
|
||||
mockAttr.wAttributes = 0
|
||||
mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr
|
||||
term = WinTerm()
|
||||
term.windll = Mock()
|
||||
|
||||
term.set_console(on_stderr=True)
|
||||
|
||||
self.assertEqual(
|
||||
mockWin32.SetConsoleTextAttribute.call_args,
|
||||
((mockWin32.STDERR, term.get_attrs()), {})
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,180 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
|
||||
# from winbase.h
|
||||
STDOUT = -11
|
||||
STDERR = -12
|
||||
|
||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
|
||||
|
||||
try:
|
||||
import ctypes
|
||||
from ctypes import LibraryLoader
|
||||
windll = LibraryLoader(ctypes.WinDLL)
|
||||
from ctypes import wintypes
|
||||
except (AttributeError, ImportError):
|
||||
windll = None
|
||||
SetConsoleTextAttribute = lambda *_: None
|
||||
winapi_test = lambda *_: None
|
||||
else:
|
||||
from ctypes import byref, Structure, c_char, POINTER
|
||||
|
||||
COORD = wintypes._COORD
|
||||
|
||||
class CONSOLE_SCREEN_BUFFER_INFO(Structure):
|
||||
"""struct in wincon.h."""
|
||||
_fields_ = [
|
||||
("dwSize", COORD),
|
||||
("dwCursorPosition", COORD),
|
||||
("wAttributes", wintypes.WORD),
|
||||
("srWindow", wintypes.SMALL_RECT),
|
||||
("dwMaximumWindowSize", COORD),
|
||||
]
|
||||
def __str__(self):
|
||||
return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % (
|
||||
self.dwSize.Y, self.dwSize.X
|
||||
, self.dwCursorPosition.Y, self.dwCursorPosition.X
|
||||
, self.wAttributes
|
||||
, self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right
|
||||
, self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X
|
||||
)
|
||||
|
||||
_GetStdHandle = windll.kernel32.GetStdHandle
|
||||
_GetStdHandle.argtypes = [
|
||||
wintypes.DWORD,
|
||||
]
|
||||
_GetStdHandle.restype = wintypes.HANDLE
|
||||
|
||||
_GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
|
||||
_GetConsoleScreenBufferInfo.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
POINTER(CONSOLE_SCREEN_BUFFER_INFO),
|
||||
]
|
||||
_GetConsoleScreenBufferInfo.restype = wintypes.BOOL
|
||||
|
||||
_SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
|
||||
_SetConsoleTextAttribute.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
wintypes.WORD,
|
||||
]
|
||||
_SetConsoleTextAttribute.restype = wintypes.BOOL
|
||||
|
||||
_SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition
|
||||
_SetConsoleCursorPosition.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
COORD,
|
||||
]
|
||||
_SetConsoleCursorPosition.restype = wintypes.BOOL
|
||||
|
||||
_FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA
|
||||
_FillConsoleOutputCharacterA.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
c_char,
|
||||
wintypes.DWORD,
|
||||
COORD,
|
||||
POINTER(wintypes.DWORD),
|
||||
]
|
||||
_FillConsoleOutputCharacterA.restype = wintypes.BOOL
|
||||
|
||||
_FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute
|
||||
_FillConsoleOutputAttribute.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
wintypes.WORD,
|
||||
wintypes.DWORD,
|
||||
COORD,
|
||||
POINTER(wintypes.DWORD),
|
||||
]
|
||||
_FillConsoleOutputAttribute.restype = wintypes.BOOL
|
||||
|
||||
_SetConsoleTitleW = windll.kernel32.SetConsoleTitleW
|
||||
_SetConsoleTitleW.argtypes = [
|
||||
wintypes.LPCWSTR
|
||||
]
|
||||
_SetConsoleTitleW.restype = wintypes.BOOL
|
||||
|
||||
_GetConsoleMode = windll.kernel32.GetConsoleMode
|
||||
_GetConsoleMode.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
POINTER(wintypes.DWORD)
|
||||
]
|
||||
_GetConsoleMode.restype = wintypes.BOOL
|
||||
|
||||
_SetConsoleMode = windll.kernel32.SetConsoleMode
|
||||
_SetConsoleMode.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
wintypes.DWORD
|
||||
]
|
||||
_SetConsoleMode.restype = wintypes.BOOL
|
||||
|
||||
def _winapi_test(handle):
|
||||
csbi = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
success = _GetConsoleScreenBufferInfo(
|
||||
handle, byref(csbi))
|
||||
return bool(success)
|
||||
|
||||
def winapi_test():
|
||||
return any(_winapi_test(h) for h in
|
||||
(_GetStdHandle(STDOUT), _GetStdHandle(STDERR)))
|
||||
|
||||
def GetConsoleScreenBufferInfo(stream_id=STDOUT):
|
||||
handle = _GetStdHandle(stream_id)
|
||||
csbi = CONSOLE_SCREEN_BUFFER_INFO()
|
||||
success = _GetConsoleScreenBufferInfo(
|
||||
handle, byref(csbi))
|
||||
return csbi
|
||||
|
||||
def SetConsoleTextAttribute(stream_id, attrs):
|
||||
handle = _GetStdHandle(stream_id)
|
||||
return _SetConsoleTextAttribute(handle, attrs)
|
||||
|
||||
def SetConsoleCursorPosition(stream_id, position, adjust=True):
|
||||
position = COORD(*position)
|
||||
# If the position is out of range, do nothing.
|
||||
if position.Y <= 0 or position.X <= 0:
|
||||
return
|
||||
# Adjust for Windows' SetConsoleCursorPosition:
|
||||
# 1. being 0-based, while ANSI is 1-based.
|
||||
# 2. expecting (x,y), while ANSI uses (y,x).
|
||||
adjusted_position = COORD(position.Y - 1, position.X - 1)
|
||||
if adjust:
|
||||
# Adjust for viewport's scroll position
|
||||
sr = GetConsoleScreenBufferInfo(STDOUT).srWindow
|
||||
adjusted_position.Y += sr.Top
|
||||
adjusted_position.X += sr.Left
|
||||
# Resume normal processing
|
||||
handle = _GetStdHandle(stream_id)
|
||||
return _SetConsoleCursorPosition(handle, adjusted_position)
|
||||
|
||||
def FillConsoleOutputCharacter(stream_id, char, length, start):
|
||||
handle = _GetStdHandle(stream_id)
|
||||
char = c_char(char.encode())
|
||||
length = wintypes.DWORD(length)
|
||||
num_written = wintypes.DWORD(0)
|
||||
# Note that this is hard-coded for ANSI (vs wide) bytes.
|
||||
success = _FillConsoleOutputCharacterA(
|
||||
handle, char, length, start, byref(num_written))
|
||||
return num_written.value
|
||||
|
||||
def FillConsoleOutputAttribute(stream_id, attr, length, start):
|
||||
''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )'''
|
||||
handle = _GetStdHandle(stream_id)
|
||||
attribute = wintypes.WORD(attr)
|
||||
length = wintypes.DWORD(length)
|
||||
num_written = wintypes.DWORD(0)
|
||||
# Note that this is hard-coded for ANSI (vs wide) bytes.
|
||||
return _FillConsoleOutputAttribute(
|
||||
handle, attribute, length, start, byref(num_written))
|
||||
|
||||
def SetConsoleTitle(title):
|
||||
return _SetConsoleTitleW(title)
|
||||
|
||||
def GetConsoleMode(handle):
|
||||
mode = wintypes.DWORD()
|
||||
success = _GetConsoleMode(handle, byref(mode))
|
||||
if not success:
|
||||
raise ctypes.WinError()
|
||||
return mode.value
|
||||
|
||||
def SetConsoleMode(handle, mode):
|
||||
success = _SetConsoleMode(handle, mode)
|
||||
if not success:
|
||||
raise ctypes.WinError()
|
|
@ -1,195 +0,0 @@
|
|||
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
|
||||
try:
|
||||
from msvcrt import get_osfhandle
|
||||
except ImportError:
|
||||
def get_osfhandle(_):
|
||||
raise OSError("This isn't windows!")
|
||||
|
||||
|
||||
from . import win32
|
||||
|
||||
# from wincon.h
|
||||
class WinColor(object):
|
||||
BLACK = 0
|
||||
BLUE = 1
|
||||
GREEN = 2
|
||||
CYAN = 3
|
||||
RED = 4
|
||||
MAGENTA = 5
|
||||
YELLOW = 6
|
||||
GREY = 7
|
||||
|
||||
# from wincon.h
|
||||
class WinStyle(object):
|
||||
NORMAL = 0x00 # dim text, dim background
|
||||
BRIGHT = 0x08 # bright text, dim background
|
||||
BRIGHT_BACKGROUND = 0x80 # dim text, bright background
|
||||
|
||||
class WinTerm(object):
|
||||
|
||||
def __init__(self):
|
||||
self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes
|
||||
self.set_attrs(self._default)
|
||||
self._default_fore = self._fore
|
||||
self._default_back = self._back
|
||||
self._default_style = self._style
|
||||
# In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style.
|
||||
# So that LIGHT_EX colors and BRIGHT style do not clobber each other,
|
||||
# we track them separately, since LIGHT_EX is overwritten by Fore/Back
|
||||
# and BRIGHT is overwritten by Style codes.
|
||||
self._light = 0
|
||||
|
||||
def get_attrs(self):
|
||||
return self._fore + self._back * 16 + (self._style | self._light)
|
||||
|
||||
def set_attrs(self, value):
|
||||
self._fore = value & 7
|
||||
self._back = (value >> 4) & 7
|
||||
self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND)
|
||||
|
||||
def reset_all(self, on_stderr=None):
|
||||
self.set_attrs(self._default)
|
||||
self.set_console(attrs=self._default)
|
||||
self._light = 0
|
||||
|
||||
def fore(self, fore=None, light=False, on_stderr=False):
|
||||
if fore is None:
|
||||
fore = self._default_fore
|
||||
self._fore = fore
|
||||
# Emulate LIGHT_EX with BRIGHT Style
|
||||
if light:
|
||||
self._light |= WinStyle.BRIGHT
|
||||
else:
|
||||
self._light &= ~WinStyle.BRIGHT
|
||||
self.set_console(on_stderr=on_stderr)
|
||||
|
||||
def back(self, back=None, light=False, on_stderr=False):
|
||||
if back is None:
|
||||
back = self._default_back
|
||||
self._back = back
|
||||
# Emulate LIGHT_EX with BRIGHT_BACKGROUND Style
|
||||
if light:
|
||||
self._light |= WinStyle.BRIGHT_BACKGROUND
|
||||
else:
|
||||
self._light &= ~WinStyle.BRIGHT_BACKGROUND
|
||||
self.set_console(on_stderr=on_stderr)
|
||||
|
||||
def style(self, style=None, on_stderr=False):
|
||||
if style is None:
|
||||
style = self._default_style
|
||||
self._style = style
|
||||
self.set_console(on_stderr=on_stderr)
|
||||
|
||||
def set_console(self, attrs=None, on_stderr=False):
|
||||
if attrs is None:
|
||||
attrs = self.get_attrs()
|
||||
handle = win32.STDOUT
|
||||
if on_stderr:
|
||||
handle = win32.STDERR
|
||||
win32.SetConsoleTextAttribute(handle, attrs)
|
||||
|
||||
def get_position(self, handle):
|
||||
position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition
|
||||
# Because Windows coordinates are 0-based,
|
||||
# and win32.SetConsoleCursorPosition expects 1-based.
|
||||
position.X += 1
|
||||
position.Y += 1
|
||||
return position
|
||||
|
||||
def set_cursor_position(self, position=None, on_stderr=False):
|
||||
if position is None:
|
||||
# I'm not currently tracking the position, so there is no default.
|
||||
# position = self.get_position()
|
||||
return
|
||||
handle = win32.STDOUT
|
||||
if on_stderr:
|
||||
handle = win32.STDERR
|
||||
win32.SetConsoleCursorPosition(handle, position)
|
||||
|
||||
def cursor_adjust(self, x, y, on_stderr=False):
|
||||
handle = win32.STDOUT
|
||||
if on_stderr:
|
||||
handle = win32.STDERR
|
||||
position = self.get_position(handle)
|
||||
adjusted_position = (position.Y + y, position.X + x)
|
||||
win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False)
|
||||
|
||||
def erase_screen(self, mode=0, on_stderr=False):
|
||||
# 0 should clear from the cursor to the end of the screen.
|
||||
# 1 should clear from the cursor to the beginning of the screen.
|
||||
# 2 should clear the entire screen, and move cursor to (1,1)
|
||||
handle = win32.STDOUT
|
||||
if on_stderr:
|
||||
handle = win32.STDERR
|
||||
csbi = win32.GetConsoleScreenBufferInfo(handle)
|
||||
# get the number of character cells in the current buffer
|
||||
cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y
|
||||
# get number of character cells before current cursor position
|
||||
cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X
|
||||
if mode == 0:
|
||||
from_coord = csbi.dwCursorPosition
|
||||
cells_to_erase = cells_in_screen - cells_before_cursor
|
||||
elif mode == 1:
|
||||
from_coord = win32.COORD(0, 0)
|
||||
cells_to_erase = cells_before_cursor
|
||||
elif mode == 2:
|
||||
from_coord = win32.COORD(0, 0)
|
||||
cells_to_erase = cells_in_screen
|
||||
else:
|
||||
# invalid mode
|
||||
return
|
||||
# fill the entire screen with blanks
|
||||
win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
|
||||
# now set the buffer's attributes accordingly
|
||||
win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
|
||||
if mode == 2:
|
||||
# put the cursor where needed
|
||||
win32.SetConsoleCursorPosition(handle, (1, 1))
|
||||
|
||||
def erase_line(self, mode=0, on_stderr=False):
|
||||
# 0 should clear from the cursor to the end of the line.
|
||||
# 1 should clear from the cursor to the beginning of the line.
|
||||
# 2 should clear the entire line.
|
||||
handle = win32.STDOUT
|
||||
if on_stderr:
|
||||
handle = win32.STDERR
|
||||
csbi = win32.GetConsoleScreenBufferInfo(handle)
|
||||
if mode == 0:
|
||||
from_coord = csbi.dwCursorPosition
|
||||
cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X
|
||||
elif mode == 1:
|
||||
from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
|
||||
cells_to_erase = csbi.dwCursorPosition.X
|
||||
elif mode == 2:
|
||||
from_coord = win32.COORD(0, csbi.dwCursorPosition.Y)
|
||||
cells_to_erase = csbi.dwSize.X
|
||||
else:
|
||||
# invalid mode
|
||||
return
|
||||
# fill the entire screen with blanks
|
||||
win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord)
|
||||
# now set the buffer's attributes accordingly
|
||||
win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord)
|
||||
|
||||
def set_title(self, title):
|
||||
win32.SetConsoleTitle(title)
|
||||
|
||||
|
||||
def enable_vt_processing(fd):
|
||||
if win32.windll is None or not win32.winapi_test():
|
||||
return False
|
||||
|
||||
try:
|
||||
handle = get_osfhandle(fd)
|
||||
mode = win32.GetConsoleMode(handle)
|
||||
win32.SetConsoleMode(
|
||||
handle,
|
||||
mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
|
||||
)
|
||||
|
||||
mode = win32.GetConsoleMode(handle)
|
||||
if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING:
|
||||
return True
|
||||
# Can get TypeError in testsuite where 'fd' is a Mock()
|
||||
except (OSError, TypeError):
|
||||
return False
|
|
@ -1 +0,0 @@
|
|||
import os; var = 'SETUPTOOLS_USE_DISTUTILS'; enabled = os.environ.get(var, 'local') == 'local'; enabled and __import__('_distutils_hack').add_shim();
|
|
@ -1,51 +0,0 @@
|
|||
Original Authors
|
||||
----------------
|
||||
* Armin Rigo
|
||||
* Christian Tismer
|
||||
|
||||
Contributors
|
||||
------------
|
||||
* Al Stone
|
||||
* Alexander Schmidt
|
||||
* Alexey Borzenkov
|
||||
* Andreas Schwab
|
||||
* Armin Ronacher
|
||||
* Bin Wang <feisuzhu@163.com>
|
||||
* Bob Ippolito
|
||||
* ChangBo Guo
|
||||
* Christoph Gohlke
|
||||
* Denis Bilenko
|
||||
* Dirk Mueller
|
||||
* Donovan Preston
|
||||
* Fantix King
|
||||
* Floris Bruynooghe
|
||||
* Fredrik Fornwall
|
||||
* Gerd Woetzel
|
||||
* Giel van Schijndel
|
||||
* Gökhan Karabulut
|
||||
* Gustavo Niemeyer
|
||||
* Guy Rozendorn
|
||||
* Hye-Shik Chang
|
||||
* Jared Kuolt
|
||||
* Jason Madden
|
||||
* Josh Snyder
|
||||
* Kyle Ambroff
|
||||
* Laszlo Boszormenyi
|
||||
* Mao Han
|
||||
* Marc Abramowitz
|
||||
* Marc Schlaich
|
||||
* Marcin Bachry
|
||||
* Matt Madison
|
||||
* Matt Turner
|
||||
* Michael Ellerman
|
||||
* Michael Matz
|
||||
* Ralf Schmitt
|
||||
* Robie Basak
|
||||
* Ronny Pfannschmidt
|
||||
* Samual M. Rushing
|
||||
* Tony Bowles
|
||||
* Tony Breeds
|
||||
* Trevor Bowen
|
||||
* Tulio Magno Quites Machado Filho
|
||||
* Ulrich Weigand
|
||||
* Victor Stinner
|
|
@ -1 +0,0 @@
|
|||
pip
|
|
@ -1,30 +0,0 @@
|
|||
The following files are derived from Stackless Python and are subject to the
|
||||
same license as Stackless Python:
|
||||
|
||||
src/greenlet/slp_platformselect.h
|
||||
files in src/greenlet/platform/ directory
|
||||
|
||||
See LICENSE.PSF and http://www.stackless.com/ for details.
|
||||
|
||||
Unless otherwise noted, the files in greenlet have been released under the
|
||||
following MIT license:
|
||||
|
||||
Copyright (c) Armin Rigo, Christian Tismer and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -1,47 +0,0 @@
|
|||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011 Python Software Foundation; All Rights Reserved" are retained in Python
|
||||
alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
|
@ -1,102 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: greenlet
|
||||
Version: 3.0.3
|
||||
Summary: Lightweight in-process concurrent programming
|
||||
Home-page: https://greenlet.readthedocs.io/
|
||||
Author: Alexey Borzenkov
|
||||
Author-email: snaury@gmail.com
|
||||
Maintainer: Jason Madden
|
||||
Maintainer-email: jason@seecoresoftware.com
|
||||
License: MIT License
|
||||
Project-URL: Bug Tracker, https://github.com/python-greenlet/greenlet/issues
|
||||
Project-URL: Source Code, https://github.com/python-greenlet/greenlet/
|
||||
Project-URL: Documentation, https://greenlet.readthedocs.io/
|
||||
Keywords: greenlet coroutine concurrency threads cooperative
|
||||
Platform: any
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Natural Language :: English
|
||||
Classifier: Programming Language :: C
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3 :: Only
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Requires-Python: >=3.7
|
||||
Description-Content-Type: text/x-rst
|
||||
License-File: LICENSE
|
||||
License-File: LICENSE.PSF
|
||||
License-File: AUTHORS
|
||||
Provides-Extra: docs
|
||||
Requires-Dist: Sphinx ; extra == 'docs'
|
||||
Requires-Dist: furo ; extra == 'docs'
|
||||
Provides-Extra: test
|
||||
Requires-Dist: objgraph ; extra == 'test'
|
||||
Requires-Dist: psutil ; extra == 'test'
|
||||
|
||||
.. This file is included into docs/history.rst
|
||||
|
||||
|
||||
Greenlets are lightweight coroutines for in-process concurrent
|
||||
programming.
|
||||
|
||||
The "greenlet" package is a spin-off of `Stackless`_, a version of
|
||||
CPython that supports micro-threads called "tasklets". Tasklets run
|
||||
pseudo-concurrently (typically in a single or a few OS-level threads)
|
||||
and are synchronized with data exchanges on "channels".
|
||||
|
||||
A "greenlet", on the other hand, is a still more primitive notion of
|
||||
micro-thread with no implicit scheduling; coroutines, in other words.
|
||||
This is useful when you want to control exactly when your code runs.
|
||||
You can build custom scheduled micro-threads on top of greenlet;
|
||||
however, it seems that greenlets are useful on their own as a way to
|
||||
make advanced control flow structures. For example, we can recreate
|
||||
generators; the difference with Python's own generators is that our
|
||||
generators can call nested functions and the nested functions can
|
||||
yield values too. (Additionally, you don't need a "yield" keyword. See
|
||||
the example in `test_generator.py
|
||||
<https://github.com/python-greenlet/greenlet/blob/adca19bf1f287b3395896a8f41f3f4fd1797fdc7/src/greenlet/tests/test_generator.py#L1>`_).
|
||||
|
||||
Greenlets are provided as a C extension module for the regular unmodified
|
||||
interpreter.
|
||||
|
||||
.. _`Stackless`: http://www.stackless.com
|
||||
|
||||
|
||||
Who is using Greenlet?
|
||||
======================
|
||||
|
||||
There are several libraries that use Greenlet as a more flexible
|
||||
alternative to Python's built in coroutine support:
|
||||
|
||||
- `Concurrence`_
|
||||
- `Eventlet`_
|
||||
- `Gevent`_
|
||||
|
||||
.. _Concurrence: http://opensource.hyves.org/concurrence/
|
||||
.. _Eventlet: http://eventlet.net/
|
||||
.. _Gevent: http://www.gevent.org/
|
||||
|
||||
Getting Greenlet
|
||||
================
|
||||
|
||||
The easiest way to get Greenlet is to install it with pip::
|
||||
|
||||
pip install greenlet
|
||||
|
||||
|
||||
Source code archives and binary distributions are available on the
|
||||
python package index at https://pypi.org/project/greenlet
|
||||
|
||||
The source code repository is hosted on github:
|
||||
https://github.com/python-greenlet/greenlet
|
||||
|
||||
Documentation is available on readthedocs.org:
|
||||
https://greenlet.readthedocs.io
|
|
@ -1,117 +0,0 @@
|
|||
../../../include/site/python3.11/greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755
|
||||
greenlet-3.0.3.dist-info/AUTHORS,sha256=swW28t2knVRxRkaEQNZtO7MP9Sgnompb7B6cNgJM8Gk,849
|
||||
greenlet-3.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
greenlet-3.0.3.dist-info/LICENSE,sha256=dpgx1uXfrywggC-sz_H6-0wgJd2PYlPfpH_K1Z1NCXk,1434
|
||||
greenlet-3.0.3.dist-info/LICENSE.PSF,sha256=5f88I8EQ5JTNfXNsEP2W1GJFe6_soxCEDbZScpjH1Gs,2424
|
||||
greenlet-3.0.3.dist-info/METADATA,sha256=CHtHlitUM_AS9hKoJfYLF3Vz-UFJlqRnhbRl2-1JrjU,3779
|
||||
greenlet-3.0.3.dist-info/RECORD,,
|
||||
greenlet-3.0.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
greenlet-3.0.3.dist-info/WHEEL,sha256=xlJUan517virathN2lKmlOcMObJx20JZaCR_iv23glU,153
|
||||
greenlet-3.0.3.dist-info/top_level.txt,sha256=YSnRsCRoO61JGlP57o8iKL6rdLWDWuiyKD8ekpWUsDc,9
|
||||
greenlet/TBrokenGreenlet.cpp,sha256=YgKaHkQV6_dKBrgS0HKDSqZroskv0IwSZDo4bsiwz3w,1029
|
||||
greenlet/TExceptionState.cpp,sha256=Ctg2YfyEYNjOYbteRB_oIJa9lNGyC7N1F3h4XqqQdg8,1367
|
||||
greenlet/TGreenlet.cpp,sha256=1xwAzGNqO68AZ4D5lD5DHmGPBohM6nv4BYnLatgIL68,25637
|
||||
greenlet/TGreenletGlobals.cpp,sha256=qLi1icS1UDSbefTkolz9TycEi_GOUblsEznMp0HFywQ,3268
|
||||
greenlet/TMainGreenlet.cpp,sha256=FvWtGJDKb64DLy0n-ddcTF6xJDwczPMKSm9mXSsHJKg,3365
|
||||
greenlet/TPythonState.cpp,sha256=QUoIQzF0HYmAJO_nwX5gXSSlMNL1mkxlN24KJCXIrIQ,14861
|
||||
greenlet/TStackState.cpp,sha256=VclDR-qiMeJjuiJxL9_u24MJiTgdSaYvr8bWQdTEZjY,7389
|
||||
greenlet/TThreadStateDestroy.cpp,sha256=EqZ-GjksrWNC20CY_P0yXN43wVRMYEh659SmRRqBaI4,7214
|
||||
greenlet/TUserGreenlet.cpp,sha256=b_Bmh4WZdS6I1yM2AfHRtd535WovtpYMkpfu2GQpaDs,23618
|
||||
greenlet/__init__.py,sha256=Dw4tovn18bpPaWQ4SK7jDJe24uV4ao264UfaT0uufxU,1723
|
||||
greenlet/__pycache__/__init__.cpython-311.pyc,,
|
||||
greenlet/_greenlet.cpython-311-x86_64-linux-gnu.so,sha256=89kThwDfvkHXs3GXeuXnnZb-wShF60h1XyHXZYmkymU,1506232
|
||||
greenlet/greenlet.cpp,sha256=k9RZolayY79WgjPXwcA3Vcv48MuW7TAtogIZPaDD3gM,48815
|
||||
greenlet/greenlet.h,sha256=sz5pYRSQqedgOt2AMgxLZdTjO-qcr_JMvgiEJR9IAJ8,4755
|
||||
greenlet/greenlet_allocator.hpp,sha256=kxyWW4Qdwlrc7ufgdb5vd6Y7jhauQ699Kod0mqiO1iM,1582
|
||||
greenlet/greenlet_compiler_compat.hpp,sha256=m7wvwrZqBoCQpDMTP-Z7whdXIES7e3AuXBgvPHSsfxg,4140
|
||||
greenlet/greenlet_cpython_add_pending.hpp,sha256=apAwIhGlgYrnYn03zWL6Sxy68kltDeb1e0QupZfb3DQ,6043
|
||||
greenlet/greenlet_cpython_compat.hpp,sha256=ZpN8gewZeOtd6T-mLidA7zteQ_P4vG8T1za_KPvCijg,3621
|
||||
greenlet/greenlet_exceptions.hpp,sha256=Dt8YdaQn8AK9nBfwU9rrDoMlR2Lw5aLTQV6ZAsHmfsw,3683
|
||||
greenlet/greenlet_greenlet.hpp,sha256=Ct_EAx4OJL6FvF5g3jV1ybSxnqzLVaRdPi2EcYT1iq4,27728
|
||||
greenlet/greenlet_internal.hpp,sha256=ZXH5zemWCN8wH8zAqMUGycvz_3IulRL6Gf2hZA6CknE,2703
|
||||
greenlet/greenlet_refs.hpp,sha256=ECkHKV1CVamtzmWWGKXXMpw8lXLeIzastXM9tfqlsNI,33864
|
||||
greenlet/greenlet_slp_switch.hpp,sha256=kM1QHA2iV-gH4cFyN6lfIagHQxvJZjWOVJdIxRE3TlQ,3198
|
||||
greenlet/greenlet_thread_state.hpp,sha256=0UwJCNd86ifwM2yDd3QrNmHAECL-eNADHubwiB_XGA4,20614
|
||||
greenlet/greenlet_thread_state_dict_cleanup.hpp,sha256=tEN0rI1pZiEsdtr7Oda24gr52fGiHnYTLyM8Vme3Gns,3831
|
||||
greenlet/greenlet_thread_support.hpp,sha256=XUJ6ljWjf9OYyuOILiz8e_yHvT3fbaUiHdhiPNQUV4s,867
|
||||
greenlet/platform/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
greenlet/platform/__pycache__/__init__.cpython-311.pyc,,
|
||||
greenlet/platform/setup_switch_x64_masm.cmd,sha256=ZpClUJeU0ujEPSTWNSepP0W2f9XiYQKA8QKSoVou8EU,143
|
||||
greenlet/platform/switch_aarch64_gcc.h,sha256=GKC0yWNXnbK2X--X6aguRCMj2Tg7hDU1Zkl3RljDvC8,4307
|
||||
greenlet/platform/switch_alpha_unix.h,sha256=Z-SvF8JQV3oxWT8JRbL9RFu4gRFxPdJ7cviM8YayMmw,671
|
||||
greenlet/platform/switch_amd64_unix.h,sha256=EcSFCBlodEBhqhKjcJqY_5Dn_jn7pKpkJlOvp7gFXLI,2748
|
||||
greenlet/platform/switch_arm32_gcc.h,sha256=Z3KkHszdgq6uU4YN3BxvKMG2AdDnovwCCNrqGWZ1Lyo,2479
|
||||
greenlet/platform/switch_arm32_ios.h,sha256=mm5_R9aXB92hyxzFRwB71M60H6AlvHjrpTrc72Pz3l8,1892
|
||||
greenlet/platform/switch_arm64_masm.asm,sha256=4kpTtfy7rfcr8j1CpJLAK21EtZpGDAJXWRU68HEy5A8,1245
|
||||
greenlet/platform/switch_arm64_masm.obj,sha256=DmLnIB_icoEHAz1naue_pJPTZgR9ElM7-Nmztr-o9_U,746
|
||||
greenlet/platform/switch_arm64_msvc.h,sha256=RqK5MHLmXI3Q-FQ7tm32KWnbDNZKnkJdq8CR89cz640,398
|
||||
greenlet/platform/switch_csky_gcc.h,sha256=kDikyiPpewP71KoBZQO_MukDTXTXBiC7x-hF0_2DL0w,1331
|
||||
greenlet/platform/switch_loongarch64_linux.h,sha256=7M-Dhc4Q8tRbJCJhalDLwU6S9Mx8MjmN1RbTDgIvQTM,779
|
||||
greenlet/platform/switch_m68k_gcc.h,sha256=VSa6NpZhvyyvF-Q58CTIWSpEDo4FKygOyTz00whctlw,928
|
||||
greenlet/platform/switch_mips_unix.h,sha256=E0tYsqc5anDY1BhenU1l8DW-nVHC_BElzLgJw3TGtPk,1426
|
||||
greenlet/platform/switch_ppc64_aix.h,sha256=_BL0iyRr3ZA5iPlr3uk9SJ5sNRWGYLrXcZ5z-CE9anE,3860
|
||||
greenlet/platform/switch_ppc64_linux.h,sha256=0rriT5XyxPb0GqsSSn_bP9iQsnjsPbBmu0yqo5goSyQ,3815
|
||||
greenlet/platform/switch_ppc_aix.h,sha256=pHA4slEjUFP3J3SYm1TAlNPhgb2G_PAtax5cO8BEe1A,2941
|
||||
greenlet/platform/switch_ppc_linux.h,sha256=YwrlKUzxlXuiKMQqr6MFAV1bPzWnmvk6X1AqJZEpOWU,2759
|
||||
greenlet/platform/switch_ppc_macosx.h,sha256=L8sB0c00V4G2_5cQCG3zX-23DKq3le_Dcj0sUDcACos,2624
|
||||
greenlet/platform/switch_ppc_unix.h,sha256=POy4bRBcH74Chfw4viFE9bVlZ-7BaNsFC0NnXr1L2tg,2652
|
||||
greenlet/platform/switch_riscv_unix.h,sha256=jX3vC_xZXiUho8tz4J6Ai8BNQB80yLn03fxkoMztVCU,740
|
||||
greenlet/platform/switch_s390_unix.h,sha256=RRlGu957ybmq95qNNY4Qw1mcaoT3eBnW5KbVwu48KX8,2763
|
||||
greenlet/platform/switch_sparc_sun_gcc.h,sha256=xZish9GsMHBienUbUMsX1-ZZ-as7hs36sVhYIE3ew8Y,2797
|
||||
greenlet/platform/switch_x32_unix.h,sha256=nM98PKtzTWc1lcM7TRMUZJzskVdR1C69U1UqZRWX0GE,1509
|
||||
greenlet/platform/switch_x64_masm.asm,sha256=nu6n2sWyXuXfpPx40d9YmLfHXUc1sHgeTvX1kUzuvEM,1841
|
||||
greenlet/platform/switch_x64_masm.obj,sha256=GNtTNxYdo7idFUYsQv-mrXWgyT5EJ93-9q90lN6svtQ,1078
|
||||
greenlet/platform/switch_x64_msvc.h,sha256=LIeasyKo_vHzspdMzMHbosRhrBfKI4BkQOh4qcTHyJw,1805
|
||||
greenlet/platform/switch_x86_msvc.h,sha256=TtGOwinbFfnn6clxMNkCz8i6OmgB6kVRrShoF5iT9to,12838
|
||||
greenlet/platform/switch_x86_unix.h,sha256=VplW9H0FF0cZHw1DhJdIUs5q6YLS4cwb2nYwjF83R1s,3059
|
||||
greenlet/slp_platformselect.h,sha256=JEnia_2HsTwdqvnnEsDxHQqalYvFJqx_CDsqvNUQYe8,3600
|
||||
greenlet/tests/__init__.py,sha256=F282jaIavKrhsYgHJEXtIQXKHdHpe9OJOPTK7R40JzI,9022
|
||||
greenlet/tests/__pycache__/__init__.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/fail_clearing_run_switches.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/fail_cpp_exception.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/fail_initialstub_already_started.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/fail_slp_switch.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/fail_switch_three_greenlets.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/fail_switch_three_greenlets2.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/fail_switch_two_greenlets.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/leakcheck.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_contextvars.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_cpp.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_extension_interface.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_gc.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_generator.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_generator_nested.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_greenlet.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_greenlet_trash.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_leaks.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_stack_saved.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_throw.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_tracing.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_version.cpython-311.pyc,,
|
||||
greenlet/tests/__pycache__/test_weakref.cpython-311.pyc,,
|
||||
greenlet/tests/_test_extension.c,sha256=vkeGA-6oeJcGILsD7oIrT1qZop2GaTOHXiNT7mcSl-0,5773
|
||||
greenlet/tests/_test_extension.cpython-311-x86_64-linux-gnu.so,sha256=cYvKKnDFhjTDjM_mYc_4l53g44Iz-CJR5woKXR6Ddqg,36624
|
||||
greenlet/tests/_test_extension_cpp.cpp,sha256=e0kVnaB8CCaEhE9yHtNyfqTjevsPDKKx-zgxk7PPK48,6565
|
||||
greenlet/tests/_test_extension_cpp.cpython-311-x86_64-linux-gnu.so,sha256=de1fYlFMrBJRAwPKHWl-OMuBy8AmSXsh14FYYyLj6dI,57288
|
||||
greenlet/tests/fail_clearing_run_switches.py,sha256=o433oA_nUCtOPaMEGc8VEhZIKa71imVHXFw7TsXaP8M,1263
|
||||
greenlet/tests/fail_cpp_exception.py,sha256=o_ZbipWikok8Bjc-vjiQvcb5FHh2nVW-McGKMLcMzh0,985
|
||||
greenlet/tests/fail_initialstub_already_started.py,sha256=txENn5IyzGx2p-XR1XB7qXmC8JX_4mKDEA8kYBXUQKc,1961
|
||||
greenlet/tests/fail_slp_switch.py,sha256=rJBZcZfTWR3e2ERQtPAud6YKShiDsP84PmwOJbp4ey0,524
|
||||
greenlet/tests/fail_switch_three_greenlets.py,sha256=zSitV7rkNnaoHYVzAGGLnxz-yPtohXJJzaE8ehFDQ0M,956
|
||||
greenlet/tests/fail_switch_three_greenlets2.py,sha256=FPJensn2EJxoropl03JSTVP3kgP33k04h6aDWWozrOk,1285
|
||||
greenlet/tests/fail_switch_two_greenlets.py,sha256=1CaI8s3504VbbF1vj1uBYuy-zxBHVzHPIAd1LIc8ONg,817
|
||||
greenlet/tests/leakcheck.py,sha256=inbfM7_oVzd8jIKGxCgo4JqpFZaDAnWPkSULJ8vIE1s,11964
|
||||
greenlet/tests/test_contextvars.py,sha256=0n5pR_lbpAppc5wFfK0e1SwYLM-fsSFp72B5_ArLPGE,10348
|
||||
greenlet/tests/test_cpp.py,sha256=hpxhFAdKJTpAVZP8CBGs1ZcrKdscI9BaDZk4btkI5d4,2736
|
||||
greenlet/tests/test_extension_interface.py,sha256=eJ3cwLacdK2WbsrC-4DgeyHdwLRcG4zx7rrkRtqSzC4,3829
|
||||
greenlet/tests/test_gc.py,sha256=PCOaRpIyjNnNlDogGL3FZU_lrdXuM-pv1rxeE5TP5mc,2923
|
||||
greenlet/tests/test_generator.py,sha256=tONXiTf98VGm347o1b-810daPiwdla5cbpFg6QI1R1g,1240
|
||||
greenlet/tests/test_generator_nested.py,sha256=7v4HOYrf1XZP39dk5IUMubdZ8yc3ynwZcqj9GUJyMSA,3718
|
||||
greenlet/tests/test_greenlet.py,sha256=95qgDR-xtB0jzEFLirNx7HPUdwHikVMvDdyUoCvyjOo,45354
|
||||
greenlet/tests/test_greenlet_trash.py,sha256=P6r-3K4fmXX8foW8BVgthuqVKjicHMDvxfK7Al4x028,7508
|
||||
greenlet/tests/test_leaks.py,sha256=wskLqCAvqZ3qTZkam_wXzd-E5zelUjlXS5Ss8KshtZY,17465
|
||||
greenlet/tests/test_stack_saved.py,sha256=eyzqNY2VCGuGlxhT_In6TvZ6Okb0AXFZVyBEnK1jDwA,446
|
||||
greenlet/tests/test_throw.py,sha256=u2TQ_WvvCd6N6JdXWIxVEcXkKu5fepDlz9dktYdmtng,3712
|
||||
greenlet/tests/test_tracing.py,sha256=VlwzMU0C1noospZhuUMyB7MHw200emIvGCN_6G2p2ZU,8250
|
||||
greenlet/tests/test_version.py,sha256=O9DpAITsOFgiRcjd4odQ7ejmwx_N9Q1zQENVcbtFHIc,1339
|
||||
greenlet/tests/test_weakref.py,sha256=F8M23btEF87bIbpptLNBORosbQqNZGiYeKMqYjWrsak,883
|
|
@ -1,6 +0,0 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.42.0)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp311-cp311-manylinux_2_24_x86_64
|
||||
Tag: cp311-cp311-manylinux_2_28_x86_64
|
||||
|
|
@ -1 +0,0 @@
|
|||
greenlet
|
|
@ -1,45 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
/**
|
||||
* Implementation of greenlet::UserGreenlet.
|
||||
*
|
||||
* Format with:
|
||||
* clang-format -i --style=file src/greenlet/greenlet.c
|
||||
*
|
||||
*
|
||||
* Fix missing braces with:
|
||||
* clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
|
||||
*/
|
||||
|
||||
#include "greenlet_greenlet.hpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
void* BrokenGreenlet::operator new(size_t UNUSED(count))
|
||||
{
|
||||
return allocator.allocate(1);
|
||||
}
|
||||
|
||||
|
||||
void BrokenGreenlet::operator delete(void* ptr)
|
||||
{
|
||||
return allocator.deallocate(static_cast<BrokenGreenlet*>(ptr),
|
||||
1);
|
||||
}
|
||||
|
||||
greenlet::PythonAllocator<greenlet::BrokenGreenlet> greenlet::BrokenGreenlet::allocator;
|
||||
|
||||
bool
|
||||
BrokenGreenlet::force_slp_switch_error() const noexcept
|
||||
{
|
||||
return this->_force_slp_switch_error;
|
||||
}
|
||||
|
||||
UserGreenlet::switchstack_result_t BrokenGreenlet::g_switchstack(void)
|
||||
{
|
||||
if (this->_force_switch_error) {
|
||||
return switchstack_result_t(-1);
|
||||
}
|
||||
return UserGreenlet::g_switchstack();
|
||||
}
|
||||
|
||||
}; //namespace greenlet
|
|
@ -1,62 +0,0 @@
|
|||
#ifndef GREENLET_EXCEPTION_STATE_CPP
|
||||
#define GREENLET_EXCEPTION_STATE_CPP
|
||||
|
||||
#include <Python.h>
|
||||
#include "greenlet_greenlet.hpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
|
||||
ExceptionState::ExceptionState()
|
||||
{
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void ExceptionState::operator<<(const PyThreadState *const tstate) noexcept
|
||||
{
|
||||
this->exc_info = tstate->exc_info;
|
||||
this->exc_state = tstate->exc_state;
|
||||
}
|
||||
|
||||
void ExceptionState::operator>>(PyThreadState *const tstate) noexcept
|
||||
{
|
||||
tstate->exc_state = this->exc_state;
|
||||
tstate->exc_info =
|
||||
this->exc_info ? this->exc_info : &tstate->exc_state;
|
||||
this->clear();
|
||||
}
|
||||
|
||||
void ExceptionState::clear() noexcept
|
||||
{
|
||||
this->exc_info = nullptr;
|
||||
this->exc_state.exc_value = nullptr;
|
||||
#if !GREENLET_PY311
|
||||
this->exc_state.exc_type = nullptr;
|
||||
this->exc_state.exc_traceback = nullptr;
|
||||
#endif
|
||||
this->exc_state.previous_item = nullptr;
|
||||
}
|
||||
|
||||
int ExceptionState::tp_traverse(visitproc visit, void* arg) noexcept
|
||||
{
|
||||
Py_VISIT(this->exc_state.exc_value);
|
||||
#if !GREENLET_PY311
|
||||
Py_VISIT(this->exc_state.exc_type);
|
||||
Py_VISIT(this->exc_state.exc_traceback);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExceptionState::tp_clear() noexcept
|
||||
{
|
||||
Py_CLEAR(this->exc_state.exc_value);
|
||||
#if !GREENLET_PY311
|
||||
Py_CLEAR(this->exc_state.exc_type);
|
||||
Py_CLEAR(this->exc_state.exc_traceback);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
#endif // GREENLET_EXCEPTION_STATE_CPP
|
|
@ -1,714 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
/**
|
||||
* Implementation of greenlet::Greenlet.
|
||||
*
|
||||
* Format with:
|
||||
* clang-format -i --style=file src/greenlet/greenlet.c
|
||||
*
|
||||
*
|
||||
* Fix missing braces with:
|
||||
* clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
|
||||
*/
|
||||
|
||||
#include "greenlet_internal.hpp"
|
||||
#include "greenlet_greenlet.hpp"
|
||||
#include "greenlet_thread_state.hpp"
|
||||
|
||||
#include "TGreenletGlobals.cpp"
|
||||
#include "TThreadStateDestroy.cpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
Greenlet::Greenlet(PyGreenlet* p)
|
||||
{
|
||||
p ->pimpl = this;
|
||||
}
|
||||
|
||||
Greenlet::~Greenlet()
|
||||
{
|
||||
// XXX: Can't do this. tp_clear is a virtual function, and by the
|
||||
// time we're here, we've sliced off our child classes.
|
||||
//this->tp_clear();
|
||||
}
|
||||
|
||||
Greenlet::Greenlet(PyGreenlet* p, const StackState& initial_stack)
|
||||
: stack_state(initial_stack)
|
||||
{
|
||||
// can't use a delegating constructor because of
|
||||
// MSVC for Python 2.7
|
||||
p->pimpl = this;
|
||||
}
|
||||
|
||||
bool
|
||||
Greenlet::force_slp_switch_error() const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Greenlet::release_args()
|
||||
{
|
||||
this->switch_args.CLEAR();
|
||||
}
|
||||
|
||||
/**
|
||||
* CAUTION: This will allocate memory and may trigger garbage
|
||||
* collection and arbitrary Python code.
|
||||
*/
|
||||
OwnedObject
|
||||
Greenlet::throw_GreenletExit_during_dealloc(const ThreadState& UNUSED(current_thread_state))
|
||||
{
|
||||
// If we're killed because we lost all references in the
|
||||
// middle of a switch, that's ok. Don't reset the args/kwargs,
|
||||
// we still want to pass them to the parent.
|
||||
PyErr_SetString(mod_globs->PyExc_GreenletExit,
|
||||
"Killing the greenlet because all references have vanished.");
|
||||
// To get here it had to have run before
|
||||
return this->g_switch();
|
||||
}
|
||||
|
||||
inline void
|
||||
Greenlet::slp_restore_state() noexcept
|
||||
{
|
||||
#ifdef SLP_BEFORE_RESTORE_STATE
|
||||
SLP_BEFORE_RESTORE_STATE();
|
||||
#endif
|
||||
this->stack_state.copy_heap_to_stack(
|
||||
this->thread_state()->borrow_current()->stack_state);
|
||||
}
|
||||
|
||||
|
||||
inline int
|
||||
Greenlet::slp_save_state(char *const stackref) noexcept
|
||||
{
|
||||
// XXX: This used to happen in the middle, before saving, but
|
||||
// after finding the next owner. Does that matter? This is
|
||||
// only defined for Sparc/GCC where it flushes register
|
||||
// windows to the stack (I think)
|
||||
#ifdef SLP_BEFORE_SAVE_STATE
|
||||
SLP_BEFORE_SAVE_STATE();
|
||||
#endif
|
||||
return this->stack_state.copy_stack_to_heap(stackref,
|
||||
this->thread_state()->borrow_current()->stack_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* CAUTION: This will allocate memory and may trigger garbage
|
||||
* collection and arbitrary Python code.
|
||||
*/
|
||||
OwnedObject
|
||||
Greenlet::on_switchstack_or_initialstub_failure(
|
||||
Greenlet* target,
|
||||
const Greenlet::switchstack_result_t& err,
|
||||
const bool target_was_me,
|
||||
const bool was_initial_stub)
|
||||
{
|
||||
// If we get here, either g_initialstub()
|
||||
// failed, or g_switchstack() failed. Either one of those
|
||||
// cases SHOULD leave us in the original greenlet with a valid stack.
|
||||
if (!PyErr_Occurred()) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
was_initial_stub
|
||||
? "Failed to switch stacks into a greenlet for the first time."
|
||||
: "Failed to switch stacks into a running greenlet.");
|
||||
}
|
||||
this->release_args();
|
||||
|
||||
if (target && !target_was_me) {
|
||||
target->murder_in_place();
|
||||
}
|
||||
|
||||
assert(!err.the_new_current_greenlet);
|
||||
assert(!err.origin_greenlet);
|
||||
return OwnedObject();
|
||||
|
||||
}
|
||||
|
||||
OwnedGreenlet
|
||||
Greenlet::g_switchstack_success() noexcept
|
||||
{
|
||||
PyThreadState* tstate = PyThreadState_GET();
|
||||
// restore the saved state
|
||||
this->python_state >> tstate;
|
||||
this->exception_state >> tstate;
|
||||
|
||||
// The thread state hasn't been changed yet.
|
||||
ThreadState* thread_state = this->thread_state();
|
||||
OwnedGreenlet result(thread_state->get_current());
|
||||
thread_state->set_current(this->self());
|
||||
//assert(thread_state->borrow_current().borrow() == this->_self);
|
||||
return result;
|
||||
}
|
||||
|
||||
Greenlet::switchstack_result_t
|
||||
Greenlet::g_switchstack(void)
|
||||
{
|
||||
// if any of these assertions fail, it's likely because we
|
||||
// switched away and tried to switch back to us. Early stages of
|
||||
// switching are not reentrant because we re-use ``this->args()``.
|
||||
// Switching away would happen if we trigger a garbage collection
|
||||
// (by just using some Python APIs that happen to allocate Python
|
||||
// objects) and some garbage had weakref callbacks or __del__ that
|
||||
// switches (people don't write code like that by hand, but with
|
||||
// gevent it's possible without realizing it)
|
||||
assert(this->args() || PyErr_Occurred());
|
||||
{ /* save state */
|
||||
if (this->thread_state()->is_current(this->self())) {
|
||||
// Hmm, nothing to do.
|
||||
// TODO: Does this bypass trace events that are
|
||||
// important?
|
||||
return switchstack_result_t(0,
|
||||
this, this->thread_state()->borrow_current());
|
||||
}
|
||||
BorrowedGreenlet current = this->thread_state()->borrow_current();
|
||||
PyThreadState* tstate = PyThreadState_GET();
|
||||
|
||||
current->python_state << tstate;
|
||||
current->exception_state << tstate;
|
||||
this->python_state.will_switch_from(tstate);
|
||||
switching_thread_state = this;
|
||||
current->expose_frames();
|
||||
}
|
||||
assert(this->args() || PyErr_Occurred());
|
||||
// If this is the first switch into a greenlet, this will
|
||||
// return twice, once with 1 in the new greenlet, once with 0
|
||||
// in the origin.
|
||||
int err;
|
||||
if (this->force_slp_switch_error()) {
|
||||
err = -1;
|
||||
}
|
||||
else {
|
||||
err = slp_switch();
|
||||
}
|
||||
|
||||
if (err < 0) { /* error */
|
||||
// Tested by
|
||||
// test_greenlet.TestBrokenGreenlets.test_failed_to_slp_switch_into_running
|
||||
//
|
||||
// It's not clear if it's worth trying to clean up and
|
||||
// continue here. Failing to switch stacks is a big deal which
|
||||
// may not be recoverable (who knows what state the stack is in).
|
||||
// Also, we've stolen references in preparation for calling
|
||||
// ``g_switchstack_success()`` and we don't have a clean
|
||||
// mechanism for backing that all out.
|
||||
Py_FatalError("greenlet: Failed low-level slp_switch(). The stack is probably corrupt.");
|
||||
}
|
||||
|
||||
// No stack-based variables are valid anymore.
|
||||
|
||||
// But the global is volatile so we can reload it without the
|
||||
// compiler caching it from earlier.
|
||||
Greenlet* greenlet_that_switched_in = switching_thread_state; // aka this
|
||||
switching_thread_state = nullptr;
|
||||
// except that no stack variables are valid, we would:
|
||||
// assert(this == greenlet_that_switched_in);
|
||||
|
||||
// switchstack success is where we restore the exception state,
|
||||
// etc. It returns the origin greenlet because its convenient.
|
||||
|
||||
OwnedGreenlet origin = greenlet_that_switched_in->g_switchstack_success();
|
||||
assert(greenlet_that_switched_in->args() || PyErr_Occurred());
|
||||
return switchstack_result_t(err, greenlet_that_switched_in, origin);
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
Greenlet::check_switch_allowed() const
|
||||
{
|
||||
// TODO: Make this take a parameter of the current greenlet,
|
||||
// or current main greenlet, to make the check for
|
||||
// cross-thread switching cheaper. Surely somewhere up the
|
||||
// call stack we've already accessed the thread local variable.
|
||||
|
||||
// We expect to always have a main greenlet now; accessing the thread state
|
||||
// created it. However, if we get here and cleanup has already
|
||||
// begun because we're a greenlet that was running in a
|
||||
// (now dead) thread, these invariants will not hold true. In
|
||||
// fact, accessing `this->thread_state` may not even be possible.
|
||||
|
||||
// If the thread this greenlet was running in is dead,
|
||||
// we'll still have a reference to a main greenlet, but the
|
||||
// thread state pointer we have is bogus.
|
||||
// TODO: Give the objects an API to determine if they belong
|
||||
// to a dead thread.
|
||||
|
||||
const BorrowedMainGreenlet main_greenlet = this->find_main_greenlet_in_lineage();
|
||||
|
||||
if (!main_greenlet) {
|
||||
throw PyErrOccurred(mod_globs->PyExc_GreenletError,
|
||||
"cannot switch to a garbage collected greenlet");
|
||||
}
|
||||
|
||||
if (!main_greenlet->thread_state()) {
|
||||
throw PyErrOccurred(mod_globs->PyExc_GreenletError,
|
||||
"cannot switch to a different thread (which happens to have exited)");
|
||||
}
|
||||
|
||||
// The main greenlet we found was from the .parent lineage.
|
||||
// That may or may not have any relationship to the main
|
||||
// greenlet of the running thread. We can't actually access
|
||||
// our this->thread_state members to try to check that,
|
||||
// because it could be in the process of getting destroyed,
|
||||
// but setting the main_greenlet->thread_state member to NULL
|
||||
// may not be visible yet. So we need to check against the
|
||||
// current thread state (once the cheaper checks are out of
|
||||
// the way)
|
||||
const BorrowedMainGreenlet current_main_greenlet = GET_THREAD_STATE().state().borrow_main_greenlet();
|
||||
if (
|
||||
// lineage main greenlet is not this thread's greenlet
|
||||
current_main_greenlet != main_greenlet
|
||||
|| (
|
||||
// atteched to some thread
|
||||
this->main_greenlet()
|
||||
// XXX: Same condition as above. Was this supposed to be
|
||||
// this->main_greenlet()?
|
||||
&& current_main_greenlet != main_greenlet)
|
||||
// switching into a known dead thread (XXX: which, if we get here,
|
||||
// is bad, because we just accessed the thread state, which is
|
||||
// gone!)
|
||||
|| (!current_main_greenlet->thread_state())) {
|
||||
// CAUTION: This may trigger memory allocations, gc, and
|
||||
// arbitrary Python code.
|
||||
throw PyErrOccurred(mod_globs->PyExc_GreenletError,
|
||||
"cannot switch to a different thread");
|
||||
}
|
||||
}
|
||||
|
||||
const OwnedObject
|
||||
Greenlet::context() const
|
||||
{
|
||||
using greenlet::PythonStateContext;
|
||||
OwnedObject result;
|
||||
|
||||
if (this->is_currently_running_in_some_thread()) {
|
||||
/* Currently running greenlet: context is stored in the thread state,
|
||||
not the greenlet object. */
|
||||
if (GET_THREAD_STATE().state().is_current(this->self())) {
|
||||
result = PythonStateContext::context(PyThreadState_GET());
|
||||
}
|
||||
else {
|
||||
throw ValueError(
|
||||
"cannot get context of a "
|
||||
"greenlet that is running in a different thread");
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Greenlet is not running: just return context. */
|
||||
result = this->python_state.context();
|
||||
}
|
||||
if (!result) {
|
||||
result = OwnedObject::None();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Greenlet::context(BorrowedObject given)
|
||||
{
|
||||
using greenlet::PythonStateContext;
|
||||
if (!given) {
|
||||
throw AttributeError("can't delete context attribute");
|
||||
}
|
||||
if (given.is_None()) {
|
||||
/* "Empty context" is stored as NULL, not None. */
|
||||
given = nullptr;
|
||||
}
|
||||
|
||||
//checks type, incrs refcnt
|
||||
greenlet::refs::OwnedContext context(given);
|
||||
PyThreadState* tstate = PyThreadState_GET();
|
||||
|
||||
if (this->is_currently_running_in_some_thread()) {
|
||||
if (!GET_THREAD_STATE().state().is_current(this->self())) {
|
||||
throw ValueError("cannot set context of a greenlet"
|
||||
" that is running in a different thread");
|
||||
}
|
||||
|
||||
/* Currently running greenlet: context is stored in the thread state,
|
||||
not the greenlet object. */
|
||||
OwnedObject octx = OwnedObject::consuming(PythonStateContext::context(tstate));
|
||||
PythonStateContext::context(tstate, context.relinquish_ownership());
|
||||
}
|
||||
else {
|
||||
/* Greenlet is not running: just set context. Note that the
|
||||
greenlet may be dead.*/
|
||||
this->python_state.context() = context;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CAUTION: May invoke arbitrary Python code.
|
||||
*
|
||||
* Figure out what the result of ``greenlet.switch(arg, kwargs)``
|
||||
* should be and transfers ownership of it to the left-hand-side.
|
||||
*
|
||||
* If switch() was just passed an arg tuple, then we'll just return that.
|
||||
* If only keyword arguments were passed, then we'll pass the keyword
|
||||
* argument dict. Otherwise, we'll create a tuple of (args, kwargs) and
|
||||
* return both.
|
||||
*
|
||||
* CAUTION: This may allocate a new tuple object, which may
|
||||
* cause the Python garbage collector to run, which in turn may
|
||||
* run arbitrary Python code that switches.
|
||||
*/
|
||||
OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept
|
||||
{
|
||||
// Because this may invoke arbitrary Python code, which could
|
||||
// result in switching back to us, we need to get the
|
||||
// arguments locally on the stack.
|
||||
assert(rhs);
|
||||
OwnedObject args = rhs.args();
|
||||
OwnedObject kwargs = rhs.kwargs();
|
||||
rhs.CLEAR();
|
||||
// We shouldn't be called twice for the same switch.
|
||||
assert(args || kwargs);
|
||||
assert(!rhs);
|
||||
|
||||
if (!kwargs) {
|
||||
lhs = args;
|
||||
}
|
||||
else if (!PyDict_Size(kwargs.borrow())) {
|
||||
lhs = args;
|
||||
}
|
||||
else if (!PySequence_Length(args.borrow())) {
|
||||
lhs = kwargs;
|
||||
}
|
||||
else {
|
||||
// PyTuple_Pack allocates memory, may GC, may run arbitrary
|
||||
// Python code.
|
||||
lhs = OwnedObject::consuming(PyTuple_Pack(2, args.borrow(), kwargs.borrow()));
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
static OwnedObject
|
||||
g_handle_exit(const OwnedObject& greenlet_result)
|
||||
{
|
||||
if (!greenlet_result && mod_globs->PyExc_GreenletExit.PyExceptionMatches()) {
|
||||
/* catch and ignore GreenletExit */
|
||||
PyErrFetchParam val;
|
||||
PyErr_Fetch(PyErrFetchParam(), val, PyErrFetchParam());
|
||||
if (!val) {
|
||||
return OwnedObject::None();
|
||||
}
|
||||
return OwnedObject(val);
|
||||
}
|
||||
|
||||
if (greenlet_result) {
|
||||
// package the result into a 1-tuple
|
||||
// PyTuple_Pack increments the reference of its arguments,
|
||||
// so we always need to decref the greenlet result;
|
||||
// the owner will do that.
|
||||
return OwnedObject::consuming(PyTuple_Pack(1, greenlet_result.borrow()));
|
||||
}
|
||||
|
||||
return OwnedObject();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* May run arbitrary Python code.
|
||||
*/
|
||||
OwnedObject
|
||||
Greenlet::g_switch_finish(const switchstack_result_t& err)
|
||||
{
|
||||
assert(err.the_new_current_greenlet == this);
|
||||
|
||||
ThreadState& state = *this->thread_state();
|
||||
// Because calling the trace function could do arbitrary things,
|
||||
// including switching away from this greenlet and then maybe
|
||||
// switching back, we need to capture the arguments now so that
|
||||
// they don't change.
|
||||
OwnedObject result;
|
||||
if (this->args()) {
|
||||
result <<= this->args();
|
||||
}
|
||||
else {
|
||||
assert(PyErr_Occurred());
|
||||
}
|
||||
assert(!this->args());
|
||||
try {
|
||||
// Our only caller handles the bad error case
|
||||
assert(err.status >= 0);
|
||||
assert(state.borrow_current() == this->self());
|
||||
if (OwnedObject tracefunc = state.get_tracefunc()) {
|
||||
assert(result || PyErr_Occurred());
|
||||
g_calltrace(tracefunc,
|
||||
result ? mod_globs->event_switch : mod_globs->event_throw,
|
||||
err.origin_greenlet,
|
||||
this->self());
|
||||
}
|
||||
// The above could have invoked arbitrary Python code, but
|
||||
// it couldn't switch back to this object and *also*
|
||||
// throw an exception, so the args won't have changed.
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
// We get here if we fell of the end of the run() function
|
||||
// raising an exception. The switch itself was
|
||||
// successful, but the function raised.
|
||||
// valgrind reports that memory allocated here can still
|
||||
// be reached after a test run.
|
||||
throw PyErrOccurred::from_current();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
/* Turn switch errors into switch throws */
|
||||
/* Turn trace errors into switch throws */
|
||||
this->release_args();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Greenlet::g_calltrace(const OwnedObject& tracefunc,
|
||||
const greenlet::refs::ImmortalEventName& event,
|
||||
const BorrowedGreenlet& origin,
|
||||
const BorrowedGreenlet& target)
|
||||
{
|
||||
PyErrPieces saved_exc;
|
||||
try {
|
||||
TracingGuard tracing_guard;
|
||||
// TODO: We have saved the active exception (if any) that's
|
||||
// about to be raised. In the 'throw' case, we could provide
|
||||
// the exception to the tracefunction, which seems very helpful.
|
||||
tracing_guard.CallTraceFunction(tracefunc, event, origin, target);
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
// In case of exceptions trace function is removed,
|
||||
// and any existing exception is replaced with the tracing
|
||||
// exception.
|
||||
GET_THREAD_STATE().state().set_tracefunc(Py_None);
|
||||
throw;
|
||||
}
|
||||
|
||||
saved_exc.PyErrRestore();
|
||||
assert(
|
||||
(event == mod_globs->event_throw && PyErr_Occurred())
|
||||
|| (event == mod_globs->event_switch && !PyErr_Occurred())
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
Greenlet::murder_in_place()
|
||||
{
|
||||
if (this->active()) {
|
||||
assert(!this->is_currently_running_in_some_thread());
|
||||
this->deactivate_and_free();
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
Greenlet::deactivate_and_free()
|
||||
{
|
||||
if (!this->active()) {
|
||||
return;
|
||||
}
|
||||
// Throw away any saved stack.
|
||||
this->stack_state = StackState();
|
||||
assert(!this->stack_state.active());
|
||||
// Throw away any Python references.
|
||||
// We're holding a borrowed reference to the last
|
||||
// frame we executed. Since we borrowed it, the
|
||||
// normal traversal, clear, and dealloc functions
|
||||
// ignore it, meaning it leaks. (The thread state
|
||||
// object can't find it to clear it when that's
|
||||
// deallocated either, because by definition if we
|
||||
// got an object on this list, it wasn't
|
||||
// running and the thread state doesn't have
|
||||
// this frame.)
|
||||
// So here, we *do* clear it.
|
||||
this->python_state.tp_clear(true);
|
||||
}
|
||||
|
||||
bool
|
||||
Greenlet::belongs_to_thread(const ThreadState* thread_state) const
|
||||
{
|
||||
if (!this->thread_state() // not running anywhere, or thread
|
||||
// exited
|
||||
|| !thread_state) { // same, or there is no thread state.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Greenlet::deallocing_greenlet_in_thread(const ThreadState* current_thread_state)
|
||||
{
|
||||
/* Cannot raise an exception to kill the greenlet if
|
||||
it is not running in the same thread! */
|
||||
if (this->belongs_to_thread(current_thread_state)) {
|
||||
assert(current_thread_state);
|
||||
// To get here it had to have run before
|
||||
/* Send the greenlet a GreenletExit exception. */
|
||||
|
||||
// We don't care about the return value, only whether an
|
||||
// exception happened.
|
||||
this->throw_GreenletExit_during_dealloc(*current_thread_state);
|
||||
return;
|
||||
}
|
||||
|
||||
// Not the same thread! Temporarily save the greenlet
|
||||
// into its thread's deleteme list, *if* it exists.
|
||||
// If that thread has already exited, and processed its pending
|
||||
// cleanup, we'll never be able to clean everything up: we won't
|
||||
// be able to raise an exception.
|
||||
// That's mostly OK! Since we can't add it to a list, our refcount
|
||||
// won't increase, and we'll go ahead with the DECREFs later.
|
||||
ThreadState *const thread_state = this->thread_state();
|
||||
if (thread_state) {
|
||||
thread_state->delete_when_thread_running(this->self());
|
||||
}
|
||||
else {
|
||||
// The thread is dead, we can't raise an exception.
|
||||
// We need to make it look non-active, though, so that dealloc
|
||||
// finishes killing it.
|
||||
this->deactivate_and_free();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Greenlet::tp_traverse(visitproc visit, void* arg)
|
||||
{
|
||||
|
||||
int result;
|
||||
if ((result = this->exception_state.tp_traverse(visit, arg)) != 0) {
|
||||
return result;
|
||||
}
|
||||
//XXX: This is ugly. But so is handling everything having to do
|
||||
//with the top frame.
|
||||
bool visit_top_frame = this->was_running_in_dead_thread();
|
||||
// When true, the thread is dead. Our implicit weak reference to the
|
||||
// frame is now all that's left; we consider ourselves to
|
||||
// strongly own it now.
|
||||
if ((result = this->python_state.tp_traverse(visit, arg, visit_top_frame)) != 0) {
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Greenlet::tp_clear()
|
||||
{
|
||||
bool own_top_frame = this->was_running_in_dead_thread();
|
||||
this->exception_state.tp_clear();
|
||||
this->python_state.tp_clear(own_top_frame);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Greenlet::is_currently_running_in_some_thread() const
|
||||
{
|
||||
return this->stack_state.active() && !this->python_state.top_frame();
|
||||
}
|
||||
|
||||
#if GREENLET_PY312
|
||||
void GREENLET_NOINLINE(Greenlet::expose_frames)()
|
||||
{
|
||||
if (!this->python_state.top_frame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_PyInterpreterFrame* last_complete_iframe = nullptr;
|
||||
_PyInterpreterFrame* iframe = this->python_state.top_frame()->f_frame;
|
||||
while (iframe) {
|
||||
// We must make a copy before looking at the iframe contents,
|
||||
// since iframe might point to a portion of the greenlet's C stack
|
||||
// that was spilled when switching greenlets.
|
||||
_PyInterpreterFrame iframe_copy;
|
||||
this->stack_state.copy_from_stack(&iframe_copy, iframe, sizeof(*iframe));
|
||||
if (!_PyFrame_IsIncomplete(&iframe_copy)) {
|
||||
// If the iframe were OWNED_BY_CSTACK then it would always be
|
||||
// incomplete. Since it's not incomplete, it's not on the C stack
|
||||
// and we can access it through the original `iframe` pointer
|
||||
// directly. This is important since GetFrameObject might
|
||||
// lazily _create_ the frame object and we don't want the
|
||||
// interpreter to lose track of it.
|
||||
assert(iframe_copy.owner != FRAME_OWNED_BY_CSTACK);
|
||||
|
||||
// We really want to just write:
|
||||
// PyFrameObject* frame = _PyFrame_GetFrameObject(iframe);
|
||||
// but _PyFrame_GetFrameObject calls _PyFrame_MakeAndSetFrameObject
|
||||
// which is not a visible symbol in libpython. The easiest
|
||||
// way to get a public function to call it is using
|
||||
// PyFrame_GetBack, which is defined as follows:
|
||||
// assert(frame != NULL);
|
||||
// assert(!_PyFrame_IsIncomplete(frame->f_frame));
|
||||
// PyFrameObject *back = frame->f_back;
|
||||
// if (back == NULL) {
|
||||
// _PyInterpreterFrame *prev = frame->f_frame->previous;
|
||||
// prev = _PyFrame_GetFirstComplete(prev);
|
||||
// if (prev) {
|
||||
// back = _PyFrame_GetFrameObject(prev);
|
||||
// }
|
||||
// }
|
||||
// return (PyFrameObject*)Py_XNewRef(back);
|
||||
if (!iframe->frame_obj) {
|
||||
PyFrameObject dummy_frame;
|
||||
_PyInterpreterFrame dummy_iframe;
|
||||
dummy_frame.f_back = nullptr;
|
||||
dummy_frame.f_frame = &dummy_iframe;
|
||||
// force the iframe to be considered complete without
|
||||
// needing to check its code object:
|
||||
dummy_iframe.owner = FRAME_OWNED_BY_GENERATOR;
|
||||
dummy_iframe.previous = iframe;
|
||||
assert(!_PyFrame_IsIncomplete(&dummy_iframe));
|
||||
// Drop the returned reference immediately; the iframe
|
||||
// continues to hold a strong reference
|
||||
Py_XDECREF(PyFrame_GetBack(&dummy_frame));
|
||||
assert(iframe->frame_obj);
|
||||
}
|
||||
|
||||
// This is a complete frame, so make the last one of those we saw
|
||||
// point at it, bypassing any incomplete frames (which may have
|
||||
// been on the C stack) in between the two. We're overwriting
|
||||
// last_complete_iframe->previous and need that to be reversible,
|
||||
// so we store the original previous ptr in the frame object
|
||||
// (which we must have created on a previous iteration through
|
||||
// this loop). The frame object has a bunch of storage that is
|
||||
// only used when its iframe is OWNED_BY_FRAME_OBJECT, which only
|
||||
// occurs when the frame object outlives the frame's execution,
|
||||
// which can't have happened yet because the frame is currently
|
||||
// executing as far as the interpreter is concerned. So, we can
|
||||
// reuse it for our own purposes.
|
||||
assert(iframe->owner == FRAME_OWNED_BY_THREAD
|
||||
|| iframe->owner == FRAME_OWNED_BY_GENERATOR);
|
||||
if (last_complete_iframe) {
|
||||
assert(last_complete_iframe->frame_obj);
|
||||
memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0],
|
||||
&last_complete_iframe->previous, sizeof(void *));
|
||||
last_complete_iframe->previous = iframe;
|
||||
}
|
||||
last_complete_iframe = iframe;
|
||||
}
|
||||
// Frames that are OWNED_BY_FRAME_OBJECT are linked via the
|
||||
// frame's f_back while all others are linked via the iframe's
|
||||
// previous ptr. Since all the frames we traverse are running
|
||||
// as far as the interpreter is concerned, we don't have to
|
||||
// worry about the OWNED_BY_FRAME_OBJECT case.
|
||||
iframe = iframe_copy.previous;
|
||||
}
|
||||
|
||||
// Give the outermost complete iframe a null previous pointer to
|
||||
// account for any potential incomplete/C-stack iframes between it
|
||||
// and the actual top-of-stack
|
||||
if (last_complete_iframe) {
|
||||
assert(last_complete_iframe->frame_obj);
|
||||
memcpy(&last_complete_iframe->frame_obj->_f_frame_data[0],
|
||||
&last_complete_iframe->previous, sizeof(void *));
|
||||
last_complete_iframe->previous = nullptr;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Greenlet::expose_frames()
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
}; // namespace greenlet
|
|
@ -1,94 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
/**
|
||||
* Implementation of GreenletGlobals.
|
||||
*
|
||||
* Format with:
|
||||
* clang-format -i --style=file src/greenlet/greenlet.c
|
||||
*
|
||||
*
|
||||
* Fix missing braces with:
|
||||
* clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
|
||||
*/
|
||||
#ifndef T_GREENLET_GLOBALS
|
||||
#define T_GREENLET_GLOBALS
|
||||
|
||||
#include "greenlet_refs.hpp"
|
||||
#include "greenlet_exceptions.hpp"
|
||||
#include "greenlet_thread_support.hpp"
|
||||
#include "greenlet_thread_state.hpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
// This encapsulates what were previously module global "constants"
|
||||
// established at init time.
|
||||
// This is a step towards Python3 style module state that allows
|
||||
// reloading.
|
||||
//
|
||||
// In an earlier iteration of this code, we used placement new to be
|
||||
// able to allocate this object statically still, so that references
|
||||
// to its members don't incur an extra pointer indirection.
|
||||
// But under some scenarios, that could result in crashes at
|
||||
// shutdown because apparently the destructor was getting run twice?
|
||||
class GreenletGlobals
|
||||
{
|
||||
|
||||
public:
|
||||
const greenlet::refs::ImmortalEventName event_switch;
|
||||
const greenlet::refs::ImmortalEventName event_throw;
|
||||
const greenlet::refs::ImmortalException PyExc_GreenletError;
|
||||
const greenlet::refs::ImmortalException PyExc_GreenletExit;
|
||||
const greenlet::refs::ImmortalObject empty_tuple;
|
||||
const greenlet::refs::ImmortalObject empty_dict;
|
||||
const greenlet::refs::ImmortalString str_run;
|
||||
Mutex* const thread_states_to_destroy_lock;
|
||||
greenlet::cleanup_queue_t thread_states_to_destroy;
|
||||
|
||||
GreenletGlobals() :
|
||||
event_switch("switch"),
|
||||
event_throw("throw"),
|
||||
PyExc_GreenletError("greenlet.error"),
|
||||
PyExc_GreenletExit("greenlet.GreenletExit", PyExc_BaseException),
|
||||
empty_tuple(Require(PyTuple_New(0))),
|
||||
empty_dict(Require(PyDict_New())),
|
||||
str_run("run"),
|
||||
thread_states_to_destroy_lock(new Mutex())
|
||||
{}
|
||||
|
||||
~GreenletGlobals()
|
||||
{
|
||||
// This object is (currently) effectively immortal, and not
|
||||
// just because of those placement new tricks; if we try to
|
||||
// deallocate the static object we allocated, and overwrote,
|
||||
// we would be doing so at C++ teardown time, which is after
|
||||
// the final Python GIL is released, and we can't use the API
|
||||
// then.
|
||||
// (The members will still be destructed, but they also don't
|
||||
// do any deallocation.)
|
||||
}
|
||||
|
||||
void queue_to_destroy(ThreadState* ts) const
|
||||
{
|
||||
// we're currently accessed through a static const object,
|
||||
// implicitly marking our members as const, so code can't just
|
||||
// call push_back (or pop_back) without casting away the
|
||||
// const.
|
||||
//
|
||||
// Do that for callers.
|
||||
greenlet::cleanup_queue_t& q = const_cast<greenlet::cleanup_queue_t&>(this->thread_states_to_destroy);
|
||||
q.push_back(ts);
|
||||
}
|
||||
|
||||
ThreadState* take_next_to_destroy() const
|
||||
{
|
||||
greenlet::cleanup_queue_t& q = const_cast<greenlet::cleanup_queue_t&>(this->thread_states_to_destroy);
|
||||
ThreadState* result = q.back();
|
||||
q.pop_back();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
static const greenlet::GreenletGlobals* mod_globs;
|
||||
|
||||
#endif // T_GREENLET_GLOBALS
|
|
@ -1,155 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
/**
|
||||
* Implementation of greenlet::MainGreenlet.
|
||||
*
|
||||
* Format with:
|
||||
* clang-format -i --style=file src/greenlet/greenlet.c
|
||||
*
|
||||
*
|
||||
* Fix missing braces with:
|
||||
* clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
|
||||
*/
|
||||
|
||||
#include "greenlet_greenlet.hpp"
|
||||
#include "greenlet_thread_state.hpp"
|
||||
|
||||
|
||||
// Protected by the GIL. Incremented when we create a main greenlet,
|
||||
// in a new thread, decremented when it is destroyed.
|
||||
static Py_ssize_t G_TOTAL_MAIN_GREENLETS;
|
||||
|
||||
namespace greenlet {
|
||||
greenlet::PythonAllocator<MainGreenlet> MainGreenlet::allocator;
|
||||
|
||||
void* MainGreenlet::operator new(size_t UNUSED(count))
|
||||
{
|
||||
return allocator.allocate(1);
|
||||
}
|
||||
|
||||
|
||||
void MainGreenlet::operator delete(void* ptr)
|
||||
{
|
||||
return allocator.deallocate(static_cast<MainGreenlet*>(ptr),
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
MainGreenlet::MainGreenlet(PyGreenlet* p, ThreadState* state)
|
||||
: Greenlet(p, StackState::make_main()),
|
||||
_self(p),
|
||||
_thread_state(state)
|
||||
{
|
||||
G_TOTAL_MAIN_GREENLETS++;
|
||||
}
|
||||
|
||||
MainGreenlet::~MainGreenlet()
|
||||
{
|
||||
G_TOTAL_MAIN_GREENLETS--;
|
||||
this->tp_clear();
|
||||
}
|
||||
|
||||
ThreadState*
|
||||
MainGreenlet::thread_state() const noexcept
|
||||
{
|
||||
return this->_thread_state;
|
||||
}
|
||||
|
||||
void
|
||||
MainGreenlet::thread_state(ThreadState* t) noexcept
|
||||
{
|
||||
assert(!t);
|
||||
this->_thread_state = t;
|
||||
}
|
||||
|
||||
BorrowedGreenlet
|
||||
MainGreenlet::self() const noexcept
|
||||
{
|
||||
return BorrowedGreenlet(this->_self.borrow());
|
||||
}
|
||||
|
||||
|
||||
const BorrowedMainGreenlet
|
||||
MainGreenlet::main_greenlet() const
|
||||
{
|
||||
return this->_self;
|
||||
}
|
||||
|
||||
BorrowedMainGreenlet
|
||||
MainGreenlet::find_main_greenlet_in_lineage() const
|
||||
{
|
||||
return BorrowedMainGreenlet(this->_self);
|
||||
}
|
||||
|
||||
bool
|
||||
MainGreenlet::was_running_in_dead_thread() const noexcept
|
||||
{
|
||||
return !this->_thread_state;
|
||||
}
|
||||
|
||||
OwnedObject
|
||||
MainGreenlet::g_switch()
|
||||
{
|
||||
try {
|
||||
this->check_switch_allowed();
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
this->release_args();
|
||||
throw;
|
||||
}
|
||||
|
||||
switchstack_result_t err = this->g_switchstack();
|
||||
if (err.status < 0) {
|
||||
// XXX: This code path is untested, but it is shared
|
||||
// with the UserGreenlet path that is tested.
|
||||
return this->on_switchstack_or_initialstub_failure(
|
||||
this,
|
||||
err,
|
||||
true, // target was me
|
||||
false // was initial stub
|
||||
);
|
||||
}
|
||||
|
||||
return err.the_new_current_greenlet->g_switch_finish(err);
|
||||
}
|
||||
|
||||
int
|
||||
MainGreenlet::tp_traverse(visitproc visit, void* arg)
|
||||
{
|
||||
if (this->_thread_state) {
|
||||
// we've already traversed main, (self), don't do it again.
|
||||
int result = this->_thread_state->tp_traverse(visit, arg, false);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return Greenlet::tp_traverse(visit, arg);
|
||||
}
|
||||
|
||||
const OwnedObject&
|
||||
MainGreenlet::run() const
|
||||
{
|
||||
throw AttributeError("Main greenlets do not have a run attribute.");
|
||||
}
|
||||
|
||||
void
|
||||
MainGreenlet::run(const BorrowedObject UNUSED(nrun))
|
||||
{
|
||||
throw AttributeError("Main greenlets do not have a run attribute.");
|
||||
}
|
||||
|
||||
void
|
||||
MainGreenlet::parent(const BorrowedObject raw_new_parent)
|
||||
{
|
||||
if (!raw_new_parent) {
|
||||
throw AttributeError("can't delete attribute");
|
||||
}
|
||||
throw AttributeError("cannot set the parent of a main greenlet");
|
||||
}
|
||||
|
||||
const OwnedGreenlet
|
||||
MainGreenlet::parent() const
|
||||
{
|
||||
return OwnedGreenlet(); // null becomes None
|
||||
}
|
||||
|
||||
}; // namespace greenlet
|
|
@ -1,375 +0,0 @@
|
|||
#ifndef GREENLET_PYTHON_STATE_CPP
|
||||
#define GREENLET_PYTHON_STATE_CPP
|
||||
|
||||
#include <Python.h>
|
||||
#include "greenlet_greenlet.hpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
PythonState::PythonState()
|
||||
: _top_frame()
|
||||
#if GREENLET_USE_CFRAME
|
||||
,cframe(nullptr)
|
||||
,use_tracing(0)
|
||||
#endif
|
||||
#if GREENLET_PY312
|
||||
,py_recursion_depth(0)
|
||||
,c_recursion_depth(0)
|
||||
#else
|
||||
,recursion_depth(0)
|
||||
#endif
|
||||
,trash_delete_nesting(0)
|
||||
#if GREENLET_PY311
|
||||
,current_frame(nullptr)
|
||||
,datastack_chunk(nullptr)
|
||||
,datastack_top(nullptr)
|
||||
,datastack_limit(nullptr)
|
||||
#endif
|
||||
{
|
||||
#if GREENLET_USE_CFRAME
|
||||
/*
|
||||
The PyThreadState->cframe pointer usually points to memory on
|
||||
the stack, alloceted in a call into PyEval_EvalFrameDefault.
|
||||
|
||||
Initially, before any evaluation begins, it points to the
|
||||
initial PyThreadState object's ``root_cframe`` object, which is
|
||||
statically allocated for the lifetime of the thread.
|
||||
|
||||
A greenlet can last for longer than a call to
|
||||
PyEval_EvalFrameDefault, so we can't set its ``cframe`` pointer
|
||||
to be the current ``PyThreadState->cframe``; nor could we use
|
||||
one from the greenlet parent for the same reason. Yet a further
|
||||
no: we can't allocate one scoped to the greenlet and then
|
||||
destroy it when the greenlet is deallocated, because inside the
|
||||
interpreter the _PyCFrame objects form a linked list, and that too
|
||||
can result in accessing memory beyond its dynamic lifetime (if
|
||||
the greenlet doesn't actually finish before it dies, its entry
|
||||
could still be in the list).
|
||||
|
||||
Using the ``root_cframe`` is problematic, though, because its
|
||||
members are never modified by the interpreter and are set to 0,
|
||||
meaning that its ``use_tracing`` flag is never updated. We don't
|
||||
want to modify that value in the ``root_cframe`` ourself: it
|
||||
*shouldn't* matter much because we should probably never get
|
||||
back to the point where that's the only cframe on the stack;
|
||||
even if it did matter, the major consequence of an incorrect
|
||||
value for ``use_tracing`` is that if its true the interpreter
|
||||
does some extra work --- however, it's just good code hygiene.
|
||||
|
||||
Our solution: before a greenlet runs, after its initial
|
||||
creation, it uses the ``root_cframe`` just to have something to
|
||||
put there. However, once the greenlet is actually switched to
|
||||
for the first time, ``g_initialstub`` (which doesn't actually
|
||||
"return" while the greenlet is running) stores a new _PyCFrame on
|
||||
its local stack, and copies the appropriate values from the
|
||||
currently running _PyCFrame; this is then made the _PyCFrame for the
|
||||
newly-minted greenlet. ``g_initialstub`` then proceeds to call
|
||||
``glet.run()``, which results in ``PyEval_...`` adding the
|
||||
_PyCFrame to the list. Switches continue as normal. Finally, when
|
||||
the greenlet finishes, the call to ``glet.run()`` returns and
|
||||
the _PyCFrame is taken out of the linked list and the stack value
|
||||
is now unused and free to expire.
|
||||
|
||||
XXX: I think we can do better. If we're deallocing in the same
|
||||
thread, can't we traverse the list and unlink our frame?
|
||||
Can we just keep a reference to the thread state in case we
|
||||
dealloc in another thread? (Is that even possible if we're still
|
||||
running and haven't returned from g_initialstub?)
|
||||
*/
|
||||
this->cframe = &PyThreadState_GET()->root_cframe;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
inline void PythonState::may_switch_away() noexcept
|
||||
{
|
||||
#if GREENLET_PY311
|
||||
// PyThreadState_GetFrame is probably going to have to allocate a
|
||||
// new frame object. That may trigger garbage collection. Because
|
||||
// we call this during the early phases of a switch (it doesn't
|
||||
// matter to which greenlet, as this has a global effect), if a GC
|
||||
// triggers a switch away, two things can happen, both bad:
|
||||
// - We might not get switched back to, halting forward progress.
|
||||
// this is pathological, but possible.
|
||||
// - We might get switched back to with a different set of
|
||||
// arguments or a throw instead of a switch. That would corrupt
|
||||
// our state (specifically, PyErr_Occurred() and this->args()
|
||||
// would no longer agree).
|
||||
//
|
||||
// Thus, when we call this API, we need to have GC disabled.
|
||||
// This method serves as a bottleneck we call when maybe beginning
|
||||
// a switch. In this way, it is always safe -- no risk of GC -- to
|
||||
// use ``_GetFrame()`` whenever we need to, just as it was in
|
||||
// <=3.10 (because subsequent calls will be cached and not
|
||||
// allocate memory).
|
||||
|
||||
GCDisabledGuard no_gc;
|
||||
Py_XDECREF(PyThreadState_GetFrame(PyThreadState_GET()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void PythonState::operator<<(const PyThreadState *const tstate) noexcept
|
||||
{
|
||||
this->_context.steal(tstate->context);
|
||||
#if GREENLET_USE_CFRAME
|
||||
/*
|
||||
IMPORTANT: ``cframe`` is a pointer into the STACK. Thus, because
|
||||
the call to ``slp_switch()`` changes the contents of the stack,
|
||||
you cannot read from ``ts_current->cframe`` after that call and
|
||||
necessarily get the same values you get from reading it here.
|
||||
Anything you need to restore from now to then must be saved in a
|
||||
global/threadlocal variable (because we can't use stack
|
||||
variables here either). For things that need to persist across
|
||||
the switch, use `will_switch_from`.
|
||||
*/
|
||||
this->cframe = tstate->cframe;
|
||||
#if !GREENLET_PY312
|
||||
this->use_tracing = tstate->cframe->use_tracing;
|
||||
#endif
|
||||
#endif // GREENLET_USE_CFRAME
|
||||
#if GREENLET_PY311
|
||||
#if GREENLET_PY312
|
||||
this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
|
||||
this->c_recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining;
|
||||
#else // not 312
|
||||
this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
|
||||
#endif // GREENLET_PY312
|
||||
this->current_frame = tstate->cframe->current_frame;
|
||||
this->datastack_chunk = tstate->datastack_chunk;
|
||||
this->datastack_top = tstate->datastack_top;
|
||||
this->datastack_limit = tstate->datastack_limit;
|
||||
|
||||
PyFrameObject *frame = PyThreadState_GetFrame((PyThreadState *)tstate);
|
||||
Py_XDECREF(frame); // PyThreadState_GetFrame gives us a new
|
||||
// reference.
|
||||
this->_top_frame.steal(frame);
|
||||
#if GREENLET_PY312
|
||||
this->trash_delete_nesting = tstate->trash.delete_nesting;
|
||||
#else // not 312
|
||||
this->trash_delete_nesting = tstate->trash_delete_nesting;
|
||||
#endif // GREENLET_PY312
|
||||
#else // Not 311
|
||||
this->recursion_depth = tstate->recursion_depth;
|
||||
this->_top_frame.steal(tstate->frame);
|
||||
this->trash_delete_nesting = tstate->trash_delete_nesting;
|
||||
#endif // GREENLET_PY311
|
||||
}
|
||||
|
||||
#if GREENLET_PY312
|
||||
void GREENLET_NOINLINE(PythonState::unexpose_frames)()
|
||||
{
|
||||
if (!this->top_frame()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// See GreenletState::expose_frames() and the comment on frames_were_exposed
|
||||
// for more information about this logic.
|
||||
_PyInterpreterFrame *iframe = this->_top_frame->f_frame;
|
||||
while (iframe != nullptr) {
|
||||
_PyInterpreterFrame *prev_exposed = iframe->previous;
|
||||
assert(iframe->frame_obj);
|
||||
memcpy(&iframe->previous, &iframe->frame_obj->_f_frame_data[0],
|
||||
sizeof(void *));
|
||||
iframe = prev_exposed;
|
||||
}
|
||||
}
|
||||
#else
|
||||
void PythonState::unexpose_frames()
|
||||
{}
|
||||
#endif
|
||||
|
||||
void PythonState::operator>>(PyThreadState *const tstate) noexcept
|
||||
{
|
||||
tstate->context = this->_context.relinquish_ownership();
|
||||
/* Incrementing this value invalidates the contextvars cache,
|
||||
which would otherwise remain valid across switches */
|
||||
tstate->context_ver++;
|
||||
#if GREENLET_USE_CFRAME
|
||||
tstate->cframe = this->cframe;
|
||||
/*
|
||||
If we were tracing, we need to keep tracing.
|
||||
There should never be the possibility of hitting the
|
||||
root_cframe here. See note above about why we can't
|
||||
just copy this from ``origin->cframe->use_tracing``.
|
||||
*/
|
||||
#if !GREENLET_PY312
|
||||
tstate->cframe->use_tracing = this->use_tracing;
|
||||
#endif
|
||||
#endif // GREENLET_USE_CFRAME
|
||||
#if GREENLET_PY311
|
||||
#if GREENLET_PY312
|
||||
tstate->py_recursion_remaining = tstate->py_recursion_limit - this->py_recursion_depth;
|
||||
tstate->c_recursion_remaining = C_RECURSION_LIMIT - this->c_recursion_depth;
|
||||
this->unexpose_frames();
|
||||
#else // \/ 3.11
|
||||
tstate->recursion_remaining = tstate->recursion_limit - this->recursion_depth;
|
||||
#endif // GREENLET_PY312
|
||||
tstate->cframe->current_frame = this->current_frame;
|
||||
tstate->datastack_chunk = this->datastack_chunk;
|
||||
tstate->datastack_top = this->datastack_top;
|
||||
tstate->datastack_limit = this->datastack_limit;
|
||||
this->_top_frame.relinquish_ownership();
|
||||
#if GREENLET_PY312
|
||||
tstate->trash.delete_nesting = this->trash_delete_nesting;
|
||||
#else // not 3.12
|
||||
tstate->trash_delete_nesting = this->trash_delete_nesting;
|
||||
#endif // GREENLET_PY312
|
||||
#else // not 3.11
|
||||
tstate->frame = this->_top_frame.relinquish_ownership();
|
||||
tstate->recursion_depth = this->recursion_depth;
|
||||
tstate->trash_delete_nesting = this->trash_delete_nesting;
|
||||
#endif // GREENLET_PY311
|
||||
}
|
||||
|
||||
inline void PythonState::will_switch_from(PyThreadState *const origin_tstate) noexcept
|
||||
{
|
||||
#if GREENLET_USE_CFRAME && !GREENLET_PY312
|
||||
// The weird thing is, we don't actually save this for an
|
||||
// effect on the current greenlet, it's saved for an
|
||||
// effect on the target greenlet. That is, we want
|
||||
// continuity of this setting across the greenlet switch.
|
||||
this->use_tracing = origin_tstate->cframe->use_tracing;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PythonState::set_initial_state(const PyThreadState* const tstate) noexcept
|
||||
{
|
||||
this->_top_frame = nullptr;
|
||||
#if GREENLET_PY312
|
||||
this->py_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
|
||||
// XXX: TODO: Comment from a reviewer:
|
||||
// Should this be ``C_RECURSION_LIMIT - tstate->c_recursion_remaining``?
|
||||
// But to me it looks more like that might not be the right
|
||||
// initialization either?
|
||||
this->c_recursion_depth = tstate->py_recursion_limit - tstate->py_recursion_remaining;
|
||||
#elif GREENLET_PY311
|
||||
this->recursion_depth = tstate->recursion_limit - tstate->recursion_remaining;
|
||||
#else
|
||||
this->recursion_depth = tstate->recursion_depth;
|
||||
#endif
|
||||
}
|
||||
// TODO: Better state management about when we own the top frame.
|
||||
int PythonState::tp_traverse(visitproc visit, void* arg, bool own_top_frame) noexcept
|
||||
{
|
||||
Py_VISIT(this->_context.borrow());
|
||||
if (own_top_frame) {
|
||||
Py_VISIT(this->_top_frame.borrow());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PythonState::tp_clear(bool own_top_frame) noexcept
|
||||
{
|
||||
PythonStateContext::tp_clear();
|
||||
// If we get here owning a frame,
|
||||
// we got dealloc'd without being finished. We may or may not be
|
||||
// in the same thread.
|
||||
if (own_top_frame) {
|
||||
this->_top_frame.CLEAR();
|
||||
}
|
||||
}
|
||||
|
||||
#if GREENLET_USE_CFRAME
|
||||
void PythonState::set_new_cframe(_PyCFrame& frame) noexcept
|
||||
{
|
||||
frame = *PyThreadState_GET()->cframe;
|
||||
/* Make the target greenlet refer to the stack value. */
|
||||
this->cframe = &frame;
|
||||
/*
|
||||
And restore the link to the previous frame so this one gets
|
||||
unliked appropriately.
|
||||
*/
|
||||
this->cframe->previous = &PyThreadState_GET()->root_cframe;
|
||||
}
|
||||
#endif
|
||||
|
||||
const PythonState::OwnedFrame& PythonState::top_frame() const noexcept
|
||||
{
|
||||
return this->_top_frame;
|
||||
}
|
||||
|
||||
void PythonState::did_finish(PyThreadState* tstate) noexcept
|
||||
{
|
||||
#if GREENLET_PY311
|
||||
// See https://github.com/gevent/gevent/issues/1924 and
|
||||
// https://github.com/python-greenlet/greenlet/issues/328. In
|
||||
// short, Python 3.11 allocates memory for frames as a sort of
|
||||
// linked list that's kept as part of PyThreadState in the
|
||||
// ``datastack_chunk`` member and friends. These are saved and
|
||||
// restored as part of switching greenlets.
|
||||
//
|
||||
// When we initially switch to a greenlet, we set those to NULL.
|
||||
// That causes the frame management code to treat this like a
|
||||
// brand new thread and start a fresh list of chunks, beginning
|
||||
// with a new "root" chunk. As we make calls in this greenlet,
|
||||
// those chunks get added, and as calls return, they get popped.
|
||||
// But the frame code (pystate.c) is careful to make sure that the
|
||||
// root chunk never gets popped.
|
||||
//
|
||||
// Thus, when a greenlet exits for the last time, there will be at
|
||||
// least a single root chunk that we must be responsible for
|
||||
// deallocating.
|
||||
//
|
||||
// The complex part is that these chunks are allocated and freed
|
||||
// using ``_PyObject_VirtualAlloc``/``Free``. Those aren't public
|
||||
// functions, and they aren't exported for linking. It so happens
|
||||
// that we know they are just thin wrappers around the Arena
|
||||
// allocator, so we can use that directly to deallocate in a
|
||||
// compatible way.
|
||||
//
|
||||
// CAUTION: Check this implementation detail on every major version.
|
||||
//
|
||||
// It might be nice to be able to do this in our destructor, but
|
||||
// can we be sure that no one else is using that memory? Plus, as
|
||||
// described below, our pointers may not even be valid anymore. As
|
||||
// a special case, there is one time that we know we can do this,
|
||||
// and that's from the destructor of the associated UserGreenlet
|
||||
// (NOT main greenlet)
|
||||
PyObjectArenaAllocator alloc;
|
||||
_PyStackChunk* chunk = nullptr;
|
||||
if (tstate) {
|
||||
// We really did finish, we can never be switched to again.
|
||||
chunk = tstate->datastack_chunk;
|
||||
// Unfortunately, we can't do much sanity checking. Our
|
||||
// this->datastack_chunk pointer is out of date (evaluation may
|
||||
// have popped down through it already) so we can't verify that
|
||||
// we deallocate it. I don't think we can even check datastack_top
|
||||
// for the same reason.
|
||||
|
||||
PyObject_GetArenaAllocator(&alloc);
|
||||
tstate->datastack_chunk = nullptr;
|
||||
tstate->datastack_limit = nullptr;
|
||||
tstate->datastack_top = nullptr;
|
||||
|
||||
}
|
||||
else if (this->datastack_chunk) {
|
||||
// The UserGreenlet (NOT the main greenlet!) is being deallocated. If we're
|
||||
// still holding a stack chunk, it's garbage because we know
|
||||
// we can never switch back to let cPython clean it up.
|
||||
// Because the last time we got switched away from, and we
|
||||
// haven't run since then, we know our chain is valid and can
|
||||
// be dealloced.
|
||||
chunk = this->datastack_chunk;
|
||||
PyObject_GetArenaAllocator(&alloc);
|
||||
}
|
||||
|
||||
if (alloc.free && chunk) {
|
||||
// In case the arena mechanism has been torn down already.
|
||||
while (chunk) {
|
||||
_PyStackChunk *prev = chunk->previous;
|
||||
chunk->previous = nullptr;
|
||||
alloc.free(alloc.ctx, chunk, chunk->size);
|
||||
chunk = prev;
|
||||
}
|
||||
}
|
||||
|
||||
this->datastack_chunk = nullptr;
|
||||
this->datastack_limit = nullptr;
|
||||
this->datastack_top = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
#endif // GREENLET_PYTHON_STATE_CPP
|
|
@ -1,265 +0,0 @@
|
|||
#ifndef GREENLET_STACK_STATE_CPP
|
||||
#define GREENLET_STACK_STATE_CPP
|
||||
|
||||
#include "greenlet_greenlet.hpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
#ifdef GREENLET_USE_STDIO
|
||||
#include <iostream>
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const StackState& s)
|
||||
{
|
||||
os << "StackState(stack_start=" << (void*)s._stack_start
|
||||
<< ", stack_stop=" << (void*)s.stack_stop
|
||||
<< ", stack_copy=" << (void*)s.stack_copy
|
||||
<< ", stack_saved=" << s._stack_saved
|
||||
<< ", stack_prev=" << s.stack_prev
|
||||
<< ", addr=" << &s
|
||||
<< ")";
|
||||
return os;
|
||||
}
|
||||
#endif
|
||||
|
||||
StackState::StackState(void* mark, StackState& current)
|
||||
: _stack_start(nullptr),
|
||||
stack_stop((char*)mark),
|
||||
stack_copy(nullptr),
|
||||
_stack_saved(0),
|
||||
/* Skip a dying greenlet */
|
||||
stack_prev(current._stack_start
|
||||
? ¤t
|
||||
: current.stack_prev)
|
||||
{
|
||||
}
|
||||
|
||||
StackState::StackState()
|
||||
: _stack_start(nullptr),
|
||||
stack_stop(nullptr),
|
||||
stack_copy(nullptr),
|
||||
_stack_saved(0),
|
||||
stack_prev(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
StackState::StackState(const StackState& other)
|
||||
// can't use a delegating constructor because of
|
||||
// MSVC for Python 2.7
|
||||
: _stack_start(nullptr),
|
||||
stack_stop(nullptr),
|
||||
stack_copy(nullptr),
|
||||
_stack_saved(0),
|
||||
stack_prev(nullptr)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
StackState& StackState::operator=(const StackState& other)
|
||||
{
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
if (other._stack_saved) {
|
||||
throw std::runtime_error("Refusing to steal memory.");
|
||||
}
|
||||
|
||||
//If we have memory allocated, dispose of it
|
||||
this->free_stack_copy();
|
||||
|
||||
this->_stack_start = other._stack_start;
|
||||
this->stack_stop = other.stack_stop;
|
||||
this->stack_copy = other.stack_copy;
|
||||
this->_stack_saved = other._stack_saved;
|
||||
this->stack_prev = other.stack_prev;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void StackState::free_stack_copy() noexcept
|
||||
{
|
||||
PyMem_Free(this->stack_copy);
|
||||
this->stack_copy = nullptr;
|
||||
this->_stack_saved = 0;
|
||||
}
|
||||
|
||||
inline void StackState::copy_heap_to_stack(const StackState& current) noexcept
|
||||
{
|
||||
|
||||
/* Restore the heap copy back into the C stack */
|
||||
if (this->_stack_saved != 0) {
|
||||
memcpy(this->_stack_start, this->stack_copy, this->_stack_saved);
|
||||
this->free_stack_copy();
|
||||
}
|
||||
StackState* owner = const_cast<StackState*>(¤t);
|
||||
if (!owner->_stack_start) {
|
||||
owner = owner->stack_prev; /* greenlet is dying, skip it */
|
||||
}
|
||||
while (owner && owner->stack_stop <= this->stack_stop) {
|
||||
// cerr << "\tOwner: " << owner << endl;
|
||||
owner = owner->stack_prev; /* find greenlet with more stack */
|
||||
}
|
||||
this->stack_prev = owner;
|
||||
// cerr << "\tFinished with: " << *this << endl;
|
||||
}
|
||||
|
||||
inline int StackState::copy_stack_to_heap_up_to(const char* const stop) noexcept
|
||||
{
|
||||
/* Save more of g's stack into the heap -- at least up to 'stop'
|
||||
g->stack_stop |________|
|
||||
| |
|
||||
| __ stop . . . . .
|
||||
| | ==> . .
|
||||
|________| _______
|
||||
| | | |
|
||||
| | | |
|
||||
g->stack_start | | |_______| g->stack_copy
|
||||
*/
|
||||
intptr_t sz1 = this->_stack_saved;
|
||||
intptr_t sz2 = stop - this->_stack_start;
|
||||
assert(this->_stack_start);
|
||||
if (sz2 > sz1) {
|
||||
char* c = (char*)PyMem_Realloc(this->stack_copy, sz2);
|
||||
if (!c) {
|
||||
PyErr_NoMemory();
|
||||
return -1;
|
||||
}
|
||||
memcpy(c + sz1, this->_stack_start + sz1, sz2 - sz1);
|
||||
this->stack_copy = c;
|
||||
this->_stack_saved = sz2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline int StackState::copy_stack_to_heap(char* const stackref,
|
||||
const StackState& current) noexcept
|
||||
{
|
||||
/* must free all the C stack up to target_stop */
|
||||
const char* const target_stop = this->stack_stop;
|
||||
|
||||
StackState* owner = const_cast<StackState*>(¤t);
|
||||
assert(owner->_stack_saved == 0); // everything is present on the stack
|
||||
if (!owner->_stack_start) {
|
||||
owner = owner->stack_prev; /* not saved if dying */
|
||||
}
|
||||
else {
|
||||
owner->_stack_start = stackref;
|
||||
}
|
||||
|
||||
while (owner->stack_stop < target_stop) {
|
||||
/* ts_current is entierely within the area to free */
|
||||
if (owner->copy_stack_to_heap_up_to(owner->stack_stop)) {
|
||||
return -1; /* XXX */
|
||||
}
|
||||
owner = owner->stack_prev;
|
||||
}
|
||||
if (owner != this) {
|
||||
if (owner->copy_stack_to_heap_up_to(target_stop)) {
|
||||
return -1; /* XXX */
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline bool StackState::started() const noexcept
|
||||
{
|
||||
return this->stack_stop != nullptr;
|
||||
}
|
||||
|
||||
inline bool StackState::main() const noexcept
|
||||
{
|
||||
return this->stack_stop == (char*)-1;
|
||||
}
|
||||
|
||||
inline bool StackState::active() const noexcept
|
||||
{
|
||||
return this->_stack_start != nullptr;
|
||||
}
|
||||
|
||||
inline void StackState::set_active() noexcept
|
||||
{
|
||||
assert(this->_stack_start == nullptr);
|
||||
this->_stack_start = (char*)1;
|
||||
}
|
||||
|
||||
inline void StackState::set_inactive() noexcept
|
||||
{
|
||||
this->_stack_start = nullptr;
|
||||
// XXX: What if we still have memory out there?
|
||||
// That case is actually triggered by
|
||||
// test_issue251_issue252_explicit_reference_not_collectable (greenlet.tests.test_leaks.TestLeaks)
|
||||
// and
|
||||
// test_issue251_issue252_need_to_collect_in_background
|
||||
// (greenlet.tests.test_leaks.TestLeaks)
|
||||
//
|
||||
// Those objects never get deallocated, so the destructor never
|
||||
// runs.
|
||||
// It *seems* safe to clean up the memory here?
|
||||
if (this->_stack_saved) {
|
||||
this->free_stack_copy();
|
||||
}
|
||||
}
|
||||
|
||||
inline intptr_t StackState::stack_saved() const noexcept
|
||||
{
|
||||
return this->_stack_saved;
|
||||
}
|
||||
|
||||
inline char* StackState::stack_start() const noexcept
|
||||
{
|
||||
return this->_stack_start;
|
||||
}
|
||||
|
||||
|
||||
inline StackState StackState::make_main() noexcept
|
||||
{
|
||||
StackState s;
|
||||
s._stack_start = (char*)1;
|
||||
s.stack_stop = (char*)-1;
|
||||
return s;
|
||||
}
|
||||
|
||||
StackState::~StackState()
|
||||
{
|
||||
if (this->_stack_saved != 0) {
|
||||
this->free_stack_copy();
|
||||
}
|
||||
}
|
||||
|
||||
void StackState::copy_from_stack(void* vdest, const void* vsrc, size_t n) const
|
||||
{
|
||||
char* dest = static_cast<char*>(vdest);
|
||||
const char* src = static_cast<const char*>(vsrc);
|
||||
if (src + n <= this->_stack_start
|
||||
|| src >= this->_stack_start + this->_stack_saved
|
||||
|| this->_stack_saved == 0) {
|
||||
// Nothing we're copying was spilled from the stack
|
||||
memcpy(dest, src, n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src < this->_stack_start) {
|
||||
// Copy the part before the saved stack.
|
||||
// We know src + n > _stack_start due to the test above.
|
||||
const size_t nbefore = this->_stack_start - src;
|
||||
memcpy(dest, src, nbefore);
|
||||
dest += nbefore;
|
||||
src += nbefore;
|
||||
n -= nbefore;
|
||||
}
|
||||
// We know src >= _stack_start after the before-copy, and
|
||||
// src < _stack_start + _stack_saved due to the first if condition
|
||||
size_t nspilled = std::min<size_t>(n, this->_stack_start + this->_stack_saved - src);
|
||||
memcpy(dest, this->stack_copy + (src - this->_stack_start), nspilled);
|
||||
dest += nspilled;
|
||||
src += nspilled;
|
||||
n -= nspilled;
|
||||
if (n > 0) {
|
||||
// Copy the part after the saved stack
|
||||
memcpy(dest, src, n);
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
#endif // GREENLET_STACK_STATE_CPP
|
|
@ -1,195 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
/**
|
||||
* Implementation of the ThreadState destructors.
|
||||
*
|
||||
* Format with:
|
||||
* clang-format -i --style=file src/greenlet/greenlet.c
|
||||
*
|
||||
*
|
||||
* Fix missing braces with:
|
||||
* clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
|
||||
*/
|
||||
#ifndef T_THREADSTATE_DESTROY
|
||||
#define T_THREADSTATE_DESTROY
|
||||
|
||||
#include "greenlet_greenlet.hpp"
|
||||
#include "greenlet_thread_state.hpp"
|
||||
#include "greenlet_thread_support.hpp"
|
||||
#include "greenlet_cpython_add_pending.hpp"
|
||||
#include "TGreenletGlobals.cpp"
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
struct ThreadState_DestroyWithGIL
|
||||
{
|
||||
ThreadState_DestroyWithGIL(ThreadState* state)
|
||||
{
|
||||
if (state && state->has_main_greenlet()) {
|
||||
DestroyWithGIL(state);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
DestroyWithGIL(ThreadState* state)
|
||||
{
|
||||
// Holding the GIL.
|
||||
// Passed a non-shared pointer to the actual thread state.
|
||||
// state -> main greenlet
|
||||
assert(state->has_main_greenlet());
|
||||
PyGreenlet* main(state->borrow_main_greenlet());
|
||||
// When we need to do cross-thread operations, we check this.
|
||||
// A NULL value means the thread died some time ago.
|
||||
// We do this here, rather than in a Python dealloc function
|
||||
// for the greenlet, in case there's still a reference out
|
||||
// there.
|
||||
static_cast<MainGreenlet*>(main->pimpl)->thread_state(nullptr);
|
||||
|
||||
delete state; // Deleting this runs the destructor, DECREFs the main greenlet.
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct ThreadState_DestroyNoGIL
|
||||
{
|
||||
// ensure this is actually defined.
|
||||
static_assert(GREENLET_BROKEN_PY_ADD_PENDING == 1 || GREENLET_BROKEN_PY_ADD_PENDING == 0,
|
||||
"GREENLET_BROKEN_PY_ADD_PENDING not defined correctly.");
|
||||
|
||||
#if GREENLET_BROKEN_PY_ADD_PENDING
|
||||
static int _push_pending_call(struct _pending_calls *pending,
|
||||
int (*func)(void *), void *arg)
|
||||
{
|
||||
int i = pending->last;
|
||||
int j = (i + 1) % NPENDINGCALLS;
|
||||
if (j == pending->first) {
|
||||
return -1; /* Queue full */
|
||||
}
|
||||
pending->calls[i].func = func;
|
||||
pending->calls[i].arg = arg;
|
||||
pending->last = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int AddPendingCall(int (*func)(void *), void *arg)
|
||||
{
|
||||
_PyRuntimeState *runtime = &_PyRuntime;
|
||||
if (!runtime) {
|
||||
// obviously impossible
|
||||
return 0;
|
||||
}
|
||||
struct _pending_calls *pending = &runtime->ceval.pending;
|
||||
if (!pending->lock) {
|
||||
return 0;
|
||||
}
|
||||
int result = 0;
|
||||
PyThread_acquire_lock(pending->lock, WAIT_LOCK);
|
||||
if (!pending->finishing) {
|
||||
result = _push_pending_call(pending, func, arg);
|
||||
}
|
||||
PyThread_release_lock(pending->lock);
|
||||
SIGNAL_PENDING_CALLS(&runtime->ceval);
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
// Python < 3.8 or >= 3.9
|
||||
static int AddPendingCall(int (*func)(void*), void* arg)
|
||||
{
|
||||
return Py_AddPendingCall(func, arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
ThreadState_DestroyNoGIL(ThreadState* state)
|
||||
{
|
||||
// We are *NOT* holding the GIL. Our thread is in the middle
|
||||
// of its death throes and the Python thread state is already
|
||||
// gone so we can't use most Python APIs. One that is safe is
|
||||
// ``Py_AddPendingCall``, unless the interpreter itself has
|
||||
// been torn down. There is a limited number of calls that can
|
||||
// be queued: 32 (NPENDINGCALLS) in CPython 3.10, so we
|
||||
// coalesce these calls using our own queue.
|
||||
if (state && state->has_main_greenlet()) {
|
||||
// mark the thread as dead ASAP.
|
||||
// this is racy! If we try to throw or switch to a
|
||||
// greenlet from this thread from some other thread before
|
||||
// we clear the state pointer, it won't realize the state
|
||||
// is dead which can crash the process.
|
||||
PyGreenlet* p = state->borrow_main_greenlet();
|
||||
assert(p->pimpl->thread_state() == state || p->pimpl->thread_state() == nullptr);
|
||||
static_cast<MainGreenlet*>(p->pimpl)->thread_state(nullptr);
|
||||
}
|
||||
|
||||
// NOTE: Because we're not holding the GIL here, some other
|
||||
// Python thread could run and call ``os.fork()``, which would
|
||||
// be bad if that happenend while we are holding the cleanup
|
||||
// lock (it wouldn't function in the child process).
|
||||
// Make a best effort to try to keep the duration we hold the
|
||||
// lock short.
|
||||
// TODO: On platforms that support it, use ``pthread_atfork`` to
|
||||
// drop this lock.
|
||||
LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
|
||||
|
||||
if (state && state->has_main_greenlet()) {
|
||||
// Because we don't have the GIL, this is a race condition.
|
||||
if (!PyInterpreterState_Head()) {
|
||||
// We have to leak the thread state, if the
|
||||
// interpreter has shut down when we're getting
|
||||
// deallocated, we can't run the cleanup code that
|
||||
// deleting it would imply.
|
||||
return;
|
||||
}
|
||||
|
||||
mod_globs->queue_to_destroy(state);
|
||||
if (mod_globs->thread_states_to_destroy.size() == 1) {
|
||||
// We added the first item to the queue. We need to schedule
|
||||
// the cleanup.
|
||||
int result = ThreadState_DestroyNoGIL::AddPendingCall(
|
||||
ThreadState_DestroyNoGIL::DestroyQueueWithGIL,
|
||||
NULL);
|
||||
if (result < 0) {
|
||||
// Hmm, what can we do here?
|
||||
fprintf(stderr,
|
||||
"greenlet: WARNING: failed in call to Py_AddPendingCall; "
|
||||
"expect a memory leak.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
DestroyQueueWithGIL(void* UNUSED(arg))
|
||||
{
|
||||
// We're holding the GIL here, so no Python code should be able to
|
||||
// run to call ``os.fork()``.
|
||||
while (1) {
|
||||
ThreadState* to_destroy;
|
||||
{
|
||||
LockGuard cleanup_lock(*mod_globs->thread_states_to_destroy_lock);
|
||||
if (mod_globs->thread_states_to_destroy.empty()) {
|
||||
break;
|
||||
}
|
||||
to_destroy = mod_globs->take_next_to_destroy();
|
||||
}
|
||||
// Drop the lock while we do the actual deletion.
|
||||
ThreadState_DestroyWithGIL::DestroyWithGIL(to_destroy);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
// The intent when GET_THREAD_STATE() is needed multiple times in a
|
||||
// function is to take a reference to its return value in a local
|
||||
// variable, to avoid the thread-local indirection. On some platforms
|
||||
// (macOS), accessing a thread-local involves a function call (plus an
|
||||
// initial function call in each function that uses a thread local);
|
||||
// in contrast, static volatile variables are at some pre-computed
|
||||
// offset.
|
||||
typedef greenlet::ThreadStateCreator<greenlet::ThreadState_DestroyNoGIL> ThreadStateCreator;
|
||||
static thread_local ThreadStateCreator g_thread_state_global;
|
||||
#define GET_THREAD_STATE() g_thread_state_global
|
||||
|
||||
#endif //T_THREADSTATE_DESTROY
|
|
@ -1,667 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
/**
|
||||
* Implementation of greenlet::UserGreenlet.
|
||||
*
|
||||
* Format with:
|
||||
* clang-format -i --style=file src/greenlet/greenlet.c
|
||||
*
|
||||
*
|
||||
* Fix missing braces with:
|
||||
* clang-tidy src/greenlet/greenlet.c -fix -checks="readability-braces-around-statements"
|
||||
*/
|
||||
|
||||
#include "greenlet_internal.hpp"
|
||||
#include "greenlet_greenlet.hpp"
|
||||
#include "greenlet_thread_state.hpp"
|
||||
#include "TThreadStateDestroy.cpp"
|
||||
|
||||
|
||||
namespace greenlet {
|
||||
using greenlet::refs::BorrowedMainGreenlet;
|
||||
greenlet::PythonAllocator<UserGreenlet> UserGreenlet::allocator;
|
||||
|
||||
void* UserGreenlet::operator new(size_t UNUSED(count))
|
||||
{
|
||||
return allocator.allocate(1);
|
||||
}
|
||||
|
||||
|
||||
void UserGreenlet::operator delete(void* ptr)
|
||||
{
|
||||
return allocator.deallocate(static_cast<UserGreenlet*>(ptr),
|
||||
1);
|
||||
}
|
||||
|
||||
|
||||
UserGreenlet::UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent)
|
||||
: Greenlet(p), _parent(the_parent)
|
||||
{
|
||||
this->_self = p;
|
||||
}
|
||||
|
||||
UserGreenlet::~UserGreenlet()
|
||||
{
|
||||
// Python 3.11: If we don't clear out the raw frame datastack
|
||||
// when deleting an unfinished greenlet,
|
||||
// TestLeaks.test_untracked_memory_doesnt_increase_unfinished_thread_dealloc_in_main fails.
|
||||
this->python_state.did_finish(nullptr);
|
||||
this->tp_clear();
|
||||
}
|
||||
|
||||
BorrowedGreenlet
|
||||
UserGreenlet::self() const noexcept
|
||||
{
|
||||
return this->_self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const BorrowedMainGreenlet
|
||||
UserGreenlet::main_greenlet() const
|
||||
{
|
||||
return this->_main_greenlet;
|
||||
}
|
||||
|
||||
|
||||
BorrowedMainGreenlet
|
||||
UserGreenlet::find_main_greenlet_in_lineage() const
|
||||
{
|
||||
if (this->started()) {
|
||||
assert(this->_main_greenlet);
|
||||
return BorrowedMainGreenlet(this->_main_greenlet);
|
||||
}
|
||||
|
||||
if (!this->_parent) {
|
||||
/* garbage collected greenlet in chain */
|
||||
// XXX: WHAT?
|
||||
return BorrowedMainGreenlet(nullptr);
|
||||
}
|
||||
|
||||
return this->_parent->find_main_greenlet_in_lineage();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CAUTION: This will allocate memory and may trigger garbage
|
||||
* collection and arbitrary Python code.
|
||||
*/
|
||||
OwnedObject
|
||||
UserGreenlet::throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state)
|
||||
{
|
||||
/* The dying greenlet cannot be a parent of ts_current
|
||||
because the 'parent' field chain would hold a
|
||||
reference */
|
||||
UserGreenlet::ParentIsCurrentGuard with_current_parent(this, current_thread_state);
|
||||
|
||||
// We don't care about the return value, only whether an
|
||||
// exception happened. Whether or not an exception happens,
|
||||
// we need to restore the parent in case the greenlet gets
|
||||
// resurrected.
|
||||
return Greenlet::throw_GreenletExit_during_dealloc(current_thread_state);
|
||||
}
|
||||
|
||||
ThreadState*
|
||||
UserGreenlet::thread_state() const noexcept
|
||||
{
|
||||
// TODO: maybe make this throw, if the thread state isn't there?
|
||||
// if (!this->main_greenlet) {
|
||||
// throw std::runtime_error("No thread state"); // TODO: Better exception
|
||||
// }
|
||||
if (!this->_main_greenlet) {
|
||||
return nullptr;
|
||||
}
|
||||
return this->_main_greenlet->thread_state();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
UserGreenlet::was_running_in_dead_thread() const noexcept
|
||||
{
|
||||
return this->_main_greenlet && !this->thread_state();
|
||||
}
|
||||
|
||||
OwnedObject
|
||||
UserGreenlet::g_switch()
|
||||
{
|
||||
assert(this->args() || PyErr_Occurred());
|
||||
|
||||
try {
|
||||
this->check_switch_allowed();
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
this->release_args();
|
||||
throw;
|
||||
}
|
||||
|
||||
// Switching greenlets used to attempt to clean out ones that need
|
||||
// deleted *if* we detected a thread switch. Should it still do
|
||||
// that?
|
||||
// An issue is that if we delete a greenlet from another thread,
|
||||
// it gets queued to this thread, and ``kill_greenlet()`` switches
|
||||
// back into the greenlet
|
||||
|
||||
/* find the real target by ignoring dead greenlets,
|
||||
and if necessary starting a greenlet. */
|
||||
switchstack_result_t err;
|
||||
Greenlet* target = this;
|
||||
// TODO: probably cleaner to handle the case where we do
|
||||
// switch to ourself separately from the other cases.
|
||||
// This can probably even further be simplified if we keep
|
||||
// track of the switching_state we're going for and just call
|
||||
// into g_switch() if it's not ourself. The main problem with that
|
||||
// is that we would be using more stack space.
|
||||
bool target_was_me = true;
|
||||
bool was_initial_stub = false;
|
||||
while (target) {
|
||||
if (target->active()) {
|
||||
if (!target_was_me) {
|
||||
target->args() <<= this->args();
|
||||
assert(!this->args());
|
||||
}
|
||||
err = target->g_switchstack();
|
||||
break;
|
||||
}
|
||||
if (!target->started()) {
|
||||
// We never encounter a main greenlet that's not started.
|
||||
assert(!target->main());
|
||||
UserGreenlet* real_target = static_cast<UserGreenlet*>(target);
|
||||
assert(real_target);
|
||||
void* dummymarker;
|
||||
was_initial_stub = true;
|
||||
if (!target_was_me) {
|
||||
target->args() <<= this->args();
|
||||
assert(!this->args());
|
||||
}
|
||||
try {
|
||||
// This can only throw back to us while we're
|
||||
// still in this greenlet. Once the new greenlet
|
||||
// is bootstrapped, it has its own exception state.
|
||||
err = real_target->g_initialstub(&dummymarker);
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
this->release_args();
|
||||
throw;
|
||||
}
|
||||
catch (const GreenletStartedWhileInPython&) {
|
||||
// The greenlet was started sometime before this
|
||||
// greenlet actually switched to it, i.e.,
|
||||
// "concurrent" calls to switch() or throw().
|
||||
// We need to retry the switch.
|
||||
// Note that the current greenlet has been reset
|
||||
// to this one (or we wouldn't be running!)
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
target = target->parent();
|
||||
target_was_me = false;
|
||||
}
|
||||
// The ``this`` pointer and all other stack or register based
|
||||
// variables are invalid now, at least where things succeed
|
||||
// above.
|
||||
// But this one, probably not so much? It's not clear if it's
|
||||
// safe to throw an exception at this point.
|
||||
|
||||
if (err.status < 0) {
|
||||
// If we get here, either g_initialstub()
|
||||
// failed, or g_switchstack() failed. Either one of those
|
||||
// cases SHOULD leave us in the original greenlet with a valid
|
||||
// stack.
|
||||
return this->on_switchstack_or_initialstub_failure(target, err, target_was_me, was_initial_stub);
|
||||
}
|
||||
|
||||
// err.the_new_current_greenlet would be the same as ``target``,
|
||||
// if target wasn't probably corrupt.
|
||||
return err.the_new_current_greenlet->g_switch_finish(err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Greenlet::switchstack_result_t
|
||||
UserGreenlet::g_initialstub(void* mark)
|
||||
{
|
||||
OwnedObject run;
|
||||
|
||||
// We need to grab a reference to the current switch arguments
|
||||
// in case we're entered concurrently during the call to
|
||||
// GetAttr() and have to try again.
|
||||
// We'll restore them when we return in that case.
|
||||
// Scope them tightly to avoid ref leaks.
|
||||
{
|
||||
SwitchingArgs args(this->args());
|
||||
|
||||
/* save exception in case getattr clears it */
|
||||
PyErrPieces saved;
|
||||
|
||||
/*
|
||||
self.run is the object to call in the new greenlet.
|
||||
This could run arbitrary python code and switch greenlets!
|
||||
*/
|
||||
run = this->_self.PyRequireAttr(mod_globs->str_run);
|
||||
/* restore saved exception */
|
||||
saved.PyErrRestore();
|
||||
|
||||
|
||||
/* recheck that it's safe to switch in case greenlet reparented anywhere above */
|
||||
this->check_switch_allowed();
|
||||
|
||||
/* by the time we got here another start could happen elsewhere,
|
||||
* that means it should now be a regular switch.
|
||||
* This can happen if the Python code is a subclass that implements
|
||||
* __getattribute__ or __getattr__, or makes ``run`` a descriptor;
|
||||
* all of those can run arbitrary code that switches back into
|
||||
* this greenlet.
|
||||
*/
|
||||
if (this->stack_state.started()) {
|
||||
// the successful switch cleared these out, we need to
|
||||
// restore our version. They will be copied on up to the
|
||||
// next target.
|
||||
assert(!this->args());
|
||||
this->args() <<= args;
|
||||
throw GreenletStartedWhileInPython();
|
||||
}
|
||||
}
|
||||
|
||||
// Sweet, if we got here, we have the go-ahead and will switch
|
||||
// greenlets.
|
||||
// Nothing we do from here on out should allow for a thread or
|
||||
// greenlet switch: No arbitrary calls to Python, including
|
||||
// decref'ing
|
||||
|
||||
#if GREENLET_USE_CFRAME
|
||||
/* OK, we need it, we're about to switch greenlets, save the state. */
|
||||
/*
|
||||
See green_new(). This is a stack-allocated variable used
|
||||
while *self* is in PyObject_Call().
|
||||
We want to defer copying the state info until we're sure
|
||||
we need it and are in a stable place to do so.
|
||||
*/
|
||||
_PyCFrame trace_info;
|
||||
|
||||
this->python_state.set_new_cframe(trace_info);
|
||||
#endif
|
||||
/* start the greenlet */
|
||||
ThreadState& thread_state = GET_THREAD_STATE().state();
|
||||
this->stack_state = StackState(mark,
|
||||
thread_state.borrow_current()->stack_state);
|
||||
this->python_state.set_initial_state(PyThreadState_GET());
|
||||
this->exception_state.clear();
|
||||
this->_main_greenlet = thread_state.get_main_greenlet();
|
||||
|
||||
/* perform the initial switch */
|
||||
switchstack_result_t err = this->g_switchstack();
|
||||
/* returns twice!
|
||||
The 1st time with ``err == 1``: we are in the new greenlet.
|
||||
This one owns a greenlet that used to be current.
|
||||
The 2nd time with ``err <= 0``: back in the caller's
|
||||
greenlet; this happens if the child finishes or switches
|
||||
explicitly to us. Either way, the ``err`` variable is
|
||||
created twice at the same memory location, but possibly
|
||||
having different ``origin`` values. Note that it's not
|
||||
constructed for the second time until the switch actually happens.
|
||||
*/
|
||||
if (err.status == 1) {
|
||||
// In the new greenlet.
|
||||
|
||||
// This never returns! Calling inner_bootstrap steals
|
||||
// the contents of our run object within this stack frame, so
|
||||
// it is not valid to do anything with it.
|
||||
try {
|
||||
this->inner_bootstrap(err.origin_greenlet.relinquish_ownership(),
|
||||
run.relinquish_ownership());
|
||||
}
|
||||
// Getting a C++ exception here isn't good. It's probably a
|
||||
// bug in the underlying greenlet, meaning it's probably a
|
||||
// C++ extension. We're going to abort anyway, but try to
|
||||
// display some nice information *if* possible. Some obscure
|
||||
// platforms don't properly support this (old 32-bit Arm, see see
|
||||
// https://github.com/python-greenlet/greenlet/issues/385); that's not
|
||||
// great, but should usually be OK because, as mentioned above, we're
|
||||
// terminating anyway.
|
||||
//
|
||||
// The catching is tested by
|
||||
// ``test_cpp.CPPTests.test_unhandled_exception_in_greenlet_aborts``.
|
||||
//
|
||||
// PyErrOccurred can theoretically be thrown by
|
||||
// inner_bootstrap() -> g_switch_finish(), but that should
|
||||
// never make it back to here. It is a std::exception and
|
||||
// would be caught if it is.
|
||||
catch (const std::exception& e) {
|
||||
std::string base = "greenlet: Unhandled C++ exception: ";
|
||||
base += e.what();
|
||||
Py_FatalError(base.c_str());
|
||||
}
|
||||
catch (...) {
|
||||
// Some compilers/runtimes use exceptions internally.
|
||||
// It appears that GCC on Linux with libstdc++ throws an
|
||||
// exception internally at process shutdown time to unwind
|
||||
// stacks and clean up resources. Depending on exactly
|
||||
// where we are when the process exits, that could result
|
||||
// in an unknown exception getting here. If we
|
||||
// Py_FatalError() or abort() here, we interfere with
|
||||
// orderly process shutdown. Throwing the exception on up
|
||||
// is the right thing to do.
|
||||
//
|
||||
// gevent's ``examples/dns_mass_resolve.py`` demonstrates this.
|
||||
#ifndef NDEBUG
|
||||
fprintf(stderr,
|
||||
"greenlet: inner_bootstrap threw unknown exception; "
|
||||
"is the process terminating?\n");
|
||||
#endif
|
||||
throw;
|
||||
}
|
||||
Py_FatalError("greenlet: inner_bootstrap returned with no exception.\n");
|
||||
}
|
||||
|
||||
|
||||
// In contrast, notice that we're keeping the origin greenlet
|
||||
// around as an owned reference; we need it to call the trace
|
||||
// function for the switch back into the parent. It was only
|
||||
// captured at the time the switch actually happened, though,
|
||||
// so we haven't been keeping an extra reference around this
|
||||
// whole time.
|
||||
|
||||
/* back in the parent */
|
||||
if (err.status < 0) {
|
||||
/* start failed badly, restore greenlet state */
|
||||
this->stack_state = StackState();
|
||||
this->_main_greenlet.CLEAR();
|
||||
// CAUTION: This may run arbitrary Python code.
|
||||
run.CLEAR(); // inner_bootstrap didn't run, we own the reference.
|
||||
}
|
||||
|
||||
// In the success case, the spawned code (inner_bootstrap) will
|
||||
// take care of decrefing this, so we relinquish ownership so as
|
||||
// to not double-decref.
|
||||
|
||||
run.relinquish_ownership();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UserGreenlet::inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run)
|
||||
{
|
||||
// The arguments here would be another great place for move.
|
||||
// As it is, we take them as a reference so that when we clear
|
||||
// them we clear what's on the stack above us. Do that NOW, and
|
||||
// without using a C++ RAII object,
|
||||
// so there's no way that exiting the parent frame can clear it,
|
||||
// or we clear it unexpectedly. This arises in the context of the
|
||||
// interpreter shutting down. See https://github.com/python-greenlet/greenlet/issues/325
|
||||
//PyObject* run = _run.relinquish_ownership();
|
||||
|
||||
/* in the new greenlet */
|
||||
assert(this->thread_state()->borrow_current() == this->_self);
|
||||
// C++ exceptions cannot propagate to the parent greenlet from
|
||||
// here. (TODO: Do we need a catch(...) clause, perhaps on the
|
||||
// function itself? ALl we could do is terminate the program.)
|
||||
// NOTE: On 32-bit Windows, the call chain is extremely
|
||||
// important here in ways that are subtle, having to do with
|
||||
// the depth of the SEH list. The call to restore it MUST NOT
|
||||
// add a new SEH handler to the list, or we'll restore it to
|
||||
// the wrong thing.
|
||||
this->thread_state()->restore_exception_state();
|
||||
/* stack variables from above are no good and also will not unwind! */
|
||||
// EXCEPT: That can't be true, we access run, among others, here.
|
||||
|
||||
this->stack_state.set_active(); /* running */
|
||||
|
||||
// We're about to possibly run Python code again, which
|
||||
// could switch back/away to/from us, so we need to grab the
|
||||
// arguments locally.
|
||||
SwitchingArgs args;
|
||||
args <<= this->args();
|
||||
assert(!this->args());
|
||||
|
||||
// XXX: We could clear this much earlier, right?
|
||||
// Or would that introduce the possibility of running Python
|
||||
// code when we don't want to?
|
||||
// CAUTION: This may run arbitrary Python code.
|
||||
this->_run_callable.CLEAR();
|
||||
|
||||
|
||||
// The first switch we need to manually call the trace
|
||||
// function here instead of in g_switch_finish, because we
|
||||
// never return there.
|
||||
if (OwnedObject tracefunc = this->thread_state()->get_tracefunc()) {
|
||||
OwnedGreenlet trace_origin;
|
||||
trace_origin = origin_greenlet;
|
||||
try {
|
||||
g_calltrace(tracefunc,
|
||||
args ? mod_globs->event_switch : mod_globs->event_throw,
|
||||
trace_origin,
|
||||
this->_self);
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
/* Turn trace errors into switch throws */
|
||||
args.CLEAR();
|
||||
}
|
||||
}
|
||||
|
||||
// We no longer need the origin, it was only here for
|
||||
// tracing.
|
||||
// We may never actually exit this stack frame so we need
|
||||
// to explicitly clear it.
|
||||
// This could run Python code and switch.
|
||||
Py_CLEAR(origin_greenlet);
|
||||
|
||||
OwnedObject result;
|
||||
if (!args) {
|
||||
/* pending exception */
|
||||
result = NULL;
|
||||
}
|
||||
else {
|
||||
/* call g.run(*args, **kwargs) */
|
||||
// This could result in further switches
|
||||
try {
|
||||
//result = run.PyCall(args.args(), args.kwargs());
|
||||
// CAUTION: Just invoking this, before the function even
|
||||
// runs, may cause memory allocations, which may trigger
|
||||
// GC, which may run arbitrary Python code.
|
||||
result = OwnedObject::consuming(PyObject_Call(run, args.args().borrow(), args.kwargs().borrow()));
|
||||
}
|
||||
catch (...) {
|
||||
// Unhandled C++ exception!
|
||||
|
||||
// If we declare ourselves as noexcept, if we don't catch
|
||||
// this here, most platforms will just abort() the
|
||||
// process. But on 64-bit Windows with older versions of
|
||||
// the C runtime, this can actually corrupt memory and
|
||||
// just return. We see this when compiling with the
|
||||
// Windows 7.0 SDK targeting Windows Server 2008, but not
|
||||
// when using the Appveyor Visual Studio 2019 image. So
|
||||
// this currently only affects Python 2.7 on Windows 64.
|
||||
// That is, the tests pass and the runtime aborts
|
||||
// everywhere else.
|
||||
//
|
||||
// However, if we catch it and try to continue with a
|
||||
// Python error, then all Windows 64 bit platforms corrupt
|
||||
// memory. So all we can do is manually abort, hopefully
|
||||
// with a good error message. (Note that the above was
|
||||
// tested WITHOUT the `/EHr` switch being used at compile
|
||||
// time, so MSVC may have "optimized" out important
|
||||
// checking. Using that switch, we may be in a better
|
||||
// place in terms of memory corruption.) But sometimes it
|
||||
// can't be caught here at all, which is confusing but not
|
||||
// terribly surprising; so again, the G_NOEXCEPT_WIN32
|
||||
// plus "/EHr".
|
||||
//
|
||||
// Hopefully the basic C stdlib is still functional enough
|
||||
// for us to at least print an error.
|
||||
//
|
||||
// It gets more complicated than that, though, on some
|
||||
// platforms, specifically at least Linux/gcc/libstdc++. They use
|
||||
// an exception to unwind the stack when a background
|
||||
// thread exits. (See comments about noexcept.) So this
|
||||
// may not actually represent anything untoward. On those
|
||||
// platforms we allow throws of this to propagate, or
|
||||
// attempt to anyway.
|
||||
# if defined(WIN32) || defined(_WIN32)
|
||||
Py_FatalError(
|
||||
"greenlet: Unhandled C++ exception from a greenlet run function. "
|
||||
"Because memory is likely corrupted, terminating process.");
|
||||
std::abort();
|
||||
#else
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// These lines may run arbitrary code
|
||||
args.CLEAR();
|
||||
Py_CLEAR(run);
|
||||
|
||||
if (!result
|
||||
&& mod_globs->PyExc_GreenletExit.PyExceptionMatches()
|
||||
&& (this->args())) {
|
||||
// This can happen, for example, if our only reference
|
||||
// goes away after we switch back to the parent.
|
||||
// See test_dealloc_switch_args_not_lost
|
||||
PyErrPieces clear_error;
|
||||
result <<= this->args();
|
||||
result = single_result(result);
|
||||
}
|
||||
this->release_args();
|
||||
this->python_state.did_finish(PyThreadState_GET());
|
||||
|
||||
result = g_handle_exit(result);
|
||||
assert(this->thread_state()->borrow_current() == this->_self);
|
||||
|
||||
/* jump back to parent */
|
||||
this->stack_state.set_inactive(); /* dead */
|
||||
|
||||
|
||||
// TODO: Can we decref some things here? Release our main greenlet
|
||||
// and maybe parent?
|
||||
for (Greenlet* parent = this->_parent;
|
||||
parent;
|
||||
parent = parent->parent()) {
|
||||
// We need to somewhere consume a reference to
|
||||
// the result; in most cases we'll never have control
|
||||
// back in this stack frame again. Calling
|
||||
// green_switch actually adds another reference!
|
||||
// This would probably be clearer with a specific API
|
||||
// to hand results to the parent.
|
||||
parent->args() <<= result;
|
||||
assert(!result);
|
||||
// The parent greenlet now owns the result; in the
|
||||
// typical case we'll never get back here to assign to
|
||||
// result and thus release the reference.
|
||||
try {
|
||||
result = parent->g_switch();
|
||||
}
|
||||
catch (const PyErrOccurred&) {
|
||||
// Ignore, keep passing the error on up.
|
||||
}
|
||||
|
||||
/* Return here means switch to parent failed,
|
||||
* in which case we throw *current* exception
|
||||
* to the next parent in chain.
|
||||
*/
|
||||
assert(!result);
|
||||
}
|
||||
/* We ran out of parents, cannot continue */
|
||||
PyErr_WriteUnraisable(this->self().borrow_o());
|
||||
Py_FatalError("greenlet: ran out of parent greenlets while propagating exception; "
|
||||
"cannot continue");
|
||||
std::abort();
|
||||
}
|
||||
|
||||
void
|
||||
UserGreenlet::run(const BorrowedObject nrun)
|
||||
{
|
||||
if (this->started()) {
|
||||
throw AttributeError(
|
||||
"run cannot be set "
|
||||
"after the start of the greenlet");
|
||||
}
|
||||
this->_run_callable = nrun;
|
||||
}
|
||||
|
||||
const OwnedGreenlet
|
||||
UserGreenlet::parent() const
|
||||
{
|
||||
return this->_parent;
|
||||
}
|
||||
|
||||
void
|
||||
UserGreenlet::parent(const BorrowedObject raw_new_parent)
|
||||
{
|
||||
if (!raw_new_parent) {
|
||||
throw AttributeError("can't delete attribute");
|
||||
}
|
||||
|
||||
BorrowedMainGreenlet main_greenlet_of_new_parent;
|
||||
BorrowedGreenlet new_parent(raw_new_parent.borrow()); // could
|
||||
// throw
|
||||
// TypeError!
|
||||
for (BorrowedGreenlet p = new_parent; p; p = p->parent()) {
|
||||
if (p == this->_self) {
|
||||
throw ValueError("cyclic parent chain");
|
||||
}
|
||||
main_greenlet_of_new_parent = p->main_greenlet();
|
||||
}
|
||||
|
||||
if (!main_greenlet_of_new_parent) {
|
||||
throw ValueError("parent must not be garbage collected");
|
||||
}
|
||||
|
||||
if (this->started()
|
||||
&& this->_main_greenlet != main_greenlet_of_new_parent) {
|
||||
throw ValueError("parent cannot be on a different thread");
|
||||
}
|
||||
|
||||
this->_parent = new_parent;
|
||||
}
|
||||
|
||||
void
|
||||
UserGreenlet::murder_in_place()
|
||||
{
|
||||
this->_main_greenlet.CLEAR();
|
||||
Greenlet::murder_in_place();
|
||||
}
|
||||
|
||||
bool
|
||||
UserGreenlet::belongs_to_thread(const ThreadState* thread_state) const
|
||||
{
|
||||
return Greenlet::belongs_to_thread(thread_state) && this->_main_greenlet == thread_state->borrow_main_greenlet();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
UserGreenlet::tp_traverse(visitproc visit, void* arg)
|
||||
{
|
||||
Py_VISIT(this->_parent.borrow_o());
|
||||
Py_VISIT(this->_main_greenlet.borrow_o());
|
||||
Py_VISIT(this->_run_callable.borrow_o());
|
||||
|
||||
return Greenlet::tp_traverse(visit, arg);
|
||||
}
|
||||
|
||||
int
|
||||
UserGreenlet::tp_clear()
|
||||
{
|
||||
Greenlet::tp_clear();
|
||||
this->_parent.CLEAR();
|
||||
this->_main_greenlet.CLEAR();
|
||||
this->_run_callable.CLEAR();
|
||||
return 0;
|
||||
}
|
||||
|
||||
UserGreenlet::ParentIsCurrentGuard::ParentIsCurrentGuard(UserGreenlet* p,
|
||||
const ThreadState& thread_state)
|
||||
: oldparent(p->_parent),
|
||||
greenlet(p)
|
||||
{
|
||||
p->_parent = thread_state.get_current();
|
||||
}
|
||||
|
||||
UserGreenlet::ParentIsCurrentGuard::~ParentIsCurrentGuard()
|
||||
{
|
||||
this->greenlet->_parent = oldparent;
|
||||
oldparent.CLEAR();
|
||||
}
|
||||
|
||||
}; //namespace greenlet
|
|
@ -1,71 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
The root of the greenlet package.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
__all__ = [
|
||||
'__version__',
|
||||
'_C_API',
|
||||
|
||||
'GreenletExit',
|
||||
'error',
|
||||
|
||||
'getcurrent',
|
||||
'greenlet',
|
||||
|
||||
'gettrace',
|
||||
'settrace',
|
||||
]
|
||||
|
||||
# pylint:disable=no-name-in-module
|
||||
|
||||
###
|
||||
# Metadata
|
||||
###
|
||||
__version__ = '3.0.3'
|
||||
from ._greenlet import _C_API # pylint:disable=no-name-in-module
|
||||
|
||||
###
|
||||
# Exceptions
|
||||
###
|
||||
from ._greenlet import GreenletExit
|
||||
from ._greenlet import error
|
||||
|
||||
###
|
||||
# greenlets
|
||||
###
|
||||
from ._greenlet import getcurrent
|
||||
from ._greenlet import greenlet
|
||||
|
||||
###
|
||||
# tracing
|
||||
###
|
||||
try:
|
||||
from ._greenlet import gettrace
|
||||
from ._greenlet import settrace
|
||||
except ImportError:
|
||||
# Tracing wasn't supported.
|
||||
# XXX: The option to disable it was removed in 1.0,
|
||||
# so this branch should be dead code.
|
||||
pass
|
||||
|
||||
###
|
||||
# Constants
|
||||
# These constants aren't documented and aren't recommended.
|
||||
# In 1.0, USE_GC and USE_TRACING are always true, and USE_CONTEXT_VARS
|
||||
# is the same as ``sys.version_info[:2] >= 3.7``
|
||||
###
|
||||
from ._greenlet import GREENLET_USE_CONTEXT_VARS # pylint:disable=unused-import
|
||||
from ._greenlet import GREENLET_USE_GC # pylint:disable=unused-import
|
||||
from ._greenlet import GREENLET_USE_TRACING # pylint:disable=unused-import
|
||||
|
||||
# Controlling the use of the gc module. Provisional API for this greenlet
|
||||
# implementation in 2.0.
|
||||
from ._greenlet import CLOCKS_PER_SEC # pylint:disable=unused-import
|
||||
from ._greenlet import enable_optional_cleanup # pylint:disable=unused-import
|
||||
from ._greenlet import get_clocks_used_doing_optional_cleanup # pylint:disable=unused-import
|
||||
|
||||
# Other APIS in the _greenlet module are for test support.
|
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
@ -1,164 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
|
||||
/* Greenlet object interface */
|
||||
|
||||
#ifndef Py_GREENLETOBJECT_H
|
||||
#define Py_GREENLETOBJECT_H
|
||||
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* This is deprecated and undocumented. It does not change. */
|
||||
#define GREENLET_VERSION "1.0.0"
|
||||
|
||||
#ifndef GREENLET_MODULE
|
||||
#define implementation_ptr_t void*
|
||||
#endif
|
||||
|
||||
typedef struct _greenlet {
|
||||
PyObject_HEAD
|
||||
PyObject* weakreflist;
|
||||
PyObject* dict;
|
||||
implementation_ptr_t pimpl;
|
||||
} PyGreenlet;
|
||||
|
||||
#define PyGreenlet_Check(op) (op && PyObject_TypeCheck(op, &PyGreenlet_Type))
|
||||
|
||||
|
||||
/* C API functions */
|
||||
|
||||
/* Total number of symbols that are exported */
|
||||
#define PyGreenlet_API_pointers 12
|
||||
|
||||
#define PyGreenlet_Type_NUM 0
|
||||
#define PyExc_GreenletError_NUM 1
|
||||
#define PyExc_GreenletExit_NUM 2
|
||||
|
||||
#define PyGreenlet_New_NUM 3
|
||||
#define PyGreenlet_GetCurrent_NUM 4
|
||||
#define PyGreenlet_Throw_NUM 5
|
||||
#define PyGreenlet_Switch_NUM 6
|
||||
#define PyGreenlet_SetParent_NUM 7
|
||||
|
||||
#define PyGreenlet_MAIN_NUM 8
|
||||
#define PyGreenlet_STARTED_NUM 9
|
||||
#define PyGreenlet_ACTIVE_NUM 10
|
||||
#define PyGreenlet_GET_PARENT_NUM 11
|
||||
|
||||
#ifndef GREENLET_MODULE
|
||||
/* This section is used by modules that uses the greenlet C API */
|
||||
static void** _PyGreenlet_API = NULL;
|
||||
|
||||
# define PyGreenlet_Type \
|
||||
(*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM])
|
||||
|
||||
# define PyExc_GreenletError \
|
||||
((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM])
|
||||
|
||||
# define PyExc_GreenletExit \
|
||||
((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_New(PyObject *args)
|
||||
*
|
||||
* greenlet.greenlet(run, parent=None)
|
||||
*/
|
||||
# define PyGreenlet_New \
|
||||
(*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \
|
||||
_PyGreenlet_API[PyGreenlet_New_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_GetCurrent(void)
|
||||
*
|
||||
* greenlet.getcurrent()
|
||||
*/
|
||||
# define PyGreenlet_GetCurrent \
|
||||
(*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_Throw(
|
||||
* PyGreenlet *greenlet,
|
||||
* PyObject *typ,
|
||||
* PyObject *val,
|
||||
* PyObject *tb)
|
||||
*
|
||||
* g.throw(...)
|
||||
*/
|
||||
# define PyGreenlet_Throw \
|
||||
(*(PyObject * (*)(PyGreenlet * self, \
|
||||
PyObject * typ, \
|
||||
PyObject * val, \
|
||||
PyObject * tb)) \
|
||||
_PyGreenlet_API[PyGreenlet_Throw_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args)
|
||||
*
|
||||
* g.switch(*args, **kwargs)
|
||||
*/
|
||||
# define PyGreenlet_Switch \
|
||||
(*(PyObject * \
|
||||
(*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \
|
||||
_PyGreenlet_API[PyGreenlet_Switch_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent)
|
||||
*
|
||||
* g.parent = new_parent
|
||||
*/
|
||||
# define PyGreenlet_SetParent \
|
||||
(*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \
|
||||
_PyGreenlet_API[PyGreenlet_SetParent_NUM])
|
||||
|
||||
/*
|
||||
* PyGreenlet_GetParent(PyObject* greenlet)
|
||||
*
|
||||
* return greenlet.parent;
|
||||
*
|
||||
* This could return NULL even if there is no exception active.
|
||||
* If it does not return NULL, you are responsible for decrementing the
|
||||
* reference count.
|
||||
*/
|
||||
# define PyGreenlet_GetParent \
|
||||
(*(PyGreenlet* (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_GET_PARENT_NUM])
|
||||
|
||||
/*
|
||||
* deprecated, undocumented alias.
|
||||
*/
|
||||
# define PyGreenlet_GET_PARENT PyGreenlet_GetParent
|
||||
|
||||
# define PyGreenlet_MAIN \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_MAIN_NUM])
|
||||
|
||||
# define PyGreenlet_STARTED \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_STARTED_NUM])
|
||||
|
||||
# define PyGreenlet_ACTIVE \
|
||||
(*(int (*)(PyGreenlet*)) \
|
||||
_PyGreenlet_API[PyGreenlet_ACTIVE_NUM])
|
||||
|
||||
|
||||
|
||||
|
||||
/* Macro that imports greenlet and initializes C API */
|
||||
/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we
|
||||
keep the older definition to be sure older code that might have a copy of
|
||||
the header still works. */
|
||||
# define PyGreenlet_Import() \
|
||||
{ \
|
||||
_PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \
|
||||
}
|
||||
|
||||
#endif /* GREENLET_MODULE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !Py_GREENLETOBJECT_H */
|
|
@ -1,63 +0,0 @@
|
|||
#ifndef GREENLET_ALLOCATOR_HPP
|
||||
#define GREENLET_ALLOCATOR_HPP
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include <memory>
|
||||
#include "greenlet_compiler_compat.hpp"
|
||||
|
||||
|
||||
namespace greenlet
|
||||
{
|
||||
// This allocator is stateless; all instances are identical.
|
||||
// It can *ONLY* be used when we're sure we're holding the GIL
|
||||
// (Python's allocators require the GIL).
|
||||
template <class T>
|
||||
struct PythonAllocator : public std::allocator<T> {
|
||||
|
||||
PythonAllocator(const PythonAllocator& UNUSED(other))
|
||||
: std::allocator<T>()
|
||||
{
|
||||
}
|
||||
|
||||
PythonAllocator(const std::allocator<T> other)
|
||||
: std::allocator<T>(other)
|
||||
{}
|
||||
|
||||
template <class U>
|
||||
PythonAllocator(const std::allocator<U>& other)
|
||||
: std::allocator<T>(other)
|
||||
{
|
||||
}
|
||||
|
||||
PythonAllocator() : std::allocator<T>() {}
|
||||
|
||||
T* allocate(size_t number_objects, const void* UNUSED(hint)=0)
|
||||
{
|
||||
void* p;
|
||||
if (number_objects == 1)
|
||||
p = PyObject_Malloc(sizeof(T));
|
||||
else
|
||||
p = PyMem_Malloc(sizeof(T) * number_objects);
|
||||
return static_cast<T*>(p);
|
||||
}
|
||||
|
||||
void deallocate(T* t, size_t n)
|
||||
{
|
||||
void* p = t;
|
||||
if (n == 1) {
|
||||
PyObject_Free(p);
|
||||
}
|
||||
else
|
||||
PyMem_Free(p);
|
||||
}
|
||||
// This member is deprecated in C++17 and removed in C++20
|
||||
template< class U >
|
||||
struct rebind {
|
||||
typedef PythonAllocator<U> other;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,95 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
#ifndef GREENLET_COMPILER_COMPAT_HPP
|
||||
#define GREENLET_COMPILER_COMPAT_HPP
|
||||
|
||||
/**
|
||||
* Definitions to aid with compatibility with different compilers.
|
||||
*
|
||||
* .. caution:: Use extreme care with noexcept.
|
||||
* Some compilers and runtimes, specifically gcc/libgcc/libstdc++ on
|
||||
* Linux, implement stack unwinding by throwing an uncatchable
|
||||
* exception, one that specifically does not appear to be an active
|
||||
* exception to the rest of the runtime. If this happens while we're in a noexcept function,
|
||||
* we have violated our dynamic exception contract, and so the runtime
|
||||
* will call std::terminate(), which kills the process with the
|
||||
* unhelpful message "terminate called without an active exception".
|
||||
*
|
||||
* This has happened in this scenario: A background thread is running
|
||||
* a greenlet that has made a native call and released the GIL.
|
||||
* Meanwhile, the main thread finishes and starts shutting down the
|
||||
* interpreter. When the background thread is scheduled again and
|
||||
* attempts to obtain the GIL, it notices that the interpreter is
|
||||
* exiting and calls ``pthread_exit()``. This in turn starts to unwind
|
||||
* the stack by throwing that exception. But we had the ``PyCall``
|
||||
* functions annotated as noexcept, so the runtime terminated us.
|
||||
*
|
||||
* #2 0x00007fab26fec2b7 in std::terminate() () from /lib/x86_64-linux-gnu/libstdc++.so.6
|
||||
* #3 0x00007fab26febb3c in __gxx_personality_v0 () from /lib/x86_64-linux-gnu/libstdc++.so.6
|
||||
* #4 0x00007fab26f34de6 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1
|
||||
* #6 0x00007fab276a34c6 in __GI___pthread_unwind at ./nptl/unwind.c:130
|
||||
* #7 0x00007fab2769bd3a in __do_cancel () at ../sysdeps/nptl/pthreadP.h:280
|
||||
* #8 __GI___pthread_exit (value=value@entry=0x0) at ./nptl/pthread_exit.c:36
|
||||
* #9 0x000000000052e567 in PyThread_exit_thread () at ../Python/thread_pthread.h:370
|
||||
* #10 0x00000000004d60b5 in take_gil at ../Python/ceval_gil.h:224
|
||||
* #11 0x00000000004d65f9 in PyEval_RestoreThread at ../Python/ceval.c:467
|
||||
* #12 0x000000000060cce3 in setipaddr at ../Modules/socketmodule.c:1203
|
||||
* #13 0x00000000006101cd in socket_gethostbyname
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
# if defined(__clang__)
|
||||
# define G_FP_TMPL_STATIC static
|
||||
# else
|
||||
// GCC has no problem allowing static function pointers, but emits
|
||||
// tons of warnings about "whose type uses the anonymous namespace [-Wsubobject-linkage]"
|
||||
# define G_FP_TMPL_STATIC
|
||||
# endif
|
||||
|
||||
# define G_NO_COPIES_OF_CLS(Cls) private: \
|
||||
Cls(const Cls& other) = delete; \
|
||||
Cls& operator=(const Cls& other) = delete
|
||||
|
||||
# define G_NO_ASSIGNMENT_OF_CLS(Cls) private: \
|
||||
Cls& operator=(const Cls& other) = delete
|
||||
|
||||
# define G_NO_COPY_CONSTRUCTOR_OF_CLS(Cls) private: \
|
||||
Cls(const Cls& other) = delete;
|
||||
|
||||
|
||||
// CAUTION: MSVC is stupidly picky:
|
||||
//
|
||||
// "The compiler ignores, without warning, any __declspec keywords
|
||||
// placed after * or & and in front of the variable identifier in a
|
||||
// declaration."
|
||||
// (https://docs.microsoft.com/en-us/cpp/cpp/declspec?view=msvc-160)
|
||||
//
|
||||
// So pointer return types must be handled differently (because of the
|
||||
// trailing *), or you get inscrutable compiler warnings like "error
|
||||
// C2059: syntax error: ''"
|
||||
//
|
||||
// In C++ 11, there is a standard syntax for attributes, and
|
||||
// GCC defines an attribute to use with this: [[gnu:noinline]].
|
||||
// In the future, this is expected to become standard.
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
/* We used to check for GCC 4+ or 3.4+, but those compilers are
|
||||
laughably out of date. Just assume they support it. */
|
||||
# define GREENLET_NOINLINE(name) __attribute__((noinline)) name
|
||||
# define GREENLET_NOINLINE_P(rtype, name) rtype __attribute__((noinline)) name
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
|
||||
#elif defined(_MSC_VER)
|
||||
/* We used to check for && (_MSC_VER >= 1300) but that's also out of date. */
|
||||
# define GREENLET_NOINLINE(name) __declspec(noinline) name
|
||||
# define GREENLET_NOINLINE_P(rtype, name) __declspec(noinline) rtype name
|
||||
# define UNUSED(x) UNUSED_ ## x
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define G_NOEXCEPT_WIN32 noexcept
|
||||
#else
|
||||
# define G_NOEXCEPT_WIN32
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -1,172 +0,0 @@
|
|||
#ifndef GREENLET_CPYTHON_ADD_PENDING_HPP
|
||||
#define GREENLET_CPYTHON_ADD_PENDING_HPP
|
||||
|
||||
#if (PY_VERSION_HEX >= 0x30800A0 && PY_VERSION_HEX < 0x3090000) && !(defined(_WIN32) || defined(WIN32))
|
||||
// XXX: From Python 3.8a3 [1] up until Python 3.9a6 [2][3],
|
||||
// ``Py_AddPendingCall`` would try to produce a Python exception if
|
||||
// the interpreter was in the beginning of shutting down when this
|
||||
// function is called. However, ``Py_AddPendingCall`` doesn't require
|
||||
// the GIL, and we are absolutely not holding it when we make that
|
||||
// call. That means that trying to create the Python exception is
|
||||
// using the C API in an undefined state; here the C API detects this
|
||||
// and aborts the process with an error ("Fatal Python error: Python
|
||||
// memory allocator called without holding the GIL": Add ->
|
||||
// PyErr_SetString -> PyUnicode_New -> PyObject_Malloc). This arises
|
||||
// (obviously) in multi-threaded programs and happens if one thread is
|
||||
// exiting and cleaning up its thread-local data while the other
|
||||
// thread is trying to shut down the interpreter. A crash on shutdown
|
||||
// is still a crash and could result in data loss (e.g., daemon
|
||||
// threads are still running, pending signal handlers may be present,
|
||||
// buffers may not be flushed, there may be __del__ that need run,
|
||||
// etc), so we have to work around it.
|
||||
//
|
||||
// Of course, we can (and do) check for whether the interpreter is
|
||||
// shutting down before calling ``Py_AddPendingCall``, but that's a
|
||||
// race condition since we don't hold the GIL, and so we may not
|
||||
// actually get the right answer. Plus, ``Py_FinalizeEx`` actually
|
||||
// calls ``_Py_FinishPendingCalls`` (which sets the pending->finishing
|
||||
// flag, which is used to gate creating the exceptioen) *before*
|
||||
// publishing any other data that would let us detect the shutdown
|
||||
// (such as runtime->finalizing). So that point is moot.
|
||||
//
|
||||
// Our solution for those versions is to inline the same code, without
|
||||
// the problematic bit that sets the exception. Unfortunately, all of
|
||||
// the structure definitions are private/opaque, *and* we can't
|
||||
// actually count on being able to include their definitions from
|
||||
// ``internal/pycore_*``, because on some platforms those header files
|
||||
// are incomplete (i.e., on macOS with macports 3.8, the includes are
|
||||
// fine, but on Ubuntu jammy with 3.8 from ppa:deadsnakes or GitHub
|
||||
// Actions 3.8 (I think it's Ubuntu 18.04), they con't be used; at
|
||||
// least, I couldn't get them to work). So we need to define the
|
||||
// structures and _PyRuntime data member ourself. Yet more
|
||||
// unfortunately, _PyRuntime won't link on Windows, so we can only do
|
||||
// this on other platforms.
|
||||
//
|
||||
// [1] https://github.com/python/cpython/commit/842a2f07f2f08a935ef470bfdaeef40f87490cfc
|
||||
// [2] https://github.com/python/cpython/commit/cfc3c2f8b34d3864717ab584c5b6c260014ba55a
|
||||
// [3] https://github.com/python/cpython/issues/81308
|
||||
# define GREENLET_BROKEN_PY_ADD_PENDING 1
|
||||
|
||||
// When defining these structures, the important thing is to get
|
||||
// binary compatibility, i.e., structure layout. For that, we only
|
||||
// need to define fields up to the ones we use; after that they're
|
||||
// irrelevant UNLESS the structure is included in another structure
|
||||
// *before* the structure we're interested in --- in that case, it
|
||||
// must be complete. Ellipsis indicate elided trailing members.
|
||||
// Pointer types are changed to void* to keep from having to define
|
||||
// more structures.
|
||||
|
||||
// From "internal/pycore_atomic.h"
|
||||
|
||||
// There are several different definitions of this, including the
|
||||
// plain ``int`` version, a ``volatile int`` and an ``_Atomic int``
|
||||
// I don't think any of those change the size/layout.
|
||||
typedef struct _Py_atomic_int {
|
||||
volatile int _value;
|
||||
} _Py_atomic_int;
|
||||
|
||||
// This needs too much infrastructure, so we just do a regular store.
|
||||
#define _Py_atomic_store_relaxed(ATOMIC_VAL, NEW_VAL) \
|
||||
(ATOMIC_VAL)->_value = NEW_VAL
|
||||
|
||||
|
||||
|
||||
// From "internal/pycore_pymem.h"
|
||||
#define NUM_GENERATIONS 3
|
||||
|
||||
|
||||
struct gc_generation {
|
||||
PyGC_Head head; // We already have this defined.
|
||||
int threshold;
|
||||
int count;
|
||||
};
|
||||
struct gc_generation_stats {
|
||||
Py_ssize_t collections;
|
||||
Py_ssize_t collected;
|
||||
Py_ssize_t uncollectable;
|
||||
};
|
||||
|
||||
struct _gc_runtime_state {
|
||||
void *trash_delete_later;
|
||||
int trash_delete_nesting;
|
||||
int enabled;
|
||||
int debug;
|
||||
struct gc_generation generations[NUM_GENERATIONS];
|
||||
void *generation0;
|
||||
struct gc_generation permanent_generation;
|
||||
struct gc_generation_stats generation_stats[NUM_GENERATIONS];
|
||||
int collecting;
|
||||
void *garbage;
|
||||
void *callbacks;
|
||||
Py_ssize_t long_lived_total;
|
||||
Py_ssize_t long_lived_pending;
|
||||
};
|
||||
|
||||
// From "internal/pycore_pystate.h"
|
||||
struct _pending_calls {
|
||||
int finishing;
|
||||
PyThread_type_lock lock;
|
||||
_Py_atomic_int calls_to_do;
|
||||
int async_exc;
|
||||
#define NPENDINGCALLS 32
|
||||
struct {
|
||||
int (*func)(void *);
|
||||
void *arg;
|
||||
} calls[NPENDINGCALLS];
|
||||
int first;
|
||||
int last;
|
||||
};
|
||||
|
||||
struct _ceval_runtime_state {
|
||||
int recursion_limit;
|
||||
int tracing_possible;
|
||||
_Py_atomic_int eval_breaker;
|
||||
_Py_atomic_int gil_drop_request;
|
||||
struct _pending_calls pending;
|
||||
// ...
|
||||
};
|
||||
|
||||
typedef struct pyruntimestate {
|
||||
int preinitializing;
|
||||
int preinitialized;
|
||||
int core_initialized;
|
||||
int initialized;
|
||||
void *finalizing;
|
||||
|
||||
struct pyinterpreters {
|
||||
PyThread_type_lock mutex;
|
||||
void *head;
|
||||
void *main;
|
||||
int64_t next_id;
|
||||
} interpreters;
|
||||
// XXX Remove this field once we have a tp_* slot.
|
||||
struct _xidregistry {
|
||||
PyThread_type_lock mutex;
|
||||
void *head;
|
||||
} xidregistry;
|
||||
|
||||
unsigned long main_thread;
|
||||
|
||||
#define NEXITFUNCS 32
|
||||
void (*exitfuncs[NEXITFUNCS])(void);
|
||||
int nexitfuncs;
|
||||
|
||||
struct _gc_runtime_state gc;
|
||||
struct _ceval_runtime_state ceval;
|
||||
// ...
|
||||
} _PyRuntimeState;
|
||||
|
||||
#define SIGNAL_PENDING_CALLS(ceval) \
|
||||
do { \
|
||||
_Py_atomic_store_relaxed(&(ceval)->pending.calls_to_do, 1); \
|
||||
_Py_atomic_store_relaxed(&(ceval)->eval_breaker, 1); \
|
||||
} while (0)
|
||||
|
||||
extern _PyRuntimeState _PyRuntime;
|
||||
|
||||
#else
|
||||
# define GREENLET_BROKEN_PY_ADD_PENDING 0
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -1,127 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
#ifndef GREENLET_CPYTHON_COMPAT_H
|
||||
#define GREENLET_CPYTHON_COMPAT_H
|
||||
|
||||
/**
|
||||
* Helpers for compatibility with multiple versions of CPython.
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
|
||||
|
||||
#if PY_VERSION_HEX >= 0x30A00B1
|
||||
# define GREENLET_PY310 1
|
||||
/*
|
||||
Python 3.10 beta 1 changed tstate->use_tracing to a nested cframe member.
|
||||
See https://github.com/python/cpython/pull/25276
|
||||
We have to save and restore this as well.
|
||||
*/
|
||||
# define GREENLET_USE_CFRAME 1
|
||||
#else
|
||||
# define GREENLET_USE_CFRAME 0
|
||||
# define GREENLET_PY310 0
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if PY_VERSION_HEX >= 0x30B00A4
|
||||
/*
|
||||
Greenlet won't compile on anything older than Python 3.11 alpha 4 (see
|
||||
https://bugs.python.org/issue46090). Summary of breaking internal changes:
|
||||
- Python 3.11 alpha 1 changed how frame objects are represented internally.
|
||||
- https://github.com/python/cpython/pull/30122
|
||||
- Python 3.11 alpha 3 changed how recursion limits are stored.
|
||||
- https://github.com/python/cpython/pull/29524
|
||||
- Python 3.11 alpha 4 changed how exception state is stored. It also includes a
|
||||
change to help greenlet save and restore the interpreter frame "data stack".
|
||||
- https://github.com/python/cpython/pull/30122
|
||||
- https://github.com/python/cpython/pull/30234
|
||||
*/
|
||||
# define GREENLET_PY311 1
|
||||
#else
|
||||
# define GREENLET_PY311 0
|
||||
#endif
|
||||
|
||||
|
||||
#if PY_VERSION_HEX >= 0x30C0000
|
||||
# define GREENLET_PY312 1
|
||||
#else
|
||||
# define GREENLET_PY312 0
|
||||
#endif
|
||||
|
||||
#ifndef Py_SET_REFCNT
|
||||
/* Py_REFCNT and Py_SIZE macros are converted to functions
|
||||
https://bugs.python.org/issue39573 */
|
||||
# define Py_SET_REFCNT(obj, refcnt) Py_REFCNT(obj) = (refcnt)
|
||||
#endif
|
||||
|
||||
#ifndef _Py_DEC_REFTOTAL
|
||||
/* _Py_DEC_REFTOTAL macro has been removed from Python 3.9 by:
|
||||
https://github.com/python/cpython/commit/49932fec62c616ec88da52642339d83ae719e924
|
||||
|
||||
The symbol we use to replace it was removed by at least 3.12.
|
||||
*/
|
||||
# ifdef Py_REF_DEBUG
|
||||
# if GREENLET_PY312
|
||||
# define _Py_DEC_REFTOTAL
|
||||
# else
|
||||
# define _Py_DEC_REFTOTAL _Py_RefTotal--
|
||||
# endif
|
||||
# else
|
||||
# define _Py_DEC_REFTOTAL
|
||||
# endif
|
||||
#endif
|
||||
// Define these flags like Cython does if we're on an old version.
|
||||
#ifndef Py_TPFLAGS_CHECKTYPES
|
||||
#define Py_TPFLAGS_CHECKTYPES 0
|
||||
#endif
|
||||
#ifndef Py_TPFLAGS_HAVE_INDEX
|
||||
#define Py_TPFLAGS_HAVE_INDEX 0
|
||||
#endif
|
||||
#ifndef Py_TPFLAGS_HAVE_NEWBUFFER
|
||||
#define Py_TPFLAGS_HAVE_NEWBUFFER 0
|
||||
#endif
|
||||
|
||||
#ifndef Py_TPFLAGS_HAVE_VERSION_TAG
|
||||
#define Py_TPFLAGS_HAVE_VERSION_TAG 0
|
||||
#endif
|
||||
|
||||
#define G_TPFLAGS_DEFAULT Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_VERSION_TAG | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_HAVE_NEWBUFFER | Py_TPFLAGS_HAVE_GC
|
||||
|
||||
|
||||
#if PY_VERSION_HEX < 0x03090000
|
||||
// The official version only became available in 3.9
|
||||
# define PyObject_GC_IsTracked(o) _PyObject_GC_IS_TRACKED(o)
|
||||
#endif
|
||||
|
||||
|
||||
// bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2
|
||||
#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
|
||||
static inline void PyThreadState_EnterTracing(PyThreadState *tstate)
|
||||
{
|
||||
tstate->tracing++;
|
||||
#if PY_VERSION_HEX >= 0x030A00A1
|
||||
tstate->cframe->use_tracing = 0;
|
||||
#else
|
||||
tstate->use_tracing = 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2
|
||||
#if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION)
|
||||
static inline void PyThreadState_LeaveTracing(PyThreadState *tstate)
|
||||
{
|
||||
tstate->tracing--;
|
||||
int use_tracing = (tstate->c_tracefunc != NULL
|
||||
|| tstate->c_profilefunc != NULL);
|
||||
#if PY_VERSION_HEX >= 0x030A00A1
|
||||
tstate->cframe->use_tracing = use_tracing;
|
||||
#else
|
||||
tstate->use_tracing = use_tracing;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GREENLET_CPYTHON_COMPAT_H */
|
|
@ -1,150 +0,0 @@
|
|||
#ifndef GREENLET_EXCEPTIONS_HPP
|
||||
#define GREENLET_EXCEPTIONS_HPP
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
namespace greenlet {
|
||||
|
||||
class PyErrOccurred : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
||||
// CAUTION: In debug builds, may run arbitrary Python code.
|
||||
static const PyErrOccurred
|
||||
from_current()
|
||||
{
|
||||
assert(PyErr_Occurred());
|
||||
#ifndef NDEBUG
|
||||
// This is not exception safe, and
|
||||
// not necessarily safe in general (what if it switches?)
|
||||
// But we only do this in debug mode, where we are in
|
||||
// tight control of what exceptions are getting raised and
|
||||
// can prevent those issues.
|
||||
|
||||
// You can't call PyObject_Str with a pending exception.
|
||||
PyObject* typ;
|
||||
PyObject* val;
|
||||
PyObject* tb;
|
||||
|
||||
PyErr_Fetch(&typ, &val, &tb);
|
||||
PyObject* typs = PyObject_Str(typ);
|
||||
PyObject* vals = PyObject_Str(val ? val : typ);
|
||||
const char* typ_msg = PyUnicode_AsUTF8(typs);
|
||||
const char* val_msg = PyUnicode_AsUTF8(vals);
|
||||
PyErr_Restore(typ, val, tb);
|
||||
|
||||
std::string msg(typ_msg);
|
||||
msg += ": ";
|
||||
msg += val_msg;
|
||||
PyErrOccurred ex(msg);
|
||||
Py_XDECREF(typs);
|
||||
Py_XDECREF(vals);
|
||||
|
||||
return ex;
|
||||
#else
|
||||
return PyErrOccurred();
|
||||
#endif
|
||||
}
|
||||
|
||||
PyErrOccurred() : std::runtime_error("")
|
||||
{
|
||||
assert(PyErr_Occurred());
|
||||
}
|
||||
|
||||
PyErrOccurred(const std::string& msg) : std::runtime_error(msg)
|
||||
{
|
||||
assert(PyErr_Occurred());
|
||||
}
|
||||
|
||||
PyErrOccurred(PyObject* exc_kind, const char* const msg)
|
||||
: std::runtime_error(msg)
|
||||
{
|
||||
PyErr_SetString(exc_kind, msg);
|
||||
}
|
||||
|
||||
PyErrOccurred(PyObject* exc_kind, const std::string msg)
|
||||
: std::runtime_error(msg)
|
||||
{
|
||||
// This copies the c_str, so we don't have any lifetime
|
||||
// issues to worry about.
|
||||
PyErr_SetString(exc_kind, msg.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class TypeError : public PyErrOccurred
|
||||
{
|
||||
public:
|
||||
TypeError(const char* const what)
|
||||
: PyErrOccurred(PyExc_TypeError, what)
|
||||
{
|
||||
}
|
||||
TypeError(const std::string what)
|
||||
: PyErrOccurred(PyExc_TypeError, what)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ValueError : public PyErrOccurred
|
||||
{
|
||||
public:
|
||||
ValueError(const char* const what)
|
||||
: PyErrOccurred(PyExc_ValueError, what)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class AttributeError : public PyErrOccurred
|
||||
{
|
||||
public:
|
||||
AttributeError(const char* const what)
|
||||
: PyErrOccurred(PyExc_AttributeError, what)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls `Py_FatalError` when constructed, so you can't actually
|
||||
* throw this. It just makes static analysis easier.
|
||||
*/
|
||||
class PyFatalError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
PyFatalError(const char* const msg)
|
||||
: std::runtime_error(msg)
|
||||
{
|
||||
Py_FatalError(msg);
|
||||
}
|
||||
};
|
||||
|
||||
static inline PyObject*
|
||||
Require(PyObject* p, const std::string& msg="")
|
||||
{
|
||||
if (!p) {
|
||||
throw PyErrOccurred(msg);
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
static inline void
|
||||
Require(const int retval)
|
||||
{
|
||||
if (retval < 0) {
|
||||
throw PyErrOccurred();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,805 +0,0 @@
|
|||
#ifndef GREENLET_GREENLET_HPP
|
||||
#define GREENLET_GREENLET_HPP
|
||||
/*
|
||||
* Declarations of the core data structures.
|
||||
*/
|
||||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
|
||||
#include "greenlet_compiler_compat.hpp"
|
||||
#include "greenlet_refs.hpp"
|
||||
#include "greenlet_cpython_compat.hpp"
|
||||
#include "greenlet_allocator.hpp"
|
||||
|
||||
using greenlet::refs::OwnedObject;
|
||||
using greenlet::refs::OwnedGreenlet;
|
||||
using greenlet::refs::OwnedMainGreenlet;
|
||||
using greenlet::refs::BorrowedGreenlet;
|
||||
|
||||
#if PY_VERSION_HEX < 0x30B00A6
|
||||
# define _PyCFrame CFrame
|
||||
# define _PyInterpreterFrame _interpreter_frame
|
||||
#endif
|
||||
|
||||
#if GREENLET_PY312
|
||||
# include "internal/pycore_frame.h"
|
||||
#endif
|
||||
|
||||
// XXX: TODO: Work to remove all virtual functions
|
||||
// for speed of calling and size of objects (no vtable).
|
||||
// One pattern is the Curiously Recurring Template
|
||||
namespace greenlet
|
||||
{
|
||||
class ExceptionState
|
||||
{
|
||||
private:
|
||||
G_NO_COPIES_OF_CLS(ExceptionState);
|
||||
|
||||
// Even though these are borrowed objects, we actually own
|
||||
// them, when they're not null.
|
||||
// XXX: Express that in the API.
|
||||
private:
|
||||
_PyErr_StackItem* exc_info;
|
||||
_PyErr_StackItem exc_state;
|
||||
public:
|
||||
ExceptionState();
|
||||
void operator<<(const PyThreadState *const tstate) noexcept;
|
||||
void operator>>(PyThreadState* tstate) noexcept;
|
||||
void clear() noexcept;
|
||||
|
||||
int tp_traverse(visitproc visit, void* arg) noexcept;
|
||||
void tp_clear() noexcept;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void operator<<(const PyThreadState *const tstate, T& exc);
|
||||
|
||||
class PythonStateContext
|
||||
{
|
||||
protected:
|
||||
greenlet::refs::OwnedContext _context;
|
||||
public:
|
||||
inline const greenlet::refs::OwnedContext& context() const
|
||||
{
|
||||
return this->_context;
|
||||
}
|
||||
inline greenlet::refs::OwnedContext& context()
|
||||
{
|
||||
return this->_context;
|
||||
}
|
||||
|
||||
inline void tp_clear()
|
||||
{
|
||||
this->_context.CLEAR();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static PyObject* context(T* tstate)
|
||||
{
|
||||
return tstate->context;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline static void context(T* tstate, PyObject* new_context)
|
||||
{
|
||||
tstate->context = new_context;
|
||||
tstate->context_ver++;
|
||||
}
|
||||
};
|
||||
class SwitchingArgs;
|
||||
class PythonState : public PythonStateContext
|
||||
{
|
||||
public:
|
||||
typedef greenlet::refs::OwnedReference<struct _frame> OwnedFrame;
|
||||
private:
|
||||
G_NO_COPIES_OF_CLS(PythonState);
|
||||
// We own this if we're suspended (although currently we don't
|
||||
// tp_traverse into it; that's a TODO). If we're running, it's
|
||||
// empty. If we get deallocated and *still* have a frame, it
|
||||
// won't be reachable from the place that normally decref's
|
||||
// it, so we need to do it (hence owning it).
|
||||
OwnedFrame _top_frame;
|
||||
#if GREENLET_USE_CFRAME
|
||||
_PyCFrame* cframe;
|
||||
int use_tracing;
|
||||
#endif
|
||||
#if GREENLET_PY312
|
||||
int py_recursion_depth;
|
||||
int c_recursion_depth;
|
||||
#else
|
||||
int recursion_depth;
|
||||
#endif
|
||||
int trash_delete_nesting;
|
||||
#if GREENLET_PY311
|
||||
_PyInterpreterFrame* current_frame;
|
||||
_PyStackChunk* datastack_chunk;
|
||||
PyObject** datastack_top;
|
||||
PyObject** datastack_limit;
|
||||
#endif
|
||||
// The PyInterpreterFrame list on 3.12+ contains some entries that are
|
||||
// on the C stack, which can't be directly accessed while a greenlet is
|
||||
// suspended. In order to keep greenlet gr_frame introspection working,
|
||||
// we adjust stack switching to rewrite the interpreter frame list
|
||||
// to skip these C-stack frames; we call this "exposing" the greenlet's
|
||||
// frames because it makes them valid to work with in Python. Then when
|
||||
// the greenlet is resumed we need to remember to reverse the operation
|
||||
// we did. The C-stack frames are "entry frames" which are a low-level
|
||||
// interpreter detail; they're not needed for introspection, but do
|
||||
// need to be present for the eval loop to work.
|
||||
void unexpose_frames();
|
||||
|
||||
public:
|
||||
|
||||
PythonState();
|
||||
// You can use this for testing whether we have a frame
|
||||
// or not. It returns const so they can't modify it.
|
||||
const OwnedFrame& top_frame() const noexcept;
|
||||
|
||||
inline void operator<<(const PyThreadState *const tstate) noexcept;
|
||||
inline void operator>>(PyThreadState* tstate) noexcept;
|
||||
void clear() noexcept;
|
||||
|
||||
int tp_traverse(visitproc visit, void* arg, bool visit_top_frame) noexcept;
|
||||
void tp_clear(bool own_top_frame) noexcept;
|
||||
void set_initial_state(const PyThreadState* const tstate) noexcept;
|
||||
#if GREENLET_USE_CFRAME
|
||||
void set_new_cframe(_PyCFrame& frame) noexcept;
|
||||
#endif
|
||||
|
||||
inline void may_switch_away() noexcept;
|
||||
inline void will_switch_from(PyThreadState *const origin_tstate) noexcept;
|
||||
void did_finish(PyThreadState* tstate) noexcept;
|
||||
};
|
||||
|
||||
class StackState
|
||||
{
|
||||
// By having only plain C (POD) members, no virtual functions
|
||||
// or bases, we get a trivial assignment operator generated
|
||||
// for us. However, that's not safe since we do manage memory.
|
||||
// So we declare an assignment operator that only works if we
|
||||
// don't have any memory allocated. (We don't use
|
||||
// std::shared_ptr for reference counting just to keep this
|
||||
// object small)
|
||||
private:
|
||||
char* _stack_start;
|
||||
char* stack_stop;
|
||||
char* stack_copy;
|
||||
intptr_t _stack_saved;
|
||||
StackState* stack_prev;
|
||||
inline int copy_stack_to_heap_up_to(const char* const stop) noexcept;
|
||||
inline void free_stack_copy() noexcept;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a started, but inactive, state, using *current*
|
||||
* as the previous.
|
||||
*/
|
||||
StackState(void* mark, StackState& current);
|
||||
/**
|
||||
* Creates an inactive, unstarted, state.
|
||||
*/
|
||||
StackState();
|
||||
~StackState();
|
||||
StackState(const StackState& other);
|
||||
StackState& operator=(const StackState& other);
|
||||
inline void copy_heap_to_stack(const StackState& current) noexcept;
|
||||
inline int copy_stack_to_heap(char* const stackref, const StackState& current) noexcept;
|
||||
inline bool started() const noexcept;
|
||||
inline bool main() const noexcept;
|
||||
inline bool active() const noexcept;
|
||||
inline void set_active() noexcept;
|
||||
inline void set_inactive() noexcept;
|
||||
inline intptr_t stack_saved() const noexcept;
|
||||
inline char* stack_start() const noexcept;
|
||||
static inline StackState make_main() noexcept;
|
||||
#ifdef GREENLET_USE_STDIO
|
||||
friend std::ostream& operator<<(std::ostream& os, const StackState& s);
|
||||
#endif
|
||||
|
||||
// Fill in [dest, dest + n) with the values that would be at
|
||||
// [src, src + n) while this greenlet is running. This is like memcpy
|
||||
// except that if the greenlet is suspended it accounts for the portion
|
||||
// of the greenlet's stack that was spilled to the heap. `src` may
|
||||
// be on this greenlet's stack, or on the heap, but not on a different
|
||||
// greenlet's stack.
|
||||
void copy_from_stack(void* dest, const void* src, size_t n) const;
|
||||
};
|
||||
#ifdef GREENLET_USE_STDIO
|
||||
std::ostream& operator<<(std::ostream& os, const StackState& s);
|
||||
#endif
|
||||
|
||||
class SwitchingArgs
|
||||
{
|
||||
private:
|
||||
G_NO_ASSIGNMENT_OF_CLS(SwitchingArgs);
|
||||
// If args and kwargs are both false (NULL), this is a *throw*, not a
|
||||
// switch. PyErr_... must have been called already.
|
||||
OwnedObject _args;
|
||||
OwnedObject _kwargs;
|
||||
public:
|
||||
|
||||
SwitchingArgs()
|
||||
{}
|
||||
|
||||
SwitchingArgs(const OwnedObject& args, const OwnedObject& kwargs)
|
||||
: _args(args),
|
||||
_kwargs(kwargs)
|
||||
{}
|
||||
|
||||
SwitchingArgs(const SwitchingArgs& other)
|
||||
: _args(other._args),
|
||||
_kwargs(other._kwargs)
|
||||
{}
|
||||
|
||||
const OwnedObject& args()
|
||||
{
|
||||
return this->_args;
|
||||
}
|
||||
|
||||
const OwnedObject& kwargs()
|
||||
{
|
||||
return this->_kwargs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves ownership from the argument to this object.
|
||||
*/
|
||||
SwitchingArgs& operator<<=(SwitchingArgs& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
this->_args = other._args;
|
||||
this->_kwargs = other._kwargs;
|
||||
other.CLEAR();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires ownership of the argument (consumes the reference).
|
||||
*/
|
||||
SwitchingArgs& operator<<=(PyObject* args)
|
||||
{
|
||||
this->_args = OwnedObject::consuming(args);
|
||||
this->_kwargs.CLEAR();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires ownership of the argument.
|
||||
*
|
||||
* Sets the args to be the given value; clears the kwargs.
|
||||
*/
|
||||
SwitchingArgs& operator<<=(OwnedObject& args)
|
||||
{
|
||||
assert(&args != &this->_args);
|
||||
this->_args = args;
|
||||
this->_kwargs.CLEAR();
|
||||
args.CLEAR();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return this->_args || this->_kwargs;
|
||||
}
|
||||
|
||||
inline void CLEAR()
|
||||
{
|
||||
this->_args.CLEAR();
|
||||
this->_kwargs.CLEAR();
|
||||
}
|
||||
|
||||
const std::string as_str() const noexcept
|
||||
{
|
||||
return PyUnicode_AsUTF8(
|
||||
OwnedObject::consuming(
|
||||
PyUnicode_FromFormat(
|
||||
"SwitchingArgs(args=%R, kwargs=%R)",
|
||||
this->_args.borrow(),
|
||||
this->_kwargs.borrow()
|
||||
)
|
||||
).borrow()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class ThreadState;
|
||||
|
||||
class UserGreenlet;
|
||||
class MainGreenlet;
|
||||
|
||||
class Greenlet
|
||||
{
|
||||
private:
|
||||
G_NO_COPIES_OF_CLS(Greenlet);
|
||||
private:
|
||||
// XXX: Work to remove these.
|
||||
friend class ThreadState;
|
||||
friend class UserGreenlet;
|
||||
friend class MainGreenlet;
|
||||
protected:
|
||||
ExceptionState exception_state;
|
||||
SwitchingArgs switch_args;
|
||||
StackState stack_state;
|
||||
PythonState python_state;
|
||||
Greenlet(PyGreenlet* p, const StackState& initial_state);
|
||||
public:
|
||||
Greenlet(PyGreenlet* p);
|
||||
virtual ~Greenlet();
|
||||
|
||||
const OwnedObject context() const;
|
||||
|
||||
// You MUST call this _very_ early in the switching process to
|
||||
// prepare anything that may need prepared. This might perform
|
||||
// garbage collections or otherwise run arbitrary Python code.
|
||||
//
|
||||
// One specific use of it is for Python 3.11+, preventing
|
||||
// running arbitrary code at unsafe times. See
|
||||
// PythonState::may_switch_away().
|
||||
inline void may_switch_away()
|
||||
{
|
||||
this->python_state.may_switch_away();
|
||||
}
|
||||
|
||||
inline void context(refs::BorrowedObject new_context);
|
||||
|
||||
inline SwitchingArgs& args()
|
||||
{
|
||||
return this->switch_args;
|
||||
}
|
||||
|
||||
virtual const refs::BorrowedMainGreenlet main_greenlet() const = 0;
|
||||
|
||||
inline intptr_t stack_saved() const noexcept
|
||||
{
|
||||
return this->stack_state.stack_saved();
|
||||
}
|
||||
|
||||
// This is used by the macro SLP_SAVE_STATE to compute the
|
||||
// difference in stack sizes. It might be nice to handle the
|
||||
// computation ourself, but the type of the result
|
||||
// varies by platform, so doing it in the macro is the
|
||||
// simplest way.
|
||||
inline const char* stack_start() const noexcept
|
||||
{
|
||||
return this->stack_state.stack_start();
|
||||
}
|
||||
|
||||
virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state);
|
||||
virtual OwnedObject g_switch() = 0;
|
||||
/**
|
||||
* Force the greenlet to appear dead. Used when it's not
|
||||
* possible to throw an exception into a greenlet anymore.
|
||||
*
|
||||
* This losses access to the thread state and the main greenlet.
|
||||
*/
|
||||
virtual void murder_in_place();
|
||||
|
||||
/**
|
||||
* Called when somebody notices we were running in a dead
|
||||
* thread to allow cleaning up resources (because we can't
|
||||
* raise GreenletExit into it anymore).
|
||||
* This is very similar to ``murder_in_place()``, except that
|
||||
* it DOES NOT lose the main greenlet or thread state.
|
||||
*/
|
||||
inline void deactivate_and_free();
|
||||
|
||||
|
||||
// Called when some thread wants to deallocate a greenlet
|
||||
// object.
|
||||
// The thread may or may not be the same thread the greenlet
|
||||
// was running in.
|
||||
// The thread state will be null if the thread the greenlet
|
||||
// was running in was known to have exited.
|
||||
void deallocing_greenlet_in_thread(const ThreadState* current_state);
|
||||
|
||||
// Must be called on 3.12+ before exposing a suspended greenlet's
|
||||
// frames to user code. This rewrites the linked list of interpreter
|
||||
// frames to skip the ones that are being stored on the C stack (which
|
||||
// can't be safely accessed while the greenlet is suspended because
|
||||
// that stack space might be hosting a different greenlet), and
|
||||
// sets PythonState::frames_were_exposed so we remember to restore
|
||||
// the original list before resuming the greenlet. The C-stack frames
|
||||
// are a low-level interpreter implementation detail; while they're
|
||||
// important to the bytecode eval loop, they're superfluous for
|
||||
// introspection purposes.
|
||||
void expose_frames();
|
||||
|
||||
|
||||
// TODO: Figure out how to make these non-public.
|
||||
inline void slp_restore_state() noexcept;
|
||||
inline int slp_save_state(char *const stackref) noexcept;
|
||||
|
||||
inline bool is_currently_running_in_some_thread() const;
|
||||
virtual bool belongs_to_thread(const ThreadState* state) const;
|
||||
|
||||
inline bool started() const
|
||||
{
|
||||
return this->stack_state.started();
|
||||
}
|
||||
inline bool active() const
|
||||
{
|
||||
return this->stack_state.active();
|
||||
}
|
||||
inline bool main() const
|
||||
{
|
||||
return this->stack_state.main();
|
||||
}
|
||||
virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const = 0;
|
||||
|
||||
virtual const OwnedGreenlet parent() const = 0;
|
||||
virtual void parent(const refs::BorrowedObject new_parent) = 0;
|
||||
|
||||
inline const PythonState::OwnedFrame& top_frame()
|
||||
{
|
||||
return this->python_state.top_frame();
|
||||
}
|
||||
|
||||
virtual const OwnedObject& run() const = 0;
|
||||
virtual void run(const refs::BorrowedObject nrun) = 0;
|
||||
|
||||
|
||||
virtual int tp_traverse(visitproc visit, void* arg);
|
||||
virtual int tp_clear();
|
||||
|
||||
|
||||
// Return the thread state that the greenlet is running in, or
|
||||
// null if the greenlet is not running or the thread is known
|
||||
// to have exited.
|
||||
virtual ThreadState* thread_state() const noexcept = 0;
|
||||
|
||||
// Return true if the greenlet is known to have been running
|
||||
// (active) in a thread that has now exited.
|
||||
virtual bool was_running_in_dead_thread() const noexcept = 0;
|
||||
|
||||
// Return a borrowed greenlet that is the Python object
|
||||
// this object represents.
|
||||
virtual BorrowedGreenlet self() const noexcept = 0;
|
||||
|
||||
// For testing. If this returns true, we should pretend that
|
||||
// slp_switch() failed.
|
||||
virtual bool force_slp_switch_error() const noexcept;
|
||||
|
||||
protected:
|
||||
inline void release_args();
|
||||
|
||||
// The functions that must not be inlined are declared virtual.
|
||||
// We also mark them as protected, not private, so that the
|
||||
// compiler is forced to call them through a function pointer.
|
||||
// (A sufficiently smart compiler could directly call a private
|
||||
// virtual function since it can never be overridden in a
|
||||
// subclass).
|
||||
|
||||
// Also TODO: Switch away from integer error codes and to enums,
|
||||
// or throw exceptions when possible.
|
||||
struct switchstack_result_t
|
||||
{
|
||||
int status;
|
||||
Greenlet* the_new_current_greenlet;
|
||||
OwnedGreenlet origin_greenlet;
|
||||
|
||||
switchstack_result_t()
|
||||
: status(0),
|
||||
the_new_current_greenlet(nullptr)
|
||||
{}
|
||||
|
||||
switchstack_result_t(int err)
|
||||
: status(err),
|
||||
the_new_current_greenlet(nullptr)
|
||||
{}
|
||||
|
||||
switchstack_result_t(int err, Greenlet* state, OwnedGreenlet& origin)
|
||||
: status(err),
|
||||
the_new_current_greenlet(state),
|
||||
origin_greenlet(origin)
|
||||
{
|
||||
}
|
||||
|
||||
switchstack_result_t(int err, Greenlet* state, const BorrowedGreenlet& origin)
|
||||
: status(err),
|
||||
the_new_current_greenlet(state),
|
||||
origin_greenlet(origin)
|
||||
{
|
||||
}
|
||||
|
||||
switchstack_result_t(const switchstack_result_t& other)
|
||||
: status(other.status),
|
||||
the_new_current_greenlet(other.the_new_current_greenlet),
|
||||
origin_greenlet(other.origin_greenlet)
|
||||
{}
|
||||
|
||||
switchstack_result_t& operator=(const switchstack_result_t& other)
|
||||
{
|
||||
this->status = other.status;
|
||||
this->the_new_current_greenlet = other.the_new_current_greenlet;
|
||||
this->origin_greenlet = other.origin_greenlet;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
OwnedObject on_switchstack_or_initialstub_failure(
|
||||
Greenlet* target,
|
||||
const switchstack_result_t& err,
|
||||
const bool target_was_me=false,
|
||||
const bool was_initial_stub=false);
|
||||
|
||||
// Returns the previous greenlet we just switched away from.
|
||||
virtual OwnedGreenlet g_switchstack_success() noexcept;
|
||||
|
||||
|
||||
// Check the preconditions for switching to this greenlet; if they
|
||||
// aren't met, throws PyErrOccurred. Most callers will want to
|
||||
// catch this and clear the arguments
|
||||
inline void check_switch_allowed() const;
|
||||
class GreenletStartedWhileInPython : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
GreenletStartedWhileInPython() : std::runtime_error("")
|
||||
{}
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
/**
|
||||
Perform a stack switch into this greenlet.
|
||||
|
||||
This temporarily sets the global variable
|
||||
``switching_thread_state`` to this greenlet; as soon as the
|
||||
call to ``slp_switch`` completes, this is reset to NULL.
|
||||
Consequently, this depends on the GIL.
|
||||
|
||||
TODO: Adopt the stackman model and pass ``slp_switch`` a
|
||||
callback function and context pointer; this eliminates the
|
||||
need for global variables altogether.
|
||||
|
||||
Because the stack switch happens in this function, this
|
||||
function can't use its own stack (local) variables, set
|
||||
before the switch, and then accessed after the switch.
|
||||
|
||||
Further, you con't even access ``g_thread_state_global``
|
||||
before and after the switch from the global variable.
|
||||
Because it is thread local some compilers cache it in a
|
||||
register/on the stack, notably new versions of MSVC; this
|
||||
breaks with strange crashes sometime later, because writing
|
||||
to anything in ``g_thread_state_global`` after the switch
|
||||
is actually writing to random memory. For this reason, we
|
||||
call a non-inlined function to finish the operation. (XXX:
|
||||
The ``/GT`` MSVC compiler argument probably fixes that.)
|
||||
|
||||
It is very important that stack switch is 'atomic', i.e. no
|
||||
calls into other Python code allowed (except very few that
|
||||
are safe), because global variables are very fragile. (This
|
||||
should no longer be the case with thread-local variables.)
|
||||
|
||||
*/
|
||||
// Made virtual to facilitate subclassing UserGreenlet for testing.
|
||||
virtual switchstack_result_t g_switchstack(void);
|
||||
|
||||
class TracingGuard
|
||||
{
|
||||
private:
|
||||
PyThreadState* tstate;
|
||||
public:
|
||||
TracingGuard()
|
||||
: tstate(PyThreadState_GET())
|
||||
{
|
||||
PyThreadState_EnterTracing(this->tstate);
|
||||
}
|
||||
|
||||
~TracingGuard()
|
||||
{
|
||||
PyThreadState_LeaveTracing(this->tstate);
|
||||
this->tstate = nullptr;
|
||||
}
|
||||
|
||||
inline void CallTraceFunction(const OwnedObject& tracefunc,
|
||||
const greenlet::refs::ImmortalEventName& event,
|
||||
const BorrowedGreenlet& origin,
|
||||
const BorrowedGreenlet& target)
|
||||
{
|
||||
// TODO: This calls tracefunc(event, (origin, target)). Add a shortcut
|
||||
// function for that that's specialized to avoid the Py_BuildValue
|
||||
// string parsing, or start with just using "ON" format with PyTuple_Pack(2,
|
||||
// origin, target). That seems like what the N format is meant
|
||||
// for.
|
||||
// XXX: Why does event not automatically cast back to a PyObject?
|
||||
// It tries to call the "deleted constructor ImmortalEventName
|
||||
// const" instead.
|
||||
assert(tracefunc);
|
||||
assert(event);
|
||||
assert(origin);
|
||||
assert(target);
|
||||
greenlet::refs::NewReference retval(
|
||||
PyObject_CallFunction(
|
||||
tracefunc.borrow(),
|
||||
"O(OO)",
|
||||
event.borrow(),
|
||||
origin.borrow(),
|
||||
target.borrow()
|
||||
));
|
||||
if (!retval) {
|
||||
throw PyErrOccurred::from_current();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void
|
||||
g_calltrace(const OwnedObject& tracefunc,
|
||||
const greenlet::refs::ImmortalEventName& event,
|
||||
const greenlet::refs::BorrowedGreenlet& origin,
|
||||
const BorrowedGreenlet& target);
|
||||
private:
|
||||
OwnedObject g_switch_finish(const switchstack_result_t& err);
|
||||
|
||||
};
|
||||
|
||||
class UserGreenlet : public Greenlet
|
||||
{
|
||||
private:
|
||||
static greenlet::PythonAllocator<UserGreenlet> allocator;
|
||||
BorrowedGreenlet _self;
|
||||
OwnedMainGreenlet _main_greenlet;
|
||||
OwnedObject _run_callable;
|
||||
OwnedGreenlet _parent;
|
||||
public:
|
||||
static void* operator new(size_t UNUSED(count));
|
||||
static void operator delete(void* ptr);
|
||||
|
||||
UserGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent);
|
||||
virtual ~UserGreenlet();
|
||||
|
||||
virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const;
|
||||
virtual bool was_running_in_dead_thread() const noexcept;
|
||||
virtual ThreadState* thread_state() const noexcept;
|
||||
virtual OwnedObject g_switch();
|
||||
virtual const OwnedObject& run() const
|
||||
{
|
||||
if (this->started() || !this->_run_callable) {
|
||||
throw AttributeError("run");
|
||||
}
|
||||
return this->_run_callable;
|
||||
}
|
||||
virtual void run(const refs::BorrowedObject nrun);
|
||||
|
||||
virtual const OwnedGreenlet parent() const;
|
||||
virtual void parent(const refs::BorrowedObject new_parent);
|
||||
|
||||
virtual const refs::BorrowedMainGreenlet main_greenlet() const;
|
||||
|
||||
virtual BorrowedGreenlet self() const noexcept;
|
||||
virtual void murder_in_place();
|
||||
virtual bool belongs_to_thread(const ThreadState* state) const;
|
||||
virtual int tp_traverse(visitproc visit, void* arg);
|
||||
virtual int tp_clear();
|
||||
class ParentIsCurrentGuard
|
||||
{
|
||||
private:
|
||||
OwnedGreenlet oldparent;
|
||||
UserGreenlet* greenlet;
|
||||
G_NO_COPIES_OF_CLS(ParentIsCurrentGuard);
|
||||
public:
|
||||
ParentIsCurrentGuard(UserGreenlet* p, const ThreadState& thread_state);
|
||||
~ParentIsCurrentGuard();
|
||||
};
|
||||
virtual OwnedObject throw_GreenletExit_during_dealloc(const ThreadState& current_thread_state);
|
||||
protected:
|
||||
virtual switchstack_result_t g_initialstub(void* mark);
|
||||
private:
|
||||
// This function isn't meant to return.
|
||||
// This accepts raw pointers and the ownership of them at the
|
||||
// same time. The caller should use ``inner_bootstrap(origin.relinquish_ownership())``.
|
||||
void inner_bootstrap(PyGreenlet* origin_greenlet, PyObject* run);
|
||||
};
|
||||
|
||||
class BrokenGreenlet : public UserGreenlet
|
||||
{
|
||||
private:
|
||||
static greenlet::PythonAllocator<BrokenGreenlet> allocator;
|
||||
public:
|
||||
bool _force_switch_error = false;
|
||||
bool _force_slp_switch_error = false;
|
||||
|
||||
static void* operator new(size_t UNUSED(count));
|
||||
static void operator delete(void* ptr);
|
||||
BrokenGreenlet(PyGreenlet* p, BorrowedGreenlet the_parent)
|
||||
: UserGreenlet(p, the_parent)
|
||||
{}
|
||||
virtual ~BrokenGreenlet()
|
||||
{}
|
||||
|
||||
virtual switchstack_result_t g_switchstack(void);
|
||||
virtual bool force_slp_switch_error() const noexcept;
|
||||
|
||||
};
|
||||
|
||||
class MainGreenlet : public Greenlet
|
||||
{
|
||||
private:
|
||||
static greenlet::PythonAllocator<MainGreenlet> allocator;
|
||||
refs::BorrowedMainGreenlet _self;
|
||||
ThreadState* _thread_state;
|
||||
G_NO_COPIES_OF_CLS(MainGreenlet);
|
||||
public:
|
||||
static void* operator new(size_t UNUSED(count));
|
||||
static void operator delete(void* ptr);
|
||||
|
||||
MainGreenlet(refs::BorrowedMainGreenlet::PyType*, ThreadState*);
|
||||
virtual ~MainGreenlet();
|
||||
|
||||
|
||||
virtual const OwnedObject& run() const;
|
||||
virtual void run(const refs::BorrowedObject nrun);
|
||||
|
||||
virtual const OwnedGreenlet parent() const;
|
||||
virtual void parent(const refs::BorrowedObject new_parent);
|
||||
|
||||
virtual const refs::BorrowedMainGreenlet main_greenlet() const;
|
||||
|
||||
virtual refs::BorrowedMainGreenlet find_main_greenlet_in_lineage() const;
|
||||
virtual bool was_running_in_dead_thread() const noexcept;
|
||||
virtual ThreadState* thread_state() const noexcept;
|
||||
void thread_state(ThreadState*) noexcept;
|
||||
virtual OwnedObject g_switch();
|
||||
virtual BorrowedGreenlet self() const noexcept;
|
||||
virtual int tp_traverse(visitproc visit, void* arg);
|
||||
};
|
||||
|
||||
// Instantiate one on the stack to save the GC state,
|
||||
// and then disable GC. When it goes out of scope, GC will be
|
||||
// restored to its original state. Sadly, these APIs are only
|
||||
// available on 3.10+; luckily, we only need them on 3.11+.
|
||||
#if GREENLET_PY310
|
||||
class GCDisabledGuard
|
||||
{
|
||||
private:
|
||||
int was_enabled = 0;
|
||||
public:
|
||||
GCDisabledGuard()
|
||||
: was_enabled(PyGC_IsEnabled())
|
||||
{
|
||||
PyGC_Disable();
|
||||
}
|
||||
|
||||
~GCDisabledGuard()
|
||||
{
|
||||
if (this->was_enabled) {
|
||||
PyGC_Enable();
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
OwnedObject& operator<<=(OwnedObject& lhs, greenlet::SwitchingArgs& rhs) noexcept;
|
||||
|
||||
//TODO: Greenlet::g_switch() should call this automatically on its
|
||||
//return value. As it is, the module code is calling it.
|
||||
static inline OwnedObject
|
||||
single_result(const OwnedObject& results)
|
||||
{
|
||||
if (results
|
||||
&& PyTuple_Check(results.borrow())
|
||||
&& PyTuple_GET_SIZE(results.borrow()) == 1) {
|
||||
PyObject* result = PyTuple_GET_ITEM(results.borrow(), 0);
|
||||
assert(result);
|
||||
return OwnedObject::owning(result);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
static OwnedObject
|
||||
g_handle_exit(const OwnedObject& greenlet_result);
|
||||
|
||||
|
||||
template<typename T>
|
||||
void operator<<(const PyThreadState *const lhs, T& rhs)
|
||||
{
|
||||
rhs.operator<<(lhs);
|
||||
}
|
||||
|
||||
} // namespace greenlet ;
|
||||
|
||||
#endif
|
|
@ -1,106 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */
|
||||
#ifndef GREENLET_INTERNAL_H
|
||||
#define GREENLET_INTERNAL_H
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wunused-function"
|
||||
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
# pragma clang diagnostic ignored "-Wunused-variable"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation helpers.
|
||||
*
|
||||
* C++ templates and inline functions should go here.
|
||||
*/
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "greenlet_compiler_compat.hpp"
|
||||
#include "greenlet_cpython_compat.hpp"
|
||||
#include "greenlet_exceptions.hpp"
|
||||
#include "greenlet_greenlet.hpp"
|
||||
#include "greenlet_allocator.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#define GREENLET_MODULE
|
||||
struct _greenlet;
|
||||
typedef struct _greenlet PyGreenlet;
|
||||
namespace greenlet {
|
||||
|
||||
class ThreadState;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#define implementation_ptr_t greenlet::Greenlet*
|
||||
|
||||
|
||||
#include "greenlet.h"
|
||||
|
||||
G_FP_TMPL_STATIC inline void
|
||||
greenlet::refs::MainGreenletExactChecker(void *p)
|
||||
{
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
// We control the class of the main greenlet exactly.
|
||||
if (Py_TYPE(p) != &PyGreenlet_Type) {
|
||||
std::string err("MainGreenlet: Expected exactly a greenlet, not a ");
|
||||
err += Py_TYPE(p)->tp_name;
|
||||
throw greenlet::TypeError(err);
|
||||
}
|
||||
|
||||
// Greenlets from dead threads no longer respond to main() with a
|
||||
// true value; so in that case we need to perform an additional
|
||||
// check.
|
||||
Greenlet* g = ((PyGreenlet*)p)->pimpl;
|
||||
if (g->main()) {
|
||||
return;
|
||||
}
|
||||
if (!dynamic_cast<MainGreenlet*>(g)) {
|
||||
std::string err("MainGreenlet: Expected exactly a main greenlet, not a ");
|
||||
err += Py_TYPE(p)->tp_name;
|
||||
throw greenlet::TypeError(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T, greenlet::refs::TypeChecker TC>
|
||||
inline greenlet::Greenlet* greenlet::refs::_OwnedGreenlet<T, TC>::operator->() const noexcept
|
||||
{
|
||||
return reinterpret_cast<PyGreenlet*>(this->p)->pimpl;
|
||||
}
|
||||
|
||||
template <typename T, greenlet::refs::TypeChecker TC>
|
||||
inline greenlet::Greenlet* greenlet::refs::_BorrowedGreenlet<T, TC>::operator->() const noexcept
|
||||
{
|
||||
return reinterpret_cast<PyGreenlet*>(this->p)->pimpl;
|
||||
}
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
extern PyTypeObject PyGreenlet_Type;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Forward declarations needed in multiple files.
|
||||
*/
|
||||
static PyGreenlet* green_create_main(greenlet::ThreadState*);
|
||||
static PyObject* green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs);
|
||||
static int green_is_gc(BorrowedGreenlet self);
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
// Local Variables:
|
||||
// flycheck-clang-include-path: ("../../include" "/opt/local/Library/Frameworks/Python.framework/Versions/3.10/include/python3.10")
|
||||
// End:
|
File diff suppressed because it is too large
Load diff
|
@ -1,99 +0,0 @@
|
|||
#ifndef GREENLET_SLP_SWITCH_HPP
|
||||
#define GREENLET_SLP_SWITCH_HPP
|
||||
|
||||
#include "greenlet_compiler_compat.hpp"
|
||||
#include "greenlet_refs.hpp"
|
||||
|
||||
/*
|
||||
* the following macros are spliced into the OS/compiler
|
||||
* specific code, in order to simplify maintenance.
|
||||
*/
|
||||
// We can save about 10% of the time it takes to switch greenlets if
|
||||
// we thread the thread state through the slp_save_state() and the
|
||||
// following slp_restore_state() calls from
|
||||
// slp_switch()->g_switchstack() (which already needs to access it).
|
||||
//
|
||||
// However:
|
||||
//
|
||||
// that requires changing the prototypes and implementations of the
|
||||
// switching functions. If we just change the prototype of
|
||||
// slp_switch() to accept the argument and update the macros, without
|
||||
// changing the implementation of slp_switch(), we get crashes on
|
||||
// 64-bit Linux and 32-bit x86 (for reasons that aren't 100% clear);
|
||||
// on the other hand, 64-bit macOS seems to be fine. Also, 64-bit
|
||||
// windows is an issue because slp_switch is written fully in assembly
|
||||
// and currently ignores its argument so some code would have to be
|
||||
// adjusted there to pass the argument on to the
|
||||
// ``slp_save_state_asm()`` function (but interestingly, because of
|
||||
// the calling convention, the extra argument is just ignored and
|
||||
// things function fine, albeit slower, if we just modify
|
||||
// ``slp_save_state_asm`()` to fetch the pointer to pass to the
|
||||
// macro.)
|
||||
//
|
||||
// Our compromise is to use a *glabal*, untracked, weak, pointer
|
||||
// to the necessary thread state during the process of switching only.
|
||||
// This is safe because we're protected by the GIL, and if we're
|
||||
// running this code, the thread isn't exiting. This also nets us a
|
||||
// 10-12% speed improvement.
|
||||
|
||||
static greenlet::Greenlet* volatile switching_thread_state = nullptr;
|
||||
|
||||
|
||||
extern "C" {
|
||||
static int GREENLET_NOINLINE(slp_save_state_trampoline)(char* stackref);
|
||||
static void GREENLET_NOINLINE(slp_restore_state_trampoline)();
|
||||
}
|
||||
|
||||
|
||||
#define SLP_SAVE_STATE(stackref, stsizediff) \
|
||||
do { \
|
||||
assert(switching_thread_state); \
|
||||
stackref += STACK_MAGIC; \
|
||||
if (slp_save_state_trampoline((char*)stackref)) \
|
||||
return -1; \
|
||||
if (!switching_thread_state->active()) \
|
||||
return 1; \
|
||||
stsizediff = switching_thread_state->stack_start() - (char*)stackref; \
|
||||
} while (0)
|
||||
|
||||
#define SLP_RESTORE_STATE() slp_restore_state_trampoline()
|
||||
|
||||
#define SLP_EVAL
|
||||
extern "C" {
|
||||
#define slp_switch GREENLET_NOINLINE(slp_switch)
|
||||
#include "slp_platformselect.h"
|
||||
}
|
||||
#undef slp_switch
|
||||
|
||||
#ifndef STACK_MAGIC
|
||||
# error \
|
||||
"greenlet needs to be ported to this platform, or taught how to detect your compiler properly."
|
||||
#endif /* !STACK_MAGIC */
|
||||
|
||||
|
||||
|
||||
#ifdef EXTERNAL_ASM
|
||||
/* CCP addition: Make these functions, to be called from assembler.
|
||||
* The token include file for the given platform should enable the
|
||||
* EXTERNAL_ASM define so that this is included.
|
||||
*/
|
||||
extern "C" {
|
||||
intptr_t
|
||||
slp_save_state_asm(intptr_t* ref)
|
||||
{
|
||||
intptr_t diff;
|
||||
SLP_SAVE_STATE(ref, diff);
|
||||
return diff;
|
||||
}
|
||||
|
||||
void
|
||||
slp_restore_state_asm(void)
|
||||
{
|
||||
SLP_RESTORE_STATE();
|
||||
}
|
||||
|
||||
extern int slp_switch(void);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,543 +0,0 @@
|
|||
#ifndef GREENLET_THREAD_STATE_HPP
|
||||
#define GREENLET_THREAD_STATE_HPP
|
||||
|
||||
#include <ctime>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "greenlet_internal.hpp"
|
||||
#include "greenlet_refs.hpp"
|
||||
#include "greenlet_thread_support.hpp"
|
||||
|
||||
using greenlet::refs::BorrowedObject;
|
||||
using greenlet::refs::BorrowedGreenlet;
|
||||
using greenlet::refs::BorrowedMainGreenlet;
|
||||
using greenlet::refs::OwnedMainGreenlet;
|
||||
using greenlet::refs::OwnedObject;
|
||||
using greenlet::refs::OwnedGreenlet;
|
||||
using greenlet::refs::OwnedList;
|
||||
using greenlet::refs::PyErrFetchParam;
|
||||
using greenlet::refs::PyArgParseParam;
|
||||
using greenlet::refs::ImmortalString;
|
||||
using greenlet::refs::CreatedModule;
|
||||
using greenlet::refs::PyErrPieces;
|
||||
using greenlet::refs::NewReference;
|
||||
|
||||
namespace greenlet {
|
||||
/**
|
||||
* Thread-local state of greenlets.
|
||||
*
|
||||
* Each native thread will get exactly one of these objects,
|
||||
* automatically accessed through the best available thread-local
|
||||
* mechanism the compiler supports (``thread_local`` for C++11
|
||||
* compilers or ``__thread``/``declspec(thread)`` for older GCC/clang
|
||||
* or MSVC, respectively.)
|
||||
*
|
||||
* Previously, we kept thread-local state mostly in a bunch of
|
||||
* ``static volatile`` variables in the main greenlet file.. This had
|
||||
* the problem of requiring extra checks, loops, and great care
|
||||
* accessing these variables if we potentially invoked any Python code
|
||||
* that could release the GIL, because the state could change out from
|
||||
* under us. Making the variables thread-local solves this problem.
|
||||
*
|
||||
* When we detected that a greenlet API accessing the current greenlet
|
||||
* was invoked from a different thread than the greenlet belonged to,
|
||||
* we stored a reference to the greenlet in the Python thread
|
||||
* dictionary for the thread the greenlet belonged to. This could lead
|
||||
* to memory leaks if the thread then exited (because of a reference
|
||||
* cycle, as greenlets referred to the thread dictionary, and deleting
|
||||
* non-current greenlets leaked their frame plus perhaps arguments on
|
||||
* the C stack). If a thread exited while still having running
|
||||
* greenlet objects (perhaps that had just switched back to the main
|
||||
* greenlet), and did not invoke one of the greenlet APIs *in that
|
||||
* thread, immediately before it exited, without some other thread
|
||||
* then being invoked*, such a leak was guaranteed.
|
||||
*
|
||||
* This can be partly solved by using compiler thread-local variables
|
||||
* instead of the Python thread dictionary, thus avoiding a cycle.
|
||||
*
|
||||
* To fully solve this problem, we need a reliable way to know that a
|
||||
* thread is done and we should clean up the main greenlet. On POSIX,
|
||||
* we can use the destructor function of ``pthread_key_create``, but
|
||||
* there's nothing similar on Windows; a C++11 thread local object
|
||||
* reliably invokes its destructor when the thread it belongs to exits
|
||||
* (non-C++11 compilers offer ``__thread`` or ``declspec(thread)`` to
|
||||
* create thread-local variables, but they can't hold C++ objects that
|
||||
* invoke destructors; the C++11 version is the most portable solution
|
||||
* I found). When the thread exits, we can drop references and
|
||||
* otherwise manipulate greenlets and frames that we know can no
|
||||
* longer be switched to. For compilers that don't support C++11
|
||||
* thread locals, we have a solution that uses the python thread
|
||||
* dictionary, though it may not collect everything as promptly as
|
||||
* other compilers do, if some other library is using the thread
|
||||
* dictionary and has a cycle or extra reference.
|
||||
*
|
||||
* There are two small wrinkles. The first is that when the thread
|
||||
* exits, it is too late to actually invoke Python APIs: the Python
|
||||
* thread state is gone, and the GIL is released. To solve *this*
|
||||
* problem, our destructor uses ``Py_AddPendingCall`` to transfer the
|
||||
* destruction work to the main thread. (This is not an issue for the
|
||||
* dictionary solution.)
|
||||
*
|
||||
* The second is that once the thread exits, the thread local object
|
||||
* is invalid and we can't even access a pointer to it, so we can't
|
||||
* pass it to ``Py_AddPendingCall``. This is handled by actually using
|
||||
* a second object that's thread local (ThreadStateCreator) and having
|
||||
* it dynamically allocate this object so it can live until the
|
||||
* pending call runs.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
class ThreadState {
|
||||
private:
|
||||
// As of commit 08ad1dd7012b101db953f492e0021fb08634afad
|
||||
// this class needed 56 bytes in o Py_DEBUG build
|
||||
// on 64-bit macOS 11.
|
||||
// Adding the vector takes us up to 80 bytes ()
|
||||
|
||||
/* Strong reference to the main greenlet */
|
||||
OwnedMainGreenlet main_greenlet;
|
||||
|
||||
/* Strong reference to the current greenlet. */
|
||||
OwnedGreenlet current_greenlet;
|
||||
|
||||
/* Strong reference to the trace function, if any. */
|
||||
OwnedObject tracefunc;
|
||||
|
||||
typedef std::vector<PyGreenlet*, PythonAllocator<PyGreenlet*> > deleteme_t;
|
||||
/* A vector of raw PyGreenlet pointers representing things that need
|
||||
deleted when this thread is running. The vector owns the
|
||||
references, but you need to manually INCREF/DECREF as you use
|
||||
them. We don't use a vector<refs::OwnedGreenlet> because we
|
||||
make copy of this vector, and that would become O(n) as all the
|
||||
refcounts are incremented in the copy.
|
||||
*/
|
||||
deleteme_t deleteme;
|
||||
|
||||
#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
|
||||
void* exception_state;
|
||||
#endif
|
||||
|
||||
static std::clock_t _clocks_used_doing_gc;
|
||||
static ImmortalString get_referrers_name;
|
||||
static PythonAllocator<ThreadState> allocator;
|
||||
|
||||
G_NO_COPIES_OF_CLS(ThreadState);
|
||||
|
||||
public:
|
||||
static void* operator new(size_t UNUSED(count))
|
||||
{
|
||||
return ThreadState::allocator.allocate(1);
|
||||
}
|
||||
|
||||
static void operator delete(void* ptr)
|
||||
{
|
||||
return ThreadState::allocator.deallocate(static_cast<ThreadState*>(ptr),
|
||||
1);
|
||||
}
|
||||
|
||||
static void init()
|
||||
{
|
||||
ThreadState::get_referrers_name = "get_referrers";
|
||||
ThreadState::_clocks_used_doing_gc = 0;
|
||||
}
|
||||
|
||||
ThreadState()
|
||||
: main_greenlet(OwnedMainGreenlet::consuming(green_create_main(this))),
|
||||
current_greenlet(main_greenlet)
|
||||
{
|
||||
if (!this->main_greenlet) {
|
||||
// We failed to create the main greenlet. That's bad.
|
||||
throw PyFatalError("Failed to create main greenlet");
|
||||
}
|
||||
// The main greenlet starts with 1 refs: The returned one. We
|
||||
// then copied it to the current greenlet.
|
||||
assert(this->main_greenlet.REFCNT() == 2);
|
||||
|
||||
#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
|
||||
this->exception_state = slp_get_exception_state();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void restore_exception_state()
|
||||
{
|
||||
#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
|
||||
// It's probably important this be inlined and only call C
|
||||
// functions to avoid adding an SEH frame.
|
||||
slp_set_exception_state(this->exception_state);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool has_main_greenlet()
|
||||
{
|
||||
return !!this->main_greenlet;
|
||||
}
|
||||
|
||||
// Called from the ThreadStateCreator when we're in non-standard
|
||||
// threading mode. In that case, there is an object in the Python
|
||||
// thread state dictionary that points to us. The main greenlet
|
||||
// also traverses into us, in which case it's crucial not to
|
||||
// traverse back into the main greenlet.
|
||||
int tp_traverse(visitproc visit, void* arg, bool traverse_main=true)
|
||||
{
|
||||
if (traverse_main) {
|
||||
Py_VISIT(main_greenlet.borrow_o());
|
||||
}
|
||||
if (traverse_main || current_greenlet != main_greenlet) {
|
||||
Py_VISIT(current_greenlet.borrow_o());
|
||||
}
|
||||
Py_VISIT(tracefunc.borrow());
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline BorrowedMainGreenlet borrow_main_greenlet() const
|
||||
{
|
||||
assert(this->main_greenlet);
|
||||
assert(this->main_greenlet.REFCNT() >= 2);
|
||||
return this->main_greenlet;
|
||||
};
|
||||
|
||||
inline OwnedMainGreenlet get_main_greenlet()
|
||||
{
|
||||
return this->main_greenlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* In addition to returning a new reference to the currunt
|
||||
* greenlet, this performs any maintenance needed.
|
||||
*/
|
||||
inline OwnedGreenlet get_current()
|
||||
{
|
||||
/* green_dealloc() cannot delete greenlets from other threads, so
|
||||
it stores them in the thread dict; delete them now. */
|
||||
this->clear_deleteme_list();
|
||||
//assert(this->current_greenlet->main_greenlet == this->main_greenlet);
|
||||
//assert(this->main_greenlet->main_greenlet == this->main_greenlet);
|
||||
return this->current_greenlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* As for non-const get_current();
|
||||
*/
|
||||
inline BorrowedGreenlet borrow_current()
|
||||
{
|
||||
this->clear_deleteme_list();
|
||||
return this->current_greenlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does no maintenance.
|
||||
*/
|
||||
inline OwnedGreenlet get_current() const
|
||||
{
|
||||
return this->current_greenlet;
|
||||
}
|
||||
|
||||
template<typename T, refs::TypeChecker TC>
|
||||
inline bool is_current(const refs::PyObjectPointer<T, TC>& obj) const
|
||||
{
|
||||
return this->current_greenlet.borrow_o() == obj.borrow_o();
|
||||
}
|
||||
|
||||
inline void set_current(const OwnedGreenlet& target)
|
||||
{
|
||||
this->current_greenlet = target;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Deref and remove the greenlets from the deleteme list. Must be
|
||||
* holding the GIL.
|
||||
*
|
||||
* If *murder* is true, then we must be called from a different
|
||||
* thread than the one that these greenlets were running in.
|
||||
* In that case, if the greenlet was actually running, we destroy
|
||||
* the frame reference and otherwise make it appear dead before
|
||||
* proceeding; otherwise, we would try (and fail) to raise an
|
||||
* exception in it and wind up right back in this list.
|
||||
*/
|
||||
inline void clear_deleteme_list(const bool murder=false)
|
||||
{
|
||||
if (!this->deleteme.empty()) {
|
||||
// It's possible we could add items to this list while
|
||||
// running Python code if there's a thread switch, so we
|
||||
// need to defensively copy it before that can happen.
|
||||
deleteme_t copy = this->deleteme;
|
||||
this->deleteme.clear(); // in case things come back on the list
|
||||
for(deleteme_t::iterator it = copy.begin(), end = copy.end();
|
||||
it != end;
|
||||
++it ) {
|
||||
PyGreenlet* to_del = *it;
|
||||
if (murder) {
|
||||
// Force each greenlet to appear dead; we can't raise an
|
||||
// exception into it anymore anyway.
|
||||
to_del->pimpl->murder_in_place();
|
||||
}
|
||||
|
||||
// The only reference to these greenlets should be in
|
||||
// this list, decreffing them should let them be
|
||||
// deleted again, triggering calls to green_dealloc()
|
||||
// in the correct thread (if we're not murdering).
|
||||
// This may run arbitrary Python code and switch
|
||||
// threads or greenlets!
|
||||
Py_DECREF(to_del);
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_WriteUnraisable(nullptr);
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Returns a new reference, or a false object.
|
||||
*/
|
||||
inline OwnedObject get_tracefunc() const
|
||||
{
|
||||
return tracefunc;
|
||||
};
|
||||
|
||||
|
||||
inline void set_tracefunc(BorrowedObject tracefunc)
|
||||
{
|
||||
assert(tracefunc);
|
||||
if (tracefunc == BorrowedObject(Py_None)) {
|
||||
this->tracefunc.CLEAR();
|
||||
}
|
||||
else {
|
||||
this->tracefunc = tracefunc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a reference to a greenlet that some other thread
|
||||
* attempted to delete (has a refcount of 0) store it for later
|
||||
* deletion when the thread this state belongs to is current.
|
||||
*/
|
||||
inline void delete_when_thread_running(PyGreenlet* to_del)
|
||||
{
|
||||
Py_INCREF(to_del);
|
||||
this->deleteme.push_back(to_del);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to std::clock_t(-1) to disable.
|
||||
*/
|
||||
inline static std::clock_t& clocks_used_doing_gc()
|
||||
{
|
||||
return ThreadState::_clocks_used_doing_gc;
|
||||
}
|
||||
|
||||
~ThreadState()
|
||||
{
|
||||
if (!PyInterpreterState_Head()) {
|
||||
// We shouldn't get here (our callers protect us)
|
||||
// but if we do, all we can do is bail early.
|
||||
return;
|
||||
}
|
||||
|
||||
// We should not have an "origin" greenlet; that only exists
|
||||
// for the temporary time during a switch, which should not
|
||||
// be in progress as the thread dies.
|
||||
//assert(!this->switching_state.origin);
|
||||
|
||||
this->tracefunc.CLEAR();
|
||||
|
||||
// Forcibly GC as much as we can.
|
||||
this->clear_deleteme_list(true);
|
||||
|
||||
// The pending call did this.
|
||||
assert(this->main_greenlet->thread_state() == nullptr);
|
||||
|
||||
// If the main greenlet is the current greenlet,
|
||||
// then we "fell off the end" and the thread died.
|
||||
// It's possible that there is some other greenlet that
|
||||
// switched to us, leaving a reference to the main greenlet
|
||||
// on the stack, somewhere uncollectible. Try to detect that.
|
||||
if (this->current_greenlet == this->main_greenlet && this->current_greenlet) {
|
||||
assert(this->current_greenlet->is_currently_running_in_some_thread());
|
||||
// Drop one reference we hold.
|
||||
this->current_greenlet.CLEAR();
|
||||
assert(!this->current_greenlet);
|
||||
// Only our reference to the main greenlet should be left,
|
||||
// But hold onto the pointer in case we need to do extra cleanup.
|
||||
PyGreenlet* old_main_greenlet = this->main_greenlet.borrow();
|
||||
Py_ssize_t cnt = this->main_greenlet.REFCNT();
|
||||
this->main_greenlet.CLEAR();
|
||||
if (ThreadState::_clocks_used_doing_gc != std::clock_t(-1)
|
||||
&& cnt == 2 && Py_REFCNT(old_main_greenlet) == 1) {
|
||||
// Highly likely that the reference is somewhere on
|
||||
// the stack, not reachable by GC. Verify.
|
||||
// XXX: This is O(n) in the total number of objects.
|
||||
// TODO: Add a way to disable this at runtime, and
|
||||
// another way to report on it.
|
||||
std::clock_t begin = std::clock();
|
||||
NewReference gc(PyImport_ImportModule("gc"));
|
||||
if (gc) {
|
||||
OwnedObject get_referrers = gc.PyRequireAttr(ThreadState::get_referrers_name);
|
||||
OwnedList refs(get_referrers.PyCall(old_main_greenlet));
|
||||
if (refs && refs.empty()) {
|
||||
assert(refs.REFCNT() == 1);
|
||||
// We found nothing! So we left a dangling
|
||||
// reference: Probably the last thing some
|
||||
// other greenlet did was call
|
||||
// 'getcurrent().parent.switch()' to switch
|
||||
// back to us. Clean it up. This will be the
|
||||
// case on CPython 3.7 and newer, as they use
|
||||
// an internal calling conversion that avoids
|
||||
// creating method objects and storing them on
|
||||
// the stack.
|
||||
Py_DECREF(old_main_greenlet);
|
||||
}
|
||||
else if (refs
|
||||
&& refs.size() == 1
|
||||
&& PyCFunction_Check(refs.at(0))
|
||||
&& Py_REFCNT(refs.at(0)) == 2) {
|
||||
assert(refs.REFCNT() == 1);
|
||||
// Ok, we found a C method that refers to the
|
||||
// main greenlet, and its only referenced
|
||||
// twice, once in the list we just created,
|
||||
// once from...somewhere else. If we can't
|
||||
// find where else, then this is a leak.
|
||||
// This happens in older versions of CPython
|
||||
// that create a bound method object somewhere
|
||||
// on the stack that we'll never get back to.
|
||||
if (PyCFunction_GetFunction(refs.at(0).borrow()) == (PyCFunction)green_switch) {
|
||||
BorrowedObject function_w = refs.at(0);
|
||||
refs.clear(); // destroy the reference
|
||||
// from the list.
|
||||
// back to one reference. Can *it* be
|
||||
// found?
|
||||
assert(function_w.REFCNT() == 1);
|
||||
refs = get_referrers.PyCall(function_w);
|
||||
if (refs && refs.empty()) {
|
||||
// Nope, it can't be found so it won't
|
||||
// ever be GC'd. Drop it.
|
||||
Py_CLEAR(function_w);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::clock_t end = std::clock();
|
||||
ThreadState::_clocks_used_doing_gc += (end - begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We need to make sure this greenlet appears to be dead,
|
||||
// because otherwise deallocing it would fail to raise an
|
||||
// exception in it (the thread is dead) and put it back in our
|
||||
// deleteme list.
|
||||
if (this->current_greenlet) {
|
||||
this->current_greenlet->murder_in_place();
|
||||
this->current_greenlet.CLEAR();
|
||||
}
|
||||
|
||||
if (this->main_greenlet) {
|
||||
// Couldn't have been the main greenlet that was running
|
||||
// when the thread exited (because we already cleared this
|
||||
// pointer if it was). This shouldn't be possible?
|
||||
|
||||
// If the main greenlet was current when the thread died (it
|
||||
// should be, right?) then we cleared its self pointer above
|
||||
// when we cleared the current greenlet's main greenlet pointer.
|
||||
// assert(this->main_greenlet->main_greenlet == this->main_greenlet
|
||||
// || !this->main_greenlet->main_greenlet);
|
||||
// // self reference, probably gone
|
||||
// this->main_greenlet->main_greenlet.CLEAR();
|
||||
|
||||
// This will actually go away when the ivar is destructed.
|
||||
this->main_greenlet.CLEAR();
|
||||
}
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_WriteUnraisable(NULL);
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ImmortalString ThreadState::get_referrers_name(nullptr);
|
||||
PythonAllocator<ThreadState> ThreadState::allocator;
|
||||
std::clock_t ThreadState::_clocks_used_doing_gc(0);
|
||||
|
||||
template<typename Destructor>
|
||||
class ThreadStateCreator
|
||||
{
|
||||
private:
|
||||
// Initialized to 1, and, if still 1, created on access.
|
||||
// Set to 0 on destruction.
|
||||
ThreadState* _state;
|
||||
G_NO_COPIES_OF_CLS(ThreadStateCreator);
|
||||
public:
|
||||
|
||||
// Only one of these, auto created per thread
|
||||
ThreadStateCreator() :
|
||||
_state((ThreadState*)1)
|
||||
{
|
||||
}
|
||||
|
||||
~ThreadStateCreator()
|
||||
{
|
||||
ThreadState* tmp = this->_state;
|
||||
this->_state = nullptr;
|
||||
if (tmp && tmp != (ThreadState*)1) {
|
||||
Destructor x(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
inline ThreadState& state()
|
||||
{
|
||||
// The main greenlet will own this pointer when it is created,
|
||||
// which will be right after this. The plan is to give every
|
||||
// greenlet a pointer to the main greenlet for the thread it
|
||||
// runs in; if we are doing something cross-thread, we need to
|
||||
// access the pointer from the main greenlet. Deleting the
|
||||
// thread, and hence the thread-local storage, will delete the
|
||||
// state pointer in the main greenlet.
|
||||
if (this->_state == (ThreadState*)1) {
|
||||
// XXX: Assuming allocation never fails
|
||||
this->_state = new ThreadState;
|
||||
// For non-standard threading, we need to store an object
|
||||
// in the Python thread state dictionary so that it can be
|
||||
// DECREF'd when the thread ends (ideally; the dict could
|
||||
// last longer) and clean this object up.
|
||||
}
|
||||
if (!this->_state) {
|
||||
throw std::runtime_error("Accessing state after destruction.");
|
||||
}
|
||||
return *this->_state;
|
||||
}
|
||||
|
||||
operator ThreadState&()
|
||||
{
|
||||
return this->state();
|
||||
}
|
||||
|
||||
operator ThreadState*()
|
||||
{
|
||||
return &this->state();
|
||||
}
|
||||
|
||||
inline int tp_traverse(visitproc visit, void* arg)
|
||||
{
|
||||
if (this->_state) {
|
||||
return this->_state->tp_traverse(visit, arg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// We can't use the PythonAllocator for this, because we push to it
|
||||
// from the thread state destructor, which doesn't have the GIL,
|
||||
// and Python's allocators can only be called with the GIL.
|
||||
typedef std::vector<ThreadState*> cleanup_queue_t;
|
||||
|
||||
}; // namespace greenlet
|
||||
|
||||
#endif
|
|
@ -1,118 +0,0 @@
|
|||
#ifndef GREENLET_THREAD_STATE_DICT_CLEANUP_HPP
|
||||
#define GREENLET_THREAD_STATE_DICT_CLEANUP_HPP
|
||||
|
||||
#include "greenlet_internal.hpp"
|
||||
#include "greenlet_thread_state.hpp"
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
|
||||
#ifndef G_THREAD_STATE_DICT_CLEANUP_TYPE
|
||||
// shut the compiler up if it looks at this file in isolation
|
||||
#define ThreadStateCreator int
|
||||
#endif
|
||||
|
||||
// Define a Python object that goes in the Python thread state dict
|
||||
// when the greenlet thread state is created, and which owns the
|
||||
// reference to the greenlet thread local state.
|
||||
// When the thread state dict is cleaned up, so too is the thread
|
||||
// state. This works best if we make sure there are no circular
|
||||
// references to the thread state.
|
||||
typedef struct _PyGreenletCleanup {
|
||||
PyObject_HEAD
|
||||
ThreadStateCreator* thread_state_creator;
|
||||
} PyGreenletCleanup;
|
||||
|
||||
static void
|
||||
cleanup_do_dealloc(PyGreenletCleanup* self)
|
||||
{
|
||||
ThreadStateCreator* tmp = self->thread_state_creator;
|
||||
self->thread_state_creator = nullptr;
|
||||
if (tmp) {
|
||||
delete tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_dealloc(PyGreenletCleanup* self)
|
||||
{
|
||||
PyObject_GC_UnTrack(self);
|
||||
cleanup_do_dealloc(self);
|
||||
}
|
||||
|
||||
static int
|
||||
cleanup_clear(PyGreenletCleanup* self)
|
||||
{
|
||||
// This method is never called by our test cases.
|
||||
cleanup_do_dealloc(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cleanup_traverse(PyGreenletCleanup* self, visitproc visit, void* arg)
|
||||
{
|
||||
if (self->thread_state_creator) {
|
||||
return self->thread_state_creator->tp_traverse(visit, arg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
cleanup_is_gc(PyGreenlet* UNUSED(self))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PyTypeObject PyGreenletCleanup_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"greenlet._greenlet.ThreadStateCleanup",
|
||||
sizeof(struct _PyGreenletCleanup),
|
||||
0, /* tp_itemsize */
|
||||
/* methods */
|
||||
(destructor)cleanup_dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as _number*/
|
||||
0, /* tp_as _sequence*/
|
||||
0, /* tp_as _mapping*/
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer*/
|
||||
G_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
"Internal use only", /* tp_doc */
|
||||
(traverseproc)cleanup_traverse, /* tp_traverse */
|
||||
(inquiry)cleanup_clear, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
// XXX: Don't our flags promise a weakref?
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
PyType_GenericAlloc, /* tp_alloc */
|
||||
PyType_GenericNew, /* tp_new */
|
||||
PyObject_GC_Del, /* tp_free */
|
||||
(inquiry)cleanup_is_gc, /* tp_is_gc */
|
||||
};
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -1,31 +0,0 @@
|
|||
#ifndef GREENLET_THREAD_SUPPORT_HPP
|
||||
#define GREENLET_THREAD_SUPPORT_HPP
|
||||
|
||||
/**
|
||||
* Defines various utility functions to help greenlet integrate well
|
||||
* with threads. This used to be needed when we supported Python
|
||||
* 2.7 on Windows, which used a very old compiler. We wrote an
|
||||
* alternative implementation using Python APIs and POSIX or Windows
|
||||
* APIs, but that's no longer needed. So this file is a shadow of its
|
||||
* former self --- but may be needed in the future.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include "greenlet_compiler_compat.hpp"
|
||||
|
||||
namespace greenlet {
|
||||
typedef std::mutex Mutex;
|
||||
typedef std::lock_guard<Mutex> LockGuard;
|
||||
class LockInitError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
LockInitError(const char* what) : std::runtime_error(what)
|
||||
{};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
#endif /* GREENLET_THREAD_SUPPORT_HPP */
|
|
@ -1,2 +0,0 @@
|
|||
call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" amd64
|
||||
ml64 /nologo /c /Fo switch_x64_masm.obj switch_x64_masm.asm
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* this is the internal transfer function.
|
||||
*
|
||||
* HISTORY
|
||||
* 07-Sep-16 Add clang support using x register naming. Fredrik Fornwall
|
||||
* 13-Apr-13 Add support for strange GCC caller-save decisions
|
||||
* 08-Apr-13 File creation. Michael Matz
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
* Simply save all callee saved registers
|
||||
*
|
||||
*/
|
||||
|
||||
#define STACK_REFPLUS 1
|
||||
|
||||
#ifdef SLP_EVAL
|
||||
#define STACK_MAGIC 0
|
||||
#define REGS_TO_SAVE "x19", "x20", "x21", "x22", "x23", "x24", "x25", "x26", \
|
||||
"x27", "x28", "x30" /* aka lr */, \
|
||||
"v8", "v9", "v10", "v11", \
|
||||
"v12", "v13", "v14", "v15"
|
||||
|
||||
/*
|
||||
* Recall:
|
||||
asm asm-qualifiers ( AssemblerTemplate
|
||||
: OutputOperands
|
||||
[ : InputOperands
|
||||
[ : Clobbers ] ])
|
||||
|
||||
or (if asm-qualifiers contains 'goto')
|
||||
|
||||
asm asm-qualifiers ( AssemblerTemplate
|
||||
: OutputOperands
|
||||
: InputOperands
|
||||
: Clobbers
|
||||
: GotoLabels)
|
||||
|
||||
and OutputOperands are
|
||||
|
||||
[ [asmSymbolicName] ] constraint (cvariablename)
|
||||
|
||||
When a name is given, refer to it as ``%[the name]``.
|
||||
When not given, ``%i`` where ``i`` is the zero-based index.
|
||||
|
||||
constraints starting with ``=`` means only writing; ``+`` means
|
||||
reading and writing.
|
||||
|
||||
This is followed by ``r`` (must be register) or ``m`` (must be memory)
|
||||
and these can be combined.
|
||||
|
||||
The ``cvariablename`` is actually an lvalue expression.
|
||||
|
||||
In AArch65, 31 general purpose registers. If named X0... they are
|
||||
64-bit. If named W0... they are the bottom 32 bits of the
|
||||
corresponding 64 bit register.
|
||||
|
||||
XZR and WZR are hardcoded to 0, and ignore writes.
|
||||
|
||||
Arguments are in X0..X7. C++ uses X0 for ``this``. X0 holds simple return
|
||||
values (?)
|
||||
|
||||
Whenever a W register is written, the top half of the X register is zeroed.
|
||||
*/
|
||||
|
||||
static int
|
||||
slp_switch(void)
|
||||
{
|
||||
int err;
|
||||
void *fp;
|
||||
/* Windowz uses a 32-bit long on a 64-bit platform, unlike the rest of
|
||||
the world, and in theory we can be compiled with GCC/llvm on 64-bit
|
||||
windows. So we need a fixed-width type.
|
||||
*/
|
||||
int64_t *stackref, stsizediff;
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
__asm__ volatile ("str x29, %0" : "=m"(fp) : : );
|
||||
__asm__ ("mov %0, sp" : "=r" (stackref));
|
||||
{
|
||||
SLP_SAVE_STATE(stackref, stsizediff);
|
||||
__asm__ volatile (
|
||||
"add sp,sp,%0\n"
|
||||
"add x29,x29,%0\n"
|
||||
:
|
||||
: "r" (stsizediff)
|
||||
);
|
||||
SLP_RESTORE_STATE();
|
||||
/* SLP_SAVE_STATE macro contains some return statements
|
||||
(of -1 and 1). It falls through only when
|
||||
the return value of slp_save_state() is zero, which
|
||||
is placed in x0.
|
||||
In that case we (slp_switch) also want to return zero
|
||||
(also in x0 of course).
|
||||
Now, some GCC versions (seen with 4.8) think it's a
|
||||
good idea to save/restore x0 around the call to
|
||||
slp_restore_state(), instead of simply zeroing it
|
||||
at the return below. But slp_restore_state
|
||||
writes random values to the stack slot used for this
|
||||
save/restore (from when it once was saved above in
|
||||
SLP_SAVE_STATE, when it was still uninitialized), so
|
||||
"restoring" that precious zero actually makes us
|
||||
return random values. There are some ways to make
|
||||
GCC not use that zero value in the normal return path
|
||||
(e.g. making err volatile, but that costs a little
|
||||
stack space), and the simplest is to call a function
|
||||
that returns an unknown value (which happens to be zero),
|
||||
so the saved/restored value is unused.
|
||||
|
||||
Thus, this line stores a 0 into the ``err`` variable
|
||||
(which must be held in a register for this instruction,
|
||||
of course). The ``w`` qualifier causes the instruction
|
||||
to use W0 instead of X0, otherwise we get a warning
|
||||
about a value size mismatch (because err is an int,
|
||||
and aarch64 platforms are LP64: 32-bit int, 64 bit long
|
||||
and pointer).
|
||||
*/
|
||||
__asm__ volatile ("mov %w0, #0" : "=r" (err));
|
||||
}
|
||||
__asm__ volatile ("ldr x29, %0" : : "m" (fp) :);
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,30 +0,0 @@
|
|||
#define STACK_REFPLUS 1
|
||||
|
||||
#ifdef SLP_EVAL
|
||||
#define STACK_MAGIC 0
|
||||
|
||||
#define REGS_TO_SAVE "$9", "$10", "$11", "$12", "$13", "$14", "$15", \
|
||||
"$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9"
|
||||
|
||||
static int
|
||||
slp_switch(void)
|
||||
{
|
||||
int ret;
|
||||
long *stackref, stsizediff;
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
__asm__ volatile ("mov $30, %0" : "=r" (stackref) : );
|
||||
{
|
||||
SLP_SAVE_STATE(stackref, stsizediff);
|
||||
__asm__ volatile (
|
||||
"addq $30, %0, $30\n\t"
|
||||
: /* no outputs */
|
||||
: "r" (stsizediff)
|
||||
);
|
||||
SLP_RESTORE_STATE();
|
||||
}
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
__asm__ volatile ("mov $31, %0" : "=r" (ret) : );
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* this is the internal transfer function.
|
||||
*
|
||||
* HISTORY
|
||||
* 3-May-13 Ralf Schmitt <ralf@systemexit.de>
|
||||
* Add support for strange GCC caller-save decisions
|
||||
* (ported from switch_aarch64_gcc.h)
|
||||
* 18-Aug-11 Alexey Borzenkov <snaury@gmail.com>
|
||||
* Correctly save rbp, csr and cw
|
||||
* 01-Apr-04 Hye-Shik Chang <perky@FreeBSD.org>
|
||||
* Ported from i386 to amd64.
|
||||
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
|
||||
* needed to add another magic constant to insure
|
||||
* that f in slp_eval_frame(PyFrameObject *f)
|
||||
* STACK_REFPLUS will probably be 1 in most cases.
|
||||
* gets included into the saved stack area.
|
||||
* 17-Sep-02 Christian Tismer <tismer@tismer.com>
|
||||
* after virtualizing stack save/restore, the
|
||||
* stack size shrunk a bit. Needed to introduce
|
||||
* an adjustment STACK_MAGIC per platform.
|
||||
* 15-Sep-02 Gerd Woetzel <gerd.woetzel@GMD.DE>
|
||||
* slightly changed framework for spark
|
||||
* 31-Avr-02 Armin Rigo <arigo@ulb.ac.be>
|
||||
* Added ebx, esi and edi register-saves.
|
||||
* 01-Mar-02 Samual M. Rushing <rushing@ironport.com>
|
||||
* Ported from i386.
|
||||
*/
|
||||
|
||||
#define STACK_REFPLUS 1
|
||||
|
||||
#ifdef SLP_EVAL
|
||||
|
||||
/* #define STACK_MAGIC 3 */
|
||||
/* the above works fine with gcc 2.96, but 2.95.3 wants this */
|
||||
#define STACK_MAGIC 0
|
||||
|
||||
#define REGS_TO_SAVE "r12", "r13", "r14", "r15"
|
||||
|
||||
static int
|
||||
slp_switch(void)
|
||||
{
|
||||
int err;
|
||||
void* rbp;
|
||||
void* rbx;
|
||||
unsigned int csr;
|
||||
unsigned short cw;
|
||||
/* This used to be declared 'register', but that does nothing in
|
||||
modern compilers and is explicitly forbidden in some new
|
||||
standards. */
|
||||
long *stackref, stsizediff;
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
__asm__ volatile ("fstcw %0" : "=m" (cw));
|
||||
__asm__ volatile ("stmxcsr %0" : "=m" (csr));
|
||||
__asm__ volatile ("movq %%rbp, %0" : "=m" (rbp));
|
||||
__asm__ volatile ("movq %%rbx, %0" : "=m" (rbx));
|
||||
__asm__ ("movq %%rsp, %0" : "=g" (stackref));
|
||||
{
|
||||
SLP_SAVE_STATE(stackref, stsizediff);
|
||||
__asm__ volatile (
|
||||
"addq %0, %%rsp\n"
|
||||
"addq %0, %%rbp\n"
|
||||
:
|
||||
: "r" (stsizediff)
|
||||
);
|
||||
SLP_RESTORE_STATE();
|
||||
__asm__ volatile ("xorq %%rax, %%rax" : "=a" (err));
|
||||
}
|
||||
__asm__ volatile ("movq %0, %%rbx" : : "m" (rbx));
|
||||
__asm__ volatile ("movq %0, %%rbp" : : "m" (rbp));
|
||||
__asm__ volatile ("ldmxcsr %0" : : "m" (csr));
|
||||
__asm__ volatile ("fldcw %0" : : "m" (cw));
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* further self-processing support
|
||||
*/
|
||||
|
||||
/*
|
||||
* if you want to add self-inspection tools, place them
|
||||
* here. See the x86_msvc for the necessary defines.
|
||||
* These features are highly experimental und not
|
||||
* essential yet.
|
||||
*/
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* this is the internal transfer function.
|
||||
*
|
||||
* HISTORY
|
||||
* 14-Aug-06 File creation. Ported from Arm Thumb. Sylvain Baro
|
||||
* 3-Sep-06 Commented out saving of r1-r3 (r4 already commented out) as I
|
||||
* read that these do not need to be saved. Also added notes and
|
||||
* errors related to the frame pointer. Richard Tew.
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
* It is not possible to detect if fp is used or not, so the supplied
|
||||
* switch function needs to support it, so that you can remove it if
|
||||
* it does not apply to you.
|
||||
*
|
||||
* POSSIBLE ERRORS
|
||||
*
|
||||
* "fp cannot be used in asm here"
|
||||
*
|
||||
* - Try commenting out "fp" in REGS_TO_SAVE.
|
||||
*
|
||||
*/
|
||||
|
||||
#define STACK_REFPLUS 1
|
||||
|
||||
#ifdef SLP_EVAL
|
||||
#define STACK_MAGIC 0
|
||||
#define REG_SP "sp"
|
||||
#define REG_SPSP "sp,sp"
|
||||
#ifdef __thumb__
|
||||
#define REG_FP "r7"
|
||||
#define REG_FPFP "r7,r7"
|
||||
#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r9", "r10", "r11", "lr"
|
||||
#else
|
||||
#define REG_FP "fp"
|
||||
#define REG_FPFP "fp,fp"
|
||||
#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r7", "r8", "r9", "r10", "lr"
|
||||
#endif
|
||||
#if defined(__SOFTFP__)
|
||||
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL
|
||||
#elif defined(__VFP_FP__)
|
||||
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \
|
||||
"d12", "d13", "d14", "d15"
|
||||
#elif defined(__MAVERICK__)
|
||||
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "mvf4", "mvf5", "mvf6", "mvf7", \
|
||||
"mvf8", "mvf9", "mvf10", "mvf11", \
|
||||
"mvf12", "mvf13", "mvf14", "mvf15"
|
||||
#else
|
||||
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "f4", "f5", "f6", "f7"
|
||||
#endif
|
||||
|
||||
static int
|
||||
#ifdef __GNUC__
|
||||
__attribute__((optimize("no-omit-frame-pointer")))
|
||||
#endif
|
||||
slp_switch(void)
|
||||
{
|
||||
void *fp;
|
||||
int *stackref, stsizediff;
|
||||
int result;
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
__asm__ volatile ("mov r0," REG_FP "\n\tstr r0,%0" : "=m" (fp) : : "r0");
|
||||
__asm__ ("mov %0," REG_SP : "=r" (stackref));
|
||||
{
|
||||
SLP_SAVE_STATE(stackref, stsizediff);
|
||||
__asm__ volatile (
|
||||
"add " REG_SPSP ",%0\n"
|
||||
"add " REG_FPFP ",%0\n"
|
||||
:
|
||||
: "r" (stsizediff)
|
||||
);
|
||||
SLP_RESTORE_STATE();
|
||||
}
|
||||
__asm__ volatile ("ldr r0,%1\n\tmov " REG_FP ",r0\n\tmov %0, #0" : "=r" (result) : "m" (fp) : "r0");
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
* this is the internal transfer function.
|
||||
*
|
||||
* HISTORY
|
||||
* 31-May-15 iOS support. Ported from arm32. Proton <feisuzhu@163.com>
|
||||
*
|
||||
* NOTES
|
||||
*
|
||||
* It is not possible to detect if fp is used or not, so the supplied
|
||||
* switch function needs to support it, so that you can remove it if
|
||||
* it does not apply to you.
|
||||
*
|
||||
* POSSIBLE ERRORS
|
||||
*
|
||||
* "fp cannot be used in asm here"
|
||||
*
|
||||
* - Try commenting out "fp" in REGS_TO_SAVE.
|
||||
*
|
||||
*/
|
||||
|
||||
#define STACK_REFPLUS 1
|
||||
|
||||
#ifdef SLP_EVAL
|
||||
|
||||
#define STACK_MAGIC 0
|
||||
#define REG_SP "sp"
|
||||
#define REG_SPSP "sp,sp"
|
||||
#define REG_FP "r7"
|
||||
#define REG_FPFP "r7,r7"
|
||||
#define REGS_TO_SAVE_GENERAL "r4", "r5", "r6", "r8", "r10", "r11", "lr"
|
||||
#define REGS_TO_SAVE REGS_TO_SAVE_GENERAL, "d8", "d9", "d10", "d11", \
|
||||
"d12", "d13", "d14", "d15"
|
||||
|
||||
static int
|
||||
#ifdef __GNUC__
|
||||
__attribute__((optimize("no-omit-frame-pointer")))
|
||||
#endif
|
||||
slp_switch(void)
|
||||
{
|
||||
void *fp;
|
||||
int *stackref, stsizediff, result;
|
||||
__asm__ volatile ("" : : : REGS_TO_SAVE);
|
||||
__asm__ volatile ("str " REG_FP ",%0" : "=m" (fp));
|
||||
__asm__ ("mov %0," REG_SP : "=r" (stackref));
|
||||
{
|
||||
SLP_SAVE_STATE(stackref, stsizediff);
|
||||
__asm__ volatile (
|
||||
"add " REG_SPSP ",%0\n"
|
||||
"add " REG_FPFP ",%0\n"
|
||||
:
|
||||
: "r" (stsizediff)
|
||||
: REGS_TO_SAVE /* Clobber registers, force compiler to
|
||||
* recalculate address of void *fp from REG_SP or REG_FP */
|
||||
);
|
||||
SLP_RESTORE_STATE();
|
||||
}
|
||||
__asm__ volatile (
|
||||
"ldr " REG_FP ", %1\n\t"
|
||||
"mov %0, #0"
|
||||
: "=r" (result)
|
||||
: "m" (fp)
|
||||
: REGS_TO_SAVE /* Force compiler to restore saved registers after this */
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,53 +0,0 @@
|
|||
AREA switch_arm64_masm, CODE, READONLY;
|
||||
GLOBAL slp_switch [FUNC]
|
||||
EXTERN slp_save_state_asm
|
||||
EXTERN slp_restore_state_asm
|
||||
|
||||
slp_switch
|
||||
; push callee saved registers to stack
|
||||
stp x19, x20, [sp, #-16]!
|
||||
stp x21, x22, [sp, #-16]!
|
||||
stp x23, x24, [sp, #-16]!
|
||||
stp x25, x26, [sp, #-16]!
|
||||
stp x27, x28, [sp, #-16]!
|
||||
stp x29, x30, [sp, #-16]!
|
||||
stp d8, d9, [sp, #-16]!
|
||||
stp d10, d11, [sp, #-16]!
|
||||
stp d12, d13, [sp, #-16]!
|
||||
stp d14, d15, [sp, #-16]!
|
||||
|
||||
; call slp_save_state_asm with stack pointer
|
||||
mov x0, sp
|
||||
bl slp_save_state_asm
|
||||
|
||||
; early return for return value of 1 and -1
|
||||
cmp x0, #-1
|
||||
b.eq RETURN
|
||||
cmp x0, #1
|
||||
b.eq RETURN
|
||||
|
||||
; increment stack and frame pointer
|
||||
add sp, sp, x0
|
||||
add x29, x29, x0
|
||||
|
||||
bl slp_restore_state_asm
|
||||
|
||||
; store return value for successful completion of routine
|
||||
mov x0, #0
|
||||
|
||||
RETURN
|
||||
; pop registers from stack
|
||||
ldp d14, d15, [sp], #16
|
||||
ldp d12, d13, [sp], #16
|
||||
ldp d10, d11, [sp], #16
|
||||
ldp d8, d9, [sp], #16
|
||||
ldp x29, x30, [sp], #16
|
||||
ldp x27, x28, [sp], #16
|
||||
ldp x25, x26, [sp], #16
|
||||
ldp x23, x24, [sp], #16
|
||||
ldp x21, x22, [sp], #16
|
||||
ldp x19, x20, [sp], #16
|
||||
|
||||
ret
|
||||
|
||||
END
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue