Upload folder using huggingface_hub (part 3)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +2 -0
- .venv/Lib/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/requests/__version__.py +14 -0
- .venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py +50 -0
- .venv/Lib/site-packages/pip/_vendor/requests/adapters.py +719 -0
- .venv/Lib/site-packages/pip/_vendor/requests/api.py +157 -0
- .venv/Lib/site-packages/pip/_vendor/requests/auth.py +314 -0
- .venv/Lib/site-packages/pip/_vendor/requests/certs.py +17 -0
- .venv/Lib/site-packages/pip/_vendor/requests/compat.py +90 -0
- .venv/Lib/site-packages/pip/_vendor/requests/cookies.py +561 -0
- .venv/Lib/site-packages/pip/_vendor/requests/exceptions.py +151 -0
- .venv/Lib/site-packages/pip/_vendor/requests/help.py +127 -0
- .venv/Lib/site-packages/pip/_vendor/requests/hooks.py +33 -0
- .venv/Lib/site-packages/pip/_vendor/requests/models.py +1039 -0
- .venv/Lib/site-packages/pip/_vendor/requests/packages.py +25 -0
- .venv/Lib/site-packages/pip/_vendor/requests/sessions.py +831 -0
- .venv/Lib/site-packages/pip/_vendor/requests/status_codes.py +128 -0
- .venv/Lib/site-packages/pip/_vendor/requests/structures.py +99 -0
- .venv/Lib/site-packages/pip/_vendor/requests/utils.py +1086 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py +27 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py +196 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/py.typed +0 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py +55 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py +27 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py +47 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py +48 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py +57 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py +622 -0
- .venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py +209 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__init__.py +177 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__main__.py +245 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-314.pyc +3 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-314.pyc +0 -0
- .venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-314.pyc +0 -0
.gitattributes
CHANGED
|
@@ -38,3 +38,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 38 |
.venv/Lib/site-packages/pip/_vendor/distlib/t64.exe filter=lfs diff=lfs merge=lfs -text
|
| 39 |
.venv/Lib/site-packages/pip/_vendor/distlib/w64-arm.exe filter=lfs diff=lfs merge=lfs -text
|
| 40 |
.venv/Lib/site-packages/pip/_vendor/distlib/w64.exe filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 38 |
.venv/Lib/site-packages/pip/_vendor/distlib/t64.exe filter=lfs diff=lfs merge=lfs -text
|
| 39 |
.venv/Lib/site-packages/pip/_vendor/distlib/w64-arm.exe filter=lfs diff=lfs merge=lfs -text
|
| 40 |
.venv/Lib/site-packages/pip/_vendor/distlib/w64.exe filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-314.pyc filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/console.cpython-314.pyc filter=lfs diff=lfs merge=lfs -text
|
.venv/Lib/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-314.pyc
ADDED
|
Binary file (1.38 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-314.pyc
ADDED
|
Binary file (27.8 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-314.pyc
ADDED
|
Binary file (6.03 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-314.pyc
ADDED
|
Binary file (5.75 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-314.pyc
ADDED
|
Binary file (37 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/requests/__version__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# .-. .-. .-. . . .-. .-. .-. .-.
|
| 2 |
+
# |( |- |.| | | |- `-. | `-.
|
| 3 |
+
# ' ' `-' `-`.`-' `-' `-' ' `-'
|
| 4 |
+
|
| 5 |
+
__title__ = "requests"
|
| 6 |
+
__description__ = "Python HTTP for Humans."
|
| 7 |
+
__url__ = "https://requests.readthedocs.io"
|
| 8 |
+
__version__ = "2.32.4"
|
| 9 |
+
__build__ = 0x023204
|
| 10 |
+
__author__ = "Kenneth Reitz"
|
| 11 |
+
__author_email__ = "me@kennethreitz.org"
|
| 12 |
+
__license__ = "Apache-2.0"
|
| 13 |
+
__copyright__ = "Copyright Kenneth Reitz"
|
| 14 |
+
__cake__ = "\u2728 \U0001f370 \u2728"
|
.venv/Lib/site-packages/pip/_vendor/requests/_internal_utils.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests._internal_utils
|
| 3 |
+
~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
Provides utility functions that are consumed internally by Requests
|
| 6 |
+
which depend on extremely few external helpers (such as compat)
|
| 7 |
+
"""
|
| 8 |
+
import re
|
| 9 |
+
|
| 10 |
+
from .compat import builtin_str
|
| 11 |
+
|
| 12 |
+
_VALID_HEADER_NAME_RE_BYTE = re.compile(rb"^[^:\s][^:\r\n]*$")
|
| 13 |
+
_VALID_HEADER_NAME_RE_STR = re.compile(r"^[^:\s][^:\r\n]*$")
|
| 14 |
+
_VALID_HEADER_VALUE_RE_BYTE = re.compile(rb"^\S[^\r\n]*$|^$")
|
| 15 |
+
_VALID_HEADER_VALUE_RE_STR = re.compile(r"^\S[^\r\n]*$|^$")
|
| 16 |
+
|
| 17 |
+
_HEADER_VALIDATORS_STR = (_VALID_HEADER_NAME_RE_STR, _VALID_HEADER_VALUE_RE_STR)
|
| 18 |
+
_HEADER_VALIDATORS_BYTE = (_VALID_HEADER_NAME_RE_BYTE, _VALID_HEADER_VALUE_RE_BYTE)
|
| 19 |
+
HEADER_VALIDATORS = {
|
| 20 |
+
bytes: _HEADER_VALIDATORS_BYTE,
|
| 21 |
+
str: _HEADER_VALIDATORS_STR,
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def to_native_string(string, encoding="ascii"):
|
| 26 |
+
"""Given a string object, regardless of type, returns a representation of
|
| 27 |
+
that string in the native string type, encoding and decoding where
|
| 28 |
+
necessary. This assumes ASCII unless told otherwise.
|
| 29 |
+
"""
|
| 30 |
+
if isinstance(string, builtin_str):
|
| 31 |
+
out = string
|
| 32 |
+
else:
|
| 33 |
+
out = string.decode(encoding)
|
| 34 |
+
|
| 35 |
+
return out
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def unicode_is_ascii(u_string):
|
| 39 |
+
"""Determine if unicode string only contains ASCII characters.
|
| 40 |
+
|
| 41 |
+
:param str u_string: unicode string to check. Must be unicode
|
| 42 |
+
and not Python 2 `str`.
|
| 43 |
+
:rtype: bool
|
| 44 |
+
"""
|
| 45 |
+
assert isinstance(u_string, str)
|
| 46 |
+
try:
|
| 47 |
+
u_string.encode("ascii")
|
| 48 |
+
return True
|
| 49 |
+
except UnicodeEncodeError:
|
| 50 |
+
return False
|
.venv/Lib/site-packages/pip/_vendor/requests/adapters.py
ADDED
|
@@ -0,0 +1,719 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.adapters
|
| 3 |
+
~~~~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module contains the transport adapters that Requests uses to define
|
| 6 |
+
and maintain connections.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import os.path
|
| 10 |
+
import socket # noqa: F401
|
| 11 |
+
import typing
|
| 12 |
+
import warnings
|
| 13 |
+
|
| 14 |
+
from pip._vendor.urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
|
| 15 |
+
from pip._vendor.urllib3.exceptions import HTTPError as _HTTPError
|
| 16 |
+
from pip._vendor.urllib3.exceptions import InvalidHeader as _InvalidHeader
|
| 17 |
+
from pip._vendor.urllib3.exceptions import (
|
| 18 |
+
LocationValueError,
|
| 19 |
+
MaxRetryError,
|
| 20 |
+
NewConnectionError,
|
| 21 |
+
ProtocolError,
|
| 22 |
+
)
|
| 23 |
+
from pip._vendor.urllib3.exceptions import ProxyError as _ProxyError
|
| 24 |
+
from pip._vendor.urllib3.exceptions import ReadTimeoutError, ResponseError
|
| 25 |
+
from pip._vendor.urllib3.exceptions import SSLError as _SSLError
|
| 26 |
+
from pip._vendor.urllib3.poolmanager import PoolManager, proxy_from_url
|
| 27 |
+
from pip._vendor.urllib3.util import Timeout as TimeoutSauce
|
| 28 |
+
from pip._vendor.urllib3.util import parse_url
|
| 29 |
+
from pip._vendor.urllib3.util.retry import Retry
|
| 30 |
+
from pip._vendor.urllib3.util.ssl_ import create_urllib3_context
|
| 31 |
+
|
| 32 |
+
from .auth import _basic_auth_str
|
| 33 |
+
from .compat import basestring, urlparse
|
| 34 |
+
from .cookies import extract_cookies_to_jar
|
| 35 |
+
from .exceptions import (
|
| 36 |
+
ConnectionError,
|
| 37 |
+
ConnectTimeout,
|
| 38 |
+
InvalidHeader,
|
| 39 |
+
InvalidProxyURL,
|
| 40 |
+
InvalidSchema,
|
| 41 |
+
InvalidURL,
|
| 42 |
+
ProxyError,
|
| 43 |
+
ReadTimeout,
|
| 44 |
+
RetryError,
|
| 45 |
+
SSLError,
|
| 46 |
+
)
|
| 47 |
+
from .models import Response
|
| 48 |
+
from .structures import CaseInsensitiveDict
|
| 49 |
+
from .utils import (
|
| 50 |
+
DEFAULT_CA_BUNDLE_PATH,
|
| 51 |
+
extract_zipped_paths,
|
| 52 |
+
get_auth_from_url,
|
| 53 |
+
get_encoding_from_headers,
|
| 54 |
+
prepend_scheme_if_needed,
|
| 55 |
+
select_proxy,
|
| 56 |
+
urldefragauth,
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
try:
|
| 60 |
+
from pip._vendor.urllib3.contrib.socks import SOCKSProxyManager
|
| 61 |
+
except ImportError:
|
| 62 |
+
|
| 63 |
+
def SOCKSProxyManager(*args, **kwargs):
|
| 64 |
+
raise InvalidSchema("Missing dependencies for SOCKS support.")
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
if typing.TYPE_CHECKING:
|
| 68 |
+
from .models import PreparedRequest
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
DEFAULT_POOLBLOCK = False
|
| 72 |
+
DEFAULT_POOLSIZE = 10
|
| 73 |
+
DEFAULT_RETRIES = 0
|
| 74 |
+
DEFAULT_POOL_TIMEOUT = None
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
try:
|
| 78 |
+
import ssl # noqa: F401
|
| 79 |
+
|
| 80 |
+
_preloaded_ssl_context = create_urllib3_context()
|
| 81 |
+
_preloaded_ssl_context.load_verify_locations(
|
| 82 |
+
extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
|
| 83 |
+
)
|
| 84 |
+
except ImportError:
|
| 85 |
+
# Bypass default SSLContext creation when Python
|
| 86 |
+
# interpreter isn't built with the ssl module.
|
| 87 |
+
_preloaded_ssl_context = None
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _urllib3_request_context(
|
| 91 |
+
request: "PreparedRequest",
|
| 92 |
+
verify: "bool | str | None",
|
| 93 |
+
client_cert: "typing.Tuple[str, str] | str | None",
|
| 94 |
+
poolmanager: "PoolManager",
|
| 95 |
+
) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])":
|
| 96 |
+
host_params = {}
|
| 97 |
+
pool_kwargs = {}
|
| 98 |
+
parsed_request_url = urlparse(request.url)
|
| 99 |
+
scheme = parsed_request_url.scheme.lower()
|
| 100 |
+
port = parsed_request_url.port
|
| 101 |
+
|
| 102 |
+
# Determine if we have and should use our default SSLContext
|
| 103 |
+
# to optimize performance on standard requests.
|
| 104 |
+
poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {})
|
| 105 |
+
has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context")
|
| 106 |
+
should_use_default_ssl_context = (
|
| 107 |
+
_preloaded_ssl_context is not None and not has_poolmanager_ssl_context
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
cert_reqs = "CERT_REQUIRED"
|
| 111 |
+
if verify is False:
|
| 112 |
+
cert_reqs = "CERT_NONE"
|
| 113 |
+
elif verify is True and should_use_default_ssl_context:
|
| 114 |
+
pool_kwargs["ssl_context"] = _preloaded_ssl_context
|
| 115 |
+
elif isinstance(verify, str):
|
| 116 |
+
if not os.path.isdir(verify):
|
| 117 |
+
pool_kwargs["ca_certs"] = verify
|
| 118 |
+
else:
|
| 119 |
+
pool_kwargs["ca_cert_dir"] = verify
|
| 120 |
+
pool_kwargs["cert_reqs"] = cert_reqs
|
| 121 |
+
if client_cert is not None:
|
| 122 |
+
if isinstance(client_cert, tuple) and len(client_cert) == 2:
|
| 123 |
+
pool_kwargs["cert_file"] = client_cert[0]
|
| 124 |
+
pool_kwargs["key_file"] = client_cert[1]
|
| 125 |
+
else:
|
| 126 |
+
# According to our docs, we allow users to specify just the client
|
| 127 |
+
# cert path
|
| 128 |
+
pool_kwargs["cert_file"] = client_cert
|
| 129 |
+
host_params = {
|
| 130 |
+
"scheme": scheme,
|
| 131 |
+
"host": parsed_request_url.hostname,
|
| 132 |
+
"port": port,
|
| 133 |
+
}
|
| 134 |
+
return host_params, pool_kwargs
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
class BaseAdapter:
|
| 138 |
+
"""The Base Transport Adapter"""
|
| 139 |
+
|
| 140 |
+
def __init__(self):
|
| 141 |
+
super().__init__()
|
| 142 |
+
|
| 143 |
+
def send(
|
| 144 |
+
self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None
|
| 145 |
+
):
|
| 146 |
+
"""Sends PreparedRequest object. Returns Response object.
|
| 147 |
+
|
| 148 |
+
:param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
|
| 149 |
+
:param stream: (optional) Whether to stream the request content.
|
| 150 |
+
:param timeout: (optional) How long to wait for the server to send
|
| 151 |
+
data before giving up, as a float, or a :ref:`(connect timeout,
|
| 152 |
+
read timeout) <timeouts>` tuple.
|
| 153 |
+
:type timeout: float or tuple
|
| 154 |
+
:param verify: (optional) Either a boolean, in which case it controls whether we verify
|
| 155 |
+
the server's TLS certificate, or a string, in which case it must be a path
|
| 156 |
+
to a CA bundle to use
|
| 157 |
+
:param cert: (optional) Any user-provided SSL certificate to be trusted.
|
| 158 |
+
:param proxies: (optional) The proxies dictionary to apply to the request.
|
| 159 |
+
"""
|
| 160 |
+
raise NotImplementedError
|
| 161 |
+
|
| 162 |
+
def close(self):
|
| 163 |
+
"""Cleans up adapter specific items."""
|
| 164 |
+
raise NotImplementedError
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
class HTTPAdapter(BaseAdapter):
|
| 168 |
+
"""The built-in HTTP Adapter for urllib3.
|
| 169 |
+
|
| 170 |
+
Provides a general-case interface for Requests sessions to contact HTTP and
|
| 171 |
+
HTTPS urls by implementing the Transport Adapter interface. This class will
|
| 172 |
+
usually be created by the :class:`Session <Session>` class under the
|
| 173 |
+
covers.
|
| 174 |
+
|
| 175 |
+
:param pool_connections: The number of urllib3 connection pools to cache.
|
| 176 |
+
:param pool_maxsize: The maximum number of connections to save in the pool.
|
| 177 |
+
:param max_retries: The maximum number of retries each connection
|
| 178 |
+
should attempt. Note, this applies only to failed DNS lookups, socket
|
| 179 |
+
connections and connection timeouts, never to requests where data has
|
| 180 |
+
made it to the server. By default, Requests does not retry failed
|
| 181 |
+
connections. If you need granular control over the conditions under
|
| 182 |
+
which we retry a request, import urllib3's ``Retry`` class and pass
|
| 183 |
+
that instead.
|
| 184 |
+
:param pool_block: Whether the connection pool should block for connections.
|
| 185 |
+
|
| 186 |
+
Usage::
|
| 187 |
+
|
| 188 |
+
>>> import requests
|
| 189 |
+
>>> s = requests.Session()
|
| 190 |
+
>>> a = requests.adapters.HTTPAdapter(max_retries=3)
|
| 191 |
+
>>> s.mount('http://', a)
|
| 192 |
+
"""
|
| 193 |
+
|
| 194 |
+
__attrs__ = [
|
| 195 |
+
"max_retries",
|
| 196 |
+
"config",
|
| 197 |
+
"_pool_connections",
|
| 198 |
+
"_pool_maxsize",
|
| 199 |
+
"_pool_block",
|
| 200 |
+
]
|
| 201 |
+
|
| 202 |
+
def __init__(
|
| 203 |
+
self,
|
| 204 |
+
pool_connections=DEFAULT_POOLSIZE,
|
| 205 |
+
pool_maxsize=DEFAULT_POOLSIZE,
|
| 206 |
+
max_retries=DEFAULT_RETRIES,
|
| 207 |
+
pool_block=DEFAULT_POOLBLOCK,
|
| 208 |
+
):
|
| 209 |
+
if max_retries == DEFAULT_RETRIES:
|
| 210 |
+
self.max_retries = Retry(0, read=False)
|
| 211 |
+
else:
|
| 212 |
+
self.max_retries = Retry.from_int(max_retries)
|
| 213 |
+
self.config = {}
|
| 214 |
+
self.proxy_manager = {}
|
| 215 |
+
|
| 216 |
+
super().__init__()
|
| 217 |
+
|
| 218 |
+
self._pool_connections = pool_connections
|
| 219 |
+
self._pool_maxsize = pool_maxsize
|
| 220 |
+
self._pool_block = pool_block
|
| 221 |
+
|
| 222 |
+
self.init_poolmanager(pool_connections, pool_maxsize, block=pool_block)
|
| 223 |
+
|
| 224 |
+
def __getstate__(self):
|
| 225 |
+
return {attr: getattr(self, attr, None) for attr in self.__attrs__}
|
| 226 |
+
|
| 227 |
+
def __setstate__(self, state):
|
| 228 |
+
# Can't handle by adding 'proxy_manager' to self.__attrs__ because
|
| 229 |
+
# self.poolmanager uses a lambda function, which isn't pickleable.
|
| 230 |
+
self.proxy_manager = {}
|
| 231 |
+
self.config = {}
|
| 232 |
+
|
| 233 |
+
for attr, value in state.items():
|
| 234 |
+
setattr(self, attr, value)
|
| 235 |
+
|
| 236 |
+
self.init_poolmanager(
|
| 237 |
+
self._pool_connections, self._pool_maxsize, block=self._pool_block
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
def init_poolmanager(
|
| 241 |
+
self, connections, maxsize, block=DEFAULT_POOLBLOCK, **pool_kwargs
|
| 242 |
+
):
|
| 243 |
+
"""Initializes a urllib3 PoolManager.
|
| 244 |
+
|
| 245 |
+
This method should not be called from user code, and is only
|
| 246 |
+
exposed for use when subclassing the
|
| 247 |
+
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 248 |
+
|
| 249 |
+
:param connections: The number of urllib3 connection pools to cache.
|
| 250 |
+
:param maxsize: The maximum number of connections to save in the pool.
|
| 251 |
+
:param block: Block when no free connections are available.
|
| 252 |
+
:param pool_kwargs: Extra keyword arguments used to initialize the Pool Manager.
|
| 253 |
+
"""
|
| 254 |
+
# save these values for pickling
|
| 255 |
+
self._pool_connections = connections
|
| 256 |
+
self._pool_maxsize = maxsize
|
| 257 |
+
self._pool_block = block
|
| 258 |
+
|
| 259 |
+
self.poolmanager = PoolManager(
|
| 260 |
+
num_pools=connections,
|
| 261 |
+
maxsize=maxsize,
|
| 262 |
+
block=block,
|
| 263 |
+
**pool_kwargs,
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
def proxy_manager_for(self, proxy, **proxy_kwargs):
|
| 267 |
+
"""Return urllib3 ProxyManager for the given proxy.
|
| 268 |
+
|
| 269 |
+
This method should not be called from user code, and is only
|
| 270 |
+
exposed for use when subclassing the
|
| 271 |
+
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 272 |
+
|
| 273 |
+
:param proxy: The proxy to return a urllib3 ProxyManager for.
|
| 274 |
+
:param proxy_kwargs: Extra keyword arguments used to configure the Proxy Manager.
|
| 275 |
+
:returns: ProxyManager
|
| 276 |
+
:rtype: urllib3.ProxyManager
|
| 277 |
+
"""
|
| 278 |
+
if proxy in self.proxy_manager:
|
| 279 |
+
manager = self.proxy_manager[proxy]
|
| 280 |
+
elif proxy.lower().startswith("socks"):
|
| 281 |
+
username, password = get_auth_from_url(proxy)
|
| 282 |
+
manager = self.proxy_manager[proxy] = SOCKSProxyManager(
|
| 283 |
+
proxy,
|
| 284 |
+
username=username,
|
| 285 |
+
password=password,
|
| 286 |
+
num_pools=self._pool_connections,
|
| 287 |
+
maxsize=self._pool_maxsize,
|
| 288 |
+
block=self._pool_block,
|
| 289 |
+
**proxy_kwargs,
|
| 290 |
+
)
|
| 291 |
+
else:
|
| 292 |
+
proxy_headers = self.proxy_headers(proxy)
|
| 293 |
+
manager = self.proxy_manager[proxy] = proxy_from_url(
|
| 294 |
+
proxy,
|
| 295 |
+
proxy_headers=proxy_headers,
|
| 296 |
+
num_pools=self._pool_connections,
|
| 297 |
+
maxsize=self._pool_maxsize,
|
| 298 |
+
block=self._pool_block,
|
| 299 |
+
**proxy_kwargs,
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
return manager
|
| 303 |
+
|
| 304 |
+
def cert_verify(self, conn, url, verify, cert):
|
| 305 |
+
"""Verify a SSL certificate. This method should not be called from user
|
| 306 |
+
code, and is only exposed for use when subclassing the
|
| 307 |
+
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 308 |
+
|
| 309 |
+
:param conn: The urllib3 connection object associated with the cert.
|
| 310 |
+
:param url: The requested URL.
|
| 311 |
+
:param verify: Either a boolean, in which case it controls whether we verify
|
| 312 |
+
the server's TLS certificate, or a string, in which case it must be a path
|
| 313 |
+
to a CA bundle to use
|
| 314 |
+
:param cert: The SSL certificate to verify.
|
| 315 |
+
"""
|
| 316 |
+
if url.lower().startswith("https") and verify:
|
| 317 |
+
conn.cert_reqs = "CERT_REQUIRED"
|
| 318 |
+
|
| 319 |
+
# Only load the CA certificates if 'verify' is a string indicating the CA bundle to use.
|
| 320 |
+
# Otherwise, if verify is a boolean, we don't load anything since
|
| 321 |
+
# the connection will be using a context with the default certificates already loaded,
|
| 322 |
+
# and this avoids a call to the slow load_verify_locations()
|
| 323 |
+
if verify is not True:
|
| 324 |
+
# `verify` must be a str with a path then
|
| 325 |
+
cert_loc = verify
|
| 326 |
+
|
| 327 |
+
if not os.path.exists(cert_loc):
|
| 328 |
+
raise OSError(
|
| 329 |
+
f"Could not find a suitable TLS CA certificate bundle, "
|
| 330 |
+
f"invalid path: {cert_loc}"
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
if not os.path.isdir(cert_loc):
|
| 334 |
+
conn.ca_certs = cert_loc
|
| 335 |
+
else:
|
| 336 |
+
conn.ca_cert_dir = cert_loc
|
| 337 |
+
else:
|
| 338 |
+
conn.cert_reqs = "CERT_NONE"
|
| 339 |
+
conn.ca_certs = None
|
| 340 |
+
conn.ca_cert_dir = None
|
| 341 |
+
|
| 342 |
+
if cert:
|
| 343 |
+
if not isinstance(cert, basestring):
|
| 344 |
+
conn.cert_file = cert[0]
|
| 345 |
+
conn.key_file = cert[1]
|
| 346 |
+
else:
|
| 347 |
+
conn.cert_file = cert
|
| 348 |
+
conn.key_file = None
|
| 349 |
+
if conn.cert_file and not os.path.exists(conn.cert_file):
|
| 350 |
+
raise OSError(
|
| 351 |
+
f"Could not find the TLS certificate file, "
|
| 352 |
+
f"invalid path: {conn.cert_file}"
|
| 353 |
+
)
|
| 354 |
+
if conn.key_file and not os.path.exists(conn.key_file):
|
| 355 |
+
raise OSError(
|
| 356 |
+
f"Could not find the TLS key file, invalid path: {conn.key_file}"
|
| 357 |
+
)
|
| 358 |
+
|
| 359 |
+
def build_response(self, req, resp):
|
| 360 |
+
"""Builds a :class:`Response <requests.Response>` object from a urllib3
|
| 361 |
+
response. This should not be called from user code, and is only exposed
|
| 362 |
+
for use when subclassing the
|
| 363 |
+
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`
|
| 364 |
+
|
| 365 |
+
:param req: The :class:`PreparedRequest <PreparedRequest>` used to generate the response.
|
| 366 |
+
:param resp: The urllib3 response object.
|
| 367 |
+
:rtype: requests.Response
|
| 368 |
+
"""
|
| 369 |
+
response = Response()
|
| 370 |
+
|
| 371 |
+
# Fallback to None if there's no status_code, for whatever reason.
|
| 372 |
+
response.status_code = getattr(resp, "status", None)
|
| 373 |
+
|
| 374 |
+
# Make headers case-insensitive.
|
| 375 |
+
response.headers = CaseInsensitiveDict(getattr(resp, "headers", {}))
|
| 376 |
+
|
| 377 |
+
# Set encoding.
|
| 378 |
+
response.encoding = get_encoding_from_headers(response.headers)
|
| 379 |
+
response.raw = resp
|
| 380 |
+
response.reason = response.raw.reason
|
| 381 |
+
|
| 382 |
+
if isinstance(req.url, bytes):
|
| 383 |
+
response.url = req.url.decode("utf-8")
|
| 384 |
+
else:
|
| 385 |
+
response.url = req.url
|
| 386 |
+
|
| 387 |
+
# Add new cookies from the server.
|
| 388 |
+
extract_cookies_to_jar(response.cookies, req, resp)
|
| 389 |
+
|
| 390 |
+
# Give the Response some context.
|
| 391 |
+
response.request = req
|
| 392 |
+
response.connection = self
|
| 393 |
+
|
| 394 |
+
return response
|
| 395 |
+
|
| 396 |
+
def build_connection_pool_key_attributes(self, request, verify, cert=None):
|
| 397 |
+
"""Build the PoolKey attributes used by urllib3 to return a connection.
|
| 398 |
+
|
| 399 |
+
This looks at the PreparedRequest, the user-specified verify value,
|
| 400 |
+
and the value of the cert parameter to determine what PoolKey values
|
| 401 |
+
to use to select a connection from a given urllib3 Connection Pool.
|
| 402 |
+
|
| 403 |
+
The SSL related pool key arguments are not consistently set. As of
|
| 404 |
+
this writing, use the following to determine what keys may be in that
|
| 405 |
+
dictionary:
|
| 406 |
+
|
| 407 |
+
* If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the
|
| 408 |
+
default Requests SSL Context
|
| 409 |
+
* If ``verify`` is ``False``, ``"ssl_context"`` will not be set but
|
| 410 |
+
``"cert_reqs"`` will be set
|
| 411 |
+
* If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
|
| 412 |
+
``"ca_certs"`` will be set if the string is not a directory recognized
|
| 413 |
+
by :py:func:`os.path.isdir`, otherwise ``"ca_certs_dir"`` will be
|
| 414 |
+
set.
|
| 415 |
+
* If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
|
| 416 |
+
``"cert"`` is a tuple with a second item, ``"key_file"`` will also
|
| 417 |
+
be present
|
| 418 |
+
|
| 419 |
+
To override these settings, one may subclass this class, call this
|
| 420 |
+
method and use the above logic to change parameters as desired. For
|
| 421 |
+
example, if one wishes to use a custom :py:class:`ssl.SSLContext` one
|
| 422 |
+
must both set ``"ssl_context"`` and based on what else they require,
|
| 423 |
+
alter the other keys to ensure the desired behaviour.
|
| 424 |
+
|
| 425 |
+
:param request:
|
| 426 |
+
The PreparedReqest being sent over the connection.
|
| 427 |
+
:type request:
|
| 428 |
+
:class:`~requests.models.PreparedRequest`
|
| 429 |
+
:param verify:
|
| 430 |
+
Either a boolean, in which case it controls whether
|
| 431 |
+
we verify the server's TLS certificate, or a string, in which case it
|
| 432 |
+
must be a path to a CA bundle to use.
|
| 433 |
+
:param cert:
|
| 434 |
+
(optional) Any user-provided SSL certificate for client
|
| 435 |
+
authentication (a.k.a., mTLS). This may be a string (i.e., just
|
| 436 |
+
the path to a file which holds both certificate and key) or a
|
| 437 |
+
tuple of length 2 with the certificate file path and key file
|
| 438 |
+
path.
|
| 439 |
+
:returns:
|
| 440 |
+
A tuple of two dictionaries. The first is the "host parameters"
|
| 441 |
+
portion of the Pool Key including scheme, hostname, and port. The
|
| 442 |
+
second is a dictionary of SSLContext related parameters.
|
| 443 |
+
"""
|
| 444 |
+
return _urllib3_request_context(request, verify, cert, self.poolmanager)
|
| 445 |
+
|
| 446 |
+
def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
|
| 447 |
+
"""Returns a urllib3 connection for the given request and TLS settings.
|
| 448 |
+
This should not be called from user code, and is only exposed for use
|
| 449 |
+
when subclassing the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 450 |
+
|
| 451 |
+
:param request:
|
| 452 |
+
The :class:`PreparedRequest <PreparedRequest>` object to be sent
|
| 453 |
+
over the connection.
|
| 454 |
+
:param verify:
|
| 455 |
+
Either a boolean, in which case it controls whether we verify the
|
| 456 |
+
server's TLS certificate, or a string, in which case it must be a
|
| 457 |
+
path to a CA bundle to use.
|
| 458 |
+
:param proxies:
|
| 459 |
+
(optional) The proxies dictionary to apply to the request.
|
| 460 |
+
:param cert:
|
| 461 |
+
(optional) Any user-provided SSL certificate to be used for client
|
| 462 |
+
authentication (a.k.a., mTLS).
|
| 463 |
+
:rtype:
|
| 464 |
+
urllib3.ConnectionPool
|
| 465 |
+
"""
|
| 466 |
+
proxy = select_proxy(request.url, proxies)
|
| 467 |
+
try:
|
| 468 |
+
host_params, pool_kwargs = self.build_connection_pool_key_attributes(
|
| 469 |
+
request,
|
| 470 |
+
verify,
|
| 471 |
+
cert,
|
| 472 |
+
)
|
| 473 |
+
except ValueError as e:
|
| 474 |
+
raise InvalidURL(e, request=request)
|
| 475 |
+
if proxy:
|
| 476 |
+
proxy = prepend_scheme_if_needed(proxy, "http")
|
| 477 |
+
proxy_url = parse_url(proxy)
|
| 478 |
+
if not proxy_url.host:
|
| 479 |
+
raise InvalidProxyURL(
|
| 480 |
+
"Please check proxy URL. It is malformed "
|
| 481 |
+
"and could be missing the host."
|
| 482 |
+
)
|
| 483 |
+
proxy_manager = self.proxy_manager_for(proxy)
|
| 484 |
+
conn = proxy_manager.connection_from_host(
|
| 485 |
+
**host_params, pool_kwargs=pool_kwargs
|
| 486 |
+
)
|
| 487 |
+
else:
|
| 488 |
+
# Only scheme should be lower case
|
| 489 |
+
conn = self.poolmanager.connection_from_host(
|
| 490 |
+
**host_params, pool_kwargs=pool_kwargs
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
return conn
|
| 494 |
+
|
| 495 |
+
def get_connection(self, url, proxies=None):
|
| 496 |
+
"""DEPRECATED: Users should move to `get_connection_with_tls_context`
|
| 497 |
+
for all subclasses of HTTPAdapter using Requests>=2.32.2.
|
| 498 |
+
|
| 499 |
+
Returns a urllib3 connection for the given URL. This should not be
|
| 500 |
+
called from user code, and is only exposed for use when subclassing the
|
| 501 |
+
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 502 |
+
|
| 503 |
+
:param url: The URL to connect to.
|
| 504 |
+
:param proxies: (optional) A Requests-style dictionary of proxies used on this request.
|
| 505 |
+
:rtype: urllib3.ConnectionPool
|
| 506 |
+
"""
|
| 507 |
+
warnings.warn(
|
| 508 |
+
(
|
| 509 |
+
"`get_connection` has been deprecated in favor of "
|
| 510 |
+
"`get_connection_with_tls_context`. Custom HTTPAdapter subclasses "
|
| 511 |
+
"will need to migrate for Requests>=2.32.2. Please see "
|
| 512 |
+
"https://github.com/psf/requests/pull/6710 for more details."
|
| 513 |
+
),
|
| 514 |
+
DeprecationWarning,
|
| 515 |
+
)
|
| 516 |
+
proxy = select_proxy(url, proxies)
|
| 517 |
+
|
| 518 |
+
if proxy:
|
| 519 |
+
proxy = prepend_scheme_if_needed(proxy, "http")
|
| 520 |
+
proxy_url = parse_url(proxy)
|
| 521 |
+
if not proxy_url.host:
|
| 522 |
+
raise InvalidProxyURL(
|
| 523 |
+
"Please check proxy URL. It is malformed "
|
| 524 |
+
"and could be missing the host."
|
| 525 |
+
)
|
| 526 |
+
proxy_manager = self.proxy_manager_for(proxy)
|
| 527 |
+
conn = proxy_manager.connection_from_url(url)
|
| 528 |
+
else:
|
| 529 |
+
# Only scheme should be lower case
|
| 530 |
+
parsed = urlparse(url)
|
| 531 |
+
url = parsed.geturl()
|
| 532 |
+
conn = self.poolmanager.connection_from_url(url)
|
| 533 |
+
|
| 534 |
+
return conn
|
| 535 |
+
|
| 536 |
+
def close(self):
|
| 537 |
+
"""Disposes of any internal state.
|
| 538 |
+
|
| 539 |
+
Currently, this closes the PoolManager and any active ProxyManager,
|
| 540 |
+
which closes any pooled connections.
|
| 541 |
+
"""
|
| 542 |
+
self.poolmanager.clear()
|
| 543 |
+
for proxy in self.proxy_manager.values():
|
| 544 |
+
proxy.clear()
|
| 545 |
+
|
| 546 |
+
def request_url(self, request, proxies):
|
| 547 |
+
"""Obtain the url to use when making the final request.
|
| 548 |
+
|
| 549 |
+
If the message is being sent through a HTTP proxy, the full URL has to
|
| 550 |
+
be used. Otherwise, we should only use the path portion of the URL.
|
| 551 |
+
|
| 552 |
+
This should not be called from user code, and is only exposed for use
|
| 553 |
+
when subclassing the
|
| 554 |
+
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 555 |
+
|
| 556 |
+
:param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
|
| 557 |
+
:param proxies: A dictionary of schemes or schemes and hosts to proxy URLs.
|
| 558 |
+
:rtype: str
|
| 559 |
+
"""
|
| 560 |
+
proxy = select_proxy(request.url, proxies)
|
| 561 |
+
scheme = urlparse(request.url).scheme
|
| 562 |
+
|
| 563 |
+
is_proxied_http_request = proxy and scheme != "https"
|
| 564 |
+
using_socks_proxy = False
|
| 565 |
+
if proxy:
|
| 566 |
+
proxy_scheme = urlparse(proxy).scheme.lower()
|
| 567 |
+
using_socks_proxy = proxy_scheme.startswith("socks")
|
| 568 |
+
|
| 569 |
+
url = request.path_url
|
| 570 |
+
if url.startswith("//"): # Don't confuse urllib3
|
| 571 |
+
url = f"/{url.lstrip('/')}"
|
| 572 |
+
|
| 573 |
+
if is_proxied_http_request and not using_socks_proxy:
|
| 574 |
+
url = urldefragauth(request.url)
|
| 575 |
+
|
| 576 |
+
return url
|
| 577 |
+
|
| 578 |
+
def add_headers(self, request, **kwargs):
|
| 579 |
+
"""Add any headers needed by the connection. As of v2.0 this does
|
| 580 |
+
nothing by default, but is left for overriding by users that subclass
|
| 581 |
+
the :class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 582 |
+
|
| 583 |
+
This should not be called from user code, and is only exposed for use
|
| 584 |
+
when subclassing the
|
| 585 |
+
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 586 |
+
|
| 587 |
+
:param request: The :class:`PreparedRequest <PreparedRequest>` to add headers to.
|
| 588 |
+
:param kwargs: The keyword arguments from the call to send().
|
| 589 |
+
"""
|
| 590 |
+
pass
|
| 591 |
+
|
| 592 |
+
def proxy_headers(self, proxy):
|
| 593 |
+
"""Returns a dictionary of the headers to add to any request sent
|
| 594 |
+
through a proxy. This works with urllib3 magic to ensure that they are
|
| 595 |
+
correctly sent to the proxy, rather than in a tunnelled request if
|
| 596 |
+
CONNECT is being used.
|
| 597 |
+
|
| 598 |
+
This should not be called from user code, and is only exposed for use
|
| 599 |
+
when subclassing the
|
| 600 |
+
:class:`HTTPAdapter <requests.adapters.HTTPAdapter>`.
|
| 601 |
+
|
| 602 |
+
:param proxy: The url of the proxy being used for this request.
|
| 603 |
+
:rtype: dict
|
| 604 |
+
"""
|
| 605 |
+
headers = {}
|
| 606 |
+
username, password = get_auth_from_url(proxy)
|
| 607 |
+
|
| 608 |
+
if username:
|
| 609 |
+
headers["Proxy-Authorization"] = _basic_auth_str(username, password)
|
| 610 |
+
|
| 611 |
+
return headers
|
| 612 |
+
|
| 613 |
+
def send(
|
| 614 |
+
self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None
|
| 615 |
+
):
|
| 616 |
+
"""Sends PreparedRequest object. Returns Response object.
|
| 617 |
+
|
| 618 |
+
:param request: The :class:`PreparedRequest <PreparedRequest>` being sent.
|
| 619 |
+
:param stream: (optional) Whether to stream the request content.
|
| 620 |
+
:param timeout: (optional) How long to wait for the server to send
|
| 621 |
+
data before giving up, as a float, or a :ref:`(connect timeout,
|
| 622 |
+
read timeout) <timeouts>` tuple.
|
| 623 |
+
:type timeout: float or tuple or urllib3 Timeout object
|
| 624 |
+
:param verify: (optional) Either a boolean, in which case it controls whether
|
| 625 |
+
we verify the server's TLS certificate, or a string, in which case it
|
| 626 |
+
must be a path to a CA bundle to use
|
| 627 |
+
:param cert: (optional) Any user-provided SSL certificate to be trusted.
|
| 628 |
+
:param proxies: (optional) The proxies dictionary to apply to the request.
|
| 629 |
+
:rtype: requests.Response
|
| 630 |
+
"""
|
| 631 |
+
|
| 632 |
+
try:
|
| 633 |
+
conn = self.get_connection_with_tls_context(
|
| 634 |
+
request, verify, proxies=proxies, cert=cert
|
| 635 |
+
)
|
| 636 |
+
except LocationValueError as e:
|
| 637 |
+
raise InvalidURL(e, request=request)
|
| 638 |
+
|
| 639 |
+
self.cert_verify(conn, request.url, verify, cert)
|
| 640 |
+
url = self.request_url(request, proxies)
|
| 641 |
+
self.add_headers(
|
| 642 |
+
request,
|
| 643 |
+
stream=stream,
|
| 644 |
+
timeout=timeout,
|
| 645 |
+
verify=verify,
|
| 646 |
+
cert=cert,
|
| 647 |
+
proxies=proxies,
|
| 648 |
+
)
|
| 649 |
+
|
| 650 |
+
chunked = not (request.body is None or "Content-Length" in request.headers)
|
| 651 |
+
|
| 652 |
+
if isinstance(timeout, tuple):
|
| 653 |
+
try:
|
| 654 |
+
connect, read = timeout
|
| 655 |
+
timeout = TimeoutSauce(connect=connect, read=read)
|
| 656 |
+
except ValueError:
|
| 657 |
+
raise ValueError(
|
| 658 |
+
f"Invalid timeout {timeout}. Pass a (connect, read) timeout tuple, "
|
| 659 |
+
f"or a single float to set both timeouts to the same value."
|
| 660 |
+
)
|
| 661 |
+
elif isinstance(timeout, TimeoutSauce):
|
| 662 |
+
pass
|
| 663 |
+
else:
|
| 664 |
+
timeout = TimeoutSauce(connect=timeout, read=timeout)
|
| 665 |
+
|
| 666 |
+
try:
|
| 667 |
+
resp = conn.urlopen(
|
| 668 |
+
method=request.method,
|
| 669 |
+
url=url,
|
| 670 |
+
body=request.body,
|
| 671 |
+
headers=request.headers,
|
| 672 |
+
redirect=False,
|
| 673 |
+
assert_same_host=False,
|
| 674 |
+
preload_content=False,
|
| 675 |
+
decode_content=False,
|
| 676 |
+
retries=self.max_retries,
|
| 677 |
+
timeout=timeout,
|
| 678 |
+
chunked=chunked,
|
| 679 |
+
)
|
| 680 |
+
|
| 681 |
+
except (ProtocolError, OSError) as err:
|
| 682 |
+
raise ConnectionError(err, request=request)
|
| 683 |
+
|
| 684 |
+
except MaxRetryError as e:
|
| 685 |
+
if isinstance(e.reason, ConnectTimeoutError):
|
| 686 |
+
# TODO: Remove this in 3.0.0: see #2811
|
| 687 |
+
if not isinstance(e.reason, NewConnectionError):
|
| 688 |
+
raise ConnectTimeout(e, request=request)
|
| 689 |
+
|
| 690 |
+
if isinstance(e.reason, ResponseError):
|
| 691 |
+
raise RetryError(e, request=request)
|
| 692 |
+
|
| 693 |
+
if isinstance(e.reason, _ProxyError):
|
| 694 |
+
raise ProxyError(e, request=request)
|
| 695 |
+
|
| 696 |
+
if isinstance(e.reason, _SSLError):
|
| 697 |
+
# This branch is for urllib3 v1.22 and later.
|
| 698 |
+
raise SSLError(e, request=request)
|
| 699 |
+
|
| 700 |
+
raise ConnectionError(e, request=request)
|
| 701 |
+
|
| 702 |
+
except ClosedPoolError as e:
|
| 703 |
+
raise ConnectionError(e, request=request)
|
| 704 |
+
|
| 705 |
+
except _ProxyError as e:
|
| 706 |
+
raise ProxyError(e)
|
| 707 |
+
|
| 708 |
+
except (_SSLError, _HTTPError) as e:
|
| 709 |
+
if isinstance(e, _SSLError):
|
| 710 |
+
# This branch is for urllib3 versions earlier than v1.22
|
| 711 |
+
raise SSLError(e, request=request)
|
| 712 |
+
elif isinstance(e, ReadTimeoutError):
|
| 713 |
+
raise ReadTimeout(e, request=request)
|
| 714 |
+
elif isinstance(e, _InvalidHeader):
|
| 715 |
+
raise InvalidHeader(e, request=request)
|
| 716 |
+
else:
|
| 717 |
+
raise
|
| 718 |
+
|
| 719 |
+
return self.build_response(request, resp)
|
.venv/Lib/site-packages/pip/_vendor/requests/api.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.api
|
| 3 |
+
~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module implements the Requests API.
|
| 6 |
+
|
| 7 |
+
:copyright: (c) 2012 by Kenneth Reitz.
|
| 8 |
+
:license: Apache2, see LICENSE for more details.
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from . import sessions
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def request(method, url, **kwargs):
|
| 15 |
+
"""Constructs and sends a :class:`Request <Request>`.
|
| 16 |
+
|
| 17 |
+
:param method: method for the new :class:`Request` object: ``GET``, ``OPTIONS``, ``HEAD``, ``POST``, ``PUT``, ``PATCH``, or ``DELETE``.
|
| 18 |
+
:param url: URL for the new :class:`Request` object.
|
| 19 |
+
:param params: (optional) Dictionary, list of tuples or bytes to send
|
| 20 |
+
in the query string for the :class:`Request`.
|
| 21 |
+
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
| 22 |
+
object to send in the body of the :class:`Request`.
|
| 23 |
+
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
|
| 24 |
+
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
|
| 25 |
+
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
| 26 |
+
:param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
|
| 27 |
+
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
|
| 28 |
+
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string
|
| 29 |
+
defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
|
| 30 |
+
to add for the file.
|
| 31 |
+
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
|
| 32 |
+
:param timeout: (optional) How many seconds to wait for the server to send data
|
| 33 |
+
before giving up, as a float, or a :ref:`(connect timeout, read
|
| 34 |
+
timeout) <timeouts>` tuple.
|
| 35 |
+
:type timeout: float or tuple
|
| 36 |
+
:param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``.
|
| 37 |
+
:type allow_redirects: bool
|
| 38 |
+
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
| 39 |
+
:param verify: (optional) Either a boolean, in which case it controls whether we verify
|
| 40 |
+
the server's TLS certificate, or a string, in which case it must be a path
|
| 41 |
+
to a CA bundle to use. Defaults to ``True``.
|
| 42 |
+
:param stream: (optional) if ``False``, the response content will be immediately downloaded.
|
| 43 |
+
:param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
|
| 44 |
+
:return: :class:`Response <Response>` object
|
| 45 |
+
:rtype: requests.Response
|
| 46 |
+
|
| 47 |
+
Usage::
|
| 48 |
+
|
| 49 |
+
>>> import requests
|
| 50 |
+
>>> req = requests.request('GET', 'https://httpbin.org/get')
|
| 51 |
+
>>> req
|
| 52 |
+
<Response [200]>
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
# By using the 'with' statement we are sure the session is closed, thus we
|
| 56 |
+
# avoid leaving sockets open which can trigger a ResourceWarning in some
|
| 57 |
+
# cases, and look like a memory leak in others.
|
| 58 |
+
with sessions.Session() as session:
|
| 59 |
+
return session.request(method=method, url=url, **kwargs)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def get(url, params=None, **kwargs):
|
| 63 |
+
r"""Sends a GET request.
|
| 64 |
+
|
| 65 |
+
:param url: URL for the new :class:`Request` object.
|
| 66 |
+
:param params: (optional) Dictionary, list of tuples or bytes to send
|
| 67 |
+
in the query string for the :class:`Request`.
|
| 68 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 69 |
+
:return: :class:`Response <Response>` object
|
| 70 |
+
:rtype: requests.Response
|
| 71 |
+
"""
|
| 72 |
+
|
| 73 |
+
return request("get", url, params=params, **kwargs)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def options(url, **kwargs):
|
| 77 |
+
r"""Sends an OPTIONS request.
|
| 78 |
+
|
| 79 |
+
:param url: URL for the new :class:`Request` object.
|
| 80 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 81 |
+
:return: :class:`Response <Response>` object
|
| 82 |
+
:rtype: requests.Response
|
| 83 |
+
"""
|
| 84 |
+
|
| 85 |
+
return request("options", url, **kwargs)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def head(url, **kwargs):
|
| 89 |
+
r"""Sends a HEAD request.
|
| 90 |
+
|
| 91 |
+
:param url: URL for the new :class:`Request` object.
|
| 92 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes. If
|
| 93 |
+
`allow_redirects` is not provided, it will be set to `False` (as
|
| 94 |
+
opposed to the default :meth:`request` behavior).
|
| 95 |
+
:return: :class:`Response <Response>` object
|
| 96 |
+
:rtype: requests.Response
|
| 97 |
+
"""
|
| 98 |
+
|
| 99 |
+
kwargs.setdefault("allow_redirects", False)
|
| 100 |
+
return request("head", url, **kwargs)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def post(url, data=None, json=None, **kwargs):
|
| 104 |
+
r"""Sends a POST request.
|
| 105 |
+
|
| 106 |
+
:param url: URL for the new :class:`Request` object.
|
| 107 |
+
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
| 108 |
+
object to send in the body of the :class:`Request`.
|
| 109 |
+
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
|
| 110 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 111 |
+
:return: :class:`Response <Response>` object
|
| 112 |
+
:rtype: requests.Response
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
return request("post", url, data=data, json=json, **kwargs)
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def put(url, data=None, **kwargs):
|
| 119 |
+
r"""Sends a PUT request.
|
| 120 |
+
|
| 121 |
+
:param url: URL for the new :class:`Request` object.
|
| 122 |
+
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
| 123 |
+
object to send in the body of the :class:`Request`.
|
| 124 |
+
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
|
| 125 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 126 |
+
:return: :class:`Response <Response>` object
|
| 127 |
+
:rtype: requests.Response
|
| 128 |
+
"""
|
| 129 |
+
|
| 130 |
+
return request("put", url, data=data, **kwargs)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def patch(url, data=None, **kwargs):
|
| 134 |
+
r"""Sends a PATCH request.
|
| 135 |
+
|
| 136 |
+
:param url: URL for the new :class:`Request` object.
|
| 137 |
+
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
| 138 |
+
object to send in the body of the :class:`Request`.
|
| 139 |
+
:param json: (optional) A JSON serializable Python object to send in the body of the :class:`Request`.
|
| 140 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 141 |
+
:return: :class:`Response <Response>` object
|
| 142 |
+
:rtype: requests.Response
|
| 143 |
+
"""
|
| 144 |
+
|
| 145 |
+
return request("patch", url, data=data, **kwargs)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def delete(url, **kwargs):
|
| 149 |
+
r"""Sends a DELETE request.
|
| 150 |
+
|
| 151 |
+
:param url: URL for the new :class:`Request` object.
|
| 152 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 153 |
+
:return: :class:`Response <Response>` object
|
| 154 |
+
:rtype: requests.Response
|
| 155 |
+
"""
|
| 156 |
+
|
| 157 |
+
return request("delete", url, **kwargs)
|
.venv/Lib/site-packages/pip/_vendor/requests/auth.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.auth
|
| 3 |
+
~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module contains the authentication handlers for Requests.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import hashlib
|
| 9 |
+
import os
|
| 10 |
+
import re
|
| 11 |
+
import threading
|
| 12 |
+
import time
|
| 13 |
+
import warnings
|
| 14 |
+
from base64 import b64encode
|
| 15 |
+
|
| 16 |
+
from ._internal_utils import to_native_string
|
| 17 |
+
from .compat import basestring, str, urlparse
|
| 18 |
+
from .cookies import extract_cookies_to_jar
|
| 19 |
+
from .utils import parse_dict_header
|
| 20 |
+
|
| 21 |
+
CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded"
|
| 22 |
+
CONTENT_TYPE_MULTI_PART = "multipart/form-data"
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def _basic_auth_str(username, password):
|
| 26 |
+
"""Returns a Basic Auth string."""
|
| 27 |
+
|
| 28 |
+
# "I want us to put a big-ol' comment on top of it that
|
| 29 |
+
# says that this behaviour is dumb but we need to preserve
|
| 30 |
+
# it because people are relying on it."
|
| 31 |
+
# - Lukasa
|
| 32 |
+
#
|
| 33 |
+
# These are here solely to maintain backwards compatibility
|
| 34 |
+
# for things like ints. This will be removed in 3.0.0.
|
| 35 |
+
if not isinstance(username, basestring):
|
| 36 |
+
warnings.warn(
|
| 37 |
+
"Non-string usernames will no longer be supported in Requests "
|
| 38 |
+
"3.0.0. Please convert the object you've passed in ({!r}) to "
|
| 39 |
+
"a string or bytes object in the near future to avoid "
|
| 40 |
+
"problems.".format(username),
|
| 41 |
+
category=DeprecationWarning,
|
| 42 |
+
)
|
| 43 |
+
username = str(username)
|
| 44 |
+
|
| 45 |
+
if not isinstance(password, basestring):
|
| 46 |
+
warnings.warn(
|
| 47 |
+
"Non-string passwords will no longer be supported in Requests "
|
| 48 |
+
"3.0.0. Please convert the object you've passed in ({!r}) to "
|
| 49 |
+
"a string or bytes object in the near future to avoid "
|
| 50 |
+
"problems.".format(type(password)),
|
| 51 |
+
category=DeprecationWarning,
|
| 52 |
+
)
|
| 53 |
+
password = str(password)
|
| 54 |
+
# -- End Removal --
|
| 55 |
+
|
| 56 |
+
if isinstance(username, str):
|
| 57 |
+
username = username.encode("latin1")
|
| 58 |
+
|
| 59 |
+
if isinstance(password, str):
|
| 60 |
+
password = password.encode("latin1")
|
| 61 |
+
|
| 62 |
+
authstr = "Basic " + to_native_string(
|
| 63 |
+
b64encode(b":".join((username, password))).strip()
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
return authstr
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class AuthBase:
|
| 70 |
+
"""Base class that all auth implementations derive from"""
|
| 71 |
+
|
| 72 |
+
def __call__(self, r):
|
| 73 |
+
raise NotImplementedError("Auth hooks must be callable.")
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
class HTTPBasicAuth(AuthBase):
|
| 77 |
+
"""Attaches HTTP Basic Authentication to the given Request object."""
|
| 78 |
+
|
| 79 |
+
def __init__(self, username, password):
|
| 80 |
+
self.username = username
|
| 81 |
+
self.password = password
|
| 82 |
+
|
| 83 |
+
def __eq__(self, other):
|
| 84 |
+
return all(
|
| 85 |
+
[
|
| 86 |
+
self.username == getattr(other, "username", None),
|
| 87 |
+
self.password == getattr(other, "password", None),
|
| 88 |
+
]
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
def __ne__(self, other):
|
| 92 |
+
return not self == other
|
| 93 |
+
|
| 94 |
+
def __call__(self, r):
|
| 95 |
+
r.headers["Authorization"] = _basic_auth_str(self.username, self.password)
|
| 96 |
+
return r
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class HTTPProxyAuth(HTTPBasicAuth):
|
| 100 |
+
"""Attaches HTTP Proxy Authentication to a given Request object."""
|
| 101 |
+
|
| 102 |
+
def __call__(self, r):
|
| 103 |
+
r.headers["Proxy-Authorization"] = _basic_auth_str(self.username, self.password)
|
| 104 |
+
return r
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class HTTPDigestAuth(AuthBase):
|
| 108 |
+
"""Attaches HTTP Digest Authentication to the given Request object."""
|
| 109 |
+
|
| 110 |
+
def __init__(self, username, password):
|
| 111 |
+
self.username = username
|
| 112 |
+
self.password = password
|
| 113 |
+
# Keep state in per-thread local storage
|
| 114 |
+
self._thread_local = threading.local()
|
| 115 |
+
|
| 116 |
+
def init_per_thread_state(self):
|
| 117 |
+
# Ensure state is initialized just once per-thread
|
| 118 |
+
if not hasattr(self._thread_local, "init"):
|
| 119 |
+
self._thread_local.init = True
|
| 120 |
+
self._thread_local.last_nonce = ""
|
| 121 |
+
self._thread_local.nonce_count = 0
|
| 122 |
+
self._thread_local.chal = {}
|
| 123 |
+
self._thread_local.pos = None
|
| 124 |
+
self._thread_local.num_401_calls = None
|
| 125 |
+
|
| 126 |
+
def build_digest_header(self, method, url):
|
| 127 |
+
"""
|
| 128 |
+
:rtype: str
|
| 129 |
+
"""
|
| 130 |
+
|
| 131 |
+
realm = self._thread_local.chal["realm"]
|
| 132 |
+
nonce = self._thread_local.chal["nonce"]
|
| 133 |
+
qop = self._thread_local.chal.get("qop")
|
| 134 |
+
algorithm = self._thread_local.chal.get("algorithm")
|
| 135 |
+
opaque = self._thread_local.chal.get("opaque")
|
| 136 |
+
hash_utf8 = None
|
| 137 |
+
|
| 138 |
+
if algorithm is None:
|
| 139 |
+
_algorithm = "MD5"
|
| 140 |
+
else:
|
| 141 |
+
_algorithm = algorithm.upper()
|
| 142 |
+
# lambdas assume digest modules are imported at the top level
|
| 143 |
+
if _algorithm == "MD5" or _algorithm == "MD5-SESS":
|
| 144 |
+
|
| 145 |
+
def md5_utf8(x):
|
| 146 |
+
if isinstance(x, str):
|
| 147 |
+
x = x.encode("utf-8")
|
| 148 |
+
return hashlib.md5(x).hexdigest()
|
| 149 |
+
|
| 150 |
+
hash_utf8 = md5_utf8
|
| 151 |
+
elif _algorithm == "SHA":
|
| 152 |
+
|
| 153 |
+
def sha_utf8(x):
|
| 154 |
+
if isinstance(x, str):
|
| 155 |
+
x = x.encode("utf-8")
|
| 156 |
+
return hashlib.sha1(x).hexdigest()
|
| 157 |
+
|
| 158 |
+
hash_utf8 = sha_utf8
|
| 159 |
+
elif _algorithm == "SHA-256":
|
| 160 |
+
|
| 161 |
+
def sha256_utf8(x):
|
| 162 |
+
if isinstance(x, str):
|
| 163 |
+
x = x.encode("utf-8")
|
| 164 |
+
return hashlib.sha256(x).hexdigest()
|
| 165 |
+
|
| 166 |
+
hash_utf8 = sha256_utf8
|
| 167 |
+
elif _algorithm == "SHA-512":
|
| 168 |
+
|
| 169 |
+
def sha512_utf8(x):
|
| 170 |
+
if isinstance(x, str):
|
| 171 |
+
x = x.encode("utf-8")
|
| 172 |
+
return hashlib.sha512(x).hexdigest()
|
| 173 |
+
|
| 174 |
+
hash_utf8 = sha512_utf8
|
| 175 |
+
|
| 176 |
+
KD = lambda s, d: hash_utf8(f"{s}:{d}") # noqa:E731
|
| 177 |
+
|
| 178 |
+
if hash_utf8 is None:
|
| 179 |
+
return None
|
| 180 |
+
|
| 181 |
+
# XXX not implemented yet
|
| 182 |
+
entdig = None
|
| 183 |
+
p_parsed = urlparse(url)
|
| 184 |
+
#: path is request-uri defined in RFC 2616 which should not be empty
|
| 185 |
+
path = p_parsed.path or "/"
|
| 186 |
+
if p_parsed.query:
|
| 187 |
+
path += f"?{p_parsed.query}"
|
| 188 |
+
|
| 189 |
+
A1 = f"{self.username}:{realm}:{self.password}"
|
| 190 |
+
A2 = f"{method}:{path}"
|
| 191 |
+
|
| 192 |
+
HA1 = hash_utf8(A1)
|
| 193 |
+
HA2 = hash_utf8(A2)
|
| 194 |
+
|
| 195 |
+
if nonce == self._thread_local.last_nonce:
|
| 196 |
+
self._thread_local.nonce_count += 1
|
| 197 |
+
else:
|
| 198 |
+
self._thread_local.nonce_count = 1
|
| 199 |
+
ncvalue = f"{self._thread_local.nonce_count:08x}"
|
| 200 |
+
s = str(self._thread_local.nonce_count).encode("utf-8")
|
| 201 |
+
s += nonce.encode("utf-8")
|
| 202 |
+
s += time.ctime().encode("utf-8")
|
| 203 |
+
s += os.urandom(8)
|
| 204 |
+
|
| 205 |
+
cnonce = hashlib.sha1(s).hexdigest()[:16]
|
| 206 |
+
if _algorithm == "MD5-SESS":
|
| 207 |
+
HA1 = hash_utf8(f"{HA1}:{nonce}:{cnonce}")
|
| 208 |
+
|
| 209 |
+
if not qop:
|
| 210 |
+
respdig = KD(HA1, f"{nonce}:{HA2}")
|
| 211 |
+
elif qop == "auth" or "auth" in qop.split(","):
|
| 212 |
+
noncebit = f"{nonce}:{ncvalue}:{cnonce}:auth:{HA2}"
|
| 213 |
+
respdig = KD(HA1, noncebit)
|
| 214 |
+
else:
|
| 215 |
+
# XXX handle auth-int.
|
| 216 |
+
return None
|
| 217 |
+
|
| 218 |
+
self._thread_local.last_nonce = nonce
|
| 219 |
+
|
| 220 |
+
# XXX should the partial digests be encoded too?
|
| 221 |
+
base = (
|
| 222 |
+
f'username="{self.username}", realm="{realm}", nonce="{nonce}", '
|
| 223 |
+
f'uri="{path}", response="{respdig}"'
|
| 224 |
+
)
|
| 225 |
+
if opaque:
|
| 226 |
+
base += f', opaque="{opaque}"'
|
| 227 |
+
if algorithm:
|
| 228 |
+
base += f', algorithm="{algorithm}"'
|
| 229 |
+
if entdig:
|
| 230 |
+
base += f', digest="{entdig}"'
|
| 231 |
+
if qop:
|
| 232 |
+
base += f', qop="auth", nc={ncvalue}, cnonce="{cnonce}"'
|
| 233 |
+
|
| 234 |
+
return f"Digest {base}"
|
| 235 |
+
|
| 236 |
+
def handle_redirect(self, r, **kwargs):
|
| 237 |
+
"""Reset num_401_calls counter on redirects."""
|
| 238 |
+
if r.is_redirect:
|
| 239 |
+
self._thread_local.num_401_calls = 1
|
| 240 |
+
|
| 241 |
+
def handle_401(self, r, **kwargs):
|
| 242 |
+
"""
|
| 243 |
+
Takes the given response and tries digest-auth, if needed.
|
| 244 |
+
|
| 245 |
+
:rtype: requests.Response
|
| 246 |
+
"""
|
| 247 |
+
|
| 248 |
+
# If response is not 4xx, do not auth
|
| 249 |
+
# See https://github.com/psf/requests/issues/3772
|
| 250 |
+
if not 400 <= r.status_code < 500:
|
| 251 |
+
self._thread_local.num_401_calls = 1
|
| 252 |
+
return r
|
| 253 |
+
|
| 254 |
+
if self._thread_local.pos is not None:
|
| 255 |
+
# Rewind the file position indicator of the body to where
|
| 256 |
+
# it was to resend the request.
|
| 257 |
+
r.request.body.seek(self._thread_local.pos)
|
| 258 |
+
s_auth = r.headers.get("www-authenticate", "")
|
| 259 |
+
|
| 260 |
+
if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2:
|
| 261 |
+
self._thread_local.num_401_calls += 1
|
| 262 |
+
pat = re.compile(r"digest ", flags=re.IGNORECASE)
|
| 263 |
+
self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1))
|
| 264 |
+
|
| 265 |
+
# Consume content and release the original connection
|
| 266 |
+
# to allow our new request to reuse the same one.
|
| 267 |
+
r.content
|
| 268 |
+
r.close()
|
| 269 |
+
prep = r.request.copy()
|
| 270 |
+
extract_cookies_to_jar(prep._cookies, r.request, r.raw)
|
| 271 |
+
prep.prepare_cookies(prep._cookies)
|
| 272 |
+
|
| 273 |
+
prep.headers["Authorization"] = self.build_digest_header(
|
| 274 |
+
prep.method, prep.url
|
| 275 |
+
)
|
| 276 |
+
_r = r.connection.send(prep, **kwargs)
|
| 277 |
+
_r.history.append(r)
|
| 278 |
+
_r.request = prep
|
| 279 |
+
|
| 280 |
+
return _r
|
| 281 |
+
|
| 282 |
+
self._thread_local.num_401_calls = 1
|
| 283 |
+
return r
|
| 284 |
+
|
| 285 |
+
def __call__(self, r):
|
| 286 |
+
# Initialize per-thread state, if needed
|
| 287 |
+
self.init_per_thread_state()
|
| 288 |
+
# If we have a saved nonce, skip the 401
|
| 289 |
+
if self._thread_local.last_nonce:
|
| 290 |
+
r.headers["Authorization"] = self.build_digest_header(r.method, r.url)
|
| 291 |
+
try:
|
| 292 |
+
self._thread_local.pos = r.body.tell()
|
| 293 |
+
except AttributeError:
|
| 294 |
+
# In the case of HTTPDigestAuth being reused and the body of
|
| 295 |
+
# the previous request was a file-like object, pos has the
|
| 296 |
+
# file position of the previous body. Ensure it's set to
|
| 297 |
+
# None.
|
| 298 |
+
self._thread_local.pos = None
|
| 299 |
+
r.register_hook("response", self.handle_401)
|
| 300 |
+
r.register_hook("response", self.handle_redirect)
|
| 301 |
+
self._thread_local.num_401_calls = 1
|
| 302 |
+
|
| 303 |
+
return r
|
| 304 |
+
|
| 305 |
+
def __eq__(self, other):
|
| 306 |
+
return all(
|
| 307 |
+
[
|
| 308 |
+
self.username == getattr(other, "username", None),
|
| 309 |
+
self.password == getattr(other, "password", None),
|
| 310 |
+
]
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
def __ne__(self, other):
|
| 314 |
+
return not self == other
|
.venv/Lib/site-packages/pip/_vendor/requests/certs.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
|
| 3 |
+
"""
|
| 4 |
+
requests.certs
|
| 5 |
+
~~~~~~~~~~~~~~
|
| 6 |
+
|
| 7 |
+
This module returns the preferred default CA certificate bundle. There is
|
| 8 |
+
only one — the one from the certifi package.
|
| 9 |
+
|
| 10 |
+
If you are packaging Requests, e.g., for a Linux distribution or a managed
|
| 11 |
+
environment, you can change the definition of where() to return a separately
|
| 12 |
+
packaged CA bundle.
|
| 13 |
+
"""
|
| 14 |
+
from pip._vendor.certifi import where
|
| 15 |
+
|
| 16 |
+
if __name__ == "__main__":
|
| 17 |
+
print(where())
|
.venv/Lib/site-packages/pip/_vendor/requests/compat.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.compat
|
| 3 |
+
~~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module previously handled import compatibility issues
|
| 6 |
+
between Python 2 and Python 3. It remains for backwards
|
| 7 |
+
compatibility until the next major version.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import sys
|
| 11 |
+
|
| 12 |
+
# -------
|
| 13 |
+
# urllib3
|
| 14 |
+
# -------
|
| 15 |
+
from pip._vendor.urllib3 import __version__ as urllib3_version
|
| 16 |
+
|
| 17 |
+
# Detect which major version of urllib3 is being used.
|
| 18 |
+
try:
|
| 19 |
+
is_urllib3_1 = int(urllib3_version.split(".")[0]) == 1
|
| 20 |
+
except (TypeError, AttributeError):
|
| 21 |
+
# If we can't discern a version, prefer old functionality.
|
| 22 |
+
is_urllib3_1 = True
|
| 23 |
+
|
| 24 |
+
# -------------------
|
| 25 |
+
# Character Detection
|
| 26 |
+
# -------------------
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _resolve_char_detection():
|
| 30 |
+
"""Find supported character detection libraries."""
|
| 31 |
+
chardet = None
|
| 32 |
+
return chardet
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
chardet = _resolve_char_detection()
|
| 36 |
+
|
| 37 |
+
# -------
|
| 38 |
+
# Pythons
|
| 39 |
+
# -------
|
| 40 |
+
|
| 41 |
+
# Syntax sugar.
|
| 42 |
+
_ver = sys.version_info
|
| 43 |
+
|
| 44 |
+
#: Python 2.x?
|
| 45 |
+
is_py2 = _ver[0] == 2
|
| 46 |
+
|
| 47 |
+
#: Python 3.x?
|
| 48 |
+
is_py3 = _ver[0] == 3
|
| 49 |
+
|
| 50 |
+
# Note: We've patched out simplejson support in pip because it prevents
|
| 51 |
+
# upgrading simplejson on Windows.
|
| 52 |
+
import json
|
| 53 |
+
from json import JSONDecodeError
|
| 54 |
+
|
| 55 |
+
# Keep OrderedDict for backwards compatibility.
|
| 56 |
+
from collections import OrderedDict
|
| 57 |
+
from collections.abc import Callable, Mapping, MutableMapping
|
| 58 |
+
from http import cookiejar as cookielib
|
| 59 |
+
from http.cookies import Morsel
|
| 60 |
+
from io import StringIO
|
| 61 |
+
|
| 62 |
+
# --------------
|
| 63 |
+
# Legacy Imports
|
| 64 |
+
# --------------
|
| 65 |
+
from urllib.parse import (
|
| 66 |
+
quote,
|
| 67 |
+
quote_plus,
|
| 68 |
+
unquote,
|
| 69 |
+
unquote_plus,
|
| 70 |
+
urldefrag,
|
| 71 |
+
urlencode,
|
| 72 |
+
urljoin,
|
| 73 |
+
urlparse,
|
| 74 |
+
urlsplit,
|
| 75 |
+
urlunparse,
|
| 76 |
+
)
|
| 77 |
+
from urllib.request import (
|
| 78 |
+
getproxies,
|
| 79 |
+
getproxies_environment,
|
| 80 |
+
parse_http_list,
|
| 81 |
+
proxy_bypass,
|
| 82 |
+
proxy_bypass_environment,
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
builtin_str = str
|
| 86 |
+
str = str
|
| 87 |
+
bytes = bytes
|
| 88 |
+
basestring = (str, bytes)
|
| 89 |
+
numeric_types = (int, float)
|
| 90 |
+
integer_types = (int,)
|
.venv/Lib/site-packages/pip/_vendor/requests/cookies.py
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.cookies
|
| 3 |
+
~~~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
Compatibility code to be able to use `http.cookiejar.CookieJar` with requests.
|
| 6 |
+
|
| 7 |
+
requests.utils imports from here, so be careful with imports.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import calendar
|
| 11 |
+
import copy
|
| 12 |
+
import time
|
| 13 |
+
|
| 14 |
+
from ._internal_utils import to_native_string
|
| 15 |
+
from .compat import Morsel, MutableMapping, cookielib, urlparse, urlunparse
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
import threading
|
| 19 |
+
except ImportError:
|
| 20 |
+
import dummy_threading as threading
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class MockRequest:
|
| 24 |
+
"""Wraps a `requests.Request` to mimic a `urllib2.Request`.
|
| 25 |
+
|
| 26 |
+
The code in `http.cookiejar.CookieJar` expects this interface in order to correctly
|
| 27 |
+
manage cookie policies, i.e., determine whether a cookie can be set, given the
|
| 28 |
+
domains of the request and the cookie.
|
| 29 |
+
|
| 30 |
+
The original request object is read-only. The client is responsible for collecting
|
| 31 |
+
the new headers via `get_new_headers()` and interpreting them appropriately. You
|
| 32 |
+
probably want `get_cookie_header`, defined below.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
def __init__(self, request):
|
| 36 |
+
self._r = request
|
| 37 |
+
self._new_headers = {}
|
| 38 |
+
self.type = urlparse(self._r.url).scheme
|
| 39 |
+
|
| 40 |
+
def get_type(self):
|
| 41 |
+
return self.type
|
| 42 |
+
|
| 43 |
+
def get_host(self):
|
| 44 |
+
return urlparse(self._r.url).netloc
|
| 45 |
+
|
| 46 |
+
def get_origin_req_host(self):
|
| 47 |
+
return self.get_host()
|
| 48 |
+
|
| 49 |
+
def get_full_url(self):
|
| 50 |
+
# Only return the response's URL if the user hadn't set the Host
|
| 51 |
+
# header
|
| 52 |
+
if not self._r.headers.get("Host"):
|
| 53 |
+
return self._r.url
|
| 54 |
+
# If they did set it, retrieve it and reconstruct the expected domain
|
| 55 |
+
host = to_native_string(self._r.headers["Host"], encoding="utf-8")
|
| 56 |
+
parsed = urlparse(self._r.url)
|
| 57 |
+
# Reconstruct the URL as we expect it
|
| 58 |
+
return urlunparse(
|
| 59 |
+
[
|
| 60 |
+
parsed.scheme,
|
| 61 |
+
host,
|
| 62 |
+
parsed.path,
|
| 63 |
+
parsed.params,
|
| 64 |
+
parsed.query,
|
| 65 |
+
parsed.fragment,
|
| 66 |
+
]
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
def is_unverifiable(self):
|
| 70 |
+
return True
|
| 71 |
+
|
| 72 |
+
def has_header(self, name):
|
| 73 |
+
return name in self._r.headers or name in self._new_headers
|
| 74 |
+
|
| 75 |
+
def get_header(self, name, default=None):
|
| 76 |
+
return self._r.headers.get(name, self._new_headers.get(name, default))
|
| 77 |
+
|
| 78 |
+
def add_header(self, key, val):
|
| 79 |
+
"""cookiejar has no legitimate use for this method; add it back if you find one."""
|
| 80 |
+
raise NotImplementedError(
|
| 81 |
+
"Cookie headers should be added with add_unredirected_header()"
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
def add_unredirected_header(self, name, value):
|
| 85 |
+
self._new_headers[name] = value
|
| 86 |
+
|
| 87 |
+
def get_new_headers(self):
|
| 88 |
+
return self._new_headers
|
| 89 |
+
|
| 90 |
+
@property
|
| 91 |
+
def unverifiable(self):
|
| 92 |
+
return self.is_unverifiable()
|
| 93 |
+
|
| 94 |
+
@property
|
| 95 |
+
def origin_req_host(self):
|
| 96 |
+
return self.get_origin_req_host()
|
| 97 |
+
|
| 98 |
+
@property
|
| 99 |
+
def host(self):
|
| 100 |
+
return self.get_host()
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class MockResponse:
|
| 104 |
+
"""Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
|
| 105 |
+
|
| 106 |
+
...what? Basically, expose the parsed HTTP headers from the server response
|
| 107 |
+
the way `http.cookiejar` expects to see them.
|
| 108 |
+
"""
|
| 109 |
+
|
| 110 |
+
def __init__(self, headers):
|
| 111 |
+
"""Make a MockResponse for `cookiejar` to read.
|
| 112 |
+
|
| 113 |
+
:param headers: a httplib.HTTPMessage or analogous carrying the headers
|
| 114 |
+
"""
|
| 115 |
+
self._headers = headers
|
| 116 |
+
|
| 117 |
+
def info(self):
|
| 118 |
+
return self._headers
|
| 119 |
+
|
| 120 |
+
def getheaders(self, name):
|
| 121 |
+
self._headers.getheaders(name)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def extract_cookies_to_jar(jar, request, response):
|
| 125 |
+
"""Extract the cookies from the response into a CookieJar.
|
| 126 |
+
|
| 127 |
+
:param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar)
|
| 128 |
+
:param request: our own requests.Request object
|
| 129 |
+
:param response: urllib3.HTTPResponse object
|
| 130 |
+
"""
|
| 131 |
+
if not (hasattr(response, "_original_response") and response._original_response):
|
| 132 |
+
return
|
| 133 |
+
# the _original_response field is the wrapped httplib.HTTPResponse object,
|
| 134 |
+
req = MockRequest(request)
|
| 135 |
+
# pull out the HTTPMessage with the headers and put it in the mock:
|
| 136 |
+
res = MockResponse(response._original_response.msg)
|
| 137 |
+
jar.extract_cookies(res, req)
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def get_cookie_header(jar, request):
|
| 141 |
+
"""
|
| 142 |
+
Produce an appropriate Cookie header string to be sent with `request`, or None.
|
| 143 |
+
|
| 144 |
+
:rtype: str
|
| 145 |
+
"""
|
| 146 |
+
r = MockRequest(request)
|
| 147 |
+
jar.add_cookie_header(r)
|
| 148 |
+
return r.get_new_headers().get("Cookie")
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def remove_cookie_by_name(cookiejar, name, domain=None, path=None):
|
| 152 |
+
"""Unsets a cookie by name, by default over all domains and paths.
|
| 153 |
+
|
| 154 |
+
Wraps CookieJar.clear(), is O(n).
|
| 155 |
+
"""
|
| 156 |
+
clearables = []
|
| 157 |
+
for cookie in cookiejar:
|
| 158 |
+
if cookie.name != name:
|
| 159 |
+
continue
|
| 160 |
+
if domain is not None and domain != cookie.domain:
|
| 161 |
+
continue
|
| 162 |
+
if path is not None and path != cookie.path:
|
| 163 |
+
continue
|
| 164 |
+
clearables.append((cookie.domain, cookie.path, cookie.name))
|
| 165 |
+
|
| 166 |
+
for domain, path, name in clearables:
|
| 167 |
+
cookiejar.clear(domain, path, name)
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
class CookieConflictError(RuntimeError):
|
| 171 |
+
"""There are two cookies that meet the criteria specified in the cookie jar.
|
| 172 |
+
Use .get and .set and include domain and path args in order to be more specific.
|
| 173 |
+
"""
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
|
| 177 |
+
"""Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict
|
| 178 |
+
interface.
|
| 179 |
+
|
| 180 |
+
This is the CookieJar we create by default for requests and sessions that
|
| 181 |
+
don't specify one, since some clients may expect response.cookies and
|
| 182 |
+
session.cookies to support dict operations.
|
| 183 |
+
|
| 184 |
+
Requests does not use the dict interface internally; it's just for
|
| 185 |
+
compatibility with external client code. All requests code should work
|
| 186 |
+
out of the box with externally provided instances of ``CookieJar``, e.g.
|
| 187 |
+
``LWPCookieJar`` and ``FileCookieJar``.
|
| 188 |
+
|
| 189 |
+
Unlike a regular CookieJar, this class is pickleable.
|
| 190 |
+
|
| 191 |
+
.. warning:: dictionary operations that are normally O(1) may be O(n).
|
| 192 |
+
"""
|
| 193 |
+
|
| 194 |
+
def get(self, name, default=None, domain=None, path=None):
|
| 195 |
+
"""Dict-like get() that also supports optional domain and path args in
|
| 196 |
+
order to resolve naming collisions from using one cookie jar over
|
| 197 |
+
multiple domains.
|
| 198 |
+
|
| 199 |
+
.. warning:: operation is O(n), not O(1).
|
| 200 |
+
"""
|
| 201 |
+
try:
|
| 202 |
+
return self._find_no_duplicates(name, domain, path)
|
| 203 |
+
except KeyError:
|
| 204 |
+
return default
|
| 205 |
+
|
| 206 |
+
def set(self, name, value, **kwargs):
|
| 207 |
+
"""Dict-like set() that also supports optional domain and path args in
|
| 208 |
+
order to resolve naming collisions from using one cookie jar over
|
| 209 |
+
multiple domains.
|
| 210 |
+
"""
|
| 211 |
+
# support client code that unsets cookies by assignment of a None value:
|
| 212 |
+
if value is None:
|
| 213 |
+
remove_cookie_by_name(
|
| 214 |
+
self, name, domain=kwargs.get("domain"), path=kwargs.get("path")
|
| 215 |
+
)
|
| 216 |
+
return
|
| 217 |
+
|
| 218 |
+
if isinstance(value, Morsel):
|
| 219 |
+
c = morsel_to_cookie(value)
|
| 220 |
+
else:
|
| 221 |
+
c = create_cookie(name, value, **kwargs)
|
| 222 |
+
self.set_cookie(c)
|
| 223 |
+
return c
|
| 224 |
+
|
| 225 |
+
def iterkeys(self):
|
| 226 |
+
"""Dict-like iterkeys() that returns an iterator of names of cookies
|
| 227 |
+
from the jar.
|
| 228 |
+
|
| 229 |
+
.. seealso:: itervalues() and iteritems().
|
| 230 |
+
"""
|
| 231 |
+
for cookie in iter(self):
|
| 232 |
+
yield cookie.name
|
| 233 |
+
|
| 234 |
+
def keys(self):
|
| 235 |
+
"""Dict-like keys() that returns a list of names of cookies from the
|
| 236 |
+
jar.
|
| 237 |
+
|
| 238 |
+
.. seealso:: values() and items().
|
| 239 |
+
"""
|
| 240 |
+
return list(self.iterkeys())
|
| 241 |
+
|
| 242 |
+
def itervalues(self):
|
| 243 |
+
"""Dict-like itervalues() that returns an iterator of values of cookies
|
| 244 |
+
from the jar.
|
| 245 |
+
|
| 246 |
+
.. seealso:: iterkeys() and iteritems().
|
| 247 |
+
"""
|
| 248 |
+
for cookie in iter(self):
|
| 249 |
+
yield cookie.value
|
| 250 |
+
|
| 251 |
+
def values(self):
|
| 252 |
+
"""Dict-like values() that returns a list of values of cookies from the
|
| 253 |
+
jar.
|
| 254 |
+
|
| 255 |
+
.. seealso:: keys() and items().
|
| 256 |
+
"""
|
| 257 |
+
return list(self.itervalues())
|
| 258 |
+
|
| 259 |
+
def iteritems(self):
|
| 260 |
+
"""Dict-like iteritems() that returns an iterator of name-value tuples
|
| 261 |
+
from the jar.
|
| 262 |
+
|
| 263 |
+
.. seealso:: iterkeys() and itervalues().
|
| 264 |
+
"""
|
| 265 |
+
for cookie in iter(self):
|
| 266 |
+
yield cookie.name, cookie.value
|
| 267 |
+
|
| 268 |
+
def items(self):
|
| 269 |
+
"""Dict-like items() that returns a list of name-value tuples from the
|
| 270 |
+
jar. Allows client-code to call ``dict(RequestsCookieJar)`` and get a
|
| 271 |
+
vanilla python dict of key value pairs.
|
| 272 |
+
|
| 273 |
+
.. seealso:: keys() and values().
|
| 274 |
+
"""
|
| 275 |
+
return list(self.iteritems())
|
| 276 |
+
|
| 277 |
+
def list_domains(self):
|
| 278 |
+
"""Utility method to list all the domains in the jar."""
|
| 279 |
+
domains = []
|
| 280 |
+
for cookie in iter(self):
|
| 281 |
+
if cookie.domain not in domains:
|
| 282 |
+
domains.append(cookie.domain)
|
| 283 |
+
return domains
|
| 284 |
+
|
| 285 |
+
def list_paths(self):
|
| 286 |
+
"""Utility method to list all the paths in the jar."""
|
| 287 |
+
paths = []
|
| 288 |
+
for cookie in iter(self):
|
| 289 |
+
if cookie.path not in paths:
|
| 290 |
+
paths.append(cookie.path)
|
| 291 |
+
return paths
|
| 292 |
+
|
| 293 |
+
def multiple_domains(self):
|
| 294 |
+
"""Returns True if there are multiple domains in the jar.
|
| 295 |
+
Returns False otherwise.
|
| 296 |
+
|
| 297 |
+
:rtype: bool
|
| 298 |
+
"""
|
| 299 |
+
domains = []
|
| 300 |
+
for cookie in iter(self):
|
| 301 |
+
if cookie.domain is not None and cookie.domain in domains:
|
| 302 |
+
return True
|
| 303 |
+
domains.append(cookie.domain)
|
| 304 |
+
return False # there is only one domain in jar
|
| 305 |
+
|
| 306 |
+
def get_dict(self, domain=None, path=None):
|
| 307 |
+
"""Takes as an argument an optional domain and path and returns a plain
|
| 308 |
+
old Python dict of name-value pairs of cookies that meet the
|
| 309 |
+
requirements.
|
| 310 |
+
|
| 311 |
+
:rtype: dict
|
| 312 |
+
"""
|
| 313 |
+
dictionary = {}
|
| 314 |
+
for cookie in iter(self):
|
| 315 |
+
if (domain is None or cookie.domain == domain) and (
|
| 316 |
+
path is None or cookie.path == path
|
| 317 |
+
):
|
| 318 |
+
dictionary[cookie.name] = cookie.value
|
| 319 |
+
return dictionary
|
| 320 |
+
|
| 321 |
+
def __contains__(self, name):
|
| 322 |
+
try:
|
| 323 |
+
return super().__contains__(name)
|
| 324 |
+
except CookieConflictError:
|
| 325 |
+
return True
|
| 326 |
+
|
| 327 |
+
def __getitem__(self, name):
|
| 328 |
+
"""Dict-like __getitem__() for compatibility with client code. Throws
|
| 329 |
+
exception if there are more than one cookie with name. In that case,
|
| 330 |
+
use the more explicit get() method instead.
|
| 331 |
+
|
| 332 |
+
.. warning:: operation is O(n), not O(1).
|
| 333 |
+
"""
|
| 334 |
+
return self._find_no_duplicates(name)
|
| 335 |
+
|
| 336 |
+
def __setitem__(self, name, value):
|
| 337 |
+
"""Dict-like __setitem__ for compatibility with client code. Throws
|
| 338 |
+
exception if there is already a cookie of that name in the jar. In that
|
| 339 |
+
case, use the more explicit set() method instead.
|
| 340 |
+
"""
|
| 341 |
+
self.set(name, value)
|
| 342 |
+
|
| 343 |
+
def __delitem__(self, name):
|
| 344 |
+
"""Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s
|
| 345 |
+
``remove_cookie_by_name()``.
|
| 346 |
+
"""
|
| 347 |
+
remove_cookie_by_name(self, name)
|
| 348 |
+
|
| 349 |
+
def set_cookie(self, cookie, *args, **kwargs):
|
| 350 |
+
if (
|
| 351 |
+
hasattr(cookie.value, "startswith")
|
| 352 |
+
and cookie.value.startswith('"')
|
| 353 |
+
and cookie.value.endswith('"')
|
| 354 |
+
):
|
| 355 |
+
cookie.value = cookie.value.replace('\\"', "")
|
| 356 |
+
return super().set_cookie(cookie, *args, **kwargs)
|
| 357 |
+
|
| 358 |
+
def update(self, other):
|
| 359 |
+
"""Updates this jar with cookies from another CookieJar or dict-like"""
|
| 360 |
+
if isinstance(other, cookielib.CookieJar):
|
| 361 |
+
for cookie in other:
|
| 362 |
+
self.set_cookie(copy.copy(cookie))
|
| 363 |
+
else:
|
| 364 |
+
super().update(other)
|
| 365 |
+
|
| 366 |
+
def _find(self, name, domain=None, path=None):
|
| 367 |
+
"""Requests uses this method internally to get cookie values.
|
| 368 |
+
|
| 369 |
+
If there are conflicting cookies, _find arbitrarily chooses one.
|
| 370 |
+
See _find_no_duplicates if you want an exception thrown if there are
|
| 371 |
+
conflicting cookies.
|
| 372 |
+
|
| 373 |
+
:param name: a string containing name of cookie
|
| 374 |
+
:param domain: (optional) string containing domain of cookie
|
| 375 |
+
:param path: (optional) string containing path of cookie
|
| 376 |
+
:return: cookie.value
|
| 377 |
+
"""
|
| 378 |
+
for cookie in iter(self):
|
| 379 |
+
if cookie.name == name:
|
| 380 |
+
if domain is None or cookie.domain == domain:
|
| 381 |
+
if path is None or cookie.path == path:
|
| 382 |
+
return cookie.value
|
| 383 |
+
|
| 384 |
+
raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
|
| 385 |
+
|
| 386 |
+
def _find_no_duplicates(self, name, domain=None, path=None):
|
| 387 |
+
"""Both ``__get_item__`` and ``get`` call this function: it's never
|
| 388 |
+
used elsewhere in Requests.
|
| 389 |
+
|
| 390 |
+
:param name: a string containing name of cookie
|
| 391 |
+
:param domain: (optional) string containing domain of cookie
|
| 392 |
+
:param path: (optional) string containing path of cookie
|
| 393 |
+
:raises KeyError: if cookie is not found
|
| 394 |
+
:raises CookieConflictError: if there are multiple cookies
|
| 395 |
+
that match name and optionally domain and path
|
| 396 |
+
:return: cookie.value
|
| 397 |
+
"""
|
| 398 |
+
toReturn = None
|
| 399 |
+
for cookie in iter(self):
|
| 400 |
+
if cookie.name == name:
|
| 401 |
+
if domain is None or cookie.domain == domain:
|
| 402 |
+
if path is None or cookie.path == path:
|
| 403 |
+
if toReturn is not None:
|
| 404 |
+
# if there are multiple cookies that meet passed in criteria
|
| 405 |
+
raise CookieConflictError(
|
| 406 |
+
f"There are multiple cookies with name, {name!r}"
|
| 407 |
+
)
|
| 408 |
+
# we will eventually return this as long as no cookie conflict
|
| 409 |
+
toReturn = cookie.value
|
| 410 |
+
|
| 411 |
+
if toReturn:
|
| 412 |
+
return toReturn
|
| 413 |
+
raise KeyError(f"name={name!r}, domain={domain!r}, path={path!r}")
|
| 414 |
+
|
| 415 |
+
def __getstate__(self):
|
| 416 |
+
"""Unlike a normal CookieJar, this class is pickleable."""
|
| 417 |
+
state = self.__dict__.copy()
|
| 418 |
+
# remove the unpickleable RLock object
|
| 419 |
+
state.pop("_cookies_lock")
|
| 420 |
+
return state
|
| 421 |
+
|
| 422 |
+
def __setstate__(self, state):
|
| 423 |
+
"""Unlike a normal CookieJar, this class is pickleable."""
|
| 424 |
+
self.__dict__.update(state)
|
| 425 |
+
if "_cookies_lock" not in self.__dict__:
|
| 426 |
+
self._cookies_lock = threading.RLock()
|
| 427 |
+
|
| 428 |
+
def copy(self):
|
| 429 |
+
"""Return a copy of this RequestsCookieJar."""
|
| 430 |
+
new_cj = RequestsCookieJar()
|
| 431 |
+
new_cj.set_policy(self.get_policy())
|
| 432 |
+
new_cj.update(self)
|
| 433 |
+
return new_cj
|
| 434 |
+
|
| 435 |
+
def get_policy(self):
|
| 436 |
+
"""Return the CookiePolicy instance used."""
|
| 437 |
+
return self._policy
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
def _copy_cookie_jar(jar):
|
| 441 |
+
if jar is None:
|
| 442 |
+
return None
|
| 443 |
+
|
| 444 |
+
if hasattr(jar, "copy"):
|
| 445 |
+
# We're dealing with an instance of RequestsCookieJar
|
| 446 |
+
return jar.copy()
|
| 447 |
+
# We're dealing with a generic CookieJar instance
|
| 448 |
+
new_jar = copy.copy(jar)
|
| 449 |
+
new_jar.clear()
|
| 450 |
+
for cookie in jar:
|
| 451 |
+
new_jar.set_cookie(copy.copy(cookie))
|
| 452 |
+
return new_jar
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
def create_cookie(name, value, **kwargs):
|
| 456 |
+
"""Make a cookie from underspecified parameters.
|
| 457 |
+
|
| 458 |
+
By default, the pair of `name` and `value` will be set for the domain ''
|
| 459 |
+
and sent on every request (this is sometimes called a "supercookie").
|
| 460 |
+
"""
|
| 461 |
+
result = {
|
| 462 |
+
"version": 0,
|
| 463 |
+
"name": name,
|
| 464 |
+
"value": value,
|
| 465 |
+
"port": None,
|
| 466 |
+
"domain": "",
|
| 467 |
+
"path": "/",
|
| 468 |
+
"secure": False,
|
| 469 |
+
"expires": None,
|
| 470 |
+
"discard": True,
|
| 471 |
+
"comment": None,
|
| 472 |
+
"comment_url": None,
|
| 473 |
+
"rest": {"HttpOnly": None},
|
| 474 |
+
"rfc2109": False,
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
badargs = set(kwargs) - set(result)
|
| 478 |
+
if badargs:
|
| 479 |
+
raise TypeError(
|
| 480 |
+
f"create_cookie() got unexpected keyword arguments: {list(badargs)}"
|
| 481 |
+
)
|
| 482 |
+
|
| 483 |
+
result.update(kwargs)
|
| 484 |
+
result["port_specified"] = bool(result["port"])
|
| 485 |
+
result["domain_specified"] = bool(result["domain"])
|
| 486 |
+
result["domain_initial_dot"] = result["domain"].startswith(".")
|
| 487 |
+
result["path_specified"] = bool(result["path"])
|
| 488 |
+
|
| 489 |
+
return cookielib.Cookie(**result)
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
def morsel_to_cookie(morsel):
|
| 493 |
+
"""Convert a Morsel object into a Cookie containing the one k/v pair."""
|
| 494 |
+
|
| 495 |
+
expires = None
|
| 496 |
+
if morsel["max-age"]:
|
| 497 |
+
try:
|
| 498 |
+
expires = int(time.time() + int(morsel["max-age"]))
|
| 499 |
+
except ValueError:
|
| 500 |
+
raise TypeError(f"max-age: {morsel['max-age']} must be integer")
|
| 501 |
+
elif morsel["expires"]:
|
| 502 |
+
time_template = "%a, %d-%b-%Y %H:%M:%S GMT"
|
| 503 |
+
expires = calendar.timegm(time.strptime(morsel["expires"], time_template))
|
| 504 |
+
return create_cookie(
|
| 505 |
+
comment=morsel["comment"],
|
| 506 |
+
comment_url=bool(morsel["comment"]),
|
| 507 |
+
discard=False,
|
| 508 |
+
domain=morsel["domain"],
|
| 509 |
+
expires=expires,
|
| 510 |
+
name=morsel.key,
|
| 511 |
+
path=morsel["path"],
|
| 512 |
+
port=None,
|
| 513 |
+
rest={"HttpOnly": morsel["httponly"]},
|
| 514 |
+
rfc2109=False,
|
| 515 |
+
secure=bool(morsel["secure"]),
|
| 516 |
+
value=morsel.value,
|
| 517 |
+
version=morsel["version"] or 0,
|
| 518 |
+
)
|
| 519 |
+
|
| 520 |
+
|
| 521 |
+
def cookiejar_from_dict(cookie_dict, cookiejar=None, overwrite=True):
|
| 522 |
+
"""Returns a CookieJar from a key/value dictionary.
|
| 523 |
+
|
| 524 |
+
:param cookie_dict: Dict of key/values to insert into CookieJar.
|
| 525 |
+
:param cookiejar: (optional) A cookiejar to add the cookies to.
|
| 526 |
+
:param overwrite: (optional) If False, will not replace cookies
|
| 527 |
+
already in the jar with new ones.
|
| 528 |
+
:rtype: CookieJar
|
| 529 |
+
"""
|
| 530 |
+
if cookiejar is None:
|
| 531 |
+
cookiejar = RequestsCookieJar()
|
| 532 |
+
|
| 533 |
+
if cookie_dict is not None:
|
| 534 |
+
names_from_jar = [cookie.name for cookie in cookiejar]
|
| 535 |
+
for name in cookie_dict:
|
| 536 |
+
if overwrite or (name not in names_from_jar):
|
| 537 |
+
cookiejar.set_cookie(create_cookie(name, cookie_dict[name]))
|
| 538 |
+
|
| 539 |
+
return cookiejar
|
| 540 |
+
|
| 541 |
+
|
| 542 |
+
def merge_cookies(cookiejar, cookies):
|
| 543 |
+
"""Add cookies to cookiejar and returns a merged CookieJar.
|
| 544 |
+
|
| 545 |
+
:param cookiejar: CookieJar object to add the cookies to.
|
| 546 |
+
:param cookies: Dictionary or CookieJar object to be added.
|
| 547 |
+
:rtype: CookieJar
|
| 548 |
+
"""
|
| 549 |
+
if not isinstance(cookiejar, cookielib.CookieJar):
|
| 550 |
+
raise ValueError("You can only merge into CookieJar")
|
| 551 |
+
|
| 552 |
+
if isinstance(cookies, dict):
|
| 553 |
+
cookiejar = cookiejar_from_dict(cookies, cookiejar=cookiejar, overwrite=False)
|
| 554 |
+
elif isinstance(cookies, cookielib.CookieJar):
|
| 555 |
+
try:
|
| 556 |
+
cookiejar.update(cookies)
|
| 557 |
+
except AttributeError:
|
| 558 |
+
for cookie_in_jar in cookies:
|
| 559 |
+
cookiejar.set_cookie(cookie_in_jar)
|
| 560 |
+
|
| 561 |
+
return cookiejar
|
.venv/Lib/site-packages/pip/_vendor/requests/exceptions.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.exceptions
|
| 3 |
+
~~~~~~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module contains the set of Requests' exceptions.
|
| 6 |
+
"""
|
| 7 |
+
from pip._vendor.urllib3.exceptions import HTTPError as BaseHTTPError
|
| 8 |
+
|
| 9 |
+
from .compat import JSONDecodeError as CompatJSONDecodeError
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class RequestException(IOError):
|
| 13 |
+
"""There was an ambiguous exception that occurred while handling your
|
| 14 |
+
request.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
def __init__(self, *args, **kwargs):
|
| 18 |
+
"""Initialize RequestException with `request` and `response` objects."""
|
| 19 |
+
response = kwargs.pop("response", None)
|
| 20 |
+
self.response = response
|
| 21 |
+
self.request = kwargs.pop("request", None)
|
| 22 |
+
if response is not None and not self.request and hasattr(response, "request"):
|
| 23 |
+
self.request = self.response.request
|
| 24 |
+
super().__init__(*args, **kwargs)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class InvalidJSONError(RequestException):
|
| 28 |
+
"""A JSON error occurred."""
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError):
|
| 32 |
+
"""Couldn't decode the text into json"""
|
| 33 |
+
|
| 34 |
+
def __init__(self, *args, **kwargs):
|
| 35 |
+
"""
|
| 36 |
+
Construct the JSONDecodeError instance first with all
|
| 37 |
+
args. Then use it's args to construct the IOError so that
|
| 38 |
+
the json specific args aren't used as IOError specific args
|
| 39 |
+
and the error message from JSONDecodeError is preserved.
|
| 40 |
+
"""
|
| 41 |
+
CompatJSONDecodeError.__init__(self, *args)
|
| 42 |
+
InvalidJSONError.__init__(self, *self.args, **kwargs)
|
| 43 |
+
|
| 44 |
+
def __reduce__(self):
|
| 45 |
+
"""
|
| 46 |
+
The __reduce__ method called when pickling the object must
|
| 47 |
+
be the one from the JSONDecodeError (be it json/simplejson)
|
| 48 |
+
as it expects all the arguments for instantiation, not just
|
| 49 |
+
one like the IOError, and the MRO would by default call the
|
| 50 |
+
__reduce__ method from the IOError due to the inheritance order.
|
| 51 |
+
"""
|
| 52 |
+
return CompatJSONDecodeError.__reduce__(self)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class HTTPError(RequestException):
|
| 56 |
+
"""An HTTP error occurred."""
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class ConnectionError(RequestException):
|
| 60 |
+
"""A Connection error occurred."""
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
class ProxyError(ConnectionError):
|
| 64 |
+
"""A proxy error occurred."""
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class SSLError(ConnectionError):
|
| 68 |
+
"""An SSL error occurred."""
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
class Timeout(RequestException):
|
| 72 |
+
"""The request timed out.
|
| 73 |
+
|
| 74 |
+
Catching this error will catch both
|
| 75 |
+
:exc:`~requests.exceptions.ConnectTimeout` and
|
| 76 |
+
:exc:`~requests.exceptions.ReadTimeout` errors.
|
| 77 |
+
"""
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class ConnectTimeout(ConnectionError, Timeout):
|
| 81 |
+
"""The request timed out while trying to connect to the remote server.
|
| 82 |
+
|
| 83 |
+
Requests that produced this error are safe to retry.
|
| 84 |
+
"""
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class ReadTimeout(Timeout):
|
| 88 |
+
"""The server did not send any data in the allotted amount of time."""
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
class URLRequired(RequestException):
|
| 92 |
+
"""A valid URL is required to make a request."""
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
class TooManyRedirects(RequestException):
|
| 96 |
+
"""Too many redirects."""
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
class MissingSchema(RequestException, ValueError):
|
| 100 |
+
"""The URL scheme (e.g. http or https) is missing."""
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class InvalidSchema(RequestException, ValueError):
|
| 104 |
+
"""The URL scheme provided is either invalid or unsupported."""
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
class InvalidURL(RequestException, ValueError):
|
| 108 |
+
"""The URL provided was somehow invalid."""
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
class InvalidHeader(RequestException, ValueError):
|
| 112 |
+
"""The header value provided was somehow invalid."""
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
class InvalidProxyURL(InvalidURL):
|
| 116 |
+
"""The proxy URL provided is invalid."""
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
class ChunkedEncodingError(RequestException):
|
| 120 |
+
"""The server declared chunked encoding but sent an invalid chunk."""
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
class ContentDecodingError(RequestException, BaseHTTPError):
|
| 124 |
+
"""Failed to decode response content."""
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class StreamConsumedError(RequestException, TypeError):
|
| 128 |
+
"""The content for this response was already consumed."""
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
class RetryError(RequestException):
|
| 132 |
+
"""Custom retries logic failed"""
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
class UnrewindableBodyError(RequestException):
|
| 136 |
+
"""Requests encountered an error when trying to rewind a body."""
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
# Warnings
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
class RequestsWarning(Warning):
|
| 143 |
+
"""Base warning for Requests."""
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
class FileModeWarning(RequestsWarning, DeprecationWarning):
|
| 147 |
+
"""A file was opened in text mode, but Requests determined its binary length."""
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
class RequestsDependencyWarning(RequestsWarning):
|
| 151 |
+
"""An imported dependency doesn't match the expected version range."""
|
.venv/Lib/site-packages/pip/_vendor/requests/help.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Module containing bug report helper(s)."""
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
import platform
|
| 5 |
+
import ssl
|
| 6 |
+
import sys
|
| 7 |
+
|
| 8 |
+
from pip._vendor import idna
|
| 9 |
+
from pip._vendor import urllib3
|
| 10 |
+
|
| 11 |
+
from . import __version__ as requests_version
|
| 12 |
+
|
| 13 |
+
charset_normalizer = None
|
| 14 |
+
chardet = None
|
| 15 |
+
|
| 16 |
+
try:
|
| 17 |
+
from pip._vendor.urllib3.contrib import pyopenssl
|
| 18 |
+
except ImportError:
|
| 19 |
+
pyopenssl = None
|
| 20 |
+
OpenSSL = None
|
| 21 |
+
cryptography = None
|
| 22 |
+
else:
|
| 23 |
+
import cryptography
|
| 24 |
+
import OpenSSL
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def _implementation():
|
| 28 |
+
"""Return a dict with the Python implementation and version.
|
| 29 |
+
|
| 30 |
+
Provide both the name and the version of the Python implementation
|
| 31 |
+
currently running. For example, on CPython 3.10.3 it will return
|
| 32 |
+
{'name': 'CPython', 'version': '3.10.3'}.
|
| 33 |
+
|
| 34 |
+
This function works best on CPython and PyPy: in particular, it probably
|
| 35 |
+
doesn't work for Jython or IronPython. Future investigation should be done
|
| 36 |
+
to work out the correct shape of the code for those platforms.
|
| 37 |
+
"""
|
| 38 |
+
implementation = platform.python_implementation()
|
| 39 |
+
|
| 40 |
+
if implementation == "CPython":
|
| 41 |
+
implementation_version = platform.python_version()
|
| 42 |
+
elif implementation == "PyPy":
|
| 43 |
+
implementation_version = "{}.{}.{}".format(
|
| 44 |
+
sys.pypy_version_info.major,
|
| 45 |
+
sys.pypy_version_info.minor,
|
| 46 |
+
sys.pypy_version_info.micro,
|
| 47 |
+
)
|
| 48 |
+
if sys.pypy_version_info.releaselevel != "final":
|
| 49 |
+
implementation_version = "".join(
|
| 50 |
+
[implementation_version, sys.pypy_version_info.releaselevel]
|
| 51 |
+
)
|
| 52 |
+
elif implementation == "Jython":
|
| 53 |
+
implementation_version = platform.python_version() # Complete Guess
|
| 54 |
+
elif implementation == "IronPython":
|
| 55 |
+
implementation_version = platform.python_version() # Complete Guess
|
| 56 |
+
else:
|
| 57 |
+
implementation_version = "Unknown"
|
| 58 |
+
|
| 59 |
+
return {"name": implementation, "version": implementation_version}
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def info():
|
| 63 |
+
"""Generate information for a bug report."""
|
| 64 |
+
try:
|
| 65 |
+
platform_info = {
|
| 66 |
+
"system": platform.system(),
|
| 67 |
+
"release": platform.release(),
|
| 68 |
+
}
|
| 69 |
+
except OSError:
|
| 70 |
+
platform_info = {
|
| 71 |
+
"system": "Unknown",
|
| 72 |
+
"release": "Unknown",
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
implementation_info = _implementation()
|
| 76 |
+
urllib3_info = {"version": urllib3.__version__}
|
| 77 |
+
charset_normalizer_info = {"version": None}
|
| 78 |
+
chardet_info = {"version": None}
|
| 79 |
+
if charset_normalizer:
|
| 80 |
+
charset_normalizer_info = {"version": charset_normalizer.__version__}
|
| 81 |
+
if chardet:
|
| 82 |
+
chardet_info = {"version": chardet.__version__}
|
| 83 |
+
|
| 84 |
+
pyopenssl_info = {
|
| 85 |
+
"version": None,
|
| 86 |
+
"openssl_version": "",
|
| 87 |
+
}
|
| 88 |
+
if OpenSSL:
|
| 89 |
+
pyopenssl_info = {
|
| 90 |
+
"version": OpenSSL.__version__,
|
| 91 |
+
"openssl_version": f"{OpenSSL.SSL.OPENSSL_VERSION_NUMBER:x}",
|
| 92 |
+
}
|
| 93 |
+
cryptography_info = {
|
| 94 |
+
"version": getattr(cryptography, "__version__", ""),
|
| 95 |
+
}
|
| 96 |
+
idna_info = {
|
| 97 |
+
"version": getattr(idna, "__version__", ""),
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
system_ssl = ssl.OPENSSL_VERSION_NUMBER
|
| 101 |
+
system_ssl_info = {"version": f"{system_ssl:x}" if system_ssl is not None else ""}
|
| 102 |
+
|
| 103 |
+
return {
|
| 104 |
+
"platform": platform_info,
|
| 105 |
+
"implementation": implementation_info,
|
| 106 |
+
"system_ssl": system_ssl_info,
|
| 107 |
+
"using_pyopenssl": pyopenssl is not None,
|
| 108 |
+
"using_charset_normalizer": chardet is None,
|
| 109 |
+
"pyOpenSSL": pyopenssl_info,
|
| 110 |
+
"urllib3": urllib3_info,
|
| 111 |
+
"chardet": chardet_info,
|
| 112 |
+
"charset_normalizer": charset_normalizer_info,
|
| 113 |
+
"cryptography": cryptography_info,
|
| 114 |
+
"idna": idna_info,
|
| 115 |
+
"requests": {
|
| 116 |
+
"version": requests_version,
|
| 117 |
+
},
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def main():
|
| 122 |
+
"""Pretty-print the bug information as JSON."""
|
| 123 |
+
print(json.dumps(info(), sort_keys=True, indent=2))
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
if __name__ == "__main__":
|
| 127 |
+
main()
|
.venv/Lib/site-packages/pip/_vendor/requests/hooks.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.hooks
|
| 3 |
+
~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module provides the capabilities for the Requests hooks system.
|
| 6 |
+
|
| 7 |
+
Available hooks:
|
| 8 |
+
|
| 9 |
+
``response``:
|
| 10 |
+
The response generated from a Request.
|
| 11 |
+
"""
|
| 12 |
+
HOOKS = ["response"]
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def default_hooks():
|
| 16 |
+
return {event: [] for event in HOOKS}
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
# TODO: response is the only one
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def dispatch_hook(key, hooks, hook_data, **kwargs):
|
| 23 |
+
"""Dispatches a hook dictionary on a given piece of data."""
|
| 24 |
+
hooks = hooks or {}
|
| 25 |
+
hooks = hooks.get(key)
|
| 26 |
+
if hooks:
|
| 27 |
+
if hasattr(hooks, "__call__"):
|
| 28 |
+
hooks = [hooks]
|
| 29 |
+
for hook in hooks:
|
| 30 |
+
_hook_data = hook(hook_data, **kwargs)
|
| 31 |
+
if _hook_data is not None:
|
| 32 |
+
hook_data = _hook_data
|
| 33 |
+
return hook_data
|
.venv/Lib/site-packages/pip/_vendor/requests/models.py
ADDED
|
@@ -0,0 +1,1039 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.models
|
| 3 |
+
~~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module contains the primary objects that power Requests.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import datetime
|
| 9 |
+
|
| 10 |
+
# Import encoding now, to avoid implicit import later.
|
| 11 |
+
# Implicit import within threads may cause LookupError when standard library is in a ZIP,
|
| 12 |
+
# such as in Embedded Python. See https://github.com/psf/requests/issues/3578.
|
| 13 |
+
import encodings.idna # noqa: F401
|
| 14 |
+
from io import UnsupportedOperation
|
| 15 |
+
|
| 16 |
+
from pip._vendor.urllib3.exceptions import (
|
| 17 |
+
DecodeError,
|
| 18 |
+
LocationParseError,
|
| 19 |
+
ProtocolError,
|
| 20 |
+
ReadTimeoutError,
|
| 21 |
+
SSLError,
|
| 22 |
+
)
|
| 23 |
+
from pip._vendor.urllib3.fields import RequestField
|
| 24 |
+
from pip._vendor.urllib3.filepost import encode_multipart_formdata
|
| 25 |
+
from pip._vendor.urllib3.util import parse_url
|
| 26 |
+
|
| 27 |
+
from ._internal_utils import to_native_string, unicode_is_ascii
|
| 28 |
+
from .auth import HTTPBasicAuth
|
| 29 |
+
from .compat import (
|
| 30 |
+
Callable,
|
| 31 |
+
JSONDecodeError,
|
| 32 |
+
Mapping,
|
| 33 |
+
basestring,
|
| 34 |
+
builtin_str,
|
| 35 |
+
chardet,
|
| 36 |
+
cookielib,
|
| 37 |
+
)
|
| 38 |
+
from .compat import json as complexjson
|
| 39 |
+
from .compat import urlencode, urlsplit, urlunparse
|
| 40 |
+
from .cookies import _copy_cookie_jar, cookiejar_from_dict, get_cookie_header
|
| 41 |
+
from .exceptions import (
|
| 42 |
+
ChunkedEncodingError,
|
| 43 |
+
ConnectionError,
|
| 44 |
+
ContentDecodingError,
|
| 45 |
+
HTTPError,
|
| 46 |
+
InvalidJSONError,
|
| 47 |
+
InvalidURL,
|
| 48 |
+
)
|
| 49 |
+
from .exceptions import JSONDecodeError as RequestsJSONDecodeError
|
| 50 |
+
from .exceptions import MissingSchema
|
| 51 |
+
from .exceptions import SSLError as RequestsSSLError
|
| 52 |
+
from .exceptions import StreamConsumedError
|
| 53 |
+
from .hooks import default_hooks
|
| 54 |
+
from .status_codes import codes
|
| 55 |
+
from .structures import CaseInsensitiveDict
|
| 56 |
+
from .utils import (
|
| 57 |
+
check_header_validity,
|
| 58 |
+
get_auth_from_url,
|
| 59 |
+
guess_filename,
|
| 60 |
+
guess_json_utf,
|
| 61 |
+
iter_slices,
|
| 62 |
+
parse_header_links,
|
| 63 |
+
requote_uri,
|
| 64 |
+
stream_decode_response_unicode,
|
| 65 |
+
super_len,
|
| 66 |
+
to_key_val_list,
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
#: The set of HTTP status codes that indicate an automatically
|
| 70 |
+
#: processable redirect.
|
| 71 |
+
REDIRECT_STATI = (
|
| 72 |
+
codes.moved, # 301
|
| 73 |
+
codes.found, # 302
|
| 74 |
+
codes.other, # 303
|
| 75 |
+
codes.temporary_redirect, # 307
|
| 76 |
+
codes.permanent_redirect, # 308
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
DEFAULT_REDIRECT_LIMIT = 30
|
| 80 |
+
CONTENT_CHUNK_SIZE = 10 * 1024
|
| 81 |
+
ITER_CHUNK_SIZE = 512
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
class RequestEncodingMixin:
|
| 85 |
+
@property
|
| 86 |
+
def path_url(self):
|
| 87 |
+
"""Build the path URL to use."""
|
| 88 |
+
|
| 89 |
+
url = []
|
| 90 |
+
|
| 91 |
+
p = urlsplit(self.url)
|
| 92 |
+
|
| 93 |
+
path = p.path
|
| 94 |
+
if not path:
|
| 95 |
+
path = "/"
|
| 96 |
+
|
| 97 |
+
url.append(path)
|
| 98 |
+
|
| 99 |
+
query = p.query
|
| 100 |
+
if query:
|
| 101 |
+
url.append("?")
|
| 102 |
+
url.append(query)
|
| 103 |
+
|
| 104 |
+
return "".join(url)
|
| 105 |
+
|
| 106 |
+
@staticmethod
|
| 107 |
+
def _encode_params(data):
|
| 108 |
+
"""Encode parameters in a piece of data.
|
| 109 |
+
|
| 110 |
+
Will successfully encode parameters when passed as a dict or a list of
|
| 111 |
+
2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
|
| 112 |
+
if parameters are supplied as a dict.
|
| 113 |
+
"""
|
| 114 |
+
|
| 115 |
+
if isinstance(data, (str, bytes)):
|
| 116 |
+
return data
|
| 117 |
+
elif hasattr(data, "read"):
|
| 118 |
+
return data
|
| 119 |
+
elif hasattr(data, "__iter__"):
|
| 120 |
+
result = []
|
| 121 |
+
for k, vs in to_key_val_list(data):
|
| 122 |
+
if isinstance(vs, basestring) or not hasattr(vs, "__iter__"):
|
| 123 |
+
vs = [vs]
|
| 124 |
+
for v in vs:
|
| 125 |
+
if v is not None:
|
| 126 |
+
result.append(
|
| 127 |
+
(
|
| 128 |
+
k.encode("utf-8") if isinstance(k, str) else k,
|
| 129 |
+
v.encode("utf-8") if isinstance(v, str) else v,
|
| 130 |
+
)
|
| 131 |
+
)
|
| 132 |
+
return urlencode(result, doseq=True)
|
| 133 |
+
else:
|
| 134 |
+
return data
|
| 135 |
+
|
| 136 |
+
@staticmethod
|
| 137 |
+
def _encode_files(files, data):
|
| 138 |
+
"""Build the body for a multipart/form-data request.
|
| 139 |
+
|
| 140 |
+
Will successfully encode files when passed as a dict or a list of
|
| 141 |
+
tuples. Order is retained if data is a list of tuples but arbitrary
|
| 142 |
+
if parameters are supplied as a dict.
|
| 143 |
+
The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype)
|
| 144 |
+
or 4-tuples (filename, fileobj, contentype, custom_headers).
|
| 145 |
+
"""
|
| 146 |
+
if not files:
|
| 147 |
+
raise ValueError("Files must be provided.")
|
| 148 |
+
elif isinstance(data, basestring):
|
| 149 |
+
raise ValueError("Data must not be a string.")
|
| 150 |
+
|
| 151 |
+
new_fields = []
|
| 152 |
+
fields = to_key_val_list(data or {})
|
| 153 |
+
files = to_key_val_list(files or {})
|
| 154 |
+
|
| 155 |
+
for field, val in fields:
|
| 156 |
+
if isinstance(val, basestring) or not hasattr(val, "__iter__"):
|
| 157 |
+
val = [val]
|
| 158 |
+
for v in val:
|
| 159 |
+
if v is not None:
|
| 160 |
+
# Don't call str() on bytestrings: in Py3 it all goes wrong.
|
| 161 |
+
if not isinstance(v, bytes):
|
| 162 |
+
v = str(v)
|
| 163 |
+
|
| 164 |
+
new_fields.append(
|
| 165 |
+
(
|
| 166 |
+
field.decode("utf-8")
|
| 167 |
+
if isinstance(field, bytes)
|
| 168 |
+
else field,
|
| 169 |
+
v.encode("utf-8") if isinstance(v, str) else v,
|
| 170 |
+
)
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
for k, v in files:
|
| 174 |
+
# support for explicit filename
|
| 175 |
+
ft = None
|
| 176 |
+
fh = None
|
| 177 |
+
if isinstance(v, (tuple, list)):
|
| 178 |
+
if len(v) == 2:
|
| 179 |
+
fn, fp = v
|
| 180 |
+
elif len(v) == 3:
|
| 181 |
+
fn, fp, ft = v
|
| 182 |
+
else:
|
| 183 |
+
fn, fp, ft, fh = v
|
| 184 |
+
else:
|
| 185 |
+
fn = guess_filename(v) or k
|
| 186 |
+
fp = v
|
| 187 |
+
|
| 188 |
+
if isinstance(fp, (str, bytes, bytearray)):
|
| 189 |
+
fdata = fp
|
| 190 |
+
elif hasattr(fp, "read"):
|
| 191 |
+
fdata = fp.read()
|
| 192 |
+
elif fp is None:
|
| 193 |
+
continue
|
| 194 |
+
else:
|
| 195 |
+
fdata = fp
|
| 196 |
+
|
| 197 |
+
rf = RequestField(name=k, data=fdata, filename=fn, headers=fh)
|
| 198 |
+
rf.make_multipart(content_type=ft)
|
| 199 |
+
new_fields.append(rf)
|
| 200 |
+
|
| 201 |
+
body, content_type = encode_multipart_formdata(new_fields)
|
| 202 |
+
|
| 203 |
+
return body, content_type
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
class RequestHooksMixin:
|
| 207 |
+
def register_hook(self, event, hook):
|
| 208 |
+
"""Properly register a hook."""
|
| 209 |
+
|
| 210 |
+
if event not in self.hooks:
|
| 211 |
+
raise ValueError(f'Unsupported event specified, with event name "{event}"')
|
| 212 |
+
|
| 213 |
+
if isinstance(hook, Callable):
|
| 214 |
+
self.hooks[event].append(hook)
|
| 215 |
+
elif hasattr(hook, "__iter__"):
|
| 216 |
+
self.hooks[event].extend(h for h in hook if isinstance(h, Callable))
|
| 217 |
+
|
| 218 |
+
def deregister_hook(self, event, hook):
|
| 219 |
+
"""Deregister a previously registered hook.
|
| 220 |
+
Returns True if the hook existed, False if not.
|
| 221 |
+
"""
|
| 222 |
+
|
| 223 |
+
try:
|
| 224 |
+
self.hooks[event].remove(hook)
|
| 225 |
+
return True
|
| 226 |
+
except ValueError:
|
| 227 |
+
return False
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
class Request(RequestHooksMixin):
|
| 231 |
+
"""A user-created :class:`Request <Request>` object.
|
| 232 |
+
|
| 233 |
+
Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server.
|
| 234 |
+
|
| 235 |
+
:param method: HTTP method to use.
|
| 236 |
+
:param url: URL to send.
|
| 237 |
+
:param headers: dictionary of headers to send.
|
| 238 |
+
:param files: dictionary of {filename: fileobject} files to multipart upload.
|
| 239 |
+
:param data: the body to attach to the request. If a dictionary or
|
| 240 |
+
list of tuples ``[(key, value)]`` is provided, form-encoding will
|
| 241 |
+
take place.
|
| 242 |
+
:param json: json for the body to attach to the request (if files or data is not specified).
|
| 243 |
+
:param params: URL parameters to append to the URL. If a dictionary or
|
| 244 |
+
list of tuples ``[(key, value)]`` is provided, form-encoding will
|
| 245 |
+
take place.
|
| 246 |
+
:param auth: Auth handler or (user, pass) tuple.
|
| 247 |
+
:param cookies: dictionary or CookieJar of cookies to attach to this request.
|
| 248 |
+
:param hooks: dictionary of callback hooks, for internal usage.
|
| 249 |
+
|
| 250 |
+
Usage::
|
| 251 |
+
|
| 252 |
+
>>> import requests
|
| 253 |
+
>>> req = requests.Request('GET', 'https://httpbin.org/get')
|
| 254 |
+
>>> req.prepare()
|
| 255 |
+
<PreparedRequest [GET]>
|
| 256 |
+
"""
|
| 257 |
+
|
| 258 |
+
def __init__(
|
| 259 |
+
self,
|
| 260 |
+
method=None,
|
| 261 |
+
url=None,
|
| 262 |
+
headers=None,
|
| 263 |
+
files=None,
|
| 264 |
+
data=None,
|
| 265 |
+
params=None,
|
| 266 |
+
auth=None,
|
| 267 |
+
cookies=None,
|
| 268 |
+
hooks=None,
|
| 269 |
+
json=None,
|
| 270 |
+
):
|
| 271 |
+
# Default empty dicts for dict params.
|
| 272 |
+
data = [] if data is None else data
|
| 273 |
+
files = [] if files is None else files
|
| 274 |
+
headers = {} if headers is None else headers
|
| 275 |
+
params = {} if params is None else params
|
| 276 |
+
hooks = {} if hooks is None else hooks
|
| 277 |
+
|
| 278 |
+
self.hooks = default_hooks()
|
| 279 |
+
for k, v in list(hooks.items()):
|
| 280 |
+
self.register_hook(event=k, hook=v)
|
| 281 |
+
|
| 282 |
+
self.method = method
|
| 283 |
+
self.url = url
|
| 284 |
+
self.headers = headers
|
| 285 |
+
self.files = files
|
| 286 |
+
self.data = data
|
| 287 |
+
self.json = json
|
| 288 |
+
self.params = params
|
| 289 |
+
self.auth = auth
|
| 290 |
+
self.cookies = cookies
|
| 291 |
+
|
| 292 |
+
def __repr__(self):
|
| 293 |
+
return f"<Request [{self.method}]>"
|
| 294 |
+
|
| 295 |
+
def prepare(self):
|
| 296 |
+
"""Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it."""
|
| 297 |
+
p = PreparedRequest()
|
| 298 |
+
p.prepare(
|
| 299 |
+
method=self.method,
|
| 300 |
+
url=self.url,
|
| 301 |
+
headers=self.headers,
|
| 302 |
+
files=self.files,
|
| 303 |
+
data=self.data,
|
| 304 |
+
json=self.json,
|
| 305 |
+
params=self.params,
|
| 306 |
+
auth=self.auth,
|
| 307 |
+
cookies=self.cookies,
|
| 308 |
+
hooks=self.hooks,
|
| 309 |
+
)
|
| 310 |
+
return p
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
|
| 314 |
+
"""The fully mutable :class:`PreparedRequest <PreparedRequest>` object,
|
| 315 |
+
containing the exact bytes that will be sent to the server.
|
| 316 |
+
|
| 317 |
+
Instances are generated from a :class:`Request <Request>` object, and
|
| 318 |
+
should not be instantiated manually; doing so may produce undesirable
|
| 319 |
+
effects.
|
| 320 |
+
|
| 321 |
+
Usage::
|
| 322 |
+
|
| 323 |
+
>>> import requests
|
| 324 |
+
>>> req = requests.Request('GET', 'https://httpbin.org/get')
|
| 325 |
+
>>> r = req.prepare()
|
| 326 |
+
>>> r
|
| 327 |
+
<PreparedRequest [GET]>
|
| 328 |
+
|
| 329 |
+
>>> s = requests.Session()
|
| 330 |
+
>>> s.send(r)
|
| 331 |
+
<Response [200]>
|
| 332 |
+
"""
|
| 333 |
+
|
| 334 |
+
def __init__(self):
|
| 335 |
+
#: HTTP verb to send to the server.
|
| 336 |
+
self.method = None
|
| 337 |
+
#: HTTP URL to send the request to.
|
| 338 |
+
self.url = None
|
| 339 |
+
#: dictionary of HTTP headers.
|
| 340 |
+
self.headers = None
|
| 341 |
+
# The `CookieJar` used to create the Cookie header will be stored here
|
| 342 |
+
# after prepare_cookies is called
|
| 343 |
+
self._cookies = None
|
| 344 |
+
#: request body to send to the server.
|
| 345 |
+
self.body = None
|
| 346 |
+
#: dictionary of callback hooks, for internal usage.
|
| 347 |
+
self.hooks = default_hooks()
|
| 348 |
+
#: integer denoting starting position of a readable file-like body.
|
| 349 |
+
self._body_position = None
|
| 350 |
+
|
| 351 |
+
def prepare(
|
| 352 |
+
self,
|
| 353 |
+
method=None,
|
| 354 |
+
url=None,
|
| 355 |
+
headers=None,
|
| 356 |
+
files=None,
|
| 357 |
+
data=None,
|
| 358 |
+
params=None,
|
| 359 |
+
auth=None,
|
| 360 |
+
cookies=None,
|
| 361 |
+
hooks=None,
|
| 362 |
+
json=None,
|
| 363 |
+
):
|
| 364 |
+
"""Prepares the entire request with the given parameters."""
|
| 365 |
+
|
| 366 |
+
self.prepare_method(method)
|
| 367 |
+
self.prepare_url(url, params)
|
| 368 |
+
self.prepare_headers(headers)
|
| 369 |
+
self.prepare_cookies(cookies)
|
| 370 |
+
self.prepare_body(data, files, json)
|
| 371 |
+
self.prepare_auth(auth, url)
|
| 372 |
+
|
| 373 |
+
# Note that prepare_auth must be last to enable authentication schemes
|
| 374 |
+
# such as OAuth to work on a fully prepared request.
|
| 375 |
+
|
| 376 |
+
# This MUST go after prepare_auth. Authenticators could add a hook
|
| 377 |
+
self.prepare_hooks(hooks)
|
| 378 |
+
|
| 379 |
+
def __repr__(self):
|
| 380 |
+
return f"<PreparedRequest [{self.method}]>"
|
| 381 |
+
|
| 382 |
+
def copy(self):
|
| 383 |
+
p = PreparedRequest()
|
| 384 |
+
p.method = self.method
|
| 385 |
+
p.url = self.url
|
| 386 |
+
p.headers = self.headers.copy() if self.headers is not None else None
|
| 387 |
+
p._cookies = _copy_cookie_jar(self._cookies)
|
| 388 |
+
p.body = self.body
|
| 389 |
+
p.hooks = self.hooks
|
| 390 |
+
p._body_position = self._body_position
|
| 391 |
+
return p
|
| 392 |
+
|
| 393 |
+
def prepare_method(self, method):
|
| 394 |
+
"""Prepares the given HTTP method."""
|
| 395 |
+
self.method = method
|
| 396 |
+
if self.method is not None:
|
| 397 |
+
self.method = to_native_string(self.method.upper())
|
| 398 |
+
|
| 399 |
+
@staticmethod
|
| 400 |
+
def _get_idna_encoded_host(host):
|
| 401 |
+
from pip._vendor import idna
|
| 402 |
+
|
| 403 |
+
try:
|
| 404 |
+
host = idna.encode(host, uts46=True).decode("utf-8")
|
| 405 |
+
except idna.IDNAError:
|
| 406 |
+
raise UnicodeError
|
| 407 |
+
return host
|
| 408 |
+
|
| 409 |
+
def prepare_url(self, url, params):
|
| 410 |
+
"""Prepares the given HTTP URL."""
|
| 411 |
+
#: Accept objects that have string representations.
|
| 412 |
+
#: We're unable to blindly call unicode/str functions
|
| 413 |
+
#: as this will include the bytestring indicator (b'')
|
| 414 |
+
#: on python 3.x.
|
| 415 |
+
#: https://github.com/psf/requests/pull/2238
|
| 416 |
+
if isinstance(url, bytes):
|
| 417 |
+
url = url.decode("utf8")
|
| 418 |
+
else:
|
| 419 |
+
url = str(url)
|
| 420 |
+
|
| 421 |
+
# Remove leading whitespaces from url
|
| 422 |
+
url = url.lstrip()
|
| 423 |
+
|
| 424 |
+
# Don't do any URL preparation for non-HTTP schemes like `mailto`,
|
| 425 |
+
# `data` etc to work around exceptions from `url_parse`, which
|
| 426 |
+
# handles RFC 3986 only.
|
| 427 |
+
if ":" in url and not url.lower().startswith("http"):
|
| 428 |
+
self.url = url
|
| 429 |
+
return
|
| 430 |
+
|
| 431 |
+
# Support for unicode domain names and paths.
|
| 432 |
+
try:
|
| 433 |
+
scheme, auth, host, port, path, query, fragment = parse_url(url)
|
| 434 |
+
except LocationParseError as e:
|
| 435 |
+
raise InvalidURL(*e.args)
|
| 436 |
+
|
| 437 |
+
if not scheme:
|
| 438 |
+
raise MissingSchema(
|
| 439 |
+
f"Invalid URL {url!r}: No scheme supplied. "
|
| 440 |
+
f"Perhaps you meant https://{url}?"
|
| 441 |
+
)
|
| 442 |
+
|
| 443 |
+
if not host:
|
| 444 |
+
raise InvalidURL(f"Invalid URL {url!r}: No host supplied")
|
| 445 |
+
|
| 446 |
+
# In general, we want to try IDNA encoding the hostname if the string contains
|
| 447 |
+
# non-ASCII characters. This allows users to automatically get the correct IDNA
|
| 448 |
+
# behaviour. For strings containing only ASCII characters, we need to also verify
|
| 449 |
+
# it doesn't start with a wildcard (*), before allowing the unencoded hostname.
|
| 450 |
+
if not unicode_is_ascii(host):
|
| 451 |
+
try:
|
| 452 |
+
host = self._get_idna_encoded_host(host)
|
| 453 |
+
except UnicodeError:
|
| 454 |
+
raise InvalidURL("URL has an invalid label.")
|
| 455 |
+
elif host.startswith(("*", ".")):
|
| 456 |
+
raise InvalidURL("URL has an invalid label.")
|
| 457 |
+
|
| 458 |
+
# Carefully reconstruct the network location
|
| 459 |
+
netloc = auth or ""
|
| 460 |
+
if netloc:
|
| 461 |
+
netloc += "@"
|
| 462 |
+
netloc += host
|
| 463 |
+
if port:
|
| 464 |
+
netloc += f":{port}"
|
| 465 |
+
|
| 466 |
+
# Bare domains aren't valid URLs.
|
| 467 |
+
if not path:
|
| 468 |
+
path = "/"
|
| 469 |
+
|
| 470 |
+
if isinstance(params, (str, bytes)):
|
| 471 |
+
params = to_native_string(params)
|
| 472 |
+
|
| 473 |
+
enc_params = self._encode_params(params)
|
| 474 |
+
if enc_params:
|
| 475 |
+
if query:
|
| 476 |
+
query = f"{query}&{enc_params}"
|
| 477 |
+
else:
|
| 478 |
+
query = enc_params
|
| 479 |
+
|
| 480 |
+
url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))
|
| 481 |
+
self.url = url
|
| 482 |
+
|
| 483 |
+
def prepare_headers(self, headers):
|
| 484 |
+
"""Prepares the given HTTP headers."""
|
| 485 |
+
|
| 486 |
+
self.headers = CaseInsensitiveDict()
|
| 487 |
+
if headers:
|
| 488 |
+
for header in headers.items():
|
| 489 |
+
# Raise exception on invalid header value.
|
| 490 |
+
check_header_validity(header)
|
| 491 |
+
name, value = header
|
| 492 |
+
self.headers[to_native_string(name)] = value
|
| 493 |
+
|
| 494 |
+
def prepare_body(self, data, files, json=None):
|
| 495 |
+
"""Prepares the given HTTP body data."""
|
| 496 |
+
|
| 497 |
+
# Check if file, fo, generator, iterator.
|
| 498 |
+
# If not, run through normal process.
|
| 499 |
+
|
| 500 |
+
# Nottin' on you.
|
| 501 |
+
body = None
|
| 502 |
+
content_type = None
|
| 503 |
+
|
| 504 |
+
if not data and json is not None:
|
| 505 |
+
# urllib3 requires a bytes-like body. Python 2's json.dumps
|
| 506 |
+
# provides this natively, but Python 3 gives a Unicode string.
|
| 507 |
+
content_type = "application/json"
|
| 508 |
+
|
| 509 |
+
try:
|
| 510 |
+
body = complexjson.dumps(json, allow_nan=False)
|
| 511 |
+
except ValueError as ve:
|
| 512 |
+
raise InvalidJSONError(ve, request=self)
|
| 513 |
+
|
| 514 |
+
if not isinstance(body, bytes):
|
| 515 |
+
body = body.encode("utf-8")
|
| 516 |
+
|
| 517 |
+
is_stream = all(
|
| 518 |
+
[
|
| 519 |
+
hasattr(data, "__iter__"),
|
| 520 |
+
not isinstance(data, (basestring, list, tuple, Mapping)),
|
| 521 |
+
]
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
if is_stream:
|
| 525 |
+
try:
|
| 526 |
+
length = super_len(data)
|
| 527 |
+
except (TypeError, AttributeError, UnsupportedOperation):
|
| 528 |
+
length = None
|
| 529 |
+
|
| 530 |
+
body = data
|
| 531 |
+
|
| 532 |
+
if getattr(body, "tell", None) is not None:
|
| 533 |
+
# Record the current file position before reading.
|
| 534 |
+
# This will allow us to rewind a file in the event
|
| 535 |
+
# of a redirect.
|
| 536 |
+
try:
|
| 537 |
+
self._body_position = body.tell()
|
| 538 |
+
except OSError:
|
| 539 |
+
# This differentiates from None, allowing us to catch
|
| 540 |
+
# a failed `tell()` later when trying to rewind the body
|
| 541 |
+
self._body_position = object()
|
| 542 |
+
|
| 543 |
+
if files:
|
| 544 |
+
raise NotImplementedError(
|
| 545 |
+
"Streamed bodies and files are mutually exclusive."
|
| 546 |
+
)
|
| 547 |
+
|
| 548 |
+
if length:
|
| 549 |
+
self.headers["Content-Length"] = builtin_str(length)
|
| 550 |
+
else:
|
| 551 |
+
self.headers["Transfer-Encoding"] = "chunked"
|
| 552 |
+
else:
|
| 553 |
+
# Multi-part file uploads.
|
| 554 |
+
if files:
|
| 555 |
+
(body, content_type) = self._encode_files(files, data)
|
| 556 |
+
else:
|
| 557 |
+
if data:
|
| 558 |
+
body = self._encode_params(data)
|
| 559 |
+
if isinstance(data, basestring) or hasattr(data, "read"):
|
| 560 |
+
content_type = None
|
| 561 |
+
else:
|
| 562 |
+
content_type = "application/x-www-form-urlencoded"
|
| 563 |
+
|
| 564 |
+
self.prepare_content_length(body)
|
| 565 |
+
|
| 566 |
+
# Add content-type if it wasn't explicitly provided.
|
| 567 |
+
if content_type and ("content-type" not in self.headers):
|
| 568 |
+
self.headers["Content-Type"] = content_type
|
| 569 |
+
|
| 570 |
+
self.body = body
|
| 571 |
+
|
| 572 |
+
def prepare_content_length(self, body):
|
| 573 |
+
"""Prepare Content-Length header based on request method and body"""
|
| 574 |
+
if body is not None:
|
| 575 |
+
length = super_len(body)
|
| 576 |
+
if length:
|
| 577 |
+
# If length exists, set it. Otherwise, we fallback
|
| 578 |
+
# to Transfer-Encoding: chunked.
|
| 579 |
+
self.headers["Content-Length"] = builtin_str(length)
|
| 580 |
+
elif (
|
| 581 |
+
self.method not in ("GET", "HEAD")
|
| 582 |
+
and self.headers.get("Content-Length") is None
|
| 583 |
+
):
|
| 584 |
+
# Set Content-Length to 0 for methods that can have a body
|
| 585 |
+
# but don't provide one. (i.e. not GET or HEAD)
|
| 586 |
+
self.headers["Content-Length"] = "0"
|
| 587 |
+
|
| 588 |
+
def prepare_auth(self, auth, url=""):
|
| 589 |
+
"""Prepares the given HTTP auth data."""
|
| 590 |
+
|
| 591 |
+
# If no Auth is explicitly provided, extract it from the URL first.
|
| 592 |
+
if auth is None:
|
| 593 |
+
url_auth = get_auth_from_url(self.url)
|
| 594 |
+
auth = url_auth if any(url_auth) else None
|
| 595 |
+
|
| 596 |
+
if auth:
|
| 597 |
+
if isinstance(auth, tuple) and len(auth) == 2:
|
| 598 |
+
# special-case basic HTTP auth
|
| 599 |
+
auth = HTTPBasicAuth(*auth)
|
| 600 |
+
|
| 601 |
+
# Allow auth to make its changes.
|
| 602 |
+
r = auth(self)
|
| 603 |
+
|
| 604 |
+
# Update self to reflect the auth changes.
|
| 605 |
+
self.__dict__.update(r.__dict__)
|
| 606 |
+
|
| 607 |
+
# Recompute Content-Length
|
| 608 |
+
self.prepare_content_length(self.body)
|
| 609 |
+
|
| 610 |
+
def prepare_cookies(self, cookies):
|
| 611 |
+
"""Prepares the given HTTP cookie data.
|
| 612 |
+
|
| 613 |
+
This function eventually generates a ``Cookie`` header from the
|
| 614 |
+
given cookies using cookielib. Due to cookielib's design, the header
|
| 615 |
+
will not be regenerated if it already exists, meaning this function
|
| 616 |
+
can only be called once for the life of the
|
| 617 |
+
:class:`PreparedRequest <PreparedRequest>` object. Any subsequent calls
|
| 618 |
+
to ``prepare_cookies`` will have no actual effect, unless the "Cookie"
|
| 619 |
+
header is removed beforehand.
|
| 620 |
+
"""
|
| 621 |
+
if isinstance(cookies, cookielib.CookieJar):
|
| 622 |
+
self._cookies = cookies
|
| 623 |
+
else:
|
| 624 |
+
self._cookies = cookiejar_from_dict(cookies)
|
| 625 |
+
|
| 626 |
+
cookie_header = get_cookie_header(self._cookies, self)
|
| 627 |
+
if cookie_header is not None:
|
| 628 |
+
self.headers["Cookie"] = cookie_header
|
| 629 |
+
|
| 630 |
+
def prepare_hooks(self, hooks):
|
| 631 |
+
"""Prepares the given hooks."""
|
| 632 |
+
# hooks can be passed as None to the prepare method and to this
|
| 633 |
+
# method. To prevent iterating over None, simply use an empty list
|
| 634 |
+
# if hooks is False-y
|
| 635 |
+
hooks = hooks or []
|
| 636 |
+
for event in hooks:
|
| 637 |
+
self.register_hook(event, hooks[event])
|
| 638 |
+
|
| 639 |
+
|
| 640 |
+
class Response:
|
| 641 |
+
"""The :class:`Response <Response>` object, which contains a
|
| 642 |
+
server's response to an HTTP request.
|
| 643 |
+
"""
|
| 644 |
+
|
| 645 |
+
__attrs__ = [
|
| 646 |
+
"_content",
|
| 647 |
+
"status_code",
|
| 648 |
+
"headers",
|
| 649 |
+
"url",
|
| 650 |
+
"history",
|
| 651 |
+
"encoding",
|
| 652 |
+
"reason",
|
| 653 |
+
"cookies",
|
| 654 |
+
"elapsed",
|
| 655 |
+
"request",
|
| 656 |
+
]
|
| 657 |
+
|
| 658 |
+
def __init__(self):
|
| 659 |
+
self._content = False
|
| 660 |
+
self._content_consumed = False
|
| 661 |
+
self._next = None
|
| 662 |
+
|
| 663 |
+
#: Integer Code of responded HTTP Status, e.g. 404 or 200.
|
| 664 |
+
self.status_code = None
|
| 665 |
+
|
| 666 |
+
#: Case-insensitive Dictionary of Response Headers.
|
| 667 |
+
#: For example, ``headers['content-encoding']`` will return the
|
| 668 |
+
#: value of a ``'Content-Encoding'`` response header.
|
| 669 |
+
self.headers = CaseInsensitiveDict()
|
| 670 |
+
|
| 671 |
+
#: File-like object representation of response (for advanced usage).
|
| 672 |
+
#: Use of ``raw`` requires that ``stream=True`` be set on the request.
|
| 673 |
+
#: This requirement does not apply for use internally to Requests.
|
| 674 |
+
self.raw = None
|
| 675 |
+
|
| 676 |
+
#: Final URL location of Response.
|
| 677 |
+
self.url = None
|
| 678 |
+
|
| 679 |
+
#: Encoding to decode with when accessing r.text.
|
| 680 |
+
self.encoding = None
|
| 681 |
+
|
| 682 |
+
#: A list of :class:`Response <Response>` objects from
|
| 683 |
+
#: the history of the Request. Any redirect responses will end
|
| 684 |
+
#: up here. The list is sorted from the oldest to the most recent request.
|
| 685 |
+
self.history = []
|
| 686 |
+
|
| 687 |
+
#: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK".
|
| 688 |
+
self.reason = None
|
| 689 |
+
|
| 690 |
+
#: A CookieJar of Cookies the server sent back.
|
| 691 |
+
self.cookies = cookiejar_from_dict({})
|
| 692 |
+
|
| 693 |
+
#: The amount of time elapsed between sending the request
|
| 694 |
+
#: and the arrival of the response (as a timedelta).
|
| 695 |
+
#: This property specifically measures the time taken between sending
|
| 696 |
+
#: the first byte of the request and finishing parsing the headers. It
|
| 697 |
+
#: is therefore unaffected by consuming the response content or the
|
| 698 |
+
#: value of the ``stream`` keyword argument.
|
| 699 |
+
self.elapsed = datetime.timedelta(0)
|
| 700 |
+
|
| 701 |
+
#: The :class:`PreparedRequest <PreparedRequest>` object to which this
|
| 702 |
+
#: is a response.
|
| 703 |
+
self.request = None
|
| 704 |
+
|
| 705 |
+
def __enter__(self):
|
| 706 |
+
return self
|
| 707 |
+
|
| 708 |
+
def __exit__(self, *args):
|
| 709 |
+
self.close()
|
| 710 |
+
|
| 711 |
+
def __getstate__(self):
|
| 712 |
+
# Consume everything; accessing the content attribute makes
|
| 713 |
+
# sure the content has been fully read.
|
| 714 |
+
if not self._content_consumed:
|
| 715 |
+
self.content
|
| 716 |
+
|
| 717 |
+
return {attr: getattr(self, attr, None) for attr in self.__attrs__}
|
| 718 |
+
|
| 719 |
+
def __setstate__(self, state):
|
| 720 |
+
for name, value in state.items():
|
| 721 |
+
setattr(self, name, value)
|
| 722 |
+
|
| 723 |
+
# pickled objects do not have .raw
|
| 724 |
+
setattr(self, "_content_consumed", True)
|
| 725 |
+
setattr(self, "raw", None)
|
| 726 |
+
|
| 727 |
+
def __repr__(self):
|
| 728 |
+
return f"<Response [{self.status_code}]>"
|
| 729 |
+
|
| 730 |
+
def __bool__(self):
|
| 731 |
+
"""Returns True if :attr:`status_code` is less than 400.
|
| 732 |
+
|
| 733 |
+
This attribute checks if the status code of the response is between
|
| 734 |
+
400 and 600 to see if there was a client error or a server error. If
|
| 735 |
+
the status code, is between 200 and 400, this will return True. This
|
| 736 |
+
is **not** a check to see if the response code is ``200 OK``.
|
| 737 |
+
"""
|
| 738 |
+
return self.ok
|
| 739 |
+
|
| 740 |
+
def __nonzero__(self):
|
| 741 |
+
"""Returns True if :attr:`status_code` is less than 400.
|
| 742 |
+
|
| 743 |
+
This attribute checks if the status code of the response is between
|
| 744 |
+
400 and 600 to see if there was a client error or a server error. If
|
| 745 |
+
the status code, is between 200 and 400, this will return True. This
|
| 746 |
+
is **not** a check to see if the response code is ``200 OK``.
|
| 747 |
+
"""
|
| 748 |
+
return self.ok
|
| 749 |
+
|
| 750 |
+
def __iter__(self):
|
| 751 |
+
"""Allows you to use a response as an iterator."""
|
| 752 |
+
return self.iter_content(128)
|
| 753 |
+
|
| 754 |
+
@property
|
| 755 |
+
def ok(self):
|
| 756 |
+
"""Returns True if :attr:`status_code` is less than 400, False if not.
|
| 757 |
+
|
| 758 |
+
This attribute checks if the status code of the response is between
|
| 759 |
+
400 and 600 to see if there was a client error or a server error. If
|
| 760 |
+
the status code is between 200 and 400, this will return True. This
|
| 761 |
+
is **not** a check to see if the response code is ``200 OK``.
|
| 762 |
+
"""
|
| 763 |
+
try:
|
| 764 |
+
self.raise_for_status()
|
| 765 |
+
except HTTPError:
|
| 766 |
+
return False
|
| 767 |
+
return True
|
| 768 |
+
|
| 769 |
+
@property
|
| 770 |
+
def is_redirect(self):
|
| 771 |
+
"""True if this Response is a well-formed HTTP redirect that could have
|
| 772 |
+
been processed automatically (by :meth:`Session.resolve_redirects`).
|
| 773 |
+
"""
|
| 774 |
+
return "location" in self.headers and self.status_code in REDIRECT_STATI
|
| 775 |
+
|
| 776 |
+
@property
|
| 777 |
+
def is_permanent_redirect(self):
|
| 778 |
+
"""True if this Response one of the permanent versions of redirect."""
|
| 779 |
+
return "location" in self.headers and self.status_code in (
|
| 780 |
+
codes.moved_permanently,
|
| 781 |
+
codes.permanent_redirect,
|
| 782 |
+
)
|
| 783 |
+
|
| 784 |
+
@property
|
| 785 |
+
def next(self):
|
| 786 |
+
"""Returns a PreparedRequest for the next request in a redirect chain, if there is one."""
|
| 787 |
+
return self._next
|
| 788 |
+
|
| 789 |
+
@property
|
| 790 |
+
def apparent_encoding(self):
|
| 791 |
+
"""The apparent encoding, provided by the charset_normalizer or chardet libraries."""
|
| 792 |
+
if chardet is not None:
|
| 793 |
+
return chardet.detect(self.content)["encoding"]
|
| 794 |
+
else:
|
| 795 |
+
# If no character detection library is available, we'll fall back
|
| 796 |
+
# to a standard Python utf-8 str.
|
| 797 |
+
return "utf-8"
|
| 798 |
+
|
| 799 |
+
def iter_content(self, chunk_size=1, decode_unicode=False):
|
| 800 |
+
"""Iterates over the response data. When stream=True is set on the
|
| 801 |
+
request, this avoids reading the content at once into memory for
|
| 802 |
+
large responses. The chunk size is the number of bytes it should
|
| 803 |
+
read into memory. This is not necessarily the length of each item
|
| 804 |
+
returned as decoding can take place.
|
| 805 |
+
|
| 806 |
+
chunk_size must be of type int or None. A value of None will
|
| 807 |
+
function differently depending on the value of `stream`.
|
| 808 |
+
stream=True will read data as it arrives in whatever size the
|
| 809 |
+
chunks are received. If stream=False, data is returned as
|
| 810 |
+
a single chunk.
|
| 811 |
+
|
| 812 |
+
If decode_unicode is True, content will be decoded using the best
|
| 813 |
+
available encoding based on the response.
|
| 814 |
+
"""
|
| 815 |
+
|
| 816 |
+
def generate():
|
| 817 |
+
# Special case for urllib3.
|
| 818 |
+
if hasattr(self.raw, "stream"):
|
| 819 |
+
try:
|
| 820 |
+
yield from self.raw.stream(chunk_size, decode_content=True)
|
| 821 |
+
except ProtocolError as e:
|
| 822 |
+
raise ChunkedEncodingError(e)
|
| 823 |
+
except DecodeError as e:
|
| 824 |
+
raise ContentDecodingError(e)
|
| 825 |
+
except ReadTimeoutError as e:
|
| 826 |
+
raise ConnectionError(e)
|
| 827 |
+
except SSLError as e:
|
| 828 |
+
raise RequestsSSLError(e)
|
| 829 |
+
else:
|
| 830 |
+
# Standard file-like object.
|
| 831 |
+
while True:
|
| 832 |
+
chunk = self.raw.read(chunk_size)
|
| 833 |
+
if not chunk:
|
| 834 |
+
break
|
| 835 |
+
yield chunk
|
| 836 |
+
|
| 837 |
+
self._content_consumed = True
|
| 838 |
+
|
| 839 |
+
if self._content_consumed and isinstance(self._content, bool):
|
| 840 |
+
raise StreamConsumedError()
|
| 841 |
+
elif chunk_size is not None and not isinstance(chunk_size, int):
|
| 842 |
+
raise TypeError(
|
| 843 |
+
f"chunk_size must be an int, it is instead a {type(chunk_size)}."
|
| 844 |
+
)
|
| 845 |
+
# simulate reading small chunks of the content
|
| 846 |
+
reused_chunks = iter_slices(self._content, chunk_size)
|
| 847 |
+
|
| 848 |
+
stream_chunks = generate()
|
| 849 |
+
|
| 850 |
+
chunks = reused_chunks if self._content_consumed else stream_chunks
|
| 851 |
+
|
| 852 |
+
if decode_unicode:
|
| 853 |
+
chunks = stream_decode_response_unicode(chunks, self)
|
| 854 |
+
|
| 855 |
+
return chunks
|
| 856 |
+
|
| 857 |
+
def iter_lines(
|
| 858 |
+
self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=False, delimiter=None
|
| 859 |
+
):
|
| 860 |
+
"""Iterates over the response data, one line at a time. When
|
| 861 |
+
stream=True is set on the request, this avoids reading the
|
| 862 |
+
content at once into memory for large responses.
|
| 863 |
+
|
| 864 |
+
.. note:: This method is not reentrant safe.
|
| 865 |
+
"""
|
| 866 |
+
|
| 867 |
+
pending = None
|
| 868 |
+
|
| 869 |
+
for chunk in self.iter_content(
|
| 870 |
+
chunk_size=chunk_size, decode_unicode=decode_unicode
|
| 871 |
+
):
|
| 872 |
+
if pending is not None:
|
| 873 |
+
chunk = pending + chunk
|
| 874 |
+
|
| 875 |
+
if delimiter:
|
| 876 |
+
lines = chunk.split(delimiter)
|
| 877 |
+
else:
|
| 878 |
+
lines = chunk.splitlines()
|
| 879 |
+
|
| 880 |
+
if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
|
| 881 |
+
pending = lines.pop()
|
| 882 |
+
else:
|
| 883 |
+
pending = None
|
| 884 |
+
|
| 885 |
+
yield from lines
|
| 886 |
+
|
| 887 |
+
if pending is not None:
|
| 888 |
+
yield pending
|
| 889 |
+
|
| 890 |
+
@property
|
| 891 |
+
def content(self):
|
| 892 |
+
"""Content of the response, in bytes."""
|
| 893 |
+
|
| 894 |
+
if self._content is False:
|
| 895 |
+
# Read the contents.
|
| 896 |
+
if self._content_consumed:
|
| 897 |
+
raise RuntimeError("The content for this response was already consumed")
|
| 898 |
+
|
| 899 |
+
if self.status_code == 0 or self.raw is None:
|
| 900 |
+
self._content = None
|
| 901 |
+
else:
|
| 902 |
+
self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b""
|
| 903 |
+
|
| 904 |
+
self._content_consumed = True
|
| 905 |
+
# don't need to release the connection; that's been handled by urllib3
|
| 906 |
+
# since we exhausted the data.
|
| 907 |
+
return self._content
|
| 908 |
+
|
| 909 |
+
@property
|
| 910 |
+
def text(self):
|
| 911 |
+
"""Content of the response, in unicode.
|
| 912 |
+
|
| 913 |
+
If Response.encoding is None, encoding will be guessed using
|
| 914 |
+
``charset_normalizer`` or ``chardet``.
|
| 915 |
+
|
| 916 |
+
The encoding of the response content is determined based solely on HTTP
|
| 917 |
+
headers, following RFC 2616 to the letter. If you can take advantage of
|
| 918 |
+
non-HTTP knowledge to make a better guess at the encoding, you should
|
| 919 |
+
set ``r.encoding`` appropriately before accessing this property.
|
| 920 |
+
"""
|
| 921 |
+
|
| 922 |
+
# Try charset from content-type
|
| 923 |
+
content = None
|
| 924 |
+
encoding = self.encoding
|
| 925 |
+
|
| 926 |
+
if not self.content:
|
| 927 |
+
return ""
|
| 928 |
+
|
| 929 |
+
# Fallback to auto-detected encoding.
|
| 930 |
+
if self.encoding is None:
|
| 931 |
+
encoding = self.apparent_encoding
|
| 932 |
+
|
| 933 |
+
# Decode unicode from given encoding.
|
| 934 |
+
try:
|
| 935 |
+
content = str(self.content, encoding, errors="replace")
|
| 936 |
+
except (LookupError, TypeError):
|
| 937 |
+
# A LookupError is raised if the encoding was not found which could
|
| 938 |
+
# indicate a misspelling or similar mistake.
|
| 939 |
+
#
|
| 940 |
+
# A TypeError can be raised if encoding is None
|
| 941 |
+
#
|
| 942 |
+
# So we try blindly encoding.
|
| 943 |
+
content = str(self.content, errors="replace")
|
| 944 |
+
|
| 945 |
+
return content
|
| 946 |
+
|
| 947 |
+
def json(self, **kwargs):
|
| 948 |
+
r"""Decodes the JSON response body (if any) as a Python object.
|
| 949 |
+
|
| 950 |
+
This may return a dictionary, list, etc. depending on what is in the response.
|
| 951 |
+
|
| 952 |
+
:param \*\*kwargs: Optional arguments that ``json.loads`` takes.
|
| 953 |
+
:raises requests.exceptions.JSONDecodeError: If the response body does not
|
| 954 |
+
contain valid json.
|
| 955 |
+
"""
|
| 956 |
+
|
| 957 |
+
if not self.encoding and self.content and len(self.content) > 3:
|
| 958 |
+
# No encoding set. JSON RFC 4627 section 3 states we should expect
|
| 959 |
+
# UTF-8, -16 or -32. Detect which one to use; If the detection or
|
| 960 |
+
# decoding fails, fall back to `self.text` (using charset_normalizer to make
|
| 961 |
+
# a best guess).
|
| 962 |
+
encoding = guess_json_utf(self.content)
|
| 963 |
+
if encoding is not None:
|
| 964 |
+
try:
|
| 965 |
+
return complexjson.loads(self.content.decode(encoding), **kwargs)
|
| 966 |
+
except UnicodeDecodeError:
|
| 967 |
+
# Wrong UTF codec detected; usually because it's not UTF-8
|
| 968 |
+
# but some other 8-bit codec. This is an RFC violation,
|
| 969 |
+
# and the server didn't bother to tell us what codec *was*
|
| 970 |
+
# used.
|
| 971 |
+
pass
|
| 972 |
+
except JSONDecodeError as e:
|
| 973 |
+
raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
|
| 974 |
+
|
| 975 |
+
try:
|
| 976 |
+
return complexjson.loads(self.text, **kwargs)
|
| 977 |
+
except JSONDecodeError as e:
|
| 978 |
+
# Catch JSON-related errors and raise as requests.JSONDecodeError
|
| 979 |
+
# This aliases json.JSONDecodeError and simplejson.JSONDecodeError
|
| 980 |
+
raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
|
| 981 |
+
|
| 982 |
+
@property
|
| 983 |
+
def links(self):
|
| 984 |
+
"""Returns the parsed header links of the response, if any."""
|
| 985 |
+
|
| 986 |
+
header = self.headers.get("link")
|
| 987 |
+
|
| 988 |
+
resolved_links = {}
|
| 989 |
+
|
| 990 |
+
if header:
|
| 991 |
+
links = parse_header_links(header)
|
| 992 |
+
|
| 993 |
+
for link in links:
|
| 994 |
+
key = link.get("rel") or link.get("url")
|
| 995 |
+
resolved_links[key] = link
|
| 996 |
+
|
| 997 |
+
return resolved_links
|
| 998 |
+
|
| 999 |
+
def raise_for_status(self):
|
| 1000 |
+
"""Raises :class:`HTTPError`, if one occurred."""
|
| 1001 |
+
|
| 1002 |
+
http_error_msg = ""
|
| 1003 |
+
if isinstance(self.reason, bytes):
|
| 1004 |
+
# We attempt to decode utf-8 first because some servers
|
| 1005 |
+
# choose to localize their reason strings. If the string
|
| 1006 |
+
# isn't utf-8, we fall back to iso-8859-1 for all other
|
| 1007 |
+
# encodings. (See PR #3538)
|
| 1008 |
+
try:
|
| 1009 |
+
reason = self.reason.decode("utf-8")
|
| 1010 |
+
except UnicodeDecodeError:
|
| 1011 |
+
reason = self.reason.decode("iso-8859-1")
|
| 1012 |
+
else:
|
| 1013 |
+
reason = self.reason
|
| 1014 |
+
|
| 1015 |
+
if 400 <= self.status_code < 500:
|
| 1016 |
+
http_error_msg = (
|
| 1017 |
+
f"{self.status_code} Client Error: {reason} for url: {self.url}"
|
| 1018 |
+
)
|
| 1019 |
+
|
| 1020 |
+
elif 500 <= self.status_code < 600:
|
| 1021 |
+
http_error_msg = (
|
| 1022 |
+
f"{self.status_code} Server Error: {reason} for url: {self.url}"
|
| 1023 |
+
)
|
| 1024 |
+
|
| 1025 |
+
if http_error_msg:
|
| 1026 |
+
raise HTTPError(http_error_msg, response=self)
|
| 1027 |
+
|
| 1028 |
+
def close(self):
|
| 1029 |
+
"""Releases the connection back to the pool. Once this method has been
|
| 1030 |
+
called the underlying ``raw`` object must not be accessed again.
|
| 1031 |
+
|
| 1032 |
+
*Note: Should not normally need to be called explicitly.*
|
| 1033 |
+
"""
|
| 1034 |
+
if not self._content_consumed:
|
| 1035 |
+
self.raw.close()
|
| 1036 |
+
|
| 1037 |
+
release_conn = getattr(self.raw, "release_conn", None)
|
| 1038 |
+
if release_conn is not None:
|
| 1039 |
+
release_conn()
|
.venv/Lib/site-packages/pip/_vendor/requests/packages.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
|
| 3 |
+
from .compat import chardet
|
| 4 |
+
|
| 5 |
+
# This code exists for backwards compatibility reasons.
|
| 6 |
+
# I don't like it either. Just look the other way. :)
|
| 7 |
+
|
| 8 |
+
for package in ("urllib3", "idna"):
|
| 9 |
+
vendored_package = "pip._vendor." + package
|
| 10 |
+
locals()[package] = __import__(vendored_package)
|
| 11 |
+
# This traversal is apparently necessary such that the identities are
|
| 12 |
+
# preserved (requests.packages.urllib3.* is urllib3.*)
|
| 13 |
+
for mod in list(sys.modules):
|
| 14 |
+
if mod == vendored_package or mod.startswith(vendored_package + '.'):
|
| 15 |
+
unprefixed_mod = mod[len("pip._vendor."):]
|
| 16 |
+
sys.modules['pip._vendor.requests.packages.' + unprefixed_mod] = sys.modules[mod]
|
| 17 |
+
|
| 18 |
+
if chardet is not None:
|
| 19 |
+
target = chardet.__name__
|
| 20 |
+
for mod in list(sys.modules):
|
| 21 |
+
if mod == target or mod.startswith(f"{target}."):
|
| 22 |
+
imported_mod = sys.modules[mod]
|
| 23 |
+
sys.modules[f"requests.packages.{mod}"] = imported_mod
|
| 24 |
+
mod = mod.replace(target, "chardet")
|
| 25 |
+
sys.modules[f"requests.packages.{mod}"] = imported_mod
|
.venv/Lib/site-packages/pip/_vendor/requests/sessions.py
ADDED
|
@@ -0,0 +1,831 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.sessions
|
| 3 |
+
~~~~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module provides a Session object to manage and persist settings across
|
| 6 |
+
requests (cookies, auth, proxies).
|
| 7 |
+
"""
|
| 8 |
+
import os
|
| 9 |
+
import sys
|
| 10 |
+
import time
|
| 11 |
+
from collections import OrderedDict
|
| 12 |
+
from datetime import timedelta
|
| 13 |
+
|
| 14 |
+
from ._internal_utils import to_native_string
|
| 15 |
+
from .adapters import HTTPAdapter
|
| 16 |
+
from .auth import _basic_auth_str
|
| 17 |
+
from .compat import Mapping, cookielib, urljoin, urlparse
|
| 18 |
+
from .cookies import (
|
| 19 |
+
RequestsCookieJar,
|
| 20 |
+
cookiejar_from_dict,
|
| 21 |
+
extract_cookies_to_jar,
|
| 22 |
+
merge_cookies,
|
| 23 |
+
)
|
| 24 |
+
from .exceptions import (
|
| 25 |
+
ChunkedEncodingError,
|
| 26 |
+
ContentDecodingError,
|
| 27 |
+
InvalidSchema,
|
| 28 |
+
TooManyRedirects,
|
| 29 |
+
)
|
| 30 |
+
from .hooks import default_hooks, dispatch_hook
|
| 31 |
+
|
| 32 |
+
# formerly defined here, reexposed here for backward compatibility
|
| 33 |
+
from .models import ( # noqa: F401
|
| 34 |
+
DEFAULT_REDIRECT_LIMIT,
|
| 35 |
+
REDIRECT_STATI,
|
| 36 |
+
PreparedRequest,
|
| 37 |
+
Request,
|
| 38 |
+
)
|
| 39 |
+
from .status_codes import codes
|
| 40 |
+
from .structures import CaseInsensitiveDict
|
| 41 |
+
from .utils import ( # noqa: F401
|
| 42 |
+
DEFAULT_PORTS,
|
| 43 |
+
default_headers,
|
| 44 |
+
get_auth_from_url,
|
| 45 |
+
get_environ_proxies,
|
| 46 |
+
get_netrc_auth,
|
| 47 |
+
requote_uri,
|
| 48 |
+
resolve_proxies,
|
| 49 |
+
rewind_body,
|
| 50 |
+
should_bypass_proxies,
|
| 51 |
+
to_key_val_list,
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
# Preferred clock, based on which one is more accurate on a given system.
|
| 55 |
+
if sys.platform == "win32":
|
| 56 |
+
preferred_clock = time.perf_counter
|
| 57 |
+
else:
|
| 58 |
+
preferred_clock = time.time
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
|
| 62 |
+
"""Determines appropriate setting for a given request, taking into account
|
| 63 |
+
the explicit setting on that request, and the setting in the session. If a
|
| 64 |
+
setting is a dictionary, they will be merged together using `dict_class`
|
| 65 |
+
"""
|
| 66 |
+
|
| 67 |
+
if session_setting is None:
|
| 68 |
+
return request_setting
|
| 69 |
+
|
| 70 |
+
if request_setting is None:
|
| 71 |
+
return session_setting
|
| 72 |
+
|
| 73 |
+
# Bypass if not a dictionary (e.g. verify)
|
| 74 |
+
if not (
|
| 75 |
+
isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping)
|
| 76 |
+
):
|
| 77 |
+
return request_setting
|
| 78 |
+
|
| 79 |
+
merged_setting = dict_class(to_key_val_list(session_setting))
|
| 80 |
+
merged_setting.update(to_key_val_list(request_setting))
|
| 81 |
+
|
| 82 |
+
# Remove keys that are set to None. Extract keys first to avoid altering
|
| 83 |
+
# the dictionary during iteration.
|
| 84 |
+
none_keys = [k for (k, v) in merged_setting.items() if v is None]
|
| 85 |
+
for key in none_keys:
|
| 86 |
+
del merged_setting[key]
|
| 87 |
+
|
| 88 |
+
return merged_setting
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
|
| 92 |
+
"""Properly merges both requests and session hooks.
|
| 93 |
+
|
| 94 |
+
This is necessary because when request_hooks == {'response': []}, the
|
| 95 |
+
merge breaks Session hooks entirely.
|
| 96 |
+
"""
|
| 97 |
+
if session_hooks is None or session_hooks.get("response") == []:
|
| 98 |
+
return request_hooks
|
| 99 |
+
|
| 100 |
+
if request_hooks is None or request_hooks.get("response") == []:
|
| 101 |
+
return session_hooks
|
| 102 |
+
|
| 103 |
+
return merge_setting(request_hooks, session_hooks, dict_class)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
class SessionRedirectMixin:
|
| 107 |
+
def get_redirect_target(self, resp):
|
| 108 |
+
"""Receives a Response. Returns a redirect URI or ``None``"""
|
| 109 |
+
# Due to the nature of how requests processes redirects this method will
|
| 110 |
+
# be called at least once upon the original response and at least twice
|
| 111 |
+
# on each subsequent redirect response (if any).
|
| 112 |
+
# If a custom mixin is used to handle this logic, it may be advantageous
|
| 113 |
+
# to cache the redirect location onto the response object as a private
|
| 114 |
+
# attribute.
|
| 115 |
+
if resp.is_redirect:
|
| 116 |
+
location = resp.headers["location"]
|
| 117 |
+
# Currently the underlying http module on py3 decode headers
|
| 118 |
+
# in latin1, but empirical evidence suggests that latin1 is very
|
| 119 |
+
# rarely used with non-ASCII characters in HTTP headers.
|
| 120 |
+
# It is more likely to get UTF8 header rather than latin1.
|
| 121 |
+
# This causes incorrect handling of UTF8 encoded location headers.
|
| 122 |
+
# To solve this, we re-encode the location in latin1.
|
| 123 |
+
location = location.encode("latin1")
|
| 124 |
+
return to_native_string(location, "utf8")
|
| 125 |
+
return None
|
| 126 |
+
|
| 127 |
+
def should_strip_auth(self, old_url, new_url):
|
| 128 |
+
"""Decide whether Authorization header should be removed when redirecting"""
|
| 129 |
+
old_parsed = urlparse(old_url)
|
| 130 |
+
new_parsed = urlparse(new_url)
|
| 131 |
+
if old_parsed.hostname != new_parsed.hostname:
|
| 132 |
+
return True
|
| 133 |
+
# Special case: allow http -> https redirect when using the standard
|
| 134 |
+
# ports. This isn't specified by RFC 7235, but is kept to avoid
|
| 135 |
+
# breaking backwards compatibility with older versions of requests
|
| 136 |
+
# that allowed any redirects on the same host.
|
| 137 |
+
if (
|
| 138 |
+
old_parsed.scheme == "http"
|
| 139 |
+
and old_parsed.port in (80, None)
|
| 140 |
+
and new_parsed.scheme == "https"
|
| 141 |
+
and new_parsed.port in (443, None)
|
| 142 |
+
):
|
| 143 |
+
return False
|
| 144 |
+
|
| 145 |
+
# Handle default port usage corresponding to scheme.
|
| 146 |
+
changed_port = old_parsed.port != new_parsed.port
|
| 147 |
+
changed_scheme = old_parsed.scheme != new_parsed.scheme
|
| 148 |
+
default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None)
|
| 149 |
+
if (
|
| 150 |
+
not changed_scheme
|
| 151 |
+
and old_parsed.port in default_port
|
| 152 |
+
and new_parsed.port in default_port
|
| 153 |
+
):
|
| 154 |
+
return False
|
| 155 |
+
|
| 156 |
+
# Standard case: root URI must match
|
| 157 |
+
return changed_port or changed_scheme
|
| 158 |
+
|
| 159 |
+
def resolve_redirects(
|
| 160 |
+
self,
|
| 161 |
+
resp,
|
| 162 |
+
req,
|
| 163 |
+
stream=False,
|
| 164 |
+
timeout=None,
|
| 165 |
+
verify=True,
|
| 166 |
+
cert=None,
|
| 167 |
+
proxies=None,
|
| 168 |
+
yield_requests=False,
|
| 169 |
+
**adapter_kwargs,
|
| 170 |
+
):
|
| 171 |
+
"""Receives a Response. Returns a generator of Responses or Requests."""
|
| 172 |
+
|
| 173 |
+
hist = [] # keep track of history
|
| 174 |
+
|
| 175 |
+
url = self.get_redirect_target(resp)
|
| 176 |
+
previous_fragment = urlparse(req.url).fragment
|
| 177 |
+
while url:
|
| 178 |
+
prepared_request = req.copy()
|
| 179 |
+
|
| 180 |
+
# Update history and keep track of redirects.
|
| 181 |
+
# resp.history must ignore the original request in this loop
|
| 182 |
+
hist.append(resp)
|
| 183 |
+
resp.history = hist[1:]
|
| 184 |
+
|
| 185 |
+
try:
|
| 186 |
+
resp.content # Consume socket so it can be released
|
| 187 |
+
except (ChunkedEncodingError, ContentDecodingError, RuntimeError):
|
| 188 |
+
resp.raw.read(decode_content=False)
|
| 189 |
+
|
| 190 |
+
if len(resp.history) >= self.max_redirects:
|
| 191 |
+
raise TooManyRedirects(
|
| 192 |
+
f"Exceeded {self.max_redirects} redirects.", response=resp
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
# Release the connection back into the pool.
|
| 196 |
+
resp.close()
|
| 197 |
+
|
| 198 |
+
# Handle redirection without scheme (see: RFC 1808 Section 4)
|
| 199 |
+
if url.startswith("//"):
|
| 200 |
+
parsed_rurl = urlparse(resp.url)
|
| 201 |
+
url = ":".join([to_native_string(parsed_rurl.scheme), url])
|
| 202 |
+
|
| 203 |
+
# Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)
|
| 204 |
+
parsed = urlparse(url)
|
| 205 |
+
if parsed.fragment == "" and previous_fragment:
|
| 206 |
+
parsed = parsed._replace(fragment=previous_fragment)
|
| 207 |
+
elif parsed.fragment:
|
| 208 |
+
previous_fragment = parsed.fragment
|
| 209 |
+
url = parsed.geturl()
|
| 210 |
+
|
| 211 |
+
# Facilitate relative 'location' headers, as allowed by RFC 7231.
|
| 212 |
+
# (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
|
| 213 |
+
# Compliant with RFC3986, we percent encode the url.
|
| 214 |
+
if not parsed.netloc:
|
| 215 |
+
url = urljoin(resp.url, requote_uri(url))
|
| 216 |
+
else:
|
| 217 |
+
url = requote_uri(url)
|
| 218 |
+
|
| 219 |
+
prepared_request.url = to_native_string(url)
|
| 220 |
+
|
| 221 |
+
self.rebuild_method(prepared_request, resp)
|
| 222 |
+
|
| 223 |
+
# https://github.com/psf/requests/issues/1084
|
| 224 |
+
if resp.status_code not in (
|
| 225 |
+
codes.temporary_redirect,
|
| 226 |
+
codes.permanent_redirect,
|
| 227 |
+
):
|
| 228 |
+
# https://github.com/psf/requests/issues/3490
|
| 229 |
+
purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding")
|
| 230 |
+
for header in purged_headers:
|
| 231 |
+
prepared_request.headers.pop(header, None)
|
| 232 |
+
prepared_request.body = None
|
| 233 |
+
|
| 234 |
+
headers = prepared_request.headers
|
| 235 |
+
headers.pop("Cookie", None)
|
| 236 |
+
|
| 237 |
+
# Extract any cookies sent on the response to the cookiejar
|
| 238 |
+
# in the new request. Because we've mutated our copied prepared
|
| 239 |
+
# request, use the old one that we haven't yet touched.
|
| 240 |
+
extract_cookies_to_jar(prepared_request._cookies, req, resp.raw)
|
| 241 |
+
merge_cookies(prepared_request._cookies, self.cookies)
|
| 242 |
+
prepared_request.prepare_cookies(prepared_request._cookies)
|
| 243 |
+
|
| 244 |
+
# Rebuild auth and proxy information.
|
| 245 |
+
proxies = self.rebuild_proxies(prepared_request, proxies)
|
| 246 |
+
self.rebuild_auth(prepared_request, resp)
|
| 247 |
+
|
| 248 |
+
# A failed tell() sets `_body_position` to `object()`. This non-None
|
| 249 |
+
# value ensures `rewindable` will be True, allowing us to raise an
|
| 250 |
+
# UnrewindableBodyError, instead of hanging the connection.
|
| 251 |
+
rewindable = prepared_request._body_position is not None and (
|
| 252 |
+
"Content-Length" in headers or "Transfer-Encoding" in headers
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
# Attempt to rewind consumed file-like object.
|
| 256 |
+
if rewindable:
|
| 257 |
+
rewind_body(prepared_request)
|
| 258 |
+
|
| 259 |
+
# Override the original request.
|
| 260 |
+
req = prepared_request
|
| 261 |
+
|
| 262 |
+
if yield_requests:
|
| 263 |
+
yield req
|
| 264 |
+
else:
|
| 265 |
+
resp = self.send(
|
| 266 |
+
req,
|
| 267 |
+
stream=stream,
|
| 268 |
+
timeout=timeout,
|
| 269 |
+
verify=verify,
|
| 270 |
+
cert=cert,
|
| 271 |
+
proxies=proxies,
|
| 272 |
+
allow_redirects=False,
|
| 273 |
+
**adapter_kwargs,
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
|
| 277 |
+
|
| 278 |
+
# extract redirect url, if any, for the next loop
|
| 279 |
+
url = self.get_redirect_target(resp)
|
| 280 |
+
yield resp
|
| 281 |
+
|
| 282 |
+
def rebuild_auth(self, prepared_request, response):
|
| 283 |
+
"""When being redirected we may want to strip authentication from the
|
| 284 |
+
request to avoid leaking credentials. This method intelligently removes
|
| 285 |
+
and reapplies authentication where possible to avoid credential loss.
|
| 286 |
+
"""
|
| 287 |
+
headers = prepared_request.headers
|
| 288 |
+
url = prepared_request.url
|
| 289 |
+
|
| 290 |
+
if "Authorization" in headers and self.should_strip_auth(
|
| 291 |
+
response.request.url, url
|
| 292 |
+
):
|
| 293 |
+
# If we get redirected to a new host, we should strip out any
|
| 294 |
+
# authentication headers.
|
| 295 |
+
del headers["Authorization"]
|
| 296 |
+
|
| 297 |
+
# .netrc might have more auth for us on our new host.
|
| 298 |
+
new_auth = get_netrc_auth(url) if self.trust_env else None
|
| 299 |
+
if new_auth is not None:
|
| 300 |
+
prepared_request.prepare_auth(new_auth)
|
| 301 |
+
|
| 302 |
+
def rebuild_proxies(self, prepared_request, proxies):
|
| 303 |
+
"""This method re-evaluates the proxy configuration by considering the
|
| 304 |
+
environment variables. If we are redirected to a URL covered by
|
| 305 |
+
NO_PROXY, we strip the proxy configuration. Otherwise, we set missing
|
| 306 |
+
proxy keys for this URL (in case they were stripped by a previous
|
| 307 |
+
redirect).
|
| 308 |
+
|
| 309 |
+
This method also replaces the Proxy-Authorization header where
|
| 310 |
+
necessary.
|
| 311 |
+
|
| 312 |
+
:rtype: dict
|
| 313 |
+
"""
|
| 314 |
+
headers = prepared_request.headers
|
| 315 |
+
scheme = urlparse(prepared_request.url).scheme
|
| 316 |
+
new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env)
|
| 317 |
+
|
| 318 |
+
if "Proxy-Authorization" in headers:
|
| 319 |
+
del headers["Proxy-Authorization"]
|
| 320 |
+
|
| 321 |
+
try:
|
| 322 |
+
username, password = get_auth_from_url(new_proxies[scheme])
|
| 323 |
+
except KeyError:
|
| 324 |
+
username, password = None, None
|
| 325 |
+
|
| 326 |
+
# urllib3 handles proxy authorization for us in the standard adapter.
|
| 327 |
+
# Avoid appending this to TLS tunneled requests where it may be leaked.
|
| 328 |
+
if not scheme.startswith("https") and username and password:
|
| 329 |
+
headers["Proxy-Authorization"] = _basic_auth_str(username, password)
|
| 330 |
+
|
| 331 |
+
return new_proxies
|
| 332 |
+
|
| 333 |
+
def rebuild_method(self, prepared_request, response):
|
| 334 |
+
"""When being redirected we may want to change the method of the request
|
| 335 |
+
based on certain specs or browser behavior.
|
| 336 |
+
"""
|
| 337 |
+
method = prepared_request.method
|
| 338 |
+
|
| 339 |
+
# https://tools.ietf.org/html/rfc7231#section-6.4.4
|
| 340 |
+
if response.status_code == codes.see_other and method != "HEAD":
|
| 341 |
+
method = "GET"
|
| 342 |
+
|
| 343 |
+
# Do what the browsers do, despite standards...
|
| 344 |
+
# First, turn 302s into GETs.
|
| 345 |
+
if response.status_code == codes.found and method != "HEAD":
|
| 346 |
+
method = "GET"
|
| 347 |
+
|
| 348 |
+
# Second, if a POST is responded to with a 301, turn it into a GET.
|
| 349 |
+
# This bizarre behaviour is explained in Issue 1704.
|
| 350 |
+
if response.status_code == codes.moved and method == "POST":
|
| 351 |
+
method = "GET"
|
| 352 |
+
|
| 353 |
+
prepared_request.method = method
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
class Session(SessionRedirectMixin):
|
| 357 |
+
"""A Requests session.
|
| 358 |
+
|
| 359 |
+
Provides cookie persistence, connection-pooling, and configuration.
|
| 360 |
+
|
| 361 |
+
Basic Usage::
|
| 362 |
+
|
| 363 |
+
>>> import requests
|
| 364 |
+
>>> s = requests.Session()
|
| 365 |
+
>>> s.get('https://httpbin.org/get')
|
| 366 |
+
<Response [200]>
|
| 367 |
+
|
| 368 |
+
Or as a context manager::
|
| 369 |
+
|
| 370 |
+
>>> with requests.Session() as s:
|
| 371 |
+
... s.get('https://httpbin.org/get')
|
| 372 |
+
<Response [200]>
|
| 373 |
+
"""
|
| 374 |
+
|
| 375 |
+
__attrs__ = [
|
| 376 |
+
"headers",
|
| 377 |
+
"cookies",
|
| 378 |
+
"auth",
|
| 379 |
+
"proxies",
|
| 380 |
+
"hooks",
|
| 381 |
+
"params",
|
| 382 |
+
"verify",
|
| 383 |
+
"cert",
|
| 384 |
+
"adapters",
|
| 385 |
+
"stream",
|
| 386 |
+
"trust_env",
|
| 387 |
+
"max_redirects",
|
| 388 |
+
]
|
| 389 |
+
|
| 390 |
+
def __init__(self):
|
| 391 |
+
#: A case-insensitive dictionary of headers to be sent on each
|
| 392 |
+
#: :class:`Request <Request>` sent from this
|
| 393 |
+
#: :class:`Session <Session>`.
|
| 394 |
+
self.headers = default_headers()
|
| 395 |
+
|
| 396 |
+
#: Default Authentication tuple or object to attach to
|
| 397 |
+
#: :class:`Request <Request>`.
|
| 398 |
+
self.auth = None
|
| 399 |
+
|
| 400 |
+
#: Dictionary mapping protocol or protocol and host to the URL of the proxy
|
| 401 |
+
#: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to
|
| 402 |
+
#: be used on each :class:`Request <Request>`.
|
| 403 |
+
self.proxies = {}
|
| 404 |
+
|
| 405 |
+
#: Event-handling hooks.
|
| 406 |
+
self.hooks = default_hooks()
|
| 407 |
+
|
| 408 |
+
#: Dictionary of querystring data to attach to each
|
| 409 |
+
#: :class:`Request <Request>`. The dictionary values may be lists for
|
| 410 |
+
#: representing multivalued query parameters.
|
| 411 |
+
self.params = {}
|
| 412 |
+
|
| 413 |
+
#: Stream response content default.
|
| 414 |
+
self.stream = False
|
| 415 |
+
|
| 416 |
+
#: SSL Verification default.
|
| 417 |
+
#: Defaults to `True`, requiring requests to verify the TLS certificate at the
|
| 418 |
+
#: remote end.
|
| 419 |
+
#: If verify is set to `False`, requests will accept any TLS certificate
|
| 420 |
+
#: presented by the server, and will ignore hostname mismatches and/or
|
| 421 |
+
#: expired certificates, which will make your application vulnerable to
|
| 422 |
+
#: man-in-the-middle (MitM) attacks.
|
| 423 |
+
#: Only set this to `False` for testing.
|
| 424 |
+
self.verify = True
|
| 425 |
+
|
| 426 |
+
#: SSL client certificate default, if String, path to ssl client
|
| 427 |
+
#: cert file (.pem). If Tuple, ('cert', 'key') pair.
|
| 428 |
+
self.cert = None
|
| 429 |
+
|
| 430 |
+
#: Maximum number of redirects allowed. If the request exceeds this
|
| 431 |
+
#: limit, a :class:`TooManyRedirects` exception is raised.
|
| 432 |
+
#: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is
|
| 433 |
+
#: 30.
|
| 434 |
+
self.max_redirects = DEFAULT_REDIRECT_LIMIT
|
| 435 |
+
|
| 436 |
+
#: Trust environment settings for proxy configuration, default
|
| 437 |
+
#: authentication and similar.
|
| 438 |
+
self.trust_env = True
|
| 439 |
+
|
| 440 |
+
#: A CookieJar containing all currently outstanding cookies set on this
|
| 441 |
+
#: session. By default it is a
|
| 442 |
+
#: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but
|
| 443 |
+
#: may be any other ``cookielib.CookieJar`` compatible object.
|
| 444 |
+
self.cookies = cookiejar_from_dict({})
|
| 445 |
+
|
| 446 |
+
# Default connection adapters.
|
| 447 |
+
self.adapters = OrderedDict()
|
| 448 |
+
self.mount("https://", HTTPAdapter())
|
| 449 |
+
self.mount("http://", HTTPAdapter())
|
| 450 |
+
|
| 451 |
+
def __enter__(self):
|
| 452 |
+
return self
|
| 453 |
+
|
| 454 |
+
def __exit__(self, *args):
|
| 455 |
+
self.close()
|
| 456 |
+
|
| 457 |
+
def prepare_request(self, request):
|
| 458 |
+
"""Constructs a :class:`PreparedRequest <PreparedRequest>` for
|
| 459 |
+
transmission and returns it. The :class:`PreparedRequest` has settings
|
| 460 |
+
merged from the :class:`Request <Request>` instance and those of the
|
| 461 |
+
:class:`Session`.
|
| 462 |
+
|
| 463 |
+
:param request: :class:`Request` instance to prepare with this
|
| 464 |
+
session's settings.
|
| 465 |
+
:rtype: requests.PreparedRequest
|
| 466 |
+
"""
|
| 467 |
+
cookies = request.cookies or {}
|
| 468 |
+
|
| 469 |
+
# Bootstrap CookieJar.
|
| 470 |
+
if not isinstance(cookies, cookielib.CookieJar):
|
| 471 |
+
cookies = cookiejar_from_dict(cookies)
|
| 472 |
+
|
| 473 |
+
# Merge with session cookies
|
| 474 |
+
merged_cookies = merge_cookies(
|
| 475 |
+
merge_cookies(RequestsCookieJar(), self.cookies), cookies
|
| 476 |
+
)
|
| 477 |
+
|
| 478 |
+
# Set environment's basic authentication if not explicitly set.
|
| 479 |
+
auth = request.auth
|
| 480 |
+
if self.trust_env and not auth and not self.auth:
|
| 481 |
+
auth = get_netrc_auth(request.url)
|
| 482 |
+
|
| 483 |
+
p = PreparedRequest()
|
| 484 |
+
p.prepare(
|
| 485 |
+
method=request.method.upper(),
|
| 486 |
+
url=request.url,
|
| 487 |
+
files=request.files,
|
| 488 |
+
data=request.data,
|
| 489 |
+
json=request.json,
|
| 490 |
+
headers=merge_setting(
|
| 491 |
+
request.headers, self.headers, dict_class=CaseInsensitiveDict
|
| 492 |
+
),
|
| 493 |
+
params=merge_setting(request.params, self.params),
|
| 494 |
+
auth=merge_setting(auth, self.auth),
|
| 495 |
+
cookies=merged_cookies,
|
| 496 |
+
hooks=merge_hooks(request.hooks, self.hooks),
|
| 497 |
+
)
|
| 498 |
+
return p
|
| 499 |
+
|
| 500 |
+
def request(
|
| 501 |
+
self,
|
| 502 |
+
method,
|
| 503 |
+
url,
|
| 504 |
+
params=None,
|
| 505 |
+
data=None,
|
| 506 |
+
headers=None,
|
| 507 |
+
cookies=None,
|
| 508 |
+
files=None,
|
| 509 |
+
auth=None,
|
| 510 |
+
timeout=None,
|
| 511 |
+
allow_redirects=True,
|
| 512 |
+
proxies=None,
|
| 513 |
+
hooks=None,
|
| 514 |
+
stream=None,
|
| 515 |
+
verify=None,
|
| 516 |
+
cert=None,
|
| 517 |
+
json=None,
|
| 518 |
+
):
|
| 519 |
+
"""Constructs a :class:`Request <Request>`, prepares it and sends it.
|
| 520 |
+
Returns :class:`Response <Response>` object.
|
| 521 |
+
|
| 522 |
+
:param method: method for the new :class:`Request` object.
|
| 523 |
+
:param url: URL for the new :class:`Request` object.
|
| 524 |
+
:param params: (optional) Dictionary or bytes to be sent in the query
|
| 525 |
+
string for the :class:`Request`.
|
| 526 |
+
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
| 527 |
+
object to send in the body of the :class:`Request`.
|
| 528 |
+
:param json: (optional) json to send in the body of the
|
| 529 |
+
:class:`Request`.
|
| 530 |
+
:param headers: (optional) Dictionary of HTTP Headers to send with the
|
| 531 |
+
:class:`Request`.
|
| 532 |
+
:param cookies: (optional) Dict or CookieJar object to send with the
|
| 533 |
+
:class:`Request`.
|
| 534 |
+
:param files: (optional) Dictionary of ``'filename': file-like-objects``
|
| 535 |
+
for multipart encoding upload.
|
| 536 |
+
:param auth: (optional) Auth tuple or callable to enable
|
| 537 |
+
Basic/Digest/Custom HTTP Auth.
|
| 538 |
+
:param timeout: (optional) How long to wait for the server to send
|
| 539 |
+
data before giving up, as a float, or a :ref:`(connect timeout,
|
| 540 |
+
read timeout) <timeouts>` tuple.
|
| 541 |
+
:type timeout: float or tuple
|
| 542 |
+
:param allow_redirects: (optional) Set to True by default.
|
| 543 |
+
:type allow_redirects: bool
|
| 544 |
+
:param proxies: (optional) Dictionary mapping protocol or protocol and
|
| 545 |
+
hostname to the URL of the proxy.
|
| 546 |
+
:param hooks: (optional) Dictionary mapping hook name to one event or
|
| 547 |
+
list of events, event must be callable.
|
| 548 |
+
:param stream: (optional) whether to immediately download the response
|
| 549 |
+
content. Defaults to ``False``.
|
| 550 |
+
:param verify: (optional) Either a boolean, in which case it controls whether we verify
|
| 551 |
+
the server's TLS certificate, or a string, in which case it must be a path
|
| 552 |
+
to a CA bundle to use. Defaults to ``True``. When set to
|
| 553 |
+
``False``, requests will accept any TLS certificate presented by
|
| 554 |
+
the server, and will ignore hostname mismatches and/or expired
|
| 555 |
+
certificates, which will make your application vulnerable to
|
| 556 |
+
man-in-the-middle (MitM) attacks. Setting verify to ``False``
|
| 557 |
+
may be useful during local development or testing.
|
| 558 |
+
:param cert: (optional) if String, path to ssl client cert file (.pem).
|
| 559 |
+
If Tuple, ('cert', 'key') pair.
|
| 560 |
+
:rtype: requests.Response
|
| 561 |
+
"""
|
| 562 |
+
# Create the Request.
|
| 563 |
+
req = Request(
|
| 564 |
+
method=method.upper(),
|
| 565 |
+
url=url,
|
| 566 |
+
headers=headers,
|
| 567 |
+
files=files,
|
| 568 |
+
data=data or {},
|
| 569 |
+
json=json,
|
| 570 |
+
params=params or {},
|
| 571 |
+
auth=auth,
|
| 572 |
+
cookies=cookies,
|
| 573 |
+
hooks=hooks,
|
| 574 |
+
)
|
| 575 |
+
prep = self.prepare_request(req)
|
| 576 |
+
|
| 577 |
+
proxies = proxies or {}
|
| 578 |
+
|
| 579 |
+
settings = self.merge_environment_settings(
|
| 580 |
+
prep.url, proxies, stream, verify, cert
|
| 581 |
+
)
|
| 582 |
+
|
| 583 |
+
# Send the request.
|
| 584 |
+
send_kwargs = {
|
| 585 |
+
"timeout": timeout,
|
| 586 |
+
"allow_redirects": allow_redirects,
|
| 587 |
+
}
|
| 588 |
+
send_kwargs.update(settings)
|
| 589 |
+
resp = self.send(prep, **send_kwargs)
|
| 590 |
+
|
| 591 |
+
return resp
|
| 592 |
+
|
| 593 |
+
def get(self, url, **kwargs):
|
| 594 |
+
r"""Sends a GET request. Returns :class:`Response` object.
|
| 595 |
+
|
| 596 |
+
:param url: URL for the new :class:`Request` object.
|
| 597 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 598 |
+
:rtype: requests.Response
|
| 599 |
+
"""
|
| 600 |
+
|
| 601 |
+
kwargs.setdefault("allow_redirects", True)
|
| 602 |
+
return self.request("GET", url, **kwargs)
|
| 603 |
+
|
| 604 |
+
def options(self, url, **kwargs):
|
| 605 |
+
r"""Sends a OPTIONS request. Returns :class:`Response` object.
|
| 606 |
+
|
| 607 |
+
:param url: URL for the new :class:`Request` object.
|
| 608 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 609 |
+
:rtype: requests.Response
|
| 610 |
+
"""
|
| 611 |
+
|
| 612 |
+
kwargs.setdefault("allow_redirects", True)
|
| 613 |
+
return self.request("OPTIONS", url, **kwargs)
|
| 614 |
+
|
| 615 |
+
def head(self, url, **kwargs):
|
| 616 |
+
r"""Sends a HEAD request. Returns :class:`Response` object.
|
| 617 |
+
|
| 618 |
+
:param url: URL for the new :class:`Request` object.
|
| 619 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 620 |
+
:rtype: requests.Response
|
| 621 |
+
"""
|
| 622 |
+
|
| 623 |
+
kwargs.setdefault("allow_redirects", False)
|
| 624 |
+
return self.request("HEAD", url, **kwargs)
|
| 625 |
+
|
| 626 |
+
def post(self, url, data=None, json=None, **kwargs):
|
| 627 |
+
r"""Sends a POST request. Returns :class:`Response` object.
|
| 628 |
+
|
| 629 |
+
:param url: URL for the new :class:`Request` object.
|
| 630 |
+
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
| 631 |
+
object to send in the body of the :class:`Request`.
|
| 632 |
+
:param json: (optional) json to send in the body of the :class:`Request`.
|
| 633 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 634 |
+
:rtype: requests.Response
|
| 635 |
+
"""
|
| 636 |
+
|
| 637 |
+
return self.request("POST", url, data=data, json=json, **kwargs)
|
| 638 |
+
|
| 639 |
+
def put(self, url, data=None, **kwargs):
|
| 640 |
+
r"""Sends a PUT request. Returns :class:`Response` object.
|
| 641 |
+
|
| 642 |
+
:param url: URL for the new :class:`Request` object.
|
| 643 |
+
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
| 644 |
+
object to send in the body of the :class:`Request`.
|
| 645 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 646 |
+
:rtype: requests.Response
|
| 647 |
+
"""
|
| 648 |
+
|
| 649 |
+
return self.request("PUT", url, data=data, **kwargs)
|
| 650 |
+
|
| 651 |
+
def patch(self, url, data=None, **kwargs):
|
| 652 |
+
r"""Sends a PATCH request. Returns :class:`Response` object.
|
| 653 |
+
|
| 654 |
+
:param url: URL for the new :class:`Request` object.
|
| 655 |
+
:param data: (optional) Dictionary, list of tuples, bytes, or file-like
|
| 656 |
+
object to send in the body of the :class:`Request`.
|
| 657 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 658 |
+
:rtype: requests.Response
|
| 659 |
+
"""
|
| 660 |
+
|
| 661 |
+
return self.request("PATCH", url, data=data, **kwargs)
|
| 662 |
+
|
| 663 |
+
def delete(self, url, **kwargs):
|
| 664 |
+
r"""Sends a DELETE request. Returns :class:`Response` object.
|
| 665 |
+
|
| 666 |
+
:param url: URL for the new :class:`Request` object.
|
| 667 |
+
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
| 668 |
+
:rtype: requests.Response
|
| 669 |
+
"""
|
| 670 |
+
|
| 671 |
+
return self.request("DELETE", url, **kwargs)
|
| 672 |
+
|
| 673 |
+
def send(self, request, **kwargs):
|
| 674 |
+
"""Send a given PreparedRequest.
|
| 675 |
+
|
| 676 |
+
:rtype: requests.Response
|
| 677 |
+
"""
|
| 678 |
+
# Set defaults that the hooks can utilize to ensure they always have
|
| 679 |
+
# the correct parameters to reproduce the previous request.
|
| 680 |
+
kwargs.setdefault("stream", self.stream)
|
| 681 |
+
kwargs.setdefault("verify", self.verify)
|
| 682 |
+
kwargs.setdefault("cert", self.cert)
|
| 683 |
+
if "proxies" not in kwargs:
|
| 684 |
+
kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env)
|
| 685 |
+
|
| 686 |
+
# It's possible that users might accidentally send a Request object.
|
| 687 |
+
# Guard against that specific failure case.
|
| 688 |
+
if isinstance(request, Request):
|
| 689 |
+
raise ValueError("You can only send PreparedRequests.")
|
| 690 |
+
|
| 691 |
+
# Set up variables needed for resolve_redirects and dispatching of hooks
|
| 692 |
+
allow_redirects = kwargs.pop("allow_redirects", True)
|
| 693 |
+
stream = kwargs.get("stream")
|
| 694 |
+
hooks = request.hooks
|
| 695 |
+
|
| 696 |
+
# Get the appropriate adapter to use
|
| 697 |
+
adapter = self.get_adapter(url=request.url)
|
| 698 |
+
|
| 699 |
+
# Start time (approximately) of the request
|
| 700 |
+
start = preferred_clock()
|
| 701 |
+
|
| 702 |
+
# Send the request
|
| 703 |
+
r = adapter.send(request, **kwargs)
|
| 704 |
+
|
| 705 |
+
# Total elapsed time of the request (approximately)
|
| 706 |
+
elapsed = preferred_clock() - start
|
| 707 |
+
r.elapsed = timedelta(seconds=elapsed)
|
| 708 |
+
|
| 709 |
+
# Response manipulation hooks
|
| 710 |
+
r = dispatch_hook("response", hooks, r, **kwargs)
|
| 711 |
+
|
| 712 |
+
# Persist cookies
|
| 713 |
+
if r.history:
|
| 714 |
+
# If the hooks create history then we want those cookies too
|
| 715 |
+
for resp in r.history:
|
| 716 |
+
extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
|
| 717 |
+
|
| 718 |
+
extract_cookies_to_jar(self.cookies, request, r.raw)
|
| 719 |
+
|
| 720 |
+
# Resolve redirects if allowed.
|
| 721 |
+
if allow_redirects:
|
| 722 |
+
# Redirect resolving generator.
|
| 723 |
+
gen = self.resolve_redirects(r, request, **kwargs)
|
| 724 |
+
history = [resp for resp in gen]
|
| 725 |
+
else:
|
| 726 |
+
history = []
|
| 727 |
+
|
| 728 |
+
# Shuffle things around if there's history.
|
| 729 |
+
if history:
|
| 730 |
+
# Insert the first (original) request at the start
|
| 731 |
+
history.insert(0, r)
|
| 732 |
+
# Get the last request made
|
| 733 |
+
r = history.pop()
|
| 734 |
+
r.history = history
|
| 735 |
+
|
| 736 |
+
# If redirects aren't being followed, store the response on the Request for Response.next().
|
| 737 |
+
if not allow_redirects:
|
| 738 |
+
try:
|
| 739 |
+
r._next = next(
|
| 740 |
+
self.resolve_redirects(r, request, yield_requests=True, **kwargs)
|
| 741 |
+
)
|
| 742 |
+
except StopIteration:
|
| 743 |
+
pass
|
| 744 |
+
|
| 745 |
+
if not stream:
|
| 746 |
+
r.content
|
| 747 |
+
|
| 748 |
+
return r
|
| 749 |
+
|
| 750 |
+
def merge_environment_settings(self, url, proxies, stream, verify, cert):
|
| 751 |
+
"""
|
| 752 |
+
Check the environment and merge it with some settings.
|
| 753 |
+
|
| 754 |
+
:rtype: dict
|
| 755 |
+
"""
|
| 756 |
+
# Gather clues from the surrounding environment.
|
| 757 |
+
if self.trust_env:
|
| 758 |
+
# Set environment's proxies.
|
| 759 |
+
no_proxy = proxies.get("no_proxy") if proxies is not None else None
|
| 760 |
+
env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
|
| 761 |
+
for k, v in env_proxies.items():
|
| 762 |
+
proxies.setdefault(k, v)
|
| 763 |
+
|
| 764 |
+
# Look for requests environment configuration
|
| 765 |
+
# and be compatible with cURL.
|
| 766 |
+
if verify is True or verify is None:
|
| 767 |
+
verify = (
|
| 768 |
+
os.environ.get("REQUESTS_CA_BUNDLE")
|
| 769 |
+
or os.environ.get("CURL_CA_BUNDLE")
|
| 770 |
+
or verify
|
| 771 |
+
)
|
| 772 |
+
|
| 773 |
+
# Merge all the kwargs.
|
| 774 |
+
proxies = merge_setting(proxies, self.proxies)
|
| 775 |
+
stream = merge_setting(stream, self.stream)
|
| 776 |
+
verify = merge_setting(verify, self.verify)
|
| 777 |
+
cert = merge_setting(cert, self.cert)
|
| 778 |
+
|
| 779 |
+
return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert}
|
| 780 |
+
|
| 781 |
+
def get_adapter(self, url):
|
| 782 |
+
"""
|
| 783 |
+
Returns the appropriate connection adapter for the given URL.
|
| 784 |
+
|
| 785 |
+
:rtype: requests.adapters.BaseAdapter
|
| 786 |
+
"""
|
| 787 |
+
for prefix, adapter in self.adapters.items():
|
| 788 |
+
if url.lower().startswith(prefix.lower()):
|
| 789 |
+
return adapter
|
| 790 |
+
|
| 791 |
+
# Nothing matches :-/
|
| 792 |
+
raise InvalidSchema(f"No connection adapters were found for {url!r}")
|
| 793 |
+
|
| 794 |
+
def close(self):
|
| 795 |
+
"""Closes all adapters and as such the session"""
|
| 796 |
+
for v in self.adapters.values():
|
| 797 |
+
v.close()
|
| 798 |
+
|
| 799 |
+
def mount(self, prefix, adapter):
|
| 800 |
+
"""Registers a connection adapter to a prefix.
|
| 801 |
+
|
| 802 |
+
Adapters are sorted in descending order by prefix length.
|
| 803 |
+
"""
|
| 804 |
+
self.adapters[prefix] = adapter
|
| 805 |
+
keys_to_move = [k for k in self.adapters if len(k) < len(prefix)]
|
| 806 |
+
|
| 807 |
+
for key in keys_to_move:
|
| 808 |
+
self.adapters[key] = self.adapters.pop(key)
|
| 809 |
+
|
| 810 |
+
def __getstate__(self):
|
| 811 |
+
state = {attr: getattr(self, attr, None) for attr in self.__attrs__}
|
| 812 |
+
return state
|
| 813 |
+
|
| 814 |
+
def __setstate__(self, state):
|
| 815 |
+
for attr, value in state.items():
|
| 816 |
+
setattr(self, attr, value)
|
| 817 |
+
|
| 818 |
+
|
| 819 |
+
def session():
|
| 820 |
+
"""
|
| 821 |
+
Returns a :class:`Session` for context-management.
|
| 822 |
+
|
| 823 |
+
.. deprecated:: 1.0.0
|
| 824 |
+
|
| 825 |
+
This method has been deprecated since version 1.0.0 and is only kept for
|
| 826 |
+
backwards compatibility. New code should use :class:`~requests.sessions.Session`
|
| 827 |
+
to create a session. This may be removed at a future date.
|
| 828 |
+
|
| 829 |
+
:rtype: Session
|
| 830 |
+
"""
|
| 831 |
+
return Session()
|
.venv/Lib/site-packages/pip/_vendor/requests/status_codes.py
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
r"""
|
| 2 |
+
The ``codes`` object defines a mapping from common names for HTTP statuses
|
| 3 |
+
to their numerical codes, accessible either as attributes or as dictionary
|
| 4 |
+
items.
|
| 5 |
+
|
| 6 |
+
Example::
|
| 7 |
+
|
| 8 |
+
>>> import requests
|
| 9 |
+
>>> requests.codes['temporary_redirect']
|
| 10 |
+
307
|
| 11 |
+
>>> requests.codes.teapot
|
| 12 |
+
418
|
| 13 |
+
>>> requests.codes['\o/']
|
| 14 |
+
200
|
| 15 |
+
|
| 16 |
+
Some codes have multiple names, and both upper- and lower-case versions of
|
| 17 |
+
the names are allowed. For example, ``codes.ok``, ``codes.OK``, and
|
| 18 |
+
``codes.okay`` all correspond to the HTTP status code 200.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
from .structures import LookupDict
|
| 22 |
+
|
| 23 |
+
_codes = {
|
| 24 |
+
# Informational.
|
| 25 |
+
100: ("continue",),
|
| 26 |
+
101: ("switching_protocols",),
|
| 27 |
+
102: ("processing", "early-hints"),
|
| 28 |
+
103: ("checkpoint",),
|
| 29 |
+
122: ("uri_too_long", "request_uri_too_long"),
|
| 30 |
+
200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"),
|
| 31 |
+
201: ("created",),
|
| 32 |
+
202: ("accepted",),
|
| 33 |
+
203: ("non_authoritative_info", "non_authoritative_information"),
|
| 34 |
+
204: ("no_content",),
|
| 35 |
+
205: ("reset_content", "reset"),
|
| 36 |
+
206: ("partial_content", "partial"),
|
| 37 |
+
207: ("multi_status", "multiple_status", "multi_stati", "multiple_stati"),
|
| 38 |
+
208: ("already_reported",),
|
| 39 |
+
226: ("im_used",),
|
| 40 |
+
# Redirection.
|
| 41 |
+
300: ("multiple_choices",),
|
| 42 |
+
301: ("moved_permanently", "moved", "\\o-"),
|
| 43 |
+
302: ("found",),
|
| 44 |
+
303: ("see_other", "other"),
|
| 45 |
+
304: ("not_modified",),
|
| 46 |
+
305: ("use_proxy",),
|
| 47 |
+
306: ("switch_proxy",),
|
| 48 |
+
307: ("temporary_redirect", "temporary_moved", "temporary"),
|
| 49 |
+
308: (
|
| 50 |
+
"permanent_redirect",
|
| 51 |
+
"resume_incomplete",
|
| 52 |
+
"resume",
|
| 53 |
+
), # "resume" and "resume_incomplete" to be removed in 3.0
|
| 54 |
+
# Client Error.
|
| 55 |
+
400: ("bad_request", "bad"),
|
| 56 |
+
401: ("unauthorized",),
|
| 57 |
+
402: ("payment_required", "payment"),
|
| 58 |
+
403: ("forbidden",),
|
| 59 |
+
404: ("not_found", "-o-"),
|
| 60 |
+
405: ("method_not_allowed", "not_allowed"),
|
| 61 |
+
406: ("not_acceptable",),
|
| 62 |
+
407: ("proxy_authentication_required", "proxy_auth", "proxy_authentication"),
|
| 63 |
+
408: ("request_timeout", "timeout"),
|
| 64 |
+
409: ("conflict",),
|
| 65 |
+
410: ("gone",),
|
| 66 |
+
411: ("length_required",),
|
| 67 |
+
412: ("precondition_failed", "precondition"),
|
| 68 |
+
413: ("request_entity_too_large", "content_too_large"),
|
| 69 |
+
414: ("request_uri_too_large", "uri_too_long"),
|
| 70 |
+
415: ("unsupported_media_type", "unsupported_media", "media_type"),
|
| 71 |
+
416: (
|
| 72 |
+
"requested_range_not_satisfiable",
|
| 73 |
+
"requested_range",
|
| 74 |
+
"range_not_satisfiable",
|
| 75 |
+
),
|
| 76 |
+
417: ("expectation_failed",),
|
| 77 |
+
418: ("im_a_teapot", "teapot", "i_am_a_teapot"),
|
| 78 |
+
421: ("misdirected_request",),
|
| 79 |
+
422: ("unprocessable_entity", "unprocessable", "unprocessable_content"),
|
| 80 |
+
423: ("locked",),
|
| 81 |
+
424: ("failed_dependency", "dependency"),
|
| 82 |
+
425: ("unordered_collection", "unordered", "too_early"),
|
| 83 |
+
426: ("upgrade_required", "upgrade"),
|
| 84 |
+
428: ("precondition_required", "precondition"),
|
| 85 |
+
429: ("too_many_requests", "too_many"),
|
| 86 |
+
431: ("header_fields_too_large", "fields_too_large"),
|
| 87 |
+
444: ("no_response", "none"),
|
| 88 |
+
449: ("retry_with", "retry"),
|
| 89 |
+
450: ("blocked_by_windows_parental_controls", "parental_controls"),
|
| 90 |
+
451: ("unavailable_for_legal_reasons", "legal_reasons"),
|
| 91 |
+
499: ("client_closed_request",),
|
| 92 |
+
# Server Error.
|
| 93 |
+
500: ("internal_server_error", "server_error", "/o\\", "✗"),
|
| 94 |
+
501: ("not_implemented",),
|
| 95 |
+
502: ("bad_gateway",),
|
| 96 |
+
503: ("service_unavailable", "unavailable"),
|
| 97 |
+
504: ("gateway_timeout",),
|
| 98 |
+
505: ("http_version_not_supported", "http_version"),
|
| 99 |
+
506: ("variant_also_negotiates",),
|
| 100 |
+
507: ("insufficient_storage",),
|
| 101 |
+
509: ("bandwidth_limit_exceeded", "bandwidth"),
|
| 102 |
+
510: ("not_extended",),
|
| 103 |
+
511: ("network_authentication_required", "network_auth", "network_authentication"),
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
codes = LookupDict(name="status_codes")
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def _init():
|
| 110 |
+
for code, titles in _codes.items():
|
| 111 |
+
for title in titles:
|
| 112 |
+
setattr(codes, title, code)
|
| 113 |
+
if not title.startswith(("\\", "/")):
|
| 114 |
+
setattr(codes, title.upper(), code)
|
| 115 |
+
|
| 116 |
+
def doc(code):
|
| 117 |
+
names = ", ".join(f"``{n}``" for n in _codes[code])
|
| 118 |
+
return "* %d: %s" % (code, names)
|
| 119 |
+
|
| 120 |
+
global __doc__
|
| 121 |
+
__doc__ = (
|
| 122 |
+
__doc__ + "\n" + "\n".join(doc(code) for code in sorted(_codes))
|
| 123 |
+
if __doc__ is not None
|
| 124 |
+
else None
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
_init()
|
.venv/Lib/site-packages/pip/_vendor/requests/structures.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.structures
|
| 3 |
+
~~~~~~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
Data structures that power Requests.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from collections import OrderedDict
|
| 9 |
+
|
| 10 |
+
from .compat import Mapping, MutableMapping
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class CaseInsensitiveDict(MutableMapping):
|
| 14 |
+
"""A case-insensitive ``dict``-like object.
|
| 15 |
+
|
| 16 |
+
Implements all methods and operations of
|
| 17 |
+
``MutableMapping`` as well as dict's ``copy``. Also
|
| 18 |
+
provides ``lower_items``.
|
| 19 |
+
|
| 20 |
+
All keys are expected to be strings. The structure remembers the
|
| 21 |
+
case of the last key to be set, and ``iter(instance)``,
|
| 22 |
+
``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
|
| 23 |
+
will contain case-sensitive keys. However, querying and contains
|
| 24 |
+
testing is case insensitive::
|
| 25 |
+
|
| 26 |
+
cid = CaseInsensitiveDict()
|
| 27 |
+
cid['Accept'] = 'application/json'
|
| 28 |
+
cid['aCCEPT'] == 'application/json' # True
|
| 29 |
+
list(cid) == ['Accept'] # True
|
| 30 |
+
|
| 31 |
+
For example, ``headers['content-encoding']`` will return the
|
| 32 |
+
value of a ``'Content-Encoding'`` response header, regardless
|
| 33 |
+
of how the header name was originally stored.
|
| 34 |
+
|
| 35 |
+
If the constructor, ``.update``, or equality comparison
|
| 36 |
+
operations are given keys that have equal ``.lower()``s, the
|
| 37 |
+
behavior is undefined.
|
| 38 |
+
"""
|
| 39 |
+
|
| 40 |
+
def __init__(self, data=None, **kwargs):
|
| 41 |
+
self._store = OrderedDict()
|
| 42 |
+
if data is None:
|
| 43 |
+
data = {}
|
| 44 |
+
self.update(data, **kwargs)
|
| 45 |
+
|
| 46 |
+
def __setitem__(self, key, value):
|
| 47 |
+
# Use the lowercased key for lookups, but store the actual
|
| 48 |
+
# key alongside the value.
|
| 49 |
+
self._store[key.lower()] = (key, value)
|
| 50 |
+
|
| 51 |
+
def __getitem__(self, key):
|
| 52 |
+
return self._store[key.lower()][1]
|
| 53 |
+
|
| 54 |
+
def __delitem__(self, key):
|
| 55 |
+
del self._store[key.lower()]
|
| 56 |
+
|
| 57 |
+
def __iter__(self):
|
| 58 |
+
return (casedkey for casedkey, mappedvalue in self._store.values())
|
| 59 |
+
|
| 60 |
+
def __len__(self):
|
| 61 |
+
return len(self._store)
|
| 62 |
+
|
| 63 |
+
def lower_items(self):
|
| 64 |
+
"""Like iteritems(), but with all lowercase keys."""
|
| 65 |
+
return ((lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items())
|
| 66 |
+
|
| 67 |
+
def __eq__(self, other):
|
| 68 |
+
if isinstance(other, Mapping):
|
| 69 |
+
other = CaseInsensitiveDict(other)
|
| 70 |
+
else:
|
| 71 |
+
return NotImplemented
|
| 72 |
+
# Compare insensitively
|
| 73 |
+
return dict(self.lower_items()) == dict(other.lower_items())
|
| 74 |
+
|
| 75 |
+
# Copy is required
|
| 76 |
+
def copy(self):
|
| 77 |
+
return CaseInsensitiveDict(self._store.values())
|
| 78 |
+
|
| 79 |
+
def __repr__(self):
|
| 80 |
+
return str(dict(self.items()))
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class LookupDict(dict):
|
| 84 |
+
"""Dictionary lookup object."""
|
| 85 |
+
|
| 86 |
+
def __init__(self, name=None):
|
| 87 |
+
self.name = name
|
| 88 |
+
super().__init__()
|
| 89 |
+
|
| 90 |
+
def __repr__(self):
|
| 91 |
+
return f"<lookup '{self.name}'>"
|
| 92 |
+
|
| 93 |
+
def __getitem__(self, key):
|
| 94 |
+
# We allow fall-through here, so values default to None
|
| 95 |
+
|
| 96 |
+
return self.__dict__.get(key, None)
|
| 97 |
+
|
| 98 |
+
def get(self, key, default=None):
|
| 99 |
+
return self.__dict__.get(key, default)
|
.venv/Lib/site-packages/pip/_vendor/requests/utils.py
ADDED
|
@@ -0,0 +1,1086 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
requests.utils
|
| 3 |
+
~~~~~~~~~~~~~~
|
| 4 |
+
|
| 5 |
+
This module provides utility functions that are used within Requests
|
| 6 |
+
that are also useful for external consumption.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import codecs
|
| 10 |
+
import contextlib
|
| 11 |
+
import io
|
| 12 |
+
import os
|
| 13 |
+
import re
|
| 14 |
+
import socket
|
| 15 |
+
import struct
|
| 16 |
+
import sys
|
| 17 |
+
import tempfile
|
| 18 |
+
import warnings
|
| 19 |
+
import zipfile
|
| 20 |
+
from collections import OrderedDict
|
| 21 |
+
|
| 22 |
+
from pip._vendor.urllib3.util import make_headers, parse_url
|
| 23 |
+
|
| 24 |
+
from . import certs
|
| 25 |
+
from .__version__ import __version__
|
| 26 |
+
|
| 27 |
+
# to_native_string is unused here, but imported here for backwards compatibility
|
| 28 |
+
from ._internal_utils import ( # noqa: F401
|
| 29 |
+
_HEADER_VALIDATORS_BYTE,
|
| 30 |
+
_HEADER_VALIDATORS_STR,
|
| 31 |
+
HEADER_VALIDATORS,
|
| 32 |
+
to_native_string,
|
| 33 |
+
)
|
| 34 |
+
from .compat import (
|
| 35 |
+
Mapping,
|
| 36 |
+
basestring,
|
| 37 |
+
bytes,
|
| 38 |
+
getproxies,
|
| 39 |
+
getproxies_environment,
|
| 40 |
+
integer_types,
|
| 41 |
+
is_urllib3_1,
|
| 42 |
+
)
|
| 43 |
+
from .compat import parse_http_list as _parse_list_header
|
| 44 |
+
from .compat import (
|
| 45 |
+
proxy_bypass,
|
| 46 |
+
proxy_bypass_environment,
|
| 47 |
+
quote,
|
| 48 |
+
str,
|
| 49 |
+
unquote,
|
| 50 |
+
urlparse,
|
| 51 |
+
urlunparse,
|
| 52 |
+
)
|
| 53 |
+
from .cookies import cookiejar_from_dict
|
| 54 |
+
from .exceptions import (
|
| 55 |
+
FileModeWarning,
|
| 56 |
+
InvalidHeader,
|
| 57 |
+
InvalidURL,
|
| 58 |
+
UnrewindableBodyError,
|
| 59 |
+
)
|
| 60 |
+
from .structures import CaseInsensitiveDict
|
| 61 |
+
|
| 62 |
+
NETRC_FILES = (".netrc", "_netrc")
|
| 63 |
+
|
| 64 |
+
DEFAULT_CA_BUNDLE_PATH = certs.where()
|
| 65 |
+
|
| 66 |
+
DEFAULT_PORTS = {"http": 80, "https": 443}
|
| 67 |
+
|
| 68 |
+
# Ensure that ', ' is used to preserve previous delimiter behavior.
|
| 69 |
+
DEFAULT_ACCEPT_ENCODING = ", ".join(
|
| 70 |
+
re.split(r",\s*", make_headers(accept_encoding=True)["accept-encoding"])
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
if sys.platform == "win32":
|
| 75 |
+
# provide a proxy_bypass version on Windows without DNS lookups
|
| 76 |
+
|
| 77 |
+
def proxy_bypass_registry(host):
|
| 78 |
+
try:
|
| 79 |
+
import winreg
|
| 80 |
+
except ImportError:
|
| 81 |
+
return False
|
| 82 |
+
|
| 83 |
+
try:
|
| 84 |
+
internetSettings = winreg.OpenKey(
|
| 85 |
+
winreg.HKEY_CURRENT_USER,
|
| 86 |
+
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
|
| 87 |
+
)
|
| 88 |
+
# ProxyEnable could be REG_SZ or REG_DWORD, normalizing it
|
| 89 |
+
proxyEnable = int(winreg.QueryValueEx(internetSettings, "ProxyEnable")[0])
|
| 90 |
+
# ProxyOverride is almost always a string
|
| 91 |
+
proxyOverride = winreg.QueryValueEx(internetSettings, "ProxyOverride")[0]
|
| 92 |
+
except (OSError, ValueError):
|
| 93 |
+
return False
|
| 94 |
+
if not proxyEnable or not proxyOverride:
|
| 95 |
+
return False
|
| 96 |
+
|
| 97 |
+
# make a check value list from the registry entry: replace the
|
| 98 |
+
# '<local>' string by the localhost entry and the corresponding
|
| 99 |
+
# canonical entry.
|
| 100 |
+
proxyOverride = proxyOverride.split(";")
|
| 101 |
+
# filter out empty strings to avoid re.match return true in the following code.
|
| 102 |
+
proxyOverride = filter(None, proxyOverride)
|
| 103 |
+
# now check if we match one of the registry values.
|
| 104 |
+
for test in proxyOverride:
|
| 105 |
+
if test == "<local>":
|
| 106 |
+
if "." not in host:
|
| 107 |
+
return True
|
| 108 |
+
test = test.replace(".", r"\.") # mask dots
|
| 109 |
+
test = test.replace("*", r".*") # change glob sequence
|
| 110 |
+
test = test.replace("?", r".") # change glob char
|
| 111 |
+
if re.match(test, host, re.I):
|
| 112 |
+
return True
|
| 113 |
+
return False
|
| 114 |
+
|
| 115 |
+
def proxy_bypass(host): # noqa
|
| 116 |
+
"""Return True, if the host should be bypassed.
|
| 117 |
+
|
| 118 |
+
Checks proxy settings gathered from the environment, if specified,
|
| 119 |
+
or the registry.
|
| 120 |
+
"""
|
| 121 |
+
if getproxies_environment():
|
| 122 |
+
return proxy_bypass_environment(host)
|
| 123 |
+
else:
|
| 124 |
+
return proxy_bypass_registry(host)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def dict_to_sequence(d):
|
| 128 |
+
"""Returns an internal sequence dictionary update."""
|
| 129 |
+
|
| 130 |
+
if hasattr(d, "items"):
|
| 131 |
+
d = d.items()
|
| 132 |
+
|
| 133 |
+
return d
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
def super_len(o):
|
| 137 |
+
total_length = None
|
| 138 |
+
current_position = 0
|
| 139 |
+
|
| 140 |
+
if not is_urllib3_1 and isinstance(o, str):
|
| 141 |
+
# urllib3 2.x+ treats all strings as utf-8 instead
|
| 142 |
+
# of latin-1 (iso-8859-1) like http.client.
|
| 143 |
+
o = o.encode("utf-8")
|
| 144 |
+
|
| 145 |
+
if hasattr(o, "__len__"):
|
| 146 |
+
total_length = len(o)
|
| 147 |
+
|
| 148 |
+
elif hasattr(o, "len"):
|
| 149 |
+
total_length = o.len
|
| 150 |
+
|
| 151 |
+
elif hasattr(o, "fileno"):
|
| 152 |
+
try:
|
| 153 |
+
fileno = o.fileno()
|
| 154 |
+
except (io.UnsupportedOperation, AttributeError):
|
| 155 |
+
# AttributeError is a surprising exception, seeing as how we've just checked
|
| 156 |
+
# that `hasattr(o, 'fileno')`. It happens for objects obtained via
|
| 157 |
+
# `Tarfile.extractfile()`, per issue 5229.
|
| 158 |
+
pass
|
| 159 |
+
else:
|
| 160 |
+
total_length = os.fstat(fileno).st_size
|
| 161 |
+
|
| 162 |
+
# Having used fstat to determine the file length, we need to
|
| 163 |
+
# confirm that this file was opened up in binary mode.
|
| 164 |
+
if "b" not in o.mode:
|
| 165 |
+
warnings.warn(
|
| 166 |
+
(
|
| 167 |
+
"Requests has determined the content-length for this "
|
| 168 |
+
"request using the binary size of the file: however, the "
|
| 169 |
+
"file has been opened in text mode (i.e. without the 'b' "
|
| 170 |
+
"flag in the mode). This may lead to an incorrect "
|
| 171 |
+
"content-length. In Requests 3.0, support will be removed "
|
| 172 |
+
"for files in text mode."
|
| 173 |
+
),
|
| 174 |
+
FileModeWarning,
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
if hasattr(o, "tell"):
|
| 178 |
+
try:
|
| 179 |
+
current_position = o.tell()
|
| 180 |
+
except OSError:
|
| 181 |
+
# This can happen in some weird situations, such as when the file
|
| 182 |
+
# is actually a special file descriptor like stdin. In this
|
| 183 |
+
# instance, we don't know what the length is, so set it to zero and
|
| 184 |
+
# let requests chunk it instead.
|
| 185 |
+
if total_length is not None:
|
| 186 |
+
current_position = total_length
|
| 187 |
+
else:
|
| 188 |
+
if hasattr(o, "seek") and total_length is None:
|
| 189 |
+
# StringIO and BytesIO have seek but no usable fileno
|
| 190 |
+
try:
|
| 191 |
+
# seek to end of file
|
| 192 |
+
o.seek(0, 2)
|
| 193 |
+
total_length = o.tell()
|
| 194 |
+
|
| 195 |
+
# seek back to current position to support
|
| 196 |
+
# partially read file-like objects
|
| 197 |
+
o.seek(current_position or 0)
|
| 198 |
+
except OSError:
|
| 199 |
+
total_length = 0
|
| 200 |
+
|
| 201 |
+
if total_length is None:
|
| 202 |
+
total_length = 0
|
| 203 |
+
|
| 204 |
+
return max(0, total_length - current_position)
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def get_netrc_auth(url, raise_errors=False):
|
| 208 |
+
"""Returns the Requests tuple auth for a given url from netrc."""
|
| 209 |
+
|
| 210 |
+
netrc_file = os.environ.get("NETRC")
|
| 211 |
+
if netrc_file is not None:
|
| 212 |
+
netrc_locations = (netrc_file,)
|
| 213 |
+
else:
|
| 214 |
+
netrc_locations = (f"~/{f}" for f in NETRC_FILES)
|
| 215 |
+
|
| 216 |
+
try:
|
| 217 |
+
from netrc import NetrcParseError, netrc
|
| 218 |
+
|
| 219 |
+
netrc_path = None
|
| 220 |
+
|
| 221 |
+
for f in netrc_locations:
|
| 222 |
+
loc = os.path.expanduser(f)
|
| 223 |
+
if os.path.exists(loc):
|
| 224 |
+
netrc_path = loc
|
| 225 |
+
break
|
| 226 |
+
|
| 227 |
+
# Abort early if there isn't one.
|
| 228 |
+
if netrc_path is None:
|
| 229 |
+
return
|
| 230 |
+
|
| 231 |
+
ri = urlparse(url)
|
| 232 |
+
host = ri.hostname
|
| 233 |
+
|
| 234 |
+
try:
|
| 235 |
+
_netrc = netrc(netrc_path).authenticators(host)
|
| 236 |
+
if _netrc:
|
| 237 |
+
# Return with login / password
|
| 238 |
+
login_i = 0 if _netrc[0] else 1
|
| 239 |
+
return (_netrc[login_i], _netrc[2])
|
| 240 |
+
except (NetrcParseError, OSError):
|
| 241 |
+
# If there was a parsing error or a permissions issue reading the file,
|
| 242 |
+
# we'll just skip netrc auth unless explicitly asked to raise errors.
|
| 243 |
+
if raise_errors:
|
| 244 |
+
raise
|
| 245 |
+
|
| 246 |
+
# App Engine hackiness.
|
| 247 |
+
except (ImportError, AttributeError):
|
| 248 |
+
pass
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
def guess_filename(obj):
|
| 252 |
+
"""Tries to guess the filename of the given object."""
|
| 253 |
+
name = getattr(obj, "name", None)
|
| 254 |
+
if name and isinstance(name, basestring) and name[0] != "<" and name[-1] != ">":
|
| 255 |
+
return os.path.basename(name)
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
def extract_zipped_paths(path):
|
| 259 |
+
"""Replace nonexistent paths that look like they refer to a member of a zip
|
| 260 |
+
archive with the location of an extracted copy of the target, or else
|
| 261 |
+
just return the provided path unchanged.
|
| 262 |
+
"""
|
| 263 |
+
if os.path.exists(path):
|
| 264 |
+
# this is already a valid path, no need to do anything further
|
| 265 |
+
return path
|
| 266 |
+
|
| 267 |
+
# find the first valid part of the provided path and treat that as a zip archive
|
| 268 |
+
# assume the rest of the path is the name of a member in the archive
|
| 269 |
+
archive, member = os.path.split(path)
|
| 270 |
+
while archive and not os.path.exists(archive):
|
| 271 |
+
archive, prefix = os.path.split(archive)
|
| 272 |
+
if not prefix:
|
| 273 |
+
# If we don't check for an empty prefix after the split (in other words, archive remains unchanged after the split),
|
| 274 |
+
# we _can_ end up in an infinite loop on a rare corner case affecting a small number of users
|
| 275 |
+
break
|
| 276 |
+
member = "/".join([prefix, member])
|
| 277 |
+
|
| 278 |
+
if not zipfile.is_zipfile(archive):
|
| 279 |
+
return path
|
| 280 |
+
|
| 281 |
+
zip_file = zipfile.ZipFile(archive)
|
| 282 |
+
if member not in zip_file.namelist():
|
| 283 |
+
return path
|
| 284 |
+
|
| 285 |
+
# we have a valid zip archive and a valid member of that archive
|
| 286 |
+
tmp = tempfile.gettempdir()
|
| 287 |
+
extracted_path = os.path.join(tmp, member.split("/")[-1])
|
| 288 |
+
if not os.path.exists(extracted_path):
|
| 289 |
+
# use read + write to avoid the creating nested folders, we only want the file, avoids mkdir racing condition
|
| 290 |
+
with atomic_open(extracted_path) as file_handler:
|
| 291 |
+
file_handler.write(zip_file.read(member))
|
| 292 |
+
return extracted_path
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
@contextlib.contextmanager
|
| 296 |
+
def atomic_open(filename):
|
| 297 |
+
"""Write a file to the disk in an atomic fashion"""
|
| 298 |
+
tmp_descriptor, tmp_name = tempfile.mkstemp(dir=os.path.dirname(filename))
|
| 299 |
+
try:
|
| 300 |
+
with os.fdopen(tmp_descriptor, "wb") as tmp_handler:
|
| 301 |
+
yield tmp_handler
|
| 302 |
+
os.replace(tmp_name, filename)
|
| 303 |
+
except BaseException:
|
| 304 |
+
os.remove(tmp_name)
|
| 305 |
+
raise
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def from_key_val_list(value):
|
| 309 |
+
"""Take an object and test to see if it can be represented as a
|
| 310 |
+
dictionary. Unless it can not be represented as such, return an
|
| 311 |
+
OrderedDict, e.g.,
|
| 312 |
+
|
| 313 |
+
::
|
| 314 |
+
|
| 315 |
+
>>> from_key_val_list([('key', 'val')])
|
| 316 |
+
OrderedDict([('key', 'val')])
|
| 317 |
+
>>> from_key_val_list('string')
|
| 318 |
+
Traceback (most recent call last):
|
| 319 |
+
...
|
| 320 |
+
ValueError: cannot encode objects that are not 2-tuples
|
| 321 |
+
>>> from_key_val_list({'key': 'val'})
|
| 322 |
+
OrderedDict([('key', 'val')])
|
| 323 |
+
|
| 324 |
+
:rtype: OrderedDict
|
| 325 |
+
"""
|
| 326 |
+
if value is None:
|
| 327 |
+
return None
|
| 328 |
+
|
| 329 |
+
if isinstance(value, (str, bytes, bool, int)):
|
| 330 |
+
raise ValueError("cannot encode objects that are not 2-tuples")
|
| 331 |
+
|
| 332 |
+
return OrderedDict(value)
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
def to_key_val_list(value):
|
| 336 |
+
"""Take an object and test to see if it can be represented as a
|
| 337 |
+
dictionary. If it can be, return a list of tuples, e.g.,
|
| 338 |
+
|
| 339 |
+
::
|
| 340 |
+
|
| 341 |
+
>>> to_key_val_list([('key', 'val')])
|
| 342 |
+
[('key', 'val')]
|
| 343 |
+
>>> to_key_val_list({'key': 'val'})
|
| 344 |
+
[('key', 'val')]
|
| 345 |
+
>>> to_key_val_list('string')
|
| 346 |
+
Traceback (most recent call last):
|
| 347 |
+
...
|
| 348 |
+
ValueError: cannot encode objects that are not 2-tuples
|
| 349 |
+
|
| 350 |
+
:rtype: list
|
| 351 |
+
"""
|
| 352 |
+
if value is None:
|
| 353 |
+
return None
|
| 354 |
+
|
| 355 |
+
if isinstance(value, (str, bytes, bool, int)):
|
| 356 |
+
raise ValueError("cannot encode objects that are not 2-tuples")
|
| 357 |
+
|
| 358 |
+
if isinstance(value, Mapping):
|
| 359 |
+
value = value.items()
|
| 360 |
+
|
| 361 |
+
return list(value)
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
# From mitsuhiko/werkzeug (used with permission).
|
| 365 |
+
def parse_list_header(value):
|
| 366 |
+
"""Parse lists as described by RFC 2068 Section 2.
|
| 367 |
+
|
| 368 |
+
In particular, parse comma-separated lists where the elements of
|
| 369 |
+
the list may include quoted-strings. A quoted-string could
|
| 370 |
+
contain a comma. A non-quoted string could have quotes in the
|
| 371 |
+
middle. Quotes are removed automatically after parsing.
|
| 372 |
+
|
| 373 |
+
It basically works like :func:`parse_set_header` just that items
|
| 374 |
+
may appear multiple times and case sensitivity is preserved.
|
| 375 |
+
|
| 376 |
+
The return value is a standard :class:`list`:
|
| 377 |
+
|
| 378 |
+
>>> parse_list_header('token, "quoted value"')
|
| 379 |
+
['token', 'quoted value']
|
| 380 |
+
|
| 381 |
+
To create a header from the :class:`list` again, use the
|
| 382 |
+
:func:`dump_header` function.
|
| 383 |
+
|
| 384 |
+
:param value: a string with a list header.
|
| 385 |
+
:return: :class:`list`
|
| 386 |
+
:rtype: list
|
| 387 |
+
"""
|
| 388 |
+
result = []
|
| 389 |
+
for item in _parse_list_header(value):
|
| 390 |
+
if item[:1] == item[-1:] == '"':
|
| 391 |
+
item = unquote_header_value(item[1:-1])
|
| 392 |
+
result.append(item)
|
| 393 |
+
return result
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
# From mitsuhiko/werkzeug (used with permission).
|
| 397 |
+
def parse_dict_header(value):
|
| 398 |
+
"""Parse lists of key, value pairs as described by RFC 2068 Section 2 and
|
| 399 |
+
convert them into a python dict:
|
| 400 |
+
|
| 401 |
+
>>> d = parse_dict_header('foo="is a fish", bar="as well"')
|
| 402 |
+
>>> type(d) is dict
|
| 403 |
+
True
|
| 404 |
+
>>> sorted(d.items())
|
| 405 |
+
[('bar', 'as well'), ('foo', 'is a fish')]
|
| 406 |
+
|
| 407 |
+
If there is no value for a key it will be `None`:
|
| 408 |
+
|
| 409 |
+
>>> parse_dict_header('key_without_value')
|
| 410 |
+
{'key_without_value': None}
|
| 411 |
+
|
| 412 |
+
To create a header from the :class:`dict` again, use the
|
| 413 |
+
:func:`dump_header` function.
|
| 414 |
+
|
| 415 |
+
:param value: a string with a dict header.
|
| 416 |
+
:return: :class:`dict`
|
| 417 |
+
:rtype: dict
|
| 418 |
+
"""
|
| 419 |
+
result = {}
|
| 420 |
+
for item in _parse_list_header(value):
|
| 421 |
+
if "=" not in item:
|
| 422 |
+
result[item] = None
|
| 423 |
+
continue
|
| 424 |
+
name, value = item.split("=", 1)
|
| 425 |
+
if value[:1] == value[-1:] == '"':
|
| 426 |
+
value = unquote_header_value(value[1:-1])
|
| 427 |
+
result[name] = value
|
| 428 |
+
return result
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
# From mitsuhiko/werkzeug (used with permission).
|
| 432 |
+
def unquote_header_value(value, is_filename=False):
|
| 433 |
+
r"""Unquotes a header value. (Reversal of :func:`quote_header_value`).
|
| 434 |
+
This does not use the real unquoting but what browsers are actually
|
| 435 |
+
using for quoting.
|
| 436 |
+
|
| 437 |
+
:param value: the header value to unquote.
|
| 438 |
+
:rtype: str
|
| 439 |
+
"""
|
| 440 |
+
if value and value[0] == value[-1] == '"':
|
| 441 |
+
# this is not the real unquoting, but fixing this so that the
|
| 442 |
+
# RFC is met will result in bugs with internet explorer and
|
| 443 |
+
# probably some other browsers as well. IE for example is
|
| 444 |
+
# uploading files with "C:\foo\bar.txt" as filename
|
| 445 |
+
value = value[1:-1]
|
| 446 |
+
|
| 447 |
+
# if this is a filename and the starting characters look like
|
| 448 |
+
# a UNC path, then just return the value without quotes. Using the
|
| 449 |
+
# replace sequence below on a UNC path has the effect of turning
|
| 450 |
+
# the leading double slash into a single slash and then
|
| 451 |
+
# _fix_ie_filename() doesn't work correctly. See #458.
|
| 452 |
+
if not is_filename or value[:2] != "\\\\":
|
| 453 |
+
return value.replace("\\\\", "\\").replace('\\"', '"')
|
| 454 |
+
return value
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
def dict_from_cookiejar(cj):
|
| 458 |
+
"""Returns a key/value dictionary from a CookieJar.
|
| 459 |
+
|
| 460 |
+
:param cj: CookieJar object to extract cookies from.
|
| 461 |
+
:rtype: dict
|
| 462 |
+
"""
|
| 463 |
+
|
| 464 |
+
cookie_dict = {cookie.name: cookie.value for cookie in cj}
|
| 465 |
+
return cookie_dict
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
def add_dict_to_cookiejar(cj, cookie_dict):
|
| 469 |
+
"""Returns a CookieJar from a key/value dictionary.
|
| 470 |
+
|
| 471 |
+
:param cj: CookieJar to insert cookies into.
|
| 472 |
+
:param cookie_dict: Dict of key/values to insert into CookieJar.
|
| 473 |
+
:rtype: CookieJar
|
| 474 |
+
"""
|
| 475 |
+
|
| 476 |
+
return cookiejar_from_dict(cookie_dict, cj)
|
| 477 |
+
|
| 478 |
+
|
| 479 |
+
def get_encodings_from_content(content):
|
| 480 |
+
"""Returns encodings from given content string.
|
| 481 |
+
|
| 482 |
+
:param content: bytestring to extract encodings from.
|
| 483 |
+
"""
|
| 484 |
+
warnings.warn(
|
| 485 |
+
(
|
| 486 |
+
"In requests 3.0, get_encodings_from_content will be removed. For "
|
| 487 |
+
"more information, please see the discussion on issue #2266. (This"
|
| 488 |
+
" warning should only appear once.)"
|
| 489 |
+
),
|
| 490 |
+
DeprecationWarning,
|
| 491 |
+
)
|
| 492 |
+
|
| 493 |
+
charset_re = re.compile(r'<meta.*?charset=["\']*(.+?)["\'>]', flags=re.I)
|
| 494 |
+
pragma_re = re.compile(r'<meta.*?content=["\']*;?charset=(.+?)["\'>]', flags=re.I)
|
| 495 |
+
xml_re = re.compile(r'^<\?xml.*?encoding=["\']*(.+?)["\'>]')
|
| 496 |
+
|
| 497 |
+
return (
|
| 498 |
+
charset_re.findall(content)
|
| 499 |
+
+ pragma_re.findall(content)
|
| 500 |
+
+ xml_re.findall(content)
|
| 501 |
+
)
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
def _parse_content_type_header(header):
|
| 505 |
+
"""Returns content type and parameters from given header
|
| 506 |
+
|
| 507 |
+
:param header: string
|
| 508 |
+
:return: tuple containing content type and dictionary of
|
| 509 |
+
parameters
|
| 510 |
+
"""
|
| 511 |
+
|
| 512 |
+
tokens = header.split(";")
|
| 513 |
+
content_type, params = tokens[0].strip(), tokens[1:]
|
| 514 |
+
params_dict = {}
|
| 515 |
+
items_to_strip = "\"' "
|
| 516 |
+
|
| 517 |
+
for param in params:
|
| 518 |
+
param = param.strip()
|
| 519 |
+
if param:
|
| 520 |
+
key, value = param, True
|
| 521 |
+
index_of_equals = param.find("=")
|
| 522 |
+
if index_of_equals != -1:
|
| 523 |
+
key = param[:index_of_equals].strip(items_to_strip)
|
| 524 |
+
value = param[index_of_equals + 1 :].strip(items_to_strip)
|
| 525 |
+
params_dict[key.lower()] = value
|
| 526 |
+
return content_type, params_dict
|
| 527 |
+
|
| 528 |
+
|
| 529 |
+
def get_encoding_from_headers(headers):
|
| 530 |
+
"""Returns encodings from given HTTP Header Dict.
|
| 531 |
+
|
| 532 |
+
:param headers: dictionary to extract encoding from.
|
| 533 |
+
:rtype: str
|
| 534 |
+
"""
|
| 535 |
+
|
| 536 |
+
content_type = headers.get("content-type")
|
| 537 |
+
|
| 538 |
+
if not content_type:
|
| 539 |
+
return None
|
| 540 |
+
|
| 541 |
+
content_type, params = _parse_content_type_header(content_type)
|
| 542 |
+
|
| 543 |
+
if "charset" in params:
|
| 544 |
+
return params["charset"].strip("'\"")
|
| 545 |
+
|
| 546 |
+
if "text" in content_type:
|
| 547 |
+
return "ISO-8859-1"
|
| 548 |
+
|
| 549 |
+
if "application/json" in content_type:
|
| 550 |
+
# Assume UTF-8 based on RFC 4627: https://www.ietf.org/rfc/rfc4627.txt since the charset was unset
|
| 551 |
+
return "utf-8"
|
| 552 |
+
|
| 553 |
+
|
| 554 |
+
def stream_decode_response_unicode(iterator, r):
|
| 555 |
+
"""Stream decodes an iterator."""
|
| 556 |
+
|
| 557 |
+
if r.encoding is None:
|
| 558 |
+
yield from iterator
|
| 559 |
+
return
|
| 560 |
+
|
| 561 |
+
decoder = codecs.getincrementaldecoder(r.encoding)(errors="replace")
|
| 562 |
+
for chunk in iterator:
|
| 563 |
+
rv = decoder.decode(chunk)
|
| 564 |
+
if rv:
|
| 565 |
+
yield rv
|
| 566 |
+
rv = decoder.decode(b"", final=True)
|
| 567 |
+
if rv:
|
| 568 |
+
yield rv
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
def iter_slices(string, slice_length):
|
| 572 |
+
"""Iterate over slices of a string."""
|
| 573 |
+
pos = 0
|
| 574 |
+
if slice_length is None or slice_length <= 0:
|
| 575 |
+
slice_length = len(string)
|
| 576 |
+
while pos < len(string):
|
| 577 |
+
yield string[pos : pos + slice_length]
|
| 578 |
+
pos += slice_length
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
def get_unicode_from_response(r):
|
| 582 |
+
"""Returns the requested content back in unicode.
|
| 583 |
+
|
| 584 |
+
:param r: Response object to get unicode content from.
|
| 585 |
+
|
| 586 |
+
Tried:
|
| 587 |
+
|
| 588 |
+
1. charset from content-type
|
| 589 |
+
2. fall back and replace all unicode characters
|
| 590 |
+
|
| 591 |
+
:rtype: str
|
| 592 |
+
"""
|
| 593 |
+
warnings.warn(
|
| 594 |
+
(
|
| 595 |
+
"In requests 3.0, get_unicode_from_response will be removed. For "
|
| 596 |
+
"more information, please see the discussion on issue #2266. (This"
|
| 597 |
+
" warning should only appear once.)"
|
| 598 |
+
),
|
| 599 |
+
DeprecationWarning,
|
| 600 |
+
)
|
| 601 |
+
|
| 602 |
+
tried_encodings = []
|
| 603 |
+
|
| 604 |
+
# Try charset from content-type
|
| 605 |
+
encoding = get_encoding_from_headers(r.headers)
|
| 606 |
+
|
| 607 |
+
if encoding:
|
| 608 |
+
try:
|
| 609 |
+
return str(r.content, encoding)
|
| 610 |
+
except UnicodeError:
|
| 611 |
+
tried_encodings.append(encoding)
|
| 612 |
+
|
| 613 |
+
# Fall back:
|
| 614 |
+
try:
|
| 615 |
+
return str(r.content, encoding, errors="replace")
|
| 616 |
+
except TypeError:
|
| 617 |
+
return r.content
|
| 618 |
+
|
| 619 |
+
|
| 620 |
+
# The unreserved URI characters (RFC 3986)
|
| 621 |
+
UNRESERVED_SET = frozenset(
|
| 622 |
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789-._~"
|
| 623 |
+
)
|
| 624 |
+
|
| 625 |
+
|
| 626 |
+
def unquote_unreserved(uri):
|
| 627 |
+
"""Un-escape any percent-escape sequences in a URI that are unreserved
|
| 628 |
+
characters. This leaves all reserved, illegal and non-ASCII bytes encoded.
|
| 629 |
+
|
| 630 |
+
:rtype: str
|
| 631 |
+
"""
|
| 632 |
+
parts = uri.split("%")
|
| 633 |
+
for i in range(1, len(parts)):
|
| 634 |
+
h = parts[i][0:2]
|
| 635 |
+
if len(h) == 2 and h.isalnum():
|
| 636 |
+
try:
|
| 637 |
+
c = chr(int(h, 16))
|
| 638 |
+
except ValueError:
|
| 639 |
+
raise InvalidURL(f"Invalid percent-escape sequence: '{h}'")
|
| 640 |
+
|
| 641 |
+
if c in UNRESERVED_SET:
|
| 642 |
+
parts[i] = c + parts[i][2:]
|
| 643 |
+
else:
|
| 644 |
+
parts[i] = f"%{parts[i]}"
|
| 645 |
+
else:
|
| 646 |
+
parts[i] = f"%{parts[i]}"
|
| 647 |
+
return "".join(parts)
|
| 648 |
+
|
| 649 |
+
|
| 650 |
+
def requote_uri(uri):
|
| 651 |
+
"""Re-quote the given URI.
|
| 652 |
+
|
| 653 |
+
This function passes the given URI through an unquote/quote cycle to
|
| 654 |
+
ensure that it is fully and consistently quoted.
|
| 655 |
+
|
| 656 |
+
:rtype: str
|
| 657 |
+
"""
|
| 658 |
+
safe_with_percent = "!#$%&'()*+,/:;=?@[]~"
|
| 659 |
+
safe_without_percent = "!#$&'()*+,/:;=?@[]~"
|
| 660 |
+
try:
|
| 661 |
+
# Unquote only the unreserved characters
|
| 662 |
+
# Then quote only illegal characters (do not quote reserved,
|
| 663 |
+
# unreserved, or '%')
|
| 664 |
+
return quote(unquote_unreserved(uri), safe=safe_with_percent)
|
| 665 |
+
except InvalidURL:
|
| 666 |
+
# We couldn't unquote the given URI, so let's try quoting it, but
|
| 667 |
+
# there may be unquoted '%'s in the URI. We need to make sure they're
|
| 668 |
+
# properly quoted so they do not cause issues elsewhere.
|
| 669 |
+
return quote(uri, safe=safe_without_percent)
|
| 670 |
+
|
| 671 |
+
|
| 672 |
+
def address_in_network(ip, net):
|
| 673 |
+
"""This function allows you to check if an IP belongs to a network subnet
|
| 674 |
+
|
| 675 |
+
Example: returns True if ip = 192.168.1.1 and net = 192.168.1.0/24
|
| 676 |
+
returns False if ip = 192.168.1.1 and net = 192.168.100.0/24
|
| 677 |
+
|
| 678 |
+
:rtype: bool
|
| 679 |
+
"""
|
| 680 |
+
ipaddr = struct.unpack("=L", socket.inet_aton(ip))[0]
|
| 681 |
+
netaddr, bits = net.split("/")
|
| 682 |
+
netmask = struct.unpack("=L", socket.inet_aton(dotted_netmask(int(bits))))[0]
|
| 683 |
+
network = struct.unpack("=L", socket.inet_aton(netaddr))[0] & netmask
|
| 684 |
+
return (ipaddr & netmask) == (network & netmask)
|
| 685 |
+
|
| 686 |
+
|
| 687 |
+
def dotted_netmask(mask):
|
| 688 |
+
"""Converts mask from /xx format to xxx.xxx.xxx.xxx
|
| 689 |
+
|
| 690 |
+
Example: if mask is 24 function returns 255.255.255.0
|
| 691 |
+
|
| 692 |
+
:rtype: str
|
| 693 |
+
"""
|
| 694 |
+
bits = 0xFFFFFFFF ^ (1 << 32 - mask) - 1
|
| 695 |
+
return socket.inet_ntoa(struct.pack(">I", bits))
|
| 696 |
+
|
| 697 |
+
|
| 698 |
+
def is_ipv4_address(string_ip):
|
| 699 |
+
"""
|
| 700 |
+
:rtype: bool
|
| 701 |
+
"""
|
| 702 |
+
try:
|
| 703 |
+
socket.inet_aton(string_ip)
|
| 704 |
+
except OSError:
|
| 705 |
+
return False
|
| 706 |
+
return True
|
| 707 |
+
|
| 708 |
+
|
| 709 |
+
def is_valid_cidr(string_network):
|
| 710 |
+
"""
|
| 711 |
+
Very simple check of the cidr format in no_proxy variable.
|
| 712 |
+
|
| 713 |
+
:rtype: bool
|
| 714 |
+
"""
|
| 715 |
+
if string_network.count("/") == 1:
|
| 716 |
+
try:
|
| 717 |
+
mask = int(string_network.split("/")[1])
|
| 718 |
+
except ValueError:
|
| 719 |
+
return False
|
| 720 |
+
|
| 721 |
+
if mask < 1 or mask > 32:
|
| 722 |
+
return False
|
| 723 |
+
|
| 724 |
+
try:
|
| 725 |
+
socket.inet_aton(string_network.split("/")[0])
|
| 726 |
+
except OSError:
|
| 727 |
+
return False
|
| 728 |
+
else:
|
| 729 |
+
return False
|
| 730 |
+
return True
|
| 731 |
+
|
| 732 |
+
|
| 733 |
+
@contextlib.contextmanager
|
| 734 |
+
def set_environ(env_name, value):
|
| 735 |
+
"""Set the environment variable 'env_name' to 'value'
|
| 736 |
+
|
| 737 |
+
Save previous value, yield, and then restore the previous value stored in
|
| 738 |
+
the environment variable 'env_name'.
|
| 739 |
+
|
| 740 |
+
If 'value' is None, do nothing"""
|
| 741 |
+
value_changed = value is not None
|
| 742 |
+
if value_changed:
|
| 743 |
+
old_value = os.environ.get(env_name)
|
| 744 |
+
os.environ[env_name] = value
|
| 745 |
+
try:
|
| 746 |
+
yield
|
| 747 |
+
finally:
|
| 748 |
+
if value_changed:
|
| 749 |
+
if old_value is None:
|
| 750 |
+
del os.environ[env_name]
|
| 751 |
+
else:
|
| 752 |
+
os.environ[env_name] = old_value
|
| 753 |
+
|
| 754 |
+
|
| 755 |
+
def should_bypass_proxies(url, no_proxy):
|
| 756 |
+
"""
|
| 757 |
+
Returns whether we should bypass proxies or not.
|
| 758 |
+
|
| 759 |
+
:rtype: bool
|
| 760 |
+
"""
|
| 761 |
+
|
| 762 |
+
# Prioritize lowercase environment variables over uppercase
|
| 763 |
+
# to keep a consistent behaviour with other http projects (curl, wget).
|
| 764 |
+
def get_proxy(key):
|
| 765 |
+
return os.environ.get(key) or os.environ.get(key.upper())
|
| 766 |
+
|
| 767 |
+
# First check whether no_proxy is defined. If it is, check that the URL
|
| 768 |
+
# we're getting isn't in the no_proxy list.
|
| 769 |
+
no_proxy_arg = no_proxy
|
| 770 |
+
if no_proxy is None:
|
| 771 |
+
no_proxy = get_proxy("no_proxy")
|
| 772 |
+
parsed = urlparse(url)
|
| 773 |
+
|
| 774 |
+
if parsed.hostname is None:
|
| 775 |
+
# URLs don't always have hostnames, e.g. file:/// urls.
|
| 776 |
+
return True
|
| 777 |
+
|
| 778 |
+
if no_proxy:
|
| 779 |
+
# We need to check whether we match here. We need to see if we match
|
| 780 |
+
# the end of the hostname, both with and without the port.
|
| 781 |
+
no_proxy = (host for host in no_proxy.replace(" ", "").split(",") if host)
|
| 782 |
+
|
| 783 |
+
if is_ipv4_address(parsed.hostname):
|
| 784 |
+
for proxy_ip in no_proxy:
|
| 785 |
+
if is_valid_cidr(proxy_ip):
|
| 786 |
+
if address_in_network(parsed.hostname, proxy_ip):
|
| 787 |
+
return True
|
| 788 |
+
elif parsed.hostname == proxy_ip:
|
| 789 |
+
# If no_proxy ip was defined in plain IP notation instead of cidr notation &
|
| 790 |
+
# matches the IP of the index
|
| 791 |
+
return True
|
| 792 |
+
else:
|
| 793 |
+
host_with_port = parsed.hostname
|
| 794 |
+
if parsed.port:
|
| 795 |
+
host_with_port += f":{parsed.port}"
|
| 796 |
+
|
| 797 |
+
for host in no_proxy:
|
| 798 |
+
if parsed.hostname.endswith(host) or host_with_port.endswith(host):
|
| 799 |
+
# The URL does match something in no_proxy, so we don't want
|
| 800 |
+
# to apply the proxies on this URL.
|
| 801 |
+
return True
|
| 802 |
+
|
| 803 |
+
with set_environ("no_proxy", no_proxy_arg):
|
| 804 |
+
# parsed.hostname can be `None` in cases such as a file URI.
|
| 805 |
+
try:
|
| 806 |
+
bypass = proxy_bypass(parsed.hostname)
|
| 807 |
+
except (TypeError, socket.gaierror):
|
| 808 |
+
bypass = False
|
| 809 |
+
|
| 810 |
+
if bypass:
|
| 811 |
+
return True
|
| 812 |
+
|
| 813 |
+
return False
|
| 814 |
+
|
| 815 |
+
|
| 816 |
+
def get_environ_proxies(url, no_proxy=None):
|
| 817 |
+
"""
|
| 818 |
+
Return a dict of environment proxies.
|
| 819 |
+
|
| 820 |
+
:rtype: dict
|
| 821 |
+
"""
|
| 822 |
+
if should_bypass_proxies(url, no_proxy=no_proxy):
|
| 823 |
+
return {}
|
| 824 |
+
else:
|
| 825 |
+
return getproxies()
|
| 826 |
+
|
| 827 |
+
|
| 828 |
+
def select_proxy(url, proxies):
|
| 829 |
+
"""Select a proxy for the url, if applicable.
|
| 830 |
+
|
| 831 |
+
:param url: The url being for the request
|
| 832 |
+
:param proxies: A dictionary of schemes or schemes and hosts to proxy URLs
|
| 833 |
+
"""
|
| 834 |
+
proxies = proxies or {}
|
| 835 |
+
urlparts = urlparse(url)
|
| 836 |
+
if urlparts.hostname is None:
|
| 837 |
+
return proxies.get(urlparts.scheme, proxies.get("all"))
|
| 838 |
+
|
| 839 |
+
proxy_keys = [
|
| 840 |
+
urlparts.scheme + "://" + urlparts.hostname,
|
| 841 |
+
urlparts.scheme,
|
| 842 |
+
"all://" + urlparts.hostname,
|
| 843 |
+
"all",
|
| 844 |
+
]
|
| 845 |
+
proxy = None
|
| 846 |
+
for proxy_key in proxy_keys:
|
| 847 |
+
if proxy_key in proxies:
|
| 848 |
+
proxy = proxies[proxy_key]
|
| 849 |
+
break
|
| 850 |
+
|
| 851 |
+
return proxy
|
| 852 |
+
|
| 853 |
+
|
| 854 |
+
def resolve_proxies(request, proxies, trust_env=True):
|
| 855 |
+
"""This method takes proxy information from a request and configuration
|
| 856 |
+
input to resolve a mapping of target proxies. This will consider settings
|
| 857 |
+
such as NO_PROXY to strip proxy configurations.
|
| 858 |
+
|
| 859 |
+
:param request: Request or PreparedRequest
|
| 860 |
+
:param proxies: A dictionary of schemes or schemes and hosts to proxy URLs
|
| 861 |
+
:param trust_env: Boolean declaring whether to trust environment configs
|
| 862 |
+
|
| 863 |
+
:rtype: dict
|
| 864 |
+
"""
|
| 865 |
+
proxies = proxies if proxies is not None else {}
|
| 866 |
+
url = request.url
|
| 867 |
+
scheme = urlparse(url).scheme
|
| 868 |
+
no_proxy = proxies.get("no_proxy")
|
| 869 |
+
new_proxies = proxies.copy()
|
| 870 |
+
|
| 871 |
+
if trust_env and not should_bypass_proxies(url, no_proxy=no_proxy):
|
| 872 |
+
environ_proxies = get_environ_proxies(url, no_proxy=no_proxy)
|
| 873 |
+
|
| 874 |
+
proxy = environ_proxies.get(scheme, environ_proxies.get("all"))
|
| 875 |
+
|
| 876 |
+
if proxy:
|
| 877 |
+
new_proxies.setdefault(scheme, proxy)
|
| 878 |
+
return new_proxies
|
| 879 |
+
|
| 880 |
+
|
| 881 |
+
def default_user_agent(name="python-requests"):
|
| 882 |
+
"""
|
| 883 |
+
Return a string representing the default user agent.
|
| 884 |
+
|
| 885 |
+
:rtype: str
|
| 886 |
+
"""
|
| 887 |
+
return f"{name}/{__version__}"
|
| 888 |
+
|
| 889 |
+
|
| 890 |
+
def default_headers():
|
| 891 |
+
"""
|
| 892 |
+
:rtype: requests.structures.CaseInsensitiveDict
|
| 893 |
+
"""
|
| 894 |
+
return CaseInsensitiveDict(
|
| 895 |
+
{
|
| 896 |
+
"User-Agent": default_user_agent(),
|
| 897 |
+
"Accept-Encoding": DEFAULT_ACCEPT_ENCODING,
|
| 898 |
+
"Accept": "*/*",
|
| 899 |
+
"Connection": "keep-alive",
|
| 900 |
+
}
|
| 901 |
+
)
|
| 902 |
+
|
| 903 |
+
|
| 904 |
+
def parse_header_links(value):
|
| 905 |
+
"""Return a list of parsed link headers proxies.
|
| 906 |
+
|
| 907 |
+
i.e. Link: <http:/.../front.jpeg>; rel=front; type="image/jpeg",<http://.../back.jpeg>; rel=back;type="image/jpeg"
|
| 908 |
+
|
| 909 |
+
:rtype: list
|
| 910 |
+
"""
|
| 911 |
+
|
| 912 |
+
links = []
|
| 913 |
+
|
| 914 |
+
replace_chars = " '\""
|
| 915 |
+
|
| 916 |
+
value = value.strip(replace_chars)
|
| 917 |
+
if not value:
|
| 918 |
+
return links
|
| 919 |
+
|
| 920 |
+
for val in re.split(", *<", value):
|
| 921 |
+
try:
|
| 922 |
+
url, params = val.split(";", 1)
|
| 923 |
+
except ValueError:
|
| 924 |
+
url, params = val, ""
|
| 925 |
+
|
| 926 |
+
link = {"url": url.strip("<> '\"")}
|
| 927 |
+
|
| 928 |
+
for param in params.split(";"):
|
| 929 |
+
try:
|
| 930 |
+
key, value = param.split("=")
|
| 931 |
+
except ValueError:
|
| 932 |
+
break
|
| 933 |
+
|
| 934 |
+
link[key.strip(replace_chars)] = value.strip(replace_chars)
|
| 935 |
+
|
| 936 |
+
links.append(link)
|
| 937 |
+
|
| 938 |
+
return links
|
| 939 |
+
|
| 940 |
+
|
| 941 |
+
# Null bytes; no need to recreate these on each call to guess_json_utf
|
| 942 |
+
_null = "\x00".encode("ascii") # encoding to ASCII for Python 3
|
| 943 |
+
_null2 = _null * 2
|
| 944 |
+
_null3 = _null * 3
|
| 945 |
+
|
| 946 |
+
|
| 947 |
+
def guess_json_utf(data):
|
| 948 |
+
"""
|
| 949 |
+
:rtype: str
|
| 950 |
+
"""
|
| 951 |
+
# JSON always starts with two ASCII characters, so detection is as
|
| 952 |
+
# easy as counting the nulls and from their location and count
|
| 953 |
+
# determine the encoding. Also detect a BOM, if present.
|
| 954 |
+
sample = data[:4]
|
| 955 |
+
if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE):
|
| 956 |
+
return "utf-32" # BOM included
|
| 957 |
+
if sample[:3] == codecs.BOM_UTF8:
|
| 958 |
+
return "utf-8-sig" # BOM included, MS style (discouraged)
|
| 959 |
+
if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE):
|
| 960 |
+
return "utf-16" # BOM included
|
| 961 |
+
nullcount = sample.count(_null)
|
| 962 |
+
if nullcount == 0:
|
| 963 |
+
return "utf-8"
|
| 964 |
+
if nullcount == 2:
|
| 965 |
+
if sample[::2] == _null2: # 1st and 3rd are null
|
| 966 |
+
return "utf-16-be"
|
| 967 |
+
if sample[1::2] == _null2: # 2nd and 4th are null
|
| 968 |
+
return "utf-16-le"
|
| 969 |
+
# Did not detect 2 valid UTF-16 ascii-range characters
|
| 970 |
+
if nullcount == 3:
|
| 971 |
+
if sample[:3] == _null3:
|
| 972 |
+
return "utf-32-be"
|
| 973 |
+
if sample[1:] == _null3:
|
| 974 |
+
return "utf-32-le"
|
| 975 |
+
# Did not detect a valid UTF-32 ascii-range character
|
| 976 |
+
return None
|
| 977 |
+
|
| 978 |
+
|
| 979 |
+
def prepend_scheme_if_needed(url, new_scheme):
|
| 980 |
+
"""Given a URL that may or may not have a scheme, prepend the given scheme.
|
| 981 |
+
Does not replace a present scheme with the one provided as an argument.
|
| 982 |
+
|
| 983 |
+
:rtype: str
|
| 984 |
+
"""
|
| 985 |
+
parsed = parse_url(url)
|
| 986 |
+
scheme, auth, host, port, path, query, fragment = parsed
|
| 987 |
+
|
| 988 |
+
# A defect in urlparse determines that there isn't a netloc present in some
|
| 989 |
+
# urls. We previously assumed parsing was overly cautious, and swapped the
|
| 990 |
+
# netloc and path. Due to a lack of tests on the original defect, this is
|
| 991 |
+
# maintained with parse_url for backwards compatibility.
|
| 992 |
+
netloc = parsed.netloc
|
| 993 |
+
if not netloc:
|
| 994 |
+
netloc, path = path, netloc
|
| 995 |
+
|
| 996 |
+
if auth:
|
| 997 |
+
# parse_url doesn't provide the netloc with auth
|
| 998 |
+
# so we'll add it ourselves.
|
| 999 |
+
netloc = "@".join([auth, netloc])
|
| 1000 |
+
if scheme is None:
|
| 1001 |
+
scheme = new_scheme
|
| 1002 |
+
if path is None:
|
| 1003 |
+
path = ""
|
| 1004 |
+
|
| 1005 |
+
return urlunparse((scheme, netloc, path, "", query, fragment))
|
| 1006 |
+
|
| 1007 |
+
|
| 1008 |
+
def get_auth_from_url(url):
|
| 1009 |
+
"""Given a url with authentication components, extract them into a tuple of
|
| 1010 |
+
username,password.
|
| 1011 |
+
|
| 1012 |
+
:rtype: (str,str)
|
| 1013 |
+
"""
|
| 1014 |
+
parsed = urlparse(url)
|
| 1015 |
+
|
| 1016 |
+
try:
|
| 1017 |
+
auth = (unquote(parsed.username), unquote(parsed.password))
|
| 1018 |
+
except (AttributeError, TypeError):
|
| 1019 |
+
auth = ("", "")
|
| 1020 |
+
|
| 1021 |
+
return auth
|
| 1022 |
+
|
| 1023 |
+
|
| 1024 |
+
def check_header_validity(header):
|
| 1025 |
+
"""Verifies that header parts don't contain leading whitespace
|
| 1026 |
+
reserved characters, or return characters.
|
| 1027 |
+
|
| 1028 |
+
:param header: tuple, in the format (name, value).
|
| 1029 |
+
"""
|
| 1030 |
+
name, value = header
|
| 1031 |
+
_validate_header_part(header, name, 0)
|
| 1032 |
+
_validate_header_part(header, value, 1)
|
| 1033 |
+
|
| 1034 |
+
|
| 1035 |
+
def _validate_header_part(header, header_part, header_validator_index):
|
| 1036 |
+
if isinstance(header_part, str):
|
| 1037 |
+
validator = _HEADER_VALIDATORS_STR[header_validator_index]
|
| 1038 |
+
elif isinstance(header_part, bytes):
|
| 1039 |
+
validator = _HEADER_VALIDATORS_BYTE[header_validator_index]
|
| 1040 |
+
else:
|
| 1041 |
+
raise InvalidHeader(
|
| 1042 |
+
f"Header part ({header_part!r}) from {header} "
|
| 1043 |
+
f"must be of type str or bytes, not {type(header_part)}"
|
| 1044 |
+
)
|
| 1045 |
+
|
| 1046 |
+
if not validator.match(header_part):
|
| 1047 |
+
header_kind = "name" if header_validator_index == 0 else "value"
|
| 1048 |
+
raise InvalidHeader(
|
| 1049 |
+
f"Invalid leading whitespace, reserved character(s), or return "
|
| 1050 |
+
f"character(s) in header {header_kind}: {header_part!r}"
|
| 1051 |
+
)
|
| 1052 |
+
|
| 1053 |
+
|
| 1054 |
+
def urldefragauth(url):
|
| 1055 |
+
"""
|
| 1056 |
+
Given a url remove the fragment and the authentication part.
|
| 1057 |
+
|
| 1058 |
+
:rtype: str
|
| 1059 |
+
"""
|
| 1060 |
+
scheme, netloc, path, params, query, fragment = urlparse(url)
|
| 1061 |
+
|
| 1062 |
+
# see func:`prepend_scheme_if_needed`
|
| 1063 |
+
if not netloc:
|
| 1064 |
+
netloc, path = path, netloc
|
| 1065 |
+
|
| 1066 |
+
netloc = netloc.rsplit("@", 1)[-1]
|
| 1067 |
+
|
| 1068 |
+
return urlunparse((scheme, netloc, path, params, query, ""))
|
| 1069 |
+
|
| 1070 |
+
|
| 1071 |
+
def rewind_body(prepared_request):
|
| 1072 |
+
"""Move file pointer back to its recorded starting position
|
| 1073 |
+
so it can be read again on redirect.
|
| 1074 |
+
"""
|
| 1075 |
+
body_seek = getattr(prepared_request.body, "seek", None)
|
| 1076 |
+
if body_seek is not None and isinstance(
|
| 1077 |
+
prepared_request._body_position, integer_types
|
| 1078 |
+
):
|
| 1079 |
+
try:
|
| 1080 |
+
body_seek(prepared_request._body_position)
|
| 1081 |
+
except OSError:
|
| 1082 |
+
raise UnrewindableBodyError(
|
| 1083 |
+
"An error occurred when rewinding request body for redirect."
|
| 1084 |
+
)
|
| 1085 |
+
else:
|
| 1086 |
+
raise UnrewindableBodyError("Unable to rewind request body for redirect.")
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__all__ = [
|
| 2 |
+
"AbstractProvider",
|
| 3 |
+
"AbstractResolver",
|
| 4 |
+
"BaseReporter",
|
| 5 |
+
"InconsistentCandidate",
|
| 6 |
+
"RequirementsConflicted",
|
| 7 |
+
"ResolutionError",
|
| 8 |
+
"ResolutionImpossible",
|
| 9 |
+
"ResolutionTooDeep",
|
| 10 |
+
"Resolver",
|
| 11 |
+
"__version__",
|
| 12 |
+
]
|
| 13 |
+
|
| 14 |
+
__version__ = "1.2.0"
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
from .providers import AbstractProvider
|
| 18 |
+
from .reporters import BaseReporter
|
| 19 |
+
from .resolvers import (
|
| 20 |
+
AbstractResolver,
|
| 21 |
+
InconsistentCandidate,
|
| 22 |
+
RequirementsConflicted,
|
| 23 |
+
ResolutionError,
|
| 24 |
+
ResolutionImpossible,
|
| 25 |
+
ResolutionTooDeep,
|
| 26 |
+
Resolver,
|
| 27 |
+
)
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/providers.py
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import (
|
| 4 |
+
TYPE_CHECKING,
|
| 5 |
+
Generic,
|
| 6 |
+
Iterable,
|
| 7 |
+
Iterator,
|
| 8 |
+
Mapping,
|
| 9 |
+
Sequence,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
from .structs import CT, KT, RT, Matches, RequirementInformation
|
| 13 |
+
|
| 14 |
+
if TYPE_CHECKING:
|
| 15 |
+
from typing import Any, Protocol
|
| 16 |
+
|
| 17 |
+
class Preference(Protocol):
|
| 18 |
+
def __lt__(self, __other: Any) -> bool: ...
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class AbstractProvider(Generic[RT, CT, KT]):
|
| 22 |
+
"""Delegate class to provide the required interface for the resolver."""
|
| 23 |
+
|
| 24 |
+
def identify(self, requirement_or_candidate: RT | CT) -> KT:
|
| 25 |
+
"""Given a requirement or candidate, return an identifier for it.
|
| 26 |
+
|
| 27 |
+
This is used to identify, e.g. whether two requirements
|
| 28 |
+
should have their specifier parts merged or a candidate matches a
|
| 29 |
+
requirement via ``find_matches()``.
|
| 30 |
+
"""
|
| 31 |
+
raise NotImplementedError
|
| 32 |
+
|
| 33 |
+
def get_preference(
|
| 34 |
+
self,
|
| 35 |
+
identifier: KT,
|
| 36 |
+
resolutions: Mapping[KT, CT],
|
| 37 |
+
candidates: Mapping[KT, Iterator[CT]],
|
| 38 |
+
information: Mapping[KT, Iterator[RequirementInformation[RT, CT]]],
|
| 39 |
+
backtrack_causes: Sequence[RequirementInformation[RT, CT]],
|
| 40 |
+
) -> Preference:
|
| 41 |
+
"""Produce a sort key for given requirement based on preference.
|
| 42 |
+
|
| 43 |
+
As this is a sort key it will be called O(n) times per backtrack
|
| 44 |
+
step, where n is the number of `identifier`s, if you have a check
|
| 45 |
+
which is expensive in some sense. E.g. It needs to make O(n) checks
|
| 46 |
+
per call or takes significant wall clock time, consider using
|
| 47 |
+
`narrow_requirement_selection` to filter the `identifier`s, which
|
| 48 |
+
is applied before this sort key is called.
|
| 49 |
+
|
| 50 |
+
The preference is defined as "I think this requirement should be
|
| 51 |
+
resolved first". The lower the return value is, the more preferred
|
| 52 |
+
this group of arguments is.
|
| 53 |
+
|
| 54 |
+
:param identifier: An identifier as returned by ``identify()``. This
|
| 55 |
+
identifies the requirement being considered.
|
| 56 |
+
:param resolutions: Mapping of candidates currently pinned by the
|
| 57 |
+
resolver. Each key is an identifier, and the value is a candidate.
|
| 58 |
+
The candidate may conflict with requirements from ``information``.
|
| 59 |
+
:param candidates: Mapping of each dependency's possible candidates.
|
| 60 |
+
Each value is an iterator of candidates.
|
| 61 |
+
:param information: Mapping of requirement information of each package.
|
| 62 |
+
Each value is an iterator of *requirement information*.
|
| 63 |
+
:param backtrack_causes: Sequence of *requirement information* that are
|
| 64 |
+
the requirements that caused the resolver to most recently
|
| 65 |
+
backtrack.
|
| 66 |
+
|
| 67 |
+
A *requirement information* instance is a named tuple with two members:
|
| 68 |
+
|
| 69 |
+
* ``requirement`` specifies a requirement contributing to the current
|
| 70 |
+
list of candidates.
|
| 71 |
+
* ``parent`` specifies the candidate that provides (depended on) the
|
| 72 |
+
requirement, or ``None`` to indicate a root requirement.
|
| 73 |
+
|
| 74 |
+
The preference could depend on various issues, including (not
|
| 75 |
+
necessarily in this order):
|
| 76 |
+
|
| 77 |
+
* Is this package pinned in the current resolution result?
|
| 78 |
+
* How relaxed is the requirement? Stricter ones should probably be
|
| 79 |
+
worked on first? (I don't know, actually.)
|
| 80 |
+
* How many possibilities are there to satisfy this requirement? Those
|
| 81 |
+
with few left should likely be worked on first, I guess?
|
| 82 |
+
* Are there any known conflicts for this requirement? We should
|
| 83 |
+
probably work on those with the most known conflicts.
|
| 84 |
+
|
| 85 |
+
A sortable value should be returned (this will be used as the ``key``
|
| 86 |
+
parameter of the built-in sorting function). The smaller the value is,
|
| 87 |
+
the more preferred this requirement is (i.e. the sorting function
|
| 88 |
+
is called with ``reverse=False``).
|
| 89 |
+
"""
|
| 90 |
+
raise NotImplementedError
|
| 91 |
+
|
| 92 |
+
def find_matches(
|
| 93 |
+
self,
|
| 94 |
+
identifier: KT,
|
| 95 |
+
requirements: Mapping[KT, Iterator[RT]],
|
| 96 |
+
incompatibilities: Mapping[KT, Iterator[CT]],
|
| 97 |
+
) -> Matches[CT]:
|
| 98 |
+
"""Find all possible candidates that satisfy the given constraints.
|
| 99 |
+
|
| 100 |
+
:param identifier: An identifier as returned by ``identify()``. All
|
| 101 |
+
candidates returned by this method should produce the same
|
| 102 |
+
identifier.
|
| 103 |
+
:param requirements: A mapping of requirements that all returned
|
| 104 |
+
candidates must satisfy. Each key is an identifier, and the value
|
| 105 |
+
an iterator of requirements for that dependency.
|
| 106 |
+
:param incompatibilities: A mapping of known incompatibile candidates of
|
| 107 |
+
each dependency. Each key is an identifier, and the value an
|
| 108 |
+
iterator of incompatibilities known to the resolver. All
|
| 109 |
+
incompatibilities *must* be excluded from the return value.
|
| 110 |
+
|
| 111 |
+
This should try to get candidates based on the requirements' types.
|
| 112 |
+
For VCS, local, and archive requirements, the one-and-only match is
|
| 113 |
+
returned, and for a "named" requirement, the index(es) should be
|
| 114 |
+
consulted to find concrete candidates for this requirement.
|
| 115 |
+
|
| 116 |
+
The return value should produce candidates ordered by preference; the
|
| 117 |
+
most preferred candidate should come first. The return type may be one
|
| 118 |
+
of the following:
|
| 119 |
+
|
| 120 |
+
* A callable that returns an iterator that yields candidates.
|
| 121 |
+
* An collection of candidates.
|
| 122 |
+
* An iterable of candidates. This will be consumed immediately into a
|
| 123 |
+
list of candidates.
|
| 124 |
+
"""
|
| 125 |
+
raise NotImplementedError
|
| 126 |
+
|
| 127 |
+
def is_satisfied_by(self, requirement: RT, candidate: CT) -> bool:
|
| 128 |
+
"""Whether the given requirement can be satisfied by a candidate.
|
| 129 |
+
|
| 130 |
+
The candidate is guaranteed to have been generated from the
|
| 131 |
+
requirement.
|
| 132 |
+
|
| 133 |
+
A boolean should be returned to indicate whether ``candidate`` is a
|
| 134 |
+
viable solution to the requirement.
|
| 135 |
+
"""
|
| 136 |
+
raise NotImplementedError
|
| 137 |
+
|
| 138 |
+
def get_dependencies(self, candidate: CT) -> Iterable[RT]:
|
| 139 |
+
"""Get dependencies of a candidate.
|
| 140 |
+
|
| 141 |
+
This should return a collection of requirements that `candidate`
|
| 142 |
+
specifies as its dependencies.
|
| 143 |
+
"""
|
| 144 |
+
raise NotImplementedError
|
| 145 |
+
|
| 146 |
+
def narrow_requirement_selection(
|
| 147 |
+
self,
|
| 148 |
+
identifiers: Iterable[KT],
|
| 149 |
+
resolutions: Mapping[KT, CT],
|
| 150 |
+
candidates: Mapping[KT, Iterator[CT]],
|
| 151 |
+
information: Mapping[KT, Iterator[RequirementInformation[RT, CT]]],
|
| 152 |
+
backtrack_causes: Sequence[RequirementInformation[RT, CT]],
|
| 153 |
+
) -> Iterable[KT]:
|
| 154 |
+
"""
|
| 155 |
+
An optional method to narrow the selection of requirements being
|
| 156 |
+
considered during resolution. This method is called O(1) time per
|
| 157 |
+
backtrack step.
|
| 158 |
+
|
| 159 |
+
:param identifiers: An iterable of `identifiers` as returned by
|
| 160 |
+
``identify()``. These identify all requirements currently being
|
| 161 |
+
considered.
|
| 162 |
+
:param resolutions: A mapping of candidates currently pinned by the
|
| 163 |
+
resolver. Each key is an identifier, and the value is a candidate
|
| 164 |
+
that may conflict with requirements from ``information``.
|
| 165 |
+
:param candidates: A mapping of each dependency's possible candidates.
|
| 166 |
+
Each value is an iterator of candidates.
|
| 167 |
+
:param information: A mapping of requirement information for each package.
|
| 168 |
+
Each value is an iterator of *requirement information*.
|
| 169 |
+
:param backtrack_causes: A sequence of *requirement information* that are
|
| 170 |
+
the requirements causing the resolver to most recently
|
| 171 |
+
backtrack.
|
| 172 |
+
|
| 173 |
+
A *requirement information* instance is a named tuple with two members:
|
| 174 |
+
|
| 175 |
+
* ``requirement`` specifies a requirement contributing to the current
|
| 176 |
+
list of candidates.
|
| 177 |
+
* ``parent`` specifies the candidate that provides (is depended on for)
|
| 178 |
+
the requirement, or ``None`` to indicate a root requirement.
|
| 179 |
+
|
| 180 |
+
Must return a non-empty subset of `identifiers`, with the default
|
| 181 |
+
implementation being to return `identifiers` unchanged. Those `identifiers`
|
| 182 |
+
will then be passed to the sort key `get_preference` to pick the most
|
| 183 |
+
prefered requirement to attempt to pin, unless `narrow_requirement_selection`
|
| 184 |
+
returns only 1 requirement, in which case that will be used without
|
| 185 |
+
calling the sort key `get_preference`.
|
| 186 |
+
|
| 187 |
+
This method is designed to be used by the provider to optimize the
|
| 188 |
+
dependency resolution, e.g. if a check cost is O(m) and it can be done
|
| 189 |
+
against all identifiers at once then filtering the requirement selection
|
| 190 |
+
here will cost O(m) but making it part of the sort key in `get_preference`
|
| 191 |
+
will cost O(m*n), where n is the number of `identifiers`.
|
| 192 |
+
|
| 193 |
+
Returns:
|
| 194 |
+
Iterable[KT]: A non-empty subset of `identifiers`.
|
| 195 |
+
"""
|
| 196 |
+
return identifiers
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/py.typed
ADDED
|
File without changes
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/reporters.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import TYPE_CHECKING, Collection, Generic
|
| 4 |
+
|
| 5 |
+
from .structs import CT, KT, RT, RequirementInformation, State
|
| 6 |
+
|
| 7 |
+
if TYPE_CHECKING:
|
| 8 |
+
from .resolvers import Criterion
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class BaseReporter(Generic[RT, CT, KT]):
|
| 12 |
+
"""Delegate class to provide progress reporting for the resolver."""
|
| 13 |
+
|
| 14 |
+
def starting(self) -> None:
|
| 15 |
+
"""Called before the resolution actually starts."""
|
| 16 |
+
|
| 17 |
+
def starting_round(self, index: int) -> None:
|
| 18 |
+
"""Called before each round of resolution starts.
|
| 19 |
+
|
| 20 |
+
The index is zero-based.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
def ending_round(self, index: int, state: State[RT, CT, KT]) -> None:
|
| 24 |
+
"""Called before each round of resolution ends.
|
| 25 |
+
|
| 26 |
+
This is NOT called if the resolution ends at this round. Use `ending`
|
| 27 |
+
if you want to report finalization. The index is zero-based.
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
def ending(self, state: State[RT, CT, KT]) -> None:
|
| 31 |
+
"""Called before the resolution ends successfully."""
|
| 32 |
+
|
| 33 |
+
def adding_requirement(self, requirement: RT, parent: CT | None) -> None:
|
| 34 |
+
"""Called when adding a new requirement into the resolve criteria.
|
| 35 |
+
|
| 36 |
+
:param requirement: The additional requirement to be applied to filter
|
| 37 |
+
the available candidaites.
|
| 38 |
+
:param parent: The candidate that requires ``requirement`` as a
|
| 39 |
+
dependency, or None if ``requirement`` is one of the root
|
| 40 |
+
requirements passed in from ``Resolver.resolve()``.
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
def resolving_conflicts(
|
| 44 |
+
self, causes: Collection[RequirementInformation[RT, CT]]
|
| 45 |
+
) -> None:
|
| 46 |
+
"""Called when starting to attempt requirement conflict resolution.
|
| 47 |
+
|
| 48 |
+
:param causes: The information on the collision that caused the backtracking.
|
| 49 |
+
"""
|
| 50 |
+
|
| 51 |
+
def rejecting_candidate(self, criterion: Criterion[RT, CT], candidate: CT) -> None:
|
| 52 |
+
"""Called when rejecting a candidate during backtracking."""
|
| 53 |
+
|
| 54 |
+
def pinning(self, candidate: CT) -> None:
|
| 55 |
+
"""Called when adding a candidate to the potential solution."""
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from ..structs import RequirementInformation
|
| 2 |
+
from .abstract import AbstractResolver, Result
|
| 3 |
+
from .criterion import Criterion
|
| 4 |
+
from .exceptions import (
|
| 5 |
+
InconsistentCandidate,
|
| 6 |
+
RequirementsConflicted,
|
| 7 |
+
ResolutionError,
|
| 8 |
+
ResolutionImpossible,
|
| 9 |
+
ResolutionTooDeep,
|
| 10 |
+
ResolverException,
|
| 11 |
+
)
|
| 12 |
+
from .resolution import Resolution, Resolver
|
| 13 |
+
|
| 14 |
+
__all__ = [
|
| 15 |
+
"AbstractResolver",
|
| 16 |
+
"Criterion",
|
| 17 |
+
"InconsistentCandidate",
|
| 18 |
+
"RequirementInformation",
|
| 19 |
+
"RequirementsConflicted",
|
| 20 |
+
"Resolution",
|
| 21 |
+
"ResolutionError",
|
| 22 |
+
"ResolutionImpossible",
|
| 23 |
+
"ResolutionTooDeep",
|
| 24 |
+
"Resolver",
|
| 25 |
+
"ResolverException",
|
| 26 |
+
"Result",
|
| 27 |
+
]
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/abstract.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections
|
| 4 |
+
from typing import TYPE_CHECKING, Any, Generic, Iterable, Mapping, NamedTuple
|
| 5 |
+
|
| 6 |
+
from ..structs import CT, KT, RT, DirectedGraph
|
| 7 |
+
|
| 8 |
+
if TYPE_CHECKING:
|
| 9 |
+
from ..providers import AbstractProvider
|
| 10 |
+
from ..reporters import BaseReporter
|
| 11 |
+
from .criterion import Criterion
|
| 12 |
+
|
| 13 |
+
class Result(NamedTuple, Generic[RT, CT, KT]):
|
| 14 |
+
mapping: Mapping[KT, CT]
|
| 15 |
+
graph: DirectedGraph[KT | None]
|
| 16 |
+
criteria: Mapping[KT, Criterion[RT, CT]]
|
| 17 |
+
|
| 18 |
+
else:
|
| 19 |
+
Result = collections.namedtuple("Result", ["mapping", "graph", "criteria"])
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class AbstractResolver(Generic[RT, CT, KT]):
|
| 23 |
+
"""The thing that performs the actual resolution work."""
|
| 24 |
+
|
| 25 |
+
base_exception = Exception
|
| 26 |
+
|
| 27 |
+
def __init__(
|
| 28 |
+
self,
|
| 29 |
+
provider: AbstractProvider[RT, CT, KT],
|
| 30 |
+
reporter: BaseReporter[RT, CT, KT],
|
| 31 |
+
) -> None:
|
| 32 |
+
self.provider = provider
|
| 33 |
+
self.reporter = reporter
|
| 34 |
+
|
| 35 |
+
def resolve(self, requirements: Iterable[RT], **kwargs: Any) -> Result[RT, CT, KT]:
|
| 36 |
+
"""Take a collection of constraints, spit out the resolution result.
|
| 37 |
+
|
| 38 |
+
This returns a representation of the final resolution state, with one
|
| 39 |
+
guarenteed attribute ``mapping`` that contains resolved candidates as
|
| 40 |
+
values. The keys are their respective identifiers.
|
| 41 |
+
|
| 42 |
+
:param requirements: A collection of constraints.
|
| 43 |
+
:param kwargs: Additional keyword arguments that subclasses may accept.
|
| 44 |
+
|
| 45 |
+
:raises: ``self.base_exception`` or its subclass.
|
| 46 |
+
"""
|
| 47 |
+
raise NotImplementedError
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/criterion.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import Collection, Generic, Iterable, Iterator
|
| 4 |
+
|
| 5 |
+
from ..structs import CT, RT, RequirementInformation
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class Criterion(Generic[RT, CT]):
|
| 9 |
+
"""Representation of possible resolution results of a package.
|
| 10 |
+
|
| 11 |
+
This holds three attributes:
|
| 12 |
+
|
| 13 |
+
* `information` is a collection of `RequirementInformation` pairs.
|
| 14 |
+
Each pair is a requirement contributing to this criterion, and the
|
| 15 |
+
candidate that provides the requirement.
|
| 16 |
+
* `incompatibilities` is a collection of all known not-to-work candidates
|
| 17 |
+
to exclude from consideration.
|
| 18 |
+
* `candidates` is a collection containing all possible candidates deducted
|
| 19 |
+
from the union of contributing requirements and known incompatibilities.
|
| 20 |
+
It should never be empty, except when the criterion is an attribute of a
|
| 21 |
+
raised `RequirementsConflicted` (in which case it is always empty).
|
| 22 |
+
|
| 23 |
+
.. note::
|
| 24 |
+
This class is intended to be externally immutable. **Do not** mutate
|
| 25 |
+
any of its attribute containers.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
def __init__(
|
| 29 |
+
self,
|
| 30 |
+
candidates: Iterable[CT],
|
| 31 |
+
information: Collection[RequirementInformation[RT, CT]],
|
| 32 |
+
incompatibilities: Collection[CT],
|
| 33 |
+
) -> None:
|
| 34 |
+
self.candidates = candidates
|
| 35 |
+
self.information = information
|
| 36 |
+
self.incompatibilities = incompatibilities
|
| 37 |
+
|
| 38 |
+
def __repr__(self) -> str:
|
| 39 |
+
requirements = ", ".join(
|
| 40 |
+
f"({req!r}, via={parent!r})" for req, parent in self.information
|
| 41 |
+
)
|
| 42 |
+
return f"Criterion({requirements})"
|
| 43 |
+
|
| 44 |
+
def iter_requirement(self) -> Iterator[RT]:
|
| 45 |
+
return (i.requirement for i in self.information)
|
| 46 |
+
|
| 47 |
+
def iter_parent(self) -> Iterator[CT | None]:
|
| 48 |
+
return (i.parent for i in self.information)
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/exceptions.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import TYPE_CHECKING, Collection, Generic
|
| 4 |
+
|
| 5 |
+
from ..structs import CT, RT, RequirementInformation
|
| 6 |
+
|
| 7 |
+
if TYPE_CHECKING:
|
| 8 |
+
from .criterion import Criterion
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class ResolverException(Exception):
|
| 12 |
+
"""A base class for all exceptions raised by this module.
|
| 13 |
+
|
| 14 |
+
Exceptions derived by this class should all be handled in this module. Any
|
| 15 |
+
bubbling pass the resolver should be treated as a bug.
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class RequirementsConflicted(ResolverException, Generic[RT, CT]):
|
| 20 |
+
def __init__(self, criterion: Criterion[RT, CT]) -> None:
|
| 21 |
+
super().__init__(criterion)
|
| 22 |
+
self.criterion = criterion
|
| 23 |
+
|
| 24 |
+
def __str__(self) -> str:
|
| 25 |
+
return "Requirements conflict: {}".format(
|
| 26 |
+
", ".join(repr(r) for r in self.criterion.iter_requirement()),
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class InconsistentCandidate(ResolverException, Generic[RT, CT]):
|
| 31 |
+
def __init__(self, candidate: CT, criterion: Criterion[RT, CT]):
|
| 32 |
+
super().__init__(candidate, criterion)
|
| 33 |
+
self.candidate = candidate
|
| 34 |
+
self.criterion = criterion
|
| 35 |
+
|
| 36 |
+
def __str__(self) -> str:
|
| 37 |
+
return "Provided candidate {!r} does not satisfy {}".format(
|
| 38 |
+
self.candidate,
|
| 39 |
+
", ".join(repr(r) for r in self.criterion.iter_requirement()),
|
| 40 |
+
)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class ResolutionError(ResolverException):
|
| 44 |
+
pass
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class ResolutionImpossible(ResolutionError, Generic[RT, CT]):
|
| 48 |
+
def __init__(self, causes: Collection[RequirementInformation[RT, CT]]):
|
| 49 |
+
super().__init__(causes)
|
| 50 |
+
# causes is a list of RequirementInformation objects
|
| 51 |
+
self.causes = causes
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
class ResolutionTooDeep(ResolutionError):
|
| 55 |
+
def __init__(self, round_count: int) -> None:
|
| 56 |
+
super().__init__(round_count)
|
| 57 |
+
self.round_count = round_count
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/resolvers/resolution.py
ADDED
|
@@ -0,0 +1,622 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import collections
|
| 4 |
+
import itertools
|
| 5 |
+
import operator
|
| 6 |
+
from typing import TYPE_CHECKING, Generic
|
| 7 |
+
|
| 8 |
+
from ..structs import (
|
| 9 |
+
CT,
|
| 10 |
+
KT,
|
| 11 |
+
RT,
|
| 12 |
+
DirectedGraph,
|
| 13 |
+
IterableView,
|
| 14 |
+
IteratorMapping,
|
| 15 |
+
RequirementInformation,
|
| 16 |
+
State,
|
| 17 |
+
build_iter_view,
|
| 18 |
+
)
|
| 19 |
+
from .abstract import AbstractResolver, Result
|
| 20 |
+
from .criterion import Criterion
|
| 21 |
+
from .exceptions import (
|
| 22 |
+
InconsistentCandidate,
|
| 23 |
+
RequirementsConflicted,
|
| 24 |
+
ResolutionImpossible,
|
| 25 |
+
ResolutionTooDeep,
|
| 26 |
+
ResolverException,
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
if TYPE_CHECKING:
|
| 30 |
+
from collections.abc import Collection, Iterable, Mapping
|
| 31 |
+
|
| 32 |
+
from ..providers import AbstractProvider, Preference
|
| 33 |
+
from ..reporters import BaseReporter
|
| 34 |
+
|
| 35 |
+
_OPTIMISTIC_BACKJUMPING_RATIO: float = 0.1
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _build_result(state: State[RT, CT, KT]) -> Result[RT, CT, KT]:
|
| 39 |
+
mapping = state.mapping
|
| 40 |
+
all_keys: dict[int, KT | None] = {id(v): k for k, v in mapping.items()}
|
| 41 |
+
all_keys[id(None)] = None
|
| 42 |
+
|
| 43 |
+
graph: DirectedGraph[KT | None] = DirectedGraph()
|
| 44 |
+
graph.add(None) # Sentinel as root dependencies' parent.
|
| 45 |
+
|
| 46 |
+
connected: set[KT | None] = {None}
|
| 47 |
+
for key, criterion in state.criteria.items():
|
| 48 |
+
if not _has_route_to_root(state.criteria, key, all_keys, connected):
|
| 49 |
+
continue
|
| 50 |
+
if key not in graph:
|
| 51 |
+
graph.add(key)
|
| 52 |
+
for p in criterion.iter_parent():
|
| 53 |
+
try:
|
| 54 |
+
pkey = all_keys[id(p)]
|
| 55 |
+
except KeyError:
|
| 56 |
+
continue
|
| 57 |
+
if pkey not in graph:
|
| 58 |
+
graph.add(pkey)
|
| 59 |
+
graph.connect(pkey, key)
|
| 60 |
+
|
| 61 |
+
return Result(
|
| 62 |
+
mapping={k: v for k, v in mapping.items() if k in connected},
|
| 63 |
+
graph=graph,
|
| 64 |
+
criteria=state.criteria,
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class Resolution(Generic[RT, CT, KT]):
|
| 69 |
+
"""Stateful resolution object.
|
| 70 |
+
|
| 71 |
+
This is designed as a one-off object that holds information to kick start
|
| 72 |
+
the resolution process, and holds the results afterwards.
|
| 73 |
+
"""
|
| 74 |
+
|
| 75 |
+
def __init__(
|
| 76 |
+
self,
|
| 77 |
+
provider: AbstractProvider[RT, CT, KT],
|
| 78 |
+
reporter: BaseReporter[RT, CT, KT],
|
| 79 |
+
) -> None:
|
| 80 |
+
self._p = provider
|
| 81 |
+
self._r = reporter
|
| 82 |
+
self._states: list[State[RT, CT, KT]] = []
|
| 83 |
+
|
| 84 |
+
# Optimistic backjumping variables
|
| 85 |
+
self._optimistic_backjumping_ratio = _OPTIMISTIC_BACKJUMPING_RATIO
|
| 86 |
+
self._save_states: list[State[RT, CT, KT]] | None = None
|
| 87 |
+
self._optimistic_start_round: int | None = None
|
| 88 |
+
|
| 89 |
+
@property
|
| 90 |
+
def state(self) -> State[RT, CT, KT]:
|
| 91 |
+
try:
|
| 92 |
+
return self._states[-1]
|
| 93 |
+
except IndexError as e:
|
| 94 |
+
raise AttributeError("state") from e
|
| 95 |
+
|
| 96 |
+
def _push_new_state(self) -> None:
|
| 97 |
+
"""Push a new state into history.
|
| 98 |
+
|
| 99 |
+
This new state will be used to hold resolution results of the next
|
| 100 |
+
coming round.
|
| 101 |
+
"""
|
| 102 |
+
base = self._states[-1]
|
| 103 |
+
state = State(
|
| 104 |
+
mapping=base.mapping.copy(),
|
| 105 |
+
criteria=base.criteria.copy(),
|
| 106 |
+
backtrack_causes=base.backtrack_causes[:],
|
| 107 |
+
)
|
| 108 |
+
self._states.append(state)
|
| 109 |
+
|
| 110 |
+
def _add_to_criteria(
|
| 111 |
+
self,
|
| 112 |
+
criteria: dict[KT, Criterion[RT, CT]],
|
| 113 |
+
requirement: RT,
|
| 114 |
+
parent: CT | None,
|
| 115 |
+
) -> None:
|
| 116 |
+
self._r.adding_requirement(requirement=requirement, parent=parent)
|
| 117 |
+
|
| 118 |
+
identifier = self._p.identify(requirement_or_candidate=requirement)
|
| 119 |
+
criterion = criteria.get(identifier)
|
| 120 |
+
if criterion:
|
| 121 |
+
incompatibilities = list(criterion.incompatibilities)
|
| 122 |
+
else:
|
| 123 |
+
incompatibilities = []
|
| 124 |
+
|
| 125 |
+
matches = self._p.find_matches(
|
| 126 |
+
identifier=identifier,
|
| 127 |
+
requirements=IteratorMapping(
|
| 128 |
+
criteria,
|
| 129 |
+
operator.methodcaller("iter_requirement"),
|
| 130 |
+
{identifier: [requirement]},
|
| 131 |
+
),
|
| 132 |
+
incompatibilities=IteratorMapping(
|
| 133 |
+
criteria,
|
| 134 |
+
operator.attrgetter("incompatibilities"),
|
| 135 |
+
{identifier: incompatibilities},
|
| 136 |
+
),
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
if criterion:
|
| 140 |
+
information = list(criterion.information)
|
| 141 |
+
information.append(RequirementInformation(requirement, parent))
|
| 142 |
+
else:
|
| 143 |
+
information = [RequirementInformation(requirement, parent)]
|
| 144 |
+
|
| 145 |
+
criterion = Criterion(
|
| 146 |
+
candidates=build_iter_view(matches),
|
| 147 |
+
information=information,
|
| 148 |
+
incompatibilities=incompatibilities,
|
| 149 |
+
)
|
| 150 |
+
if not criterion.candidates:
|
| 151 |
+
raise RequirementsConflicted(criterion)
|
| 152 |
+
criteria[identifier] = criterion
|
| 153 |
+
|
| 154 |
+
def _remove_information_from_criteria(
|
| 155 |
+
self, criteria: dict[KT, Criterion[RT, CT]], parents: Collection[KT]
|
| 156 |
+
) -> None:
|
| 157 |
+
"""Remove information from parents of criteria.
|
| 158 |
+
|
| 159 |
+
Concretely, removes all values from each criterion's ``information``
|
| 160 |
+
field that have one of ``parents`` as provider of the requirement.
|
| 161 |
+
|
| 162 |
+
:param criteria: The criteria to update.
|
| 163 |
+
:param parents: Identifiers for which to remove information from all criteria.
|
| 164 |
+
"""
|
| 165 |
+
if not parents:
|
| 166 |
+
return
|
| 167 |
+
for key, criterion in criteria.items():
|
| 168 |
+
criteria[key] = Criterion(
|
| 169 |
+
criterion.candidates,
|
| 170 |
+
[
|
| 171 |
+
information
|
| 172 |
+
for information in criterion.information
|
| 173 |
+
if (
|
| 174 |
+
information.parent is None
|
| 175 |
+
or self._p.identify(information.parent) not in parents
|
| 176 |
+
)
|
| 177 |
+
],
|
| 178 |
+
criterion.incompatibilities,
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
def _get_preference(self, name: KT) -> Preference:
|
| 182 |
+
return self._p.get_preference(
|
| 183 |
+
identifier=name,
|
| 184 |
+
resolutions=self.state.mapping,
|
| 185 |
+
candidates=IteratorMapping(
|
| 186 |
+
self.state.criteria,
|
| 187 |
+
operator.attrgetter("candidates"),
|
| 188 |
+
),
|
| 189 |
+
information=IteratorMapping(
|
| 190 |
+
self.state.criteria,
|
| 191 |
+
operator.attrgetter("information"),
|
| 192 |
+
),
|
| 193 |
+
backtrack_causes=self.state.backtrack_causes,
|
| 194 |
+
)
|
| 195 |
+
|
| 196 |
+
def _is_current_pin_satisfying(
|
| 197 |
+
self, name: KT, criterion: Criterion[RT, CT]
|
| 198 |
+
) -> bool:
|
| 199 |
+
try:
|
| 200 |
+
current_pin = self.state.mapping[name]
|
| 201 |
+
except KeyError:
|
| 202 |
+
return False
|
| 203 |
+
return all(
|
| 204 |
+
self._p.is_satisfied_by(requirement=r, candidate=current_pin)
|
| 205 |
+
for r in criterion.iter_requirement()
|
| 206 |
+
)
|
| 207 |
+
|
| 208 |
+
def _get_updated_criteria(self, candidate: CT) -> dict[KT, Criterion[RT, CT]]:
|
| 209 |
+
criteria = self.state.criteria.copy()
|
| 210 |
+
for requirement in self._p.get_dependencies(candidate=candidate):
|
| 211 |
+
self._add_to_criteria(criteria, requirement, parent=candidate)
|
| 212 |
+
return criteria
|
| 213 |
+
|
| 214 |
+
def _attempt_to_pin_criterion(self, name: KT) -> list[Criterion[RT, CT]]:
|
| 215 |
+
criterion = self.state.criteria[name]
|
| 216 |
+
|
| 217 |
+
causes: list[Criterion[RT, CT]] = []
|
| 218 |
+
for candidate in criterion.candidates:
|
| 219 |
+
try:
|
| 220 |
+
criteria = self._get_updated_criteria(candidate)
|
| 221 |
+
except RequirementsConflicted as e:
|
| 222 |
+
self._r.rejecting_candidate(e.criterion, candidate)
|
| 223 |
+
causes.append(e.criterion)
|
| 224 |
+
continue
|
| 225 |
+
|
| 226 |
+
# Check the newly-pinned candidate actually works. This should
|
| 227 |
+
# always pass under normal circumstances, but in the case of a
|
| 228 |
+
# faulty provider, we will raise an error to notify the implementer
|
| 229 |
+
# to fix find_matches() and/or is_satisfied_by().
|
| 230 |
+
satisfied = all(
|
| 231 |
+
self._p.is_satisfied_by(requirement=r, candidate=candidate)
|
| 232 |
+
for r in criterion.iter_requirement()
|
| 233 |
+
)
|
| 234 |
+
if not satisfied:
|
| 235 |
+
raise InconsistentCandidate(candidate, criterion)
|
| 236 |
+
|
| 237 |
+
self._r.pinning(candidate=candidate)
|
| 238 |
+
self.state.criteria.update(criteria)
|
| 239 |
+
|
| 240 |
+
# Put newly-pinned candidate at the end. This is essential because
|
| 241 |
+
# backtracking looks at this mapping to get the last pin.
|
| 242 |
+
self.state.mapping.pop(name, None)
|
| 243 |
+
self.state.mapping[name] = candidate
|
| 244 |
+
|
| 245 |
+
return []
|
| 246 |
+
|
| 247 |
+
# All candidates tried, nothing works. This criterion is a dead
|
| 248 |
+
# end, signal for backtracking.
|
| 249 |
+
return causes
|
| 250 |
+
|
| 251 |
+
def _patch_criteria(
|
| 252 |
+
self, incompatibilities_from_broken: list[tuple[KT, list[CT]]]
|
| 253 |
+
) -> bool:
|
| 254 |
+
# Create a new state from the last known-to-work one, and apply
|
| 255 |
+
# the previously gathered incompatibility information.
|
| 256 |
+
for k, incompatibilities in incompatibilities_from_broken:
|
| 257 |
+
if not incompatibilities:
|
| 258 |
+
continue
|
| 259 |
+
try:
|
| 260 |
+
criterion = self.state.criteria[k]
|
| 261 |
+
except KeyError:
|
| 262 |
+
continue
|
| 263 |
+
matches = self._p.find_matches(
|
| 264 |
+
identifier=k,
|
| 265 |
+
requirements=IteratorMapping(
|
| 266 |
+
self.state.criteria,
|
| 267 |
+
operator.methodcaller("iter_requirement"),
|
| 268 |
+
),
|
| 269 |
+
incompatibilities=IteratorMapping(
|
| 270 |
+
self.state.criteria,
|
| 271 |
+
operator.attrgetter("incompatibilities"),
|
| 272 |
+
{k: incompatibilities},
|
| 273 |
+
),
|
| 274 |
+
)
|
| 275 |
+
candidates: IterableView[CT] = build_iter_view(matches)
|
| 276 |
+
if not candidates:
|
| 277 |
+
return False
|
| 278 |
+
incompatibilities.extend(criterion.incompatibilities)
|
| 279 |
+
self.state.criteria[k] = Criterion(
|
| 280 |
+
candidates=candidates,
|
| 281 |
+
information=list(criterion.information),
|
| 282 |
+
incompatibilities=incompatibilities,
|
| 283 |
+
)
|
| 284 |
+
return True
|
| 285 |
+
|
| 286 |
+
def _save_state(self) -> None:
|
| 287 |
+
"""Save states for potential rollback if optimistic backjumping fails."""
|
| 288 |
+
if self._save_states is None:
|
| 289 |
+
self._save_states = [
|
| 290 |
+
State(
|
| 291 |
+
mapping=s.mapping.copy(),
|
| 292 |
+
criteria=s.criteria.copy(),
|
| 293 |
+
backtrack_causes=s.backtrack_causes[:],
|
| 294 |
+
)
|
| 295 |
+
for s in self._states
|
| 296 |
+
]
|
| 297 |
+
|
| 298 |
+
def _rollback_states(self) -> None:
|
| 299 |
+
"""Rollback states and disable optimistic backjumping."""
|
| 300 |
+
self._optimistic_backjumping_ratio = 0.0
|
| 301 |
+
if self._save_states:
|
| 302 |
+
self._states = self._save_states
|
| 303 |
+
self._save_states = None
|
| 304 |
+
|
| 305 |
+
def _backjump(self, causes: list[RequirementInformation[RT, CT]]) -> bool:
|
| 306 |
+
"""Perform backjumping.
|
| 307 |
+
|
| 308 |
+
When we enter here, the stack is like this::
|
| 309 |
+
|
| 310 |
+
[ state Z ]
|
| 311 |
+
[ state Y ]
|
| 312 |
+
[ state X ]
|
| 313 |
+
.... earlier states are irrelevant.
|
| 314 |
+
|
| 315 |
+
1. No pins worked for Z, so it does not have a pin.
|
| 316 |
+
2. We want to reset state Y to unpinned, and pin another candidate.
|
| 317 |
+
3. State X holds what state Y was before the pin, but does not
|
| 318 |
+
have the incompatibility information gathered in state Y.
|
| 319 |
+
|
| 320 |
+
Each iteration of the loop will:
|
| 321 |
+
|
| 322 |
+
1. Identify Z. The incompatibility is not always caused by the latest
|
| 323 |
+
state. For example, given three requirements A, B and C, with
|
| 324 |
+
dependencies A1, B1 and C1, where A1 and B1 are incompatible: the
|
| 325 |
+
last state might be related to C, so we want to discard the
|
| 326 |
+
previous state.
|
| 327 |
+
2. Discard Z.
|
| 328 |
+
3. Discard Y but remember its incompatibility information gathered
|
| 329 |
+
previously, and the failure we're dealing with right now.
|
| 330 |
+
4. Push a new state Y' based on X, and apply the incompatibility
|
| 331 |
+
information from Y to Y'.
|
| 332 |
+
5a. If this causes Y' to conflict, we need to backtrack again. Make Y'
|
| 333 |
+
the new Z and go back to step 2.
|
| 334 |
+
5b. If the incompatibilities apply cleanly, end backtracking.
|
| 335 |
+
"""
|
| 336 |
+
incompatible_reqs: Iterable[CT | RT] = itertools.chain(
|
| 337 |
+
(c.parent for c in causes if c.parent is not None),
|
| 338 |
+
(c.requirement for c in causes),
|
| 339 |
+
)
|
| 340 |
+
incompatible_deps = {self._p.identify(r) for r in incompatible_reqs}
|
| 341 |
+
while len(self._states) >= 3:
|
| 342 |
+
# Remove the state that triggered backtracking.
|
| 343 |
+
del self._states[-1]
|
| 344 |
+
|
| 345 |
+
# Optimistically backtrack to a state that caused the incompatibility
|
| 346 |
+
broken_state = self.state
|
| 347 |
+
while True:
|
| 348 |
+
# Retrieve the last candidate pin and known incompatibilities.
|
| 349 |
+
try:
|
| 350 |
+
broken_state = self._states.pop()
|
| 351 |
+
name, candidate = broken_state.mapping.popitem()
|
| 352 |
+
except (IndexError, KeyError):
|
| 353 |
+
raise ResolutionImpossible(causes) from None
|
| 354 |
+
|
| 355 |
+
if (
|
| 356 |
+
not self._optimistic_backjumping_ratio
|
| 357 |
+
and name not in incompatible_deps
|
| 358 |
+
):
|
| 359 |
+
# For safe backjumping only backjump if the current dependency
|
| 360 |
+
# is not the same as the incompatible dependency
|
| 361 |
+
break
|
| 362 |
+
|
| 363 |
+
# On the first time a non-safe backjump is done the state
|
| 364 |
+
# is saved so we can restore it later if the resolution fails
|
| 365 |
+
if (
|
| 366 |
+
self._optimistic_backjumping_ratio
|
| 367 |
+
and self._save_states is None
|
| 368 |
+
and name not in incompatible_deps
|
| 369 |
+
):
|
| 370 |
+
self._save_state()
|
| 371 |
+
|
| 372 |
+
# If the current dependencies and the incompatible dependencies
|
| 373 |
+
# are overlapping then we have likely found a cause of the
|
| 374 |
+
# incompatibility
|
| 375 |
+
current_dependencies = {
|
| 376 |
+
self._p.identify(d) for d in self._p.get_dependencies(candidate)
|
| 377 |
+
}
|
| 378 |
+
if not current_dependencies.isdisjoint(incompatible_deps):
|
| 379 |
+
break
|
| 380 |
+
|
| 381 |
+
# Fallback: We should not backtrack to the point where
|
| 382 |
+
# broken_state.mapping is empty, so stop backtracking for
|
| 383 |
+
# a chance for the resolution to recover
|
| 384 |
+
if not broken_state.mapping:
|
| 385 |
+
break
|
| 386 |
+
|
| 387 |
+
incompatibilities_from_broken = [
|
| 388 |
+
(k, list(v.incompatibilities)) for k, v in broken_state.criteria.items()
|
| 389 |
+
]
|
| 390 |
+
|
| 391 |
+
# Also mark the newly known incompatibility.
|
| 392 |
+
incompatibilities_from_broken.append((name, [candidate]))
|
| 393 |
+
|
| 394 |
+
self._push_new_state()
|
| 395 |
+
success = self._patch_criteria(incompatibilities_from_broken)
|
| 396 |
+
|
| 397 |
+
# It works! Let's work on this new state.
|
| 398 |
+
if success:
|
| 399 |
+
return True
|
| 400 |
+
|
| 401 |
+
# State does not work after applying known incompatibilities.
|
| 402 |
+
# Try the still previous state.
|
| 403 |
+
|
| 404 |
+
# No way to backtrack anymore.
|
| 405 |
+
return False
|
| 406 |
+
|
| 407 |
+
def _extract_causes(
|
| 408 |
+
self, criteron: list[Criterion[RT, CT]]
|
| 409 |
+
) -> list[RequirementInformation[RT, CT]]:
|
| 410 |
+
"""Extract causes from list of criterion and deduplicate"""
|
| 411 |
+
return list({id(i): i for c in criteron for i in c.information}.values())
|
| 412 |
+
|
| 413 |
+
def resolve(self, requirements: Iterable[RT], max_rounds: int) -> State[RT, CT, KT]:
|
| 414 |
+
if self._states:
|
| 415 |
+
raise RuntimeError("already resolved")
|
| 416 |
+
|
| 417 |
+
self._r.starting()
|
| 418 |
+
|
| 419 |
+
# Initialize the root state.
|
| 420 |
+
self._states = [
|
| 421 |
+
State(
|
| 422 |
+
mapping=collections.OrderedDict(),
|
| 423 |
+
criteria={},
|
| 424 |
+
backtrack_causes=[],
|
| 425 |
+
)
|
| 426 |
+
]
|
| 427 |
+
for r in requirements:
|
| 428 |
+
try:
|
| 429 |
+
self._add_to_criteria(self.state.criteria, r, parent=None)
|
| 430 |
+
except RequirementsConflicted as e:
|
| 431 |
+
raise ResolutionImpossible(e.criterion.information) from e
|
| 432 |
+
|
| 433 |
+
# The root state is saved as a sentinel so the first ever pin can have
|
| 434 |
+
# something to backtrack to if it fails. The root state is basically
|
| 435 |
+
# pinning the virtual "root" package in the graph.
|
| 436 |
+
self._push_new_state()
|
| 437 |
+
|
| 438 |
+
# Variables for optimistic backjumping
|
| 439 |
+
optimistic_rounds_cutoff: int | None = None
|
| 440 |
+
optimistic_backjumping_start_round: int | None = None
|
| 441 |
+
|
| 442 |
+
for round_index in range(max_rounds):
|
| 443 |
+
self._r.starting_round(index=round_index)
|
| 444 |
+
|
| 445 |
+
# Handle if optimistic backjumping has been running for too long
|
| 446 |
+
if self._optimistic_backjumping_ratio and self._save_states is not None:
|
| 447 |
+
if optimistic_backjumping_start_round is None:
|
| 448 |
+
optimistic_backjumping_start_round = round_index
|
| 449 |
+
optimistic_rounds_cutoff = int(
|
| 450 |
+
(max_rounds - round_index) * self._optimistic_backjumping_ratio
|
| 451 |
+
)
|
| 452 |
+
|
| 453 |
+
if optimistic_rounds_cutoff <= 0:
|
| 454 |
+
self._rollback_states()
|
| 455 |
+
continue
|
| 456 |
+
elif optimistic_rounds_cutoff is not None:
|
| 457 |
+
if (
|
| 458 |
+
round_index - optimistic_backjumping_start_round
|
| 459 |
+
>= optimistic_rounds_cutoff
|
| 460 |
+
):
|
| 461 |
+
self._rollback_states()
|
| 462 |
+
continue
|
| 463 |
+
|
| 464 |
+
unsatisfied_names = [
|
| 465 |
+
key
|
| 466 |
+
for key, criterion in self.state.criteria.items()
|
| 467 |
+
if not self._is_current_pin_satisfying(key, criterion)
|
| 468 |
+
]
|
| 469 |
+
|
| 470 |
+
# All criteria are accounted for. Nothing more to pin, we are done!
|
| 471 |
+
if not unsatisfied_names:
|
| 472 |
+
self._r.ending(state=self.state)
|
| 473 |
+
return self.state
|
| 474 |
+
|
| 475 |
+
# keep track of satisfied names to calculate diff after pinning
|
| 476 |
+
satisfied_names = set(self.state.criteria.keys()) - set(unsatisfied_names)
|
| 477 |
+
|
| 478 |
+
if len(unsatisfied_names) > 1:
|
| 479 |
+
narrowed_unstatisfied_names = list(
|
| 480 |
+
self._p.narrow_requirement_selection(
|
| 481 |
+
identifiers=unsatisfied_names,
|
| 482 |
+
resolutions=self.state.mapping,
|
| 483 |
+
candidates=IteratorMapping(
|
| 484 |
+
self.state.criteria,
|
| 485 |
+
operator.attrgetter("candidates"),
|
| 486 |
+
),
|
| 487 |
+
information=IteratorMapping(
|
| 488 |
+
self.state.criteria,
|
| 489 |
+
operator.attrgetter("information"),
|
| 490 |
+
),
|
| 491 |
+
backtrack_causes=self.state.backtrack_causes,
|
| 492 |
+
)
|
| 493 |
+
)
|
| 494 |
+
else:
|
| 495 |
+
narrowed_unstatisfied_names = unsatisfied_names
|
| 496 |
+
|
| 497 |
+
# If there are no unsatisfied names use unsatisfied names
|
| 498 |
+
if not narrowed_unstatisfied_names:
|
| 499 |
+
raise RuntimeError("narrow_requirement_selection returned 0 names")
|
| 500 |
+
|
| 501 |
+
# If there is only 1 unsatisfied name skip calling self._get_preference
|
| 502 |
+
if len(narrowed_unstatisfied_names) > 1:
|
| 503 |
+
# Choose the most preferred unpinned criterion to try.
|
| 504 |
+
name = min(narrowed_unstatisfied_names, key=self._get_preference)
|
| 505 |
+
else:
|
| 506 |
+
name = narrowed_unstatisfied_names[0]
|
| 507 |
+
|
| 508 |
+
failure_criterion = self._attempt_to_pin_criterion(name)
|
| 509 |
+
|
| 510 |
+
if failure_criterion:
|
| 511 |
+
causes = self._extract_causes(failure_criterion)
|
| 512 |
+
# Backjump if pinning fails. The backjump process puts us in
|
| 513 |
+
# an unpinned state, so we can work on it in the next round.
|
| 514 |
+
self._r.resolving_conflicts(causes=causes)
|
| 515 |
+
|
| 516 |
+
try:
|
| 517 |
+
success = self._backjump(causes)
|
| 518 |
+
except ResolutionImpossible:
|
| 519 |
+
if self._optimistic_backjumping_ratio and self._save_states:
|
| 520 |
+
failed_optimistic_backjumping = True
|
| 521 |
+
else:
|
| 522 |
+
raise
|
| 523 |
+
else:
|
| 524 |
+
failed_optimistic_backjumping = bool(
|
| 525 |
+
not success
|
| 526 |
+
and self._optimistic_backjumping_ratio
|
| 527 |
+
and self._save_states
|
| 528 |
+
)
|
| 529 |
+
|
| 530 |
+
if failed_optimistic_backjumping and self._save_states:
|
| 531 |
+
self._rollback_states()
|
| 532 |
+
else:
|
| 533 |
+
self.state.backtrack_causes[:] = causes
|
| 534 |
+
|
| 535 |
+
# Dead ends everywhere. Give up.
|
| 536 |
+
if not success:
|
| 537 |
+
raise ResolutionImpossible(self.state.backtrack_causes)
|
| 538 |
+
else:
|
| 539 |
+
# discard as information sources any invalidated names
|
| 540 |
+
# (unsatisfied names that were previously satisfied)
|
| 541 |
+
newly_unsatisfied_names = {
|
| 542 |
+
key
|
| 543 |
+
for key, criterion in self.state.criteria.items()
|
| 544 |
+
if key in satisfied_names
|
| 545 |
+
and not self._is_current_pin_satisfying(key, criterion)
|
| 546 |
+
}
|
| 547 |
+
self._remove_information_from_criteria(
|
| 548 |
+
self.state.criteria, newly_unsatisfied_names
|
| 549 |
+
)
|
| 550 |
+
# Pinning was successful. Push a new state to do another pin.
|
| 551 |
+
self._push_new_state()
|
| 552 |
+
|
| 553 |
+
self._r.ending_round(index=round_index, state=self.state)
|
| 554 |
+
|
| 555 |
+
raise ResolutionTooDeep(max_rounds)
|
| 556 |
+
|
| 557 |
+
|
| 558 |
+
class Resolver(AbstractResolver[RT, CT, KT]):
|
| 559 |
+
"""The thing that performs the actual resolution work."""
|
| 560 |
+
|
| 561 |
+
base_exception = ResolverException
|
| 562 |
+
|
| 563 |
+
def resolve( # type: ignore[override]
|
| 564 |
+
self,
|
| 565 |
+
requirements: Iterable[RT],
|
| 566 |
+
max_rounds: int = 100,
|
| 567 |
+
) -> Result[RT, CT, KT]:
|
| 568 |
+
"""Take a collection of constraints, spit out the resolution result.
|
| 569 |
+
|
| 570 |
+
The return value is a representation to the final resolution result. It
|
| 571 |
+
is a tuple subclass with three public members:
|
| 572 |
+
|
| 573 |
+
* `mapping`: A dict of resolved candidates. Each key is an identifier
|
| 574 |
+
of a requirement (as returned by the provider's `identify` method),
|
| 575 |
+
and the value is the resolved candidate.
|
| 576 |
+
* `graph`: A `DirectedGraph` instance representing the dependency tree.
|
| 577 |
+
The vertices are keys of `mapping`, and each edge represents *why*
|
| 578 |
+
a particular package is included. A special vertex `None` is
|
| 579 |
+
included to represent parents of user-supplied requirements.
|
| 580 |
+
* `criteria`: A dict of "criteria" that hold detailed information on
|
| 581 |
+
how edges in the graph are derived. Each key is an identifier of a
|
| 582 |
+
requirement, and the value is a `Criterion` instance.
|
| 583 |
+
|
| 584 |
+
The following exceptions may be raised if a resolution cannot be found:
|
| 585 |
+
|
| 586 |
+
* `ResolutionImpossible`: A resolution cannot be found for the given
|
| 587 |
+
combination of requirements. The `causes` attribute of the
|
| 588 |
+
exception is a list of (requirement, parent), giving the
|
| 589 |
+
requirements that could not be satisfied.
|
| 590 |
+
* `ResolutionTooDeep`: The dependency tree is too deeply nested and
|
| 591 |
+
the resolver gave up. This is usually caused by a circular
|
| 592 |
+
dependency, but you can try to resolve this by increasing the
|
| 593 |
+
`max_rounds` argument.
|
| 594 |
+
"""
|
| 595 |
+
resolution = Resolution(self.provider, self.reporter)
|
| 596 |
+
state = resolution.resolve(requirements, max_rounds=max_rounds)
|
| 597 |
+
return _build_result(state)
|
| 598 |
+
|
| 599 |
+
|
| 600 |
+
def _has_route_to_root(
|
| 601 |
+
criteria: Mapping[KT, Criterion[RT, CT]],
|
| 602 |
+
key: KT | None,
|
| 603 |
+
all_keys: dict[int, KT | None],
|
| 604 |
+
connected: set[KT | None],
|
| 605 |
+
) -> bool:
|
| 606 |
+
if key in connected:
|
| 607 |
+
return True
|
| 608 |
+
if key not in criteria:
|
| 609 |
+
return False
|
| 610 |
+
assert key is not None
|
| 611 |
+
for p in criteria[key].iter_parent():
|
| 612 |
+
try:
|
| 613 |
+
pkey = all_keys[id(p)]
|
| 614 |
+
except KeyError:
|
| 615 |
+
continue
|
| 616 |
+
if pkey in connected:
|
| 617 |
+
connected.add(key)
|
| 618 |
+
return True
|
| 619 |
+
if _has_route_to_root(criteria, pkey, all_keys, connected):
|
| 620 |
+
connected.add(key)
|
| 621 |
+
return True
|
| 622 |
+
return False
|
.venv/Lib/site-packages/pip/_vendor/resolvelib/structs.py
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import itertools
|
| 4 |
+
from collections import namedtuple
|
| 5 |
+
from typing import (
|
| 6 |
+
TYPE_CHECKING,
|
| 7 |
+
Callable,
|
| 8 |
+
Generic,
|
| 9 |
+
Iterable,
|
| 10 |
+
Iterator,
|
| 11 |
+
Mapping,
|
| 12 |
+
NamedTuple,
|
| 13 |
+
Sequence,
|
| 14 |
+
TypeVar,
|
| 15 |
+
Union,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
KT = TypeVar("KT") # Identifier.
|
| 19 |
+
RT = TypeVar("RT") # Requirement.
|
| 20 |
+
CT = TypeVar("CT") # Candidate.
|
| 21 |
+
|
| 22 |
+
Matches = Union[Iterable[CT], Callable[[], Iterable[CT]]]
|
| 23 |
+
|
| 24 |
+
if TYPE_CHECKING:
|
| 25 |
+
from .resolvers.criterion import Criterion
|
| 26 |
+
|
| 27 |
+
class RequirementInformation(NamedTuple, Generic[RT, CT]):
|
| 28 |
+
requirement: RT
|
| 29 |
+
parent: CT | None
|
| 30 |
+
|
| 31 |
+
class State(NamedTuple, Generic[RT, CT, KT]):
|
| 32 |
+
"""Resolution state in a round."""
|
| 33 |
+
|
| 34 |
+
mapping: dict[KT, CT]
|
| 35 |
+
criteria: dict[KT, Criterion[RT, CT]]
|
| 36 |
+
backtrack_causes: list[RequirementInformation[RT, CT]]
|
| 37 |
+
|
| 38 |
+
else:
|
| 39 |
+
RequirementInformation = namedtuple(
|
| 40 |
+
"RequirementInformation", ["requirement", "parent"]
|
| 41 |
+
)
|
| 42 |
+
State = namedtuple("State", ["mapping", "criteria", "backtrack_causes"])
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class DirectedGraph(Generic[KT]):
|
| 46 |
+
"""A graph structure with directed edges."""
|
| 47 |
+
|
| 48 |
+
def __init__(self) -> None:
|
| 49 |
+
self._vertices: set[KT] = set()
|
| 50 |
+
self._forwards: dict[KT, set[KT]] = {} # <key> -> Set[<key>]
|
| 51 |
+
self._backwards: dict[KT, set[KT]] = {} # <key> -> Set[<key>]
|
| 52 |
+
|
| 53 |
+
def __iter__(self) -> Iterator[KT]:
|
| 54 |
+
return iter(self._vertices)
|
| 55 |
+
|
| 56 |
+
def __len__(self) -> int:
|
| 57 |
+
return len(self._vertices)
|
| 58 |
+
|
| 59 |
+
def __contains__(self, key: KT) -> bool:
|
| 60 |
+
return key in self._vertices
|
| 61 |
+
|
| 62 |
+
def copy(self) -> DirectedGraph[KT]:
|
| 63 |
+
"""Return a shallow copy of this graph."""
|
| 64 |
+
other = type(self)()
|
| 65 |
+
other._vertices = set(self._vertices)
|
| 66 |
+
other._forwards = {k: set(v) for k, v in self._forwards.items()}
|
| 67 |
+
other._backwards = {k: set(v) for k, v in self._backwards.items()}
|
| 68 |
+
return other
|
| 69 |
+
|
| 70 |
+
def add(self, key: KT) -> None:
|
| 71 |
+
"""Add a new vertex to the graph."""
|
| 72 |
+
if key in self._vertices:
|
| 73 |
+
raise ValueError("vertex exists")
|
| 74 |
+
self._vertices.add(key)
|
| 75 |
+
self._forwards[key] = set()
|
| 76 |
+
self._backwards[key] = set()
|
| 77 |
+
|
| 78 |
+
def remove(self, key: KT) -> None:
|
| 79 |
+
"""Remove a vertex from the graph, disconnecting all edges from/to it."""
|
| 80 |
+
self._vertices.remove(key)
|
| 81 |
+
for f in self._forwards.pop(key):
|
| 82 |
+
self._backwards[f].remove(key)
|
| 83 |
+
for t in self._backwards.pop(key):
|
| 84 |
+
self._forwards[t].remove(key)
|
| 85 |
+
|
| 86 |
+
def connected(self, f: KT, t: KT) -> bool:
|
| 87 |
+
return f in self._backwards[t] and t in self._forwards[f]
|
| 88 |
+
|
| 89 |
+
def connect(self, f: KT, t: KT) -> None:
|
| 90 |
+
"""Connect two existing vertices.
|
| 91 |
+
|
| 92 |
+
Nothing happens if the vertices are already connected.
|
| 93 |
+
"""
|
| 94 |
+
if t not in self._vertices:
|
| 95 |
+
raise KeyError(t)
|
| 96 |
+
self._forwards[f].add(t)
|
| 97 |
+
self._backwards[t].add(f)
|
| 98 |
+
|
| 99 |
+
def iter_edges(self) -> Iterator[tuple[KT, KT]]:
|
| 100 |
+
for f, children in self._forwards.items():
|
| 101 |
+
for t in children:
|
| 102 |
+
yield f, t
|
| 103 |
+
|
| 104 |
+
def iter_children(self, key: KT) -> Iterator[KT]:
|
| 105 |
+
return iter(self._forwards[key])
|
| 106 |
+
|
| 107 |
+
def iter_parents(self, key: KT) -> Iterator[KT]:
|
| 108 |
+
return iter(self._backwards[key])
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
class IteratorMapping(Mapping[KT, Iterator[CT]], Generic[RT, CT, KT]):
|
| 112 |
+
def __init__(
|
| 113 |
+
self,
|
| 114 |
+
mapping: Mapping[KT, RT],
|
| 115 |
+
accessor: Callable[[RT], Iterable[CT]],
|
| 116 |
+
appends: Mapping[KT, Iterable[CT]] | None = None,
|
| 117 |
+
) -> None:
|
| 118 |
+
self._mapping = mapping
|
| 119 |
+
self._accessor = accessor
|
| 120 |
+
self._appends: Mapping[KT, Iterable[CT]] = appends or {}
|
| 121 |
+
|
| 122 |
+
def __repr__(self) -> str:
|
| 123 |
+
return "IteratorMapping({!r}, {!r}, {!r})".format(
|
| 124 |
+
self._mapping,
|
| 125 |
+
self._accessor,
|
| 126 |
+
self._appends,
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
def __bool__(self) -> bool:
|
| 130 |
+
return bool(self._mapping or self._appends)
|
| 131 |
+
|
| 132 |
+
def __contains__(self, key: object) -> bool:
|
| 133 |
+
return key in self._mapping or key in self._appends
|
| 134 |
+
|
| 135 |
+
def __getitem__(self, k: KT) -> Iterator[CT]:
|
| 136 |
+
try:
|
| 137 |
+
v = self._mapping[k]
|
| 138 |
+
except KeyError:
|
| 139 |
+
return iter(self._appends[k])
|
| 140 |
+
return itertools.chain(self._accessor(v), self._appends.get(k, ()))
|
| 141 |
+
|
| 142 |
+
def __iter__(self) -> Iterator[KT]:
|
| 143 |
+
more = (k for k in self._appends if k not in self._mapping)
|
| 144 |
+
return itertools.chain(self._mapping, more)
|
| 145 |
+
|
| 146 |
+
def __len__(self) -> int:
|
| 147 |
+
more = sum(1 for k in self._appends if k not in self._mapping)
|
| 148 |
+
return len(self._mapping) + more
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class _FactoryIterableView(Iterable[RT]):
|
| 152 |
+
"""Wrap an iterator factory returned by `find_matches()`.
|
| 153 |
+
|
| 154 |
+
Calling `iter()` on this class would invoke the underlying iterator
|
| 155 |
+
factory, making it a "collection with ordering" that can be iterated
|
| 156 |
+
through multiple times, but lacks random access methods presented in
|
| 157 |
+
built-in Python sequence types.
|
| 158 |
+
"""
|
| 159 |
+
|
| 160 |
+
def __init__(self, factory: Callable[[], Iterable[RT]]) -> None:
|
| 161 |
+
self._factory = factory
|
| 162 |
+
self._iterable: Iterable[RT] | None = None
|
| 163 |
+
|
| 164 |
+
def __repr__(self) -> str:
|
| 165 |
+
return f"{type(self).__name__}({list(self)})"
|
| 166 |
+
|
| 167 |
+
def __bool__(self) -> bool:
|
| 168 |
+
try:
|
| 169 |
+
next(iter(self))
|
| 170 |
+
except StopIteration:
|
| 171 |
+
return False
|
| 172 |
+
return True
|
| 173 |
+
|
| 174 |
+
def __iter__(self) -> Iterator[RT]:
|
| 175 |
+
iterable = self._factory() if self._iterable is None else self._iterable
|
| 176 |
+
self._iterable, current = itertools.tee(iterable)
|
| 177 |
+
return current
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class _SequenceIterableView(Iterable[RT]):
|
| 181 |
+
"""Wrap an iterable returned by find_matches().
|
| 182 |
+
|
| 183 |
+
This is essentially just a proxy to the underlying sequence that provides
|
| 184 |
+
the same interface as `_FactoryIterableView`.
|
| 185 |
+
"""
|
| 186 |
+
|
| 187 |
+
def __init__(self, sequence: Sequence[RT]):
|
| 188 |
+
self._sequence = sequence
|
| 189 |
+
|
| 190 |
+
def __repr__(self) -> str:
|
| 191 |
+
return f"{type(self).__name__}({self._sequence})"
|
| 192 |
+
|
| 193 |
+
def __bool__(self) -> bool:
|
| 194 |
+
return bool(self._sequence)
|
| 195 |
+
|
| 196 |
+
def __iter__(self) -> Iterator[RT]:
|
| 197 |
+
return iter(self._sequence)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def build_iter_view(matches: Matches[CT]) -> Iterable[CT]:
|
| 201 |
+
"""Build an iterable view from the value returned by `find_matches()`."""
|
| 202 |
+
if callable(matches):
|
| 203 |
+
return _FactoryIterableView(matches)
|
| 204 |
+
if not isinstance(matches, Sequence):
|
| 205 |
+
matches = list(matches)
|
| 206 |
+
return _SequenceIterableView(matches)
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
IterableView = Iterable
|
.venv/Lib/site-packages/pip/_vendor/rich/__init__.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Rich text and beautiful formatting in the terminal."""
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
from typing import IO, TYPE_CHECKING, Any, Callable, Optional, Union
|
| 5 |
+
|
| 6 |
+
from ._extension import load_ipython_extension # noqa: F401
|
| 7 |
+
|
| 8 |
+
__all__ = ["get_console", "reconfigure", "print", "inspect", "print_json"]
|
| 9 |
+
|
| 10 |
+
if TYPE_CHECKING:
|
| 11 |
+
from .console import Console
|
| 12 |
+
|
| 13 |
+
# Global console used by alternative print
|
| 14 |
+
_console: Optional["Console"] = None
|
| 15 |
+
|
| 16 |
+
try:
|
| 17 |
+
_IMPORT_CWD = os.path.abspath(os.getcwd())
|
| 18 |
+
except FileNotFoundError:
|
| 19 |
+
# Can happen if the cwd has been deleted
|
| 20 |
+
_IMPORT_CWD = ""
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def get_console() -> "Console":
|
| 24 |
+
"""Get a global :class:`~rich.console.Console` instance. This function is used when Rich requires a Console,
|
| 25 |
+
and hasn't been explicitly given one.
|
| 26 |
+
|
| 27 |
+
Returns:
|
| 28 |
+
Console: A console instance.
|
| 29 |
+
"""
|
| 30 |
+
global _console
|
| 31 |
+
if _console is None:
|
| 32 |
+
from .console import Console
|
| 33 |
+
|
| 34 |
+
_console = Console()
|
| 35 |
+
|
| 36 |
+
return _console
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def reconfigure(*args: Any, **kwargs: Any) -> None:
|
| 40 |
+
"""Reconfigures the global console by replacing it with another.
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
*args (Any): Positional arguments for the replacement :class:`~rich.console.Console`.
|
| 44 |
+
**kwargs (Any): Keyword arguments for the replacement :class:`~rich.console.Console`.
|
| 45 |
+
"""
|
| 46 |
+
from pip._vendor.rich.console import Console
|
| 47 |
+
|
| 48 |
+
new_console = Console(*args, **kwargs)
|
| 49 |
+
_console = get_console()
|
| 50 |
+
_console.__dict__ = new_console.__dict__
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def print(
|
| 54 |
+
*objects: Any,
|
| 55 |
+
sep: str = " ",
|
| 56 |
+
end: str = "\n",
|
| 57 |
+
file: Optional[IO[str]] = None,
|
| 58 |
+
flush: bool = False,
|
| 59 |
+
) -> None:
|
| 60 |
+
r"""Print object(s) supplied via positional arguments.
|
| 61 |
+
This function has an identical signature to the built-in print.
|
| 62 |
+
For more advanced features, see the :class:`~rich.console.Console` class.
|
| 63 |
+
|
| 64 |
+
Args:
|
| 65 |
+
sep (str, optional): Separator between printed objects. Defaults to " ".
|
| 66 |
+
end (str, optional): Character to write at end of output. Defaults to "\\n".
|
| 67 |
+
file (IO[str], optional): File to write to, or None for stdout. Defaults to None.
|
| 68 |
+
flush (bool, optional): Has no effect as Rich always flushes output. Defaults to False.
|
| 69 |
+
|
| 70 |
+
"""
|
| 71 |
+
from .console import Console
|
| 72 |
+
|
| 73 |
+
write_console = get_console() if file is None else Console(file=file)
|
| 74 |
+
return write_console.print(*objects, sep=sep, end=end)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def print_json(
|
| 78 |
+
json: Optional[str] = None,
|
| 79 |
+
*,
|
| 80 |
+
data: Any = None,
|
| 81 |
+
indent: Union[None, int, str] = 2,
|
| 82 |
+
highlight: bool = True,
|
| 83 |
+
skip_keys: bool = False,
|
| 84 |
+
ensure_ascii: bool = False,
|
| 85 |
+
check_circular: bool = True,
|
| 86 |
+
allow_nan: bool = True,
|
| 87 |
+
default: Optional[Callable[[Any], Any]] = None,
|
| 88 |
+
sort_keys: bool = False,
|
| 89 |
+
) -> None:
|
| 90 |
+
"""Pretty prints JSON. Output will be valid JSON.
|
| 91 |
+
|
| 92 |
+
Args:
|
| 93 |
+
json (str): A string containing JSON.
|
| 94 |
+
data (Any): If json is not supplied, then encode this data.
|
| 95 |
+
indent (int, optional): Number of spaces to indent. Defaults to 2.
|
| 96 |
+
highlight (bool, optional): Enable highlighting of output: Defaults to True.
|
| 97 |
+
skip_keys (bool, optional): Skip keys not of a basic type. Defaults to False.
|
| 98 |
+
ensure_ascii (bool, optional): Escape all non-ascii characters. Defaults to False.
|
| 99 |
+
check_circular (bool, optional): Check for circular references. Defaults to True.
|
| 100 |
+
allow_nan (bool, optional): Allow NaN and Infinity values. Defaults to True.
|
| 101 |
+
default (Callable, optional): A callable that converts values that can not be encoded
|
| 102 |
+
in to something that can be JSON encoded. Defaults to None.
|
| 103 |
+
sort_keys (bool, optional): Sort dictionary keys. Defaults to False.
|
| 104 |
+
"""
|
| 105 |
+
|
| 106 |
+
get_console().print_json(
|
| 107 |
+
json,
|
| 108 |
+
data=data,
|
| 109 |
+
indent=indent,
|
| 110 |
+
highlight=highlight,
|
| 111 |
+
skip_keys=skip_keys,
|
| 112 |
+
ensure_ascii=ensure_ascii,
|
| 113 |
+
check_circular=check_circular,
|
| 114 |
+
allow_nan=allow_nan,
|
| 115 |
+
default=default,
|
| 116 |
+
sort_keys=sort_keys,
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def inspect(
|
| 121 |
+
obj: Any,
|
| 122 |
+
*,
|
| 123 |
+
console: Optional["Console"] = None,
|
| 124 |
+
title: Optional[str] = None,
|
| 125 |
+
help: bool = False,
|
| 126 |
+
methods: bool = False,
|
| 127 |
+
docs: bool = True,
|
| 128 |
+
private: bool = False,
|
| 129 |
+
dunder: bool = False,
|
| 130 |
+
sort: bool = True,
|
| 131 |
+
all: bool = False,
|
| 132 |
+
value: bool = True,
|
| 133 |
+
) -> None:
|
| 134 |
+
"""Inspect any Python object.
|
| 135 |
+
|
| 136 |
+
* inspect(<OBJECT>) to see summarized info.
|
| 137 |
+
* inspect(<OBJECT>, methods=True) to see methods.
|
| 138 |
+
* inspect(<OBJECT>, help=True) to see full (non-abbreviated) help.
|
| 139 |
+
* inspect(<OBJECT>, private=True) to see private attributes (single underscore).
|
| 140 |
+
* inspect(<OBJECT>, dunder=True) to see attributes beginning with double underscore.
|
| 141 |
+
* inspect(<OBJECT>, all=True) to see all attributes.
|
| 142 |
+
|
| 143 |
+
Args:
|
| 144 |
+
obj (Any): An object to inspect.
|
| 145 |
+
title (str, optional): Title to display over inspect result, or None use type. Defaults to None.
|
| 146 |
+
help (bool, optional): Show full help text rather than just first paragraph. Defaults to False.
|
| 147 |
+
methods (bool, optional): Enable inspection of callables. Defaults to False.
|
| 148 |
+
docs (bool, optional): Also render doc strings. Defaults to True.
|
| 149 |
+
private (bool, optional): Show private attributes (beginning with underscore). Defaults to False.
|
| 150 |
+
dunder (bool, optional): Show attributes starting with double underscore. Defaults to False.
|
| 151 |
+
sort (bool, optional): Sort attributes alphabetically. Defaults to True.
|
| 152 |
+
all (bool, optional): Show all attributes. Defaults to False.
|
| 153 |
+
value (bool, optional): Pretty print value. Defaults to True.
|
| 154 |
+
"""
|
| 155 |
+
_console = console or get_console()
|
| 156 |
+
from pip._vendor.rich._inspect import Inspect
|
| 157 |
+
|
| 158 |
+
# Special case for inspect(inspect)
|
| 159 |
+
is_inspect = obj is inspect
|
| 160 |
+
|
| 161 |
+
_inspect = Inspect(
|
| 162 |
+
obj,
|
| 163 |
+
title=title,
|
| 164 |
+
help=is_inspect or help,
|
| 165 |
+
methods=is_inspect or methods,
|
| 166 |
+
docs=is_inspect or docs,
|
| 167 |
+
private=private,
|
| 168 |
+
dunder=dunder,
|
| 169 |
+
sort=sort,
|
| 170 |
+
all=all,
|
| 171 |
+
value=value,
|
| 172 |
+
)
|
| 173 |
+
_console.print(_inspect)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
if __name__ == "__main__": # pragma: no cover
|
| 177 |
+
print("Hello, **World**")
|
.venv/Lib/site-packages/pip/_vendor/rich/__main__.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import colorsys
|
| 2 |
+
import io
|
| 3 |
+
from time import process_time
|
| 4 |
+
|
| 5 |
+
from pip._vendor.rich import box
|
| 6 |
+
from pip._vendor.rich.color import Color
|
| 7 |
+
from pip._vendor.rich.console import Console, ConsoleOptions, Group, RenderableType, RenderResult
|
| 8 |
+
from pip._vendor.rich.markdown import Markdown
|
| 9 |
+
from pip._vendor.rich.measure import Measurement
|
| 10 |
+
from pip._vendor.rich.pretty import Pretty
|
| 11 |
+
from pip._vendor.rich.segment import Segment
|
| 12 |
+
from pip._vendor.rich.style import Style
|
| 13 |
+
from pip._vendor.rich.syntax import Syntax
|
| 14 |
+
from pip._vendor.rich.table import Table
|
| 15 |
+
from pip._vendor.rich.text import Text
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class ColorBox:
|
| 19 |
+
def __rich_console__(
|
| 20 |
+
self, console: Console, options: ConsoleOptions
|
| 21 |
+
) -> RenderResult:
|
| 22 |
+
for y in range(0, 5):
|
| 23 |
+
for x in range(options.max_width):
|
| 24 |
+
h = x / options.max_width
|
| 25 |
+
l = 0.1 + ((y / 5) * 0.7)
|
| 26 |
+
r1, g1, b1 = colorsys.hls_to_rgb(h, l, 1.0)
|
| 27 |
+
r2, g2, b2 = colorsys.hls_to_rgb(h, l + 0.7 / 10, 1.0)
|
| 28 |
+
bgcolor = Color.from_rgb(r1 * 255, g1 * 255, b1 * 255)
|
| 29 |
+
color = Color.from_rgb(r2 * 255, g2 * 255, b2 * 255)
|
| 30 |
+
yield Segment("▄", Style(color=color, bgcolor=bgcolor))
|
| 31 |
+
yield Segment.line()
|
| 32 |
+
|
| 33 |
+
def __rich_measure__(
|
| 34 |
+
self, console: "Console", options: ConsoleOptions
|
| 35 |
+
) -> Measurement:
|
| 36 |
+
return Measurement(1, options.max_width)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def make_test_card() -> Table:
|
| 40 |
+
"""Get a renderable that demonstrates a number of features."""
|
| 41 |
+
table = Table.grid(padding=1, pad_edge=True)
|
| 42 |
+
table.title = "Rich features"
|
| 43 |
+
table.add_column("Feature", no_wrap=True, justify="center", style="bold red")
|
| 44 |
+
table.add_column("Demonstration")
|
| 45 |
+
|
| 46 |
+
color_table = Table(
|
| 47 |
+
box=None,
|
| 48 |
+
expand=False,
|
| 49 |
+
show_header=False,
|
| 50 |
+
show_edge=False,
|
| 51 |
+
pad_edge=False,
|
| 52 |
+
)
|
| 53 |
+
color_table.add_row(
|
| 54 |
+
(
|
| 55 |
+
"✓ [bold green]4-bit color[/]\n"
|
| 56 |
+
"✓ [bold blue]8-bit color[/]\n"
|
| 57 |
+
"✓ [bold magenta]Truecolor (16.7 million)[/]\n"
|
| 58 |
+
"✓ [bold yellow]Dumb terminals[/]\n"
|
| 59 |
+
"✓ [bold cyan]Automatic color conversion"
|
| 60 |
+
),
|
| 61 |
+
ColorBox(),
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
table.add_row("Colors", color_table)
|
| 65 |
+
|
| 66 |
+
table.add_row(
|
| 67 |
+
"Styles",
|
| 68 |
+
"All ansi styles: [bold]bold[/], [dim]dim[/], [italic]italic[/italic], [underline]underline[/], [strike]strikethrough[/], [reverse]reverse[/], and even [blink]blink[/].",
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque in metus sed sapien ultricies pretium a at justo. Maecenas luctus velit et auctor maximus."
|
| 72 |
+
lorem_table = Table.grid(padding=1, collapse_padding=True)
|
| 73 |
+
lorem_table.pad_edge = False
|
| 74 |
+
lorem_table.add_row(
|
| 75 |
+
Text(lorem, justify="left", style="green"),
|
| 76 |
+
Text(lorem, justify="center", style="yellow"),
|
| 77 |
+
Text(lorem, justify="right", style="blue"),
|
| 78 |
+
Text(lorem, justify="full", style="red"),
|
| 79 |
+
)
|
| 80 |
+
table.add_row(
|
| 81 |
+
"Text",
|
| 82 |
+
Group(
|
| 83 |
+
Text.from_markup(
|
| 84 |
+
"""Word wrap text. Justify [green]left[/], [yellow]center[/], [blue]right[/] or [red]full[/].\n"""
|
| 85 |
+
),
|
| 86 |
+
lorem_table,
|
| 87 |
+
),
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
def comparison(renderable1: RenderableType, renderable2: RenderableType) -> Table:
|
| 91 |
+
table = Table(show_header=False, pad_edge=False, box=None, expand=True)
|
| 92 |
+
table.add_column("1", ratio=1)
|
| 93 |
+
table.add_column("2", ratio=1)
|
| 94 |
+
table.add_row(renderable1, renderable2)
|
| 95 |
+
return table
|
| 96 |
+
|
| 97 |
+
table.add_row(
|
| 98 |
+
"Asian\nlanguage\nsupport",
|
| 99 |
+
":flag_for_china: 该库支持中文,日文和韩文文本!\n:flag_for_japan: ライブラリは中国語、日本語、韓国語のテキストをサポートしています\n:flag_for_south_korea: 이 라이브러리는 중국어, 일본어 및 한국어 텍스트를 지원합니다",
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
markup_example = (
|
| 103 |
+
"[bold magenta]Rich[/] supports a simple [i]bbcode[/i]-like [b]markup[/b] for [yellow]color[/], [underline]style[/], and emoji! "
|
| 104 |
+
":+1: :apple: :ant: :bear: :baguette_bread: :bus: "
|
| 105 |
+
)
|
| 106 |
+
table.add_row("Markup", markup_example)
|
| 107 |
+
|
| 108 |
+
example_table = Table(
|
| 109 |
+
show_edge=False,
|
| 110 |
+
show_header=True,
|
| 111 |
+
expand=False,
|
| 112 |
+
row_styles=["none", "dim"],
|
| 113 |
+
box=box.SIMPLE,
|
| 114 |
+
)
|
| 115 |
+
example_table.add_column("[green]Date", style="green", no_wrap=True)
|
| 116 |
+
example_table.add_column("[blue]Title", style="blue")
|
| 117 |
+
example_table.add_column(
|
| 118 |
+
"[cyan]Production Budget",
|
| 119 |
+
style="cyan",
|
| 120 |
+
justify="right",
|
| 121 |
+
no_wrap=True,
|
| 122 |
+
)
|
| 123 |
+
example_table.add_column(
|
| 124 |
+
"[magenta]Box Office",
|
| 125 |
+
style="magenta",
|
| 126 |
+
justify="right",
|
| 127 |
+
no_wrap=True,
|
| 128 |
+
)
|
| 129 |
+
example_table.add_row(
|
| 130 |
+
"Dec 20, 2019",
|
| 131 |
+
"Star Wars: The Rise of Skywalker",
|
| 132 |
+
"$275,000,000",
|
| 133 |
+
"$375,126,118",
|
| 134 |
+
)
|
| 135 |
+
example_table.add_row(
|
| 136 |
+
"May 25, 2018",
|
| 137 |
+
"[b]Solo[/]: A Star Wars Story",
|
| 138 |
+
"$275,000,000",
|
| 139 |
+
"$393,151,347",
|
| 140 |
+
)
|
| 141 |
+
example_table.add_row(
|
| 142 |
+
"Dec 15, 2017",
|
| 143 |
+
"Star Wars Ep. VIII: The Last Jedi",
|
| 144 |
+
"$262,000,000",
|
| 145 |
+
"[bold]$1,332,539,889[/bold]",
|
| 146 |
+
)
|
| 147 |
+
example_table.add_row(
|
| 148 |
+
"May 19, 1999",
|
| 149 |
+
"Star Wars Ep. [b]I[/b]: [i]The phantom Menace",
|
| 150 |
+
"$115,000,000",
|
| 151 |
+
"$1,027,044,677",
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
table.add_row("Tables", example_table)
|
| 155 |
+
|
| 156 |
+
code = '''\
|
| 157 |
+
def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]:
|
| 158 |
+
"""Iterate and generate a tuple with a flag for last value."""
|
| 159 |
+
iter_values = iter(values)
|
| 160 |
+
try:
|
| 161 |
+
previous_value = next(iter_values)
|
| 162 |
+
except StopIteration:
|
| 163 |
+
return
|
| 164 |
+
for value in iter_values:
|
| 165 |
+
yield False, previous_value
|
| 166 |
+
previous_value = value
|
| 167 |
+
yield True, previous_value'''
|
| 168 |
+
|
| 169 |
+
pretty_data = {
|
| 170 |
+
"foo": [
|
| 171 |
+
3.1427,
|
| 172 |
+
(
|
| 173 |
+
"Paul Atreides",
|
| 174 |
+
"Vladimir Harkonnen",
|
| 175 |
+
"Thufir Hawat",
|
| 176 |
+
),
|
| 177 |
+
],
|
| 178 |
+
"atomic": (False, True, None),
|
| 179 |
+
}
|
| 180 |
+
table.add_row(
|
| 181 |
+
"Syntax\nhighlighting\n&\npretty\nprinting",
|
| 182 |
+
comparison(
|
| 183 |
+
Syntax(code, "python3", line_numbers=True, indent_guides=True),
|
| 184 |
+
Pretty(pretty_data, indent_guides=True),
|
| 185 |
+
),
|
| 186 |
+
)
|
| 187 |
+
|
| 188 |
+
markdown_example = """\
|
| 189 |
+
# Markdown
|
| 190 |
+
|
| 191 |
+
Supports much of the *markdown* __syntax__!
|
| 192 |
+
|
| 193 |
+
- Headers
|
| 194 |
+
- Basic formatting: **bold**, *italic*, `code`
|
| 195 |
+
- Block quotes
|
| 196 |
+
- Lists, and more...
|
| 197 |
+
"""
|
| 198 |
+
table.add_row(
|
| 199 |
+
"Markdown", comparison("[cyan]" + markdown_example, Markdown(markdown_example))
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
table.add_row(
|
| 203 |
+
"+more!",
|
| 204 |
+
"""Progress bars, columns, styled logging handler, tracebacks, etc...""",
|
| 205 |
+
)
|
| 206 |
+
return table
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
if __name__ == "__main__": # pragma: no cover
|
| 210 |
+
from pip._vendor.rich.panel import Panel
|
| 211 |
+
|
| 212 |
+
console = Console(
|
| 213 |
+
file=io.StringIO(),
|
| 214 |
+
force_terminal=True,
|
| 215 |
+
)
|
| 216 |
+
test_card = make_test_card()
|
| 217 |
+
|
| 218 |
+
# Print once to warm cache
|
| 219 |
+
start = process_time()
|
| 220 |
+
console.print(test_card)
|
| 221 |
+
pre_cache_taken = round((process_time() - start) * 1000.0, 1)
|
| 222 |
+
|
| 223 |
+
console.file = io.StringIO()
|
| 224 |
+
|
| 225 |
+
start = process_time()
|
| 226 |
+
console.print(test_card)
|
| 227 |
+
taken = round((process_time() - start) * 1000.0, 1)
|
| 228 |
+
|
| 229 |
+
c = Console(record=True)
|
| 230 |
+
c.print(test_card)
|
| 231 |
+
|
| 232 |
+
console = Console()
|
| 233 |
+
console.print(f"[dim]rendered in [not dim]{pre_cache_taken}ms[/] (cold cache)")
|
| 234 |
+
console.print(f"[dim]rendered in [not dim]{taken}ms[/] (warm cache)")
|
| 235 |
+
console.print()
|
| 236 |
+
console.print(
|
| 237 |
+
Panel.fit(
|
| 238 |
+
"[b magenta]Hope you enjoy using Rich![/]\n\n"
|
| 239 |
+
"Please consider sponsoring me if you get value from my work.\n\n"
|
| 240 |
+
"Even the price of a ☕ can brighten my day!\n\n"
|
| 241 |
+
"https://github.com/sponsors/willmcgugan",
|
| 242 |
+
border_style="red",
|
| 243 |
+
title="Help ensure Rich is maintained",
|
| 244 |
+
)
|
| 245 |
+
)
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/__init__.cpython-314.pyc
ADDED
|
Binary file (8.41 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_cell_widths.cpython-314.pyc
ADDED
|
Binary file (15.9 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_codes.cpython-314.pyc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ff25f5e0e460b84e08bd74293cabb0cb98963bb4ef7d584cf0d548178abede72
|
| 3 |
+
size 206008
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_emoji_replace.cpython-314.pyc
ADDED
|
Binary file (2.14 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_export_format.cpython-314.pyc
ADDED
|
Binary file (2.36 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_extension.cpython-314.pyc
ADDED
|
Binary file (694 Bytes). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_fileno.cpython-314.pyc
ADDED
|
Binary file (1.01 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_log_render.cpython-314.pyc
ADDED
|
Binary file (4.9 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_loop.cpython-314.pyc
ADDED
|
Binary file (2.54 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_null_file.cpython-314.pyc
ADDED
|
Binary file (6.63 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_palettes.cpython-314.pyc
ADDED
|
Binary file (9.41 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_pick.cpython-314.pyc
ADDED
|
Binary file (901 Bytes). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_ratio.cpython-314.pyc
ADDED
|
Binary file (7.66 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_spinners.cpython-314.pyc
ADDED
|
Binary file (15.7 kB). View file
|
|
|
.venv/Lib/site-packages/pip/_vendor/rich/__pycache__/_win32_console.cpython-314.pyc
ADDED
|
Binary file (33.5 kB). View file
|
|
|