generated from mwc/lab_encoding
k
This commit is contained in:
247
venv/bin/Activate.ps1
Normal file
247
venv/bin/Activate.ps1
Normal file
@@ -0,0 +1,247 @@
|
||||
<#
|
||||
.Synopsis
|
||||
Activate a Python virtual environment for the current PowerShell session.
|
||||
|
||||
.Description
|
||||
Pushes the python executable for a virtual environment to the front of the
|
||||
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||
in a Python virtual environment. Makes use of the command line switches as
|
||||
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||
|
||||
.Parameter VenvDir
|
||||
Path to the directory that contains the virtual environment to activate. The
|
||||
default value for this is the parent of the directory that the Activate.ps1
|
||||
script is located within.
|
||||
|
||||
.Parameter Prompt
|
||||
The prompt prefix to display when this virtual environment is activated. By
|
||||
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||
|
||||
.Example
|
||||
Activate.ps1
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Verbose
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and shows extra information about the activation as it executes.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||
Activates the Python virtual environment located in the specified location.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Prompt "MyPython"
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and prefixes the current prompt with the specified string (surrounded in
|
||||
parentheses) while the virtual environment is active.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
Param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$VenvDir,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$Prompt
|
||||
)
|
||||
|
||||
<# Function declarations --------------------------------------------------- #>
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Remove all shell session elements added by the Activate script, including the
|
||||
addition of the virtual environment's Python executable from the beginning of
|
||||
the PATH variable.
|
||||
|
||||
.Parameter NonDestructive
|
||||
If present, do not remove this function from the global namespace for the
|
||||
session.
|
||||
|
||||
#>
|
||||
function global:deactivate ([switch]$NonDestructive) {
|
||||
# Revert to original values
|
||||
|
||||
# The prior prompt:
|
||||
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# The prior PYTHONHOME:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
# The prior PATH:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Just remove the VIRTUAL_ENV altogether:
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV
|
||||
}
|
||||
|
||||
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||
}
|
||||
|
||||
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||
}
|
||||
|
||||
# Leave deactivate function in the global namespace if requested:
|
||||
if (-not $NonDestructive) {
|
||||
Remove-Item -Path function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Description
|
||||
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||
given folder, and returns them in a map.
|
||||
|
||||
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||
then it is considered a `key = value` line. The left hand string is the key,
|
||||
the right hand is the value.
|
||||
|
||||
If the value starts with a `'` or a `"` then the first and last character is
|
||||
stripped from the value before being captured.
|
||||
|
||||
.Parameter ConfigDir
|
||||
Path to the directory that contains the `pyvenv.cfg` file.
|
||||
#>
|
||||
function Get-PyVenvConfig(
|
||||
[String]
|
||||
$ConfigDir
|
||||
) {
|
||||
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||
|
||||
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||
|
||||
# An empty map will be returned if no config file is found.
|
||||
$pyvenvConfig = @{ }
|
||||
|
||||
if ($pyvenvConfigPath) {
|
||||
|
||||
Write-Verbose "File exists, parse `key = value` lines"
|
||||
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||
|
||||
$pyvenvConfigContent | ForEach-Object {
|
||||
$keyval = $PSItem -split "\s*=\s*", 2
|
||||
if ($keyval[0] -and $keyval[1]) {
|
||||
$val = $keyval[1]
|
||||
|
||||
# Remove extraneous quotations around a string value.
|
||||
if ("'""".Contains($val.Substring(0, 1))) {
|
||||
$val = $val.Substring(1, $val.Length - 2)
|
||||
}
|
||||
|
||||
$pyvenvConfig[$keyval[0]] = $val
|
||||
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pyvenvConfig
|
||||
}
|
||||
|
||||
|
||||
<# Begin Activate script --------------------------------------------------- #>
|
||||
|
||||
# Determine the containing directory of this script
|
||||
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||
|
||||
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||
|
||||
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||
# First, get the location of the virtual environment, it might not be
|
||||
# VenvExecDir if specified on the command line.
|
||||
if ($VenvDir) {
|
||||
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||
Write-Verbose "VenvDir=$VenvDir"
|
||||
}
|
||||
|
||||
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||
# as `prompt`.
|
||||
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||
|
||||
# Next, set the prompt from the command line, or the config file, or
|
||||
# just use the name of the virtual environment folder.
|
||||
if ($Prompt) {
|
||||
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||
$Prompt = $pyvenvCfg['prompt'];
|
||||
}
|
||||
else {
|
||||
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Prompt = '$Prompt'"
|
||||
Write-Verbose "VenvDir='$VenvDir'"
|
||||
|
||||
# Deactivate any currently active virtual environment, but leave the
|
||||
# deactivate function in place.
|
||||
deactivate -nondestructive
|
||||
|
||||
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
|
||||
# Set the prompt to include the env name
|
||||
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||
|
||||
function global:prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path -Path Env:PYTHONHOME) {
|
||||
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
Remove-Item -Path Env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
||||
70
venv/bin/activate
Normal file
70
venv/bin/activate
Normal file
@@ -0,0 +1,70 @@
|
||||
# This file must be used with "source bin/activate" *from bash*
|
||||
# You cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# Call hash to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
hash -r 2> /dev/null
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
unset VIRTUAL_ENV_PROMPT
|
||||
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
# on Windows, a path can contain colons and backslashes and has to be converted:
|
||||
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
|
||||
# transform D:\path\to\venv to /d/path/to/venv on MSYS
|
||||
# and to /cygdrive/d/path/to/venv on Cygwin
|
||||
export VIRTUAL_ENV=$(cygpath /home/mattd/mattd/mwc2/mwc2/unit2/lab_encoding/venv)
|
||||
else
|
||||
# use the path as-is
|
||||
export VIRTUAL_ENV=/home/mattd/mattd/mwc2/mwc2/unit2/lab_encoding/venv
|
||||
fi
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/"bin":$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1='(venv) '"${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT='(venv) '
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# Call hash to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
hash -r 2> /dev/null
|
||||
27
venv/bin/activate.csh
Normal file
27
venv/bin/activate.csh
Normal file
@@ -0,0 +1,27 @@
|
||||
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||
# You cannot run it directly.
|
||||
|
||||
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||
|
||||
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV /home/mattd/mattd/mwc2/mwc2/unit2/lab_encoding/venv
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH"
|
||||
setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
|
||||
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||
|
||||
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||
set prompt = '(venv) '"$prompt"
|
||||
setenv VIRTUAL_ENV_PROMPT '(venv) '
|
||||
endif
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
||||
69
venv/bin/activate.fish
Normal file
69
venv/bin/activate.fish
Normal file
@@ -0,0 +1,69 @@
|
||||
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
||||
# (https://fishshell.com/). You cannot run it directly.
|
||||
|
||||
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
||||
# reset old environment variables
|
||||
if test -n "$_OLD_VIRTUAL_PATH"
|
||||
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||
set -e _OLD_VIRTUAL_PATH
|
||||
end
|
||||
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
||||
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||
end
|
||||
|
||||
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||
# prevents error when using nested fish instances (Issue #93858)
|
||||
if functions -q _old_fish_prompt
|
||||
functions -e fish_prompt
|
||||
functions -c _old_fish_prompt fish_prompt
|
||||
functions -e _old_fish_prompt
|
||||
end
|
||||
end
|
||||
|
||||
set -e VIRTUAL_ENV
|
||||
set -e VIRTUAL_ENV_PROMPT
|
||||
if test "$argv[1]" != "nondestructive"
|
||||
# Self-destruct!
|
||||
functions -e deactivate
|
||||
end
|
||||
end
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
set -gx VIRTUAL_ENV /home/mattd/mattd/mwc2/mwc2/unit2/lab_encoding/venv
|
||||
|
||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||
set -gx PATH "$VIRTUAL_ENV/"bin $PATH
|
||||
|
||||
# Unset PYTHONHOME if set.
|
||||
if set -q PYTHONHOME
|
||||
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||
set -e PYTHONHOME
|
||||
end
|
||||
|
||||
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
# fish uses a function instead of an env var to generate the prompt.
|
||||
|
||||
# Save the current fish_prompt function as the function _old_fish_prompt.
|
||||
functions -c fish_prompt _old_fish_prompt
|
||||
|
||||
# With the original prompt function renamed, we can override with our own.
|
||||
function fish_prompt
|
||||
# Save the return status of the last command.
|
||||
set -l old_status $status
|
||||
|
||||
# Output the venv prompt; color taken from the blue of the Python logo.
|
||||
printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (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 '(venv) '
|
||||
end
|
||||
8
venv/bin/pip
Executable file
8
venv/bin/pip
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/mattd/mattd/mwc2/mwc2/unit2/lab_encoding/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
8
venv/bin/pip3
Executable file
8
venv/bin/pip3
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/mattd/mattd/mwc2/mwc2/unit2/lab_encoding/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
8
venv/bin/pip3.12
Executable file
8
venv/bin/pip3.12
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/home/mattd/mattd/mwc2/mwc2/unit2/lab_encoding/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
1
venv/bin/python
Symbolic link
1
venv/bin/python
Symbolic link
@@ -0,0 +1 @@
|
||||
python3
|
||||
1
venv/bin/python3
Symbolic link
1
venv/bin/python3
Symbolic link
@@ -0,0 +1 @@
|
||||
/usr/bin/python3
|
||||
1
venv/bin/python3.12
Symbolic link
1
venv/bin/python3.12
Symbolic link
@@ -0,0 +1 @@
|
||||
python3
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,992 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: bitarray
|
||||
Version: 3.8.0
|
||||
Summary: efficient arrays of booleans -- C extension
|
||||
Home-page: https://github.com/ilanschnell/bitarray
|
||||
Author: Ilan Schnell
|
||||
Author-email: ilanschnell@gmail.com
|
||||
License: PSF-2.0
|
||||
Classifier: Development Status :: 6 - Mature
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: C
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Classifier: Programming Language :: Python :: 3.14
|
||||
Classifier: Programming Language :: Python :: Free Threading :: 1 - Unstable
|
||||
Classifier: Topic :: Utilities
|
||||
License-File: LICENSE
|
||||
Dynamic: author
|
||||
Dynamic: author-email
|
||||
Dynamic: classifier
|
||||
Dynamic: description
|
||||
Dynamic: home-page
|
||||
Dynamic: license
|
||||
Dynamic: license-file
|
||||
Dynamic: summary
|
||||
|
||||
bitarray: efficient arrays of booleans
|
||||
======================================
|
||||
|
||||
This library provides an object type which efficiently represents an array
|
||||
of booleans. Bitarrays are sequence types and behave very much like usual
|
||||
lists. Eight bits are represented by one byte in a contiguous block of
|
||||
memory. The user can select between two representations: little-endian
|
||||
and big-endian. All functionality is implemented in C.
|
||||
Methods for accessing the machine representation are provided, including the
|
||||
ability to import and export buffers. This allows creating bitarrays that
|
||||
are mapped to other objects, including memory-mapped files.
|
||||
|
||||
|
||||
Key features
|
||||
------------
|
||||
|
||||
* The bit-endianness can be specified for each bitarray object, see below.
|
||||
* Sequence methods: slicing (including slice assignment and deletion),
|
||||
operations ``+``, ``*``, ``+=``, ``*=``, the ``in`` operator, ``len()``
|
||||
* Bitwise operations: ``~``, ``&``, ``|``, ``^``, ``<<``, ``>>`` (as well as
|
||||
their in-place versions ``&=``, ``|=``, ``^=``, ``<<=``, ``>>=``).
|
||||
* Fast methods for encoding and decoding variable bit length prefix codes.
|
||||
* Bitarray objects support the buffer protocol (both importing and
|
||||
exporting buffers).
|
||||
* Packing and unpacking to other binary data formats, e.g. ``numpy.ndarray``.
|
||||
* Pickling and unpickling of bitarray objects.
|
||||
* Immutable ``frozenbitarray`` objects which are hashable
|
||||
* Sequential search
|
||||
* Type hinting
|
||||
* Extensive test suite with about 600 unittests
|
||||
* Utility module ``bitarray.util``:
|
||||
|
||||
* conversion to and from hexadecimal strings
|
||||
* generating random bitarrays
|
||||
* pretty printing
|
||||
* conversion to and from integers
|
||||
* creating Huffman codes
|
||||
* compression of sparse bitarrays
|
||||
* (de-) serialization
|
||||
* various count functions
|
||||
* other helpful functions
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Python wheels are are available on PyPI for all major platforms and Python
|
||||
versions. Which means you can simply:
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ pip install bitarray
|
||||
|
||||
Once you have installed the package, you may want to test it:
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ python -c 'import bitarray; bitarray.test()'
|
||||
bitarray is installed in: /Users/ilan/bitarray/bitarray
|
||||
bitarray version: 3.8.0
|
||||
sys.version: 3.13.5 (main, Jun 16 2025) [Clang 18.1.8]
|
||||
sys.prefix: /Users/ilan/miniforge
|
||||
pointer size: 64 bit
|
||||
sizeof(size_t): 8
|
||||
sizeof(bitarrayobject): 80
|
||||
HAVE_BUILTIN_BSWAP64: 1
|
||||
default bit-endianness: big
|
||||
machine byte-order: little
|
||||
Py_GIL_DISABLED: 0
|
||||
Py_DEBUG: 0
|
||||
DEBUG: 0
|
||||
.........................................................................
|
||||
.........................................................................
|
||||
................................................................
|
||||
----------------------------------------------------------------------
|
||||
Ran 595 tests in 0.165s
|
||||
|
||||
OK
|
||||
|
||||
The ``test()`` function is part of the API. It will return
|
||||
a ``unittest.runner.TextTestResult`` object, such that one can verify that
|
||||
all tests ran successfully by:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import bitarray
|
||||
assert bitarray.test().wasSuccessful()
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
As mentioned above, bitarray objects behave very much like lists, so
|
||||
there is not too much to learn. The biggest difference from list
|
||||
objects (except that bitarray are obviously homogeneous) is the ability
|
||||
to access the machine representation of the object.
|
||||
When doing so, the bit-endianness is of importance; this issue is
|
||||
explained in detail in the section below. Here, we demonstrate the
|
||||
basic usage of bitarray objects:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from bitarray import bitarray
|
||||
>>> a = bitarray() # create empty bitarray
|
||||
>>> a.append(1)
|
||||
>>> a.extend([1, 0])
|
||||
>>> a
|
||||
bitarray('110')
|
||||
>>> x = bitarray(2 ** 20) # bitarray of length 1048576 (initialized to 0)
|
||||
>>> len(x)
|
||||
1048576
|
||||
>>> bitarray('1001 011') # initialize from string (whitespace is ignored)
|
||||
bitarray('1001011')
|
||||
>>> lst = [1, 0, False, True, True]
|
||||
>>> a = bitarray(lst) # initialize from iterable
|
||||
>>> a
|
||||
bitarray('10011')
|
||||
>>> a[2] # indexing a single item will always return an integer
|
||||
0
|
||||
>>> a[2:4] # whereas indexing a slice will always return a bitarray
|
||||
bitarray('01')
|
||||
>>> a[2:3] # even when the slice length is just one
|
||||
bitarray('0')
|
||||
>>> a.count(1)
|
||||
3
|
||||
>>> a.remove(0) # removes first occurrence of 0
|
||||
>>> a
|
||||
bitarray('1011')
|
||||
|
||||
Like lists, bitarray objects support slice assignment and deletion:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> a = bitarray(50)
|
||||
>>> a.setall(0) # set all elements in a to 0
|
||||
>>> a[11:37:3] = 9 * bitarray('1')
|
||||
>>> a
|
||||
bitarray('00000000000100100100100100100100100100000000000000')
|
||||
>>> del a[12::3]
|
||||
>>> a
|
||||
bitarray('0000000000010101010101010101000000000')
|
||||
>>> a[-6:] = bitarray('10011')
|
||||
>>> a
|
||||
bitarray('000000000001010101010101010100010011')
|
||||
>>> a += bitarray('000111')
|
||||
>>> a[9:]
|
||||
bitarray('001010101010101010100010011000111')
|
||||
|
||||
In addition, slices can be assigned to booleans, which is easier (and
|
||||
faster) than assigning to a bitarray in which all values are the same:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> a = 20 * bitarray('0')
|
||||
>>> a[1:15:3] = True
|
||||
>>> a
|
||||
bitarray('01001001001001000000')
|
||||
|
||||
This is easier and faster than:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> a = 20 * bitarray('0')
|
||||
>>> a[1:15:3] = 5 * bitarray('1')
|
||||
>>> a
|
||||
bitarray('01001001001001000000')
|
||||
|
||||
Note that in the latter we have to create a temporary bitarray whose length
|
||||
must be known or calculated. Another example of assigning slices to Booleans,
|
||||
is setting ranges:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> a = bitarray(30)
|
||||
>>> a[:] = 0 # set all elements to 0 - equivalent to a.setall(0)
|
||||
>>> a[10:25] = 1 # set elements in range(10, 25) to 1
|
||||
>>> a
|
||||
bitarray('000000000011111111111111100000')
|
||||
|
||||
As of bitarray version 2.8, indices may also be lists of arbitrary
|
||||
indices (like in NumPy), or bitarrays that are treated as masks,
|
||||
see `Bitarray indexing <https://github.com/ilanschnell/bitarray/blob/master/doc/indexing.rst>`__.
|
||||
|
||||
|
||||
Bitwise operators
|
||||
-----------------
|
||||
|
||||
Bitarray objects support the bitwise operators ``~``, ``&``, ``|``, ``^``,
|
||||
``<<``, ``>>`` (as well as their in-place versions ``&=``, ``|=``, ``^=``,
|
||||
``<<=``, ``>>=``). The behavior is very much what one would expect:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> a = bitarray('101110001')
|
||||
>>> ~a # invert
|
||||
bitarray('010001110')
|
||||
>>> b = bitarray('111001011')
|
||||
>>> a ^ b # bitwise XOR
|
||||
bitarray('010111010')
|
||||
>>> a &= b # inplace AND
|
||||
>>> a
|
||||
bitarray('101000001')
|
||||
>>> a <<= 2 # in-place left-shift by 2
|
||||
>>> a
|
||||
bitarray('100000100')
|
||||
>>> b >> 1 # return b right-shifted by 1
|
||||
bitarray('011100101')
|
||||
|
||||
The C language does not specify the behavior of negative shifts and
|
||||
of left shifts larger or equal than the width of the promoted left operand.
|
||||
The exact behavior is compiler/machine specific.
|
||||
This Python bitarray library specifies the behavior as follows:
|
||||
|
||||
* the length of the bitarray is never changed by any shift operation
|
||||
* blanks are filled by 0
|
||||
* negative shifts raise ``ValueError``
|
||||
* shifts larger or equal to the length of the bitarray result in
|
||||
bitarrays with all values 0
|
||||
|
||||
It is worth noting that (regardless of bit-endianness) the bitarray left
|
||||
shift (``<<``) always shifts towards lower indices, and the right
|
||||
shift (``>>``) always shifts towards higher indices.
|
||||
|
||||
|
||||
Bit-endianness
|
||||
--------------
|
||||
|
||||
For many purposes the bit-endianness is not of any relevance to the end user
|
||||
and can be regarded as an implementation detail of bitarray objects.
|
||||
However, there are use cases when the bit-endianness becomes important.
|
||||
These use cases involve explicitly reading and writing the bitarray buffer
|
||||
using ``.tobytes()``, ``.frombytes()``, ``.tofile()`` or ``.fromfile()``,
|
||||
importing and exporting buffers. Also, a number of utility functions
|
||||
in ``bitarray.util`` will return different results depending on
|
||||
bit-endianness, such as ``ba2hex()`` or ``ba2int``.
|
||||
To better understand this topic, please read `bit-endianness <https://github.com/ilanschnell/bitarray/blob/master/doc/endianness.rst>`__.
|
||||
|
||||
|
||||
Buffer protocol
|
||||
---------------
|
||||
|
||||
Bitarray objects support the buffer protocol. They can both export their
|
||||
own buffer, as well as import another object's buffer. To learn more about
|
||||
this topic, please read `buffer protocol <https://github.com/ilanschnell/bitarray/blob/master/doc/buffer.rst>`__. There is also an example that shows how
|
||||
to memory-map a file to a bitarray: `mmapped-file.py <https://github.com/ilanschnell/bitarray/blob/master/examples/mmapped-file.py>`__
|
||||
|
||||
|
||||
Variable bit length prefix codes
|
||||
--------------------------------
|
||||
|
||||
The ``.encode()`` method takes a dictionary mapping symbols to bitarrays
|
||||
and an iterable, and extends the bitarray object with the encoded symbols
|
||||
found while iterating. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> d = {'H':bitarray('111'), 'e':bitarray('0'),
|
||||
... 'l':bitarray('110'), 'o':bitarray('10')}
|
||||
...
|
||||
>>> a = bitarray()
|
||||
>>> a.encode(d, 'Hello')
|
||||
>>> a
|
||||
bitarray('111011011010')
|
||||
|
||||
Note that the string ``'Hello'`` is an iterable, but the symbols are not
|
||||
limited to characters, in fact any immutable Python object can be a symbol.
|
||||
Taking the same dictionary, we can apply the ``.decode()`` method which will
|
||||
return an iterable of the symbols:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> list(a.decode(d))
|
||||
['H', 'e', 'l', 'l', 'o']
|
||||
>>> ''.join(a.decode(d))
|
||||
'Hello'
|
||||
|
||||
Symbols are not limited to being characters.
|
||||
The above dictionary ``d`` can be efficiently constructed using the function
|
||||
``bitarray.util.huffman_code()``. I also wrote `Huffman coding in Python
|
||||
using bitarray <http://ilan.schnell-web.net/prog/huffman/>`__ for more
|
||||
background information.
|
||||
|
||||
When the codes are large, and you have many decode calls, most time will
|
||||
be spent creating the (same) internal decode tree objects. In this case,
|
||||
it will be much faster to create a ``decodetree`` object, which can be
|
||||
passed to bitarray's ``.decode()`` method, instead of passing the prefix
|
||||
code dictionary to those methods itself:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from bitarray import bitarray, decodetree
|
||||
>>> t = decodetree({'a': bitarray('0'), 'b': bitarray('1')})
|
||||
>>> a = bitarray('0110')
|
||||
>>> list(a.decode(t))
|
||||
['a', 'b', 'b', 'a']
|
||||
|
||||
The sole purpose of the immutable ``decodetree`` object is to be passed
|
||||
to bitarray's ``.decode()`` method.
|
||||
|
||||
|
||||
Frozenbitarrays
|
||||
---------------
|
||||
|
||||
A ``frozenbitarray`` object is very similar to the bitarray object.
|
||||
The difference is that this a ``frozenbitarray`` is immutable, and hashable,
|
||||
and can therefore be used as a dictionary key:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from bitarray import frozenbitarray
|
||||
>>> key = frozenbitarray('1100011')
|
||||
>>> {key: 'some value'}
|
||||
{frozenbitarray('1100011'): 'some value'}
|
||||
>>> key[3] = 1
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: frozenbitarray is immutable
|
||||
|
||||
|
||||
Reference
|
||||
=========
|
||||
|
||||
bitarray version: 3.8.0 -- `change log <https://github.com/ilanschnell/bitarray/blob/master/doc/changelog.rst>`__
|
||||
|
||||
In the following, ``item`` and ``value`` are usually a single bit -
|
||||
an integer 0 or 1.
|
||||
|
||||
Also, ``sub_bitarray`` refers to either a bitarray, or an ``item``.
|
||||
|
||||
|
||||
The bitarray object:
|
||||
--------------------
|
||||
|
||||
``bitarray(initializer=0, /, endian='big', buffer=None)`` -> bitarray
|
||||
Return a new bitarray object whose items are bits initialized from
|
||||
the optional initializer, and bit-endianness.
|
||||
The initializer may be one of the following types:
|
||||
a.) ``int`` bitarray, initialized to zeros, of given length
|
||||
b.) ``bytes`` or ``bytearray`` to initialize buffer directly
|
||||
c.) ``str`` of 0s and 1s, ignoring whitespace and "_"
|
||||
d.) iterable of integers 0 or 1.
|
||||
|
||||
Optional keyword arguments:
|
||||
|
||||
``endian``: Specifies the bit-endianness of the created bitarray object.
|
||||
Allowed values are ``big`` and ``little`` (the default is ``big``).
|
||||
The bit-endianness effects the buffer representation of the bitarray.
|
||||
|
||||
``buffer``: Any object which exposes a buffer. When provided, ``initializer``
|
||||
cannot be present (or has to be ``None``). The imported buffer may be
|
||||
read-only or writable, depending on the object type.
|
||||
|
||||
New in version 2.3: optional ``buffer`` argument
|
||||
|
||||
New in version 3.4: allow initializer ``bytes`` or ``bytearray`` to set buffer directly
|
||||
|
||||
|
||||
bitarray methods:
|
||||
-----------------
|
||||
|
||||
``all()`` -> bool
|
||||
Return ``True`` when all bits in bitarray are 1.
|
||||
``a.all()`` is a faster version of ``all(a)``.
|
||||
|
||||
|
||||
``any()`` -> bool
|
||||
Return ``True`` when any bit in bitarray is 1.
|
||||
``a.any()`` is a faster version of ``any(a)``.
|
||||
|
||||
|
||||
``append(item, /)``
|
||||
Append ``item`` to the end of the bitarray.
|
||||
|
||||
|
||||
``buffer_info()`` -> BufferInfo
|
||||
Return named tuple with following fields:
|
||||
|
||||
0. ``address``: memory address of buffer
|
||||
1. ``nbytes``: buffer size (in bytes)
|
||||
2. ``endian``: bit-endianness as a string
|
||||
3. ``padbits``: number of pad bits
|
||||
4. ``alloc``: allocated memory for buffer (in bytes)
|
||||
5. ``readonly``: memory is read-only (bool)
|
||||
6. ``imported``: buffer is imported (bool)
|
||||
7. ``exports``: number of buffer exports
|
||||
|
||||
New in version 3.7: return named tuple
|
||||
|
||||
|
||||
``bytereverse(start=0, stop=<end of buffer>, /)``
|
||||
For each byte in byte-range(``start``, ``stop``) reverse bits in-place.
|
||||
The start and stop indices are given in terms of bytes (not bits).
|
||||
Also note that this method only changes the buffer; it does not change the
|
||||
bit-endianness of the bitarray object. Pad bits are left unchanged such
|
||||
that two consecutive calls will always leave the bitarray unchanged.
|
||||
|
||||
New in version 2.2.5: optional start and stop arguments
|
||||
|
||||
|
||||
``clear()``
|
||||
Remove all items from bitarray.
|
||||
|
||||
New in version 1.4
|
||||
|
||||
|
||||
``copy()`` -> bitarray
|
||||
Return copy of bitarray (with same bit-endianness).
|
||||
|
||||
|
||||
``count(value=1, start=0, stop=<end>, step=1, /)`` -> int
|
||||
Number of occurrences of ``value`` bitarray within ``[start:stop:step]``.
|
||||
Optional arguments ``start``, ``stop`` and ``step`` are interpreted in
|
||||
slice notation, meaning ``a.count(value, start, stop, step)`` equals
|
||||
``a[start:stop:step].count(value)``.
|
||||
The ``value`` may also be a sub-bitarray. In this case non-overlapping
|
||||
occurrences are counted within ``[start:stop]`` (``step`` must be 1).
|
||||
|
||||
New in version 1.1.0: optional start and stop arguments
|
||||
|
||||
New in version 2.3.7: optional step argument
|
||||
|
||||
New in version 2.9: add non-overlapping sub-bitarray count
|
||||
|
||||
|
||||
``decode(code, /)`` -> iterator
|
||||
Given a prefix code (a dict mapping symbols to bitarrays, or ``decodetree``
|
||||
object), decode content of bitarray and return an iterator over
|
||||
corresponding symbols.
|
||||
|
||||
See also: `Bitarray 3 transition <https://github.com/ilanschnell/bitarray/blob/master/doc/bitarray3.rst>`__
|
||||
|
||||
New in version 3.0: returns iterator (equivalent to past ``.iterdecode()``)
|
||||
|
||||
|
||||
``encode(code, iterable, /)``
|
||||
Given a prefix code (a dict mapping symbols to bitarrays),
|
||||
iterate over the iterable object with symbols, and extend bitarray
|
||||
with corresponding bitarray for each symbol.
|
||||
|
||||
|
||||
``extend(iterable, /)``
|
||||
Append items from to the end of the bitarray.
|
||||
If ``iterable`` is a (Unicode) string, each ``0`` and ``1`` are appended as
|
||||
bits (ignoring whitespace and underscore).
|
||||
|
||||
New in version 3.4: allow ``bytes`` object
|
||||
|
||||
|
||||
``fill()`` -> int
|
||||
Add zeros to the end of the bitarray, such that the length will be
|
||||
a multiple of 8, and return the number of bits added [0..7].
|
||||
|
||||
|
||||
``find(sub_bitarray, start=0, stop=<end>, /, right=False)`` -> int
|
||||
Return lowest (or rightmost when ``right=True``) index where sub_bitarray
|
||||
is found, such that sub_bitarray is contained within ``[start:stop]``.
|
||||
Return -1 when sub_bitarray is not found.
|
||||
|
||||
New in version 2.1
|
||||
|
||||
New in version 2.9: add optional keyword argument ``right``
|
||||
|
||||
|
||||
``frombytes(bytes, /)``
|
||||
Extend bitarray with raw bytes from a bytes-like object.
|
||||
Each added byte will add eight bits to the bitarray.
|
||||
|
||||
New in version 2.5.0: allow bytes-like argument
|
||||
|
||||
|
||||
``fromfile(f, n=-1, /)``
|
||||
Extend bitarray with up to ``n`` bytes read from file object ``f`` (or any
|
||||
other binary stream what supports a ``.read()`` method, e.g. ``io.BytesIO``).
|
||||
Each read byte will add eight bits to the bitarray. When ``n`` is omitted
|
||||
or negative, reads and extends all data until EOF.
|
||||
When ``n`` is non-negative but exceeds the available data, ``EOFError`` is
|
||||
raised. However, the available data is still read and extended.
|
||||
|
||||
|
||||
``index(sub_bitarray, start=0, stop=<end>, /, right=False)`` -> int
|
||||
Return lowest (or rightmost when ``right=True``) index where sub_bitarray
|
||||
is found, such that sub_bitarray is contained within ``[start:stop]``.
|
||||
Raises ``ValueError`` when sub_bitarray is not present.
|
||||
|
||||
New in version 2.9: add optional keyword argument ``right``
|
||||
|
||||
|
||||
``insert(index, value, /)``
|
||||
Insert ``value`` into bitarray before ``index``.
|
||||
|
||||
|
||||
``invert(index=<all bits>, /)``
|
||||
Invert all bits in bitarray (in-place).
|
||||
When the optional ``index`` is given, only invert the single bit at ``index``.
|
||||
|
||||
New in version 1.5.3: optional index argument
|
||||
|
||||
|
||||
``pack(bytes, /)``
|
||||
Extend bitarray from a bytes-like object, where each byte corresponds
|
||||
to a single bit. The byte ``b'\x00'`` maps to bit 0 and all other bytes
|
||||
map to bit 1.
|
||||
|
||||
This method, as well as the ``.unpack()`` method, are meant for efficient
|
||||
transfer of data between bitarray objects to other Python objects (for
|
||||
example NumPy's ndarray object) which have a different memory view.
|
||||
|
||||
New in version 2.5.0: allow bytes-like argument
|
||||
|
||||
|
||||
``pop(index=-1, /)`` -> item
|
||||
Remove and return item at ``index`` (default last).
|
||||
Raises ``IndexError`` if index is out of range.
|
||||
|
||||
|
||||
``remove(value, /)``
|
||||
Remove the first occurrence of ``value``.
|
||||
Raises ``ValueError`` if value is not present.
|
||||
|
||||
|
||||
``reverse()``
|
||||
Reverse all bits in bitarray (in-place).
|
||||
|
||||
|
||||
``search(sub_bitarray, start=0, stop=<end>, /, right=False)`` -> iterator
|
||||
Return iterator over indices where sub_bitarray is found, such that
|
||||
sub_bitarray is contained within ``[start:stop]``.
|
||||
The indices are iterated in ascending order (from lowest to highest),
|
||||
unless ``right=True``, which will iterate in descending order (starting with
|
||||
rightmost match).
|
||||
|
||||
See also: `Bitarray 3 transition <https://github.com/ilanschnell/bitarray/blob/master/doc/bitarray3.rst>`__
|
||||
|
||||
New in version 2.9: optional start and stop arguments - add optional keyword argument ``right``
|
||||
|
||||
New in version 3.0: returns iterator (equivalent to past ``.itersearch()``)
|
||||
|
||||
|
||||
``setall(value, /)``
|
||||
Set all elements in bitarray to ``value``.
|
||||
Note that ``a.setall(value)`` is equivalent to ``a[:] = value``.
|
||||
|
||||
|
||||
``sort(reverse=False)``
|
||||
Sort all bits in bitarray (in-place).
|
||||
|
||||
|
||||
``to01(group=0, sep=' ')`` -> str
|
||||
Return bitarray as (Unicode) string of ``0``s and ``1``s.
|
||||
The bits are grouped into ``group`` bits (default is no grouping).
|
||||
When grouped, the string ``sep`` is inserted between groups
|
||||
of ``group`` characters, default is a space.
|
||||
|
||||
New in version 3.3: optional ``group`` and ``sep`` arguments
|
||||
|
||||
|
||||
``tobytes()`` -> bytes
|
||||
Return the bitarray buffer (pad bits are set to zero).
|
||||
``a.tobytes()`` is equivalent to ``bytes(a)``
|
||||
|
||||
|
||||
``tofile(f, /)``
|
||||
Write bitarray buffer to file object ``f``.
|
||||
|
||||
|
||||
``tolist()`` -> list
|
||||
Return bitarray as list of integers.
|
||||
``a.tolist()`` equals ``list(a)``.
|
||||
|
||||
Note that the list object being created will require 32 or 64 times more
|
||||
memory (depending on the machine architecture) than the bitarray object,
|
||||
which may cause a memory error if the bitarray is very large.
|
||||
|
||||
|
||||
``unpack(zero=b'\x00', one=b'\x01')`` -> bytes
|
||||
Return bytes that contain one byte for each bit in the bitarray,
|
||||
using specified mapping.
|
||||
|
||||
|
||||
bitarray data descriptors:
|
||||
--------------------------
|
||||
|
||||
Data descriptors were added in version 2.6.
|
||||
|
||||
``endian`` -> str
|
||||
bit-endianness as Unicode string
|
||||
|
||||
New in version 3.4: replaces former ``.endian()`` method
|
||||
|
||||
|
||||
``nbytes`` -> int
|
||||
buffer size in bytes
|
||||
|
||||
|
||||
``padbits`` -> int
|
||||
number of pad bits
|
||||
|
||||
|
||||
``readonly`` -> bool
|
||||
bool indicating whether buffer is read-only
|
||||
|
||||
|
||||
Other objects:
|
||||
--------------
|
||||
|
||||
``frozenbitarray(initializer=0, /, endian='big', buffer=None)`` -> frozenbitarray
|
||||
Return a ``frozenbitarray`` object. Initialized the same way a ``bitarray``
|
||||
object is initialized. A ``frozenbitarray`` is immutable and hashable,
|
||||
and may therefore be used as a dictionary key.
|
||||
|
||||
New in version 1.1
|
||||
|
||||
|
||||
``decodetree(code, /)`` -> decodetree
|
||||
Given a prefix code (a dict mapping symbols to bitarrays),
|
||||
create a binary tree object to be passed to ``.decode()``.
|
||||
|
||||
New in version 1.6
|
||||
|
||||
|
||||
Functions defined in the `bitarray` module:
|
||||
-------------------------------------------
|
||||
|
||||
``bits2bytes(n, /)`` -> int
|
||||
Return the number of bytes necessary to store n bits.
|
||||
|
||||
|
||||
``get_default_endian()`` -> str
|
||||
Return the default bit-endianness for new bitarray objects being created.
|
||||
|
||||
New in version 1.3
|
||||
|
||||
|
||||
``test(verbosity=1)`` -> TextTestResult
|
||||
Run self-test, and return ``unittest.runner.TextTestResult`` object.
|
||||
|
||||
|
||||
Functions defined in `bitarray.util` module:
|
||||
--------------------------------------------
|
||||
|
||||
This sub-module was added in version 1.2.
|
||||
|
||||
``any_and(a, b, /)`` -> bool
|
||||
Efficient implementation of ``any(a & b)``.
|
||||
|
||||
New in version 2.7
|
||||
|
||||
|
||||
``ba2base(n, bitarray, /, group=0, sep=' ')`` -> str
|
||||
Return a string containing the base ``n`` ASCII representation of
|
||||
the bitarray. Allowed values for ``n`` are 2, 4, 8, 16, 32 and 64.
|
||||
The bitarray has to be multiple of length 1, 2, 3, 4, 5 or 6 respectively.
|
||||
For ``n=32`` the RFC 4648 Base32 alphabet is used, and for ``n=64`` the
|
||||
standard base 64 alphabet is used.
|
||||
When grouped, the string ``sep`` is inserted between groups
|
||||
of ``group`` characters, default is a space.
|
||||
|
||||
See also: `Bitarray representations <https://github.com/ilanschnell/bitarray/blob/master/doc/represent.rst>`__
|
||||
|
||||
New in version 1.9
|
||||
|
||||
New in version 3.3: optional ``group`` and ``sep`` arguments
|
||||
|
||||
|
||||
``ba2hex(bitarray, /, group=0, sep=' ')`` -> hexstr
|
||||
Return a string containing the hexadecimal representation of
|
||||
the bitarray (which has to be multiple of 4 in length).
|
||||
When grouped, the string ``sep`` is inserted between groups
|
||||
of ``group`` characters, default is a space.
|
||||
|
||||
New in version 3.3: optional ``group`` and ``sep`` arguments
|
||||
|
||||
|
||||
``ba2int(bitarray, /, signed=False)`` -> int
|
||||
Convert the given bitarray to an integer.
|
||||
The bit-endianness of the bitarray is respected.
|
||||
``signed`` indicates whether two's complement is used to represent the integer.
|
||||
|
||||
|
||||
``base2ba(n, asciistr, /, endian=None)`` -> bitarray
|
||||
Bitarray of base ``n`` ASCII representation.
|
||||
Allowed values for ``n`` are 2, 4, 8, 16, 32 and 64.
|
||||
For ``n=32`` the RFC 4648 Base32 alphabet is used, and for ``n=64`` the
|
||||
standard base 64 alphabet is used. Whitespace is ignored.
|
||||
|
||||
See also: `Bitarray representations <https://github.com/ilanschnell/bitarray/blob/master/doc/represent.rst>`__
|
||||
|
||||
New in version 1.9
|
||||
|
||||
New in version 3.3: ignore whitespace
|
||||
|
||||
|
||||
``byteswap(a, n=<buffer size>, /)``
|
||||
Reverse every ``n`` consecutive bytes of ``a`` in-place.
|
||||
By default, all bytes are reversed. Note that ``n`` is not limited to 2, 4
|
||||
or 8, but can be any positive integer.
|
||||
Also, ``a`` may be any object that exposes a writable buffer.
|
||||
Nothing about this function is specific to bitarray objects.
|
||||
|
||||
We should mention that Python's ``array.array`` object has a
|
||||
method ``.byteswap()`` with similar functionality. However, unlike
|
||||
bitarray's ``util.byteswap()`` function, this method is limited to
|
||||
swapping 2, 4, or 8 consecutive bytes.
|
||||
|
||||
New in version 3.4
|
||||
|
||||
|
||||
``canonical_decode(bitarray, count, symbol, /)`` -> iterator
|
||||
Decode bitarray using canonical Huffman decoding tables
|
||||
where ``count`` is a sequence containing the number of symbols of each length
|
||||
and ``symbol`` is a sequence of symbols in canonical order.
|
||||
|
||||
See also: `Canonical Huffman Coding <https://github.com/ilanschnell/bitarray/blob/master/doc/canonical.rst>`__
|
||||
|
||||
New in version 2.5
|
||||
|
||||
|
||||
``canonical_huffman(dict, /)`` -> tuple
|
||||
Given a frequency map, a dictionary mapping symbols to their frequency,
|
||||
calculate the canonical Huffman code. Returns a tuple containing:
|
||||
|
||||
0. the canonical Huffman code as a dict mapping symbols to bitarrays
|
||||
1. a list containing the number of symbols of each code length
|
||||
2. a list of symbols in canonical order
|
||||
|
||||
Note: the two lists may be used as input for ``canonical_decode()``.
|
||||
|
||||
See also: `Canonical Huffman Coding <https://github.com/ilanschnell/bitarray/blob/master/doc/canonical.rst>`__
|
||||
|
||||
New in version 2.5
|
||||
|
||||
|
||||
``correspond_all(a, b, /)`` -> tuple
|
||||
Return tuple with counts of: ~a & ~b, ~a & b, a & ~b, a & b
|
||||
|
||||
New in version 3.4
|
||||
|
||||
|
||||
``count_and(a, b, /)`` -> int
|
||||
Return ``(a & b).count()`` in a memory efficient manner,
|
||||
as no intermediate bitarray object gets created.
|
||||
|
||||
|
||||
``count_n(a, n, value=1, /)`` -> int
|
||||
Return lowest index ``i`` for which ``a[:i].count(value) == n``.
|
||||
Raises ``ValueError`` when ``n`` exceeds total count (``a.count(value)``).
|
||||
|
||||
New in version 2.3.6: optional value argument
|
||||
|
||||
|
||||
``count_or(a, b, /)`` -> int
|
||||
Return ``(a | b).count()`` in a memory efficient manner,
|
||||
as no intermediate bitarray object gets created.
|
||||
|
||||
|
||||
``count_xor(a, b, /)`` -> int
|
||||
Return ``(a ^ b).count()`` in a memory efficient manner,
|
||||
as no intermediate bitarray object gets created.
|
||||
|
||||
This is also known as the Hamming distance.
|
||||
|
||||
|
||||
``deserialize(bytes, /)`` -> bitarray
|
||||
Return a bitarray given a bytes-like representation such as returned
|
||||
by ``serialize()``.
|
||||
|
||||
See also: `Bitarray representations <https://github.com/ilanschnell/bitarray/blob/master/doc/represent.rst>`__
|
||||
|
||||
New in version 1.8
|
||||
|
||||
New in version 2.5.0: allow bytes-like argument
|
||||
|
||||
|
||||
``gen_primes(n, /, endian=None, odd=False)`` -> bitarray
|
||||
Generate a bitarray of length ``n`` in which active indices are prime numbers.
|
||||
By default (``odd=False``), active indices correspond to prime numbers directly.
|
||||
When ``odd=True``, only odd prime numbers are represented in the resulting
|
||||
bitarray ``a``, and ``a[i]`` corresponds to ``2*i+1`` being prime or not.
|
||||
|
||||
Apart from working with prime numbers, this function is useful for
|
||||
testing, as it provides a simple way to create a well-defined bitarray
|
||||
of any length.
|
||||
|
||||
New in version 3.7
|
||||
|
||||
|
||||
``hex2ba(hexstr, /, endian=None)`` -> bitarray
|
||||
Bitarray of hexadecimal representation. hexstr may contain any number
|
||||
(including odd numbers) of hex digits (upper or lower case).
|
||||
Whitespace is ignored.
|
||||
|
||||
New in version 3.3: ignore whitespace
|
||||
|
||||
|
||||
``huffman_code(dict, /, endian=None)`` -> dict
|
||||
Given a frequency map, a dictionary mapping symbols to their frequency,
|
||||
calculate the Huffman code, i.e. a dict mapping those symbols to
|
||||
bitarrays (with given bit-endianness). Note that the symbols are not limited
|
||||
to being strings. Symbols may be any hashable object.
|
||||
|
||||
|
||||
``int2ba(int, /, length=None, endian=None, signed=False)`` -> bitarray
|
||||
Convert the given integer to a bitarray (with given bit-endianness,
|
||||
and no leading (big-endian) / trailing (little-endian) zeros), unless
|
||||
the ``length`` of the bitarray is provided. An ``OverflowError`` is raised
|
||||
if the integer is not representable with the given number of bits.
|
||||
``signed`` determines whether two's complement is used to represent the integer,
|
||||
and requires ``length`` to be provided.
|
||||
|
||||
|
||||
``intervals(bitarray, /)`` -> iterator
|
||||
Compute all uninterrupted intervals of 1s and 0s, and return an
|
||||
iterator over tuples ``(value, start, stop)``. The intervals are guaranteed
|
||||
to be in order, and their size is always non-zero (``stop - start > 0``).
|
||||
|
||||
New in version 2.7
|
||||
|
||||
|
||||
``ones(n, /, endian=None)`` -> bitarray
|
||||
Create a bitarray of length ``n``, with all values ``1``, and optional
|
||||
bit-endianness (``little`` or ``big``).
|
||||
|
||||
New in version 2.9
|
||||
|
||||
|
||||
``parity(a, /)`` -> int
|
||||
Return parity of bitarray ``a``.
|
||||
``parity(a)`` is equivalent to ``a.count() % 2`` but more efficient.
|
||||
|
||||
New in version 1.9
|
||||
|
||||
|
||||
``pprint(bitarray, /, stream=None, group=8, indent=4, width=80)``
|
||||
Pretty-print bitarray object to ``stream``, defaults is ``sys.stdout``.
|
||||
By default, bits are grouped in bytes (8 bits), and 64 bits per line.
|
||||
Non-bitarray objects are printed using ``pprint.pprint()``.
|
||||
|
||||
New in version 1.8
|
||||
|
||||
|
||||
``random_k(n, /, k, endian=None)`` -> bitarray
|
||||
Return (pseudo-) random bitarray of length ``n`` with ``k`` elements
|
||||
set to one. Mathematically equivalent to setting (in a bitarray of
|
||||
length ``n``) all bits at indices ``random.sample(range(n), k)`` to one.
|
||||
The random bitarrays are reproducible when giving Python's ``random.seed()``
|
||||
a specific seed value.
|
||||
|
||||
New in version 3.6
|
||||
|
||||
|
||||
``random_p(n, /, p=0.5, endian=None)`` -> bitarray
|
||||
Return (pseudo-) random bitarray of length ``n``, where each bit has
|
||||
probability ``p`` of being one (independent of any other bits). Mathematically
|
||||
equivalent to ``bitarray((random() < p for _ in range(n)), endian)``, but much
|
||||
faster for large ``n``. The random bitarrays are reproducible when giving
|
||||
Python's ``random.seed()`` with a specific seed value.
|
||||
|
||||
This function requires Python 3.12 or higher, as it depends on the standard
|
||||
library function ``random.binomialvariate()``. Raises ``NotImplementedError``
|
||||
when Python version is too low.
|
||||
|
||||
See also: `Random Bitarrays <https://github.com/ilanschnell/bitarray/blob/master/doc/random_p.rst>`__
|
||||
|
||||
New in version 3.5
|
||||
|
||||
|
||||
``sc_decode(stream, /)`` -> bitarray
|
||||
Decompress binary stream (an integer iterator, or bytes-like object) of a
|
||||
sparse compressed (``sc``) bitarray, and return the decoded bitarray.
|
||||
This function consumes only one bitarray and leaves the remaining stream
|
||||
untouched. Use ``sc_encode()`` for compressing (encoding).
|
||||
|
||||
See also: `Compression of sparse bitarrays <https://github.com/ilanschnell/bitarray/blob/master/doc/sparse_compression.rst>`__
|
||||
|
||||
New in version 2.7
|
||||
|
||||
|
||||
``sc_encode(bitarray, /)`` -> bytes
|
||||
Compress a sparse bitarray and return its binary representation.
|
||||
This representation is useful for efficiently storing sparse bitarrays.
|
||||
Use ``sc_decode()`` for decompressing (decoding).
|
||||
|
||||
See also: `Compression of sparse bitarrays <https://github.com/ilanschnell/bitarray/blob/master/doc/sparse_compression.rst>`__
|
||||
|
||||
New in version 2.7
|
||||
|
||||
|
||||
``serialize(bitarray, /)`` -> bytes
|
||||
Return a serialized representation of the bitarray, which may be passed to
|
||||
``deserialize()``. It efficiently represents the bitarray object (including
|
||||
its bit-endianness) and is guaranteed not to change in future releases.
|
||||
|
||||
See also: `Bitarray representations <https://github.com/ilanschnell/bitarray/blob/master/doc/represent.rst>`__
|
||||
|
||||
New in version 1.8
|
||||
|
||||
|
||||
``strip(bitarray, /, mode='right')`` -> bitarray
|
||||
Return a new bitarray with zeros stripped from left, right or both ends.
|
||||
Allowed values for mode are the strings: ``left``, ``right``, ``both``
|
||||
|
||||
|
||||
``subset(a, b, /)`` -> bool
|
||||
Return ``True`` if bitarray ``a`` is a subset of bitarray ``b``.
|
||||
``subset(a, b)`` is equivalent to ``a | b == b`` (and equally ``a & b == a``) but
|
||||
more efficient as no intermediate bitarray object is created and the buffer
|
||||
iteration is stopped as soon as one mismatch is found.
|
||||
|
||||
|
||||
``sum_indices(a, /, mode=1)`` -> int
|
||||
Return sum of indices of all active bits in bitarray ``a``.
|
||||
Equivalent to ``sum(i for i, v in enumerate(a) if v)``.
|
||||
``mode=2`` sums square of indices.
|
||||
|
||||
New in version 3.6
|
||||
|
||||
New in version 3.7: add optional mode argument
|
||||
|
||||
|
||||
``urandom(n, /, endian=None)`` -> bitarray
|
||||
Return random bitarray of length ``n`` (uses ``os.urandom()``).
|
||||
|
||||
New in version 1.7
|
||||
|
||||
|
||||
``vl_decode(stream, /, endian=None)`` -> bitarray
|
||||
Decode binary stream (an integer iterator, or bytes-like object), and
|
||||
return the decoded bitarray. This function consumes only one bitarray and
|
||||
leaves the remaining stream untouched. Use ``vl_encode()`` for encoding.
|
||||
|
||||
See also: `Variable length bitarray format <https://github.com/ilanschnell/bitarray/blob/master/doc/variable_length.rst>`__
|
||||
|
||||
New in version 2.2
|
||||
|
||||
|
||||
``vl_encode(bitarray, /)`` -> bytes
|
||||
Return variable length binary representation of bitarray.
|
||||
This representation is useful for efficiently storing small bitarray
|
||||
in a binary stream. Use ``vl_decode()`` for decoding.
|
||||
|
||||
See also: `Variable length bitarray format <https://github.com/ilanschnell/bitarray/blob/master/doc/variable_length.rst>`__
|
||||
|
||||
New in version 2.2
|
||||
|
||||
|
||||
``xor_indices(a, /)`` -> int
|
||||
Return xor reduced indices of all active bits in bitarray ``a``.
|
||||
This is essentially equivalent to
|
||||
``reduce(operator.xor, (i for i, v in enumerate(a) if v))``.
|
||||
|
||||
New in version 3.2
|
||||
|
||||
|
||||
``zeros(n, /, endian=None)`` -> bitarray
|
||||
Create a bitarray of length ``n``, with all values ``0``, and optional
|
||||
bit-endianness (``little`` or ``big``).
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
bitarray-3.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
bitarray-3.8.0.dist-info/METADATA,sha256=SBOMhRFu2Ytaa31ZroCu8u_R-5L5Eqqp4rAfqB4deMY,34266
|
||||
bitarray-3.8.0.dist-info/RECORD,,
|
||||
bitarray-3.8.0.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190
|
||||
bitarray-3.8.0.dist-info/licenses/LICENSE,sha256=pVTCUJCtWl6orTVinaz_u02IcjSwEVUCGKWh-QkOUnQ,2412
|
||||
bitarray-3.8.0.dist-info/top_level.txt,sha256=cLdoeIHotTwh4eHSe8qy8umF7zzyxUM90I8zFb2_xhg,9
|
||||
bitarray/__init__.py,sha256=nxjmA8o5fIVjM9w_PU3nIJK0yZ2QYvPXyfV4nZZvdtQ,2258
|
||||
bitarray/__init__.pyi,sha256=ovj4zeIgw0RTDixYfA0_teL4jhpCM3M7eqTTA-IyZpg,5601
|
||||
bitarray/__pycache__/__init__.cpython-312.pyc,,
|
||||
bitarray/__pycache__/test_bitarray.cpython-312.pyc,,
|
||||
bitarray/__pycache__/test_util.cpython-312.pyc,,
|
||||
bitarray/__pycache__/util.cpython-312.pyc,,
|
||||
bitarray/_bitarray.cpython-312-x86_64-linux-gnu.so,sha256=9mYIOoi-ibj6rMuLmYR7DoK82s0PeDTO9lWgDOWvRcQ,500232
|
||||
bitarray/_util.cpython-312-x86_64-linux-gnu.so,sha256=qb4AiyNbj_zupPjzEXmS2KAgymebbfWmXy-AfeIpemY,262088
|
||||
bitarray/bitarray.h,sha256=lsJh7gRBhE3s0BX7dx7gjkrkYzpS_k7Fg4nl4tctPY0,10840
|
||||
bitarray/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
bitarray/pythoncapi_compat.h,sha256=TF6SIFdR6KYE_FlWc-2JL8GcZGpsdf_LoQSEj0ua0_o,70628
|
||||
bitarray/test_281.pickle,sha256=GaA8vsZmcyZZmIzGDP6ppfaRlsthUxgJ9tMsyjpynoc,442
|
||||
bitarray/test_bitarray.py,sha256=u3RR096Cg7Ru4IWI-m6oSOHkEjPD__Ns6u-yeHPBJes,181127
|
||||
bitarray/test_util.py,sha256=mgfGUQxa3V1XfLwIeplxVIcp7Y5dB9icXcFBuGkFiuY,107652
|
||||
bitarray/util.py,sha256=q4LSTdMMvblC6yK8ZyLgavHUfVtX29XhAQlGbUB598o,21206
|
||||
bitarray/util.pyi,sha256=6Vq5BgdfxJAOoKHcfOyAvd5dzAiC23KokoqVirKHNy0,2947
|
||||
@@ -0,0 +1,7 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: setuptools (80.9.0)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp312-cp312-manylinux_2_17_x86_64
|
||||
Tag: cp312-cp312-manylinux2014_x86_64
|
||||
Tag: cp312-cp312-manylinux_2_28_x86_64
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE
|
||||
----------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between Ilan Schnell, and the Individual or
|
||||
Organization ("Licensee") accessing and otherwise using this software
|
||||
("bitarray") in source or binary form and its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, Ilan Schnell
|
||||
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 bitarray
|
||||
alone or in any derivative version, provided, however, that Ilan Schnell's
|
||||
License Agreement and Ilan Schnell's notice of copyright, i.e., "Copyright (c)
|
||||
2008 - 2025 Ilan Schnell; All Rights Reserved" are retained in bitarray
|
||||
alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates bitarray 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 bitarray.
|
||||
|
||||
4. Ilan Schnell is making bitarray available to Licensee on an "AS IS"
|
||||
basis. ILAN SCHNELL MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, ILAN SCHNELL MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF BITARRAY WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. ILAN SCHNELL SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF BITARRAY
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING BITARRAY,
|
||||
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 Ilan Schnell
|
||||
and Licensee. This License Agreement does not grant permission to use Ilan
|
||||
Schnell 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 bitarray, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
@@ -0,0 +1 @@
|
||||
bitarray
|
||||
65
venv/lib/python3.12/site-packages/bitarray/__init__.py
Normal file
65
venv/lib/python3.12/site-packages/bitarray/__init__.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# Copyright (c) 2008 - 2025, Ilan Schnell; All Rights Reserved
|
||||
"""
|
||||
This package defines an object type which can efficiently represent
|
||||
a bitarray. Bitarrays are sequence types and behave very much like lists.
|
||||
|
||||
Please find a description of this package at:
|
||||
|
||||
https://github.com/ilanschnell/bitarray
|
||||
|
||||
Author: Ilan Schnell
|
||||
"""
|
||||
from collections import namedtuple
|
||||
|
||||
from bitarray._bitarray import (
|
||||
bitarray, decodetree, bits2bytes, _bitarray_reconstructor,
|
||||
get_default_endian, _sysinfo, BITARRAY_VERSION as __version__
|
||||
)
|
||||
|
||||
__all__ = ['bitarray', 'frozenbitarray', 'decodetree', 'bits2bytes']
|
||||
|
||||
BufferInfo = namedtuple('BufferInfo',
|
||||
['address', 'nbytes', 'endian', 'padbits',
|
||||
'alloc', 'readonly', 'imported', 'exports'])
|
||||
|
||||
class frozenbitarray(bitarray):
|
||||
"""frozenbitarray(initializer=0, /, endian='big', buffer=None) -> \
|
||||
frozenbitarray
|
||||
|
||||
Return a `frozenbitarray` object. Initialized the same way a `bitarray`
|
||||
object is initialized. A `frozenbitarray` is immutable and hashable,
|
||||
and may therefore be used as a dictionary key.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self._freeze()
|
||||
|
||||
def __repr__(self):
|
||||
return 'frozen' + bitarray.__repr__(self)
|
||||
|
||||
def __hash__(self):
|
||||
"Return hash(self)."
|
||||
# ensure hash is independent of endianness
|
||||
a = bitarray(self, 'big')
|
||||
return hash((len(a), a.tobytes()))
|
||||
|
||||
# Technically the code below is not necessary, as all these methods will
|
||||
# raise a TypeError on read-only memory. However, with a different error
|
||||
# message.
|
||||
def __delitem__(self, *args, **kwargs):
|
||||
"" # no docstring
|
||||
raise TypeError("frozenbitarray is immutable")
|
||||
|
||||
append = bytereverse = clear = extend = encode = fill = __delitem__
|
||||
frombytes = fromfile = insert = invert = pack = pop = __delitem__
|
||||
remove = reverse = setall = sort = __setitem__ = __delitem__
|
||||
__iadd__ = __iand__ = __imul__ = __ior__ = __ixor__ = __delitem__
|
||||
__ilshift__ = __irshift__ = __delitem__
|
||||
|
||||
|
||||
def test(verbosity=1):
|
||||
"""test(verbosity=1) -> TextTestResult
|
||||
|
||||
Run self-test, and return `unittest.runner.TextTestResult` object.
|
||||
"""
|
||||
from bitarray import test_bitarray
|
||||
return test_bitarray.run(verbosity=verbosity)
|
||||
170
venv/lib/python3.12/site-packages/bitarray/__init__.pyi
Normal file
170
venv/lib/python3.12/site-packages/bitarray/__init__.pyi
Normal file
@@ -0,0 +1,170 @@
|
||||
# Copyright (c) 2021 - 2025, Ilan Schnell; All Rights Reserved
|
||||
#
|
||||
# This stub, as well as util.pyi, are tested with Python 3.10 and mypy 1.11.2
|
||||
|
||||
from collections.abc import Iterable, Iterator, Sequence
|
||||
from unittest.runner import TextTestResult
|
||||
|
||||
from typing import Any, BinaryIO, Dict, Union, overload, NamedTuple
|
||||
|
||||
|
||||
CodeDict = Dict[Any, bitarray]
|
||||
# Python 3.12 has abc.Buffer which should be used instead
|
||||
BytesLike = Union[bytes, bytearray]
|
||||
|
||||
|
||||
class BufferInfo(NamedTuple):
|
||||
address: int
|
||||
nbytes: int
|
||||
endian: str
|
||||
padbits: int
|
||||
alloc: int
|
||||
readonly: bool
|
||||
imported: bool
|
||||
exports: int
|
||||
|
||||
|
||||
class decodetree:
|
||||
def __init__(self, code: CodeDict) -> None: ...
|
||||
def complete(self) -> bool: ...
|
||||
def nodes(self) -> int: ...
|
||||
def todict(self) -> CodeDict: ...
|
||||
|
||||
|
||||
class bitarray:
|
||||
def __init__(self,
|
||||
initializer: Union[int, str, Iterable[int], None] = ...,
|
||||
endian: Union[str, None] = ...,
|
||||
buffer: Any = ...) -> None: ...
|
||||
|
||||
def all(self) -> bool: ...
|
||||
def any(self) -> bool: ...
|
||||
def append(self, value: int) -> None: ...
|
||||
def buffer_info(self) -> BufferInfo: ...
|
||||
def bytereverse(self,
|
||||
start: int = ...,
|
||||
stop: int = ...) -> None: ...
|
||||
|
||||
def clear(self) -> None: ...
|
||||
def copy(self) -> bitarray: ...
|
||||
def count(self,
|
||||
sub_bitarray: Union[bitarray, int] = ...,
|
||||
start: int = ...,
|
||||
stop: int = ...,
|
||||
step: int = ...) -> int: ...
|
||||
|
||||
def encode(self, code: CodeDict, x: Iterable) -> None: ...
|
||||
def decode(self,
|
||||
code: Union[CodeDict, decodetree]) -> Iterator: ...
|
||||
|
||||
def extend(self, x: Union[str, Iterable[int]]) -> None: ...
|
||||
def fill(self) -> int: ...
|
||||
def find(self,
|
||||
sub_bitarray: Union[bitarray, int],
|
||||
start: int = ...,
|
||||
stop: int = ...,
|
||||
right: int = ...) -> int: ...
|
||||
|
||||
def frombytes(self, a: BytesLike) -> None: ...
|
||||
def fromfile(self, f: BinaryIO, n: int = ...) -> None: ...
|
||||
def index(self,
|
||||
sub_bitarray: Union[bitarray, int],
|
||||
start: int = ...,
|
||||
stop: int = ...,
|
||||
right: int = ...) -> int: ...
|
||||
|
||||
def insert(self, i: int, value: int) -> None: ...
|
||||
def invert(self, i: int = ...) -> None: ...
|
||||
|
||||
def search(self,
|
||||
sub_bitarray: Union[bitarray, int],
|
||||
start: int = ...,
|
||||
stop: int = ...,
|
||||
right: int = ...) -> Iterator[int]: ...
|
||||
|
||||
def pack(self, b: BytesLike) -> None: ...
|
||||
def pop(self, i: int = ...) -> int: ...
|
||||
def remove(self, value: int) -> None: ...
|
||||
def reverse(self) -> None: ...
|
||||
|
||||
def setall(self, value: int) -> None: ...
|
||||
def sort(self, reverse: int) -> None: ...
|
||||
def to01(self,
|
||||
group: int = ...,
|
||||
sep: str = ...) -> str: ...
|
||||
def tobytes(self) -> bytes: ...
|
||||
def tofile(self, f: BinaryIO) -> None: ...
|
||||
def tolist(self) -> list[int]: ...
|
||||
def unpack(self,
|
||||
zero: bytes = ...,
|
||||
one: bytes = ...) -> bytes: ...
|
||||
|
||||
def __len__(self) -> int: ...
|
||||
def __iter__(self) -> Iterator[int]: ...
|
||||
@overload
|
||||
def __getitem__(self, i: int) -> int: ...
|
||||
@overload
|
||||
def __getitem__(self,
|
||||
s: Union[slice, bitarray, Sequence]) -> bitarray: ...
|
||||
@overload
|
||||
def __setitem__(self,
|
||||
i: Union[int, slice, Sequence],
|
||||
o: int) -> None: ...
|
||||
@overload
|
||||
def __setitem__(self,
|
||||
s: Union[slice, bitarray, Sequence],
|
||||
o: bitarray) -> None: ...
|
||||
def __delitem__(self,
|
||||
i: Union[int, slice, bitarray, Sequence]) -> None: ...
|
||||
|
||||
def __buffer__(self, flags: int, /) -> memoryview: ...
|
||||
def __release_buffer__(self, buffer: memoryview, /) -> None: ...
|
||||
|
||||
def __add__(self, other: bitarray) -> bitarray: ...
|
||||
def __iadd__(self, other: bitarray) -> bitarray: ...
|
||||
def __mul__(self, n: int) -> bitarray: ...
|
||||
def __imul__(self, n: int) -> bitarray: ...
|
||||
def __rmul__(self, n: int) -> bitarray: ...
|
||||
|
||||
def __ge__(self, other: bitarray) -> bool: ...
|
||||
def __gt__(self, other: bitarray) -> bool: ...
|
||||
def __le__(self, other: bitarray) -> bool: ...
|
||||
def __lt__(self, other: bitarray) -> bool: ...
|
||||
|
||||
def __and__(self, other: bitarray) -> bitarray: ...
|
||||
def __or__(self, other: bitarray) -> bitarray: ...
|
||||
def __xor__(self, other: bitarray) -> bitarray: ...
|
||||
def __iand__(self, other: bitarray) -> bitarray: ...
|
||||
def __ior__(self, other: bitarray) -> bitarray: ...
|
||||
def __ixor__(self, other: bitarray) -> bitarray: ...
|
||||
def __invert__(self) -> bitarray: ...
|
||||
def __lshift__(self, n: int) -> bitarray: ...
|
||||
def __rshift__(self, n: int) -> bitarray: ...
|
||||
def __ilshift__(self, n: int) -> bitarray: ...
|
||||
def __irshift__(self, n: int) -> bitarray: ...
|
||||
|
||||
# data descriptors
|
||||
@property
|
||||
def endian(self) -> str: ...
|
||||
@property
|
||||
def nbytes(self) -> int: ...
|
||||
@property
|
||||
def padbits(self) -> int: ...
|
||||
@property
|
||||
def readonly(self) -> bool: ...
|
||||
|
||||
|
||||
class frozenbitarray(bitarray):
|
||||
def __hash__(self) -> int: ...
|
||||
|
||||
|
||||
__version__: str
|
||||
def bits2bytes(n: int) -> int: ...
|
||||
def get_default_endian() -> str: ...
|
||||
def test(verbosity: int = ...) -> TextTestResult: ...
|
||||
def _sysinfo(key: str) -> int: ...
|
||||
def _bitarray_reconstructor(cls: type,
|
||||
buffer: bytes,
|
||||
endian: str,
|
||||
padbits: int,
|
||||
readonly: int) -> bitarray: ...
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
venv/lib/python3.12/site-packages/bitarray/_util.cpython-312-x86_64-linux-gnu.so
Executable file
BIN
venv/lib/python3.12/site-packages/bitarray/_util.cpython-312-x86_64-linux-gnu.so
Executable file
Binary file not shown.
354
venv/lib/python3.12/site-packages/bitarray/bitarray.h
Normal file
354
venv/lib/python3.12/site-packages/bitarray/bitarray.h
Normal file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
Copyright (c) 2008 - 2025, Ilan Schnell; All Rights Reserved
|
||||
bitarray is published under the PSF license.
|
||||
|
||||
Author: Ilan Schnell
|
||||
*/
|
||||
#define BITARRAY_VERSION "3.8.0"
|
||||
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_SYS_TYPES_H
|
||||
# include <sys/types.h> /* For size_t */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Compatibility with Visual Studio 2013 and older which don't support
|
||||
the inline keyword in C (only in C++): use __inline instead.
|
||||
(copied from pythoncapi_compat.h) */
|
||||
#if (defined(_MSC_VER) && _MSC_VER < 1900 \
|
||||
&& !defined(__cplusplus) && !defined(inline))
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h> /* For _byteswap_uint64() */
|
||||
#endif
|
||||
|
||||
/* --- definitions specific to Python --- */
|
||||
|
||||
/* Py_UNREACHABLE was introduced in Python 3.7 */
|
||||
#ifndef Py_UNREACHABLE
|
||||
#define Py_UNREACHABLE() assert(0)
|
||||
#endif
|
||||
|
||||
/* --- bitarrayobject --- */
|
||||
|
||||
/* .ob_size is the buffer size (in bytes), not the number of elements.
|
||||
The number of elements (bits) is .nbits. */
|
||||
typedef struct {
|
||||
PyObject_VAR_HEAD
|
||||
char *ob_item; /* buffer */
|
||||
Py_ssize_t allocated; /* allocated buffer size (in bytes) */
|
||||
Py_ssize_t nbits; /* length of bitarray, i.e. elements */
|
||||
int endian; /* bit-endianness of bitarray */
|
||||
int ob_exports; /* how many buffer exports */
|
||||
PyObject *weakreflist; /* list of weak references */
|
||||
Py_buffer *buffer; /* used when importing a buffer */
|
||||
int readonly; /* buffer is readonly */
|
||||
} bitarrayobject;
|
||||
|
||||
/* --- bit-endianness --- */
|
||||
#define ENDIAN_LITTLE 0
|
||||
#define ENDIAN_BIG 1
|
||||
/* default bit-endianness */
|
||||
#define ENDIAN_DEFAULT ENDIAN_BIG
|
||||
|
||||
#define IS_LE(self) ((self)->endian == ENDIAN_LITTLE)
|
||||
#define IS_BE(self) ((self)->endian == ENDIAN_BIG)
|
||||
|
||||
/* endianness as string */
|
||||
#define ENDIAN_STR(endian) ((endian) == ENDIAN_LITTLE ? "little" : "big")
|
||||
|
||||
/* number of pad bits */
|
||||
#define PADBITS(self) ((8 - (self)->nbits % 8) % 8)
|
||||
|
||||
/* number of bytes necessary to store given nunmber of bits */
|
||||
#define BYTES(bits) (((bits) + 7) >> 3)
|
||||
|
||||
/* we're not using bitmask_table here, as it is actually slower */
|
||||
#define BITMASK(self, i) (((char) 1) << ((self)->endian == ENDIAN_LITTLE ? \
|
||||
((i) % 8) : (7 - (i) % 8)))
|
||||
|
||||
/* buffer as uint64 array */
|
||||
#define WBUFF(self) ((uint64_t *) (self)->ob_item)
|
||||
|
||||
/* assert that .nbits is in agreement with .ob_size */
|
||||
#define assert_nbits(self) assert(BYTES((self)->nbits) == Py_SIZE(self))
|
||||
|
||||
/* assert byte index is in range */
|
||||
#define assert_byte_in_range(self, j) \
|
||||
assert(self->ob_item && 0 <= (j) && (j) < Py_SIZE(self))
|
||||
|
||||
/* ------------ low level access to bits in bitarrayobject ------------- */
|
||||
|
||||
static inline int
|
||||
getbit(bitarrayobject *self, Py_ssize_t i)
|
||||
{
|
||||
assert_nbits(self);
|
||||
assert(0 <= i && i < self->nbits);
|
||||
return self->ob_item[i >> 3] & BITMASK(self, i) ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
setbit(bitarrayobject *self, Py_ssize_t i, int vi)
|
||||
{
|
||||
char *cp, mask;
|
||||
|
||||
assert_nbits(self);
|
||||
assert(0 <= i && i < self->nbits);
|
||||
assert(self->readonly == 0);
|
||||
|
||||
mask = BITMASK(self, i);
|
||||
cp = self->ob_item + (i >> 3);
|
||||
if (vi)
|
||||
*cp |= mask;
|
||||
else
|
||||
*cp &= ~mask;
|
||||
}
|
||||
|
||||
static const char bitmask_table[2][8] = {
|
||||
{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}, /* little endian */
|
||||
{0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}, /* big endian */
|
||||
};
|
||||
|
||||
/* character with n leading ones is: ones_table[endian][n] */
|
||||
static const char ones_table[2][8] = {
|
||||
{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f}, /* little endian */
|
||||
{0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe}, /* big endian */
|
||||
};
|
||||
|
||||
/* Return last byte in buffer with pad bits zeroed out.
|
||||
If the length of the bitarray is a multiple of 8 (which includes an empty
|
||||
bitarray), 0 is returned. */
|
||||
static inline char
|
||||
zlc(bitarrayobject *self) /* zlc = zeroed last char */
|
||||
{
|
||||
const int r = self->nbits % 8; /* index into mask table */
|
||||
|
||||
if (r == 0)
|
||||
return 0;
|
||||
return self->ob_item[Py_SIZE(self) - 1] & ones_table[IS_BE(self)][r];
|
||||
}
|
||||
|
||||
/* Return a uint64_t word representing the last (up to 63) remaining bits
|
||||
of the buffer. All missing bytes (to complete the word) and padbits are
|
||||
treated as zeros.
|
||||
If the length of the bitarray is a multiple of 64 (which also includes
|
||||
an empty bitarray), 0 is returned. */
|
||||
static inline uint64_t
|
||||
zlw(bitarrayobject *self) /* zlw = zeroed last word */
|
||||
{
|
||||
const size_t nbits = self->nbits;
|
||||
const size_t nw = (nbits / 64) * 8; /* bytes in complete words */
|
||||
const size_t nr = (nbits % 64) / 8; /* complete remaining bytes */
|
||||
uint64_t res = 0;
|
||||
|
||||
assert(nw + nr == nbits / 8 && 8 * (nw + nr) + nbits % 8 == nbits);
|
||||
memcpy((char *) &res, self->ob_item + nw, nr);
|
||||
if (nbits % 8)
|
||||
*(((char *) &res) + nr) = zlc(self);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* unless buffer is readonly, zero out pad bits - self->nbits is unchanged */
|
||||
static inline void
|
||||
set_padbits(bitarrayobject *self)
|
||||
{
|
||||
if (self->readonly == 0) {
|
||||
int r = self->nbits % 8; /* index into mask table */
|
||||
if (r)
|
||||
self->ob_item[Py_SIZE(self) - 1] &= ones_table[IS_BE(self)][r];
|
||||
}
|
||||
}
|
||||
|
||||
/* population count - number of 1's in uint64 */
|
||||
static inline int
|
||||
popcnt_64(uint64_t x)
|
||||
{
|
||||
#if (defined(__clang__) || defined(__GNUC__))
|
||||
return __builtin_popcountll(x);
|
||||
#else
|
||||
/* https://en.wikipedia.org/wiki/Hamming_weight popcount64c */
|
||||
const uint64_t m1 = 0x5555555555555555;
|
||||
const uint64_t m2 = 0x3333333333333333;
|
||||
const uint64_t m4 = 0x0f0f0f0f0f0f0f0f;
|
||||
const uint64_t h01 = 0x0101010101010101;
|
||||
|
||||
x -= (x >> 1) & m1;
|
||||
x = (x & m2) + ((x >> 2) & m2);
|
||||
x = (x + (x >> 4)) & m4;
|
||||
return (x * h01) >> 56;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int
|
||||
parity_64(uint64_t x)
|
||||
{
|
||||
#if (defined(__clang__) || defined(__GNUC__))
|
||||
return __builtin_parityll(x);
|
||||
#else
|
||||
int i;
|
||||
for (i = 32; i > 0; i /= 2)
|
||||
x ^= x >> i;
|
||||
return x & 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
builtin_bswap64(uint64_t word)
|
||||
{
|
||||
#if (defined(__clang__) || \
|
||||
(defined(__GNUC__) \
|
||||
&& ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))))
|
||||
/* __builtin_bswap64() is available since GCC 4.3 */
|
||||
# define HAVE_BUILTIN_BSWAP64 1
|
||||
return __builtin_bswap64(word);
|
||||
#elif defined(_MSC_VER)
|
||||
# define HAVE_BUILTIN_BSWAP64 1
|
||||
return _byteswap_uint64(word);
|
||||
#else
|
||||
# define HAVE_BUILTIN_BSWAP64 0
|
||||
Py_UNREACHABLE();
|
||||
#endif
|
||||
}
|
||||
|
||||
/* reverse order of first n bytes of p */
|
||||
static inline void
|
||||
swap_bytes(char *p, Py_ssize_t n)
|
||||
{
|
||||
Py_ssize_t i, j;
|
||||
for (i = 0, j = n - 1; i < j; i++, j--) {
|
||||
char t = p[i];
|
||||
p[i] = p[j];
|
||||
p[j] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* write 256 characters into table for given kernel operation */
|
||||
static inline void
|
||||
setup_table(char *table, char kop)
|
||||
{
|
||||
int k;
|
||||
for (k = 0; k < 256; k++) {
|
||||
char t = 0, j;
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (k & 1 << j) {
|
||||
/* j are the indices of active bits in k (little endian) */
|
||||
switch (kop) {
|
||||
case 'a': t += j; break; /* add active indices */
|
||||
case 'A': t += 7 - j; break; /* 'a' for big endian */
|
||||
case 's': t += j * j; /* add squares of active indices */
|
||||
break;
|
||||
case 'S': t += (7-j) * (7-j); /* 's' for big endian */
|
||||
break;
|
||||
case 'x': t ^= j; break; /* xor active indices */
|
||||
case 'X': t ^= 7 - j; break; /* 'x' for big endian */
|
||||
case 'c': t++; break; /* bit count */
|
||||
case 'p': t ^= 1; break; /* parity */
|
||||
case 'r': t |= 128 >> j; break; /* reverse bits */
|
||||
default: Py_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
table[k] = t;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return distance [0..3] to next aligned pointer.
|
||||
While on modern compilers uint64_t pointers may be misaligned, it may
|
||||
cause problems on older ones. Moreover, it may lead to slowdown (even
|
||||
on modern compilers). */
|
||||
static inline int
|
||||
to_aligned(void *p)
|
||||
{
|
||||
int r = ((uintptr_t) p) % 4;
|
||||
return (4 - r) % 4;
|
||||
}
|
||||
|
||||
/* population count of n words starting from at uint64_t pointer w */
|
||||
static inline Py_ssize_t
|
||||
popcnt_words(uint64_t *w, Py_ssize_t n)
|
||||
{
|
||||
Py_ssize_t cnt = 0;
|
||||
|
||||
assert(n >= 0 && ((uintptr_t) w) % 4 == 0);
|
||||
while (n--)
|
||||
cnt += popcnt_64(*w++);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* Adjust slice parameters such that step is always positive.
|
||||
This produces simpler loops over elements when their order is irrelevant.
|
||||
Moreover, for step = -1, we can now use set_span() in set_range() and
|
||||
count_span() in count_range().
|
||||
*/
|
||||
static inline void
|
||||
adjust_step_positive(Py_ssize_t slicelength,
|
||||
Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step)
|
||||
{
|
||||
if (*step < 0) {
|
||||
*stop = *start + 1;
|
||||
*start = *stop + *step * (slicelength - 1) - 1;
|
||||
*step = -(*step);
|
||||
}
|
||||
assert(*start >= 0 && *stop >= 0 && *step > 0 && slicelength >= 0);
|
||||
/* slicelength == 0 implies stop <= start */
|
||||
assert(slicelength != 0 || *stop <= *start);
|
||||
/* step == 1 and slicelength != 0 implies stop - start == slicelength */
|
||||
assert(*step != 1 || slicelength == 0 || *stop - *start == slicelength);
|
||||
}
|
||||
|
||||
/* convert Python object to C int and set value at address -
|
||||
return 1 on success, 0 on failure (and set exception) */
|
||||
static inline int
|
||||
conv_pybit(PyObject *value, int *vi)
|
||||
{
|
||||
Py_ssize_t n;
|
||||
|
||||
n = PyNumber_AsSsize_t(value, NULL);
|
||||
if (n == -1 && PyErr_Occurred())
|
||||
return 0;
|
||||
|
||||
if (n >> 1) {
|
||||
PyErr_Format(PyExc_ValueError, "bit must be 0 or 1, got %zd", n);
|
||||
return 0;
|
||||
}
|
||||
*vi = (int) n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return 0 if bitarrays have equal length and bit-endianness.
|
||||
Otherwise, set exception and return -1. */
|
||||
static inline int
|
||||
ensure_eq_size_endian(bitarrayobject *a, bitarrayobject *b)
|
||||
{
|
||||
if (a->nbits != b->nbits) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"bitarrays of equal length expected");
|
||||
return -1;
|
||||
}
|
||||
if (a->endian != b->endian) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"bitarrays of equal bit-endianness expected");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Equivalent to: import bitarray; return getattr(bitarray, name) */
|
||||
static inline PyObject *
|
||||
bitarray_module_attr(char *name)
|
||||
{
|
||||
PyObject *bitarray_module, *result;
|
||||
|
||||
bitarray_module = PyImport_ImportModule("bitarray");
|
||||
if (bitarray_module == NULL)
|
||||
return NULL;
|
||||
|
||||
result = PyObject_GetAttrString(bitarray_module, name);
|
||||
Py_DECREF(bitarray_module);
|
||||
return result;
|
||||
}
|
||||
0
venv/lib/python3.12/site-packages/bitarray/py.typed
Normal file
0
venv/lib/python3.12/site-packages/bitarray/py.typed
Normal file
2594
venv/lib/python3.12/site-packages/bitarray/pythoncapi_compat.h
Normal file
2594
venv/lib/python3.12/site-packages/bitarray/pythoncapi_compat.h
Normal file
File diff suppressed because it is too large
Load Diff
BIN
venv/lib/python3.12/site-packages/bitarray/test_281.pickle
Normal file
BIN
venv/lib/python3.12/site-packages/bitarray/test_281.pickle
Normal file
Binary file not shown.
5180
venv/lib/python3.12/site-packages/bitarray/test_bitarray.py
Normal file
5180
venv/lib/python3.12/site-packages/bitarray/test_bitarray.py
Normal file
File diff suppressed because it is too large
Load Diff
2894
venv/lib/python3.12/site-packages/bitarray/test_util.py
Normal file
2894
venv/lib/python3.12/site-packages/bitarray/test_util.py
Normal file
File diff suppressed because it is too large
Load Diff
632
venv/lib/python3.12/site-packages/bitarray/util.py
Normal file
632
venv/lib/python3.12/site-packages/bitarray/util.py
Normal file
@@ -0,0 +1,632 @@
|
||||
# Copyright (c) 2019 - 2025, Ilan Schnell; All Rights Reserved
|
||||
# bitarray is published under the PSF license.
|
||||
#
|
||||
# Author: Ilan Schnell
|
||||
"""
|
||||
Useful utilities for working with bitarrays.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
import random
|
||||
|
||||
from bitarray import bitarray, bits2bytes
|
||||
|
||||
from bitarray._util import (
|
||||
zeros, ones, count_n, parity, _ssqi, xor_indices,
|
||||
count_and, count_or, count_xor, any_and, subset,
|
||||
correspond_all, byteswap,
|
||||
serialize, deserialize,
|
||||
ba2hex, hex2ba,
|
||||
ba2base, base2ba,
|
||||
sc_encode, sc_decode,
|
||||
vl_encode, vl_decode,
|
||||
canonical_decode,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'zeros', 'ones', 'urandom', 'random_k', 'random_p', 'gen_primes',
|
||||
'pprint', 'strip', 'count_n',
|
||||
'parity', 'sum_indices', 'xor_indices',
|
||||
'count_and', 'count_or', 'count_xor', 'any_and', 'subset',
|
||||
'correspond_all', 'byteswap', 'intervals',
|
||||
'ba2hex', 'hex2ba',
|
||||
'ba2base', 'base2ba',
|
||||
'ba2int', 'int2ba',
|
||||
'serialize', 'deserialize',
|
||||
'sc_encode', 'sc_decode',
|
||||
'vl_encode', 'vl_decode',
|
||||
'huffman_code', 'canonical_huffman', 'canonical_decode',
|
||||
]
|
||||
|
||||
|
||||
def urandom(__length, endian=None):
|
||||
"""urandom(n, /, endian=None) -> bitarray
|
||||
|
||||
Return random bitarray of length `n` (uses `os.urandom()`).
|
||||
"""
|
||||
a = bitarray(os.urandom(bits2bytes(__length)), endian)
|
||||
del a[__length:]
|
||||
return a
|
||||
|
||||
|
||||
def random_k(__n, k, endian=None):
|
||||
"""random_k(n, /, k, endian=None) -> bitarray
|
||||
|
||||
Return (pseudo-) random bitarray of length `n` with `k` elements
|
||||
set to one. Mathematically equivalent to setting (in a bitarray of
|
||||
length `n`) all bits at indices `random.sample(range(n), k)` to one.
|
||||
The random bitarrays are reproducible when giving Python's `random.seed()`
|
||||
a specific seed value.
|
||||
"""
|
||||
r = _Random(__n, endian)
|
||||
if not isinstance(k, int):
|
||||
raise TypeError("int expected, got '%s'" % type(k).__name__)
|
||||
|
||||
return r.random_k(k)
|
||||
|
||||
|
||||
def random_p(__n, p=0.5, endian=None):
|
||||
"""random_p(n, /, p=0.5, endian=None) -> bitarray
|
||||
|
||||
Return (pseudo-) random bitarray of length `n`, where each bit has
|
||||
probability `p` of being one (independent of any other bits). Mathematically
|
||||
equivalent to `bitarray((random() < p for _ in range(n)), endian)`, but much
|
||||
faster for large `n`. The random bitarrays are reproducible when giving
|
||||
Python's `random.seed()` with a specific seed value.
|
||||
|
||||
This function requires Python 3.12 or higher, as it depends on the standard
|
||||
library function `random.binomialvariate()`. Raises `NotImplementedError`
|
||||
when Python version is too low.
|
||||
"""
|
||||
if sys.version_info[:2] < (3, 12):
|
||||
raise NotImplementedError("bitarray.util.random_p() requires "
|
||||
"Python 3.12 or higher")
|
||||
r = _Random(__n, endian)
|
||||
return r.random_p(p)
|
||||
|
||||
|
||||
class _Random:
|
||||
|
||||
# The main reason for this class it to enable testing functionality
|
||||
# individually in the test class Random_P_Tests in 'test_util.py'.
|
||||
# The test class also contains many comments and explanations.
|
||||
# To better understand how the algorithm works, see ./doc/random_p.rst
|
||||
# See also, VerificationTests in devel/test_random.py
|
||||
|
||||
# maximal number of calls to .random_half() in .combine()
|
||||
M = 8
|
||||
|
||||
# number of resulting probability intervals
|
||||
K = 1 << M
|
||||
|
||||
# limit for setting individual bits randomly
|
||||
SMALL_P = 0.01
|
||||
|
||||
def __init__(self, n=0, endian=None):
|
||||
self.n = n
|
||||
self.nbytes = bits2bytes(n)
|
||||
self.endian = endian
|
||||
|
||||
def random_half(self):
|
||||
"""
|
||||
Return bitarray with each bit having probability p = 1/2 of being 1.
|
||||
"""
|
||||
nbytes = self.nbytes
|
||||
# use random module function for reproducibility (not urandom())
|
||||
b = random.getrandbits(8 * nbytes).to_bytes(nbytes, 'little')
|
||||
a = bitarray(b, self.endian)
|
||||
del a[self.n:]
|
||||
return a
|
||||
|
||||
def op_seq(self, i):
|
||||
"""
|
||||
Return bitarray containing operator sequence.
|
||||
Each item represents a bitwise operation: 0: AND 1: OR
|
||||
After applying the sequence (see .combine_half()), we
|
||||
obtain a bitarray with probability q = i / K
|
||||
"""
|
||||
if not 0 < i < self.K:
|
||||
raise ValueError("0 < i < %d, got i = %d" % (self.K, i))
|
||||
|
||||
# sequence of &, | operations - least significant operations first
|
||||
a = bitarray(i.to_bytes(2, byteorder="little"), "little")
|
||||
return a[a.index(1) + 1 : self.M]
|
||||
|
||||
def combine_half(self, seq):
|
||||
"""
|
||||
Combine random bitarrays with probability 1/2
|
||||
according to given operator sequence.
|
||||
"""
|
||||
a = self.random_half()
|
||||
for k in seq:
|
||||
if k:
|
||||
a |= self.random_half()
|
||||
else:
|
||||
a &= self.random_half()
|
||||
return a
|
||||
|
||||
def random_k(self, k):
|
||||
n = self.n
|
||||
# error check inputs and handle edge cases
|
||||
if k <= 0 or k >= n:
|
||||
if k == 0:
|
||||
return zeros(n, self.endian)
|
||||
if k == n:
|
||||
return ones(n, self.endian)
|
||||
raise ValueError("k must be in range 0 <= k <= n, got %s" % k)
|
||||
|
||||
# exploit symmetry to establish: k <= n // 2
|
||||
if k > n // 2:
|
||||
a = self.random_k(n - k)
|
||||
a.invert() # use in-place to avoid copying
|
||||
return a
|
||||
|
||||
# decide on sequence, see VerificationTests devel/test_random.py
|
||||
if k < 16 or k * self.K < 3 * n:
|
||||
i = 0
|
||||
else:
|
||||
p = k / n # p <= 0.5
|
||||
p -= (0.2 - 0.4 * p) / math.sqrt(n)
|
||||
i = int(p * (self.K + 1))
|
||||
|
||||
# combine random bitarrays using bitwise AND and OR operations
|
||||
if i < 3:
|
||||
a = zeros(n, self.endian)
|
||||
diff = -k
|
||||
else:
|
||||
a = self.combine_half(self.op_seq(i))
|
||||
diff = a.count() - k
|
||||
|
||||
randrange = random.randrange
|
||||
if diff < 0: # not enough bits 1 - increase count
|
||||
for _ in range(-diff):
|
||||
i = randrange(n)
|
||||
while a[i]:
|
||||
i = randrange(n)
|
||||
a[i] = 1
|
||||
elif diff > 0: # too many bits 1 - decrease count
|
||||
for _ in range(diff):
|
||||
i = randrange(n)
|
||||
while not a[i]:
|
||||
i = randrange(n)
|
||||
a[i] = 0
|
||||
|
||||
return a
|
||||
|
||||
def random_p(self, p):
|
||||
# error check inputs and handle edge cases
|
||||
if p <= 0.0 or p == 0.5 or p >= 1.0:
|
||||
if p == 0.0:
|
||||
return zeros(self.n, self.endian)
|
||||
if p == 0.5:
|
||||
return self.random_half()
|
||||
if p == 1.0:
|
||||
return ones(self.n, self.endian)
|
||||
raise ValueError("p must be in range 0.0 <= p <= 1.0, got %s" % p)
|
||||
|
||||
# for small n, use literal definition
|
||||
if self.n < 16:
|
||||
return bitarray((random.random() < p for _ in range(self.n)),
|
||||
self.endian)
|
||||
|
||||
# exploit symmetry to establish: p < 0.5
|
||||
if p > 0.5:
|
||||
a = self.random_p(1.0 - p)
|
||||
a.invert() # use in-place to avoid copying
|
||||
return a
|
||||
|
||||
# for small p, set randomly individual bits
|
||||
if p < self.SMALL_P:
|
||||
return self.random_k(random.binomialvariate(self.n, p))
|
||||
|
||||
# calculate operator sequence
|
||||
i = int(p * self.K)
|
||||
if p * (self.K + 1) > i + 1: # see devel/test_random.py
|
||||
i += 1
|
||||
seq = self.op_seq(i)
|
||||
q = i / self.K
|
||||
|
||||
# when n is small compared to number of operations, also use literal
|
||||
if self.n < 100 and self.nbytes <= len(seq) + 3 * bool(q != p):
|
||||
return bitarray((random.random() < p for _ in range(self.n)),
|
||||
self.endian)
|
||||
|
||||
# combine random bitarrays using bitwise AND and OR operations
|
||||
a = self.combine_half(seq)
|
||||
if q < p:
|
||||
x = (p - q) / (1.0 - q)
|
||||
a |= self.random_p(x)
|
||||
elif q > p:
|
||||
x = p / q
|
||||
a &= self.random_p(x)
|
||||
|
||||
return a
|
||||
|
||||
|
||||
def gen_primes(__n, endian=None, odd=False):
|
||||
"""gen_primes(n, /, endian=None, odd=False) -> bitarray
|
||||
|
||||
Generate a bitarray of length `n` in which active indices are prime numbers.
|
||||
By default (`odd=False`), active indices correspond to prime numbers directly.
|
||||
When `odd=True`, only odd prime numbers are represented in the resulting
|
||||
bitarray `a`, and `a[i]` corresponds to `2*i+1` being prime or not.
|
||||
"""
|
||||
n = int(__n)
|
||||
if n < 0:
|
||||
raise ValueError("bitarray length must be >= 0")
|
||||
|
||||
if odd:
|
||||
a = ones(105, endian) # 105 = 3 * 5 * 7
|
||||
a[1::3] = 0
|
||||
a[2::5] = 0
|
||||
a[3::7] = 0
|
||||
f = "01110110"
|
||||
else:
|
||||
a = ones(210, endian) # 210 = 2 * 3 * 5 * 7
|
||||
for i in 2, 3, 5, 7:
|
||||
a[::i] = 0
|
||||
f = "00110101"
|
||||
|
||||
# repeating the array many times is faster than setting the multiples
|
||||
# of the low primes to 0
|
||||
a *= (n + len(a) - 1) // len(a)
|
||||
a[:8] = bitarray(f, endian)
|
||||
del a[n:]
|
||||
# perform sieve starting at 11
|
||||
if odd:
|
||||
for i in a.search(1, 5, int(math.sqrt(n // 2) + 1.0)): # 11//2 = 5
|
||||
j = 2 * i + 1
|
||||
a[(j * j) // 2 :: j] = 0
|
||||
else:
|
||||
# i*i is always odd, and even bits are already set to 0: use step 2*i
|
||||
for i in a.search(1, 11, int(math.sqrt(n) + 1.0)):
|
||||
a[i * i :: 2 * i] = 0
|
||||
return a
|
||||
|
||||
|
||||
def sum_indices(__a, mode=1):
|
||||
"""sum_indices(a, /, mode=1) -> int
|
||||
|
||||
Return sum of indices of all active bits in bitarray `a`.
|
||||
Equivalent to `sum(i for i, v in enumerate(a) if v)`.
|
||||
`mode=2` sums square of indices.
|
||||
"""
|
||||
if mode not in (1, 2):
|
||||
raise ValueError("unexpected mode %r" % mode)
|
||||
|
||||
# For details see: devel/test_sum_indices.py
|
||||
n = 1 << 19 # block size 512 Kbits
|
||||
if len(__a) <= n: # shortcut for single block
|
||||
return _ssqi(__a, mode)
|
||||
|
||||
# Constants
|
||||
m = n // 8 # block size in bytes
|
||||
o1 = n * (n - 1) // 2
|
||||
o2 = o1 * (2 * n - 1) // 3
|
||||
|
||||
nblocks = (len(__a) + n - 1) // n
|
||||
padbits = __a.padbits
|
||||
sm = 0
|
||||
for i in range(nblocks):
|
||||
# use memoryview to avoid copying memory
|
||||
v = memoryview(__a)[i * m : (i + 1) * m]
|
||||
block = bitarray(None, __a.endian, buffer=v)
|
||||
if padbits and i == nblocks - 1:
|
||||
if block.readonly:
|
||||
block = bitarray(block)
|
||||
block[-padbits:] = 0
|
||||
|
||||
k = block.count()
|
||||
if k:
|
||||
y = n * i
|
||||
z1 = o1 if k == n else _ssqi(block)
|
||||
if mode == 1:
|
||||
sm += k * y + z1
|
||||
else:
|
||||
z2 = o2 if k == n else _ssqi(block, 2)
|
||||
sm += (k * y + 2 * z1) * y + z2
|
||||
|
||||
return sm
|
||||
|
||||
|
||||
def pprint(__a, stream=None, group=8, indent=4, width=80):
|
||||
"""pprint(bitarray, /, stream=None, group=8, indent=4, width=80)
|
||||
|
||||
Pretty-print bitarray object to `stream`, defaults is `sys.stdout`.
|
||||
By default, bits are grouped in bytes (8 bits), and 64 bits per line.
|
||||
Non-bitarray objects are printed using `pprint.pprint()`.
|
||||
"""
|
||||
if stream is None:
|
||||
stream = sys.stdout
|
||||
|
||||
if not isinstance(__a, bitarray):
|
||||
import pprint as _pprint
|
||||
_pprint.pprint(__a, stream=stream, indent=indent, width=width)
|
||||
return
|
||||
|
||||
group = int(group)
|
||||
if group < 1:
|
||||
raise ValueError('group must be >= 1')
|
||||
indent = int(indent)
|
||||
if indent < 0:
|
||||
raise ValueError('indent must be >= 0')
|
||||
width = int(width)
|
||||
if width <= indent:
|
||||
raise ValueError('width must be > %d (indent)' % indent)
|
||||
|
||||
gpl = (width - indent) // (group + 1) # groups per line
|
||||
epl = group * gpl # elements per line
|
||||
if epl == 0:
|
||||
epl = width - indent - 2
|
||||
type_name = type(__a).__name__
|
||||
# here 4 is len("'()'")
|
||||
multiline = len(type_name) + 4 + len(__a) + len(__a) // group >= width
|
||||
if multiline:
|
||||
quotes = "'''"
|
||||
elif __a:
|
||||
quotes = "'"
|
||||
else:
|
||||
quotes = ""
|
||||
|
||||
stream.write("%s(%s" % (type_name, quotes))
|
||||
for i, b in enumerate(__a):
|
||||
if multiline and i % epl == 0:
|
||||
stream.write('\n%s' % (indent * ' '))
|
||||
if i % group == 0 and i % epl != 0:
|
||||
stream.write(' ')
|
||||
stream.write(str(b))
|
||||
|
||||
if multiline:
|
||||
stream.write('\n')
|
||||
|
||||
stream.write("%s)\n" % quotes)
|
||||
stream.flush()
|
||||
|
||||
|
||||
def strip(__a, mode='right'):
|
||||
"""strip(bitarray, /, mode='right') -> bitarray
|
||||
|
||||
Return a new bitarray with zeros stripped from left, right or both ends.
|
||||
Allowed values for mode are the strings: `left`, `right`, `both`
|
||||
"""
|
||||
if not isinstance(mode, str):
|
||||
raise TypeError("str expected for mode, got '%s'" %
|
||||
type(__a).__name__)
|
||||
if mode not in ('left', 'right', 'both'):
|
||||
raise ValueError("mode must be 'left', 'right' or 'both', got %r" %
|
||||
mode)
|
||||
|
||||
start = None if mode == 'right' else __a.find(1)
|
||||
if start == -1:
|
||||
return __a[:0]
|
||||
stop = None if mode == 'left' else __a.find(1, right=1) + 1
|
||||
return __a[start:stop]
|
||||
|
||||
|
||||
def intervals(__a):
|
||||
"""intervals(bitarray, /) -> iterator
|
||||
|
||||
Compute all uninterrupted intervals of 1s and 0s, and return an
|
||||
iterator over tuples `(value, start, stop)`. The intervals are guaranteed
|
||||
to be in order, and their size is always non-zero (`stop - start > 0`).
|
||||
"""
|
||||
try:
|
||||
value = __a[0] # value of current interval
|
||||
except IndexError:
|
||||
return
|
||||
n = len(__a)
|
||||
stop = 0 # "previous" stop - becomes next start
|
||||
|
||||
while stop < n:
|
||||
start = stop
|
||||
# assert __a[start] == value
|
||||
try: # find next occurrence of opposite value
|
||||
stop = __a.index(not value, start)
|
||||
except ValueError:
|
||||
stop = n
|
||||
yield int(value), start, stop
|
||||
value = not value # next interval has opposite value
|
||||
|
||||
|
||||
def ba2int(__a, signed=False):
|
||||
"""ba2int(bitarray, /, signed=False) -> int
|
||||
|
||||
Convert the given bitarray to an integer.
|
||||
The bit-endianness of the bitarray is respected.
|
||||
`signed` indicates whether two's complement is used to represent the integer.
|
||||
"""
|
||||
if not isinstance(__a, bitarray):
|
||||
raise TypeError("bitarray expected, got '%s'" % type(__a).__name__)
|
||||
length = len(__a)
|
||||
if length == 0:
|
||||
raise ValueError("non-empty bitarray expected")
|
||||
|
||||
if __a.padbits:
|
||||
pad = zeros(__a.padbits, __a.endian)
|
||||
__a = __a + pad if __a.endian == "little" else pad + __a
|
||||
|
||||
res = int.from_bytes(__a.tobytes(), byteorder=__a.endian)
|
||||
|
||||
if signed and res >> length - 1:
|
||||
res -= 1 << length
|
||||
return res
|
||||
|
||||
|
||||
def int2ba(__i, length=None, endian=None, signed=False):
|
||||
"""int2ba(int, /, length=None, endian=None, signed=False) -> bitarray
|
||||
|
||||
Convert the given integer to a bitarray (with given bit-endianness,
|
||||
and no leading (big-endian) / trailing (little-endian) zeros), unless
|
||||
the `length` of the bitarray is provided. An `OverflowError` is raised
|
||||
if the integer is not representable with the given number of bits.
|
||||
`signed` determines whether two's complement is used to represent the integer,
|
||||
and requires `length` to be provided.
|
||||
"""
|
||||
if not isinstance(__i, int):
|
||||
raise TypeError("int expected, got '%s'" % type(__i).__name__)
|
||||
if length is not None:
|
||||
if not isinstance(length, int):
|
||||
raise TypeError("int expected for argument 'length'")
|
||||
if length <= 0:
|
||||
raise ValueError("length must be > 0")
|
||||
|
||||
if signed:
|
||||
if length is None:
|
||||
raise TypeError("signed requires argument 'length'")
|
||||
m = 1 << length - 1
|
||||
if not (-m <= __i < m):
|
||||
raise OverflowError("signed integer not in range(%d, %d), "
|
||||
"got %d" % (-m, m, __i))
|
||||
if __i < 0:
|
||||
__i += 1 << length
|
||||
else: # unsigned
|
||||
if length and __i >> length:
|
||||
raise OverflowError("unsigned integer not in range(0, %d), "
|
||||
"got %d" % (1 << length, __i))
|
||||
|
||||
a = bitarray(0, endian)
|
||||
b = __i.to_bytes(bits2bytes(__i.bit_length()), byteorder=a.endian)
|
||||
a.frombytes(b)
|
||||
le = a.endian == 'little'
|
||||
if length is None:
|
||||
return strip(a, 'right' if le else 'left') if a else a + '0'
|
||||
|
||||
if len(a) > length:
|
||||
return a[:length] if le else a[-length:]
|
||||
if len(a) == length:
|
||||
return a
|
||||
# len(a) < length, we need padding
|
||||
pad = zeros(length - len(a), a.endian)
|
||||
return a + pad if le else pad + a
|
||||
|
||||
# ------------------------------ Huffman coding -----------------------------
|
||||
|
||||
def _huffman_tree(__freq_map):
|
||||
"""_huffman_tree(dict, /) -> Node
|
||||
|
||||
Given a dict mapping symbols to their frequency, construct a Huffman tree
|
||||
and return its root node.
|
||||
"""
|
||||
from heapq import heappush, heappop
|
||||
|
||||
class Node(object):
|
||||
"""
|
||||
There are to tyes of Node instances (both have 'freq' attribute):
|
||||
* leaf node: has 'symbol' attribute
|
||||
* parent node: has 'child' attribute (tuple with both children)
|
||||
"""
|
||||
def __lt__(self, other):
|
||||
# heapq needs to be able to compare the nodes
|
||||
return self.freq < other.freq
|
||||
|
||||
minheap = []
|
||||
# create all leaf nodes and push them onto the queue
|
||||
for sym, f in __freq_map.items():
|
||||
leaf = Node()
|
||||
leaf.symbol = sym
|
||||
leaf.freq = f
|
||||
heappush(minheap, leaf)
|
||||
|
||||
# repeat the process until only one node remains
|
||||
while len(minheap) > 1:
|
||||
# take the two nodes with lowest frequencies from the queue
|
||||
# to construct a new parent node and push it onto the queue
|
||||
parent = Node()
|
||||
parent.child = heappop(minheap), heappop(minheap)
|
||||
parent.freq = parent.child[0].freq + parent.child[1].freq
|
||||
heappush(minheap, parent)
|
||||
|
||||
# the single remaining node is the root of the Huffman tree
|
||||
return minheap[0]
|
||||
|
||||
|
||||
def huffman_code(__freq_map, endian=None):
|
||||
"""huffman_code(dict, /, endian=None) -> dict
|
||||
|
||||
Given a frequency map, a dictionary mapping symbols to their frequency,
|
||||
calculate the Huffman code, i.e. a dict mapping those symbols to
|
||||
bitarrays (with given bit-endianness). Note that the symbols are not limited
|
||||
to being strings. Symbols may be any hashable object.
|
||||
"""
|
||||
if not isinstance(__freq_map, dict):
|
||||
raise TypeError("dict expected, got '%s'" % type(__freq_map).__name__)
|
||||
|
||||
if len(__freq_map) < 2:
|
||||
if len(__freq_map) == 0:
|
||||
raise ValueError("cannot create Huffman code with no symbols")
|
||||
# Only one symbol: Normally if only one symbol is given, the code
|
||||
# could be represented with zero bits. However here, the code should
|
||||
# be at least one bit for the .encode() and .decode() methods to work.
|
||||
# So we represent the symbol by a single code of length one, in
|
||||
# particular one 0 bit. This is an incomplete code, since if a 1 bit
|
||||
# is received, it has no meaning and will result in an error.
|
||||
sym = list(__freq_map)[0]
|
||||
return {sym: bitarray('0', endian)}
|
||||
|
||||
result = {}
|
||||
|
||||
def traverse(nd, prefix=bitarray(0, endian)):
|
||||
try: # leaf
|
||||
result[nd.symbol] = prefix
|
||||
except AttributeError: # parent, so traverse each child
|
||||
traverse(nd.child[0], prefix + '0')
|
||||
traverse(nd.child[1], prefix + '1')
|
||||
|
||||
traverse(_huffman_tree(__freq_map))
|
||||
return result
|
||||
|
||||
|
||||
def canonical_huffman(__freq_map):
|
||||
"""canonical_huffman(dict, /) -> tuple
|
||||
|
||||
Given a frequency map, a dictionary mapping symbols to their frequency,
|
||||
calculate the canonical Huffman code. Returns a tuple containing:
|
||||
|
||||
0. the canonical Huffman code as a dict mapping symbols to bitarrays
|
||||
1. a list containing the number of symbols of each code length
|
||||
2. a list of symbols in canonical order
|
||||
|
||||
Note: the two lists may be used as input for `canonical_decode()`.
|
||||
"""
|
||||
if not isinstance(__freq_map, dict):
|
||||
raise TypeError("dict expected, got '%s'" % type(__freq_map).__name__)
|
||||
|
||||
if len(__freq_map) < 2:
|
||||
if len(__freq_map) == 0:
|
||||
raise ValueError("cannot create Huffman code with no symbols")
|
||||
# Only one symbol: see note above in huffman_code()
|
||||
sym = list(__freq_map)[0]
|
||||
return {sym: bitarray('0', 'big')}, [0, 1], [sym]
|
||||
|
||||
code_length = {} # map symbols to their code length
|
||||
|
||||
def traverse(nd, length=0):
|
||||
# traverse the Huffman tree, but (unlike in huffman_code() above) we
|
||||
# now just simply record the length for reaching each symbol
|
||||
try: # leaf
|
||||
code_length[nd.symbol] = length
|
||||
except AttributeError: # parent, so traverse each child
|
||||
traverse(nd.child[0], length + 1)
|
||||
traverse(nd.child[1], length + 1)
|
||||
|
||||
traverse(_huffman_tree(__freq_map))
|
||||
|
||||
# We now have a mapping of symbols to their code length, which is all we
|
||||
# need to construct a list of tuples (symbol, code length) sorted by
|
||||
# code length:
|
||||
table = sorted(code_length.items(), key=lambda item: item[1])
|
||||
|
||||
maxbits = table[-1][1]
|
||||
codedict = {}
|
||||
count = (maxbits + 1) * [0]
|
||||
|
||||
code = 0
|
||||
for i, (sym, length) in enumerate(table):
|
||||
codedict[sym] = int2ba(code, length, 'big')
|
||||
count[length] += 1
|
||||
if i + 1 < len(table):
|
||||
code += 1
|
||||
code <<= table[i + 1][1] - length
|
||||
|
||||
return codedict, count, [item[0] for item in table]
|
||||
84
venv/lib/python3.12/site-packages/bitarray/util.pyi
Normal file
84
venv/lib/python3.12/site-packages/bitarray/util.pyi
Normal file
@@ -0,0 +1,84 @@
|
||||
# Copyright (c) 2021 - 2025, Ilan Schnell; All Rights Reserved
|
||||
|
||||
from collections import Counter
|
||||
from collections.abc import Iterable, Iterator, Sequence
|
||||
from typing import Any, AnyStr, BinaryIO, Optional, Union
|
||||
|
||||
from bitarray import bitarray, BytesLike, CodeDict
|
||||
|
||||
|
||||
FreqMap = Union[Counter[int], dict[Any, Union[int, float]]]
|
||||
|
||||
|
||||
def zeros(length: int, endian: Optional[str] = ...) -> bitarray: ...
|
||||
def ones(length: int, endian: Optional[str] = ...) -> bitarray: ...
|
||||
|
||||
def urandom(length: int, endian: Optional[str] = ...) -> bitarray: ...
|
||||
def random_p(n: int,
|
||||
p = ...,
|
||||
endian: Optional[str] = ...) -> bitarray: ...
|
||||
def random_k(n: int,
|
||||
k: int,
|
||||
endian: Optional[str] = ...) -> bitarray: ...
|
||||
def gen_primes(n: int,
|
||||
endian: Optional[str] = ...,
|
||||
odd: Optional[bool] = ...) -> bitarray: ...
|
||||
|
||||
def pprint(a: Any, stream: BinaryIO = ...,
|
||||
group: int = ...,
|
||||
indent: int = ...,
|
||||
width: int = ...) -> None: ...
|
||||
|
||||
def strip(a: bitarray, mode: str = ...) -> bitarray: ...
|
||||
|
||||
def count_n(a: bitarray,
|
||||
n: int,
|
||||
value: int = ...) -> int: ...
|
||||
|
||||
def parity(a: bitarray) -> int: ...
|
||||
def sum_indices(a: bitarray, mode: int = ...) -> int: ...
|
||||
def xor_indices(a: bitarray) -> int: ...
|
||||
def count_and(a: bitarray, b: bitarray) -> int: ...
|
||||
def count_or(a: bitarray, b: bitarray) -> int: ...
|
||||
def count_xor(a: bitarray, b: bitarray) -> int: ...
|
||||
def any_and(a: bitarray, b: bitarray) -> bool: ...
|
||||
def subset(a: bitarray, b: bitarray) -> bool: ...
|
||||
def correspond_all(a: bitarray, b: bitarray) -> tuple: ...
|
||||
def byteswap(a: BytesLike, n: int) -> None: ...
|
||||
|
||||
def intervals(a: bitarray) -> Iterator: ...
|
||||
|
||||
def ba2hex(a: bitarray,
|
||||
group: int = ...,
|
||||
sep: str = ...) -> str: ...
|
||||
def hex2ba(s: AnyStr,
|
||||
endian: Optional[str] = ...) -> bitarray: ...
|
||||
def ba2base(n: int,
|
||||
a: bitarray,
|
||||
group: int = ...,
|
||||
sep: str = ...) -> str: ...
|
||||
def base2ba(n: int,
|
||||
s: AnyStr,
|
||||
endian: Optional[str] = ...) -> bitarray: ...
|
||||
|
||||
def ba2int(a: bitarray, signed: int = ...) -> int: ...
|
||||
def int2ba(i: int,
|
||||
length: int = ...,
|
||||
endian: str = ...,
|
||||
signed: int = ...) -> bitarray: ...
|
||||
|
||||
def serialize(a: bitarray) -> bytes: ...
|
||||
def deserialize(b: BytesLike) -> bitarray: ...
|
||||
def sc_encode(a: bitarray) -> bytes: ...
|
||||
def sc_decode(stream: Iterable[int]) -> bitarray: ...
|
||||
def vl_encode(a: bitarray) -> bytes: ...
|
||||
def vl_decode(stream: Iterable[int],
|
||||
endian: Optional[str] = ...) -> bitarray: ...
|
||||
|
||||
def _huffman_tree(freq_map: FreqMap) -> Any: ...
|
||||
def huffman_code(freq_map: FreqMap,
|
||||
endian: Optional[str] = ...) -> CodeDict: ...
|
||||
def canonical_huffman(Freq_Map) -> tuple[CodeDict, list, list]: ...
|
||||
def canonical_decode(a: bitarray,
|
||||
count: Sequence[int],
|
||||
symbol: Iterable[Any]) -> Iterator: ...
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,20 @@
|
||||
Metadata-Version: 2.3
|
||||
Name: easybits
|
||||
Version: 0.1.4
|
||||
Summary: A friendly interface for exploring bits.
|
||||
Author: Chris Proctor
|
||||
Author-email: chris@chrisproctor.net
|
||||
Requires-Python: >=3.9,<4.0
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Requires-Dist: bitarray (>=3.0.0,<4.0.0)
|
||||
Description-Content-Type: text/markdown
|
||||
|
||||
# Easybits
|
||||
|
||||
A friendly interface for exploring bits.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
easybits-0.1.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
easybits-0.1.4.dist-info/METADATA,sha256=ZSqDCyY0XWhgZREZ_lMaTKSJiglvwBUdc9_3Qhq-pxI,627
|
||||
easybits-0.1.4.dist-info/RECORD,,
|
||||
easybits-0.1.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
easybits-0.1.4.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
|
||||
easybits/__init__.py,sha256=na58npOKL8pdkPpaG06rTvv8EX1BQaNYx_K0tisschA,3456
|
||||
easybits/__pycache__/__init__.cpython-312.pyc,,
|
||||
easybits/__pycache__/errors.cpython-312.pyc,,
|
||||
easybits/__pycache__/util.cpython-312.pyc,,
|
||||
easybits/errors.py,sha256=MPQGCYUave-PCKP4IkfIS2NJzL2j2KH2W3rlPofsigo,478
|
||||
easybits/util.py,sha256=mPo6AyWfZrfz5m31sFylHsEdMQt0OixEgpN-U-ZPIuU,118
|
||||
@@ -0,0 +1,4 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: poetry-core 2.0.0
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
121
venv/lib/python3.12/site-packages/easybits/__init__.py
Normal file
121
venv/lib/python3.12/site-packages/easybits/__init__.py
Normal file
@@ -0,0 +1,121 @@
|
||||
from bitarray import bitarray
|
||||
from bitarray.util import (
|
||||
zeros,
|
||||
ones,
|
||||
int2ba,
|
||||
ba2int,
|
||||
)
|
||||
from easybits.util import is_bit_string
|
||||
from easybits.errors import (
|
||||
NotEnoughBits,
|
||||
IntegersRequireLength,
|
||||
IntegerAdditionRequiresSameLength,
|
||||
)
|
||||
|
||||
class Bits(bitarray):
|
||||
"""A wrapper around bitarray with a more idiomatic interface for beginners.
|
||||
- Integers are always signed.
|
||||
"""
|
||||
|
||||
default_text_encoding = 'ascii'
|
||||
|
||||
@classmethod
|
||||
def zeros(cls, length):
|
||||
return Bits(zeros(length))
|
||||
|
||||
@classmethod
|
||||
def ones(cls, length):
|
||||
return Bits(ones(length))
|
||||
|
||||
def __new__(cls, value=None, length=None, encoding=None):
|
||||
if value is None:
|
||||
bits = bitarray(length or 0)
|
||||
elif isinstance(value, bytes):
|
||||
bits = bitarray()
|
||||
bits.frombytes(value)
|
||||
elif isinstance(value, bool):
|
||||
bits = bitarray([value])
|
||||
elif isinstance(value, int):
|
||||
if not length:
|
||||
raise IntegersRequireLength()
|
||||
bits = int2ba(value, length=length, signed=True)
|
||||
elif isinstance(value, str):
|
||||
if encoding:
|
||||
bits = bitarray()
|
||||
bits.frombytes(value.encode(encoding))
|
||||
elif is_bit_string(value):
|
||||
bits = bitarray(value)
|
||||
else:
|
||||
bits = bitarray()
|
||||
bits.frombytes(value.encode(cls.default_text_encoding))
|
||||
elif isinstance(value, list):
|
||||
bits = bitarray(value)
|
||||
elif isinstance(value, bitarray):
|
||||
bits = value
|
||||
else:
|
||||
raise ValueError(f"Can't create bits from {value}")
|
||||
if length:
|
||||
if length < len(bits):
|
||||
raise NotEnoughBits(value, length)
|
||||
else:
|
||||
sized_bits = bitarray(length)
|
||||
sized_bits[-len(bits):] = bits
|
||||
else:
|
||||
sized_bits = bits
|
||||
return super().__new__(cls, sized_bits)
|
||||
|
||||
def __str__(self):
|
||||
return self.to01()
|
||||
|
||||
def __repr__(self):
|
||||
return self.to01()
|
||||
|
||||
@property
|
||||
def bool(self):
|
||||
return [bool(b) for b in self.tolist()]
|
||||
|
||||
@property
|
||||
def int(self):
|
||||
return ba2int(self, signed=True)
|
||||
|
||||
@property
|
||||
def bytes(self):
|
||||
return self.tobytes()
|
||||
|
||||
@property
|
||||
def ascii(self):
|
||||
return self.bytes.decode("ascii")
|
||||
|
||||
def __add__(self, other):
|
||||
"""Performs bitwise addition on `self` and `other`. Does not
|
||||
check for overflow.
|
||||
"""
|
||||
a, b = self, Bits(other)
|
||||
if not len(a) == len(b):
|
||||
raise IntegerAdditionRequiresSameLength()
|
||||
result = Bits.zeros(len(a))
|
||||
carry = 0
|
||||
for i in reversed(range(len(a))):
|
||||
result[i] = a[i] ^ b[i] ^ carry
|
||||
carry = (a[i] & b[i]) | (a[i] & carry) | (b[i] & carry)
|
||||
return result
|
||||
|
||||
def __sub__(self, other):
|
||||
"""Performs bitwise subtraction on `self` and `other`. Does not
|
||||
check for overflow.
|
||||
"""
|
||||
a, b = self, Bits(other)
|
||||
if not len(a) == len(b):
|
||||
raise IntegerAdditionRequiresSameLength()
|
||||
return a + (-b)
|
||||
|
||||
def __neg__(self):
|
||||
"""Treats `self` as an integer, and flips its sign.
|
||||
"""
|
||||
if self[0]:
|
||||
return ~self + Bits(1, length=len(self))
|
||||
else:
|
||||
return ~(self + Bits(-1, length=len(self)))
|
||||
|
||||
def concat(self, other):
|
||||
return super().__add__(other)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
15
venv/lib/python3.12/site-packages/easybits/errors.py
Normal file
15
venv/lib/python3.12/site-packages/easybits/errors.py
Normal file
@@ -0,0 +1,15 @@
|
||||
class BitsError(Exception):
|
||||
message = "Bits error"
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
|
||||
class NotEnoughBits(BitsError):
|
||||
def __init__(self, obj, length):
|
||||
self.message = f"{obj} cannot fit into {length} bits."
|
||||
|
||||
class IntegersRequireLength(BitsError):
|
||||
message = "Binary representations of integers require that a length be provided."
|
||||
|
||||
class IntegerAdditionRequiresSameLength(BitsError):
|
||||
message = "Can only add integers of the same length."
|
||||
5
venv/lib/python3.12/site-packages/easybits/util.py
Normal file
5
venv/lib/python3.12/site-packages/easybits/util.py
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
def is_bit_string(value):
|
||||
bit_chars = {'1', '0', ' '}
|
||||
return value.strip() and not (set(value) - bit_chars)
|
||||
|
||||
760
venv/lib/python3.12/site-packages/pip-24.0.dist-info/AUTHORS.txt
Normal file
760
venv/lib/python3.12/site-packages/pip-24.0.dist-info/AUTHORS.txt
Normal file
@@ -0,0 +1,760 @@
|
||||
@Switch01
|
||||
A_Rog
|
||||
Aakanksha Agrawal
|
||||
Abhinav Sagar
|
||||
ABHYUDAY PRATAP SINGH
|
||||
abs51295
|
||||
AceGentile
|
||||
Adam Chainz
|
||||
Adam Tse
|
||||
Adam Wentz
|
||||
admin
|
||||
Adrien Morison
|
||||
ahayrapetyan
|
||||
Ahilya
|
||||
AinsworthK
|
||||
Akash Srivastava
|
||||
Alan Yee
|
||||
Albert Tugushev
|
||||
Albert-Guan
|
||||
albertg
|
||||
Alberto Sottile
|
||||
Aleks Bunin
|
||||
Ales Erjavec
|
||||
Alethea Flowers
|
||||
Alex Gaynor
|
||||
Alex Grönholm
|
||||
Alex Hedges
|
||||
Alex Loosley
|
||||
Alex Morega
|
||||
Alex Stachowiak
|
||||
Alexander Shtyrov
|
||||
Alexandre Conrad
|
||||
Alexey Popravka
|
||||
Aleš Erjavec
|
||||
Alli
|
||||
Ami Fischman
|
||||
Ananya Maiti
|
||||
Anatoly Techtonik
|
||||
Anders Kaseorg
|
||||
Andre Aguiar
|
||||
Andreas Lutro
|
||||
Andrei Geacar
|
||||
Andrew Gaul
|
||||
Andrew Shymanel
|
||||
Andrey Bienkowski
|
||||
Andrey Bulgakov
|
||||
Andrés Delfino
|
||||
Andy Freeland
|
||||
Andy Kluger
|
||||
Ani Hayrapetyan
|
||||
Aniruddha Basak
|
||||
Anish Tambe
|
||||
Anrs Hu
|
||||
Anthony Sottile
|
||||
Antoine Musso
|
||||
Anton Ovchinnikov
|
||||
Anton Patrushev
|
||||
Antonio Alvarado Hernandez
|
||||
Antony Lee
|
||||
Antti Kaihola
|
||||
Anubhav Patel
|
||||
Anudit Nagar
|
||||
Anuj Godase
|
||||
AQNOUCH Mohammed
|
||||
AraHaan
|
||||
Arindam Choudhury
|
||||
Armin Ronacher
|
||||
Artem
|
||||
Arun Babu Neelicattu
|
||||
Ashley Manton
|
||||
Ashwin Ramaswami
|
||||
atse
|
||||
Atsushi Odagiri
|
||||
Avinash Karhana
|
||||
Avner Cohen
|
||||
Awit (Ah-Wit) Ghirmai
|
||||
Baptiste Mispelon
|
||||
Barney Gale
|
||||
barneygale
|
||||
Bartek Ogryczak
|
||||
Bastian Venthur
|
||||
Ben Bodenmiller
|
||||
Ben Darnell
|
||||
Ben Hoyt
|
||||
Ben Mares
|
||||
Ben Rosser
|
||||
Bence Nagy
|
||||
Benjamin Peterson
|
||||
Benjamin VanEvery
|
||||
Benoit Pierre
|
||||
Berker Peksag
|
||||
Bernard
|
||||
Bernard Tyers
|
||||
Bernardo B. Marques
|
||||
Bernhard M. Wiedemann
|
||||
Bertil Hatt
|
||||
Bhavam Vidyarthi
|
||||
Blazej Michalik
|
||||
Bogdan Opanchuk
|
||||
BorisZZZ
|
||||
Brad Erickson
|
||||
Bradley Ayers
|
||||
Brandon L. Reiss
|
||||
Brandt Bucher
|
||||
Brett Randall
|
||||
Brett Rosen
|
||||
Brian Cristante
|
||||
Brian Rosner
|
||||
briantracy
|
||||
BrownTruck
|
||||
Bruno Oliveira
|
||||
Bruno Renié
|
||||
Bruno S
|
||||
Bstrdsmkr
|
||||
Buck Golemon
|
||||
burrows
|
||||
Bussonnier Matthias
|
||||
bwoodsend
|
||||
c22
|
||||
Caleb Martinez
|
||||
Calvin Smith
|
||||
Carl Meyer
|
||||
Carlos Liam
|
||||
Carol Willing
|
||||
Carter Thayer
|
||||
Cass
|
||||
Chandrasekhar Atina
|
||||
Chih-Hsuan Yen
|
||||
Chris Brinker
|
||||
Chris Hunt
|
||||
Chris Jerdonek
|
||||
Chris Kuehl
|
||||
Chris McDonough
|
||||
Chris Pawley
|
||||
Chris Pryer
|
||||
Chris Wolfe
|
||||
Christian Clauss
|
||||
Christian Heimes
|
||||
Christian Oudard
|
||||
Christoph Reiter
|
||||
Christopher Hunt
|
||||
Christopher Snyder
|
||||
cjc7373
|
||||
Clark Boylan
|
||||
Claudio Jolowicz
|
||||
Clay McClure
|
||||
Cody
|
||||
Cody Soyland
|
||||
Colin Watson
|
||||
Collin Anderson
|
||||
Connor Osborn
|
||||
Cooper Lees
|
||||
Cooper Ry Lees
|
||||
Cory Benfield
|
||||
Cory Wright
|
||||
Craig Kerstiens
|
||||
Cristian Sorinel
|
||||
Cristina
|
||||
Cristina Muñoz
|
||||
Curtis Doty
|
||||
cytolentino
|
||||
Daan De Meyer
|
||||
Dale
|
||||
Damian
|
||||
Damian Quiroga
|
||||
Damian Shaw
|
||||
Dan Black
|
||||
Dan Savilonis
|
||||
Dan Sully
|
||||
Dane Hillard
|
||||
daniel
|
||||
Daniel Collins
|
||||
Daniel Hahler
|
||||
Daniel Holth
|
||||
Daniel Jost
|
||||
Daniel Katz
|
||||
Daniel Shaulov
|
||||
Daniele Esposti
|
||||
Daniele Nicolodi
|
||||
Daniele Procida
|
||||
Daniil Konovalenko
|
||||
Danny Hermes
|
||||
Danny McClanahan
|
||||
Darren Kavanagh
|
||||
Dav Clark
|
||||
Dave Abrahams
|
||||
Dave Jones
|
||||
David Aguilar
|
||||
David Black
|
||||
David Bordeynik
|
||||
David Caro
|
||||
David D Lowe
|
||||
David Evans
|
||||
David Hewitt
|
||||
David Linke
|
||||
David Poggi
|
||||
David Pursehouse
|
||||
David Runge
|
||||
David Tucker
|
||||
David Wales
|
||||
Davidovich
|
||||
ddelange
|
||||
Deepak Sharma
|
||||
Deepyaman Datta
|
||||
Denise Yu
|
||||
dependabot[bot]
|
||||
derwolfe
|
||||
Desetude
|
||||
Devesh Kumar Singh
|
||||
Diego Caraballo
|
||||
Diego Ramirez
|
||||
DiegoCaraballo
|
||||
Dimitri Merejkowsky
|
||||
Dimitri Papadopoulos
|
||||
Dirk Stolle
|
||||
Dmitry Gladkov
|
||||
Dmitry Volodin
|
||||
Domen Kožar
|
||||
Dominic Davis-Foster
|
||||
Donald Stufft
|
||||
Dongweiming
|
||||
doron zarhi
|
||||
Dos Moonen
|
||||
Douglas Thor
|
||||
DrFeathers
|
||||
Dustin Ingram
|
||||
Dwayne Bailey
|
||||
Ed Morley
|
||||
Edgar Ramírez
|
||||
Edgar Ramírez Mondragón
|
||||
Ee Durbin
|
||||
Efflam Lemaillet
|
||||
efflamlemaillet
|
||||
Eitan Adler
|
||||
ekristina
|
||||
elainechan
|
||||
Eli Schwartz
|
||||
Elisha Hollander
|
||||
Ellen Marie Dash
|
||||
Emil Burzo
|
||||
Emil Styrke
|
||||
Emmanuel Arias
|
||||
Endoh Takanao
|
||||
enoch
|
||||
Erdinc Mutlu
|
||||
Eric Cousineau
|
||||
Eric Gillingham
|
||||
Eric Hanchrow
|
||||
Eric Hopper
|
||||
Erik M. Bray
|
||||
Erik Rose
|
||||
Erwin Janssen
|
||||
Eugene Vereshchagin
|
||||
everdimension
|
||||
Federico
|
||||
Felipe Peter
|
||||
Felix Yan
|
||||
fiber-space
|
||||
Filip Kokosiński
|
||||
Filipe Laíns
|
||||
Finn Womack
|
||||
finnagin
|
||||
Flavio Amurrio
|
||||
Florian Briand
|
||||
Florian Rathgeber
|
||||
Francesco
|
||||
Francesco Montesano
|
||||
Frost Ming
|
||||
Gabriel Curio
|
||||
Gabriel de Perthuis
|
||||
Garry Polley
|
||||
gavin
|
||||
gdanielson
|
||||
Geoffrey Sneddon
|
||||
George Song
|
||||
Georgi Valkov
|
||||
Georgy Pchelkin
|
||||
ghost
|
||||
Giftlin Rajaiah
|
||||
gizmoguy1
|
||||
gkdoc
|
||||
Godefroid Chapelle
|
||||
Gopinath M
|
||||
GOTO Hayato
|
||||
gousaiyang
|
||||
gpiks
|
||||
Greg Roodt
|
||||
Greg Ward
|
||||
Guilherme Espada
|
||||
Guillaume Seguin
|
||||
gutsytechster
|
||||
Guy Rozendorn
|
||||
Guy Tuval
|
||||
gzpan123
|
||||
Hanjun Kim
|
||||
Hari Charan
|
||||
Harsh Vardhan
|
||||
harupy
|
||||
Harutaka Kawamura
|
||||
hauntsaninja
|
||||
Henrich Hartzer
|
||||
Henry Schreiner
|
||||
Herbert Pfennig
|
||||
Holly Stotelmyer
|
||||
Honnix
|
||||
Hsiaoming Yang
|
||||
Hugo Lopes Tavares
|
||||
Hugo van Kemenade
|
||||
Hugues Bruant
|
||||
Hynek Schlawack
|
||||
Ian Bicking
|
||||
Ian Cordasco
|
||||
Ian Lee
|
||||
Ian Stapleton Cordasco
|
||||
Ian Wienand
|
||||
Igor Kuzmitshov
|
||||
Igor Sobreira
|
||||
Ilan Schnell
|
||||
Illia Volochii
|
||||
Ilya Baryshev
|
||||
Inada Naoki
|
||||
Ionel Cristian Mărieș
|
||||
Ionel Maries Cristian
|
||||
Itamar Turner-Trauring
|
||||
Ivan Pozdeev
|
||||
J. Nick Koston
|
||||
Jacob Kim
|
||||
Jacob Walls
|
||||
Jaime Sanz
|
||||
jakirkham
|
||||
Jakub Kuczys
|
||||
Jakub Stasiak
|
||||
Jakub Vysoky
|
||||
Jakub Wilk
|
||||
James Cleveland
|
||||
James Curtin
|
||||
James Firth
|
||||
James Gerity
|
||||
James Polley
|
||||
Jan Pokorný
|
||||
Jannis Leidel
|
||||
Jarek Potiuk
|
||||
jarondl
|
||||
Jason Curtis
|
||||
Jason R. Coombs
|
||||
JasonMo
|
||||
JasonMo1
|
||||
Jay Graves
|
||||
Jean Abou Samra
|
||||
Jean-Christophe Fillion-Robin
|
||||
Jeff Barber
|
||||
Jeff Dairiki
|
||||
Jeff Widman
|
||||
Jelmer Vernooij
|
||||
jenix21
|
||||
Jeremy Stanley
|
||||
Jeremy Zafran
|
||||
Jesse Rittner
|
||||
Jiashuo Li
|
||||
Jim Fisher
|
||||
Jim Garrison
|
||||
Jiun Bae
|
||||
Jivan Amara
|
||||
Joe Bylund
|
||||
Joe Michelini
|
||||
John Paton
|
||||
John T. Wodder II
|
||||
John-Scott Atlakson
|
||||
johnthagen
|
||||
Jon Banafato
|
||||
Jon Dufresne
|
||||
Jon Parise
|
||||
Jonas Nockert
|
||||
Jonathan Herbert
|
||||
Joonatan Partanen
|
||||
Joost Molenaar
|
||||
Jorge Niedbalski
|
||||
Joseph Bylund
|
||||
Joseph Long
|
||||
Josh Bronson
|
||||
Josh Hansen
|
||||
Josh Schneier
|
||||
Joshua
|
||||
Juan Luis Cano Rodríguez
|
||||
Juanjo Bazán
|
||||
Judah Rand
|
||||
Julian Berman
|
||||
Julian Gethmann
|
||||
Julien Demoor
|
||||
Jussi Kukkonen
|
||||
jwg4
|
||||
Jyrki Pulliainen
|
||||
Kai Chen
|
||||
Kai Mueller
|
||||
Kamal Bin Mustafa
|
||||
kasium
|
||||
kaustav haldar
|
||||
keanemind
|
||||
Keith Maxwell
|
||||
Kelsey Hightower
|
||||
Kenneth Belitzky
|
||||
Kenneth Reitz
|
||||
Kevin Burke
|
||||
Kevin Carter
|
||||
Kevin Frommelt
|
||||
Kevin R Patterson
|
||||
Kexuan Sun
|
||||
Kit Randel
|
||||
Klaas van Schelven
|
||||
KOLANICH
|
||||
kpinc
|
||||
Krishna Oza
|
||||
Kumar McMillan
|
||||
Kurt McKee
|
||||
Kyle Persohn
|
||||
lakshmanaram
|
||||
Laszlo Kiss-Kollar
|
||||
Laurent Bristiel
|
||||
Laurent LAPORTE
|
||||
Laurie O
|
||||
Laurie Opperman
|
||||
layday
|
||||
Leon Sasson
|
||||
Lev Givon
|
||||
Lincoln de Sousa
|
||||
Lipis
|
||||
lorddavidiii
|
||||
Loren Carvalho
|
||||
Lucas Cimon
|
||||
Ludovic Gasc
|
||||
Lukas Geiger
|
||||
Lukas Juhrich
|
||||
Luke Macken
|
||||
Luo Jiebin
|
||||
luojiebin
|
||||
luz.paz
|
||||
László Kiss Kollár
|
||||
M00nL1ght
|
||||
Marc Abramowitz
|
||||
Marc Tamlyn
|
||||
Marcus Smith
|
||||
Mariatta
|
||||
Mark Kohler
|
||||
Mark Williams
|
||||
Markus Hametner
|
||||
Martey Dodoo
|
||||
Martin Fischer
|
||||
Martin Häcker
|
||||
Martin Pavlasek
|
||||
Masaki
|
||||
Masklinn
|
||||
Matej Stuchlik
|
||||
Mathew Jennings
|
||||
Mathieu Bridon
|
||||
Mathieu Kniewallner
|
||||
Matt Bacchi
|
||||
Matt Good
|
||||
Matt Maker
|
||||
Matt Robenolt
|
||||
matthew
|
||||
Matthew Einhorn
|
||||
Matthew Feickert
|
||||
Matthew Gilliard
|
||||
Matthew Iversen
|
||||
Matthew Treinish
|
||||
Matthew Trumbell
|
||||
Matthew Willson
|
||||
Matthias Bussonnier
|
||||
mattip
|
||||
Maurits van Rees
|
||||
Max W Chase
|
||||
Maxim Kurnikov
|
||||
Maxime Rouyrre
|
||||
mayeut
|
||||
mbaluna
|
||||
mdebi
|
||||
memoselyk
|
||||
meowmeowcat
|
||||
Michael
|
||||
Michael Aquilina
|
||||
Michael E. Karpeles
|
||||
Michael Klich
|
||||
Michael Mintz
|
||||
Michael Williamson
|
||||
michaelpacer
|
||||
Michał Górny
|
||||
Mickaël Schoentgen
|
||||
Miguel Araujo Perez
|
||||
Mihir Singh
|
||||
Mike
|
||||
Mike Hendricks
|
||||
Min RK
|
||||
MinRK
|
||||
Miro Hrončok
|
||||
Monica Baluna
|
||||
montefra
|
||||
Monty Taylor
|
||||
Muha Ajjan
|
||||
Nadav Wexler
|
||||
Nahuel Ambrosini
|
||||
Nate Coraor
|
||||
Nate Prewitt
|
||||
Nathan Houghton
|
||||
Nathaniel J. Smith
|
||||
Nehal J Wani
|
||||
Neil Botelho
|
||||
Nguyễn Gia Phong
|
||||
Nicholas Serra
|
||||
Nick Coghlan
|
||||
Nick Stenning
|
||||
Nick Timkovich
|
||||
Nicolas Bock
|
||||
Nicole Harris
|
||||
Nikhil Benesch
|
||||
Nikhil Ladha
|
||||
Nikita Chepanov
|
||||
Nikolay Korolev
|
||||
Nipunn Koorapati
|
||||
Nitesh Sharma
|
||||
Niyas Sait
|
||||
Noah
|
||||
Noah Gorny
|
||||
Nowell Strite
|
||||
NtaleGrey
|
||||
nvdv
|
||||
OBITORASU
|
||||
Ofek Lev
|
||||
ofrinevo
|
||||
Oliver Freund
|
||||
Oliver Jeeves
|
||||
Oliver Mannion
|
||||
Oliver Tonnhofer
|
||||
Olivier Girardot
|
||||
Olivier Grisel
|
||||
Ollie Rutherfurd
|
||||
OMOTO Kenji
|
||||
Omry Yadan
|
||||
onlinejudge95
|
||||
Oren Held
|
||||
Oscar Benjamin
|
||||
Oz N Tiram
|
||||
Pachwenko
|
||||
Patrick Dubroy
|
||||
Patrick Jenkins
|
||||
Patrick Lawson
|
||||
patricktokeeffe
|
||||
Patrik Kopkan
|
||||
Paul Ganssle
|
||||
Paul Kehrer
|
||||
Paul Moore
|
||||
Paul Nasrat
|
||||
Paul Oswald
|
||||
Paul van der Linden
|
||||
Paulus Schoutsen
|
||||
Pavel Safronov
|
||||
Pavithra Eswaramoorthy
|
||||
Pawel Jasinski
|
||||
Paweł Szramowski
|
||||
Pekka Klärck
|
||||
Peter Gessler
|
||||
Peter Lisák
|
||||
Peter Waller
|
||||
petr-tik
|
||||
Phaneendra Chiruvella
|
||||
Phil Elson
|
||||
Phil Freo
|
||||
Phil Pennock
|
||||
Phil Whelan
|
||||
Philip Jägenstedt
|
||||
Philip Molloy
|
||||
Philippe Ombredanne
|
||||
Pi Delport
|
||||
Pierre-Yves Rofes
|
||||
Pieter Degroote
|
||||
pip
|
||||
Prabakaran Kumaresshan
|
||||
Prabhjyotsing Surjit Singh Sodhi
|
||||
Prabhu Marappan
|
||||
Pradyun Gedam
|
||||
Prashant Sharma
|
||||
Pratik Mallya
|
||||
pre-commit-ci[bot]
|
||||
Preet Thakkar
|
||||
Preston Holmes
|
||||
Przemek Wrzos
|
||||
Pulkit Goyal
|
||||
q0w
|
||||
Qiangning Hong
|
||||
Qiming Xu
|
||||
Quentin Lee
|
||||
Quentin Pradet
|
||||
R. David Murray
|
||||
Rafael Caricio
|
||||
Ralf Schmitt
|
||||
Razzi Abuissa
|
||||
rdb
|
||||
Reece Dunham
|
||||
Remi Rampin
|
||||
Rene Dudfield
|
||||
Riccardo Magliocchetti
|
||||
Riccardo Schirone
|
||||
Richard Jones
|
||||
Richard Si
|
||||
Ricky Ng-Adam
|
||||
Rishi
|
||||
RobberPhex
|
||||
Robert Collins
|
||||
Robert McGibbon
|
||||
Robert Pollak
|
||||
Robert T. McGibbon
|
||||
robin elisha robinson
|
||||
Roey Berman
|
||||
Rohan Jain
|
||||
Roman Bogorodskiy
|
||||
Roman Donchenko
|
||||
Romuald Brunet
|
||||
ronaudinho
|
||||
Ronny Pfannschmidt
|
||||
Rory McCann
|
||||
Ross Brattain
|
||||
Roy Wellington Ⅳ
|
||||
Ruairidh MacLeod
|
||||
Russell Keith-Magee
|
||||
Ryan Shepherd
|
||||
Ryan Wooden
|
||||
ryneeverett
|
||||
Sachi King
|
||||
Salvatore Rinchiera
|
||||
sandeepkiran-js
|
||||
Sander Van Balen
|
||||
Savio Jomton
|
||||
schlamar
|
||||
Scott Kitterman
|
||||
Sean
|
||||
seanj
|
||||
Sebastian Jordan
|
||||
Sebastian Schaetz
|
||||
Segev Finer
|
||||
SeongSoo Cho
|
||||
Sergey Vasilyev
|
||||
Seth Michael Larson
|
||||
Seth Woodworth
|
||||
Shahar Epstein
|
||||
Shantanu
|
||||
shireenrao
|
||||
Shivansh-007
|
||||
Shlomi Fish
|
||||
Shovan Maity
|
||||
Simeon Visser
|
||||
Simon Cross
|
||||
Simon Pichugin
|
||||
sinoroc
|
||||
sinscary
|
||||
snook92
|
||||
socketubs
|
||||
Sorin Sbarnea
|
||||
Srinivas Nyayapati
|
||||
Stavros Korokithakis
|
||||
Stefan Scherfke
|
||||
Stefano Rivera
|
||||
Stephan Erb
|
||||
Stephen Rosen
|
||||
stepshal
|
||||
Steve (Gadget) Barnes
|
||||
Steve Barnes
|
||||
Steve Dower
|
||||
Steve Kowalik
|
||||
Steven Myint
|
||||
Steven Silvester
|
||||
stonebig
|
||||
studioj
|
||||
Stéphane Bidoul
|
||||
Stéphane Bidoul (ACSONE)
|
||||
Stéphane Klein
|
||||
Sumana Harihareswara
|
||||
Surbhi Sharma
|
||||
Sviatoslav Sydorenko
|
||||
Swat009
|
||||
Sylvain
|
||||
Takayuki SHIMIZUKAWA
|
||||
Taneli Hukkinen
|
||||
tbeswick
|
||||
Thiago
|
||||
Thijs Triemstra
|
||||
Thomas Fenzl
|
||||
Thomas Grainger
|
||||
Thomas Guettler
|
||||
Thomas Johansson
|
||||
Thomas Kluyver
|
||||
Thomas Smith
|
||||
Thomas VINCENT
|
||||
Tim D. Smith
|
||||
Tim Gates
|
||||
Tim Harder
|
||||
Tim Heap
|
||||
tim smith
|
||||
tinruufu
|
||||
Tobias Hermann
|
||||
Tom Forbes
|
||||
Tom Freudenheim
|
||||
Tom V
|
||||
Tomas Hrnciar
|
||||
Tomas Orsava
|
||||
Tomer Chachamu
|
||||
Tommi Enenkel | AnB
|
||||
Tomáš Hrnčiar
|
||||
Tony Beswick
|
||||
Tony Narlock
|
||||
Tony Zhaocheng Tan
|
||||
TonyBeswick
|
||||
toonarmycaptain
|
||||
Toshio Kuratomi
|
||||
toxinu
|
||||
Travis Swicegood
|
||||
Tushar Sadhwani
|
||||
Tzu-ping Chung
|
||||
Valentin Haenel
|
||||
Victor Stinner
|
||||
victorvpaulo
|
||||
Vikram - Google
|
||||
Viktor Szépe
|
||||
Ville Skyttä
|
||||
Vinay Sajip
|
||||
Vincent Philippon
|
||||
Vinicyus Macedo
|
||||
Vipul Kumar
|
||||
Vitaly Babiy
|
||||
Vladimir Fokow
|
||||
Vladimir Rutsky
|
||||
W. Trevor King
|
||||
Wil Tan
|
||||
Wilfred Hughes
|
||||
William Edwards
|
||||
William ML Leslie
|
||||
William T Olson
|
||||
William Woodruff
|
||||
Wilson Mo
|
||||
wim glenn
|
||||
Winson Luk
|
||||
Wolfgang Maier
|
||||
Wu Zhenyu
|
||||
XAMES3
|
||||
Xavier Fernandez
|
||||
xoviat
|
||||
xtreak
|
||||
YAMAMOTO Takashi
|
||||
Yen Chi Hsuan
|
||||
Yeray Diaz Diaz
|
||||
Yoval P
|
||||
Yu Jian
|
||||
Yuan Jing Vincent Yan
|
||||
Yusuke Hayashi
|
||||
Zearin
|
||||
Zhiping Deng
|
||||
ziebam
|
||||
Zvezdan Petkovic
|
||||
Łukasz Langa
|
||||
Роман Донченко
|
||||
Семён Марьясин
|
||||
rekcäH nitraM
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2008-present The pip developers (see AUTHORS.txt 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.
|
||||
@@ -0,0 +1,88 @@
|
||||
Metadata-Version: 2.1
|
||||
Name: pip
|
||||
Version: 24.0
|
||||
Summary: The PyPA recommended tool for installing Python packages.
|
||||
Author-email: The pip developers <distutils-sig@python.org>
|
||||
License: MIT
|
||||
Project-URL: Homepage, https://pip.pypa.io/
|
||||
Project-URL: Documentation, https://pip.pypa.io
|
||||
Project-URL: Source, https://github.com/pypa/pip
|
||||
Project-URL: Changelog, https://pip.pypa.io/en/stable/news/
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Topic :: Software Development :: Build Tools
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3 :: Only
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Requires-Python: >=3.7
|
||||
Description-Content-Type: text/x-rst
|
||||
License-File: LICENSE.txt
|
||||
License-File: AUTHORS.txt
|
||||
|
||||
pip - The Python Package Installer
|
||||
==================================
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/pip.svg
|
||||
:target: https://pypi.org/project/pip/
|
||||
:alt: PyPI
|
||||
|
||||
.. image:: https://img.shields.io/pypi/pyversions/pip
|
||||
:target: https://pypi.org/project/pip
|
||||
:alt: PyPI - Python Version
|
||||
|
||||
.. image:: https://readthedocs.org/projects/pip/badge/?version=latest
|
||||
:target: https://pip.pypa.io/en/latest
|
||||
:alt: Documentation
|
||||
|
||||
pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.
|
||||
|
||||
Please take a look at our documentation for how to install and use pip:
|
||||
|
||||
* `Installation`_
|
||||
* `Usage`_
|
||||
|
||||
We release updates regularly, with a new version every 3 months. Find more details in our documentation:
|
||||
|
||||
* `Release notes`_
|
||||
* `Release process`_
|
||||
|
||||
If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
|
||||
|
||||
* `Issue tracking`_
|
||||
* `Discourse channel`_
|
||||
* `User IRC`_
|
||||
|
||||
If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms:
|
||||
|
||||
* `GitHub page`_
|
||||
* `Development documentation`_
|
||||
* `Development IRC`_
|
||||
|
||||
Code of Conduct
|
||||
---------------
|
||||
|
||||
Everyone interacting in the pip project's codebases, issue trackers, chat
|
||||
rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
|
||||
|
||||
.. _package installer: https://packaging.python.org/guides/tool-recommendations/
|
||||
.. _Python Package Index: https://pypi.org
|
||||
.. _Installation: https://pip.pypa.io/en/stable/installation/
|
||||
.. _Usage: https://pip.pypa.io/en/stable/
|
||||
.. _Release notes: https://pip.pypa.io/en/stable/news.html
|
||||
.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
|
||||
.. _GitHub page: https://github.com/pypa/pip
|
||||
.. _Development documentation: https://pip.pypa.io/en/latest/development
|
||||
.. _Issue tracking: https://github.com/pypa/pip/issues
|
||||
.. _Discourse channel: https://discuss.python.org/c/packaging
|
||||
.. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
|
||||
.. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev
|
||||
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
|
||||
1005
venv/lib/python3.12/site-packages/pip-24.0.dist-info/RECORD
Normal file
1005
venv/lib/python3.12/site-packages/pip-24.0.dist-info/RECORD
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.42.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[console_scripts]
|
||||
pip = pip._internal.cli.main:main
|
||||
pip3 = pip._internal.cli.main:main
|
||||
pip3.12 = pip._internal.cli.main:main
|
||||
@@ -0,0 +1 @@
|
||||
pip
|
||||
13
venv/lib/python3.12/site-packages/pip/__init__.py
Normal file
13
venv/lib/python3.12/site-packages/pip/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from typing import List, Optional
|
||||
|
||||
__version__ = "24.0"
|
||||
|
||||
|
||||
def main(args: Optional[List[str]] = None) -> int:
|
||||
"""This is an internal API only meant for use by pip's own console scripts.
|
||||
|
||||
For additional details, see https://github.com/pypa/pip/issues/7498.
|
||||
"""
|
||||
from pip._internal.utils.entrypoints import _wrapper
|
||||
|
||||
return _wrapper(args)
|
||||
24
venv/lib/python3.12/site-packages/pip/__main__.py
Normal file
24
venv/lib/python3.12/site-packages/pip/__main__.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Remove '' and current working directory from the first entry
|
||||
# of sys.path, if present to avoid using current directory
|
||||
# in pip commands check, freeze, install, list and show,
|
||||
# when invoked as python -m pip <command>
|
||||
if sys.path[0] in ("", os.getcwd()):
|
||||
sys.path.pop(0)
|
||||
|
||||
# If we are running from a wheel, add the wheel to sys.path
|
||||
# This allows the usage python pip-*.whl/pip install pip-*.whl
|
||||
if __package__ == "":
|
||||
# __file__ is pip-*.whl/pip/__main__.py
|
||||
# first dirname call strips of '/__main__.py', second strips off '/pip'
|
||||
# Resulting path is the name of the wheel itself
|
||||
# Add that to sys.path so we can import pip
|
||||
path = os.path.dirname(os.path.dirname(__file__))
|
||||
sys.path.insert(0, path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
from pip._internal.cli.main import main as _main
|
||||
|
||||
sys.exit(_main())
|
||||
50
venv/lib/python3.12/site-packages/pip/__pip-runner__.py
Normal file
50
venv/lib/python3.12/site-packages/pip/__pip-runner__.py
Normal file
@@ -0,0 +1,50 @@
|
||||
"""Execute exactly this copy of pip, within a different environment.
|
||||
|
||||
This file is named as it is, to ensure that this module can't be imported via
|
||||
an import statement.
|
||||
"""
|
||||
|
||||
# /!\ This version compatibility check section must be Python 2 compatible. /!\
|
||||
|
||||
import sys
|
||||
|
||||
# Copied from setup.py
|
||||
PYTHON_REQUIRES = (3, 7)
|
||||
|
||||
|
||||
def version_str(version): # type: ignore
|
||||
return ".".join(str(v) for v in version)
|
||||
|
||||
|
||||
if sys.version_info[:2] < PYTHON_REQUIRES:
|
||||
raise SystemExit(
|
||||
"This version of pip does not support python {} (requires >={}).".format(
|
||||
version_str(sys.version_info[:2]), version_str(PYTHON_REQUIRES)
|
||||
)
|
||||
)
|
||||
|
||||
# From here on, we can use Python 3 features, but the syntax must remain
|
||||
# Python 2 compatible.
|
||||
|
||||
import runpy # noqa: E402
|
||||
from importlib.machinery import PathFinder # noqa: E402
|
||||
from os.path import dirname # noqa: E402
|
||||
|
||||
PIP_SOURCES_ROOT = dirname(dirname(__file__))
|
||||
|
||||
|
||||
class PipImportRedirectingFinder:
|
||||
@classmethod
|
||||
def find_spec(self, fullname, path=None, target=None): # type: ignore
|
||||
if fullname != "pip":
|
||||
return None
|
||||
|
||||
spec = PathFinder.find_spec(fullname, [PIP_SOURCES_ROOT], target)
|
||||
assert spec, (PIP_SOURCES_ROOT, fullname)
|
||||
return spec
|
||||
|
||||
|
||||
sys.meta_path.insert(0, PipImportRedirectingFinder())
|
||||
|
||||
assert __name__ == "__main__", "Cannot run __pip-runner__.py as a non-main module"
|
||||
runpy.run_module("pip", run_name="__main__", alter_sys=True)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
18
venv/lib/python3.12/site-packages/pip/_internal/__init__.py
Normal file
18
venv/lib/python3.12/site-packages/pip/_internal/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from pip._internal.utils import _log
|
||||
|
||||
# init_logging() must be called before any call to logging.getLogger()
|
||||
# which happens at import of most modules.
|
||||
_log.init_logging()
|
||||
|
||||
|
||||
def main(args: (Optional[List[str]]) = None) -> int:
|
||||
"""This is preserved for old console scripts that may still be referencing
|
||||
it.
|
||||
|
||||
For additional details, see https://github.com/pypa/pip/issues/7498.
|
||||
"""
|
||||
from pip._internal.utils.entrypoints import _wrapper
|
||||
|
||||
return _wrapper(args)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
311
venv/lib/python3.12/site-packages/pip/_internal/build_env.py
Normal file
311
venv/lib/python3.12/site-packages/pip/_internal/build_env.py
Normal file
@@ -0,0 +1,311 @@
|
||||
"""Build Environment used for isolation during sdist building
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import site
|
||||
import sys
|
||||
import textwrap
|
||||
from collections import OrderedDict
|
||||
from types import TracebackType
|
||||
from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type, Union
|
||||
|
||||
from pip._vendor.certifi import where
|
||||
from pip._vendor.packaging.requirements import Requirement
|
||||
from pip._vendor.packaging.version import Version
|
||||
|
||||
from pip import __file__ as pip_location
|
||||
from pip._internal.cli.spinners import open_spinner
|
||||
from pip._internal.locations import get_platlib, get_purelib, get_scheme
|
||||
from pip._internal.metadata import get_default_environment, get_environment
|
||||
from pip._internal.utils.subprocess import call_subprocess
|
||||
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pip._internal.index.package_finder import PackageFinder
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _dedup(a: str, b: str) -> Union[Tuple[str], Tuple[str, str]]:
|
||||
return (a, b) if a != b else (a,)
|
||||
|
||||
|
||||
class _Prefix:
|
||||
def __init__(self, path: str) -> None:
|
||||
self.path = path
|
||||
self.setup = False
|
||||
scheme = get_scheme("", prefix=path)
|
||||
self.bin_dir = scheme.scripts
|
||||
self.lib_dirs = _dedup(scheme.purelib, scheme.platlib)
|
||||
|
||||
|
||||
def get_runnable_pip() -> str:
|
||||
"""Get a file to pass to a Python executable, to run the currently-running pip.
|
||||
|
||||
This is used to run a pip subprocess, for installing requirements into the build
|
||||
environment.
|
||||
"""
|
||||
source = pathlib.Path(pip_location).resolve().parent
|
||||
|
||||
if not source.is_dir():
|
||||
# This would happen if someone is using pip from inside a zip file. In that
|
||||
# case, we can use that directly.
|
||||
return str(source)
|
||||
|
||||
return os.fsdecode(source / "__pip-runner__.py")
|
||||
|
||||
|
||||
def _get_system_sitepackages() -> Set[str]:
|
||||
"""Get system site packages
|
||||
|
||||
Usually from site.getsitepackages,
|
||||
but fallback on `get_purelib()/get_platlib()` if unavailable
|
||||
(e.g. in a virtualenv created by virtualenv<20)
|
||||
|
||||
Returns normalized set of strings.
|
||||
"""
|
||||
if hasattr(site, "getsitepackages"):
|
||||
system_sites = site.getsitepackages()
|
||||
else:
|
||||
# virtualenv < 20 overwrites site.py without getsitepackages
|
||||
# fallback on get_purelib/get_platlib.
|
||||
# this is known to miss things, but shouldn't in the cases
|
||||
# where getsitepackages() has been removed (inside a virtualenv)
|
||||
system_sites = [get_purelib(), get_platlib()]
|
||||
return {os.path.normcase(path) for path in system_sites}
|
||||
|
||||
|
||||
class BuildEnvironment:
|
||||
"""Creates and manages an isolated environment to install build deps"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
temp_dir = TempDirectory(kind=tempdir_kinds.BUILD_ENV, globally_managed=True)
|
||||
|
||||
self._prefixes = OrderedDict(
|
||||
(name, _Prefix(os.path.join(temp_dir.path, name)))
|
||||
for name in ("normal", "overlay")
|
||||
)
|
||||
|
||||
self._bin_dirs: List[str] = []
|
||||
self._lib_dirs: List[str] = []
|
||||
for prefix in reversed(list(self._prefixes.values())):
|
||||
self._bin_dirs.append(prefix.bin_dir)
|
||||
self._lib_dirs.extend(prefix.lib_dirs)
|
||||
|
||||
# Customize site to:
|
||||
# - ensure .pth files are honored
|
||||
# - prevent access to system site packages
|
||||
system_sites = _get_system_sitepackages()
|
||||
|
||||
self._site_dir = os.path.join(temp_dir.path, "site")
|
||||
if not os.path.exists(self._site_dir):
|
||||
os.mkdir(self._site_dir)
|
||||
with open(
|
||||
os.path.join(self._site_dir, "sitecustomize.py"), "w", encoding="utf-8"
|
||||
) as fp:
|
||||
fp.write(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
import os, site, sys
|
||||
|
||||
# First, drop system-sites related paths.
|
||||
original_sys_path = sys.path[:]
|
||||
known_paths = set()
|
||||
for path in {system_sites!r}:
|
||||
site.addsitedir(path, known_paths=known_paths)
|
||||
system_paths = set(
|
||||
os.path.normcase(path)
|
||||
for path in sys.path[len(original_sys_path):]
|
||||
)
|
||||
original_sys_path = [
|
||||
path for path in original_sys_path
|
||||
if os.path.normcase(path) not in system_paths
|
||||
]
|
||||
sys.path = original_sys_path
|
||||
|
||||
# Second, add lib directories.
|
||||
# ensuring .pth file are processed.
|
||||
for path in {lib_dirs!r}:
|
||||
assert not path in sys.path
|
||||
site.addsitedir(path)
|
||||
"""
|
||||
).format(system_sites=system_sites, lib_dirs=self._lib_dirs)
|
||||
)
|
||||
|
||||
def __enter__(self) -> None:
|
||||
self._save_env = {
|
||||
name: os.environ.get(name, None)
|
||||
for name in ("PATH", "PYTHONNOUSERSITE", "PYTHONPATH")
|
||||
}
|
||||
|
||||
path = self._bin_dirs[:]
|
||||
old_path = self._save_env["PATH"]
|
||||
if old_path:
|
||||
path.extend(old_path.split(os.pathsep))
|
||||
|
||||
pythonpath = [self._site_dir]
|
||||
|
||||
os.environ.update(
|
||||
{
|
||||
"PATH": os.pathsep.join(path),
|
||||
"PYTHONNOUSERSITE": "1",
|
||||
"PYTHONPATH": os.pathsep.join(pythonpath),
|
||||
}
|
||||
)
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
for varname, old_value in self._save_env.items():
|
||||
if old_value is None:
|
||||
os.environ.pop(varname, None)
|
||||
else:
|
||||
os.environ[varname] = old_value
|
||||
|
||||
def check_requirements(
|
||||
self, reqs: Iterable[str]
|
||||
) -> Tuple[Set[Tuple[str, str]], Set[str]]:
|
||||
"""Return 2 sets:
|
||||
- conflicting requirements: set of (installed, wanted) reqs tuples
|
||||
- missing requirements: set of reqs
|
||||
"""
|
||||
missing = set()
|
||||
conflicting = set()
|
||||
if reqs:
|
||||
env = (
|
||||
get_environment(self._lib_dirs)
|
||||
if hasattr(self, "_lib_dirs")
|
||||
else get_default_environment()
|
||||
)
|
||||
for req_str in reqs:
|
||||
req = Requirement(req_str)
|
||||
# We're explicitly evaluating with an empty extra value, since build
|
||||
# environments are not provided any mechanism to select specific extras.
|
||||
if req.marker is not None and not req.marker.evaluate({"extra": ""}):
|
||||
continue
|
||||
dist = env.get_distribution(req.name)
|
||||
if not dist:
|
||||
missing.add(req_str)
|
||||
continue
|
||||
if isinstance(dist.version, Version):
|
||||
installed_req_str = f"{req.name}=={dist.version}"
|
||||
else:
|
||||
installed_req_str = f"{req.name}==={dist.version}"
|
||||
if not req.specifier.contains(dist.version, prereleases=True):
|
||||
conflicting.add((installed_req_str, req_str))
|
||||
# FIXME: Consider direct URL?
|
||||
return conflicting, missing
|
||||
|
||||
def install_requirements(
|
||||
self,
|
||||
finder: "PackageFinder",
|
||||
requirements: Iterable[str],
|
||||
prefix_as_string: str,
|
||||
*,
|
||||
kind: str,
|
||||
) -> None:
|
||||
prefix = self._prefixes[prefix_as_string]
|
||||
assert not prefix.setup
|
||||
prefix.setup = True
|
||||
if not requirements:
|
||||
return
|
||||
self._install_requirements(
|
||||
get_runnable_pip(),
|
||||
finder,
|
||||
requirements,
|
||||
prefix,
|
||||
kind=kind,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _install_requirements(
|
||||
pip_runnable: str,
|
||||
finder: "PackageFinder",
|
||||
requirements: Iterable[str],
|
||||
prefix: _Prefix,
|
||||
*,
|
||||
kind: str,
|
||||
) -> None:
|
||||
args: List[str] = [
|
||||
sys.executable,
|
||||
pip_runnable,
|
||||
"install",
|
||||
"--ignore-installed",
|
||||
"--no-user",
|
||||
"--prefix",
|
||||
prefix.path,
|
||||
"--no-warn-script-location",
|
||||
]
|
||||
if logger.getEffectiveLevel() <= logging.DEBUG:
|
||||
args.append("-v")
|
||||
for format_control in ("no_binary", "only_binary"):
|
||||
formats = getattr(finder.format_control, format_control)
|
||||
args.extend(
|
||||
(
|
||||
"--" + format_control.replace("_", "-"),
|
||||
",".join(sorted(formats or {":none:"})),
|
||||
)
|
||||
)
|
||||
|
||||
index_urls = finder.index_urls
|
||||
if index_urls:
|
||||
args.extend(["-i", index_urls[0]])
|
||||
for extra_index in index_urls[1:]:
|
||||
args.extend(["--extra-index-url", extra_index])
|
||||
else:
|
||||
args.append("--no-index")
|
||||
for link in finder.find_links:
|
||||
args.extend(["--find-links", link])
|
||||
|
||||
for host in finder.trusted_hosts:
|
||||
args.extend(["--trusted-host", host])
|
||||
if finder.allow_all_prereleases:
|
||||
args.append("--pre")
|
||||
if finder.prefer_binary:
|
||||
args.append("--prefer-binary")
|
||||
args.append("--")
|
||||
args.extend(requirements)
|
||||
extra_environ = {"_PIP_STANDALONE_CERT": where()}
|
||||
with open_spinner(f"Installing {kind}") as spinner:
|
||||
call_subprocess(
|
||||
args,
|
||||
command_desc=f"pip subprocess to install {kind}",
|
||||
spinner=spinner,
|
||||
extra_environ=extra_environ,
|
||||
)
|
||||
|
||||
|
||||
class NoOpBuildEnvironment(BuildEnvironment):
|
||||
"""A no-op drop-in replacement for BuildEnvironment"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
def __enter__(self) -> None:
|
||||
pass
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def cleanup(self) -> None:
|
||||
pass
|
||||
|
||||
def install_requirements(
|
||||
self,
|
||||
finder: "PackageFinder",
|
||||
requirements: Iterable[str],
|
||||
prefix_as_string: str,
|
||||
*,
|
||||
kind: str,
|
||||
) -> None:
|
||||
raise NotImplementedError()
|
||||
290
venv/lib/python3.12/site-packages/pip/_internal/cache.py
Normal file
290
venv/lib/python3.12/site-packages/pip/_internal/cache.py
Normal file
@@ -0,0 +1,290 @@
|
||||
"""Cache Management
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version
|
||||
from pip._vendor.packaging.utils import canonicalize_name
|
||||
|
||||
from pip._internal.exceptions import InvalidWheelFilename
|
||||
from pip._internal.models.direct_url import DirectUrl
|
||||
from pip._internal.models.link import Link
|
||||
from pip._internal.models.wheel import Wheel
|
||||
from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
|
||||
from pip._internal.utils.urls import path_to_url
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ORIGIN_JSON_NAME = "origin.json"
|
||||
|
||||
|
||||
def _hash_dict(d: Dict[str, str]) -> str:
|
||||
"""Return a stable sha224 of a dictionary."""
|
||||
s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
|
||||
return hashlib.sha224(s.encode("ascii")).hexdigest()
|
||||
|
||||
|
||||
class Cache:
|
||||
"""An abstract class - provides cache directories for data from links
|
||||
|
||||
:param cache_dir: The root of the cache.
|
||||
"""
|
||||
|
||||
def __init__(self, cache_dir: str) -> None:
|
||||
super().__init__()
|
||||
assert not cache_dir or os.path.isabs(cache_dir)
|
||||
self.cache_dir = cache_dir or None
|
||||
|
||||
def _get_cache_path_parts(self, link: Link) -> List[str]:
|
||||
"""Get parts of part that must be os.path.joined with cache_dir"""
|
||||
|
||||
# We want to generate an url to use as our cache key, we don't want to
|
||||
# just re-use the URL because it might have other items in the fragment
|
||||
# and we don't care about those.
|
||||
key_parts = {"url": link.url_without_fragment}
|
||||
if link.hash_name is not None and link.hash is not None:
|
||||
key_parts[link.hash_name] = link.hash
|
||||
if link.subdirectory_fragment:
|
||||
key_parts["subdirectory"] = link.subdirectory_fragment
|
||||
|
||||
# Include interpreter name, major and minor version in cache key
|
||||
# to cope with ill-behaved sdists that build a different wheel
|
||||
# depending on the python version their setup.py is being run on,
|
||||
# and don't encode the difference in compatibility tags.
|
||||
# https://github.com/pypa/pip/issues/7296
|
||||
key_parts["interpreter_name"] = interpreter_name()
|
||||
key_parts["interpreter_version"] = interpreter_version()
|
||||
|
||||
# Encode our key url with sha224, we'll use this because it has similar
|
||||
# security properties to sha256, but with a shorter total output (and
|
||||
# thus less secure). However the differences don't make a lot of
|
||||
# difference for our use case here.
|
||||
hashed = _hash_dict(key_parts)
|
||||
|
||||
# We want to nest the directories some to prevent having a ton of top
|
||||
# level directories where we might run out of sub directories on some
|
||||
# FS.
|
||||
parts = [hashed[:2], hashed[2:4], hashed[4:6], hashed[6:]]
|
||||
|
||||
return parts
|
||||
|
||||
def _get_candidates(self, link: Link, canonical_package_name: str) -> List[Any]:
|
||||
can_not_cache = not self.cache_dir or not canonical_package_name or not link
|
||||
if can_not_cache:
|
||||
return []
|
||||
|
||||
path = self.get_path_for_link(link)
|
||||
if os.path.isdir(path):
|
||||
return [(candidate, path) for candidate in os.listdir(path)]
|
||||
return []
|
||||
|
||||
def get_path_for_link(self, link: Link) -> str:
|
||||
"""Return a directory to store cached items in for link."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get(
|
||||
self,
|
||||
link: Link,
|
||||
package_name: Optional[str],
|
||||
supported_tags: List[Tag],
|
||||
) -> Link:
|
||||
"""Returns a link to a cached item if it exists, otherwise returns the
|
||||
passed link.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class SimpleWheelCache(Cache):
|
||||
"""A cache of wheels for future installs."""
|
||||
|
||||
def __init__(self, cache_dir: str) -> None:
|
||||
super().__init__(cache_dir)
|
||||
|
||||
def get_path_for_link(self, link: Link) -> str:
|
||||
"""Return a directory to store cached wheels for link
|
||||
|
||||
Because there are M wheels for any one sdist, we provide a directory
|
||||
to cache them in, and then consult that directory when looking up
|
||||
cache hits.
|
||||
|
||||
We only insert things into the cache if they have plausible version
|
||||
numbers, so that we don't contaminate the cache with things that were
|
||||
not unique. E.g. ./package might have dozens of installs done for it
|
||||
and build a version of 0.0...and if we built and cached a wheel, we'd
|
||||
end up using the same wheel even if the source has been edited.
|
||||
|
||||
:param link: The link of the sdist for which this will cache wheels.
|
||||
"""
|
||||
parts = self._get_cache_path_parts(link)
|
||||
assert self.cache_dir
|
||||
# Store wheels within the root cache_dir
|
||||
return os.path.join(self.cache_dir, "wheels", *parts)
|
||||
|
||||
def get(
|
||||
self,
|
||||
link: Link,
|
||||
package_name: Optional[str],
|
||||
supported_tags: List[Tag],
|
||||
) -> Link:
|
||||
candidates = []
|
||||
|
||||
if not package_name:
|
||||
return link
|
||||
|
||||
canonical_package_name = canonicalize_name(package_name)
|
||||
for wheel_name, wheel_dir in self._get_candidates(link, canonical_package_name):
|
||||
try:
|
||||
wheel = Wheel(wheel_name)
|
||||
except InvalidWheelFilename:
|
||||
continue
|
||||
if canonicalize_name(wheel.name) != canonical_package_name:
|
||||
logger.debug(
|
||||
"Ignoring cached wheel %s for %s as it "
|
||||
"does not match the expected distribution name %s.",
|
||||
wheel_name,
|
||||
link,
|
||||
package_name,
|
||||
)
|
||||
continue
|
||||
if not wheel.supported(supported_tags):
|
||||
# Built for a different python/arch/etc
|
||||
continue
|
||||
candidates.append(
|
||||
(
|
||||
wheel.support_index_min(supported_tags),
|
||||
wheel_name,
|
||||
wheel_dir,
|
||||
)
|
||||
)
|
||||
|
||||
if not candidates:
|
||||
return link
|
||||
|
||||
_, wheel_name, wheel_dir = min(candidates)
|
||||
return Link(path_to_url(os.path.join(wheel_dir, wheel_name)))
|
||||
|
||||
|
||||
class EphemWheelCache(SimpleWheelCache):
|
||||
"""A SimpleWheelCache that creates it's own temporary cache directory"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._temp_dir = TempDirectory(
|
||||
kind=tempdir_kinds.EPHEM_WHEEL_CACHE,
|
||||
globally_managed=True,
|
||||
)
|
||||
|
||||
super().__init__(self._temp_dir.path)
|
||||
|
||||
|
||||
class CacheEntry:
|
||||
def __init__(
|
||||
self,
|
||||
link: Link,
|
||||
persistent: bool,
|
||||
):
|
||||
self.link = link
|
||||
self.persistent = persistent
|
||||
self.origin: Optional[DirectUrl] = None
|
||||
origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME
|
||||
if origin_direct_url_path.exists():
|
||||
try:
|
||||
self.origin = DirectUrl.from_json(
|
||||
origin_direct_url_path.read_text(encoding="utf-8")
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Ignoring invalid cache entry origin file %s for %s (%s)",
|
||||
origin_direct_url_path,
|
||||
link.filename,
|
||||
e,
|
||||
)
|
||||
|
||||
|
||||
class WheelCache(Cache):
|
||||
"""Wraps EphemWheelCache and SimpleWheelCache into a single Cache
|
||||
|
||||
This Cache allows for gracefully degradation, using the ephem wheel cache
|
||||
when a certain link is not found in the simple wheel cache first.
|
||||
"""
|
||||
|
||||
def __init__(self, cache_dir: str) -> None:
|
||||
super().__init__(cache_dir)
|
||||
self._wheel_cache = SimpleWheelCache(cache_dir)
|
||||
self._ephem_cache = EphemWheelCache()
|
||||
|
||||
def get_path_for_link(self, link: Link) -> str:
|
||||
return self._wheel_cache.get_path_for_link(link)
|
||||
|
||||
def get_ephem_path_for_link(self, link: Link) -> str:
|
||||
return self._ephem_cache.get_path_for_link(link)
|
||||
|
||||
def get(
|
||||
self,
|
||||
link: Link,
|
||||
package_name: Optional[str],
|
||||
supported_tags: List[Tag],
|
||||
) -> Link:
|
||||
cache_entry = self.get_cache_entry(link, package_name, supported_tags)
|
||||
if cache_entry is None:
|
||||
return link
|
||||
return cache_entry.link
|
||||
|
||||
def get_cache_entry(
|
||||
self,
|
||||
link: Link,
|
||||
package_name: Optional[str],
|
||||
supported_tags: List[Tag],
|
||||
) -> Optional[CacheEntry]:
|
||||
"""Returns a CacheEntry with a link to a cached item if it exists or
|
||||
None. The cache entry indicates if the item was found in the persistent
|
||||
or ephemeral cache.
|
||||
"""
|
||||
retval = self._wheel_cache.get(
|
||||
link=link,
|
||||
package_name=package_name,
|
||||
supported_tags=supported_tags,
|
||||
)
|
||||
if retval is not link:
|
||||
return CacheEntry(retval, persistent=True)
|
||||
|
||||
retval = self._ephem_cache.get(
|
||||
link=link,
|
||||
package_name=package_name,
|
||||
supported_tags=supported_tags,
|
||||
)
|
||||
if retval is not link:
|
||||
return CacheEntry(retval, persistent=False)
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def record_download_origin(cache_dir: str, download_info: DirectUrl) -> None:
|
||||
origin_path = Path(cache_dir) / ORIGIN_JSON_NAME
|
||||
if origin_path.exists():
|
||||
try:
|
||||
origin = DirectUrl.from_json(origin_path.read_text(encoding="utf-8"))
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Could not read origin file %s in cache entry (%s). "
|
||||
"Will attempt to overwrite it.",
|
||||
origin_path,
|
||||
e,
|
||||
)
|
||||
else:
|
||||
# TODO: use DirectUrl.equivalent when
|
||||
# https://github.com/pypa/pip/pull/10564 is merged.
|
||||
if origin.url != download_info.url:
|
||||
logger.warning(
|
||||
"Origin URL %s in cache entry %s does not match download URL "
|
||||
"%s. This is likely a pip bug or a cache corruption issue. "
|
||||
"Will overwrite it with the new value.",
|
||||
origin.url,
|
||||
cache_dir,
|
||||
download_info.url,
|
||||
)
|
||||
origin_path.write_text(download_info.to_json(), encoding="utf-8")
|
||||
@@ -0,0 +1,4 @@
|
||||
"""Subpackage containing all of pip's command line interface related code
|
||||
"""
|
||||
|
||||
# This file intentionally does not import submodules
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,172 @@
|
||||
"""Logic that powers autocompletion installed by ``pip completion``.
|
||||
"""
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
from itertools import chain
|
||||
from typing import Any, Iterable, List, Optional
|
||||
|
||||
from pip._internal.cli.main_parser import create_main_parser
|
||||
from pip._internal.commands import commands_dict, create_command
|
||||
from pip._internal.metadata import get_default_environment
|
||||
|
||||
|
||||
def autocomplete() -> None:
|
||||
"""Entry Point for completion of main and subcommand options."""
|
||||
# Don't complete if user hasn't sourced bash_completion file.
|
||||
if "PIP_AUTO_COMPLETE" not in os.environ:
|
||||
return
|
||||
cwords = os.environ["COMP_WORDS"].split()[1:]
|
||||
cword = int(os.environ["COMP_CWORD"])
|
||||
try:
|
||||
current = cwords[cword - 1]
|
||||
except IndexError:
|
||||
current = ""
|
||||
|
||||
parser = create_main_parser()
|
||||
subcommands = list(commands_dict)
|
||||
options = []
|
||||
|
||||
# subcommand
|
||||
subcommand_name: Optional[str] = None
|
||||
for word in cwords:
|
||||
if word in subcommands:
|
||||
subcommand_name = word
|
||||
break
|
||||
# subcommand options
|
||||
if subcommand_name is not None:
|
||||
# special case: 'help' subcommand has no options
|
||||
if subcommand_name == "help":
|
||||
sys.exit(1)
|
||||
# special case: list locally installed dists for show and uninstall
|
||||
should_list_installed = not current.startswith("-") and subcommand_name in [
|
||||
"show",
|
||||
"uninstall",
|
||||
]
|
||||
if should_list_installed:
|
||||
env = get_default_environment()
|
||||
lc = current.lower()
|
||||
installed = [
|
||||
dist.canonical_name
|
||||
for dist in env.iter_installed_distributions(local_only=True)
|
||||
if dist.canonical_name.startswith(lc)
|
||||
and dist.canonical_name not in cwords[1:]
|
||||
]
|
||||
# if there are no dists installed, fall back to option completion
|
||||
if installed:
|
||||
for dist in installed:
|
||||
print(dist)
|
||||
sys.exit(1)
|
||||
|
||||
should_list_installables = (
|
||||
not current.startswith("-") and subcommand_name == "install"
|
||||
)
|
||||
if should_list_installables:
|
||||
for path in auto_complete_paths(current, "path"):
|
||||
print(path)
|
||||
sys.exit(1)
|
||||
|
||||
subcommand = create_command(subcommand_name)
|
||||
|
||||
for opt in subcommand.parser.option_list_all:
|
||||
if opt.help != optparse.SUPPRESS_HELP:
|
||||
options += [
|
||||
(opt_str, opt.nargs) for opt_str in opt._long_opts + opt._short_opts
|
||||
]
|
||||
|
||||
# filter out previously specified options from available options
|
||||
prev_opts = [x.split("=")[0] for x in cwords[1 : cword - 1]]
|
||||
options = [(x, v) for (x, v) in options if x not in prev_opts]
|
||||
# filter options by current input
|
||||
options = [(k, v) for k, v in options if k.startswith(current)]
|
||||
# get completion type given cwords and available subcommand options
|
||||
completion_type = get_path_completion_type(
|
||||
cwords,
|
||||
cword,
|
||||
subcommand.parser.option_list_all,
|
||||
)
|
||||
# get completion files and directories if ``completion_type`` is
|
||||
# ``<file>``, ``<dir>`` or ``<path>``
|
||||
if completion_type:
|
||||
paths = auto_complete_paths(current, completion_type)
|
||||
options = [(path, 0) for path in paths]
|
||||
for option in options:
|
||||
opt_label = option[0]
|
||||
# append '=' to options which require args
|
||||
if option[1] and option[0][:2] == "--":
|
||||
opt_label += "="
|
||||
print(opt_label)
|
||||
else:
|
||||
# show main parser options only when necessary
|
||||
|
||||
opts = [i.option_list for i in parser.option_groups]
|
||||
opts.append(parser.option_list)
|
||||
flattened_opts = chain.from_iterable(opts)
|
||||
if current.startswith("-"):
|
||||
for opt in flattened_opts:
|
||||
if opt.help != optparse.SUPPRESS_HELP:
|
||||
subcommands += opt._long_opts + opt._short_opts
|
||||
else:
|
||||
# get completion type given cwords and all available options
|
||||
completion_type = get_path_completion_type(cwords, cword, flattened_opts)
|
||||
if completion_type:
|
||||
subcommands = list(auto_complete_paths(current, completion_type))
|
||||
|
||||
print(" ".join([x for x in subcommands if x.startswith(current)]))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_path_completion_type(
|
||||
cwords: List[str], cword: int, opts: Iterable[Any]
|
||||
) -> Optional[str]:
|
||||
"""Get the type of path completion (``file``, ``dir``, ``path`` or None)
|
||||
|
||||
:param cwords: same as the environmental variable ``COMP_WORDS``
|
||||
:param cword: same as the environmental variable ``COMP_CWORD``
|
||||
:param opts: The available options to check
|
||||
:return: path completion type (``file``, ``dir``, ``path`` or None)
|
||||
"""
|
||||
if cword < 2 or not cwords[cword - 2].startswith("-"):
|
||||
return None
|
||||
for opt in opts:
|
||||
if opt.help == optparse.SUPPRESS_HELP:
|
||||
continue
|
||||
for o in str(opt).split("/"):
|
||||
if cwords[cword - 2].split("=")[0] == o:
|
||||
if not opt.metavar or any(
|
||||
x in ("path", "file", "dir") for x in opt.metavar.split("/")
|
||||
):
|
||||
return opt.metavar
|
||||
return None
|
||||
|
||||
|
||||
def auto_complete_paths(current: str, completion_type: str) -> Iterable[str]:
|
||||
"""If ``completion_type`` is ``file`` or ``path``, list all regular files
|
||||
and directories starting with ``current``; otherwise only list directories
|
||||
starting with ``current``.
|
||||
|
||||
:param current: The word to be completed
|
||||
:param completion_type: path completion type(``file``, ``path`` or ``dir``)
|
||||
:return: A generator of regular files and/or directories
|
||||
"""
|
||||
directory, filename = os.path.split(current)
|
||||
current_path = os.path.abspath(directory)
|
||||
# Don't complete paths if they can't be accessed
|
||||
if not os.access(current_path, os.R_OK):
|
||||
return
|
||||
filename = os.path.normcase(filename)
|
||||
# list all files that start with ``filename``
|
||||
file_list = (
|
||||
x for x in os.listdir(current_path) if os.path.normcase(x).startswith(filename)
|
||||
)
|
||||
for f in file_list:
|
||||
opt = os.path.join(current_path, f)
|
||||
comp_file = os.path.normcase(os.path.join(directory, f))
|
||||
# complete regular files when there is not ``<dir>`` after option
|
||||
# complete directories when there is ``<file>``, ``<path>`` or
|
||||
# ``<dir>``after option
|
||||
if completion_type != "dir" and os.path.isfile(opt):
|
||||
yield comp_file
|
||||
elif os.path.isdir(opt):
|
||||
yield os.path.join(comp_file, "")
|
||||
@@ -0,0 +1,236 @@
|
||||
"""Base Command class, and related routines"""
|
||||
|
||||
import functools
|
||||
import logging
|
||||
import logging.config
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
from optparse import Values
|
||||
from typing import Any, Callable, List, Optional, Tuple
|
||||
|
||||
from pip._vendor.rich import traceback as rich_traceback
|
||||
|
||||
from pip._internal.cli import cmdoptions
|
||||
from pip._internal.cli.command_context import CommandContextMixIn
|
||||
from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
||||
from pip._internal.cli.status_codes import (
|
||||
ERROR,
|
||||
PREVIOUS_BUILD_DIR_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
VIRTUALENV_NOT_FOUND,
|
||||
)
|
||||
from pip._internal.exceptions import (
|
||||
BadCommand,
|
||||
CommandError,
|
||||
DiagnosticPipError,
|
||||
InstallationError,
|
||||
NetworkConnectionError,
|
||||
PreviousBuildDirError,
|
||||
UninstallationError,
|
||||
)
|
||||
from pip._internal.utils.filesystem import check_path_owner
|
||||
from pip._internal.utils.logging import BrokenStdoutLoggingError, setup_logging
|
||||
from pip._internal.utils.misc import get_prog, normalize_path
|
||||
from pip._internal.utils.temp_dir import TempDirectoryTypeRegistry as TempDirRegistry
|
||||
from pip._internal.utils.temp_dir import global_tempdir_manager, tempdir_registry
|
||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
|
||||
__all__ = ["Command"]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(CommandContextMixIn):
|
||||
usage: str = ""
|
||||
ignore_require_venv: bool = False
|
||||
|
||||
def __init__(self, name: str, summary: str, isolated: bool = False) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.name = name
|
||||
self.summary = summary
|
||||
self.parser = ConfigOptionParser(
|
||||
usage=self.usage,
|
||||
prog=f"{get_prog()} {name}",
|
||||
formatter=UpdatingDefaultsHelpFormatter(),
|
||||
add_help_option=False,
|
||||
name=name,
|
||||
description=self.__doc__,
|
||||
isolated=isolated,
|
||||
)
|
||||
|
||||
self.tempdir_registry: Optional[TempDirRegistry] = None
|
||||
|
||||
# Commands should add options to this option group
|
||||
optgroup_name = f"{self.name.capitalize()} Options"
|
||||
self.cmd_opts = optparse.OptionGroup(self.parser, optgroup_name)
|
||||
|
||||
# Add the general options
|
||||
gen_opts = cmdoptions.make_option_group(
|
||||
cmdoptions.general_group,
|
||||
self.parser,
|
||||
)
|
||||
self.parser.add_option_group(gen_opts)
|
||||
|
||||
self.add_options()
|
||||
|
||||
def add_options(self) -> None:
|
||||
pass
|
||||
|
||||
def handle_pip_version_check(self, options: Values) -> None:
|
||||
"""
|
||||
This is a no-op so that commands by default do not do the pip version
|
||||
check.
|
||||
"""
|
||||
# Make sure we do the pip version check if the index_group options
|
||||
# are present.
|
||||
assert not hasattr(options, "no_index")
|
||||
|
||||
def run(self, options: Values, args: List[str]) -> int:
|
||||
raise NotImplementedError
|
||||
|
||||
def parse_args(self, args: List[str]) -> Tuple[Values, List[str]]:
|
||||
# factored out for testability
|
||||
return self.parser.parse_args(args)
|
||||
|
||||
def main(self, args: List[str]) -> int:
|
||||
try:
|
||||
with self.main_context():
|
||||
return self._main(args)
|
||||
finally:
|
||||
logging.shutdown()
|
||||
|
||||
def _main(self, args: List[str]) -> int:
|
||||
# We must initialize this before the tempdir manager, otherwise the
|
||||
# configuration would not be accessible by the time we clean up the
|
||||
# tempdir manager.
|
||||
self.tempdir_registry = self.enter_context(tempdir_registry())
|
||||
# Intentionally set as early as possible so globally-managed temporary
|
||||
# directories are available to the rest of the code.
|
||||
self.enter_context(global_tempdir_manager())
|
||||
|
||||
options, args = self.parse_args(args)
|
||||
|
||||
# Set verbosity so that it can be used elsewhere.
|
||||
self.verbosity = options.verbose - options.quiet
|
||||
|
||||
level_number = setup_logging(
|
||||
verbosity=self.verbosity,
|
||||
no_color=options.no_color,
|
||||
user_log_file=options.log,
|
||||
)
|
||||
|
||||
always_enabled_features = set(options.features_enabled) & set(
|
||||
cmdoptions.ALWAYS_ENABLED_FEATURES
|
||||
)
|
||||
if always_enabled_features:
|
||||
logger.warning(
|
||||
"The following features are always enabled: %s. ",
|
||||
", ".join(sorted(always_enabled_features)),
|
||||
)
|
||||
|
||||
# Make sure that the --python argument isn't specified after the
|
||||
# subcommand. We can tell, because if --python was specified,
|
||||
# we should only reach this point if we're running in the created
|
||||
# subprocess, which has the _PIP_RUNNING_IN_SUBPROCESS environment
|
||||
# variable set.
|
||||
if options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
|
||||
logger.critical(
|
||||
"The --python option must be placed before the pip subcommand name"
|
||||
)
|
||||
sys.exit(ERROR)
|
||||
|
||||
# TODO: Try to get these passing down from the command?
|
||||
# without resorting to os.environ to hold these.
|
||||
# This also affects isolated builds and it should.
|
||||
|
||||
if options.no_input:
|
||||
os.environ["PIP_NO_INPUT"] = "1"
|
||||
|
||||
if options.exists_action:
|
||||
os.environ["PIP_EXISTS_ACTION"] = " ".join(options.exists_action)
|
||||
|
||||
if options.require_venv and not self.ignore_require_venv:
|
||||
# If a venv is required check if it can really be found
|
||||
if not running_under_virtualenv():
|
||||
logger.critical("Could not find an activated virtualenv (required).")
|
||||
sys.exit(VIRTUALENV_NOT_FOUND)
|
||||
|
||||
if options.cache_dir:
|
||||
options.cache_dir = normalize_path(options.cache_dir)
|
||||
if not check_path_owner(options.cache_dir):
|
||||
logger.warning(
|
||||
"The directory '%s' or its parent directory is not owned "
|
||||
"or is not writable by the current user. The cache "
|
||||
"has been disabled. Check the permissions and owner of "
|
||||
"that directory. If executing pip with sudo, you should "
|
||||
"use sudo's -H flag.",
|
||||
options.cache_dir,
|
||||
)
|
||||
options.cache_dir = None
|
||||
|
||||
def intercepts_unhandled_exc(
|
||||
run_func: Callable[..., int]
|
||||
) -> Callable[..., int]:
|
||||
@functools.wraps(run_func)
|
||||
def exc_logging_wrapper(*args: Any) -> int:
|
||||
try:
|
||||
status = run_func(*args)
|
||||
assert isinstance(status, int)
|
||||
return status
|
||||
except DiagnosticPipError as exc:
|
||||
logger.error("%s", exc, extra={"rich": True})
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
|
||||
return ERROR
|
||||
except PreviousBuildDirError as exc:
|
||||
logger.critical(str(exc))
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
|
||||
return PREVIOUS_BUILD_DIR_ERROR
|
||||
except (
|
||||
InstallationError,
|
||||
UninstallationError,
|
||||
BadCommand,
|
||||
NetworkConnectionError,
|
||||
) as exc:
|
||||
logger.critical(str(exc))
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
|
||||
return ERROR
|
||||
except CommandError as exc:
|
||||
logger.critical("%s", exc)
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
|
||||
return ERROR
|
||||
except BrokenStdoutLoggingError:
|
||||
# Bypass our logger and write any remaining messages to
|
||||
# stderr because stdout no longer works.
|
||||
print("ERROR: Pipe to stdout was broken", file=sys.stderr)
|
||||
if level_number <= logging.DEBUG:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
return ERROR
|
||||
except KeyboardInterrupt:
|
||||
logger.critical("Operation cancelled by user")
|
||||
logger.debug("Exception information:", exc_info=True)
|
||||
|
||||
return ERROR
|
||||
except BaseException:
|
||||
logger.critical("Exception:", exc_info=True)
|
||||
|
||||
return UNKNOWN_ERROR
|
||||
|
||||
return exc_logging_wrapper
|
||||
|
||||
try:
|
||||
if not options.debug_mode:
|
||||
run = intercepts_unhandled_exc(self.run)
|
||||
else:
|
||||
run = self.run
|
||||
rich_traceback.install(show_locals=True)
|
||||
return run(options, args)
|
||||
finally:
|
||||
self.handle_pip_version_check(options)
|
||||
1074
venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py
Normal file
1074
venv/lib/python3.12/site-packages/pip/_internal/cli/cmdoptions.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,27 @@
|
||||
from contextlib import ExitStack, contextmanager
|
||||
from typing import ContextManager, Generator, TypeVar
|
||||
|
||||
_T = TypeVar("_T", covariant=True)
|
||||
|
||||
|
||||
class CommandContextMixIn:
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._in_main_context = False
|
||||
self._main_context = ExitStack()
|
||||
|
||||
@contextmanager
|
||||
def main_context(self) -> Generator[None, None, None]:
|
||||
assert not self._in_main_context
|
||||
|
||||
self._in_main_context = True
|
||||
try:
|
||||
with self._main_context:
|
||||
yield
|
||||
finally:
|
||||
self._in_main_context = False
|
||||
|
||||
def enter_context(self, context_provider: ContextManager[_T]) -> _T:
|
||||
assert self._in_main_context
|
||||
|
||||
return self._main_context.enter_context(context_provider)
|
||||
79
venv/lib/python3.12/site-packages/pip/_internal/cli/main.py
Normal file
79
venv/lib/python3.12/site-packages/pip/_internal/cli/main.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""Primary application entrypoint.
|
||||
"""
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from typing import List, Optional
|
||||
|
||||
from pip._internal.cli.autocompletion import autocomplete
|
||||
from pip._internal.cli.main_parser import parse_command
|
||||
from pip._internal.commands import create_command
|
||||
from pip._internal.exceptions import PipError
|
||||
from pip._internal.utils import deprecation
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Do not import and use main() directly! Using it directly is actively
|
||||
# discouraged by pip's maintainers. The name, location and behavior of
|
||||
# this function is subject to change, so calling it directly is not
|
||||
# portable across different pip versions.
|
||||
|
||||
# In addition, running pip in-process is unsupported and unsafe. This is
|
||||
# elaborated in detail at
|
||||
# https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program.
|
||||
# That document also provides suggestions that should work for nearly
|
||||
# all users that are considering importing and using main() directly.
|
||||
|
||||
# However, we know that certain users will still want to invoke pip
|
||||
# in-process. If you understand and accept the implications of using pip
|
||||
# in an unsupported manner, the best approach is to use runpy to avoid
|
||||
# depending on the exact location of this entry point.
|
||||
|
||||
# The following example shows how to use runpy to invoke pip in that
|
||||
# case:
|
||||
#
|
||||
# sys.argv = ["pip", your, args, here]
|
||||
# runpy.run_module("pip", run_name="__main__")
|
||||
#
|
||||
# Note that this will exit the process after running, unlike a direct
|
||||
# call to main. As it is not safe to do any processing after calling
|
||||
# main, this should not be an issue in practice.
|
||||
|
||||
|
||||
def main(args: Optional[List[str]] = None) -> int:
|
||||
if args is None:
|
||||
args = sys.argv[1:]
|
||||
|
||||
# Suppress the pkg_resources deprecation warning
|
||||
# Note - we use a module of .*pkg_resources to cover
|
||||
# the normal case (pip._vendor.pkg_resources) and the
|
||||
# devendored case (a bare pkg_resources)
|
||||
warnings.filterwarnings(
|
||||
action="ignore", category=DeprecationWarning, module=".*pkg_resources"
|
||||
)
|
||||
|
||||
# Configure our deprecation warnings to be sent through loggers
|
||||
deprecation.install_warning_logger()
|
||||
|
||||
autocomplete()
|
||||
|
||||
try:
|
||||
cmd_name, cmd_args = parse_command(args)
|
||||
except PipError as exc:
|
||||
sys.stderr.write(f"ERROR: {exc}")
|
||||
sys.stderr.write(os.linesep)
|
||||
sys.exit(1)
|
||||
|
||||
# Needed for locale.getpreferredencoding(False) to work
|
||||
# in pip._internal.utils.encoding.auto_decode
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
except locale.Error as e:
|
||||
# setlocale can apparently crash if locale are uninitialized
|
||||
logger.debug("Ignoring error %s when setting locale", e)
|
||||
command = create_command(cmd_name, isolated=("--isolated" in cmd_args))
|
||||
|
||||
return command.main(cmd_args)
|
||||
@@ -0,0 +1,134 @@
|
||||
"""A single place for constructing and exposing the main parser
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from pip._internal.build_env import get_runnable_pip
|
||||
from pip._internal.cli import cmdoptions
|
||||
from pip._internal.cli.parser import ConfigOptionParser, UpdatingDefaultsHelpFormatter
|
||||
from pip._internal.commands import commands_dict, get_similar_commands
|
||||
from pip._internal.exceptions import CommandError
|
||||
from pip._internal.utils.misc import get_pip_version, get_prog
|
||||
|
||||
__all__ = ["create_main_parser", "parse_command"]
|
||||
|
||||
|
||||
def create_main_parser() -> ConfigOptionParser:
|
||||
"""Creates and returns the main parser for pip's CLI"""
|
||||
|
||||
parser = ConfigOptionParser(
|
||||
usage="\n%prog <command> [options]",
|
||||
add_help_option=False,
|
||||
formatter=UpdatingDefaultsHelpFormatter(),
|
||||
name="global",
|
||||
prog=get_prog(),
|
||||
)
|
||||
parser.disable_interspersed_args()
|
||||
|
||||
parser.version = get_pip_version()
|
||||
|
||||
# add the general options
|
||||
gen_opts = cmdoptions.make_option_group(cmdoptions.general_group, parser)
|
||||
parser.add_option_group(gen_opts)
|
||||
|
||||
# so the help formatter knows
|
||||
parser.main = True # type: ignore
|
||||
|
||||
# create command listing for description
|
||||
description = [""] + [
|
||||
f"{name:27} {command_info.summary}"
|
||||
for name, command_info in commands_dict.items()
|
||||
]
|
||||
parser.description = "\n".join(description)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def identify_python_interpreter(python: str) -> Optional[str]:
|
||||
# If the named file exists, use it.
|
||||
# If it's a directory, assume it's a virtual environment and
|
||||
# look for the environment's Python executable.
|
||||
if os.path.exists(python):
|
||||
if os.path.isdir(python):
|
||||
# bin/python for Unix, Scripts/python.exe for Windows
|
||||
# Try both in case of odd cases like cygwin.
|
||||
for exe in ("bin/python", "Scripts/python.exe"):
|
||||
py = os.path.join(python, exe)
|
||||
if os.path.exists(py):
|
||||
return py
|
||||
else:
|
||||
return python
|
||||
|
||||
# Could not find the interpreter specified
|
||||
return None
|
||||
|
||||
|
||||
def parse_command(args: List[str]) -> Tuple[str, List[str]]:
|
||||
parser = create_main_parser()
|
||||
|
||||
# Note: parser calls disable_interspersed_args(), so the result of this
|
||||
# call is to split the initial args into the general options before the
|
||||
# subcommand and everything else.
|
||||
# For example:
|
||||
# args: ['--timeout=5', 'install', '--user', 'INITools']
|
||||
# general_options: ['--timeout==5']
|
||||
# args_else: ['install', '--user', 'INITools']
|
||||
general_options, args_else = parser.parse_args(args)
|
||||
|
||||
# --python
|
||||
if general_options.python and "_PIP_RUNNING_IN_SUBPROCESS" not in os.environ:
|
||||
# Re-invoke pip using the specified Python interpreter
|
||||
interpreter = identify_python_interpreter(general_options.python)
|
||||
if interpreter is None:
|
||||
raise CommandError(
|
||||
f"Could not locate Python interpreter {general_options.python}"
|
||||
)
|
||||
|
||||
pip_cmd = [
|
||||
interpreter,
|
||||
get_runnable_pip(),
|
||||
]
|
||||
pip_cmd.extend(args)
|
||||
|
||||
# Set a flag so the child doesn't re-invoke itself, causing
|
||||
# an infinite loop.
|
||||
os.environ["_PIP_RUNNING_IN_SUBPROCESS"] = "1"
|
||||
returncode = 0
|
||||
try:
|
||||
proc = subprocess.run(pip_cmd)
|
||||
returncode = proc.returncode
|
||||
except (subprocess.SubprocessError, OSError) as exc:
|
||||
raise CommandError(f"Failed to run pip under {interpreter}: {exc}")
|
||||
sys.exit(returncode)
|
||||
|
||||
# --version
|
||||
if general_options.version:
|
||||
sys.stdout.write(parser.version)
|
||||
sys.stdout.write(os.linesep)
|
||||
sys.exit()
|
||||
|
||||
# pip || pip help -> print_help()
|
||||
if not args_else or (args_else[0] == "help" and len(args_else) == 1):
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
|
||||
# the subcommand name
|
||||
cmd_name = args_else[0]
|
||||
|
||||
if cmd_name not in commands_dict:
|
||||
guess = get_similar_commands(cmd_name)
|
||||
|
||||
msg = [f'unknown command "{cmd_name}"']
|
||||
if guess:
|
||||
msg.append(f'maybe you meant "{guess}"')
|
||||
|
||||
raise CommandError(" - ".join(msg))
|
||||
|
||||
# all the args without the subcommand
|
||||
cmd_args = args[:]
|
||||
cmd_args.remove(cmd_name)
|
||||
|
||||
return cmd_name, cmd_args
|
||||
294
venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py
Normal file
294
venv/lib/python3.12/site-packages/pip/_internal/cli/parser.py
Normal file
@@ -0,0 +1,294 @@
|
||||
"""Base option parser setup"""
|
||||
|
||||
import logging
|
||||
import optparse
|
||||
import shutil
|
||||
import sys
|
||||
import textwrap
|
||||
from contextlib import suppress
|
||||
from typing import Any, Dict, Generator, List, Tuple
|
||||
|
||||
from pip._internal.cli.status_codes import UNKNOWN_ERROR
|
||||
from pip._internal.configuration import Configuration, ConfigurationError
|
||||
from pip._internal.utils.misc import redact_auth_from_url, strtobool
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PrettyHelpFormatter(optparse.IndentedHelpFormatter):
|
||||
"""A prettier/less verbose help formatter for optparse."""
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
# help position must be aligned with __init__.parseopts.description
|
||||
kwargs["max_help_position"] = 30
|
||||
kwargs["indent_increment"] = 1
|
||||
kwargs["width"] = shutil.get_terminal_size()[0] - 2
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def format_option_strings(self, option: optparse.Option) -> str:
|
||||
return self._format_option_strings(option)
|
||||
|
||||
def _format_option_strings(
|
||||
self, option: optparse.Option, mvarfmt: str = " <{}>", optsep: str = ", "
|
||||
) -> str:
|
||||
"""
|
||||
Return a comma-separated list of option strings and metavars.
|
||||
|
||||
:param option: tuple of (short opt, long opt), e.g: ('-f', '--format')
|
||||
:param mvarfmt: metavar format string
|
||||
:param optsep: separator
|
||||
"""
|
||||
opts = []
|
||||
|
||||
if option._short_opts:
|
||||
opts.append(option._short_opts[0])
|
||||
if option._long_opts:
|
||||
opts.append(option._long_opts[0])
|
||||
if len(opts) > 1:
|
||||
opts.insert(1, optsep)
|
||||
|
||||
if option.takes_value():
|
||||
assert option.dest is not None
|
||||
metavar = option.metavar or option.dest.lower()
|
||||
opts.append(mvarfmt.format(metavar.lower()))
|
||||
|
||||
return "".join(opts)
|
||||
|
||||
def format_heading(self, heading: str) -> str:
|
||||
if heading == "Options":
|
||||
return ""
|
||||
return heading + ":\n"
|
||||
|
||||
def format_usage(self, usage: str) -> str:
|
||||
"""
|
||||
Ensure there is only one newline between usage and the first heading
|
||||
if there is no description.
|
||||
"""
|
||||
msg = "\nUsage: {}\n".format(self.indent_lines(textwrap.dedent(usage), " "))
|
||||
return msg
|
||||
|
||||
def format_description(self, description: str) -> str:
|
||||
# leave full control over description to us
|
||||
if description:
|
||||
if hasattr(self.parser, "main"):
|
||||
label = "Commands"
|
||||
else:
|
||||
label = "Description"
|
||||
# some doc strings have initial newlines, some don't
|
||||
description = description.lstrip("\n")
|
||||
# some doc strings have final newlines and spaces, some don't
|
||||
description = description.rstrip()
|
||||
# dedent, then reindent
|
||||
description = self.indent_lines(textwrap.dedent(description), " ")
|
||||
description = f"{label}:\n{description}\n"
|
||||
return description
|
||||
else:
|
||||
return ""
|
||||
|
||||
def format_epilog(self, epilog: str) -> str:
|
||||
# leave full control over epilog to us
|
||||
if epilog:
|
||||
return epilog
|
||||
else:
|
||||
return ""
|
||||
|
||||
def indent_lines(self, text: str, indent: str) -> str:
|
||||
new_lines = [indent + line for line in text.split("\n")]
|
||||
return "\n".join(new_lines)
|
||||
|
||||
|
||||
class UpdatingDefaultsHelpFormatter(PrettyHelpFormatter):
|
||||
"""Custom help formatter for use in ConfigOptionParser.
|
||||
|
||||
This is updates the defaults before expanding them, allowing
|
||||
them to show up correctly in the help listing.
|
||||
|
||||
Also redact auth from url type options
|
||||
"""
|
||||
|
||||
def expand_default(self, option: optparse.Option) -> str:
|
||||
default_values = None
|
||||
if self.parser is not None:
|
||||
assert isinstance(self.parser, ConfigOptionParser)
|
||||
self.parser._update_defaults(self.parser.defaults)
|
||||
assert option.dest is not None
|
||||
default_values = self.parser.defaults.get(option.dest)
|
||||
help_text = super().expand_default(option)
|
||||
|
||||
if default_values and option.metavar == "URL":
|
||||
if isinstance(default_values, str):
|
||||
default_values = [default_values]
|
||||
|
||||
# If its not a list, we should abort and just return the help text
|
||||
if not isinstance(default_values, list):
|
||||
default_values = []
|
||||
|
||||
for val in default_values:
|
||||
help_text = help_text.replace(val, redact_auth_from_url(val))
|
||||
|
||||
return help_text
|
||||
|
||||
|
||||
class CustomOptionParser(optparse.OptionParser):
|
||||
def insert_option_group(
|
||||
self, idx: int, *args: Any, **kwargs: Any
|
||||
) -> optparse.OptionGroup:
|
||||
"""Insert an OptionGroup at a given position."""
|
||||
group = self.add_option_group(*args, **kwargs)
|
||||
|
||||
self.option_groups.pop()
|
||||
self.option_groups.insert(idx, group)
|
||||
|
||||
return group
|
||||
|
||||
@property
|
||||
def option_list_all(self) -> List[optparse.Option]:
|
||||
"""Get a list of all options, including those in option groups."""
|
||||
res = self.option_list[:]
|
||||
for i in self.option_groups:
|
||||
res.extend(i.option_list)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
class ConfigOptionParser(CustomOptionParser):
|
||||
"""Custom option parser which updates its defaults by checking the
|
||||
configuration files and environmental variables"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*args: Any,
|
||||
name: str,
|
||||
isolated: bool = False,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.config = Configuration(isolated)
|
||||
|
||||
assert self.name
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def check_default(self, option: optparse.Option, key: str, val: Any) -> Any:
|
||||
try:
|
||||
return option.check_value(key, val)
|
||||
except optparse.OptionValueError as exc:
|
||||
print(f"An error occurred during configuration: {exc}")
|
||||
sys.exit(3)
|
||||
|
||||
def _get_ordered_configuration_items(
|
||||
self,
|
||||
) -> Generator[Tuple[str, Any], None, None]:
|
||||
# Configuration gives keys in an unordered manner. Order them.
|
||||
override_order = ["global", self.name, ":env:"]
|
||||
|
||||
# Pool the options into different groups
|
||||
section_items: Dict[str, List[Tuple[str, Any]]] = {
|
||||
name: [] for name in override_order
|
||||
}
|
||||
for section_key, val in self.config.items():
|
||||
# ignore empty values
|
||||
if not val:
|
||||
logger.debug(
|
||||
"Ignoring configuration key '%s' as it's value is empty.",
|
||||
section_key,
|
||||
)
|
||||
continue
|
||||
|
||||
section, key = section_key.split(".", 1)
|
||||
if section in override_order:
|
||||
section_items[section].append((key, val))
|
||||
|
||||
# Yield each group in their override order
|
||||
for section in override_order:
|
||||
for key, val in section_items[section]:
|
||||
yield key, val
|
||||
|
||||
def _update_defaults(self, defaults: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Updates the given defaults with values from the config files and
|
||||
the environ. Does a little special handling for certain types of
|
||||
options (lists)."""
|
||||
|
||||
# Accumulate complex default state.
|
||||
self.values = optparse.Values(self.defaults)
|
||||
late_eval = set()
|
||||
# Then set the options with those values
|
||||
for key, val in self._get_ordered_configuration_items():
|
||||
# '--' because configuration supports only long names
|
||||
option = self.get_option("--" + key)
|
||||
|
||||
# Ignore options not present in this parser. E.g. non-globals put
|
||||
# in [global] by users that want them to apply to all applicable
|
||||
# commands.
|
||||
if option is None:
|
||||
continue
|
||||
|
||||
assert option.dest is not None
|
||||
|
||||
if option.action in ("store_true", "store_false"):
|
||||
try:
|
||||
val = strtobool(val)
|
||||
except ValueError:
|
||||
self.error(
|
||||
f"{val} is not a valid value for {key} option, "
|
||||
"please specify a boolean value like yes/no, "
|
||||
"true/false or 1/0 instead."
|
||||
)
|
||||
elif option.action == "count":
|
||||
with suppress(ValueError):
|
||||
val = strtobool(val)
|
||||
with suppress(ValueError):
|
||||
val = int(val)
|
||||
if not isinstance(val, int) or val < 0:
|
||||
self.error(
|
||||
f"{val} is not a valid value for {key} option, "
|
||||
"please instead specify either a non-negative integer "
|
||||
"or a boolean value like yes/no or false/true "
|
||||
"which is equivalent to 1/0."
|
||||
)
|
||||
elif option.action == "append":
|
||||
val = val.split()
|
||||
val = [self.check_default(option, key, v) for v in val]
|
||||
elif option.action == "callback":
|
||||
assert option.callback is not None
|
||||
late_eval.add(option.dest)
|
||||
opt_str = option.get_opt_string()
|
||||
val = option.convert_value(opt_str, val)
|
||||
# From take_action
|
||||
args = option.callback_args or ()
|
||||
kwargs = option.callback_kwargs or {}
|
||||
option.callback(option, opt_str, val, self, *args, **kwargs)
|
||||
else:
|
||||
val = self.check_default(option, key, val)
|
||||
|
||||
defaults[option.dest] = val
|
||||
|
||||
for key in late_eval:
|
||||
defaults[key] = getattr(self.values, key)
|
||||
self.values = None
|
||||
return defaults
|
||||
|
||||
def get_default_values(self) -> optparse.Values:
|
||||
"""Overriding to make updating the defaults after instantiation of
|
||||
the option parser possible, _update_defaults() does the dirty work."""
|
||||
if not self.process_default_values:
|
||||
# Old, pre-Optik 1.5 behaviour.
|
||||
return optparse.Values(self.defaults)
|
||||
|
||||
# Load the configuration, or error out in case of an error
|
||||
try:
|
||||
self.config.load()
|
||||
except ConfigurationError as err:
|
||||
self.exit(UNKNOWN_ERROR, str(err))
|
||||
|
||||
defaults = self._update_defaults(self.defaults.copy()) # ours
|
||||
for option in self._get_all_options():
|
||||
assert option.dest is not None
|
||||
default = defaults.get(option.dest)
|
||||
if isinstance(default, str):
|
||||
opt_str = option.get_opt_string()
|
||||
defaults[option.dest] = option.check_value(opt_str, default)
|
||||
return optparse.Values(defaults)
|
||||
|
||||
def error(self, msg: str) -> None:
|
||||
self.print_usage(sys.stderr)
|
||||
self.exit(UNKNOWN_ERROR, f"{msg}\n")
|
||||
@@ -0,0 +1,68 @@
|
||||
import functools
|
||||
from typing import Callable, Generator, Iterable, Iterator, Optional, Tuple
|
||||
|
||||
from pip._vendor.rich.progress import (
|
||||
BarColumn,
|
||||
DownloadColumn,
|
||||
FileSizeColumn,
|
||||
Progress,
|
||||
ProgressColumn,
|
||||
SpinnerColumn,
|
||||
TextColumn,
|
||||
TimeElapsedColumn,
|
||||
TimeRemainingColumn,
|
||||
TransferSpeedColumn,
|
||||
)
|
||||
|
||||
from pip._internal.utils.logging import get_indentation
|
||||
|
||||
DownloadProgressRenderer = Callable[[Iterable[bytes]], Iterator[bytes]]
|
||||
|
||||
|
||||
def _rich_progress_bar(
|
||||
iterable: Iterable[bytes],
|
||||
*,
|
||||
bar_type: str,
|
||||
size: int,
|
||||
) -> Generator[bytes, None, None]:
|
||||
assert bar_type == "on", "This should only be used in the default mode."
|
||||
|
||||
if not size:
|
||||
total = float("inf")
|
||||
columns: Tuple[ProgressColumn, ...] = (
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
SpinnerColumn("line", speed=1.5),
|
||||
FileSizeColumn(),
|
||||
TransferSpeedColumn(),
|
||||
TimeElapsedColumn(),
|
||||
)
|
||||
else:
|
||||
total = size
|
||||
columns = (
|
||||
TextColumn("[progress.description]{task.description}"),
|
||||
BarColumn(),
|
||||
DownloadColumn(),
|
||||
TransferSpeedColumn(),
|
||||
TextColumn("eta"),
|
||||
TimeRemainingColumn(),
|
||||
)
|
||||
|
||||
progress = Progress(*columns, refresh_per_second=30)
|
||||
task_id = progress.add_task(" " * (get_indentation() + 2), total=total)
|
||||
with progress:
|
||||
for chunk in iterable:
|
||||
yield chunk
|
||||
progress.update(task_id, advance=len(chunk))
|
||||
|
||||
|
||||
def get_download_progress_renderer(
|
||||
*, bar_type: str, size: Optional[int] = None
|
||||
) -> DownloadProgressRenderer:
|
||||
"""Get an object that can be used to render the download progress.
|
||||
|
||||
Returns a callable, that takes an iterable to "wrap".
|
||||
"""
|
||||
if bar_type == "on":
|
||||
return functools.partial(_rich_progress_bar, bar_type=bar_type, size=size)
|
||||
else:
|
||||
return iter # no-op, when passed an iterator
|
||||
@@ -0,0 +1,505 @@
|
||||
"""Contains the Command base classes that depend on PipSession.
|
||||
|
||||
The classes in this module are in a separate module so the commands not
|
||||
needing download / PackageFinder capability don't unnecessarily import the
|
||||
PackageFinder machinery and all its vendored dependencies, etc.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from functools import partial
|
||||
from optparse import Values
|
||||
from typing import TYPE_CHECKING, Any, List, Optional, Tuple
|
||||
|
||||
from pip._internal.cache import WheelCache
|
||||
from pip._internal.cli import cmdoptions
|
||||
from pip._internal.cli.base_command import Command
|
||||
from pip._internal.cli.command_context import CommandContextMixIn
|
||||
from pip._internal.exceptions import CommandError, PreviousBuildDirError
|
||||
from pip._internal.index.collector import LinkCollector
|
||||
from pip._internal.index.package_finder import PackageFinder
|
||||
from pip._internal.models.selection_prefs import SelectionPreferences
|
||||
from pip._internal.models.target_python import TargetPython
|
||||
from pip._internal.network.session import PipSession
|
||||
from pip._internal.operations.build.build_tracker import BuildTracker
|
||||
from pip._internal.operations.prepare import RequirementPreparer
|
||||
from pip._internal.req.constructors import (
|
||||
install_req_from_editable,
|
||||
install_req_from_line,
|
||||
install_req_from_parsed_requirement,
|
||||
install_req_from_req_string,
|
||||
)
|
||||
from pip._internal.req.req_file import parse_requirements
|
||||
from pip._internal.req.req_install import InstallRequirement
|
||||
from pip._internal.resolution.base import BaseResolver
|
||||
from pip._internal.self_outdated_check import pip_self_version_check
|
||||
from pip._internal.utils.temp_dir import (
|
||||
TempDirectory,
|
||||
TempDirectoryTypeRegistry,
|
||||
tempdir_kinds,
|
||||
)
|
||||
from pip._internal.utils.virtualenv import running_under_virtualenv
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ssl import SSLContext
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _create_truststore_ssl_context() -> Optional["SSLContext"]:
|
||||
if sys.version_info < (3, 10):
|
||||
raise CommandError("The truststore feature is only available for Python 3.10+")
|
||||
|
||||
try:
|
||||
import ssl
|
||||
except ImportError:
|
||||
logger.warning("Disabling truststore since ssl support is missing")
|
||||
return None
|
||||
|
||||
try:
|
||||
from pip._vendor import truststore
|
||||
except ImportError as e:
|
||||
raise CommandError(f"The truststore feature is unavailable: {e}")
|
||||
|
||||
return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
|
||||
|
||||
class SessionCommandMixin(CommandContextMixIn):
|
||||
|
||||
"""
|
||||
A class mixin for command classes needing _build_session().
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._session: Optional[PipSession] = None
|
||||
|
||||
@classmethod
|
||||
def _get_index_urls(cls, options: Values) -> Optional[List[str]]:
|
||||
"""Return a list of index urls from user-provided options."""
|
||||
index_urls = []
|
||||
if not getattr(options, "no_index", False):
|
||||
url = getattr(options, "index_url", None)
|
||||
if url:
|
||||
index_urls.append(url)
|
||||
urls = getattr(options, "extra_index_urls", None)
|
||||
if urls:
|
||||
index_urls.extend(urls)
|
||||
# Return None rather than an empty list
|
||||
return index_urls or None
|
||||
|
||||
def get_default_session(self, options: Values) -> PipSession:
|
||||
"""Get a default-managed session."""
|
||||
if self._session is None:
|
||||
self._session = self.enter_context(self._build_session(options))
|
||||
# there's no type annotation on requests.Session, so it's
|
||||
# automatically ContextManager[Any] and self._session becomes Any,
|
||||
# then https://github.com/python/mypy/issues/7696 kicks in
|
||||
assert self._session is not None
|
||||
return self._session
|
||||
|
||||
def _build_session(
|
||||
self,
|
||||
options: Values,
|
||||
retries: Optional[int] = None,
|
||||
timeout: Optional[int] = None,
|
||||
fallback_to_certifi: bool = False,
|
||||
) -> PipSession:
|
||||
cache_dir = options.cache_dir
|
||||
assert not cache_dir or os.path.isabs(cache_dir)
|
||||
|
||||
if "truststore" in options.features_enabled:
|
||||
try:
|
||||
ssl_context = _create_truststore_ssl_context()
|
||||
except Exception:
|
||||
if not fallback_to_certifi:
|
||||
raise
|
||||
ssl_context = None
|
||||
else:
|
||||
ssl_context = None
|
||||
|
||||
session = PipSession(
|
||||
cache=os.path.join(cache_dir, "http-v2") if cache_dir else None,
|
||||
retries=retries if retries is not None else options.retries,
|
||||
trusted_hosts=options.trusted_hosts,
|
||||
index_urls=self._get_index_urls(options),
|
||||
ssl_context=ssl_context,
|
||||
)
|
||||
|
||||
# Handle custom ca-bundles from the user
|
||||
if options.cert:
|
||||
session.verify = options.cert
|
||||
|
||||
# Handle SSL client certificate
|
||||
if options.client_cert:
|
||||
session.cert = options.client_cert
|
||||
|
||||
# Handle timeouts
|
||||
if options.timeout or timeout:
|
||||
session.timeout = timeout if timeout is not None else options.timeout
|
||||
|
||||
# Handle configured proxies
|
||||
if options.proxy:
|
||||
session.proxies = {
|
||||
"http": options.proxy,
|
||||
"https": options.proxy,
|
||||
}
|
||||
|
||||
# Determine if we can prompt the user for authentication or not
|
||||
session.auth.prompting = not options.no_input
|
||||
session.auth.keyring_provider = options.keyring_provider
|
||||
|
||||
return session
|
||||
|
||||
|
||||
class IndexGroupCommand(Command, SessionCommandMixin):
|
||||
|
||||
"""
|
||||
Abstract base class for commands with the index_group options.
|
||||
|
||||
This also corresponds to the commands that permit the pip version check.
|
||||
"""
|
||||
|
||||
def handle_pip_version_check(self, options: Values) -> None:
|
||||
"""
|
||||
Do the pip version check if not disabled.
|
||||
|
||||
This overrides the default behavior of not doing the check.
|
||||
"""
|
||||
# Make sure the index_group options are present.
|
||||
assert hasattr(options, "no_index")
|
||||
|
||||
if options.disable_pip_version_check or options.no_index:
|
||||
return
|
||||
|
||||
# Otherwise, check if we're using the latest version of pip available.
|
||||
session = self._build_session(
|
||||
options,
|
||||
retries=0,
|
||||
timeout=min(5, options.timeout),
|
||||
# This is set to ensure the function does not fail when truststore is
|
||||
# specified in use-feature but cannot be loaded. This usually raises a
|
||||
# CommandError and shows a nice user-facing error, but this function is not
|
||||
# called in that try-except block.
|
||||
fallback_to_certifi=True,
|
||||
)
|
||||
with session:
|
||||
pip_self_version_check(session, options)
|
||||
|
||||
|
||||
KEEPABLE_TEMPDIR_TYPES = [
|
||||
tempdir_kinds.BUILD_ENV,
|
||||
tempdir_kinds.EPHEM_WHEEL_CACHE,
|
||||
tempdir_kinds.REQ_BUILD,
|
||||
]
|
||||
|
||||
|
||||
def warn_if_run_as_root() -> None:
|
||||
"""Output a warning for sudo users on Unix.
|
||||
|
||||
In a virtual environment, sudo pip still writes to virtualenv.
|
||||
On Windows, users may run pip as Administrator without issues.
|
||||
This warning only applies to Unix root users outside of virtualenv.
|
||||
"""
|
||||
if running_under_virtualenv():
|
||||
return
|
||||
if not hasattr(os, "getuid"):
|
||||
return
|
||||
# On Windows, there are no "system managed" Python packages. Installing as
|
||||
# Administrator via pip is the correct way of updating system environments.
|
||||
#
|
||||
# We choose sys.platform over utils.compat.WINDOWS here to enable Mypy platform
|
||||
# checks: https://mypy.readthedocs.io/en/stable/common_issues.html
|
||||
if sys.platform == "win32" or sys.platform == "cygwin":
|
||||
return
|
||||
|
||||
if os.getuid() != 0:
|
||||
return
|
||||
|
||||
logger.warning(
|
||||
"Running pip as the 'root' user can result in broken permissions and "
|
||||
"conflicting behaviour with the system package manager. "
|
||||
"It is recommended to use a virtual environment instead: "
|
||||
"https://pip.pypa.io/warnings/venv"
|
||||
)
|
||||
|
||||
|
||||
def with_cleanup(func: Any) -> Any:
|
||||
"""Decorator for common logic related to managing temporary
|
||||
directories.
|
||||
"""
|
||||
|
||||
def configure_tempdir_registry(registry: TempDirectoryTypeRegistry) -> None:
|
||||
for t in KEEPABLE_TEMPDIR_TYPES:
|
||||
registry.set_delete(t, False)
|
||||
|
||||
def wrapper(
|
||||
self: RequirementCommand, options: Values, args: List[Any]
|
||||
) -> Optional[int]:
|
||||
assert self.tempdir_registry is not None
|
||||
if options.no_clean:
|
||||
configure_tempdir_registry(self.tempdir_registry)
|
||||
|
||||
try:
|
||||
return func(self, options, args)
|
||||
except PreviousBuildDirError:
|
||||
# This kind of conflict can occur when the user passes an explicit
|
||||
# build directory with a pre-existing folder. In that case we do
|
||||
# not want to accidentally remove it.
|
||||
configure_tempdir_registry(self.tempdir_registry)
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
class RequirementCommand(IndexGroupCommand):
|
||||
def __init__(self, *args: Any, **kw: Any) -> None:
|
||||
super().__init__(*args, **kw)
|
||||
|
||||
self.cmd_opts.add_option(cmdoptions.no_clean())
|
||||
|
||||
@staticmethod
|
||||
def determine_resolver_variant(options: Values) -> str:
|
||||
"""Determines which resolver should be used, based on the given options."""
|
||||
if "legacy-resolver" in options.deprecated_features_enabled:
|
||||
return "legacy"
|
||||
|
||||
return "resolvelib"
|
||||
|
||||
@classmethod
|
||||
def make_requirement_preparer(
|
||||
cls,
|
||||
temp_build_dir: TempDirectory,
|
||||
options: Values,
|
||||
build_tracker: BuildTracker,
|
||||
session: PipSession,
|
||||
finder: PackageFinder,
|
||||
use_user_site: bool,
|
||||
download_dir: Optional[str] = None,
|
||||
verbosity: int = 0,
|
||||
) -> RequirementPreparer:
|
||||
"""
|
||||
Create a RequirementPreparer instance for the given parameters.
|
||||
"""
|
||||
temp_build_dir_path = temp_build_dir.path
|
||||
assert temp_build_dir_path is not None
|
||||
legacy_resolver = False
|
||||
|
||||
resolver_variant = cls.determine_resolver_variant(options)
|
||||
if resolver_variant == "resolvelib":
|
||||
lazy_wheel = "fast-deps" in options.features_enabled
|
||||
if lazy_wheel:
|
||||
logger.warning(
|
||||
"pip is using lazily downloaded wheels using HTTP "
|
||||
"range requests to obtain dependency information. "
|
||||
"This experimental feature is enabled through "
|
||||
"--use-feature=fast-deps and it is not ready for "
|
||||
"production."
|
||||
)
|
||||
else:
|
||||
legacy_resolver = True
|
||||
lazy_wheel = False
|
||||
if "fast-deps" in options.features_enabled:
|
||||
logger.warning(
|
||||
"fast-deps has no effect when used with the legacy resolver."
|
||||
)
|
||||
|
||||
return RequirementPreparer(
|
||||
build_dir=temp_build_dir_path,
|
||||
src_dir=options.src_dir,
|
||||
download_dir=download_dir,
|
||||
build_isolation=options.build_isolation,
|
||||
check_build_deps=options.check_build_deps,
|
||||
build_tracker=build_tracker,
|
||||
session=session,
|
||||
progress_bar=options.progress_bar,
|
||||
finder=finder,
|
||||
require_hashes=options.require_hashes,
|
||||
use_user_site=use_user_site,
|
||||
lazy_wheel=lazy_wheel,
|
||||
verbosity=verbosity,
|
||||
legacy_resolver=legacy_resolver,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def make_resolver(
|
||||
cls,
|
||||
preparer: RequirementPreparer,
|
||||
finder: PackageFinder,
|
||||
options: Values,
|
||||
wheel_cache: Optional[WheelCache] = None,
|
||||
use_user_site: bool = False,
|
||||
ignore_installed: bool = True,
|
||||
ignore_requires_python: bool = False,
|
||||
force_reinstall: bool = False,
|
||||
upgrade_strategy: str = "to-satisfy-only",
|
||||
use_pep517: Optional[bool] = None,
|
||||
py_version_info: Optional[Tuple[int, ...]] = None,
|
||||
) -> BaseResolver:
|
||||
"""
|
||||
Create a Resolver instance for the given parameters.
|
||||
"""
|
||||
make_install_req = partial(
|
||||
install_req_from_req_string,
|
||||
isolated=options.isolated_mode,
|
||||
use_pep517=use_pep517,
|
||||
)
|
||||
resolver_variant = cls.determine_resolver_variant(options)
|
||||
# The long import name and duplicated invocation is needed to convince
|
||||
# Mypy into correctly typechecking. Otherwise it would complain the
|
||||
# "Resolver" class being redefined.
|
||||
if resolver_variant == "resolvelib":
|
||||
import pip._internal.resolution.resolvelib.resolver
|
||||
|
||||
return pip._internal.resolution.resolvelib.resolver.Resolver(
|
||||
preparer=preparer,
|
||||
finder=finder,
|
||||
wheel_cache=wheel_cache,
|
||||
make_install_req=make_install_req,
|
||||
use_user_site=use_user_site,
|
||||
ignore_dependencies=options.ignore_dependencies,
|
||||
ignore_installed=ignore_installed,
|
||||
ignore_requires_python=ignore_requires_python,
|
||||
force_reinstall=force_reinstall,
|
||||
upgrade_strategy=upgrade_strategy,
|
||||
py_version_info=py_version_info,
|
||||
)
|
||||
import pip._internal.resolution.legacy.resolver
|
||||
|
||||
return pip._internal.resolution.legacy.resolver.Resolver(
|
||||
preparer=preparer,
|
||||
finder=finder,
|
||||
wheel_cache=wheel_cache,
|
||||
make_install_req=make_install_req,
|
||||
use_user_site=use_user_site,
|
||||
ignore_dependencies=options.ignore_dependencies,
|
||||
ignore_installed=ignore_installed,
|
||||
ignore_requires_python=ignore_requires_python,
|
||||
force_reinstall=force_reinstall,
|
||||
upgrade_strategy=upgrade_strategy,
|
||||
py_version_info=py_version_info,
|
||||
)
|
||||
|
||||
def get_requirements(
|
||||
self,
|
||||
args: List[str],
|
||||
options: Values,
|
||||
finder: PackageFinder,
|
||||
session: PipSession,
|
||||
) -> List[InstallRequirement]:
|
||||
"""
|
||||
Parse command-line arguments into the corresponding requirements.
|
||||
"""
|
||||
requirements: List[InstallRequirement] = []
|
||||
for filename in options.constraints:
|
||||
for parsed_req in parse_requirements(
|
||||
filename,
|
||||
constraint=True,
|
||||
finder=finder,
|
||||
options=options,
|
||||
session=session,
|
||||
):
|
||||
req_to_add = install_req_from_parsed_requirement(
|
||||
parsed_req,
|
||||
isolated=options.isolated_mode,
|
||||
user_supplied=False,
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
for req in args:
|
||||
req_to_add = install_req_from_line(
|
||||
req,
|
||||
comes_from=None,
|
||||
isolated=options.isolated_mode,
|
||||
use_pep517=options.use_pep517,
|
||||
user_supplied=True,
|
||||
config_settings=getattr(options, "config_settings", None),
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
for req in options.editables:
|
||||
req_to_add = install_req_from_editable(
|
||||
req,
|
||||
user_supplied=True,
|
||||
isolated=options.isolated_mode,
|
||||
use_pep517=options.use_pep517,
|
||||
config_settings=getattr(options, "config_settings", None),
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
# NOTE: options.require_hashes may be set if --require-hashes is True
|
||||
for filename in options.requirements:
|
||||
for parsed_req in parse_requirements(
|
||||
filename, finder=finder, options=options, session=session
|
||||
):
|
||||
req_to_add = install_req_from_parsed_requirement(
|
||||
parsed_req,
|
||||
isolated=options.isolated_mode,
|
||||
use_pep517=options.use_pep517,
|
||||
user_supplied=True,
|
||||
config_settings=parsed_req.options.get("config_settings")
|
||||
if parsed_req.options
|
||||
else None,
|
||||
)
|
||||
requirements.append(req_to_add)
|
||||
|
||||
# If any requirement has hash options, enable hash checking.
|
||||
if any(req.has_hash_options for req in requirements):
|
||||
options.require_hashes = True
|
||||
|
||||
if not (args or options.editables or options.requirements):
|
||||
opts = {"name": self.name}
|
||||
if options.find_links:
|
||||
raise CommandError(
|
||||
"You must give at least one requirement to {name} "
|
||||
'(maybe you meant "pip {name} {links}"?)'.format(
|
||||
**dict(opts, links=" ".join(options.find_links))
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise CommandError(
|
||||
"You must give at least one requirement to {name} "
|
||||
'(see "pip help {name}")'.format(**opts)
|
||||
)
|
||||
|
||||
return requirements
|
||||
|
||||
@staticmethod
|
||||
def trace_basic_info(finder: PackageFinder) -> None:
|
||||
"""
|
||||
Trace basic information about the provided objects.
|
||||
"""
|
||||
# Display where finder is looking for packages
|
||||
search_scope = finder.search_scope
|
||||
locations = search_scope.get_formatted_locations()
|
||||
if locations:
|
||||
logger.info(locations)
|
||||
|
||||
def _build_package_finder(
|
||||
self,
|
||||
options: Values,
|
||||
session: PipSession,
|
||||
target_python: Optional[TargetPython] = None,
|
||||
ignore_requires_python: Optional[bool] = None,
|
||||
) -> PackageFinder:
|
||||
"""
|
||||
Create a package finder appropriate to this requirement command.
|
||||
|
||||
:param ignore_requires_python: Whether to ignore incompatible
|
||||
"Requires-Python" values in links. Defaults to False.
|
||||
"""
|
||||
link_collector = LinkCollector.create(session, options=options)
|
||||
selection_prefs = SelectionPreferences(
|
||||
allow_yanked=True,
|
||||
format_control=options.format_control,
|
||||
allow_all_prereleases=options.pre,
|
||||
prefer_binary=options.prefer_binary,
|
||||
ignore_requires_python=ignore_requires_python,
|
||||
)
|
||||
|
||||
return PackageFinder.create(
|
||||
link_collector=link_collector,
|
||||
selection_prefs=selection_prefs,
|
||||
target_python=target_python,
|
||||
)
|
||||
159
venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py
Normal file
159
venv/lib/python3.12/site-packages/pip/_internal/cli/spinners.py
Normal file
@@ -0,0 +1,159 @@
|
||||
import contextlib
|
||||
import itertools
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
from typing import IO, Generator, Optional
|
||||
|
||||
from pip._internal.utils.compat import WINDOWS
|
||||
from pip._internal.utils.logging import get_indentation
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SpinnerInterface:
|
||||
def spin(self) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def finish(self, final_status: str) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class InteractiveSpinner(SpinnerInterface):
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
file: Optional[IO[str]] = None,
|
||||
spin_chars: str = "-\\|/",
|
||||
# Empirically, 8 updates/second looks nice
|
||||
min_update_interval_seconds: float = 0.125,
|
||||
):
|
||||
self._message = message
|
||||
if file is None:
|
||||
file = sys.stdout
|
||||
self._file = file
|
||||
self._rate_limiter = RateLimiter(min_update_interval_seconds)
|
||||
self._finished = False
|
||||
|
||||
self._spin_cycle = itertools.cycle(spin_chars)
|
||||
|
||||
self._file.write(" " * get_indentation() + self._message + " ... ")
|
||||
self._width = 0
|
||||
|
||||
def _write(self, status: str) -> None:
|
||||
assert not self._finished
|
||||
# Erase what we wrote before by backspacing to the beginning, writing
|
||||
# spaces to overwrite the old text, and then backspacing again
|
||||
backup = "\b" * self._width
|
||||
self._file.write(backup + " " * self._width + backup)
|
||||
# Now we have a blank slate to add our status
|
||||
self._file.write(status)
|
||||
self._width = len(status)
|
||||
self._file.flush()
|
||||
self._rate_limiter.reset()
|
||||
|
||||
def spin(self) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
if not self._rate_limiter.ready():
|
||||
return
|
||||
self._write(next(self._spin_cycle))
|
||||
|
||||
def finish(self, final_status: str) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
self._write(final_status)
|
||||
self._file.write("\n")
|
||||
self._file.flush()
|
||||
self._finished = True
|
||||
|
||||
|
||||
# Used for dumb terminals, non-interactive installs (no tty), etc.
|
||||
# We still print updates occasionally (once every 60 seconds by default) to
|
||||
# act as a keep-alive for systems like Travis-CI that take lack-of-output as
|
||||
# an indication that a task has frozen.
|
||||
class NonInteractiveSpinner(SpinnerInterface):
|
||||
def __init__(self, message: str, min_update_interval_seconds: float = 60.0) -> None:
|
||||
self._message = message
|
||||
self._finished = False
|
||||
self._rate_limiter = RateLimiter(min_update_interval_seconds)
|
||||
self._update("started")
|
||||
|
||||
def _update(self, status: str) -> None:
|
||||
assert not self._finished
|
||||
self._rate_limiter.reset()
|
||||
logger.info("%s: %s", self._message, status)
|
||||
|
||||
def spin(self) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
if not self._rate_limiter.ready():
|
||||
return
|
||||
self._update("still running...")
|
||||
|
||||
def finish(self, final_status: str) -> None:
|
||||
if self._finished:
|
||||
return
|
||||
self._update(f"finished with status '{final_status}'")
|
||||
self._finished = True
|
||||
|
||||
|
||||
class RateLimiter:
|
||||
def __init__(self, min_update_interval_seconds: float) -> None:
|
||||
self._min_update_interval_seconds = min_update_interval_seconds
|
||||
self._last_update: float = 0
|
||||
|
||||
def ready(self) -> bool:
|
||||
now = time.time()
|
||||
delta = now - self._last_update
|
||||
return delta >= self._min_update_interval_seconds
|
||||
|
||||
def reset(self) -> None:
|
||||
self._last_update = time.time()
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def open_spinner(message: str) -> Generator[SpinnerInterface, None, None]:
|
||||
# Interactive spinner goes directly to sys.stdout rather than being routed
|
||||
# through the logging system, but it acts like it has level INFO,
|
||||
# i.e. it's only displayed if we're at level INFO or better.
|
||||
# Non-interactive spinner goes through the logging system, so it is always
|
||||
# in sync with logging configuration.
|
||||
if sys.stdout.isatty() and logger.getEffectiveLevel() <= logging.INFO:
|
||||
spinner: SpinnerInterface = InteractiveSpinner(message)
|
||||
else:
|
||||
spinner = NonInteractiveSpinner(message)
|
||||
try:
|
||||
with hidden_cursor(sys.stdout):
|
||||
yield spinner
|
||||
except KeyboardInterrupt:
|
||||
spinner.finish("canceled")
|
||||
raise
|
||||
except Exception:
|
||||
spinner.finish("error")
|
||||
raise
|
||||
else:
|
||||
spinner.finish("done")
|
||||
|
||||
|
||||
HIDE_CURSOR = "\x1b[?25l"
|
||||
SHOW_CURSOR = "\x1b[?25h"
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def hidden_cursor(file: IO[str]) -> Generator[None, None, None]:
|
||||
# The Windows terminal does not support the hide/show cursor ANSI codes,
|
||||
# even via colorama. So don't even try.
|
||||
if WINDOWS:
|
||||
yield
|
||||
# We don't want to clutter the output with control characters if we're
|
||||
# writing to a file, or if the user is running with --quiet.
|
||||
# See https://github.com/pypa/pip/issues/3418
|
||||
elif not file.isatty() or logger.getEffectiveLevel() > logging.INFO:
|
||||
yield
|
||||
else:
|
||||
file.write(HIDE_CURSOR)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
file.write(SHOW_CURSOR)
|
||||
@@ -0,0 +1,6 @@
|
||||
SUCCESS = 0
|
||||
ERROR = 1
|
||||
UNKNOWN_ERROR = 2
|
||||
VIRTUALENV_NOT_FOUND = 3
|
||||
PREVIOUS_BUILD_DIR_ERROR = 4
|
||||
NO_MATCHES_FOUND = 23
|
||||
@@ -0,0 +1,132 @@
|
||||
"""
|
||||
Package containing all pip commands
|
||||
"""
|
||||
|
||||
import importlib
|
||||
from collections import namedtuple
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pip._internal.cli.base_command import Command
|
||||
|
||||
CommandInfo = namedtuple("CommandInfo", "module_path, class_name, summary")
|
||||
|
||||
# This dictionary does a bunch of heavy lifting for help output:
|
||||
# - Enables avoiding additional (costly) imports for presenting `--help`.
|
||||
# - The ordering matters for help display.
|
||||
#
|
||||
# Even though the module path starts with the same "pip._internal.commands"
|
||||
# prefix, the full path makes testing easier (specifically when modifying
|
||||
# `commands_dict` in test setup / teardown).
|
||||
commands_dict: Dict[str, CommandInfo] = {
|
||||
"install": CommandInfo(
|
||||
"pip._internal.commands.install",
|
||||
"InstallCommand",
|
||||
"Install packages.",
|
||||
),
|
||||
"download": CommandInfo(
|
||||
"pip._internal.commands.download",
|
||||
"DownloadCommand",
|
||||
"Download packages.",
|
||||
),
|
||||
"uninstall": CommandInfo(
|
||||
"pip._internal.commands.uninstall",
|
||||
"UninstallCommand",
|
||||
"Uninstall packages.",
|
||||
),
|
||||
"freeze": CommandInfo(
|
||||
"pip._internal.commands.freeze",
|
||||
"FreezeCommand",
|
||||
"Output installed packages in requirements format.",
|
||||
),
|
||||
"inspect": CommandInfo(
|
||||
"pip._internal.commands.inspect",
|
||||
"InspectCommand",
|
||||
"Inspect the python environment.",
|
||||
),
|
||||
"list": CommandInfo(
|
||||
"pip._internal.commands.list",
|
||||
"ListCommand",
|
||||
"List installed packages.",
|
||||
),
|
||||
"show": CommandInfo(
|
||||
"pip._internal.commands.show",
|
||||
"ShowCommand",
|
||||
"Show information about installed packages.",
|
||||
),
|
||||
"check": CommandInfo(
|
||||
"pip._internal.commands.check",
|
||||
"CheckCommand",
|
||||
"Verify installed packages have compatible dependencies.",
|
||||
),
|
||||
"config": CommandInfo(
|
||||
"pip._internal.commands.configuration",
|
||||
"ConfigurationCommand",
|
||||
"Manage local and global configuration.",
|
||||
),
|
||||
"search": CommandInfo(
|
||||
"pip._internal.commands.search",
|
||||
"SearchCommand",
|
||||
"Search PyPI for packages.",
|
||||
),
|
||||
"cache": CommandInfo(
|
||||
"pip._internal.commands.cache",
|
||||
"CacheCommand",
|
||||
"Inspect and manage pip's wheel cache.",
|
||||
),
|
||||
"index": CommandInfo(
|
||||
"pip._internal.commands.index",
|
||||
"IndexCommand",
|
||||
"Inspect information available from package indexes.",
|
||||
),
|
||||
"wheel": CommandInfo(
|
||||
"pip._internal.commands.wheel",
|
||||
"WheelCommand",
|
||||
"Build wheels from your requirements.",
|
||||
),
|
||||
"hash": CommandInfo(
|
||||
"pip._internal.commands.hash",
|
||||
"HashCommand",
|
||||
"Compute hashes of package archives.",
|
||||
),
|
||||
"completion": CommandInfo(
|
||||
"pip._internal.commands.completion",
|
||||
"CompletionCommand",
|
||||
"A helper command used for command completion.",
|
||||
),
|
||||
"debug": CommandInfo(
|
||||
"pip._internal.commands.debug",
|
||||
"DebugCommand",
|
||||
"Show information useful for debugging.",
|
||||
),
|
||||
"help": CommandInfo(
|
||||
"pip._internal.commands.help",
|
||||
"HelpCommand",
|
||||
"Show help for commands.",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def create_command(name: str, **kwargs: Any) -> Command:
|
||||
"""
|
||||
Create an instance of the Command class with the given name.
|
||||
"""
|
||||
module_path, class_name, summary = commands_dict[name]
|
||||
module = importlib.import_module(module_path)
|
||||
command_class = getattr(module, class_name)
|
||||
command = command_class(name=name, summary=summary, **kwargs)
|
||||
|
||||
return command
|
||||
|
||||
|
||||
def get_similar_commands(name: str) -> Optional[str]:
|
||||
"""Command name auto-correct."""
|
||||
from difflib import get_close_matches
|
||||
|
||||
name = name.lower()
|
||||
|
||||
close_commands = get_close_matches(name, commands_dict.keys())
|
||||
|
||||
if close_commands:
|
||||
return close_commands[0]
|
||||
else:
|
||||
return None
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user