ZhengyangZhang commited on
Commit
1bb7d7e
·
verified ·
1 Parent(s): 9a4e1b3

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py +33 -0
  2. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-312.pyc +0 -0
  15. lib/python3.12/site-packages/pip/_vendor/distlib/markers.py +162 -0
  16. lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py +447 -0
  17. lib/python3.12/site-packages/pip/_vendor/distlib/t32.exe +0 -0
  18. lib/python3.12/site-packages/pip/_vendor/distlib/version.py +750 -0
  19. lib/python3.12/site-packages/pip/_vendor/distlib/w32.exe +0 -0
  20. lib/python3.12/site-packages/pip/_vendor/distlib/wheel.py +1100 -0
  21. lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py +76 -0
  22. lib/python3.12/site-packages/pip/_vendor/rich/table.py +1007 -0
  23. lib/python3.12/site-packages/pip/_vendor/urllib3/__init__.py +102 -0
  24. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc +0 -0
  25. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc +0 -0
  26. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc +0 -0
  27. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc +0 -0
  30. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/pip/_vendor/urllib3/_collections.py +355 -0
  36. lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py +2 -0
  37. lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py +572 -0
  38. lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py +1140 -0
  39. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__init__.py +0 -0
  40. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc +0 -0
  41. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc +0 -0
  42. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-312.pyc +0 -0
  44. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc +0 -0
  45. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-312.pyc +0 -0
  46. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py +36 -0
  48. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py +0 -0
  49. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc +0 -0
  50. lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc +0 -0
lib/python3.12/site-packages/pip/_vendor/distlib/__init__.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012-2023 Vinay Sajip.
4
+ # Licensed to the Python Software Foundation under a contributor agreement.
5
+ # See LICENSE.txt and CONTRIBUTORS.txt.
6
+ #
7
+ import logging
8
+
9
+ __version__ = '0.3.9'
10
+
11
+
12
+ class DistlibException(Exception):
13
+ pass
14
+
15
+
16
+ try:
17
+ from logging import NullHandler
18
+ except ImportError: # pragma: no cover
19
+
20
+ class NullHandler(logging.Handler):
21
+
22
+ def handle(self, record):
23
+ pass
24
+
25
+ def emit(self, record):
26
+ pass
27
+
28
+ def createLock(self):
29
+ self.lock = None
30
+
31
+
32
+ logger = logging.getLogger(__name__)
33
+ logger.addHandler(NullHandler())
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (1.28 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc ADDED
Binary file (45.5 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-312.pyc ADDED
Binary file (65.6 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-312.pyc ADDED
Binary file (24.3 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc ADDED
Binary file (59.9 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc ADDED
Binary file (15.1 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc ADDED
Binary file (7.66 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc ADDED
Binary file (41.6 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc ADDED
Binary file (17.3 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc ADDED
Binary file (19.8 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-312.pyc ADDED
Binary file (88 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-312.pyc ADDED
Binary file (30.4 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-312.pyc ADDED
Binary file (52.6 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/markers.py ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012-2023 Vinay Sajip.
4
+ # Licensed to the Python Software Foundation under a contributor agreement.
5
+ # See LICENSE.txt and CONTRIBUTORS.txt.
6
+ #
7
+ """
8
+ Parser for the environment markers micro-language defined in PEP 508.
9
+ """
10
+
11
+ # Note: In PEP 345, the micro-language was Python compatible, so the ast
12
+ # module could be used to parse it. However, PEP 508 introduced operators such
13
+ # as ~= and === which aren't in Python, necessitating a different approach.
14
+
15
+ import os
16
+ import re
17
+ import sys
18
+ import platform
19
+
20
+ from .compat import string_types
21
+ from .util import in_venv, parse_marker
22
+ from .version import LegacyVersion as LV
23
+
24
+ __all__ = ['interpret']
25
+
26
+ _VERSION_PATTERN = re.compile(r'((\d+(\.\d+)*\w*)|\'(\d+(\.\d+)*\w*)\'|\"(\d+(\.\d+)*\w*)\")')
27
+ _VERSION_MARKERS = {'python_version', 'python_full_version'}
28
+
29
+
30
+ def _is_version_marker(s):
31
+ return isinstance(s, string_types) and s in _VERSION_MARKERS
32
+
33
+
34
+ def _is_literal(o):
35
+ if not isinstance(o, string_types) or not o:
36
+ return False
37
+ return o[0] in '\'"'
38
+
39
+
40
+ def _get_versions(s):
41
+ return {LV(m.groups()[0]) for m in _VERSION_PATTERN.finditer(s)}
42
+
43
+
44
+ class Evaluator(object):
45
+ """
46
+ This class is used to evaluate marker expressions.
47
+ """
48
+
49
+ operations = {
50
+ '==': lambda x, y: x == y,
51
+ '===': lambda x, y: x == y,
52
+ '~=': lambda x, y: x == y or x > y,
53
+ '!=': lambda x, y: x != y,
54
+ '<': lambda x, y: x < y,
55
+ '<=': lambda x, y: x == y or x < y,
56
+ '>': lambda x, y: x > y,
57
+ '>=': lambda x, y: x == y or x > y,
58
+ 'and': lambda x, y: x and y,
59
+ 'or': lambda x, y: x or y,
60
+ 'in': lambda x, y: x in y,
61
+ 'not in': lambda x, y: x not in y,
62
+ }
63
+
64
+ def evaluate(self, expr, context):
65
+ """
66
+ Evaluate a marker expression returned by the :func:`parse_requirement`
67
+ function in the specified context.
68
+ """
69
+ if isinstance(expr, string_types):
70
+ if expr[0] in '\'"':
71
+ result = expr[1:-1]
72
+ else:
73
+ if expr not in context:
74
+ raise SyntaxError('unknown variable: %s' % expr)
75
+ result = context[expr]
76
+ else:
77
+ assert isinstance(expr, dict)
78
+ op = expr['op']
79
+ if op not in self.operations:
80
+ raise NotImplementedError('op not implemented: %s' % op)
81
+ elhs = expr['lhs']
82
+ erhs = expr['rhs']
83
+ if _is_literal(expr['lhs']) and _is_literal(expr['rhs']):
84
+ raise SyntaxError('invalid comparison: %s %s %s' % (elhs, op, erhs))
85
+
86
+ lhs = self.evaluate(elhs, context)
87
+ rhs = self.evaluate(erhs, context)
88
+ if ((_is_version_marker(elhs) or _is_version_marker(erhs)) and
89
+ op in ('<', '<=', '>', '>=', '===', '==', '!=', '~=')):
90
+ lhs = LV(lhs)
91
+ rhs = LV(rhs)
92
+ elif _is_version_marker(elhs) and op in ('in', 'not in'):
93
+ lhs = LV(lhs)
94
+ rhs = _get_versions(rhs)
95
+ result = self.operations[op](lhs, rhs)
96
+ return result
97
+
98
+
99
+ _DIGITS = re.compile(r'\d+\.\d+')
100
+
101
+
102
+ def default_context():
103
+
104
+ def format_full_version(info):
105
+ version = '%s.%s.%s' % (info.major, info.minor, info.micro)
106
+ kind = info.releaselevel
107
+ if kind != 'final':
108
+ version += kind[0] + str(info.serial)
109
+ return version
110
+
111
+ if hasattr(sys, 'implementation'):
112
+ implementation_version = format_full_version(sys.implementation.version)
113
+ implementation_name = sys.implementation.name
114
+ else:
115
+ implementation_version = '0'
116
+ implementation_name = ''
117
+
118
+ ppv = platform.python_version()
119
+ m = _DIGITS.match(ppv)
120
+ pv = m.group(0)
121
+ result = {
122
+ 'implementation_name': implementation_name,
123
+ 'implementation_version': implementation_version,
124
+ 'os_name': os.name,
125
+ 'platform_machine': platform.machine(),
126
+ 'platform_python_implementation': platform.python_implementation(),
127
+ 'platform_release': platform.release(),
128
+ 'platform_system': platform.system(),
129
+ 'platform_version': platform.version(),
130
+ 'platform_in_venv': str(in_venv()),
131
+ 'python_full_version': ppv,
132
+ 'python_version': pv,
133
+ 'sys_platform': sys.platform,
134
+ }
135
+ return result
136
+
137
+
138
+ DEFAULT_CONTEXT = default_context()
139
+ del default_context
140
+
141
+ evaluator = Evaluator()
142
+
143
+
144
+ def interpret(marker, execution_context=None):
145
+ """
146
+ Interpret a marker and return a result depending on environment.
147
+
148
+ :param marker: The marker to interpret.
149
+ :type marker: str
150
+ :param execution_context: The context used for name lookup.
151
+ :type execution_context: mapping
152
+ """
153
+ try:
154
+ expr, rest = parse_marker(marker)
155
+ except Exception as e:
156
+ raise SyntaxError('Unable to interpret marker syntax: %s: %s' % (marker, e))
157
+ if rest and rest[0] != '#':
158
+ raise SyntaxError('unexpected trailing data in marker: %s: %s' % (marker, rest))
159
+ context = dict(DEFAULT_CONTEXT)
160
+ if execution_context:
161
+ context.update(execution_context)
162
+ return evaluator.evaluate(expr, context)
lib/python3.12/site-packages/pip/_vendor/distlib/scripts.py ADDED
@@ -0,0 +1,447 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013-2023 Vinay Sajip.
4
+ # Licensed to the Python Software Foundation under a contributor agreement.
5
+ # See LICENSE.txt and CONTRIBUTORS.txt.
6
+ #
7
+ from io import BytesIO
8
+ import logging
9
+ import os
10
+ import re
11
+ import struct
12
+ import sys
13
+ import time
14
+ from zipfile import ZipInfo
15
+
16
+ from .compat import sysconfig, detect_encoding, ZipFile
17
+ from .resources import finder
18
+ from .util import (FileOperator, get_export_entry, convert_path, get_executable, get_platform, in_venv)
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ _DEFAULT_MANIFEST = '''
23
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
24
+ <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
25
+ <assemblyIdentity version="1.0.0.0"
26
+ processorArchitecture="X86"
27
+ name="%s"
28
+ type="win32"/>
29
+
30
+ <!-- Identify the application security requirements. -->
31
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
32
+ <security>
33
+ <requestedPrivileges>
34
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
35
+ </requestedPrivileges>
36
+ </security>
37
+ </trustInfo>
38
+ </assembly>'''.strip()
39
+
40
+ # check if Python is called on the first line with this expression
41
+ FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$')
42
+ SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*-
43
+ import re
44
+ import sys
45
+ from %(module)s import %(import_name)s
46
+ if __name__ == '__main__':
47
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
48
+ sys.exit(%(func)s())
49
+ '''
50
+
51
+ # Pre-fetch the contents of all executable wrapper stubs.
52
+ # This is to address https://github.com/pypa/pip/issues/12666.
53
+ # When updating pip, we rename the old pip in place before installing the
54
+ # new version. If we try to fetch a wrapper *after* that rename, the finder
55
+ # machinery will be confused as the package is no longer available at the
56
+ # location where it was imported from. So we load everything into memory in
57
+ # advance.
58
+
59
+ if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'):
60
+ # Issue 31: don't hardcode an absolute package name, but
61
+ # determine it relative to the current package
62
+ DISTLIB_PACKAGE = __name__.rsplit('.', 1)[0]
63
+
64
+ WRAPPERS = {
65
+ r.name: r.bytes
66
+ for r in finder(DISTLIB_PACKAGE).iterator("")
67
+ if r.name.endswith(".exe")
68
+ }
69
+
70
+
71
+ def enquote_executable(executable):
72
+ if ' ' in executable:
73
+ # make sure we quote only the executable in case of env
74
+ # for example /usr/bin/env "/dir with spaces/bin/jython"
75
+ # instead of "/usr/bin/env /dir with spaces/bin/jython"
76
+ # otherwise whole
77
+ if executable.startswith('/usr/bin/env '):
78
+ env, _executable = executable.split(' ', 1)
79
+ if ' ' in _executable and not _executable.startswith('"'):
80
+ executable = '%s "%s"' % (env, _executable)
81
+ else:
82
+ if not executable.startswith('"'):
83
+ executable = '"%s"' % executable
84
+ return executable
85
+
86
+
87
+ # Keep the old name around (for now), as there is at least one project using it!
88
+ _enquote_executable = enquote_executable
89
+
90
+
91
+ class ScriptMaker(object):
92
+ """
93
+ A class to copy or create scripts from source scripts or callable
94
+ specifications.
95
+ """
96
+ script_template = SCRIPT_TEMPLATE
97
+
98
+ executable = None # for shebangs
99
+
100
+ def __init__(self, source_dir, target_dir, add_launchers=True, dry_run=False, fileop=None):
101
+ self.source_dir = source_dir
102
+ self.target_dir = target_dir
103
+ self.add_launchers = add_launchers
104
+ self.force = False
105
+ self.clobber = False
106
+ # It only makes sense to set mode bits on POSIX.
107
+ self.set_mode = (os.name == 'posix') or (os.name == 'java' and os._name == 'posix')
108
+ self.variants = set(('', 'X.Y'))
109
+ self._fileop = fileop or FileOperator(dry_run)
110
+
111
+ self._is_nt = os.name == 'nt' or (os.name == 'java' and os._name == 'nt')
112
+ self.version_info = sys.version_info
113
+
114
+ def _get_alternate_executable(self, executable, options):
115
+ if options.get('gui', False) and self._is_nt: # pragma: no cover
116
+ dn, fn = os.path.split(executable)
117
+ fn = fn.replace('python', 'pythonw')
118
+ executable = os.path.join(dn, fn)
119
+ return executable
120
+
121
+ if sys.platform.startswith('java'): # pragma: no cover
122
+
123
+ def _is_shell(self, executable):
124
+ """
125
+ Determine if the specified executable is a script
126
+ (contains a #! line)
127
+ """
128
+ try:
129
+ with open(executable) as fp:
130
+ return fp.read(2) == '#!'
131
+ except (OSError, IOError):
132
+ logger.warning('Failed to open %s', executable)
133
+ return False
134
+
135
+ def _fix_jython_executable(self, executable):
136
+ if self._is_shell(executable):
137
+ # Workaround for Jython is not needed on Linux systems.
138
+ import java
139
+
140
+ if java.lang.System.getProperty('os.name') == 'Linux':
141
+ return executable
142
+ elif executable.lower().endswith('jython.exe'):
143
+ # Use wrapper exe for Jython on Windows
144
+ return executable
145
+ return '/usr/bin/env %s' % executable
146
+
147
+ def _build_shebang(self, executable, post_interp):
148
+ """
149
+ Build a shebang line. In the simple case (on Windows, or a shebang line
150
+ which is not too long or contains spaces) use a simple formulation for
151
+ the shebang. Otherwise, use /bin/sh as the executable, with a contrived
152
+ shebang which allows the script to run either under Python or sh, using
153
+ suitable quoting. Thanks to Harald Nordgren for his input.
154
+
155
+ See also: http://www.in-ulm.de/~mascheck/various/shebang/#length
156
+ https://hg.mozilla.org/mozilla-central/file/tip/mach
157
+ """
158
+ if os.name != 'posix':
159
+ simple_shebang = True
160
+ elif getattr(sys, "cross_compiling", False):
161
+ # In a cross-compiling environment, the shebang will likely be a
162
+ # script; this *must* be invoked with the "safe" version of the
163
+ # shebang, or else using os.exec() to run the entry script will
164
+ # fail, raising "OSError 8 [Errno 8] Exec format error".
165
+ simple_shebang = False
166
+ else:
167
+ # Add 3 for '#!' prefix and newline suffix.
168
+ shebang_length = len(executable) + len(post_interp) + 3
169
+ if sys.platform == 'darwin':
170
+ max_shebang_length = 512
171
+ else:
172
+ max_shebang_length = 127
173
+ simple_shebang = ((b' ' not in executable) and (shebang_length <= max_shebang_length))
174
+
175
+ if simple_shebang:
176
+ result = b'#!' + executable + post_interp + b'\n'
177
+ else:
178
+ result = b'#!/bin/sh\n'
179
+ result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n'
180
+ result += b"' '''\n"
181
+ return result
182
+
183
+ def _get_shebang(self, encoding, post_interp=b'', options=None):
184
+ enquote = True
185
+ if self.executable:
186
+ executable = self.executable
187
+ enquote = False # assume this will be taken care of
188
+ elif not sysconfig.is_python_build():
189
+ executable = get_executable()
190
+ elif in_venv(): # pragma: no cover
191
+ executable = os.path.join(sysconfig.get_path('scripts'), 'python%s' % sysconfig.get_config_var('EXE'))
192
+ else: # pragma: no cover
193
+ if os.name == 'nt':
194
+ # for Python builds from source on Windows, no Python executables with
195
+ # a version suffix are created, so we use python.exe
196
+ executable = os.path.join(sysconfig.get_config_var('BINDIR'),
197
+ 'python%s' % (sysconfig.get_config_var('EXE')))
198
+ else:
199
+ executable = os.path.join(
200
+ sysconfig.get_config_var('BINDIR'),
201
+ 'python%s%s' % (sysconfig.get_config_var('VERSION'), sysconfig.get_config_var('EXE')))
202
+ if options:
203
+ executable = self._get_alternate_executable(executable, options)
204
+
205
+ if sys.platform.startswith('java'): # pragma: no cover
206
+ executable = self._fix_jython_executable(executable)
207
+
208
+ # Normalise case for Windows - COMMENTED OUT
209
+ # executable = os.path.normcase(executable)
210
+ # N.B. The normalising operation above has been commented out: See
211
+ # issue #124. Although paths in Windows are generally case-insensitive,
212
+ # they aren't always. For example, a path containing a ẞ (which is a
213
+ # LATIN CAPITAL LETTER SHARP S - U+1E9E) is normcased to ß (which is a
214
+ # LATIN SMALL LETTER SHARP S' - U+00DF). The two are not considered by
215
+ # Windows as equivalent in path names.
216
+
217
+ # If the user didn't specify an executable, it may be necessary to
218
+ # cater for executable paths with spaces (not uncommon on Windows)
219
+ if enquote:
220
+ executable = enquote_executable(executable)
221
+ # Issue #51: don't use fsencode, since we later try to
222
+ # check that the shebang is decodable using utf-8.
223
+ executable = executable.encode('utf-8')
224
+ # in case of IronPython, play safe and enable frames support
225
+ if (sys.platform == 'cli' and '-X:Frames' not in post_interp and
226
+ '-X:FullFrames' not in post_interp): # pragma: no cover
227
+ post_interp += b' -X:Frames'
228
+ shebang = self._build_shebang(executable, post_interp)
229
+ # Python parser starts to read a script using UTF-8 until
230
+ # it gets a #coding:xxx cookie. The shebang has to be the
231
+ # first line of a file, the #coding:xxx cookie cannot be
232
+ # written before. So the shebang has to be decodable from
233
+ # UTF-8.
234
+ try:
235
+ shebang.decode('utf-8')
236
+ except UnicodeDecodeError: # pragma: no cover
237
+ raise ValueError('The shebang (%r) is not decodable from utf-8' % shebang)
238
+ # If the script is encoded to a custom encoding (use a
239
+ # #coding:xxx cookie), the shebang has to be decodable from
240
+ # the script encoding too.
241
+ if encoding != 'utf-8':
242
+ try:
243
+ shebang.decode(encoding)
244
+ except UnicodeDecodeError: # pragma: no cover
245
+ raise ValueError('The shebang (%r) is not decodable '
246
+ 'from the script encoding (%r)' % (shebang, encoding))
247
+ return shebang
248
+
249
+ def _get_script_text(self, entry):
250
+ return self.script_template % dict(
251
+ module=entry.prefix, import_name=entry.suffix.split('.')[0], func=entry.suffix)
252
+
253
+ manifest = _DEFAULT_MANIFEST
254
+
255
+ def get_manifest(self, exename):
256
+ base = os.path.basename(exename)
257
+ return self.manifest % base
258
+
259
+ def _write_script(self, names, shebang, script_bytes, filenames, ext):
260
+ use_launcher = self.add_launchers and self._is_nt
261
+ if not use_launcher:
262
+ script_bytes = shebang + script_bytes
263
+ else: # pragma: no cover
264
+ if ext == 'py':
265
+ launcher = self._get_launcher('t')
266
+ else:
267
+ launcher = self._get_launcher('w')
268
+ stream = BytesIO()
269
+ with ZipFile(stream, 'w') as zf:
270
+ source_date_epoch = os.environ.get('SOURCE_DATE_EPOCH')
271
+ if source_date_epoch:
272
+ date_time = time.gmtime(int(source_date_epoch))[:6]
273
+ zinfo = ZipInfo(filename='__main__.py', date_time=date_time)
274
+ zf.writestr(zinfo, script_bytes)
275
+ else:
276
+ zf.writestr('__main__.py', script_bytes)
277
+ zip_data = stream.getvalue()
278
+ script_bytes = launcher + shebang + zip_data
279
+ for name in names:
280
+ outname = os.path.join(self.target_dir, name)
281
+ if use_launcher: # pragma: no cover
282
+ n, e = os.path.splitext(outname)
283
+ if e.startswith('.py'):
284
+ outname = n
285
+ outname = '%s.exe' % outname
286
+ try:
287
+ self._fileop.write_binary_file(outname, script_bytes)
288
+ except Exception:
289
+ # Failed writing an executable - it might be in use.
290
+ logger.warning('Failed to write executable - trying to '
291
+ 'use .deleteme logic')
292
+ dfname = '%s.deleteme' % outname
293
+ if os.path.exists(dfname):
294
+ os.remove(dfname) # Not allowed to fail here
295
+ os.rename(outname, dfname) # nor here
296
+ self._fileop.write_binary_file(outname, script_bytes)
297
+ logger.debug('Able to replace executable using '
298
+ '.deleteme logic')
299
+ try:
300
+ os.remove(dfname)
301
+ except Exception:
302
+ pass # still in use - ignore error
303
+ else:
304
+ if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover
305
+ outname = '%s.%s' % (outname, ext)
306
+ if os.path.exists(outname) and not self.clobber:
307
+ logger.warning('Skipping existing file %s', outname)
308
+ continue
309
+ self._fileop.write_binary_file(outname, script_bytes)
310
+ if self.set_mode:
311
+ self._fileop.set_executable_mode([outname])
312
+ filenames.append(outname)
313
+
314
+ variant_separator = '-'
315
+
316
+ def get_script_filenames(self, name):
317
+ result = set()
318
+ if '' in self.variants:
319
+ result.add(name)
320
+ if 'X' in self.variants:
321
+ result.add('%s%s' % (name, self.version_info[0]))
322
+ if 'X.Y' in self.variants:
323
+ result.add('%s%s%s.%s' % (name, self.variant_separator, self.version_info[0], self.version_info[1]))
324
+ return result
325
+
326
+ def _make_script(self, entry, filenames, options=None):
327
+ post_interp = b''
328
+ if options:
329
+ args = options.get('interpreter_args', [])
330
+ if args:
331
+ args = ' %s' % ' '.join(args)
332
+ post_interp = args.encode('utf-8')
333
+ shebang = self._get_shebang('utf-8', post_interp, options=options)
334
+ script = self._get_script_text(entry).encode('utf-8')
335
+ scriptnames = self.get_script_filenames(entry.name)
336
+ if options and options.get('gui', False):
337
+ ext = 'pyw'
338
+ else:
339
+ ext = 'py'
340
+ self._write_script(scriptnames, shebang, script, filenames, ext)
341
+
342
+ def _copy_script(self, script, filenames):
343
+ adjust = False
344
+ script = os.path.join(self.source_dir, convert_path(script))
345
+ outname = os.path.join(self.target_dir, os.path.basename(script))
346
+ if not self.force and not self._fileop.newer(script, outname):
347
+ logger.debug('not copying %s (up-to-date)', script)
348
+ return
349
+
350
+ # Always open the file, but ignore failures in dry-run mode --
351
+ # that way, we'll get accurate feedback if we can read the
352
+ # script.
353
+ try:
354
+ f = open(script, 'rb')
355
+ except IOError: # pragma: no cover
356
+ if not self.dry_run:
357
+ raise
358
+ f = None
359
+ else:
360
+ first_line = f.readline()
361
+ if not first_line: # pragma: no cover
362
+ logger.warning('%s is an empty file (skipping)', script)
363
+ return
364
+
365
+ match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n'))
366
+ if match:
367
+ adjust = True
368
+ post_interp = match.group(1) or b''
369
+
370
+ if not adjust:
371
+ if f:
372
+ f.close()
373
+ self._fileop.copy_file(script, outname)
374
+ if self.set_mode:
375
+ self._fileop.set_executable_mode([outname])
376
+ filenames.append(outname)
377
+ else:
378
+ logger.info('copying and adjusting %s -> %s', script, self.target_dir)
379
+ if not self._fileop.dry_run:
380
+ encoding, lines = detect_encoding(f.readline)
381
+ f.seek(0)
382
+ shebang = self._get_shebang(encoding, post_interp)
383
+ if b'pythonw' in first_line: # pragma: no cover
384
+ ext = 'pyw'
385
+ else:
386
+ ext = 'py'
387
+ n = os.path.basename(outname)
388
+ self._write_script([n], shebang, f.read(), filenames, ext)
389
+ if f:
390
+ f.close()
391
+
392
+ @property
393
+ def dry_run(self):
394
+ return self._fileop.dry_run
395
+
396
+ @dry_run.setter
397
+ def dry_run(self, value):
398
+ self._fileop.dry_run = value
399
+
400
+ if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover
401
+ # Executable launcher support.
402
+ # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/
403
+
404
+ def _get_launcher(self, kind):
405
+ if struct.calcsize('P') == 8: # 64-bit
406
+ bits = '64'
407
+ else:
408
+ bits = '32'
409
+ platform_suffix = '-arm' if get_platform() == 'win-arm64' else ''
410
+ name = '%s%s%s.exe' % (kind, bits, platform_suffix)
411
+ if name not in WRAPPERS:
412
+ msg = ('Unable to find resource %s in package %s' %
413
+ (name, DISTLIB_PACKAGE))
414
+ raise ValueError(msg)
415
+ return WRAPPERS[name]
416
+
417
+ # Public API follows
418
+
419
+ def make(self, specification, options=None):
420
+ """
421
+ Make a script.
422
+
423
+ :param specification: The specification, which is either a valid export
424
+ entry specification (to make a script from a
425
+ callable) or a filename (to make a script by
426
+ copying from a source location).
427
+ :param options: A dictionary of options controlling script generation.
428
+ :return: A list of all absolute pathnames written to.
429
+ """
430
+ filenames = []
431
+ entry = get_export_entry(specification)
432
+ if entry is None:
433
+ self._copy_script(specification, filenames)
434
+ else:
435
+ self._make_script(entry, filenames, options=options)
436
+ return filenames
437
+
438
+ def make_multiple(self, specifications, options=None):
439
+ """
440
+ Take a list of specifications and make scripts from them,
441
+ :param specifications: A list of specifications.
442
+ :return: A list of all absolute pathnames written to,
443
+ """
444
+ filenames = []
445
+ for specification in specifications:
446
+ filenames.extend(self.make(specification, options))
447
+ return filenames
lib/python3.12/site-packages/pip/_vendor/distlib/t32.exe ADDED
Binary file (97.8 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/version.py ADDED
@@ -0,0 +1,750 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012-2023 The Python Software Foundation.
4
+ # See LICENSE.txt and CONTRIBUTORS.txt.
5
+ #
6
+ """
7
+ Implementation of a flexible versioning scheme providing support for PEP-440,
8
+ setuptools-compatible and semantic versioning.
9
+ """
10
+
11
+ import logging
12
+ import re
13
+
14
+ from .compat import string_types
15
+ from .util import parse_requirement
16
+
17
+ __all__ = ['NormalizedVersion', 'NormalizedMatcher',
18
+ 'LegacyVersion', 'LegacyMatcher',
19
+ 'SemanticVersion', 'SemanticMatcher',
20
+ 'UnsupportedVersionError', 'get_scheme']
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class UnsupportedVersionError(ValueError):
26
+ """This is an unsupported version."""
27
+ pass
28
+
29
+
30
+ class Version(object):
31
+ def __init__(self, s):
32
+ self._string = s = s.strip()
33
+ self._parts = parts = self.parse(s)
34
+ assert isinstance(parts, tuple)
35
+ assert len(parts) > 0
36
+
37
+ def parse(self, s):
38
+ raise NotImplementedError('please implement in a subclass')
39
+
40
+ def _check_compatible(self, other):
41
+ if type(self) != type(other):
42
+ raise TypeError('cannot compare %r and %r' % (self, other))
43
+
44
+ def __eq__(self, other):
45
+ self._check_compatible(other)
46
+ return self._parts == other._parts
47
+
48
+ def __ne__(self, other):
49
+ return not self.__eq__(other)
50
+
51
+ def __lt__(self, other):
52
+ self._check_compatible(other)
53
+ return self._parts < other._parts
54
+
55
+ def __gt__(self, other):
56
+ return not (self.__lt__(other) or self.__eq__(other))
57
+
58
+ def __le__(self, other):
59
+ return self.__lt__(other) or self.__eq__(other)
60
+
61
+ def __ge__(self, other):
62
+ return self.__gt__(other) or self.__eq__(other)
63
+
64
+ # See http://docs.python.org/reference/datamodel#object.__hash__
65
+ def __hash__(self):
66
+ return hash(self._parts)
67
+
68
+ def __repr__(self):
69
+ return "%s('%s')" % (self.__class__.__name__, self._string)
70
+
71
+ def __str__(self):
72
+ return self._string
73
+
74
+ @property
75
+ def is_prerelease(self):
76
+ raise NotImplementedError('Please implement in subclasses.')
77
+
78
+
79
+ class Matcher(object):
80
+ version_class = None
81
+
82
+ # value is either a callable or the name of a method
83
+ _operators = {
84
+ '<': lambda v, c, p: v < c,
85
+ '>': lambda v, c, p: v > c,
86
+ '<=': lambda v, c, p: v == c or v < c,
87
+ '>=': lambda v, c, p: v == c or v > c,
88
+ '==': lambda v, c, p: v == c,
89
+ '===': lambda v, c, p: v == c,
90
+ # by default, compatible => >=.
91
+ '~=': lambda v, c, p: v == c or v > c,
92
+ '!=': lambda v, c, p: v != c,
93
+ }
94
+
95
+ # this is a method only to support alternative implementations
96
+ # via overriding
97
+ def parse_requirement(self, s):
98
+ return parse_requirement(s)
99
+
100
+ def __init__(self, s):
101
+ if self.version_class is None:
102
+ raise ValueError('Please specify a version class')
103
+ self._string = s = s.strip()
104
+ r = self.parse_requirement(s)
105
+ if not r:
106
+ raise ValueError('Not valid: %r' % s)
107
+ self.name = r.name
108
+ self.key = self.name.lower() # for case-insensitive comparisons
109
+ clist = []
110
+ if r.constraints:
111
+ # import pdb; pdb.set_trace()
112
+ for op, s in r.constraints:
113
+ if s.endswith('.*'):
114
+ if op not in ('==', '!='):
115
+ raise ValueError('\'.*\' not allowed for '
116
+ '%r constraints' % op)
117
+ # Could be a partial version (e.g. for '2.*') which
118
+ # won't parse as a version, so keep it as a string
119
+ vn, prefix = s[:-2], True
120
+ # Just to check that vn is a valid version
121
+ self.version_class(vn)
122
+ else:
123
+ # Should parse as a version, so we can create an
124
+ # instance for the comparison
125
+ vn, prefix = self.version_class(s), False
126
+ clist.append((op, vn, prefix))
127
+ self._parts = tuple(clist)
128
+
129
+ def match(self, version):
130
+ """
131
+ Check if the provided version matches the constraints.
132
+
133
+ :param version: The version to match against this instance.
134
+ :type version: String or :class:`Version` instance.
135
+ """
136
+ if isinstance(version, string_types):
137
+ version = self.version_class(version)
138
+ for operator, constraint, prefix in self._parts:
139
+ f = self._operators.get(operator)
140
+ if isinstance(f, string_types):
141
+ f = getattr(self, f)
142
+ if not f:
143
+ msg = ('%r not implemented '
144
+ 'for %s' % (operator, self.__class__.__name__))
145
+ raise NotImplementedError(msg)
146
+ if not f(version, constraint, prefix):
147
+ return False
148
+ return True
149
+
150
+ @property
151
+ def exact_version(self):
152
+ result = None
153
+ if len(self._parts) == 1 and self._parts[0][0] in ('==', '==='):
154
+ result = self._parts[0][1]
155
+ return result
156
+
157
+ def _check_compatible(self, other):
158
+ if type(self) != type(other) or self.name != other.name:
159
+ raise TypeError('cannot compare %s and %s' % (self, other))
160
+
161
+ def __eq__(self, other):
162
+ self._check_compatible(other)
163
+ return self.key == other.key and self._parts == other._parts
164
+
165
+ def __ne__(self, other):
166
+ return not self.__eq__(other)
167
+
168
+ # See http://docs.python.org/reference/datamodel#object.__hash__
169
+ def __hash__(self):
170
+ return hash(self.key) + hash(self._parts)
171
+
172
+ def __repr__(self):
173
+ return "%s(%r)" % (self.__class__.__name__, self._string)
174
+
175
+ def __str__(self):
176
+ return self._string
177
+
178
+
179
+ PEP440_VERSION_RE = re.compile(r'^v?(\d+!)?(\d+(\.\d+)*)((a|alpha|b|beta|c|rc|pre|preview)(\d+)?)?'
180
+ r'(\.(post|r|rev)(\d+)?)?([._-]?(dev)(\d+)?)?'
181
+ r'(\+([a-zA-Z\d]+(\.[a-zA-Z\d]+)?))?$', re.I)
182
+
183
+
184
+ def _pep_440_key(s):
185
+ s = s.strip()
186
+ m = PEP440_VERSION_RE.match(s)
187
+ if not m:
188
+ raise UnsupportedVersionError('Not a valid version: %s' % s)
189
+ groups = m.groups()
190
+ nums = tuple(int(v) for v in groups[1].split('.'))
191
+ while len(nums) > 1 and nums[-1] == 0:
192
+ nums = nums[:-1]
193
+
194
+ if not groups[0]:
195
+ epoch = 0
196
+ else:
197
+ epoch = int(groups[0][:-1])
198
+ pre = groups[4:6]
199
+ post = groups[7:9]
200
+ dev = groups[10:12]
201
+ local = groups[13]
202
+ if pre == (None, None):
203
+ pre = ()
204
+ else:
205
+ if pre[1] is None:
206
+ pre = pre[0], 0
207
+ else:
208
+ pre = pre[0], int(pre[1])
209
+ if post == (None, None):
210
+ post = ()
211
+ else:
212
+ if post[1] is None:
213
+ post = post[0], 0
214
+ else:
215
+ post = post[0], int(post[1])
216
+ if dev == (None, None):
217
+ dev = ()
218
+ else:
219
+ if dev[1] is None:
220
+ dev = dev[0], 0
221
+ else:
222
+ dev = dev[0], int(dev[1])
223
+ if local is None:
224
+ local = ()
225
+ else:
226
+ parts = []
227
+ for part in local.split('.'):
228
+ # to ensure that numeric compares as > lexicographic, avoid
229
+ # comparing them directly, but encode a tuple which ensures
230
+ # correct sorting
231
+ if part.isdigit():
232
+ part = (1, int(part))
233
+ else:
234
+ part = (0, part)
235
+ parts.append(part)
236
+ local = tuple(parts)
237
+ if not pre:
238
+ # either before pre-release, or final release and after
239
+ if not post and dev:
240
+ # before pre-release
241
+ pre = ('a', -1) # to sort before a0
242
+ else:
243
+ pre = ('z',) # to sort after all pre-releases
244
+ # now look at the state of post and dev.
245
+ if not post:
246
+ post = ('_',) # sort before 'a'
247
+ if not dev:
248
+ dev = ('final',)
249
+
250
+ return epoch, nums, pre, post, dev, local
251
+
252
+
253
+ _normalized_key = _pep_440_key
254
+
255
+
256
+ class NormalizedVersion(Version):
257
+ """A rational version.
258
+
259
+ Good:
260
+ 1.2 # equivalent to "1.2.0"
261
+ 1.2.0
262
+ 1.2a1
263
+ 1.2.3a2
264
+ 1.2.3b1
265
+ 1.2.3c1
266
+ 1.2.3.4
267
+ TODO: fill this out
268
+
269
+ Bad:
270
+ 1 # minimum two numbers
271
+ 1.2a # release level must have a release serial
272
+ 1.2.3b
273
+ """
274
+ def parse(self, s):
275
+ result = _normalized_key(s)
276
+ # _normalized_key loses trailing zeroes in the release
277
+ # clause, since that's needed to ensure that X.Y == X.Y.0 == X.Y.0.0
278
+ # However, PEP 440 prefix matching needs it: for example,
279
+ # (~= 1.4.5.0) matches differently to (~= 1.4.5.0.0).
280
+ m = PEP440_VERSION_RE.match(s) # must succeed
281
+ groups = m.groups()
282
+ self._release_clause = tuple(int(v) for v in groups[1].split('.'))
283
+ return result
284
+
285
+ PREREL_TAGS = set(['a', 'b', 'c', 'rc', 'dev'])
286
+
287
+ @property
288
+ def is_prerelease(self):
289
+ return any(t[0] in self.PREREL_TAGS for t in self._parts if t)
290
+
291
+
292
+ def _match_prefix(x, y):
293
+ x = str(x)
294
+ y = str(y)
295
+ if x == y:
296
+ return True
297
+ if not x.startswith(y):
298
+ return False
299
+ n = len(y)
300
+ return x[n] == '.'
301
+
302
+
303
+ class NormalizedMatcher(Matcher):
304
+ version_class = NormalizedVersion
305
+
306
+ # value is either a callable or the name of a method
307
+ _operators = {
308
+ '~=': '_match_compatible',
309
+ '<': '_match_lt',
310
+ '>': '_match_gt',
311
+ '<=': '_match_le',
312
+ '>=': '_match_ge',
313
+ '==': '_match_eq',
314
+ '===': '_match_arbitrary',
315
+ '!=': '_match_ne',
316
+ }
317
+
318
+ def _adjust_local(self, version, constraint, prefix):
319
+ if prefix:
320
+ strip_local = '+' not in constraint and version._parts[-1]
321
+ else:
322
+ # both constraint and version are
323
+ # NormalizedVersion instances.
324
+ # If constraint does not have a local component,
325
+ # ensure the version doesn't, either.
326
+ strip_local = not constraint._parts[-1] and version._parts[-1]
327
+ if strip_local:
328
+ s = version._string.split('+', 1)[0]
329
+ version = self.version_class(s)
330
+ return version, constraint
331
+
332
+ def _match_lt(self, version, constraint, prefix):
333
+ version, constraint = self._adjust_local(version, constraint, prefix)
334
+ if version >= constraint:
335
+ return False
336
+ release_clause = constraint._release_clause
337
+ pfx = '.'.join([str(i) for i in release_clause])
338
+ return not _match_prefix(version, pfx)
339
+
340
+ def _match_gt(self, version, constraint, prefix):
341
+ version, constraint = self._adjust_local(version, constraint, prefix)
342
+ if version <= constraint:
343
+ return False
344
+ release_clause = constraint._release_clause
345
+ pfx = '.'.join([str(i) for i in release_clause])
346
+ return not _match_prefix(version, pfx)
347
+
348
+ def _match_le(self, version, constraint, prefix):
349
+ version, constraint = self._adjust_local(version, constraint, prefix)
350
+ return version <= constraint
351
+
352
+ def _match_ge(self, version, constraint, prefix):
353
+ version, constraint = self._adjust_local(version, constraint, prefix)
354
+ return version >= constraint
355
+
356
+ def _match_eq(self, version, constraint, prefix):
357
+ version, constraint = self._adjust_local(version, constraint, prefix)
358
+ if not prefix:
359
+ result = (version == constraint)
360
+ else:
361
+ result = _match_prefix(version, constraint)
362
+ return result
363
+
364
+ def _match_arbitrary(self, version, constraint, prefix):
365
+ return str(version) == str(constraint)
366
+
367
+ def _match_ne(self, version, constraint, prefix):
368
+ version, constraint = self._adjust_local(version, constraint, prefix)
369
+ if not prefix:
370
+ result = (version != constraint)
371
+ else:
372
+ result = not _match_prefix(version, constraint)
373
+ return result
374
+
375
+ def _match_compatible(self, version, constraint, prefix):
376
+ version, constraint = self._adjust_local(version, constraint, prefix)
377
+ if version == constraint:
378
+ return True
379
+ if version < constraint:
380
+ return False
381
+ # if not prefix:
382
+ # return True
383
+ release_clause = constraint._release_clause
384
+ if len(release_clause) > 1:
385
+ release_clause = release_clause[:-1]
386
+ pfx = '.'.join([str(i) for i in release_clause])
387
+ return _match_prefix(version, pfx)
388
+
389
+
390
+ _REPLACEMENTS = (
391
+ (re.compile('[.+-]$'), ''), # remove trailing puncts
392
+ (re.compile(r'^[.](\d)'), r'0.\1'), # .N -> 0.N at start
393
+ (re.compile('^[.-]'), ''), # remove leading puncts
394
+ (re.compile(r'^\((.*)\)$'), r'\1'), # remove parentheses
395
+ (re.compile(r'^v(ersion)?\s*(\d+)'), r'\2'), # remove leading v(ersion)
396
+ (re.compile(r'^r(ev)?\s*(\d+)'), r'\2'), # remove leading v(ersion)
397
+ (re.compile('[.]{2,}'), '.'), # multiple runs of '.'
398
+ (re.compile(r'\b(alfa|apha)\b'), 'alpha'), # misspelt alpha
399
+ (re.compile(r'\b(pre-alpha|prealpha)\b'),
400
+ 'pre.alpha'), # standardise
401
+ (re.compile(r'\(beta\)$'), 'beta'), # remove parentheses
402
+ )
403
+
404
+ _SUFFIX_REPLACEMENTS = (
405
+ (re.compile('^[:~._+-]+'), ''), # remove leading puncts
406
+ (re.compile('[,*")([\\]]'), ''), # remove unwanted chars
407
+ (re.compile('[~:+_ -]'), '.'), # replace illegal chars
408
+ (re.compile('[.]{2,}'), '.'), # multiple runs of '.'
409
+ (re.compile(r'\.$'), ''), # trailing '.'
410
+ )
411
+
412
+ _NUMERIC_PREFIX = re.compile(r'(\d+(\.\d+)*)')
413
+
414
+
415
+ def _suggest_semantic_version(s):
416
+ """
417
+ Try to suggest a semantic form for a version for which
418
+ _suggest_normalized_version couldn't come up with anything.
419
+ """
420
+ result = s.strip().lower()
421
+ for pat, repl in _REPLACEMENTS:
422
+ result = pat.sub(repl, result)
423
+ if not result:
424
+ result = '0.0.0'
425
+
426
+ # Now look for numeric prefix, and separate it out from
427
+ # the rest.
428
+ # import pdb; pdb.set_trace()
429
+ m = _NUMERIC_PREFIX.match(result)
430
+ if not m:
431
+ prefix = '0.0.0'
432
+ suffix = result
433
+ else:
434
+ prefix = m.groups()[0].split('.')
435
+ prefix = [int(i) for i in prefix]
436
+ while len(prefix) < 3:
437
+ prefix.append(0)
438
+ if len(prefix) == 3:
439
+ suffix = result[m.end():]
440
+ else:
441
+ suffix = '.'.join([str(i) for i in prefix[3:]]) + result[m.end():]
442
+ prefix = prefix[:3]
443
+ prefix = '.'.join([str(i) for i in prefix])
444
+ suffix = suffix.strip()
445
+ if suffix:
446
+ # import pdb; pdb.set_trace()
447
+ # massage the suffix.
448
+ for pat, repl in _SUFFIX_REPLACEMENTS:
449
+ suffix = pat.sub(repl, suffix)
450
+
451
+ if not suffix:
452
+ result = prefix
453
+ else:
454
+ sep = '-' if 'dev' in suffix else '+'
455
+ result = prefix + sep + suffix
456
+ if not is_semver(result):
457
+ result = None
458
+ return result
459
+
460
+
461
+ def _suggest_normalized_version(s):
462
+ """Suggest a normalized version close to the given version string.
463
+
464
+ If you have a version string that isn't rational (i.e. NormalizedVersion
465
+ doesn't like it) then you might be able to get an equivalent (or close)
466
+ rational version from this function.
467
+
468
+ This does a number of simple normalizations to the given string, based
469
+ on observation of versions currently in use on PyPI. Given a dump of
470
+ those version during PyCon 2009, 4287 of them:
471
+ - 2312 (53.93%) match NormalizedVersion without change
472
+ with the automatic suggestion
473
+ - 3474 (81.04%) match when using this suggestion method
474
+
475
+ @param s {str} An irrational version string.
476
+ @returns A rational version string, or None, if couldn't determine one.
477
+ """
478
+ try:
479
+ _normalized_key(s)
480
+ return s # already rational
481
+ except UnsupportedVersionError:
482
+ pass
483
+
484
+ rs = s.lower()
485
+
486
+ # part of this could use maketrans
487
+ for orig, repl in (('-alpha', 'a'), ('-beta', 'b'), ('alpha', 'a'),
488
+ ('beta', 'b'), ('rc', 'c'), ('-final', ''),
489
+ ('-pre', 'c'),
490
+ ('-release', ''), ('.release', ''), ('-stable', ''),
491
+ ('+', '.'), ('_', '.'), (' ', ''), ('.final', ''),
492
+ ('final', '')):
493
+ rs = rs.replace(orig, repl)
494
+
495
+ # if something ends with dev or pre, we add a 0
496
+ rs = re.sub(r"pre$", r"pre0", rs)
497
+ rs = re.sub(r"dev$", r"dev0", rs)
498
+
499
+ # if we have something like "b-2" or "a.2" at the end of the
500
+ # version, that is probably beta, alpha, etc
501
+ # let's remove the dash or dot
502
+ rs = re.sub(r"([abc]|rc)[\-\.](\d+)$", r"\1\2", rs)
503
+
504
+ # 1.0-dev-r371 -> 1.0.dev371
505
+ # 0.1-dev-r79 -> 0.1.dev79
506
+ rs = re.sub(r"[\-\.](dev)[\-\.]?r?(\d+)$", r".\1\2", rs)
507
+
508
+ # Clean: 2.0.a.3, 2.0.b1, 0.9.0~c1
509
+ rs = re.sub(r"[.~]?([abc])\.?", r"\1", rs)
510
+
511
+ # Clean: v0.3, v1.0
512
+ if rs.startswith('v'):
513
+ rs = rs[1:]
514
+
515
+ # Clean leading '0's on numbers.
516
+ # TODO: unintended side-effect on, e.g., "2003.05.09"
517
+ # PyPI stats: 77 (~2%) better
518
+ rs = re.sub(r"\b0+(\d+)(?!\d)", r"\1", rs)
519
+
520
+ # Clean a/b/c with no version. E.g. "1.0a" -> "1.0a0". Setuptools infers
521
+ # zero.
522
+ # PyPI stats: 245 (7.56%) better
523
+ rs = re.sub(r"(\d+[abc])$", r"\g<1>0", rs)
524
+
525
+ # the 'dev-rNNN' tag is a dev tag
526
+ rs = re.sub(r"\.?(dev-r|dev\.r)\.?(\d+)$", r".dev\2", rs)
527
+
528
+ # clean the - when used as a pre delimiter
529
+ rs = re.sub(r"-(a|b|c)(\d+)$", r"\1\2", rs)
530
+
531
+ # a terminal "dev" or "devel" can be changed into ".dev0"
532
+ rs = re.sub(r"[\.\-](dev|devel)$", r".dev0", rs)
533
+
534
+ # a terminal "dev" can be changed into ".dev0"
535
+ rs = re.sub(r"(?![\.\-])dev$", r".dev0", rs)
536
+
537
+ # a terminal "final" or "stable" can be removed
538
+ rs = re.sub(r"(final|stable)$", "", rs)
539
+
540
+ # The 'r' and the '-' tags are post release tags
541
+ # 0.4a1.r10 -> 0.4a1.post10
542
+ # 0.9.33-17222 -> 0.9.33.post17222
543
+ # 0.9.33-r17222 -> 0.9.33.post17222
544
+ rs = re.sub(r"\.?(r|-|-r)\.?(\d+)$", r".post\2", rs)
545
+
546
+ # Clean 'r' instead of 'dev' usage:
547
+ # 0.9.33+r17222 -> 0.9.33.dev17222
548
+ # 1.0dev123 -> 1.0.dev123
549
+ # 1.0.git123 -> 1.0.dev123
550
+ # 1.0.bzr123 -> 1.0.dev123
551
+ # 0.1a0dev.123 -> 0.1a0.dev123
552
+ # PyPI stats: ~150 (~4%) better
553
+ rs = re.sub(r"\.?(dev|git|bzr)\.?(\d+)$", r".dev\2", rs)
554
+
555
+ # Clean '.pre' (normalized from '-pre' above) instead of 'c' usage:
556
+ # 0.2.pre1 -> 0.2c1
557
+ # 0.2-c1 -> 0.2c1
558
+ # 1.0preview123 -> 1.0c123
559
+ # PyPI stats: ~21 (0.62%) better
560
+ rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs)
561
+
562
+ # Tcl/Tk uses "px" for their post release markers
563
+ rs = re.sub(r"p(\d+)$", r".post\1", rs)
564
+
565
+ try:
566
+ _normalized_key(rs)
567
+ except UnsupportedVersionError:
568
+ rs = None
569
+ return rs
570
+
571
+ #
572
+ # Legacy version processing (distribute-compatible)
573
+ #
574
+
575
+
576
+ _VERSION_PART = re.compile(r'([a-z]+|\d+|[\.-])', re.I)
577
+ _VERSION_REPLACE = {
578
+ 'pre': 'c',
579
+ 'preview': 'c',
580
+ '-': 'final-',
581
+ 'rc': 'c',
582
+ 'dev': '@',
583
+ '': None,
584
+ '.': None,
585
+ }
586
+
587
+
588
+ def _legacy_key(s):
589
+ def get_parts(s):
590
+ result = []
591
+ for p in _VERSION_PART.split(s.lower()):
592
+ p = _VERSION_REPLACE.get(p, p)
593
+ if p:
594
+ if '0' <= p[:1] <= '9':
595
+ p = p.zfill(8)
596
+ else:
597
+ p = '*' + p
598
+ result.append(p)
599
+ result.append('*final')
600
+ return result
601
+
602
+ result = []
603
+ for p in get_parts(s):
604
+ if p.startswith('*'):
605
+ if p < '*final':
606
+ while result and result[-1] == '*final-':
607
+ result.pop()
608
+ while result and result[-1] == '00000000':
609
+ result.pop()
610
+ result.append(p)
611
+ return tuple(result)
612
+
613
+
614
+ class LegacyVersion(Version):
615
+ def parse(self, s):
616
+ return _legacy_key(s)
617
+
618
+ @property
619
+ def is_prerelease(self):
620
+ result = False
621
+ for x in self._parts:
622
+ if (isinstance(x, string_types) and x.startswith('*') and x < '*final'):
623
+ result = True
624
+ break
625
+ return result
626
+
627
+
628
+ class LegacyMatcher(Matcher):
629
+ version_class = LegacyVersion
630
+
631
+ _operators = dict(Matcher._operators)
632
+ _operators['~='] = '_match_compatible'
633
+
634
+ numeric_re = re.compile(r'^(\d+(\.\d+)*)')
635
+
636
+ def _match_compatible(self, version, constraint, prefix):
637
+ if version < constraint:
638
+ return False
639
+ m = self.numeric_re.match(str(constraint))
640
+ if not m:
641
+ logger.warning('Cannot compute compatible match for version %s '
642
+ ' and constraint %s', version, constraint)
643
+ return True
644
+ s = m.groups()[0]
645
+ if '.' in s:
646
+ s = s.rsplit('.', 1)[0]
647
+ return _match_prefix(version, s)
648
+
649
+ #
650
+ # Semantic versioning
651
+ #
652
+
653
+
654
+ _SEMVER_RE = re.compile(r'^(\d+)\.(\d+)\.(\d+)'
655
+ r'(-[a-z0-9]+(\.[a-z0-9-]+)*)?'
656
+ r'(\+[a-z0-9]+(\.[a-z0-9-]+)*)?$', re.I)
657
+
658
+
659
+ def is_semver(s):
660
+ return _SEMVER_RE.match(s)
661
+
662
+
663
+ def _semantic_key(s):
664
+ def make_tuple(s, absent):
665
+ if s is None:
666
+ result = (absent,)
667
+ else:
668
+ parts = s[1:].split('.')
669
+ # We can't compare ints and strings on Python 3, so fudge it
670
+ # by zero-filling numeric values so simulate a numeric comparison
671
+ result = tuple([p.zfill(8) if p.isdigit() else p for p in parts])
672
+ return result
673
+
674
+ m = is_semver(s)
675
+ if not m:
676
+ raise UnsupportedVersionError(s)
677
+ groups = m.groups()
678
+ major, minor, patch = [int(i) for i in groups[:3]]
679
+ # choose the '|' and '*' so that versions sort correctly
680
+ pre, build = make_tuple(groups[3], '|'), make_tuple(groups[5], '*')
681
+ return (major, minor, patch), pre, build
682
+
683
+
684
+ class SemanticVersion(Version):
685
+ def parse(self, s):
686
+ return _semantic_key(s)
687
+
688
+ @property
689
+ def is_prerelease(self):
690
+ return self._parts[1][0] != '|'
691
+
692
+
693
+ class SemanticMatcher(Matcher):
694
+ version_class = SemanticVersion
695
+
696
+
697
+ class VersionScheme(object):
698
+ def __init__(self, key, matcher, suggester=None):
699
+ self.key = key
700
+ self.matcher = matcher
701
+ self.suggester = suggester
702
+
703
+ def is_valid_version(self, s):
704
+ try:
705
+ self.matcher.version_class(s)
706
+ result = True
707
+ except UnsupportedVersionError:
708
+ result = False
709
+ return result
710
+
711
+ def is_valid_matcher(self, s):
712
+ try:
713
+ self.matcher(s)
714
+ result = True
715
+ except UnsupportedVersionError:
716
+ result = False
717
+ return result
718
+
719
+ def is_valid_constraint_list(self, s):
720
+ """
721
+ Used for processing some metadata fields
722
+ """
723
+ # See issue #140. Be tolerant of a single trailing comma.
724
+ if s.endswith(','):
725
+ s = s[:-1]
726
+ return self.is_valid_matcher('dummy_name (%s)' % s)
727
+
728
+ def suggest(self, s):
729
+ if self.suggester is None:
730
+ result = None
731
+ else:
732
+ result = self.suggester(s)
733
+ return result
734
+
735
+
736
+ _SCHEMES = {
737
+ 'normalized': VersionScheme(_normalized_key, NormalizedMatcher,
738
+ _suggest_normalized_version),
739
+ 'legacy': VersionScheme(_legacy_key, LegacyMatcher, lambda self, s: s),
740
+ 'semantic': VersionScheme(_semantic_key, SemanticMatcher,
741
+ _suggest_semantic_version),
742
+ }
743
+
744
+ _SCHEMES['default'] = _SCHEMES['normalized']
745
+
746
+
747
+ def get_scheme(name):
748
+ if name not in _SCHEMES:
749
+ raise ValueError('unknown scheme name: %r' % name)
750
+ return _SCHEMES[name]
lib/python3.12/site-packages/pip/_vendor/distlib/w32.exe ADDED
Binary file (91.6 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/distlib/wheel.py ADDED
@@ -0,0 +1,1100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2013-2023 Vinay Sajip.
4
+ # Licensed to the Python Software Foundation under a contributor agreement.
5
+ # See LICENSE.txt and CONTRIBUTORS.txt.
6
+ #
7
+ from __future__ import unicode_literals
8
+
9
+ import base64
10
+ import codecs
11
+ import datetime
12
+ from email import message_from_file
13
+ import hashlib
14
+ import json
15
+ import logging
16
+ import os
17
+ import posixpath
18
+ import re
19
+ import shutil
20
+ import sys
21
+ import tempfile
22
+ import zipfile
23
+
24
+ from . import __version__, DistlibException
25
+ from .compat import sysconfig, ZipFile, fsdecode, text_type, filter
26
+ from .database import InstalledDistribution
27
+ from .metadata import Metadata, WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME
28
+ from .util import (FileOperator, convert_path, CSVReader, CSVWriter, Cache, cached_property, get_cache_base,
29
+ read_exports, tempdir, get_platform)
30
+ from .version import NormalizedVersion, UnsupportedVersionError
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+ cache = None # created when needed
35
+
36
+ if hasattr(sys, 'pypy_version_info'): # pragma: no cover
37
+ IMP_PREFIX = 'pp'
38
+ elif sys.platform.startswith('java'): # pragma: no cover
39
+ IMP_PREFIX = 'jy'
40
+ elif sys.platform == 'cli': # pragma: no cover
41
+ IMP_PREFIX = 'ip'
42
+ else:
43
+ IMP_PREFIX = 'cp'
44
+
45
+ VER_SUFFIX = sysconfig.get_config_var('py_version_nodot')
46
+ if not VER_SUFFIX: # pragma: no cover
47
+ VER_SUFFIX = '%s%s' % sys.version_info[:2]
48
+ PYVER = 'py' + VER_SUFFIX
49
+ IMPVER = IMP_PREFIX + VER_SUFFIX
50
+
51
+ ARCH = get_platform().replace('-', '_').replace('.', '_')
52
+
53
+ ABI = sysconfig.get_config_var('SOABI')
54
+ if ABI and ABI.startswith('cpython-'):
55
+ ABI = ABI.replace('cpython-', 'cp').split('-')[0]
56
+ else:
57
+
58
+ def _derive_abi():
59
+ parts = ['cp', VER_SUFFIX]
60
+ if sysconfig.get_config_var('Py_DEBUG'):
61
+ parts.append('d')
62
+ if IMP_PREFIX == 'cp':
63
+ vi = sys.version_info[:2]
64
+ if vi < (3, 8):
65
+ wpm = sysconfig.get_config_var('WITH_PYMALLOC')
66
+ if wpm is None:
67
+ wpm = True
68
+ if wpm:
69
+ parts.append('m')
70
+ if vi < (3, 3):
71
+ us = sysconfig.get_config_var('Py_UNICODE_SIZE')
72
+ if us == 4 or (us is None and sys.maxunicode == 0x10FFFF):
73
+ parts.append('u')
74
+ return ''.join(parts)
75
+
76
+ ABI = _derive_abi()
77
+ del _derive_abi
78
+
79
+ FILENAME_RE = re.compile(
80
+ r'''
81
+ (?P<nm>[^-]+)
82
+ -(?P<vn>\d+[^-]*)
83
+ (-(?P<bn>\d+[^-]*))?
84
+ -(?P<py>\w+\d+(\.\w+\d+)*)
85
+ -(?P<bi>\w+)
86
+ -(?P<ar>\w+(\.\w+)*)
87
+ \.whl$
88
+ ''', re.IGNORECASE | re.VERBOSE)
89
+
90
+ NAME_VERSION_RE = re.compile(r'''
91
+ (?P<nm>[^-]+)
92
+ -(?P<vn>\d+[^-]*)
93
+ (-(?P<bn>\d+[^-]*))?$
94
+ ''', re.IGNORECASE | re.VERBOSE)
95
+
96
+ SHEBANG_RE = re.compile(br'\s*#![^\r\n]*')
97
+ SHEBANG_DETAIL_RE = re.compile(br'^(\s*#!("[^"]+"|\S+))\s+(.*)$')
98
+ SHEBANG_PYTHON = b'#!python'
99
+ SHEBANG_PYTHONW = b'#!pythonw'
100
+
101
+ if os.sep == '/':
102
+ to_posix = lambda o: o
103
+ else:
104
+ to_posix = lambda o: o.replace(os.sep, '/')
105
+
106
+ if sys.version_info[0] < 3:
107
+ import imp
108
+ else:
109
+ imp = None
110
+ import importlib.machinery
111
+ import importlib.util
112
+
113
+
114
+ def _get_suffixes():
115
+ if imp:
116
+ return [s[0] for s in imp.get_suffixes()]
117
+ else:
118
+ return importlib.machinery.EXTENSION_SUFFIXES
119
+
120
+
121
+ def _load_dynamic(name, path):
122
+ # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly
123
+ if imp:
124
+ return imp.load_dynamic(name, path)
125
+ else:
126
+ spec = importlib.util.spec_from_file_location(name, path)
127
+ module = importlib.util.module_from_spec(spec)
128
+ sys.modules[name] = module
129
+ spec.loader.exec_module(module)
130
+ return module
131
+
132
+
133
+ class Mounter(object):
134
+
135
+ def __init__(self):
136
+ self.impure_wheels = {}
137
+ self.libs = {}
138
+
139
+ def add(self, pathname, extensions):
140
+ self.impure_wheels[pathname] = extensions
141
+ self.libs.update(extensions)
142
+
143
+ def remove(self, pathname):
144
+ extensions = self.impure_wheels.pop(pathname)
145
+ for k, v in extensions:
146
+ if k in self.libs:
147
+ del self.libs[k]
148
+
149
+ def find_module(self, fullname, path=None):
150
+ if fullname in self.libs:
151
+ result = self
152
+ else:
153
+ result = None
154
+ return result
155
+
156
+ def load_module(self, fullname):
157
+ if fullname in sys.modules:
158
+ result = sys.modules[fullname]
159
+ else:
160
+ if fullname not in self.libs:
161
+ raise ImportError('unable to find extension for %s' % fullname)
162
+ result = _load_dynamic(fullname, self.libs[fullname])
163
+ result.__loader__ = self
164
+ parts = fullname.rsplit('.', 1)
165
+ if len(parts) > 1:
166
+ result.__package__ = parts[0]
167
+ return result
168
+
169
+
170
+ _hook = Mounter()
171
+
172
+
173
+ class Wheel(object):
174
+ """
175
+ Class to build and install from Wheel files (PEP 427).
176
+ """
177
+
178
+ wheel_version = (1, 1)
179
+ hash_kind = 'sha256'
180
+
181
+ def __init__(self, filename=None, sign=False, verify=False):
182
+ """
183
+ Initialise an instance using a (valid) filename.
184
+ """
185
+ self.sign = sign
186
+ self.should_verify = verify
187
+ self.buildver = ''
188
+ self.pyver = [PYVER]
189
+ self.abi = ['none']
190
+ self.arch = ['any']
191
+ self.dirname = os.getcwd()
192
+ if filename is None:
193
+ self.name = 'dummy'
194
+ self.version = '0.1'
195
+ self._filename = self.filename
196
+ else:
197
+ m = NAME_VERSION_RE.match(filename)
198
+ if m:
199
+ info = m.groupdict('')
200
+ self.name = info['nm']
201
+ # Reinstate the local version separator
202
+ self.version = info['vn'].replace('_', '-')
203
+ self.buildver = info['bn']
204
+ self._filename = self.filename
205
+ else:
206
+ dirname, filename = os.path.split(filename)
207
+ m = FILENAME_RE.match(filename)
208
+ if not m:
209
+ raise DistlibException('Invalid name or '
210
+ 'filename: %r' % filename)
211
+ if dirname:
212
+ self.dirname = os.path.abspath(dirname)
213
+ self._filename = filename
214
+ info = m.groupdict('')
215
+ self.name = info['nm']
216
+ self.version = info['vn']
217
+ self.buildver = info['bn']
218
+ self.pyver = info['py'].split('.')
219
+ self.abi = info['bi'].split('.')
220
+ self.arch = info['ar'].split('.')
221
+
222
+ @property
223
+ def filename(self):
224
+ """
225
+ Build and return a filename from the various components.
226
+ """
227
+ if self.buildver:
228
+ buildver = '-' + self.buildver
229
+ else:
230
+ buildver = ''
231
+ pyver = '.'.join(self.pyver)
232
+ abi = '.'.join(self.abi)
233
+ arch = '.'.join(self.arch)
234
+ # replace - with _ as a local version separator
235
+ version = self.version.replace('-', '_')
236
+ return '%s-%s%s-%s-%s-%s.whl' % (self.name, version, buildver, pyver, abi, arch)
237
+
238
+ @property
239
+ def exists(self):
240
+ path = os.path.join(self.dirname, self.filename)
241
+ return os.path.isfile(path)
242
+
243
+ @property
244
+ def tags(self):
245
+ for pyver in self.pyver:
246
+ for abi in self.abi:
247
+ for arch in self.arch:
248
+ yield pyver, abi, arch
249
+
250
+ @cached_property
251
+ def metadata(self):
252
+ pathname = os.path.join(self.dirname, self.filename)
253
+ name_ver = '%s-%s' % (self.name, self.version)
254
+ info_dir = '%s.dist-info' % name_ver
255
+ wrapper = codecs.getreader('utf-8')
256
+ with ZipFile(pathname, 'r') as zf:
257
+ self.get_wheel_metadata(zf)
258
+ # wv = wheel_metadata['Wheel-Version'].split('.', 1)
259
+ # file_version = tuple([int(i) for i in wv])
260
+ # if file_version < (1, 1):
261
+ # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME,
262
+ # LEGACY_METADATA_FILENAME]
263
+ # else:
264
+ # fns = [WHEEL_METADATA_FILENAME, METADATA_FILENAME]
265
+ fns = [WHEEL_METADATA_FILENAME, LEGACY_METADATA_FILENAME]
266
+ result = None
267
+ for fn in fns:
268
+ try:
269
+ metadata_filename = posixpath.join(info_dir, fn)
270
+ with zf.open(metadata_filename) as bf:
271
+ wf = wrapper(bf)
272
+ result = Metadata(fileobj=wf)
273
+ if result:
274
+ break
275
+ except KeyError:
276
+ pass
277
+ if not result:
278
+ raise ValueError('Invalid wheel, because metadata is '
279
+ 'missing: looked in %s' % ', '.join(fns))
280
+ return result
281
+
282
+ def get_wheel_metadata(self, zf):
283
+ name_ver = '%s-%s' % (self.name, self.version)
284
+ info_dir = '%s.dist-info' % name_ver
285
+ metadata_filename = posixpath.join(info_dir, 'WHEEL')
286
+ with zf.open(metadata_filename) as bf:
287
+ wf = codecs.getreader('utf-8')(bf)
288
+ message = message_from_file(wf)
289
+ return dict(message)
290
+
291
+ @cached_property
292
+ def info(self):
293
+ pathname = os.path.join(self.dirname, self.filename)
294
+ with ZipFile(pathname, 'r') as zf:
295
+ result = self.get_wheel_metadata(zf)
296
+ return result
297
+
298
+ def process_shebang(self, data):
299
+ m = SHEBANG_RE.match(data)
300
+ if m:
301
+ end = m.end()
302
+ shebang, data_after_shebang = data[:end], data[end:]
303
+ # Preserve any arguments after the interpreter
304
+ if b'pythonw' in shebang.lower():
305
+ shebang_python = SHEBANG_PYTHONW
306
+ else:
307
+ shebang_python = SHEBANG_PYTHON
308
+ m = SHEBANG_DETAIL_RE.match(shebang)
309
+ if m:
310
+ args = b' ' + m.groups()[-1]
311
+ else:
312
+ args = b''
313
+ shebang = shebang_python + args
314
+ data = shebang + data_after_shebang
315
+ else:
316
+ cr = data.find(b'\r')
317
+ lf = data.find(b'\n')
318
+ if cr < 0 or cr > lf:
319
+ term = b'\n'
320
+ else:
321
+ if data[cr:cr + 2] == b'\r\n':
322
+ term = b'\r\n'
323
+ else:
324
+ term = b'\r'
325
+ data = SHEBANG_PYTHON + term + data
326
+ return data
327
+
328
+ def get_hash(self, data, hash_kind=None):
329
+ if hash_kind is None:
330
+ hash_kind = self.hash_kind
331
+ try:
332
+ hasher = getattr(hashlib, hash_kind)
333
+ except AttributeError:
334
+ raise DistlibException('Unsupported hash algorithm: %r' % hash_kind)
335
+ result = hasher(data).digest()
336
+ result = base64.urlsafe_b64encode(result).rstrip(b'=').decode('ascii')
337
+ return hash_kind, result
338
+
339
+ def write_record(self, records, record_path, archive_record_path):
340
+ records = list(records) # make a copy, as mutated
341
+ records.append((archive_record_path, '', ''))
342
+ with CSVWriter(record_path) as writer:
343
+ for row in records:
344
+ writer.writerow(row)
345
+
346
+ def write_records(self, info, libdir, archive_paths):
347
+ records = []
348
+ distinfo, info_dir = info
349
+ # hasher = getattr(hashlib, self.hash_kind)
350
+ for ap, p in archive_paths:
351
+ with open(p, 'rb') as f:
352
+ data = f.read()
353
+ digest = '%s=%s' % self.get_hash(data)
354
+ size = os.path.getsize(p)
355
+ records.append((ap, digest, size))
356
+
357
+ p = os.path.join(distinfo, 'RECORD')
358
+ ap = to_posix(os.path.join(info_dir, 'RECORD'))
359
+ self.write_record(records, p, ap)
360
+ archive_paths.append((ap, p))
361
+
362
+ def build_zip(self, pathname, archive_paths):
363
+ with ZipFile(pathname, 'w', zipfile.ZIP_DEFLATED) as zf:
364
+ for ap, p in archive_paths:
365
+ logger.debug('Wrote %s to %s in wheel', p, ap)
366
+ zf.write(p, ap)
367
+
368
+ def build(self, paths, tags=None, wheel_version=None):
369
+ """
370
+ Build a wheel from files in specified paths, and use any specified tags
371
+ when determining the name of the wheel.
372
+ """
373
+ if tags is None:
374
+ tags = {}
375
+
376
+ libkey = list(filter(lambda o: o in paths, ('purelib', 'platlib')))[0]
377
+ if libkey == 'platlib':
378
+ is_pure = 'false'
379
+ default_pyver = [IMPVER]
380
+ default_abi = [ABI]
381
+ default_arch = [ARCH]
382
+ else:
383
+ is_pure = 'true'
384
+ default_pyver = [PYVER]
385
+ default_abi = ['none']
386
+ default_arch = ['any']
387
+
388
+ self.pyver = tags.get('pyver', default_pyver)
389
+ self.abi = tags.get('abi', default_abi)
390
+ self.arch = tags.get('arch', default_arch)
391
+
392
+ libdir = paths[libkey]
393
+
394
+ name_ver = '%s-%s' % (self.name, self.version)
395
+ data_dir = '%s.data' % name_ver
396
+ info_dir = '%s.dist-info' % name_ver
397
+
398
+ archive_paths = []
399
+
400
+ # First, stuff which is not in site-packages
401
+ for key in ('data', 'headers', 'scripts'):
402
+ if key not in paths:
403
+ continue
404
+ path = paths[key]
405
+ if os.path.isdir(path):
406
+ for root, dirs, files in os.walk(path):
407
+ for fn in files:
408
+ p = fsdecode(os.path.join(root, fn))
409
+ rp = os.path.relpath(p, path)
410
+ ap = to_posix(os.path.join(data_dir, key, rp))
411
+ archive_paths.append((ap, p))
412
+ if key == 'scripts' and not p.endswith('.exe'):
413
+ with open(p, 'rb') as f:
414
+ data = f.read()
415
+ data = self.process_shebang(data)
416
+ with open(p, 'wb') as f:
417
+ f.write(data)
418
+
419
+ # Now, stuff which is in site-packages, other than the
420
+ # distinfo stuff.
421
+ path = libdir
422
+ distinfo = None
423
+ for root, dirs, files in os.walk(path):
424
+ if root == path:
425
+ # At the top level only, save distinfo for later
426
+ # and skip it for now
427
+ for i, dn in enumerate(dirs):
428
+ dn = fsdecode(dn)
429
+ if dn.endswith('.dist-info'):
430
+ distinfo = os.path.join(root, dn)
431
+ del dirs[i]
432
+ break
433
+ assert distinfo, '.dist-info directory expected, not found'
434
+
435
+ for fn in files:
436
+ # comment out next suite to leave .pyc files in
437
+ if fsdecode(fn).endswith(('.pyc', '.pyo')):
438
+ continue
439
+ p = os.path.join(root, fn)
440
+ rp = to_posix(os.path.relpath(p, path))
441
+ archive_paths.append((rp, p))
442
+
443
+ # Now distinfo. Assumed to be flat, i.e. os.listdir is enough.
444
+ files = os.listdir(distinfo)
445
+ for fn in files:
446
+ if fn not in ('RECORD', 'INSTALLER', 'SHARED', 'WHEEL'):
447
+ p = fsdecode(os.path.join(distinfo, fn))
448
+ ap = to_posix(os.path.join(info_dir, fn))
449
+ archive_paths.append((ap, p))
450
+
451
+ wheel_metadata = [
452
+ 'Wheel-Version: %d.%d' % (wheel_version or self.wheel_version),
453
+ 'Generator: distlib %s' % __version__,
454
+ 'Root-Is-Purelib: %s' % is_pure,
455
+ ]
456
+ for pyver, abi, arch in self.tags:
457
+ wheel_metadata.append('Tag: %s-%s-%s' % (pyver, abi, arch))
458
+ p = os.path.join(distinfo, 'WHEEL')
459
+ with open(p, 'w') as f:
460
+ f.write('\n'.join(wheel_metadata))
461
+ ap = to_posix(os.path.join(info_dir, 'WHEEL'))
462
+ archive_paths.append((ap, p))
463
+
464
+ # sort the entries by archive path. Not needed by any spec, but it
465
+ # keeps the archive listing and RECORD tidier than they would otherwise
466
+ # be. Use the number of path segments to keep directory entries together,
467
+ # and keep the dist-info stuff at the end.
468
+ def sorter(t):
469
+ ap = t[0]
470
+ n = ap.count('/')
471
+ if '.dist-info' in ap:
472
+ n += 10000
473
+ return (n, ap)
474
+
475
+ archive_paths = sorted(archive_paths, key=sorter)
476
+
477
+ # Now, at last, RECORD.
478
+ # Paths in here are archive paths - nothing else makes sense.
479
+ self.write_records((distinfo, info_dir), libdir, archive_paths)
480
+ # Now, ready to build the zip file
481
+ pathname = os.path.join(self.dirname, self.filename)
482
+ self.build_zip(pathname, archive_paths)
483
+ return pathname
484
+
485
+ def skip_entry(self, arcname):
486
+ """
487
+ Determine whether an archive entry should be skipped when verifying
488
+ or installing.
489
+ """
490
+ # The signature file won't be in RECORD,
491
+ # and we don't currently don't do anything with it
492
+ # We also skip directories, as they won't be in RECORD
493
+ # either. See:
494
+ #
495
+ # https://github.com/pypa/wheel/issues/294
496
+ # https://github.com/pypa/wheel/issues/287
497
+ # https://github.com/pypa/wheel/pull/289
498
+ #
499
+ return arcname.endswith(('/', '/RECORD.jws'))
500
+
501
+ def install(self, paths, maker, **kwargs):
502
+ """
503
+ Install a wheel to the specified paths. If kwarg ``warner`` is
504
+ specified, it should be a callable, which will be called with two
505
+ tuples indicating the wheel version of this software and the wheel
506
+ version in the file, if there is a discrepancy in the versions.
507
+ This can be used to issue any warnings to raise any exceptions.
508
+ If kwarg ``lib_only`` is True, only the purelib/platlib files are
509
+ installed, and the headers, scripts, data and dist-info metadata are
510
+ not written. If kwarg ``bytecode_hashed_invalidation`` is True, written
511
+ bytecode will try to use file-hash based invalidation (PEP-552) on
512
+ supported interpreter versions (CPython 3.7+).
513
+
514
+ The return value is a :class:`InstalledDistribution` instance unless
515
+ ``options.lib_only`` is True, in which case the return value is ``None``.
516
+ """
517
+
518
+ dry_run = maker.dry_run
519
+ warner = kwargs.get('warner')
520
+ lib_only = kwargs.get('lib_only', False)
521
+ bc_hashed_invalidation = kwargs.get('bytecode_hashed_invalidation', False)
522
+
523
+ pathname = os.path.join(self.dirname, self.filename)
524
+ name_ver = '%s-%s' % (self.name, self.version)
525
+ data_dir = '%s.data' % name_ver
526
+ info_dir = '%s.dist-info' % name_ver
527
+
528
+ metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME)
529
+ wheel_metadata_name = posixpath.join(info_dir, 'WHEEL')
530
+ record_name = posixpath.join(info_dir, 'RECORD')
531
+
532
+ wrapper = codecs.getreader('utf-8')
533
+
534
+ with ZipFile(pathname, 'r') as zf:
535
+ with zf.open(wheel_metadata_name) as bwf:
536
+ wf = wrapper(bwf)
537
+ message = message_from_file(wf)
538
+ wv = message['Wheel-Version'].split('.', 1)
539
+ file_version = tuple([int(i) for i in wv])
540
+ if (file_version != self.wheel_version) and warner:
541
+ warner(self.wheel_version, file_version)
542
+
543
+ if message['Root-Is-Purelib'] == 'true':
544
+ libdir = paths['purelib']
545
+ else:
546
+ libdir = paths['platlib']
547
+
548
+ records = {}
549
+ with zf.open(record_name) as bf:
550
+ with CSVReader(stream=bf) as reader:
551
+ for row in reader:
552
+ p = row[0]
553
+ records[p] = row
554
+
555
+ data_pfx = posixpath.join(data_dir, '')
556
+ info_pfx = posixpath.join(info_dir, '')
557
+ script_pfx = posixpath.join(data_dir, 'scripts', '')
558
+
559
+ # make a new instance rather than a copy of maker's,
560
+ # as we mutate it
561
+ fileop = FileOperator(dry_run=dry_run)
562
+ fileop.record = True # so we can rollback if needed
563
+
564
+ bc = not sys.dont_write_bytecode # Double negatives. Lovely!
565
+
566
+ outfiles = [] # for RECORD writing
567
+
568
+ # for script copying/shebang processing
569
+ workdir = tempfile.mkdtemp()
570
+ # set target dir later
571
+ # we default add_launchers to False, as the
572
+ # Python Launcher should be used instead
573
+ maker.source_dir = workdir
574
+ maker.target_dir = None
575
+ try:
576
+ for zinfo in zf.infolist():
577
+ arcname = zinfo.filename
578
+ if isinstance(arcname, text_type):
579
+ u_arcname = arcname
580
+ else:
581
+ u_arcname = arcname.decode('utf-8')
582
+ if self.skip_entry(u_arcname):
583
+ continue
584
+ row = records[u_arcname]
585
+ if row[2] and str(zinfo.file_size) != row[2]:
586
+ raise DistlibException('size mismatch for '
587
+ '%s' % u_arcname)
588
+ if row[1]:
589
+ kind, value = row[1].split('=', 1)
590
+ with zf.open(arcname) as bf:
591
+ data = bf.read()
592
+ _, digest = self.get_hash(data, kind)
593
+ if digest != value:
594
+ raise DistlibException('digest mismatch for '
595
+ '%s' % arcname)
596
+
597
+ if lib_only and u_arcname.startswith((info_pfx, data_pfx)):
598
+ logger.debug('lib_only: skipping %s', u_arcname)
599
+ continue
600
+ is_script = (u_arcname.startswith(script_pfx) and not u_arcname.endswith('.exe'))
601
+
602
+ if u_arcname.startswith(data_pfx):
603
+ _, where, rp = u_arcname.split('/', 2)
604
+ outfile = os.path.join(paths[where], convert_path(rp))
605
+ else:
606
+ # meant for site-packages.
607
+ if u_arcname in (wheel_metadata_name, record_name):
608
+ continue
609
+ outfile = os.path.join(libdir, convert_path(u_arcname))
610
+ if not is_script:
611
+ with zf.open(arcname) as bf:
612
+ fileop.copy_stream(bf, outfile)
613
+ # Issue #147: permission bits aren't preserved. Using
614
+ # zf.extract(zinfo, libdir) should have worked, but didn't,
615
+ # see https://www.thetopsites.net/article/53834422.shtml
616
+ # So ... manually preserve permission bits as given in zinfo
617
+ if os.name == 'posix':
618
+ # just set the normal permission bits
619
+ os.chmod(outfile, (zinfo.external_attr >> 16) & 0x1FF)
620
+ outfiles.append(outfile)
621
+ # Double check the digest of the written file
622
+ if not dry_run and row[1]:
623
+ with open(outfile, 'rb') as bf:
624
+ data = bf.read()
625
+ _, newdigest = self.get_hash(data, kind)
626
+ if newdigest != digest:
627
+ raise DistlibException('digest mismatch '
628
+ 'on write for '
629
+ '%s' % outfile)
630
+ if bc and outfile.endswith('.py'):
631
+ try:
632
+ pyc = fileop.byte_compile(outfile, hashed_invalidation=bc_hashed_invalidation)
633
+ outfiles.append(pyc)
634
+ except Exception:
635
+ # Don't give up if byte-compilation fails,
636
+ # but log it and perhaps warn the user
637
+ logger.warning('Byte-compilation failed', exc_info=True)
638
+ else:
639
+ fn = os.path.basename(convert_path(arcname))
640
+ workname = os.path.join(workdir, fn)
641
+ with zf.open(arcname) as bf:
642
+ fileop.copy_stream(bf, workname)
643
+
644
+ dn, fn = os.path.split(outfile)
645
+ maker.target_dir = dn
646
+ filenames = maker.make(fn)
647
+ fileop.set_executable_mode(filenames)
648
+ outfiles.extend(filenames)
649
+
650
+ if lib_only:
651
+ logger.debug('lib_only: returning None')
652
+ dist = None
653
+ else:
654
+ # Generate scripts
655
+
656
+ # Try to get pydist.json so we can see if there are
657
+ # any commands to generate. If this fails (e.g. because
658
+ # of a legacy wheel), log a warning but don't give up.
659
+ commands = None
660
+ file_version = self.info['Wheel-Version']
661
+ if file_version == '1.0':
662
+ # Use legacy info
663
+ ep = posixpath.join(info_dir, 'entry_points.txt')
664
+ try:
665
+ with zf.open(ep) as bwf:
666
+ epdata = read_exports(bwf)
667
+ commands = {}
668
+ for key in ('console', 'gui'):
669
+ k = '%s_scripts' % key
670
+ if k in epdata:
671
+ commands['wrap_%s' % key] = d = {}
672
+ for v in epdata[k].values():
673
+ s = '%s:%s' % (v.prefix, v.suffix)
674
+ if v.flags:
675
+ s += ' [%s]' % ','.join(v.flags)
676
+ d[v.name] = s
677
+ except Exception:
678
+ logger.warning('Unable to read legacy script '
679
+ 'metadata, so cannot generate '
680
+ 'scripts')
681
+ else:
682
+ try:
683
+ with zf.open(metadata_name) as bwf:
684
+ wf = wrapper(bwf)
685
+ commands = json.load(wf).get('extensions')
686
+ if commands:
687
+ commands = commands.get('python.commands')
688
+ except Exception:
689
+ logger.warning('Unable to read JSON metadata, so '
690
+ 'cannot generate scripts')
691
+ if commands:
692
+ console_scripts = commands.get('wrap_console', {})
693
+ gui_scripts = commands.get('wrap_gui', {})
694
+ if console_scripts or gui_scripts:
695
+ script_dir = paths.get('scripts', '')
696
+ if not os.path.isdir(script_dir):
697
+ raise ValueError('Valid script path not '
698
+ 'specified')
699
+ maker.target_dir = script_dir
700
+ for k, v in console_scripts.items():
701
+ script = '%s = %s' % (k, v)
702
+ filenames = maker.make(script)
703
+ fileop.set_executable_mode(filenames)
704
+
705
+ if gui_scripts:
706
+ options = {'gui': True}
707
+ for k, v in gui_scripts.items():
708
+ script = '%s = %s' % (k, v)
709
+ filenames = maker.make(script, options)
710
+ fileop.set_executable_mode(filenames)
711
+
712
+ p = os.path.join(libdir, info_dir)
713
+ dist = InstalledDistribution(p)
714
+
715
+ # Write SHARED
716
+ paths = dict(paths) # don't change passed in dict
717
+ del paths['purelib']
718
+ del paths['platlib']
719
+ paths['lib'] = libdir
720
+ p = dist.write_shared_locations(paths, dry_run)
721
+ if p:
722
+ outfiles.append(p)
723
+
724
+ # Write RECORD
725
+ dist.write_installed_files(outfiles, paths['prefix'], dry_run)
726
+ return dist
727
+ except Exception: # pragma: no cover
728
+ logger.exception('installation failed.')
729
+ fileop.rollback()
730
+ raise
731
+ finally:
732
+ shutil.rmtree(workdir)
733
+
734
+ def _get_dylib_cache(self):
735
+ global cache
736
+ if cache is None:
737
+ # Use native string to avoid issues on 2.x: see Python #20140.
738
+ base = os.path.join(get_cache_base(), str('dylib-cache'), '%s.%s' % sys.version_info[:2])
739
+ cache = Cache(base)
740
+ return cache
741
+
742
+ def _get_extensions(self):
743
+ pathname = os.path.join(self.dirname, self.filename)
744
+ name_ver = '%s-%s' % (self.name, self.version)
745
+ info_dir = '%s.dist-info' % name_ver
746
+ arcname = posixpath.join(info_dir, 'EXTENSIONS')
747
+ wrapper = codecs.getreader('utf-8')
748
+ result = []
749
+ with ZipFile(pathname, 'r') as zf:
750
+ try:
751
+ with zf.open(arcname) as bf:
752
+ wf = wrapper(bf)
753
+ extensions = json.load(wf)
754
+ cache = self._get_dylib_cache()
755
+ prefix = cache.prefix_to_dir(self.filename, use_abspath=False)
756
+ cache_base = os.path.join(cache.base, prefix)
757
+ if not os.path.isdir(cache_base):
758
+ os.makedirs(cache_base)
759
+ for name, relpath in extensions.items():
760
+ dest = os.path.join(cache_base, convert_path(relpath))
761
+ if not os.path.exists(dest):
762
+ extract = True
763
+ else:
764
+ file_time = os.stat(dest).st_mtime
765
+ file_time = datetime.datetime.fromtimestamp(file_time)
766
+ info = zf.getinfo(relpath)
767
+ wheel_time = datetime.datetime(*info.date_time)
768
+ extract = wheel_time > file_time
769
+ if extract:
770
+ zf.extract(relpath, cache_base)
771
+ result.append((name, dest))
772
+ except KeyError:
773
+ pass
774
+ return result
775
+
776
+ def is_compatible(self):
777
+ """
778
+ Determine if a wheel is compatible with the running system.
779
+ """
780
+ return is_compatible(self)
781
+
782
+ def is_mountable(self):
783
+ """
784
+ Determine if a wheel is asserted as mountable by its metadata.
785
+ """
786
+ return True # for now - metadata details TBD
787
+
788
+ def mount(self, append=False):
789
+ pathname = os.path.abspath(os.path.join(self.dirname, self.filename))
790
+ if not self.is_compatible():
791
+ msg = 'Wheel %s not compatible with this Python.' % pathname
792
+ raise DistlibException(msg)
793
+ if not self.is_mountable():
794
+ msg = 'Wheel %s is marked as not mountable.' % pathname
795
+ raise DistlibException(msg)
796
+ if pathname in sys.path:
797
+ logger.debug('%s already in path', pathname)
798
+ else:
799
+ if append:
800
+ sys.path.append(pathname)
801
+ else:
802
+ sys.path.insert(0, pathname)
803
+ extensions = self._get_extensions()
804
+ if extensions:
805
+ if _hook not in sys.meta_path:
806
+ sys.meta_path.append(_hook)
807
+ _hook.add(pathname, extensions)
808
+
809
+ def unmount(self):
810
+ pathname = os.path.abspath(os.path.join(self.dirname, self.filename))
811
+ if pathname not in sys.path:
812
+ logger.debug('%s not in path', pathname)
813
+ else:
814
+ sys.path.remove(pathname)
815
+ if pathname in _hook.impure_wheels:
816
+ _hook.remove(pathname)
817
+ if not _hook.impure_wheels:
818
+ if _hook in sys.meta_path:
819
+ sys.meta_path.remove(_hook)
820
+
821
+ def verify(self):
822
+ pathname = os.path.join(self.dirname, self.filename)
823
+ name_ver = '%s-%s' % (self.name, self.version)
824
+ # data_dir = '%s.data' % name_ver
825
+ info_dir = '%s.dist-info' % name_ver
826
+
827
+ # metadata_name = posixpath.join(info_dir, LEGACY_METADATA_FILENAME)
828
+ wheel_metadata_name = posixpath.join(info_dir, 'WHEEL')
829
+ record_name = posixpath.join(info_dir, 'RECORD')
830
+
831
+ wrapper = codecs.getreader('utf-8')
832
+
833
+ with ZipFile(pathname, 'r') as zf:
834
+ with zf.open(wheel_metadata_name) as bwf:
835
+ wf = wrapper(bwf)
836
+ message_from_file(wf)
837
+ # wv = message['Wheel-Version'].split('.', 1)
838
+ # file_version = tuple([int(i) for i in wv])
839
+ # TODO version verification
840
+
841
+ records = {}
842
+ with zf.open(record_name) as bf:
843
+ with CSVReader(stream=bf) as reader:
844
+ for row in reader:
845
+ p = row[0]
846
+ records[p] = row
847
+
848
+ for zinfo in zf.infolist():
849
+ arcname = zinfo.filename
850
+ if isinstance(arcname, text_type):
851
+ u_arcname = arcname
852
+ else:
853
+ u_arcname = arcname.decode('utf-8')
854
+ # See issue #115: some wheels have .. in their entries, but
855
+ # in the filename ... e.g. __main__..py ! So the check is
856
+ # updated to look for .. in the directory portions
857
+ p = u_arcname.split('/')
858
+ if '..' in p:
859
+ raise DistlibException('invalid entry in '
860
+ 'wheel: %r' % u_arcname)
861
+
862
+ if self.skip_entry(u_arcname):
863
+ continue
864
+ row = records[u_arcname]
865
+ if row[2] and str(zinfo.file_size) != row[2]:
866
+ raise DistlibException('size mismatch for '
867
+ '%s' % u_arcname)
868
+ if row[1]:
869
+ kind, value = row[1].split('=', 1)
870
+ with zf.open(arcname) as bf:
871
+ data = bf.read()
872
+ _, digest = self.get_hash(data, kind)
873
+ if digest != value:
874
+ raise DistlibException('digest mismatch for '
875
+ '%s' % arcname)
876
+
877
+ def update(self, modifier, dest_dir=None, **kwargs):
878
+ """
879
+ Update the contents of a wheel in a generic way. The modifier should
880
+ be a callable which expects a dictionary argument: its keys are
881
+ archive-entry paths, and its values are absolute filesystem paths
882
+ where the contents the corresponding archive entries can be found. The
883
+ modifier is free to change the contents of the files pointed to, add
884
+ new entries and remove entries, before returning. This method will
885
+ extract the entire contents of the wheel to a temporary location, call
886
+ the modifier, and then use the passed (and possibly updated)
887
+ dictionary to write a new wheel. If ``dest_dir`` is specified, the new
888
+ wheel is written there -- otherwise, the original wheel is overwritten.
889
+
890
+ The modifier should return True if it updated the wheel, else False.
891
+ This method returns the same value the modifier returns.
892
+ """
893
+
894
+ def get_version(path_map, info_dir):
895
+ version = path = None
896
+ key = '%s/%s' % (info_dir, LEGACY_METADATA_FILENAME)
897
+ if key not in path_map:
898
+ key = '%s/PKG-INFO' % info_dir
899
+ if key in path_map:
900
+ path = path_map[key]
901
+ version = Metadata(path=path).version
902
+ return version, path
903
+
904
+ def update_version(version, path):
905
+ updated = None
906
+ try:
907
+ NormalizedVersion(version)
908
+ i = version.find('-')
909
+ if i < 0:
910
+ updated = '%s+1' % version
911
+ else:
912
+ parts = [int(s) for s in version[i + 1:].split('.')]
913
+ parts[-1] += 1
914
+ updated = '%s+%s' % (version[:i], '.'.join(str(i) for i in parts))
915
+ except UnsupportedVersionError:
916
+ logger.debug('Cannot update non-compliant (PEP-440) '
917
+ 'version %r', version)
918
+ if updated:
919
+ md = Metadata(path=path)
920
+ md.version = updated
921
+ legacy = path.endswith(LEGACY_METADATA_FILENAME)
922
+ md.write(path=path, legacy=legacy)
923
+ logger.debug('Version updated from %r to %r', version, updated)
924
+
925
+ pathname = os.path.join(self.dirname, self.filename)
926
+ name_ver = '%s-%s' % (self.name, self.version)
927
+ info_dir = '%s.dist-info' % name_ver
928
+ record_name = posixpath.join(info_dir, 'RECORD')
929
+ with tempdir() as workdir:
930
+ with ZipFile(pathname, 'r') as zf:
931
+ path_map = {}
932
+ for zinfo in zf.infolist():
933
+ arcname = zinfo.filename
934
+ if isinstance(arcname, text_type):
935
+ u_arcname = arcname
936
+ else:
937
+ u_arcname = arcname.decode('utf-8')
938
+ if u_arcname == record_name:
939
+ continue
940
+ if '..' in u_arcname:
941
+ raise DistlibException('invalid entry in '
942
+ 'wheel: %r' % u_arcname)
943
+ zf.extract(zinfo, workdir)
944
+ path = os.path.join(workdir, convert_path(u_arcname))
945
+ path_map[u_arcname] = path
946
+
947
+ # Remember the version.
948
+ original_version, _ = get_version(path_map, info_dir)
949
+ # Files extracted. Call the modifier.
950
+ modified = modifier(path_map, **kwargs)
951
+ if modified:
952
+ # Something changed - need to build a new wheel.
953
+ current_version, path = get_version(path_map, info_dir)
954
+ if current_version and (current_version == original_version):
955
+ # Add or update local version to signify changes.
956
+ update_version(current_version, path)
957
+ # Decide where the new wheel goes.
958
+ if dest_dir is None:
959
+ fd, newpath = tempfile.mkstemp(suffix='.whl', prefix='wheel-update-', dir=workdir)
960
+ os.close(fd)
961
+ else:
962
+ if not os.path.isdir(dest_dir):
963
+ raise DistlibException('Not a directory: %r' % dest_dir)
964
+ newpath = os.path.join(dest_dir, self.filename)
965
+ archive_paths = list(path_map.items())
966
+ distinfo = os.path.join(workdir, info_dir)
967
+ info = distinfo, info_dir
968
+ self.write_records(info, workdir, archive_paths)
969
+ self.build_zip(newpath, archive_paths)
970
+ if dest_dir is None:
971
+ shutil.copyfile(newpath, pathname)
972
+ return modified
973
+
974
+
975
+ def _get_glibc_version():
976
+ import platform
977
+ ver = platform.libc_ver()
978
+ result = []
979
+ if ver[0] == 'glibc':
980
+ for s in ver[1].split('.'):
981
+ result.append(int(s) if s.isdigit() else 0)
982
+ result = tuple(result)
983
+ return result
984
+
985
+
986
+ def compatible_tags():
987
+ """
988
+ Return (pyver, abi, arch) tuples compatible with this Python.
989
+ """
990
+ class _Version:
991
+ def __init__(self, major, minor):
992
+ self.major = major
993
+ self.major_minor = (major, minor)
994
+ self.string = ''.join((str(major), str(minor)))
995
+
996
+ def __str__(self):
997
+ return self.string
998
+
999
+
1000
+ versions = [
1001
+ _Version(sys.version_info.major, minor_version)
1002
+ for minor_version in range(sys.version_info.minor, -1, -1)
1003
+ ]
1004
+ abis = []
1005
+ for suffix in _get_suffixes():
1006
+ if suffix.startswith('.abi'):
1007
+ abis.append(suffix.split('.', 2)[1])
1008
+ abis.sort()
1009
+ if ABI != 'none':
1010
+ abis.insert(0, ABI)
1011
+ abis.append('none')
1012
+ result = []
1013
+
1014
+ arches = [ARCH]
1015
+ if sys.platform == 'darwin':
1016
+ m = re.match(r'(\w+)_(\d+)_(\d+)_(\w+)$', ARCH)
1017
+ if m:
1018
+ name, major, minor, arch = m.groups()
1019
+ minor = int(minor)
1020
+ matches = [arch]
1021
+ if arch in ('i386', 'ppc'):
1022
+ matches.append('fat')
1023
+ if arch in ('i386', 'ppc', 'x86_64'):
1024
+ matches.append('fat3')
1025
+ if arch in ('ppc64', 'x86_64'):
1026
+ matches.append('fat64')
1027
+ if arch in ('i386', 'x86_64'):
1028
+ matches.append('intel')
1029
+ if arch in ('i386', 'x86_64', 'intel', 'ppc', 'ppc64'):
1030
+ matches.append('universal')
1031
+ while minor >= 0:
1032
+ for match in matches:
1033
+ s = '%s_%s_%s_%s' % (name, major, minor, match)
1034
+ if s != ARCH: # already there
1035
+ arches.append(s)
1036
+ minor -= 1
1037
+
1038
+ # Most specific - our Python version, ABI and arch
1039
+ for i, version_object in enumerate(versions):
1040
+ version = str(version_object)
1041
+ add_abis = []
1042
+
1043
+ if i == 0:
1044
+ add_abis = abis
1045
+
1046
+ if IMP_PREFIX == 'cp' and version_object.major_minor >= (3, 2):
1047
+ limited_api_abi = 'abi' + str(version_object.major)
1048
+ if limited_api_abi not in add_abis:
1049
+ add_abis.append(limited_api_abi)
1050
+
1051
+ for abi in add_abis:
1052
+ for arch in arches:
1053
+ result.append((''.join((IMP_PREFIX, version)), abi, arch))
1054
+ # manylinux
1055
+ if abi != 'none' and sys.platform.startswith('linux'):
1056
+ arch = arch.replace('linux_', '')
1057
+ parts = _get_glibc_version()
1058
+ if len(parts) == 2:
1059
+ if parts >= (2, 5):
1060
+ result.append((''.join((IMP_PREFIX, version)), abi, 'manylinux1_%s' % arch))
1061
+ if parts >= (2, 12):
1062
+ result.append((''.join((IMP_PREFIX, version)), abi, 'manylinux2010_%s' % arch))
1063
+ if parts >= (2, 17):
1064
+ result.append((''.join((IMP_PREFIX, version)), abi, 'manylinux2014_%s' % arch))
1065
+ result.append((''.join(
1066
+ (IMP_PREFIX, version)), abi, 'manylinux_%s_%s_%s' % (parts[0], parts[1], arch)))
1067
+
1068
+ # where no ABI / arch dependency, but IMP_PREFIX dependency
1069
+ for i, version_object in enumerate(versions):
1070
+ version = str(version_object)
1071
+ result.append((''.join((IMP_PREFIX, version)), 'none', 'any'))
1072
+ if i == 0:
1073
+ result.append((''.join((IMP_PREFIX, version[0])), 'none', 'any'))
1074
+
1075
+ # no IMP_PREFIX, ABI or arch dependency
1076
+ for i, version_object in enumerate(versions):
1077
+ version = str(version_object)
1078
+ result.append((''.join(('py', version)), 'none', 'any'))
1079
+ if i == 0:
1080
+ result.append((''.join(('py', version[0])), 'none', 'any'))
1081
+
1082
+ return set(result)
1083
+
1084
+
1085
+ COMPATIBLE_TAGS = compatible_tags()
1086
+
1087
+ del compatible_tags
1088
+
1089
+
1090
+ def is_compatible(wheel, tags=None):
1091
+ if not isinstance(wheel, Wheel):
1092
+ wheel = Wheel(wheel) # assume it's a filename
1093
+ result = False
1094
+ if tags is None:
1095
+ tags = COMPATIBLE_TAGS
1096
+ for ver, abi, arch in tags:
1097
+ if ver in wheel.pyver and abi in wheel.abi and arch in wheel.arch:
1098
+ result = True
1099
+ break
1100
+ return result
lib/python3.12/site-packages/pip/_vendor/rich/_export_format.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CONSOLE_HTML_FORMAT = """\
2
+ <!DOCTYPE html>
3
+ <html>
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <style>
7
+ {stylesheet}
8
+ body {{
9
+ color: {foreground};
10
+ background-color: {background};
11
+ }}
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <pre style="font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><code style="font-family:inherit">{code}</code></pre>
16
+ </body>
17
+ </html>
18
+ """
19
+
20
+ CONSOLE_SVG_FORMAT = """\
21
+ <svg class="rich-terminal" viewBox="0 0 {width} {height}" xmlns="http://www.w3.org/2000/svg">
22
+ <!-- Generated with Rich https://www.textualize.io -->
23
+ <style>
24
+
25
+ @font-face {{
26
+ font-family: "Fira Code";
27
+ src: local("FiraCode-Regular"),
28
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2") format("woff2"),
29
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff") format("woff");
30
+ font-style: normal;
31
+ font-weight: 400;
32
+ }}
33
+ @font-face {{
34
+ font-family: "Fira Code";
35
+ src: local("FiraCode-Bold"),
36
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2") format("woff2"),
37
+ url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff") format("woff");
38
+ font-style: bold;
39
+ font-weight: 700;
40
+ }}
41
+
42
+ .{unique_id}-matrix {{
43
+ font-family: Fira Code, monospace;
44
+ font-size: {char_height}px;
45
+ line-height: {line_height}px;
46
+ font-variant-east-asian: full-width;
47
+ }}
48
+
49
+ .{unique_id}-title {{
50
+ font-size: 18px;
51
+ font-weight: bold;
52
+ font-family: arial;
53
+ }}
54
+
55
+ {styles}
56
+ </style>
57
+
58
+ <defs>
59
+ <clipPath id="{unique_id}-clip-terminal">
60
+ <rect x="0" y="0" width="{terminal_width}" height="{terminal_height}" />
61
+ </clipPath>
62
+ {lines}
63
+ </defs>
64
+
65
+ {chrome}
66
+ <g transform="translate({terminal_x}, {terminal_y})" clip-path="url(#{unique_id}-clip-terminal)">
67
+ {backgrounds}
68
+ <g class="{unique_id}-matrix">
69
+ {matrix}
70
+ </g>
71
+ </g>
72
+ </svg>
73
+ """
74
+
75
+ _SVG_FONT_FAMILY = "Rich Fira Code"
76
+ _SVG_CLASSES_PREFIX = "rich-svg"
lib/python3.12/site-packages/pip/_vendor/rich/table.py ADDED
@@ -0,0 +1,1007 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass, field, replace
2
+ from typing import (
3
+ TYPE_CHECKING,
4
+ Dict,
5
+ Iterable,
6
+ List,
7
+ NamedTuple,
8
+ Optional,
9
+ Sequence,
10
+ Tuple,
11
+ Union,
12
+ )
13
+
14
+ from . import box, errors
15
+ from ._loop import loop_first_last, loop_last
16
+ from ._pick import pick_bool
17
+ from ._ratio import ratio_distribute, ratio_reduce
18
+ from .align import VerticalAlignMethod
19
+ from .jupyter import JupyterMixin
20
+ from .measure import Measurement
21
+ from .padding import Padding, PaddingDimensions
22
+ from .protocol import is_renderable
23
+ from .segment import Segment
24
+ from .style import Style, StyleType
25
+ from .text import Text, TextType
26
+
27
+ if TYPE_CHECKING:
28
+ from .console import (
29
+ Console,
30
+ ConsoleOptions,
31
+ JustifyMethod,
32
+ OverflowMethod,
33
+ RenderableType,
34
+ RenderResult,
35
+ )
36
+
37
+
38
+ @dataclass
39
+ class Column:
40
+ """Defines a column within a ~Table.
41
+
42
+ Args:
43
+ title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.
44
+ caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.
45
+ width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.
46
+ min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.
47
+ box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.
48
+ safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
49
+ padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).
50
+ collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.
51
+ pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.
52
+ expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
53
+ show_header (bool, optional): Show a header row. Defaults to True.
54
+ show_footer (bool, optional): Show a footer row. Defaults to False.
55
+ show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.
56
+ show_lines (bool, optional): Draw lines between every row. Defaults to False.
57
+ leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.
58
+ style (Union[str, Style], optional): Default style for the table. Defaults to "none".
59
+ row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.
60
+ header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".
61
+ footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".
62
+ border_style (Union[str, Style], optional): Style of the border. Defaults to None.
63
+ title_style (Union[str, Style], optional): Style of the title. Defaults to None.
64
+ caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.
65
+ title_justify (str, optional): Justify method for title. Defaults to "center".
66
+ caption_justify (str, optional): Justify method for caption. Defaults to "center".
67
+ highlight (bool, optional): Highlight cell contents (if str). Defaults to False.
68
+ """
69
+
70
+ header: "RenderableType" = ""
71
+ """RenderableType: Renderable for the header (typically a string)"""
72
+
73
+ footer: "RenderableType" = ""
74
+ """RenderableType: Renderable for the footer (typically a string)"""
75
+
76
+ header_style: StyleType = ""
77
+ """StyleType: The style of the header."""
78
+
79
+ footer_style: StyleType = ""
80
+ """StyleType: The style of the footer."""
81
+
82
+ style: StyleType = ""
83
+ """StyleType: The style of the column."""
84
+
85
+ justify: "JustifyMethod" = "left"
86
+ """str: How to justify text within the column ("left", "center", "right", or "full")"""
87
+
88
+ vertical: "VerticalAlignMethod" = "top"
89
+ """str: How to vertically align content ("top", "middle", or "bottom")"""
90
+
91
+ overflow: "OverflowMethod" = "ellipsis"
92
+ """str: Overflow method."""
93
+
94
+ width: Optional[int] = None
95
+ """Optional[int]: Width of the column, or ``None`` (default) to auto calculate width."""
96
+
97
+ min_width: Optional[int] = None
98
+ """Optional[int]: Minimum width of column, or ``None`` for no minimum. Defaults to None."""
99
+
100
+ max_width: Optional[int] = None
101
+ """Optional[int]: Maximum width of column, or ``None`` for no maximum. Defaults to None."""
102
+
103
+ ratio: Optional[int] = None
104
+ """Optional[int]: Ratio to use when calculating column width, or ``None`` (default) to adapt to column contents."""
105
+
106
+ no_wrap: bool = False
107
+ """bool: Prevent wrapping of text within the column. Defaults to ``False``."""
108
+
109
+ highlight: bool = False
110
+ """bool: Apply highlighter to column. Defaults to ``False``."""
111
+
112
+ _index: int = 0
113
+ """Index of column."""
114
+
115
+ _cells: List["RenderableType"] = field(default_factory=list)
116
+
117
+ def copy(self) -> "Column":
118
+ """Return a copy of this Column."""
119
+ return replace(self, _cells=[])
120
+
121
+ @property
122
+ def cells(self) -> Iterable["RenderableType"]:
123
+ """Get all cells in the column, not including header."""
124
+ yield from self._cells
125
+
126
+ @property
127
+ def flexible(self) -> bool:
128
+ """Check if this column is flexible."""
129
+ return self.ratio is not None
130
+
131
+
132
+ @dataclass
133
+ class Row:
134
+ """Information regarding a row."""
135
+
136
+ style: Optional[StyleType] = None
137
+ """Style to apply to row."""
138
+
139
+ end_section: bool = False
140
+ """Indicated end of section, which will force a line beneath the row."""
141
+
142
+
143
+ class _Cell(NamedTuple):
144
+ """A single cell in a table."""
145
+
146
+ style: StyleType
147
+ """Style to apply to cell."""
148
+ renderable: "RenderableType"
149
+ """Cell renderable."""
150
+ vertical: VerticalAlignMethod
151
+ """Cell vertical alignment."""
152
+
153
+
154
+ class Table(JupyterMixin):
155
+ """A console renderable to draw a table.
156
+
157
+ Args:
158
+ *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.
159
+ title (Union[str, Text], optional): The title of the table rendered at the top. Defaults to None.
160
+ caption (Union[str, Text], optional): The table caption rendered below. Defaults to None.
161
+ width (int, optional): The width in characters of the table, or ``None`` to automatically fit. Defaults to None.
162
+ min_width (Optional[int], optional): The minimum width of the table, or ``None`` for no minimum. Defaults to None.
163
+ box (box.Box, optional): One of the constants in box.py used to draw the edges (see :ref:`appendix_box`), or ``None`` for no box lines. Defaults to box.HEAVY_HEAD.
164
+ safe_box (Optional[bool], optional): Disable box characters that don't display on windows legacy terminal with *raster* fonts. Defaults to True.
165
+ padding (PaddingDimensions, optional): Padding for cells (top, right, bottom, left). Defaults to (0, 1).
166
+ collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to False.
167
+ pad_edge (bool, optional): Enable padding of edge cells. Defaults to True.
168
+ expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
169
+ show_header (bool, optional): Show a header row. Defaults to True.
170
+ show_footer (bool, optional): Show a footer row. Defaults to False.
171
+ show_edge (bool, optional): Draw a box around the outside of the table. Defaults to True.
172
+ show_lines (bool, optional): Draw lines between every row. Defaults to False.
173
+ leading (int, optional): Number of blank lines between rows (precludes ``show_lines``). Defaults to 0.
174
+ style (Union[str, Style], optional): Default style for the table. Defaults to "none".
175
+ row_styles (List[Union, str], optional): Optional list of row styles, if more than one style is given then the styles will alternate. Defaults to None.
176
+ header_style (Union[str, Style], optional): Style of the header. Defaults to "table.header".
177
+ footer_style (Union[str, Style], optional): Style of the footer. Defaults to "table.footer".
178
+ border_style (Union[str, Style], optional): Style of the border. Defaults to None.
179
+ title_style (Union[str, Style], optional): Style of the title. Defaults to None.
180
+ caption_style (Union[str, Style], optional): Style of the caption. Defaults to None.
181
+ title_justify (str, optional): Justify method for title. Defaults to "center".
182
+ caption_justify (str, optional): Justify method for caption. Defaults to "center".
183
+ highlight (bool, optional): Highlight cell contents (if str). Defaults to False.
184
+ """
185
+
186
+ columns: List[Column]
187
+ rows: List[Row]
188
+
189
+ def __init__(
190
+ self,
191
+ *headers: Union[Column, str],
192
+ title: Optional[TextType] = None,
193
+ caption: Optional[TextType] = None,
194
+ width: Optional[int] = None,
195
+ min_width: Optional[int] = None,
196
+ box: Optional[box.Box] = box.HEAVY_HEAD,
197
+ safe_box: Optional[bool] = None,
198
+ padding: PaddingDimensions = (0, 1),
199
+ collapse_padding: bool = False,
200
+ pad_edge: bool = True,
201
+ expand: bool = False,
202
+ show_header: bool = True,
203
+ show_footer: bool = False,
204
+ show_edge: bool = True,
205
+ show_lines: bool = False,
206
+ leading: int = 0,
207
+ style: StyleType = "none",
208
+ row_styles: Optional[Iterable[StyleType]] = None,
209
+ header_style: Optional[StyleType] = "table.header",
210
+ footer_style: Optional[StyleType] = "table.footer",
211
+ border_style: Optional[StyleType] = None,
212
+ title_style: Optional[StyleType] = None,
213
+ caption_style: Optional[StyleType] = None,
214
+ title_justify: "JustifyMethod" = "center",
215
+ caption_justify: "JustifyMethod" = "center",
216
+ highlight: bool = False,
217
+ ) -> None:
218
+ self.columns: List[Column] = []
219
+ self.rows: List[Row] = []
220
+ self.title = title
221
+ self.caption = caption
222
+ self.width = width
223
+ self.min_width = min_width
224
+ self.box = box
225
+ self.safe_box = safe_box
226
+ self._padding = Padding.unpack(padding)
227
+ self.pad_edge = pad_edge
228
+ self._expand = expand
229
+ self.show_header = show_header
230
+ self.show_footer = show_footer
231
+ self.show_edge = show_edge
232
+ self.show_lines = show_lines
233
+ self.leading = leading
234
+ self.collapse_padding = collapse_padding
235
+ self.style = style
236
+ self.header_style = header_style or ""
237
+ self.footer_style = footer_style or ""
238
+ self.border_style = border_style
239
+ self.title_style = title_style
240
+ self.caption_style = caption_style
241
+ self.title_justify: "JustifyMethod" = title_justify
242
+ self.caption_justify: "JustifyMethod" = caption_justify
243
+ self.highlight = highlight
244
+ self.row_styles: Sequence[StyleType] = list(row_styles or [])
245
+ append_column = self.columns.append
246
+ for header in headers:
247
+ if isinstance(header, str):
248
+ self.add_column(header=header)
249
+ else:
250
+ header._index = len(self.columns)
251
+ append_column(header)
252
+
253
+ @classmethod
254
+ def grid(
255
+ cls,
256
+ *headers: Union[Column, str],
257
+ padding: PaddingDimensions = 0,
258
+ collapse_padding: bool = True,
259
+ pad_edge: bool = False,
260
+ expand: bool = False,
261
+ ) -> "Table":
262
+ """Get a table with no lines, headers, or footer.
263
+
264
+ Args:
265
+ *headers (Union[Column, str]): Column headers, either as a string, or :class:`~rich.table.Column` instance.
266
+ padding (PaddingDimensions, optional): Get padding around cells. Defaults to 0.
267
+ collapse_padding (bool, optional): Enable collapsing of padding around cells. Defaults to True.
268
+ pad_edge (bool, optional): Enable padding around edges of table. Defaults to False.
269
+ expand (bool, optional): Expand the table to fit the available space if ``True``, otherwise the table width will be auto-calculated. Defaults to False.
270
+
271
+ Returns:
272
+ Table: A table instance.
273
+ """
274
+ return cls(
275
+ *headers,
276
+ box=None,
277
+ padding=padding,
278
+ collapse_padding=collapse_padding,
279
+ show_header=False,
280
+ show_footer=False,
281
+ show_edge=False,
282
+ pad_edge=pad_edge,
283
+ expand=expand,
284
+ )
285
+
286
+ @property
287
+ def expand(self) -> bool:
288
+ """Setting a non-None self.width implies expand."""
289
+ return self._expand or self.width is not None
290
+
291
+ @expand.setter
292
+ def expand(self, expand: bool) -> None:
293
+ """Set expand."""
294
+ self._expand = expand
295
+
296
+ @property
297
+ def _extra_width(self) -> int:
298
+ """Get extra width to add to cell content."""
299
+ width = 0
300
+ if self.box and self.show_edge:
301
+ width += 2
302
+ if self.box:
303
+ width += len(self.columns) - 1
304
+ return width
305
+
306
+ @property
307
+ def row_count(self) -> int:
308
+ """Get the current number of rows."""
309
+ return len(self.rows)
310
+
311
+ def get_row_style(self, console: "Console", index: int) -> StyleType:
312
+ """Get the current row style."""
313
+ style = Style.null()
314
+ if self.row_styles:
315
+ style += console.get_style(self.row_styles[index % len(self.row_styles)])
316
+ row_style = self.rows[index].style
317
+ if row_style is not None:
318
+ style += console.get_style(row_style)
319
+ return style
320
+
321
+ def __rich_measure__(
322
+ self, console: "Console", options: "ConsoleOptions"
323
+ ) -> Measurement:
324
+ max_width = options.max_width
325
+ if self.width is not None:
326
+ max_width = self.width
327
+ if max_width < 0:
328
+ return Measurement(0, 0)
329
+
330
+ extra_width = self._extra_width
331
+ max_width = sum(
332
+ self._calculate_column_widths(
333
+ console, options.update_width(max_width - extra_width)
334
+ )
335
+ )
336
+ _measure_column = self._measure_column
337
+
338
+ measurements = [
339
+ _measure_column(console, options.update_width(max_width), column)
340
+ for column in self.columns
341
+ ]
342
+ minimum_width = (
343
+ sum(measurement.minimum for measurement in measurements) + extra_width
344
+ )
345
+ maximum_width = (
346
+ sum(measurement.maximum for measurement in measurements) + extra_width
347
+ if (self.width is None)
348
+ else self.width
349
+ )
350
+ measurement = Measurement(minimum_width, maximum_width)
351
+ measurement = measurement.clamp(self.min_width)
352
+ return measurement
353
+
354
+ @property
355
+ def padding(self) -> Tuple[int, int, int, int]:
356
+ """Get cell padding."""
357
+ return self._padding
358
+
359
+ @padding.setter
360
+ def padding(self, padding: PaddingDimensions) -> "Table":
361
+ """Set cell padding."""
362
+ self._padding = Padding.unpack(padding)
363
+ return self
364
+
365
+ def add_column(
366
+ self,
367
+ header: "RenderableType" = "",
368
+ footer: "RenderableType" = "",
369
+ *,
370
+ header_style: Optional[StyleType] = None,
371
+ highlight: Optional[bool] = None,
372
+ footer_style: Optional[StyleType] = None,
373
+ style: Optional[StyleType] = None,
374
+ justify: "JustifyMethod" = "left",
375
+ vertical: "VerticalAlignMethod" = "top",
376
+ overflow: "OverflowMethod" = "ellipsis",
377
+ width: Optional[int] = None,
378
+ min_width: Optional[int] = None,
379
+ max_width: Optional[int] = None,
380
+ ratio: Optional[int] = None,
381
+ no_wrap: bool = False,
382
+ ) -> None:
383
+ """Add a column to the table.
384
+
385
+ Args:
386
+ header (RenderableType, optional): Text or renderable for the header.
387
+ Defaults to "".
388
+ footer (RenderableType, optional): Text or renderable for the footer.
389
+ Defaults to "".
390
+ header_style (Union[str, Style], optional): Style for the header, or None for default. Defaults to None.
391
+ highlight (bool, optional): Whether to highlight the text. The default of None uses the value of the table (self) object.
392
+ footer_style (Union[str, Style], optional): Style for the footer, or None for default. Defaults to None.
393
+ style (Union[str, Style], optional): Style for the column cells, or None for default. Defaults to None.
394
+ justify (JustifyMethod, optional): Alignment for cells. Defaults to "left".
395
+ vertical (VerticalAlignMethod, optional): Vertical alignment, one of "top", "middle", or "bottom". Defaults to "top".
396
+ overflow (OverflowMethod): Overflow method: "crop", "fold", "ellipsis". Defaults to "ellipsis".
397
+ width (int, optional): Desired width of column in characters, or None to fit to contents. Defaults to None.
398
+ min_width (Optional[int], optional): Minimum width of column, or ``None`` for no minimum. Defaults to None.
399
+ max_width (Optional[int], optional): Maximum width of column, or ``None`` for no maximum. Defaults to None.
400
+ ratio (int, optional): Flexible ratio for the column (requires ``Table.expand`` or ``Table.width``). Defaults to None.
401
+ no_wrap (bool, optional): Set to ``True`` to disable wrapping of this column.
402
+ """
403
+
404
+ column = Column(
405
+ _index=len(self.columns),
406
+ header=header,
407
+ footer=footer,
408
+ header_style=header_style or "",
409
+ highlight=highlight if highlight is not None else self.highlight,
410
+ footer_style=footer_style or "",
411
+ style=style or "",
412
+ justify=justify,
413
+ vertical=vertical,
414
+ overflow=overflow,
415
+ width=width,
416
+ min_width=min_width,
417
+ max_width=max_width,
418
+ ratio=ratio,
419
+ no_wrap=no_wrap,
420
+ )
421
+ self.columns.append(column)
422
+
423
+ def add_row(
424
+ self,
425
+ *renderables: Optional["RenderableType"],
426
+ style: Optional[StyleType] = None,
427
+ end_section: bool = False,
428
+ ) -> None:
429
+ """Add a row of renderables.
430
+
431
+ Args:
432
+ *renderables (None or renderable): Each cell in a row must be a renderable object (including str),
433
+ or ``None`` for a blank cell.
434
+ style (StyleType, optional): An optional style to apply to the entire row. Defaults to None.
435
+ end_section (bool, optional): End a section and draw a line. Defaults to False.
436
+
437
+ Raises:
438
+ errors.NotRenderableError: If you add something that can't be rendered.
439
+ """
440
+
441
+ def add_cell(column: Column, renderable: "RenderableType") -> None:
442
+ column._cells.append(renderable)
443
+
444
+ cell_renderables: List[Optional["RenderableType"]] = list(renderables)
445
+
446
+ columns = self.columns
447
+ if len(cell_renderables) < len(columns):
448
+ cell_renderables = [
449
+ *cell_renderables,
450
+ *[None] * (len(columns) - len(cell_renderables)),
451
+ ]
452
+ for index, renderable in enumerate(cell_renderables):
453
+ if index == len(columns):
454
+ column = Column(_index=index, highlight=self.highlight)
455
+ for _ in self.rows:
456
+ add_cell(column, Text(""))
457
+ self.columns.append(column)
458
+ else:
459
+ column = columns[index]
460
+ if renderable is None:
461
+ add_cell(column, "")
462
+ elif is_renderable(renderable):
463
+ add_cell(column, renderable)
464
+ else:
465
+ raise errors.NotRenderableError(
466
+ f"unable to render {type(renderable).__name__}; a string or other renderable object is required"
467
+ )
468
+ self.rows.append(Row(style=style, end_section=end_section))
469
+
470
+ def add_section(self) -> None:
471
+ """Add a new section (draw a line after current row)."""
472
+
473
+ if self.rows:
474
+ self.rows[-1].end_section = True
475
+
476
+ def __rich_console__(
477
+ self, console: "Console", options: "ConsoleOptions"
478
+ ) -> "RenderResult":
479
+ if not self.columns:
480
+ yield Segment("\n")
481
+ return
482
+
483
+ max_width = options.max_width
484
+ if self.width is not None:
485
+ max_width = self.width
486
+
487
+ extra_width = self._extra_width
488
+ widths = self._calculate_column_widths(
489
+ console, options.update_width(max_width - extra_width)
490
+ )
491
+ table_width = sum(widths) + extra_width
492
+
493
+ render_options = options.update(
494
+ width=table_width, highlight=self.highlight, height=None
495
+ )
496
+
497
+ def render_annotation(
498
+ text: TextType, style: StyleType, justify: "JustifyMethod" = "center"
499
+ ) -> "RenderResult":
500
+ render_text = (
501
+ console.render_str(text, style=style, highlight=False)
502
+ if isinstance(text, str)
503
+ else text
504
+ )
505
+ return console.render(
506
+ render_text, options=render_options.update(justify=justify)
507
+ )
508
+
509
+ if self.title:
510
+ yield from render_annotation(
511
+ self.title,
512
+ style=Style.pick_first(self.title_style, "table.title"),
513
+ justify=self.title_justify,
514
+ )
515
+ yield from self._render(console, render_options, widths)
516
+ if self.caption:
517
+ yield from render_annotation(
518
+ self.caption,
519
+ style=Style.pick_first(self.caption_style, "table.caption"),
520
+ justify=self.caption_justify,
521
+ )
522
+
523
+ def _calculate_column_widths(
524
+ self, console: "Console", options: "ConsoleOptions"
525
+ ) -> List[int]:
526
+ """Calculate the widths of each column, including padding, not including borders."""
527
+ max_width = options.max_width
528
+ columns = self.columns
529
+ width_ranges = [
530
+ self._measure_column(console, options, column) for column in columns
531
+ ]
532
+ widths = [_range.maximum or 1 for _range in width_ranges]
533
+ get_padding_width = self._get_padding_width
534
+ extra_width = self._extra_width
535
+ if self.expand:
536
+ ratios = [col.ratio or 0 for col in columns if col.flexible]
537
+ if any(ratios):
538
+ fixed_widths = [
539
+ 0 if column.flexible else _range.maximum
540
+ for _range, column in zip(width_ranges, columns)
541
+ ]
542
+ flex_minimum = [
543
+ (column.width or 1) + get_padding_width(column._index)
544
+ for column in columns
545
+ if column.flexible
546
+ ]
547
+ flexible_width = max_width - sum(fixed_widths)
548
+ flex_widths = ratio_distribute(flexible_width, ratios, flex_minimum)
549
+ iter_flex_widths = iter(flex_widths)
550
+ for index, column in enumerate(columns):
551
+ if column.flexible:
552
+ widths[index] = fixed_widths[index] + next(iter_flex_widths)
553
+ table_width = sum(widths)
554
+
555
+ if table_width > max_width:
556
+ widths = self._collapse_widths(
557
+ widths,
558
+ [(column.width is None and not column.no_wrap) for column in columns],
559
+ max_width,
560
+ )
561
+ table_width = sum(widths)
562
+ # last resort, reduce columns evenly
563
+ if table_width > max_width:
564
+ excess_width = table_width - max_width
565
+ widths = ratio_reduce(excess_width, [1] * len(widths), widths, widths)
566
+ table_width = sum(widths)
567
+
568
+ width_ranges = [
569
+ self._measure_column(console, options.update_width(width), column)
570
+ for width, column in zip(widths, columns)
571
+ ]
572
+ widths = [_range.maximum or 0 for _range in width_ranges]
573
+
574
+ if (table_width < max_width and self.expand) or (
575
+ self.min_width is not None and table_width < (self.min_width - extra_width)
576
+ ):
577
+ _max_width = (
578
+ max_width
579
+ if self.min_width is None
580
+ else min(self.min_width - extra_width, max_width)
581
+ )
582
+ pad_widths = ratio_distribute(_max_width - table_width, widths)
583
+ widths = [_width + pad for _width, pad in zip(widths, pad_widths)]
584
+
585
+ return widths
586
+
587
+ @classmethod
588
+ def _collapse_widths(
589
+ cls, widths: List[int], wrapable: List[bool], max_width: int
590
+ ) -> List[int]:
591
+ """Reduce widths so that the total is under max_width.
592
+
593
+ Args:
594
+ widths (List[int]): List of widths.
595
+ wrapable (List[bool]): List of booleans that indicate if a column may shrink.
596
+ max_width (int): Maximum width to reduce to.
597
+
598
+ Returns:
599
+ List[int]: A new list of widths.
600
+ """
601
+ total_width = sum(widths)
602
+ excess_width = total_width - max_width
603
+ if any(wrapable):
604
+ while total_width and excess_width > 0:
605
+ max_column = max(
606
+ width for width, allow_wrap in zip(widths, wrapable) if allow_wrap
607
+ )
608
+ second_max_column = max(
609
+ width if allow_wrap and width != max_column else 0
610
+ for width, allow_wrap in zip(widths, wrapable)
611
+ )
612
+ column_difference = max_column - second_max_column
613
+ ratios = [
614
+ (1 if (width == max_column and allow_wrap) else 0)
615
+ for width, allow_wrap in zip(widths, wrapable)
616
+ ]
617
+ if not any(ratios) or not column_difference:
618
+ break
619
+ max_reduce = [min(excess_width, column_difference)] * len(widths)
620
+ widths = ratio_reduce(excess_width, ratios, max_reduce, widths)
621
+
622
+ total_width = sum(widths)
623
+ excess_width = total_width - max_width
624
+ return widths
625
+
626
+ def _get_cells(
627
+ self, console: "Console", column_index: int, column: Column
628
+ ) -> Iterable[_Cell]:
629
+ """Get all the cells with padding and optional header."""
630
+
631
+ collapse_padding = self.collapse_padding
632
+ pad_edge = self.pad_edge
633
+ padding = self.padding
634
+ any_padding = any(padding)
635
+
636
+ first_column = column_index == 0
637
+ last_column = column_index == len(self.columns) - 1
638
+
639
+ _padding_cache: Dict[Tuple[bool, bool], Tuple[int, int, int, int]] = {}
640
+
641
+ def get_padding(first_row: bool, last_row: bool) -> Tuple[int, int, int, int]:
642
+ cached = _padding_cache.get((first_row, last_row))
643
+ if cached:
644
+ return cached
645
+ top, right, bottom, left = padding
646
+
647
+ if collapse_padding:
648
+ if not first_column:
649
+ left = max(0, left - right)
650
+ if not last_row:
651
+ bottom = max(0, top - bottom)
652
+
653
+ if not pad_edge:
654
+ if first_column:
655
+ left = 0
656
+ if last_column:
657
+ right = 0
658
+ if first_row:
659
+ top = 0
660
+ if last_row:
661
+ bottom = 0
662
+ _padding = (top, right, bottom, left)
663
+ _padding_cache[(first_row, last_row)] = _padding
664
+ return _padding
665
+
666
+ raw_cells: List[Tuple[StyleType, "RenderableType"]] = []
667
+ _append = raw_cells.append
668
+ get_style = console.get_style
669
+ if self.show_header:
670
+ header_style = get_style(self.header_style or "") + get_style(
671
+ column.header_style
672
+ )
673
+ _append((header_style, column.header))
674
+ cell_style = get_style(column.style or "")
675
+ for cell in column.cells:
676
+ _append((cell_style, cell))
677
+ if self.show_footer:
678
+ footer_style = get_style(self.footer_style or "") + get_style(
679
+ column.footer_style
680
+ )
681
+ _append((footer_style, column.footer))
682
+
683
+ if any_padding:
684
+ _Padding = Padding
685
+ for first, last, (style, renderable) in loop_first_last(raw_cells):
686
+ yield _Cell(
687
+ style,
688
+ _Padding(renderable, get_padding(first, last)),
689
+ getattr(renderable, "vertical", None) or column.vertical,
690
+ )
691
+ else:
692
+ for style, renderable in raw_cells:
693
+ yield _Cell(
694
+ style,
695
+ renderable,
696
+ getattr(renderable, "vertical", None) or column.vertical,
697
+ )
698
+
699
+ def _get_padding_width(self, column_index: int) -> int:
700
+ """Get extra width from padding."""
701
+ _, pad_right, _, pad_left = self.padding
702
+ if self.collapse_padding:
703
+ if column_index > 0:
704
+ pad_left = max(0, pad_left - pad_right)
705
+ return pad_left + pad_right
706
+
707
+ def _measure_column(
708
+ self,
709
+ console: "Console",
710
+ options: "ConsoleOptions",
711
+ column: Column,
712
+ ) -> Measurement:
713
+ """Get the minimum and maximum width of the column."""
714
+
715
+ max_width = options.max_width
716
+ if max_width < 1:
717
+ return Measurement(0, 0)
718
+
719
+ padding_width = self._get_padding_width(column._index)
720
+
721
+ if column.width is not None:
722
+ # Fixed width column
723
+ return Measurement(
724
+ column.width + padding_width, column.width + padding_width
725
+ ).with_maximum(max_width)
726
+ # Flexible column, we need to measure contents
727
+ min_widths: List[int] = []
728
+ max_widths: List[int] = []
729
+ append_min = min_widths.append
730
+ append_max = max_widths.append
731
+ get_render_width = Measurement.get
732
+ for cell in self._get_cells(console, column._index, column):
733
+ _min, _max = get_render_width(console, options, cell.renderable)
734
+ append_min(_min)
735
+ append_max(_max)
736
+
737
+ measurement = Measurement(
738
+ max(min_widths) if min_widths else 1,
739
+ max(max_widths) if max_widths else max_width,
740
+ ).with_maximum(max_width)
741
+ measurement = measurement.clamp(
742
+ None if column.min_width is None else column.min_width + padding_width,
743
+ None if column.max_width is None else column.max_width + padding_width,
744
+ )
745
+ return measurement
746
+
747
+ def _render(
748
+ self, console: "Console", options: "ConsoleOptions", widths: List[int]
749
+ ) -> "RenderResult":
750
+ table_style = console.get_style(self.style or "")
751
+
752
+ border_style = table_style + console.get_style(self.border_style or "")
753
+ _column_cells = (
754
+ self._get_cells(console, column_index, column)
755
+ for column_index, column in enumerate(self.columns)
756
+ )
757
+ row_cells: List[Tuple[_Cell, ...]] = list(zip(*_column_cells))
758
+ _box = (
759
+ self.box.substitute(
760
+ options, safe=pick_bool(self.safe_box, console.safe_box)
761
+ )
762
+ if self.box
763
+ else None
764
+ )
765
+ _box = _box.get_plain_headed_box() if _box and not self.show_header else _box
766
+
767
+ new_line = Segment.line()
768
+
769
+ columns = self.columns
770
+ show_header = self.show_header
771
+ show_footer = self.show_footer
772
+ show_edge = self.show_edge
773
+ show_lines = self.show_lines
774
+ leading = self.leading
775
+
776
+ _Segment = Segment
777
+ if _box:
778
+ box_segments = [
779
+ (
780
+ _Segment(_box.head_left, border_style),
781
+ _Segment(_box.head_right, border_style),
782
+ _Segment(_box.head_vertical, border_style),
783
+ ),
784
+ (
785
+ _Segment(_box.mid_left, border_style),
786
+ _Segment(_box.mid_right, border_style),
787
+ _Segment(_box.mid_vertical, border_style),
788
+ ),
789
+ (
790
+ _Segment(_box.foot_left, border_style),
791
+ _Segment(_box.foot_right, border_style),
792
+ _Segment(_box.foot_vertical, border_style),
793
+ ),
794
+ ]
795
+ if show_edge:
796
+ yield _Segment(_box.get_top(widths), border_style)
797
+ yield new_line
798
+ else:
799
+ box_segments = []
800
+
801
+ get_row_style = self.get_row_style
802
+ get_style = console.get_style
803
+
804
+ for index, (first, last, row_cell) in enumerate(loop_first_last(row_cells)):
805
+ header_row = first and show_header
806
+ footer_row = last and show_footer
807
+ row = (
808
+ self.rows[index - show_header]
809
+ if (not header_row and not footer_row)
810
+ else None
811
+ )
812
+ max_height = 1
813
+ cells: List[List[List[Segment]]] = []
814
+ if header_row or footer_row:
815
+ row_style = Style.null()
816
+ else:
817
+ row_style = get_style(
818
+ get_row_style(console, index - 1 if show_header else index)
819
+ )
820
+ for width, cell, column in zip(widths, row_cell, columns):
821
+ render_options = options.update(
822
+ width=width,
823
+ justify=column.justify,
824
+ no_wrap=column.no_wrap,
825
+ overflow=column.overflow,
826
+ height=None,
827
+ highlight=column.highlight,
828
+ )
829
+ lines = console.render_lines(
830
+ cell.renderable,
831
+ render_options,
832
+ style=get_style(cell.style) + row_style,
833
+ )
834
+ max_height = max(max_height, len(lines))
835
+ cells.append(lines)
836
+
837
+ row_height = max(len(cell) for cell in cells)
838
+
839
+ def align_cell(
840
+ cell: List[List[Segment]],
841
+ vertical: "VerticalAlignMethod",
842
+ width: int,
843
+ style: Style,
844
+ ) -> List[List[Segment]]:
845
+ if header_row:
846
+ vertical = "bottom"
847
+ elif footer_row:
848
+ vertical = "top"
849
+
850
+ if vertical == "top":
851
+ return _Segment.align_top(cell, width, row_height, style)
852
+ elif vertical == "middle":
853
+ return _Segment.align_middle(cell, width, row_height, style)
854
+ return _Segment.align_bottom(cell, width, row_height, style)
855
+
856
+ cells[:] = [
857
+ _Segment.set_shape(
858
+ align_cell(
859
+ cell,
860
+ _cell.vertical,
861
+ width,
862
+ get_style(_cell.style) + row_style,
863
+ ),
864
+ width,
865
+ max_height,
866
+ )
867
+ for width, _cell, cell, column in zip(widths, row_cell, cells, columns)
868
+ ]
869
+
870
+ if _box:
871
+ if last and show_footer:
872
+ yield _Segment(
873
+ _box.get_row(widths, "foot", edge=show_edge), border_style
874
+ )
875
+ yield new_line
876
+ left, right, _divider = box_segments[0 if first else (2 if last else 1)]
877
+
878
+ # If the column divider is whitespace also style it with the row background
879
+ divider = (
880
+ _divider
881
+ if _divider.text.strip()
882
+ else _Segment(
883
+ _divider.text, row_style.background_style + _divider.style
884
+ )
885
+ )
886
+ for line_no in range(max_height):
887
+ if show_edge:
888
+ yield left
889
+ for last_cell, rendered_cell in loop_last(cells):
890
+ yield from rendered_cell[line_no]
891
+ if not last_cell:
892
+ yield divider
893
+ if show_edge:
894
+ yield right
895
+ yield new_line
896
+ else:
897
+ for line_no in range(max_height):
898
+ for rendered_cell in cells:
899
+ yield from rendered_cell[line_no]
900
+ yield new_line
901
+ if _box and first and show_header:
902
+ yield _Segment(
903
+ _box.get_row(widths, "head", edge=show_edge), border_style
904
+ )
905
+ yield new_line
906
+ end_section = row and row.end_section
907
+ if _box and (show_lines or leading or end_section):
908
+ if (
909
+ not last
910
+ and not (show_footer and index >= len(row_cells) - 2)
911
+ and not (show_header and header_row)
912
+ ):
913
+ if leading:
914
+ yield _Segment(
915
+ _box.get_row(widths, "mid", edge=show_edge) * leading,
916
+ border_style,
917
+ )
918
+ else:
919
+ yield _Segment(
920
+ _box.get_row(widths, "row", edge=show_edge), border_style
921
+ )
922
+ yield new_line
923
+
924
+ if _box and show_edge:
925
+ yield _Segment(_box.get_bottom(widths), border_style)
926
+ yield new_line
927
+
928
+
929
+ if __name__ == "__main__": # pragma: no cover
930
+ from pip._vendor.rich.console import Console
931
+ from pip._vendor.rich.highlighter import ReprHighlighter
932
+ from pip._vendor.rich.table import Table as Table
933
+
934
+ from ._timer import timer
935
+
936
+ with timer("Table render"):
937
+ table = Table(
938
+ title="Star Wars Movies",
939
+ caption="Rich example table",
940
+ caption_justify="right",
941
+ )
942
+
943
+ table.add_column(
944
+ "Released", header_style="bright_cyan", style="cyan", no_wrap=True
945
+ )
946
+ table.add_column("Title", style="magenta")
947
+ table.add_column("Box Office", justify="right", style="green")
948
+
949
+ table.add_row(
950
+ "Dec 20, 2019",
951
+ "Star Wars: The Rise of Skywalker",
952
+ "$952,110,690",
953
+ )
954
+ table.add_row("May 25, 2018", "Solo: A Star Wars Story", "$393,151,347")
955
+ table.add_row(
956
+ "Dec 15, 2017",
957
+ "Star Wars Ep. V111: The Last Jedi",
958
+ "$1,332,539,889",
959
+ style="on black",
960
+ end_section=True,
961
+ )
962
+ table.add_row(
963
+ "Dec 16, 2016",
964
+ "Rogue One: A Star Wars Story",
965
+ "$1,332,439,889",
966
+ )
967
+
968
+ def header(text: str) -> None:
969
+ console.print()
970
+ console.rule(highlight(text))
971
+ console.print()
972
+
973
+ console = Console()
974
+ highlight = ReprHighlighter()
975
+ header("Example Table")
976
+ console.print(table, justify="center")
977
+
978
+ table.expand = True
979
+ header("expand=True")
980
+ console.print(table)
981
+
982
+ table.width = 50
983
+ header("width=50")
984
+
985
+ console.print(table, justify="center")
986
+
987
+ table.width = None
988
+ table.expand = False
989
+ table.row_styles = ["dim", "none"]
990
+ header("row_styles=['dim', 'none']")
991
+
992
+ console.print(table, justify="center")
993
+
994
+ table.width = None
995
+ table.expand = False
996
+ table.row_styles = ["dim", "none"]
997
+ table.leading = 1
998
+ header("leading=1, row_styles=['dim', 'none']")
999
+ console.print(table, justify="center")
1000
+
1001
+ table.width = None
1002
+ table.expand = False
1003
+ table.row_styles = ["dim", "none"]
1004
+ table.show_lines = True
1005
+ table.leading = 0
1006
+ header("show_lines=True, row_styles=['dim', 'none']")
1007
+ console.print(table, justify="center")
lib/python3.12/site-packages/pip/_vendor/urllib3/__init__.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more
3
+ """
4
+ from __future__ import absolute_import
5
+
6
+ # Set default logging handler to avoid "No handler found" warnings.
7
+ import logging
8
+ import warnings
9
+ from logging import NullHandler
10
+
11
+ from . import exceptions
12
+ from ._version import __version__
13
+ from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url
14
+ from .filepost import encode_multipart_formdata
15
+ from .poolmanager import PoolManager, ProxyManager, proxy_from_url
16
+ from .response import HTTPResponse
17
+ from .util.request import make_headers
18
+ from .util.retry import Retry
19
+ from .util.timeout import Timeout
20
+ from .util.url import get_host
21
+
22
+ # === NOTE TO REPACKAGERS AND VENDORS ===
23
+ # Please delete this block, this logic is only
24
+ # for urllib3 being distributed via PyPI.
25
+ # See: https://github.com/urllib3/urllib3/issues/2680
26
+ try:
27
+ import urllib3_secure_extra # type: ignore # noqa: F401
28
+ except ImportError:
29
+ pass
30
+ else:
31
+ warnings.warn(
32
+ "'urllib3[secure]' extra is deprecated and will be removed "
33
+ "in a future release of urllib3 2.x. Read more in this issue: "
34
+ "https://github.com/urllib3/urllib3/issues/2680",
35
+ category=DeprecationWarning,
36
+ stacklevel=2,
37
+ )
38
+
39
+ __author__ = "Andrey Petrov (andrey.petrov@shazow.net)"
40
+ __license__ = "MIT"
41
+ __version__ = __version__
42
+
43
+ __all__ = (
44
+ "HTTPConnectionPool",
45
+ "HTTPSConnectionPool",
46
+ "PoolManager",
47
+ "ProxyManager",
48
+ "HTTPResponse",
49
+ "Retry",
50
+ "Timeout",
51
+ "add_stderr_logger",
52
+ "connection_from_url",
53
+ "disable_warnings",
54
+ "encode_multipart_formdata",
55
+ "get_host",
56
+ "make_headers",
57
+ "proxy_from_url",
58
+ )
59
+
60
+ logging.getLogger(__name__).addHandler(NullHandler())
61
+
62
+
63
+ def add_stderr_logger(level=logging.DEBUG):
64
+ """
65
+ Helper for quickly adding a StreamHandler to the logger. Useful for
66
+ debugging.
67
+
68
+ Returns the handler after adding it.
69
+ """
70
+ # This method needs to be in this __init__.py to get the __name__ correct
71
+ # even if urllib3 is vendored within another package.
72
+ logger = logging.getLogger(__name__)
73
+ handler = logging.StreamHandler()
74
+ handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
75
+ logger.addHandler(handler)
76
+ logger.setLevel(level)
77
+ logger.debug("Added a stderr logging handler to logger: %s", __name__)
78
+ return handler
79
+
80
+
81
+ # ... Clean up.
82
+ del NullHandler
83
+
84
+
85
+ # All warning filters *must* be appended unless you're really certain that they
86
+ # shouldn't be: otherwise, it's very hard for users to use most Python
87
+ # mechanisms to silence them.
88
+ # SecurityWarning's always go off by default.
89
+ warnings.simplefilter("always", exceptions.SecurityWarning, append=True)
90
+ # SubjectAltNameWarning's should go off once per host
91
+ warnings.simplefilter("default", exceptions.SubjectAltNameWarning, append=True)
92
+ # InsecurePlatformWarning's don't vary between requests, so we keep it default.
93
+ warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True)
94
+ # SNIMissingWarnings should go off only once.
95
+ warnings.simplefilter("default", exceptions.SNIMissingWarning, append=True)
96
+
97
+
98
+ def disable_warnings(category=exceptions.HTTPWarning):
99
+ """
100
+ Helper for quickly disabling all urllib3 warnings.
101
+ """
102
+ warnings.simplefilter("ignore", category)
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (3.41 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc ADDED
Binary file (16.4 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc ADDED
Binary file (226 Bytes). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc ADDED
Binary file (20.4 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc ADDED
Binary file (36.5 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc ADDED
Binary file (13.5 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc ADDED
Binary file (10.4 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc ADDED
Binary file (4.02 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc ADDED
Binary file (20.4 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc ADDED
Binary file (7.3 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc ADDED
Binary file (34 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/_collections.py ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import absolute_import
2
+
3
+ try:
4
+ from collections.abc import Mapping, MutableMapping
5
+ except ImportError:
6
+ from collections import Mapping, MutableMapping
7
+ try:
8
+ from threading import RLock
9
+ except ImportError: # Platform-specific: No threads available
10
+
11
+ class RLock:
12
+ def __enter__(self):
13
+ pass
14
+
15
+ def __exit__(self, exc_type, exc_value, traceback):
16
+ pass
17
+
18
+
19
+ from collections import OrderedDict
20
+
21
+ from .exceptions import InvalidHeader
22
+ from .packages import six
23
+ from .packages.six import iterkeys, itervalues
24
+
25
+ __all__ = ["RecentlyUsedContainer", "HTTPHeaderDict"]
26
+
27
+
28
+ _Null = object()
29
+
30
+
31
+ class RecentlyUsedContainer(MutableMapping):
32
+ """
33
+ Provides a thread-safe dict-like container which maintains up to
34
+ ``maxsize`` keys while throwing away the least-recently-used keys beyond
35
+ ``maxsize``.
36
+
37
+ :param maxsize:
38
+ Maximum number of recent elements to retain.
39
+
40
+ :param dispose_func:
41
+ Every time an item is evicted from the container,
42
+ ``dispose_func(value)`` is called. Callback which will get called
43
+ """
44
+
45
+ ContainerCls = OrderedDict
46
+
47
+ def __init__(self, maxsize=10, dispose_func=None):
48
+ self._maxsize = maxsize
49
+ self.dispose_func = dispose_func
50
+
51
+ self._container = self.ContainerCls()
52
+ self.lock = RLock()
53
+
54
+ def __getitem__(self, key):
55
+ # Re-insert the item, moving it to the end of the eviction line.
56
+ with self.lock:
57
+ item = self._container.pop(key)
58
+ self._container[key] = item
59
+ return item
60
+
61
+ def __setitem__(self, key, value):
62
+ evicted_value = _Null
63
+ with self.lock:
64
+ # Possibly evict the existing value of 'key'
65
+ evicted_value = self._container.get(key, _Null)
66
+ self._container[key] = value
67
+
68
+ # If we didn't evict an existing value, we might have to evict the
69
+ # least recently used item from the beginning of the container.
70
+ if len(self._container) > self._maxsize:
71
+ _key, evicted_value = self._container.popitem(last=False)
72
+
73
+ if self.dispose_func and evicted_value is not _Null:
74
+ self.dispose_func(evicted_value)
75
+
76
+ def __delitem__(self, key):
77
+ with self.lock:
78
+ value = self._container.pop(key)
79
+
80
+ if self.dispose_func:
81
+ self.dispose_func(value)
82
+
83
+ def __len__(self):
84
+ with self.lock:
85
+ return len(self._container)
86
+
87
+ def __iter__(self):
88
+ raise NotImplementedError(
89
+ "Iteration over this class is unlikely to be threadsafe."
90
+ )
91
+
92
+ def clear(self):
93
+ with self.lock:
94
+ # Copy pointers to all values, then wipe the mapping
95
+ values = list(itervalues(self._container))
96
+ self._container.clear()
97
+
98
+ if self.dispose_func:
99
+ for value in values:
100
+ self.dispose_func(value)
101
+
102
+ def keys(self):
103
+ with self.lock:
104
+ return list(iterkeys(self._container))
105
+
106
+
107
+ class HTTPHeaderDict(MutableMapping):
108
+ """
109
+ :param headers:
110
+ An iterable of field-value pairs. Must not contain multiple field names
111
+ when compared case-insensitively.
112
+
113
+ :param kwargs:
114
+ Additional field-value pairs to pass in to ``dict.update``.
115
+
116
+ A ``dict`` like container for storing HTTP Headers.
117
+
118
+ Field names are stored and compared case-insensitively in compliance with
119
+ RFC 7230. Iteration provides the first case-sensitive key seen for each
120
+ case-insensitive pair.
121
+
122
+ Using ``__setitem__`` syntax overwrites fields that compare equal
123
+ case-insensitively in order to maintain ``dict``'s api. For fields that
124
+ compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add``
125
+ in a loop.
126
+
127
+ If multiple fields that are equal case-insensitively are passed to the
128
+ constructor or ``.update``, the behavior is undefined and some will be
129
+ lost.
130
+
131
+ >>> headers = HTTPHeaderDict()
132
+ >>> headers.add('Set-Cookie', 'foo=bar')
133
+ >>> headers.add('set-cookie', 'baz=quxx')
134
+ >>> headers['content-length'] = '7'
135
+ >>> headers['SET-cookie']
136
+ 'foo=bar, baz=quxx'
137
+ >>> headers['Content-Length']
138
+ '7'
139
+ """
140
+
141
+ def __init__(self, headers=None, **kwargs):
142
+ super(HTTPHeaderDict, self).__init__()
143
+ self._container = OrderedDict()
144
+ if headers is not None:
145
+ if isinstance(headers, HTTPHeaderDict):
146
+ self._copy_from(headers)
147
+ else:
148
+ self.extend(headers)
149
+ if kwargs:
150
+ self.extend(kwargs)
151
+
152
+ def __setitem__(self, key, val):
153
+ self._container[key.lower()] = [key, val]
154
+ return self._container[key.lower()]
155
+
156
+ def __getitem__(self, key):
157
+ val = self._container[key.lower()]
158
+ return ", ".join(val[1:])
159
+
160
+ def __delitem__(self, key):
161
+ del self._container[key.lower()]
162
+
163
+ def __contains__(self, key):
164
+ return key.lower() in self._container
165
+
166
+ def __eq__(self, other):
167
+ if not isinstance(other, Mapping) and not hasattr(other, "keys"):
168
+ return False
169
+ if not isinstance(other, type(self)):
170
+ other = type(self)(other)
171
+ return dict((k.lower(), v) for k, v in self.itermerged()) == dict(
172
+ (k.lower(), v) for k, v in other.itermerged()
173
+ )
174
+
175
+ def __ne__(self, other):
176
+ return not self.__eq__(other)
177
+
178
+ if six.PY2: # Python 2
179
+ iterkeys = MutableMapping.iterkeys
180
+ itervalues = MutableMapping.itervalues
181
+
182
+ __marker = object()
183
+
184
+ def __len__(self):
185
+ return len(self._container)
186
+
187
+ def __iter__(self):
188
+ # Only provide the originally cased names
189
+ for vals in self._container.values():
190
+ yield vals[0]
191
+
192
+ def pop(self, key, default=__marker):
193
+ """D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
194
+ If key is not found, d is returned if given, otherwise KeyError is raised.
195
+ """
196
+ # Using the MutableMapping function directly fails due to the private marker.
197
+ # Using ordinary dict.pop would expose the internal structures.
198
+ # So let's reinvent the wheel.
199
+ try:
200
+ value = self[key]
201
+ except KeyError:
202
+ if default is self.__marker:
203
+ raise
204
+ return default
205
+ else:
206
+ del self[key]
207
+ return value
208
+
209
+ def discard(self, key):
210
+ try:
211
+ del self[key]
212
+ except KeyError:
213
+ pass
214
+
215
+ def add(self, key, val):
216
+ """Adds a (name, value) pair, doesn't overwrite the value if it already
217
+ exists.
218
+
219
+ >>> headers = HTTPHeaderDict(foo='bar')
220
+ >>> headers.add('Foo', 'baz')
221
+ >>> headers['foo']
222
+ 'bar, baz'
223
+ """
224
+ key_lower = key.lower()
225
+ new_vals = [key, val]
226
+ # Keep the common case aka no item present as fast as possible
227
+ vals = self._container.setdefault(key_lower, new_vals)
228
+ if new_vals is not vals:
229
+ vals.append(val)
230
+
231
+ def extend(self, *args, **kwargs):
232
+ """Generic import function for any type of header-like object.
233
+ Adapted version of MutableMapping.update in order to insert items
234
+ with self.add instead of self.__setitem__
235
+ """
236
+ if len(args) > 1:
237
+ raise TypeError(
238
+ "extend() takes at most 1 positional "
239
+ "arguments ({0} given)".format(len(args))
240
+ )
241
+ other = args[0] if len(args) >= 1 else ()
242
+
243
+ if isinstance(other, HTTPHeaderDict):
244
+ for key, val in other.iteritems():
245
+ self.add(key, val)
246
+ elif isinstance(other, Mapping):
247
+ for key in other:
248
+ self.add(key, other[key])
249
+ elif hasattr(other, "keys"):
250
+ for key in other.keys():
251
+ self.add(key, other[key])
252
+ else:
253
+ for key, value in other:
254
+ self.add(key, value)
255
+
256
+ for key, value in kwargs.items():
257
+ self.add(key, value)
258
+
259
+ def getlist(self, key, default=__marker):
260
+ """Returns a list of all the values for the named field. Returns an
261
+ empty list if the key doesn't exist."""
262
+ try:
263
+ vals = self._container[key.lower()]
264
+ except KeyError:
265
+ if default is self.__marker:
266
+ return []
267
+ return default
268
+ else:
269
+ return vals[1:]
270
+
271
+ def _prepare_for_method_change(self):
272
+ """
273
+ Remove content-specific header fields before changing the request
274
+ method to GET or HEAD according to RFC 9110, Section 15.4.
275
+ """
276
+ content_specific_headers = [
277
+ "Content-Encoding",
278
+ "Content-Language",
279
+ "Content-Location",
280
+ "Content-Type",
281
+ "Content-Length",
282
+ "Digest",
283
+ "Last-Modified",
284
+ ]
285
+ for header in content_specific_headers:
286
+ self.discard(header)
287
+ return self
288
+
289
+ # Backwards compatibility for httplib
290
+ getheaders = getlist
291
+ getallmatchingheaders = getlist
292
+ iget = getlist
293
+
294
+ # Backwards compatibility for http.cookiejar
295
+ get_all = getlist
296
+
297
+ def __repr__(self):
298
+ return "%s(%s)" % (type(self).__name__, dict(self.itermerged()))
299
+
300
+ def _copy_from(self, other):
301
+ for key in other:
302
+ val = other.getlist(key)
303
+ if isinstance(val, list):
304
+ # Don't need to convert tuples
305
+ val = list(val)
306
+ self._container[key.lower()] = [key] + val
307
+
308
+ def copy(self):
309
+ clone = type(self)()
310
+ clone._copy_from(self)
311
+ return clone
312
+
313
+ def iteritems(self):
314
+ """Iterate over all header lines, including duplicate ones."""
315
+ for key in self:
316
+ vals = self._container[key.lower()]
317
+ for val in vals[1:]:
318
+ yield vals[0], val
319
+
320
+ def itermerged(self):
321
+ """Iterate over all headers, merging duplicate ones together."""
322
+ for key in self:
323
+ val = self._container[key.lower()]
324
+ yield val[0], ", ".join(val[1:])
325
+
326
+ def items(self):
327
+ return list(self.iteritems())
328
+
329
+ @classmethod
330
+ def from_httplib(cls, message): # Python 2
331
+ """Read headers from a Python 2 httplib message object."""
332
+ # python2.7 does not expose a proper API for exporting multiheaders
333
+ # efficiently. This function re-reads raw lines from the message
334
+ # object and extracts the multiheaders properly.
335
+ obs_fold_continued_leaders = (" ", "\t")
336
+ headers = []
337
+
338
+ for line in message.headers:
339
+ if line.startswith(obs_fold_continued_leaders):
340
+ if not headers:
341
+ # We received a header line that starts with OWS as described
342
+ # in RFC-7230 S3.2.4. This indicates a multiline header, but
343
+ # there exists no previous header to which we can attach it.
344
+ raise InvalidHeader(
345
+ "Header continuation with no previous header: %s" % line
346
+ )
347
+ else:
348
+ key, value = headers[-1]
349
+ headers[-1] = (key, value + " " + line.strip())
350
+ continue
351
+
352
+ key, value = line.split(":", 1)
353
+ headers.append((key, value.strip()))
354
+
355
+ return cls(headers)
lib/python3.12/site-packages/pip/_vendor/urllib3/_version.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # This file is protected via CODEOWNERS
2
+ __version__ = "1.26.20"
lib/python3.12/site-packages/pip/_vendor/urllib3/connection.py ADDED
@@ -0,0 +1,572 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import absolute_import
2
+
3
+ import datetime
4
+ import logging
5
+ import os
6
+ import re
7
+ import socket
8
+ import warnings
9
+ from socket import error as SocketError
10
+ from socket import timeout as SocketTimeout
11
+
12
+ from .packages import six
13
+ from .packages.six.moves.http_client import HTTPConnection as _HTTPConnection
14
+ from .packages.six.moves.http_client import HTTPException # noqa: F401
15
+ from .util.proxy import create_proxy_ssl_context
16
+
17
+ try: # Compiled with SSL?
18
+ import ssl
19
+
20
+ BaseSSLError = ssl.SSLError
21
+ except (ImportError, AttributeError): # Platform-specific: No SSL.
22
+ ssl = None
23
+
24
+ class BaseSSLError(BaseException):
25
+ pass
26
+
27
+
28
+ try:
29
+ # Python 3: not a no-op, we're adding this to the namespace so it can be imported.
30
+ ConnectionError = ConnectionError
31
+ except NameError:
32
+ # Python 2
33
+ class ConnectionError(Exception):
34
+ pass
35
+
36
+
37
+ try: # Python 3:
38
+ # Not a no-op, we're adding this to the namespace so it can be imported.
39
+ BrokenPipeError = BrokenPipeError
40
+ except NameError: # Python 2:
41
+
42
+ class BrokenPipeError(Exception):
43
+ pass
44
+
45
+
46
+ from ._collections import HTTPHeaderDict # noqa (historical, removed in v2)
47
+ from ._version import __version__
48
+ from .exceptions import (
49
+ ConnectTimeoutError,
50
+ NewConnectionError,
51
+ SubjectAltNameWarning,
52
+ SystemTimeWarning,
53
+ )
54
+ from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection
55
+ from .util.ssl_ import (
56
+ assert_fingerprint,
57
+ create_urllib3_context,
58
+ is_ipaddress,
59
+ resolve_cert_reqs,
60
+ resolve_ssl_version,
61
+ ssl_wrap_socket,
62
+ )
63
+ from .util.ssl_match_hostname import CertificateError, match_hostname
64
+
65
+ log = logging.getLogger(__name__)
66
+
67
+ port_by_scheme = {"http": 80, "https": 443}
68
+
69
+ # When it comes time to update this value as a part of regular maintenance
70
+ # (ie test_recent_date is failing) update it to ~6 months before the current date.
71
+ RECENT_DATE = datetime.date(2024, 1, 1)
72
+
73
+ _CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
74
+
75
+
76
+ class HTTPConnection(_HTTPConnection, object):
77
+ """
78
+ Based on :class:`http.client.HTTPConnection` but provides an extra constructor
79
+ backwards-compatibility layer between older and newer Pythons.
80
+
81
+ Additional keyword parameters are used to configure attributes of the connection.
82
+ Accepted parameters include:
83
+
84
+ - ``strict``: See the documentation on :class:`urllib3.connectionpool.HTTPConnectionPool`
85
+ - ``source_address``: Set the source address for the current connection.
86
+ - ``socket_options``: Set specific options on the underlying socket. If not specified, then
87
+ defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling
88
+ Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy.
89
+
90
+ For example, if you wish to enable TCP Keep Alive in addition to the defaults,
91
+ you might pass:
92
+
93
+ .. code-block:: python
94
+
95
+ HTTPConnection.default_socket_options + [
96
+ (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1),
97
+ ]
98
+
99
+ Or you may want to disable the defaults by passing an empty list (e.g., ``[]``).
100
+ """
101
+
102
+ default_port = port_by_scheme["http"]
103
+
104
+ #: Disable Nagle's algorithm by default.
105
+ #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]``
106
+ default_socket_options = [(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]
107
+
108
+ #: Whether this connection verifies the host's certificate.
109
+ is_verified = False
110
+
111
+ #: Whether this proxy connection (if used) verifies the proxy host's
112
+ #: certificate.
113
+ proxy_is_verified = None
114
+
115
+ def __init__(self, *args, **kw):
116
+ if not six.PY2:
117
+ kw.pop("strict", None)
118
+
119
+ # Pre-set source_address.
120
+ self.source_address = kw.get("source_address")
121
+
122
+ #: The socket options provided by the user. If no options are
123
+ #: provided, we use the default options.
124
+ self.socket_options = kw.pop("socket_options", self.default_socket_options)
125
+
126
+ # Proxy options provided by the user.
127
+ self.proxy = kw.pop("proxy", None)
128
+ self.proxy_config = kw.pop("proxy_config", None)
129
+
130
+ _HTTPConnection.__init__(self, *args, **kw)
131
+
132
+ @property
133
+ def host(self):
134
+ """
135
+ Getter method to remove any trailing dots that indicate the hostname is an FQDN.
136
+
137
+ In general, SSL certificates don't include the trailing dot indicating a
138
+ fully-qualified domain name, and thus, they don't validate properly when
139
+ checked against a domain name that includes the dot. In addition, some
140
+ servers may not expect to receive the trailing dot when provided.
141
+
142
+ However, the hostname with trailing dot is critical to DNS resolution; doing a
143
+ lookup with the trailing dot will properly only resolve the appropriate FQDN,
144
+ whereas a lookup without a trailing dot will search the system's search domain
145
+ list. Thus, it's important to keep the original host around for use only in
146
+ those cases where it's appropriate (i.e., when doing DNS lookup to establish the
147
+ actual TCP connection across which we're going to send HTTP requests).
148
+ """
149
+ return self._dns_host.rstrip(".")
150
+
151
+ @host.setter
152
+ def host(self, value):
153
+ """
154
+ Setter for the `host` property.
155
+
156
+ We assume that only urllib3 uses the _dns_host attribute; httplib itself
157
+ only uses `host`, and it seems reasonable that other libraries follow suit.
158
+ """
159
+ self._dns_host = value
160
+
161
+ def _new_conn(self):
162
+ """Establish a socket connection and set nodelay settings on it.
163
+
164
+ :return: New socket connection.
165
+ """
166
+ extra_kw = {}
167
+ if self.source_address:
168
+ extra_kw["source_address"] = self.source_address
169
+
170
+ if self.socket_options:
171
+ extra_kw["socket_options"] = self.socket_options
172
+
173
+ try:
174
+ conn = connection.create_connection(
175
+ (self._dns_host, self.port), self.timeout, **extra_kw
176
+ )
177
+
178
+ except SocketTimeout:
179
+ raise ConnectTimeoutError(
180
+ self,
181
+ "Connection to %s timed out. (connect timeout=%s)"
182
+ % (self.host, self.timeout),
183
+ )
184
+
185
+ except SocketError as e:
186
+ raise NewConnectionError(
187
+ self, "Failed to establish a new connection: %s" % e
188
+ )
189
+
190
+ return conn
191
+
192
+ def _is_using_tunnel(self):
193
+ # Google App Engine's httplib does not define _tunnel_host
194
+ return getattr(self, "_tunnel_host", None)
195
+
196
+ def _prepare_conn(self, conn):
197
+ self.sock = conn
198
+ if self._is_using_tunnel():
199
+ # TODO: Fix tunnel so it doesn't depend on self.sock state.
200
+ self._tunnel()
201
+ # Mark this connection as not reusable
202
+ self.auto_open = 0
203
+
204
+ def connect(self):
205
+ conn = self._new_conn()
206
+ self._prepare_conn(conn)
207
+
208
+ def putrequest(self, method, url, *args, **kwargs):
209
+ """ """
210
+ # Empty docstring because the indentation of CPython's implementation
211
+ # is broken but we don't want this method in our documentation.
212
+ match = _CONTAINS_CONTROL_CHAR_RE.search(method)
213
+ if match:
214
+ raise ValueError(
215
+ "Method cannot contain non-token characters %r (found at least %r)"
216
+ % (method, match.group())
217
+ )
218
+
219
+ return _HTTPConnection.putrequest(self, method, url, *args, **kwargs)
220
+
221
+ def putheader(self, header, *values):
222
+ """ """
223
+ if not any(isinstance(v, str) and v == SKIP_HEADER for v in values):
224
+ _HTTPConnection.putheader(self, header, *values)
225
+ elif six.ensure_str(header.lower()) not in SKIPPABLE_HEADERS:
226
+ raise ValueError(
227
+ "urllib3.util.SKIP_HEADER only supports '%s'"
228
+ % ("', '".join(map(str.title, sorted(SKIPPABLE_HEADERS))),)
229
+ )
230
+
231
+ def request(self, method, url, body=None, headers=None):
232
+ # Update the inner socket's timeout value to send the request.
233
+ # This only triggers if the connection is re-used.
234
+ if getattr(self, "sock", None) is not None:
235
+ self.sock.settimeout(self.timeout)
236
+
237
+ if headers is None:
238
+ headers = {}
239
+ else:
240
+ # Avoid modifying the headers passed into .request()
241
+ headers = headers.copy()
242
+ if "user-agent" not in (six.ensure_str(k.lower()) for k in headers):
243
+ headers["User-Agent"] = _get_default_user_agent()
244
+ super(HTTPConnection, self).request(method, url, body=body, headers=headers)
245
+
246
+ def request_chunked(self, method, url, body=None, headers=None):
247
+ """
248
+ Alternative to the common request method, which sends the
249
+ body with chunked encoding and not as one block
250
+ """
251
+ headers = headers or {}
252
+ header_keys = set([six.ensure_str(k.lower()) for k in headers])
253
+ skip_accept_encoding = "accept-encoding" in header_keys
254
+ skip_host = "host" in header_keys
255
+ self.putrequest(
256
+ method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host
257
+ )
258
+ if "user-agent" not in header_keys:
259
+ self.putheader("User-Agent", _get_default_user_agent())
260
+ for header, value in headers.items():
261
+ self.putheader(header, value)
262
+ if "transfer-encoding" not in header_keys:
263
+ self.putheader("Transfer-Encoding", "chunked")
264
+ self.endheaders()
265
+
266
+ if body is not None:
267
+ stringish_types = six.string_types + (bytes,)
268
+ if isinstance(body, stringish_types):
269
+ body = (body,)
270
+ for chunk in body:
271
+ if not chunk:
272
+ continue
273
+ if not isinstance(chunk, bytes):
274
+ chunk = chunk.encode("utf8")
275
+ len_str = hex(len(chunk))[2:]
276
+ to_send = bytearray(len_str.encode())
277
+ to_send += b"\r\n"
278
+ to_send += chunk
279
+ to_send += b"\r\n"
280
+ self.send(to_send)
281
+
282
+ # After the if clause, to always have a closed body
283
+ self.send(b"0\r\n\r\n")
284
+
285
+
286
+ class HTTPSConnection(HTTPConnection):
287
+ """
288
+ Many of the parameters to this constructor are passed to the underlying SSL
289
+ socket by means of :py:func:`urllib3.util.ssl_wrap_socket`.
290
+ """
291
+
292
+ default_port = port_by_scheme["https"]
293
+
294
+ cert_reqs = None
295
+ ca_certs = None
296
+ ca_cert_dir = None
297
+ ca_cert_data = None
298
+ ssl_version = None
299
+ assert_fingerprint = None
300
+ tls_in_tls_required = False
301
+
302
+ def __init__(
303
+ self,
304
+ host,
305
+ port=None,
306
+ key_file=None,
307
+ cert_file=None,
308
+ key_password=None,
309
+ strict=None,
310
+ timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
311
+ ssl_context=None,
312
+ server_hostname=None,
313
+ **kw
314
+ ):
315
+
316
+ HTTPConnection.__init__(self, host, port, strict=strict, timeout=timeout, **kw)
317
+
318
+ self.key_file = key_file
319
+ self.cert_file = cert_file
320
+ self.key_password = key_password
321
+ self.ssl_context = ssl_context
322
+ self.server_hostname = server_hostname
323
+
324
+ # Required property for Google AppEngine 1.9.0 which otherwise causes
325
+ # HTTPS requests to go out as HTTP. (See Issue #356)
326
+ self._protocol = "https"
327
+
328
+ def set_cert(
329
+ self,
330
+ key_file=None,
331
+ cert_file=None,
332
+ cert_reqs=None,
333
+ key_password=None,
334
+ ca_certs=None,
335
+ assert_hostname=None,
336
+ assert_fingerprint=None,
337
+ ca_cert_dir=None,
338
+ ca_cert_data=None,
339
+ ):
340
+ """
341
+ This method should only be called once, before the connection is used.
342
+ """
343
+ # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also
344
+ # have an SSLContext object in which case we'll use its verify_mode.
345
+ if cert_reqs is None:
346
+ if self.ssl_context is not None:
347
+ cert_reqs = self.ssl_context.verify_mode
348
+ else:
349
+ cert_reqs = resolve_cert_reqs(None)
350
+
351
+ self.key_file = key_file
352
+ self.cert_file = cert_file
353
+ self.cert_reqs = cert_reqs
354
+ self.key_password = key_password
355
+ self.assert_hostname = assert_hostname
356
+ self.assert_fingerprint = assert_fingerprint
357
+ self.ca_certs = ca_certs and os.path.expanduser(ca_certs)
358
+ self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir)
359
+ self.ca_cert_data = ca_cert_data
360
+
361
+ def connect(self):
362
+ # Add certificate verification
363
+ self.sock = conn = self._new_conn()
364
+ hostname = self.host
365
+ tls_in_tls = False
366
+
367
+ if self._is_using_tunnel():
368
+ if self.tls_in_tls_required:
369
+ self.sock = conn = self._connect_tls_proxy(hostname, conn)
370
+ tls_in_tls = True
371
+
372
+ # Calls self._set_hostport(), so self.host is
373
+ # self._tunnel_host below.
374
+ self._tunnel()
375
+ # Mark this connection as not reusable
376
+ self.auto_open = 0
377
+
378
+ # Override the host with the one we're requesting data from.
379
+ hostname = self._tunnel_host
380
+
381
+ server_hostname = hostname
382
+ if self.server_hostname is not None:
383
+ server_hostname = self.server_hostname
384
+
385
+ is_time_off = datetime.date.today() < RECENT_DATE
386
+ if is_time_off:
387
+ warnings.warn(
388
+ (
389
+ "System time is way off (before {0}). This will probably "
390
+ "lead to SSL verification errors"
391
+ ).format(RECENT_DATE),
392
+ SystemTimeWarning,
393
+ )
394
+
395
+ # Wrap socket using verification with the root certs in
396
+ # trusted_root_certs
397
+ default_ssl_context = False
398
+ if self.ssl_context is None:
399
+ default_ssl_context = True
400
+ self.ssl_context = create_urllib3_context(
401
+ ssl_version=resolve_ssl_version(self.ssl_version),
402
+ cert_reqs=resolve_cert_reqs(self.cert_reqs),
403
+ )
404
+
405
+ context = self.ssl_context
406
+ context.verify_mode = resolve_cert_reqs(self.cert_reqs)
407
+
408
+ # Try to load OS default certs if none are given.
409
+ # Works well on Windows (requires Python3.4+)
410
+ if (
411
+ not self.ca_certs
412
+ and not self.ca_cert_dir
413
+ and not self.ca_cert_data
414
+ and default_ssl_context
415
+ and hasattr(context, "load_default_certs")
416
+ ):
417
+ context.load_default_certs()
418
+
419
+ self.sock = ssl_wrap_socket(
420
+ sock=conn,
421
+ keyfile=self.key_file,
422
+ certfile=self.cert_file,
423
+ key_password=self.key_password,
424
+ ca_certs=self.ca_certs,
425
+ ca_cert_dir=self.ca_cert_dir,
426
+ ca_cert_data=self.ca_cert_data,
427
+ server_hostname=server_hostname,
428
+ ssl_context=context,
429
+ tls_in_tls=tls_in_tls,
430
+ )
431
+
432
+ # If we're using all defaults and the connection
433
+ # is TLSv1 or TLSv1.1 we throw a DeprecationWarning
434
+ # for the host.
435
+ if (
436
+ default_ssl_context
437
+ and self.ssl_version is None
438
+ and hasattr(self.sock, "version")
439
+ and self.sock.version() in {"TLSv1", "TLSv1.1"}
440
+ ): # Defensive:
441
+ warnings.warn(
442
+ "Negotiating TLSv1/TLSv1.1 by default is deprecated "
443
+ "and will be disabled in urllib3 v2.0.0. Connecting to "
444
+ "'%s' with '%s' can be enabled by explicitly opting-in "
445
+ "with 'ssl_version'" % (self.host, self.sock.version()),
446
+ DeprecationWarning,
447
+ )
448
+
449
+ if self.assert_fingerprint:
450
+ assert_fingerprint(
451
+ self.sock.getpeercert(binary_form=True), self.assert_fingerprint
452
+ )
453
+ elif (
454
+ context.verify_mode != ssl.CERT_NONE
455
+ and not getattr(context, "check_hostname", False)
456
+ and self.assert_hostname is not False
457
+ ):
458
+ # While urllib3 attempts to always turn off hostname matching from
459
+ # the TLS library, this cannot always be done. So we check whether
460
+ # the TLS Library still thinks it's matching hostnames.
461
+ cert = self.sock.getpeercert()
462
+ if not cert.get("subjectAltName", ()):
463
+ warnings.warn(
464
+ (
465
+ "Certificate for {0} has no `subjectAltName`, falling back to check for a "
466
+ "`commonName` for now. This feature is being removed by major browsers and "
467
+ "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 "
468
+ "for details.)".format(hostname)
469
+ ),
470
+ SubjectAltNameWarning,
471
+ )
472
+ _match_hostname(cert, self.assert_hostname or server_hostname)
473
+
474
+ self.is_verified = (
475
+ context.verify_mode == ssl.CERT_REQUIRED
476
+ or self.assert_fingerprint is not None
477
+ )
478
+
479
+ def _connect_tls_proxy(self, hostname, conn):
480
+ """
481
+ Establish a TLS connection to the proxy using the provided SSL context.
482
+ """
483
+ proxy_config = self.proxy_config
484
+ ssl_context = proxy_config.ssl_context
485
+ if ssl_context:
486
+ # If the user provided a proxy context, we assume CA and client
487
+ # certificates have already been set
488
+ return ssl_wrap_socket(
489
+ sock=conn,
490
+ server_hostname=hostname,
491
+ ssl_context=ssl_context,
492
+ )
493
+
494
+ ssl_context = create_proxy_ssl_context(
495
+ self.ssl_version,
496
+ self.cert_reqs,
497
+ self.ca_certs,
498
+ self.ca_cert_dir,
499
+ self.ca_cert_data,
500
+ )
501
+
502
+ # If no cert was provided, use only the default options for server
503
+ # certificate validation
504
+ socket = ssl_wrap_socket(
505
+ sock=conn,
506
+ ca_certs=self.ca_certs,
507
+ ca_cert_dir=self.ca_cert_dir,
508
+ ca_cert_data=self.ca_cert_data,
509
+ server_hostname=hostname,
510
+ ssl_context=ssl_context,
511
+ )
512
+
513
+ if ssl_context.verify_mode != ssl.CERT_NONE and not getattr(
514
+ ssl_context, "check_hostname", False
515
+ ):
516
+ # While urllib3 attempts to always turn off hostname matching from
517
+ # the TLS library, this cannot always be done. So we check whether
518
+ # the TLS Library still thinks it's matching hostnames.
519
+ cert = socket.getpeercert()
520
+ if not cert.get("subjectAltName", ()):
521
+ warnings.warn(
522
+ (
523
+ "Certificate for {0} has no `subjectAltName`, falling back to check for a "
524
+ "`commonName` for now. This feature is being removed by major browsers and "
525
+ "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 "
526
+ "for details.)".format(hostname)
527
+ ),
528
+ SubjectAltNameWarning,
529
+ )
530
+ _match_hostname(cert, hostname)
531
+
532
+ self.proxy_is_verified = ssl_context.verify_mode == ssl.CERT_REQUIRED
533
+ return socket
534
+
535
+
536
+ def _match_hostname(cert, asserted_hostname):
537
+ # Our upstream implementation of ssl.match_hostname()
538
+ # only applies this normalization to IP addresses so it doesn't
539
+ # match DNS SANs so we do the same thing!
540
+ stripped_hostname = asserted_hostname.strip("u[]")
541
+ if is_ipaddress(stripped_hostname):
542
+ asserted_hostname = stripped_hostname
543
+
544
+ try:
545
+ match_hostname(cert, asserted_hostname)
546
+ except CertificateError as e:
547
+ log.warning(
548
+ "Certificate did not match expected hostname: %s. Certificate: %s",
549
+ asserted_hostname,
550
+ cert,
551
+ )
552
+ # Add cert to exception and reraise so client code can inspect
553
+ # the cert when catching the exception, if they want to
554
+ e._peer_cert = cert
555
+ raise
556
+
557
+
558
+ def _get_default_user_agent():
559
+ return "python-urllib3/%s" % __version__
560
+
561
+
562
+ class DummyConnection(object):
563
+ """Used to detect a failed ConnectionCls import."""
564
+
565
+ pass
566
+
567
+
568
+ if not ssl:
569
+ HTTPSConnection = DummyConnection # noqa: F811
570
+
571
+
572
+ VerifiedHTTPSConnection = HTTPSConnection
lib/python3.12/site-packages/pip/_vendor/urllib3/connectionpool.py ADDED
@@ -0,0 +1,1140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import absolute_import
2
+
3
+ import errno
4
+ import logging
5
+ import re
6
+ import socket
7
+ import sys
8
+ import warnings
9
+ from socket import error as SocketError
10
+ from socket import timeout as SocketTimeout
11
+
12
+ from ._collections import HTTPHeaderDict
13
+ from .connection import (
14
+ BaseSSLError,
15
+ BrokenPipeError,
16
+ DummyConnection,
17
+ HTTPConnection,
18
+ HTTPException,
19
+ HTTPSConnection,
20
+ VerifiedHTTPSConnection,
21
+ port_by_scheme,
22
+ )
23
+ from .exceptions import (
24
+ ClosedPoolError,
25
+ EmptyPoolError,
26
+ HeaderParsingError,
27
+ HostChangedError,
28
+ InsecureRequestWarning,
29
+ LocationValueError,
30
+ MaxRetryError,
31
+ NewConnectionError,
32
+ ProtocolError,
33
+ ProxyError,
34
+ ReadTimeoutError,
35
+ SSLError,
36
+ TimeoutError,
37
+ )
38
+ from .packages import six
39
+ from .packages.six.moves import queue
40
+ from .request import RequestMethods
41
+ from .response import HTTPResponse
42
+ from .util.connection import is_connection_dropped
43
+ from .util.proxy import connection_requires_http_tunnel
44
+ from .util.queue import LifoQueue
45
+ from .util.request import set_file_position
46
+ from .util.response import assert_header_parsing
47
+ from .util.retry import Retry
48
+ from .util.ssl_match_hostname import CertificateError
49
+ from .util.timeout import Timeout
50
+ from .util.url import Url, _encode_target
51
+ from .util.url import _normalize_host as normalize_host
52
+ from .util.url import get_host, parse_url
53
+
54
+ try: # Platform-specific: Python 3
55
+ import weakref
56
+
57
+ weakref_finalize = weakref.finalize
58
+ except AttributeError: # Platform-specific: Python 2
59
+ from .packages.backports.weakref_finalize import weakref_finalize
60
+
61
+ xrange = six.moves.xrange
62
+
63
+ log = logging.getLogger(__name__)
64
+
65
+ _Default = object()
66
+
67
+
68
+ # Pool objects
69
+ class ConnectionPool(object):
70
+ """
71
+ Base class for all connection pools, such as
72
+ :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`.
73
+
74
+ .. note::
75
+ ConnectionPool.urlopen() does not normalize or percent-encode target URIs
76
+ which is useful if your target server doesn't support percent-encoded
77
+ target URIs.
78
+ """
79
+
80
+ scheme = None
81
+ QueueCls = LifoQueue
82
+
83
+ def __init__(self, host, port=None):
84
+ if not host:
85
+ raise LocationValueError("No host specified.")
86
+
87
+ self.host = _normalize_host(host, scheme=self.scheme)
88
+ self._proxy_host = host.lower()
89
+ self.port = port
90
+
91
+ def __str__(self):
92
+ return "%s(host=%r, port=%r)" % (type(self).__name__, self.host, self.port)
93
+
94
+ def __enter__(self):
95
+ return self
96
+
97
+ def __exit__(self, exc_type, exc_val, exc_tb):
98
+ self.close()
99
+ # Return False to re-raise any potential exceptions
100
+ return False
101
+
102
+ def close(self):
103
+ """
104
+ Close all pooled connections and disable the pool.
105
+ """
106
+ pass
107
+
108
+
109
+ # This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252
110
+ _blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK}
111
+
112
+
113
+ class HTTPConnectionPool(ConnectionPool, RequestMethods):
114
+ """
115
+ Thread-safe connection pool for one host.
116
+
117
+ :param host:
118
+ Host used for this HTTP Connection (e.g. "localhost"), passed into
119
+ :class:`http.client.HTTPConnection`.
120
+
121
+ :param port:
122
+ Port used for this HTTP Connection (None is equivalent to 80), passed
123
+ into :class:`http.client.HTTPConnection`.
124
+
125
+ :param strict:
126
+ Causes BadStatusLine to be raised if the status line can't be parsed
127
+ as a valid HTTP/1.0 or 1.1 status line, passed into
128
+ :class:`http.client.HTTPConnection`.
129
+
130
+ .. note::
131
+ Only works in Python 2. This parameter is ignored in Python 3.
132
+
133
+ :param timeout:
134
+ Socket timeout in seconds for each individual connection. This can
135
+ be a float or integer, which sets the timeout for the HTTP request,
136
+ or an instance of :class:`urllib3.util.Timeout` which gives you more
137
+ fine-grained control over request timeouts. After the constructor has
138
+ been parsed, this is always a `urllib3.util.Timeout` object.
139
+
140
+ :param maxsize:
141
+ Number of connections to save that can be reused. More than 1 is useful
142
+ in multithreaded situations. If ``block`` is set to False, more
143
+ connections will be created but they will not be saved once they've
144
+ been used.
145
+
146
+ :param block:
147
+ If set to True, no more than ``maxsize`` connections will be used at
148
+ a time. When no free connections are available, the call will block
149
+ until a connection has been released. This is a useful side effect for
150
+ particular multithreaded situations where one does not want to use more
151
+ than maxsize connections per host to prevent flooding.
152
+
153
+ :param headers:
154
+ Headers to include with all requests, unless other headers are given
155
+ explicitly.
156
+
157
+ :param retries:
158
+ Retry configuration to use by default with requests in this pool.
159
+
160
+ :param _proxy:
161
+ Parsed proxy URL, should not be used directly, instead, see
162
+ :class:`urllib3.ProxyManager`
163
+
164
+ :param _proxy_headers:
165
+ A dictionary with proxy headers, should not be used directly,
166
+ instead, see :class:`urllib3.ProxyManager`
167
+
168
+ :param \\**conn_kw:
169
+ Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`,
170
+ :class:`urllib3.connection.HTTPSConnection` instances.
171
+ """
172
+
173
+ scheme = "http"
174
+ ConnectionCls = HTTPConnection
175
+ ResponseCls = HTTPResponse
176
+
177
+ def __init__(
178
+ self,
179
+ host,
180
+ port=None,
181
+ strict=False,
182
+ timeout=Timeout.DEFAULT_TIMEOUT,
183
+ maxsize=1,
184
+ block=False,
185
+ headers=None,
186
+ retries=None,
187
+ _proxy=None,
188
+ _proxy_headers=None,
189
+ _proxy_config=None,
190
+ **conn_kw
191
+ ):
192
+ ConnectionPool.__init__(self, host, port)
193
+ RequestMethods.__init__(self, headers)
194
+
195
+ self.strict = strict
196
+
197
+ if not isinstance(timeout, Timeout):
198
+ timeout = Timeout.from_float(timeout)
199
+
200
+ if retries is None:
201
+ retries = Retry.DEFAULT
202
+
203
+ self.timeout = timeout
204
+ self.retries = retries
205
+
206
+ self.pool = self.QueueCls(maxsize)
207
+ self.block = block
208
+
209
+ self.proxy = _proxy
210
+ self.proxy_headers = _proxy_headers or {}
211
+ self.proxy_config = _proxy_config
212
+
213
+ # Fill the queue up so that doing get() on it will block properly
214
+ for _ in xrange(maxsize):
215
+ self.pool.put(None)
216
+
217
+ # These are mostly for testing and debugging purposes.
218
+ self.num_connections = 0
219
+ self.num_requests = 0
220
+ self.conn_kw = conn_kw
221
+
222
+ if self.proxy:
223
+ # Enable Nagle's algorithm for proxies, to avoid packet fragmentation.
224
+ # We cannot know if the user has added default socket options, so we cannot replace the
225
+ # list.
226
+ self.conn_kw.setdefault("socket_options", [])
227
+
228
+ self.conn_kw["proxy"] = self.proxy
229
+ self.conn_kw["proxy_config"] = self.proxy_config
230
+
231
+ # Do not pass 'self' as callback to 'finalize'.
232
+ # Then the 'finalize' would keep an endless living (leak) to self.
233
+ # By just passing a reference to the pool allows the garbage collector
234
+ # to free self if nobody else has a reference to it.
235
+ pool = self.pool
236
+
237
+ # Close all the HTTPConnections in the pool before the
238
+ # HTTPConnectionPool object is garbage collected.
239
+ weakref_finalize(self, _close_pool_connections, pool)
240
+
241
+ def _new_conn(self):
242
+ """
243
+ Return a fresh :class:`HTTPConnection`.
244
+ """
245
+ self.num_connections += 1
246
+ log.debug(
247
+ "Starting new HTTP connection (%d): %s:%s",
248
+ self.num_connections,
249
+ self.host,
250
+ self.port or "80",
251
+ )
252
+
253
+ conn = self.ConnectionCls(
254
+ host=self.host,
255
+ port=self.port,
256
+ timeout=self.timeout.connect_timeout,
257
+ strict=self.strict,
258
+ **self.conn_kw
259
+ )
260
+ return conn
261
+
262
+ def _get_conn(self, timeout=None):
263
+ """
264
+ Get a connection. Will return a pooled connection if one is available.
265
+
266
+ If no connections are available and :prop:`.block` is ``False``, then a
267
+ fresh connection is returned.
268
+
269
+ :param timeout:
270
+ Seconds to wait before giving up and raising
271
+ :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and
272
+ :prop:`.block` is ``True``.
273
+ """
274
+ conn = None
275
+ try:
276
+ conn = self.pool.get(block=self.block, timeout=timeout)
277
+
278
+ except AttributeError: # self.pool is None
279
+ raise ClosedPoolError(self, "Pool is closed.")
280
+
281
+ except queue.Empty:
282
+ if self.block:
283
+ raise EmptyPoolError(
284
+ self,
285
+ "Pool reached maximum size and no more connections are allowed.",
286
+ )
287
+ pass # Oh well, we'll create a new connection then
288
+
289
+ # If this is a persistent connection, check if it got disconnected
290
+ if conn and is_connection_dropped(conn):
291
+ log.debug("Resetting dropped connection: %s", self.host)
292
+ conn.close()
293
+ if getattr(conn, "auto_open", 1) == 0:
294
+ # This is a proxied connection that has been mutated by
295
+ # http.client._tunnel() and cannot be reused (since it would
296
+ # attempt to bypass the proxy)
297
+ conn = None
298
+
299
+ return conn or self._new_conn()
300
+
301
+ def _put_conn(self, conn):
302
+ """
303
+ Put a connection back into the pool.
304
+
305
+ :param conn:
306
+ Connection object for the current host and port as returned by
307
+ :meth:`._new_conn` or :meth:`._get_conn`.
308
+
309
+ If the pool is already full, the connection is closed and discarded
310
+ because we exceeded maxsize. If connections are discarded frequently,
311
+ then maxsize should be increased.
312
+
313
+ If the pool is closed, then the connection will be closed and discarded.
314
+ """
315
+ try:
316
+ self.pool.put(conn, block=False)
317
+ return # Everything is dandy, done.
318
+ except AttributeError:
319
+ # self.pool is None.
320
+ pass
321
+ except queue.Full:
322
+ # This should never happen if self.block == True
323
+ log.warning(
324
+ "Connection pool is full, discarding connection: %s. Connection pool size: %s",
325
+ self.host,
326
+ self.pool.qsize(),
327
+ )
328
+ # Connection never got put back into the pool, close it.
329
+ if conn:
330
+ conn.close()
331
+
332
+ def _validate_conn(self, conn):
333
+ """
334
+ Called right before a request is made, after the socket is created.
335
+ """
336
+ pass
337
+
338
+ def _prepare_proxy(self, conn):
339
+ # Nothing to do for HTTP connections.
340
+ pass
341
+
342
+ def _get_timeout(self, timeout):
343
+ """Helper that always returns a :class:`urllib3.util.Timeout`"""
344
+ if timeout is _Default:
345
+ return self.timeout.clone()
346
+
347
+ if isinstance(timeout, Timeout):
348
+ return timeout.clone()
349
+ else:
350
+ # User passed us an int/float. This is for backwards compatibility,
351
+ # can be removed later
352
+ return Timeout.from_float(timeout)
353
+
354
+ def _raise_timeout(self, err, url, timeout_value):
355
+ """Is the error actually a timeout? Will raise a ReadTimeout or pass"""
356
+
357
+ if isinstance(err, SocketTimeout):
358
+ raise ReadTimeoutError(
359
+ self, url, "Read timed out. (read timeout=%s)" % timeout_value
360
+ )
361
+
362
+ # See the above comment about EAGAIN in Python 3. In Python 2 we have
363
+ # to specifically catch it and throw the timeout error
364
+ if hasattr(err, "errno") and err.errno in _blocking_errnos:
365
+ raise ReadTimeoutError(
366
+ self, url, "Read timed out. (read timeout=%s)" % timeout_value
367
+ )
368
+
369
+ # Catch possible read timeouts thrown as SSL errors. If not the
370
+ # case, rethrow the original. We need to do this because of:
371
+ # http://bugs.python.org/issue10272
372
+ if "timed out" in str(err) or "did not complete (read)" in str(
373
+ err
374
+ ): # Python < 2.7.4
375
+ raise ReadTimeoutError(
376
+ self, url, "Read timed out. (read timeout=%s)" % timeout_value
377
+ )
378
+
379
+ def _make_request(
380
+ self, conn, method, url, timeout=_Default, chunked=False, **httplib_request_kw
381
+ ):
382
+ """
383
+ Perform a request on a given urllib connection object taken from our
384
+ pool.
385
+
386
+ :param conn:
387
+ a connection from one of our connection pools
388
+
389
+ :param timeout:
390
+ Socket timeout in seconds for the request. This can be a
391
+ float or integer, which will set the same timeout value for
392
+ the socket connect and the socket read, or an instance of
393
+ :class:`urllib3.util.Timeout`, which gives you more fine-grained
394
+ control over your timeouts.
395
+ """
396
+ self.num_requests += 1
397
+
398
+ timeout_obj = self._get_timeout(timeout)
399
+ timeout_obj.start_connect()
400
+ conn.timeout = Timeout.resolve_default_timeout(timeout_obj.connect_timeout)
401
+
402
+ # Trigger any extra validation we need to do.
403
+ try:
404
+ self._validate_conn(conn)
405
+ except (SocketTimeout, BaseSSLError) as e:
406
+ # Py2 raises this as a BaseSSLError, Py3 raises it as socket timeout.
407
+ self._raise_timeout(err=e, url=url, timeout_value=conn.timeout)
408
+ raise
409
+
410
+ # conn.request() calls http.client.*.request, not the method in
411
+ # urllib3.request. It also calls makefile (recv) on the socket.
412
+ try:
413
+ if chunked:
414
+ conn.request_chunked(method, url, **httplib_request_kw)
415
+ else:
416
+ conn.request(method, url, **httplib_request_kw)
417
+
418
+ # We are swallowing BrokenPipeError (errno.EPIPE) since the server is
419
+ # legitimately able to close the connection after sending a valid response.
420
+ # With this behaviour, the received response is still readable.
421
+ except BrokenPipeError:
422
+ # Python 3
423
+ pass
424
+ except IOError as e:
425
+ # Python 2 and macOS/Linux
426
+ # EPIPE and ESHUTDOWN are BrokenPipeError on Python 2, and EPROTOTYPE/ECONNRESET are needed on macOS
427
+ # https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/
428
+ if e.errno not in {
429
+ errno.EPIPE,
430
+ errno.ESHUTDOWN,
431
+ errno.EPROTOTYPE,
432
+ errno.ECONNRESET,
433
+ }:
434
+ raise
435
+
436
+ # Reset the timeout for the recv() on the socket
437
+ read_timeout = timeout_obj.read_timeout
438
+
439
+ # App Engine doesn't have a sock attr
440
+ if getattr(conn, "sock", None):
441
+ # In Python 3 socket.py will catch EAGAIN and return None when you
442
+ # try and read into the file pointer created by http.client, which
443
+ # instead raises a BadStatusLine exception. Instead of catching
444
+ # the exception and assuming all BadStatusLine exceptions are read
445
+ # timeouts, check for a zero timeout before making the request.
446
+ if read_timeout == 0:
447
+ raise ReadTimeoutError(
448
+ self, url, "Read timed out. (read timeout=%s)" % read_timeout
449
+ )
450
+ if read_timeout is Timeout.DEFAULT_TIMEOUT:
451
+ conn.sock.settimeout(socket.getdefaulttimeout())
452
+ else: # None or a value
453
+ conn.sock.settimeout(read_timeout)
454
+
455
+ # Receive the response from the server
456
+ try:
457
+ try:
458
+ # Python 2.7, use buffering of HTTP responses
459
+ httplib_response = conn.getresponse(buffering=True)
460
+ except TypeError:
461
+ # Python 3
462
+ try:
463
+ httplib_response = conn.getresponse()
464
+ except BaseException as e:
465
+ # Remove the TypeError from the exception chain in
466
+ # Python 3 (including for exceptions like SystemExit).
467
+ # Otherwise it looks like a bug in the code.
468
+ six.raise_from(e, None)
469
+ except (SocketTimeout, BaseSSLError, SocketError) as e:
470
+ self._raise_timeout(err=e, url=url, timeout_value=read_timeout)
471
+ raise
472
+
473
+ # AppEngine doesn't have a version attr.
474
+ http_version = getattr(conn, "_http_vsn_str", "HTTP/?")
475
+ log.debug(
476
+ '%s://%s:%s "%s %s %s" %s %s',
477
+ self.scheme,
478
+ self.host,
479
+ self.port,
480
+ method,
481
+ url,
482
+ http_version,
483
+ httplib_response.status,
484
+ httplib_response.length,
485
+ )
486
+
487
+ try:
488
+ assert_header_parsing(httplib_response.msg)
489
+ except (HeaderParsingError, TypeError) as hpe: # Platform-specific: Python 3
490
+ log.warning(
491
+ "Failed to parse headers (url=%s): %s",
492
+ self._absolute_url(url),
493
+ hpe,
494
+ exc_info=True,
495
+ )
496
+
497
+ return httplib_response
498
+
499
+ def _absolute_url(self, path):
500
+ return Url(scheme=self.scheme, host=self.host, port=self.port, path=path).url
501
+
502
+ def close(self):
503
+ """
504
+ Close all pooled connections and disable the pool.
505
+ """
506
+ if self.pool is None:
507
+ return
508
+ # Disable access to the pool
509
+ old_pool, self.pool = self.pool, None
510
+
511
+ # Close all the HTTPConnections in the pool.
512
+ _close_pool_connections(old_pool)
513
+
514
+ def is_same_host(self, url):
515
+ """
516
+ Check if the given ``url`` is a member of the same host as this
517
+ connection pool.
518
+ """
519
+ if url.startswith("/"):
520
+ return True
521
+
522
+ # TODO: Add optional support for socket.gethostbyname checking.
523
+ scheme, host, port = get_host(url)
524
+ if host is not None:
525
+ host = _normalize_host(host, scheme=scheme)
526
+
527
+ # Use explicit default port for comparison when none is given
528
+ if self.port and not port:
529
+ port = port_by_scheme.get(scheme)
530
+ elif not self.port and port == port_by_scheme.get(scheme):
531
+ port = None
532
+
533
+ return (scheme, host, port) == (self.scheme, self.host, self.port)
534
+
535
+ def urlopen(
536
+ self,
537
+ method,
538
+ url,
539
+ body=None,
540
+ headers=None,
541
+ retries=None,
542
+ redirect=True,
543
+ assert_same_host=True,
544
+ timeout=_Default,
545
+ pool_timeout=None,
546
+ release_conn=None,
547
+ chunked=False,
548
+ body_pos=None,
549
+ **response_kw
550
+ ):
551
+ """
552
+ Get a connection from the pool and perform an HTTP request. This is the
553
+ lowest level call for making a request, so you'll need to specify all
554
+ the raw details.
555
+
556
+ .. note::
557
+
558
+ More commonly, it's appropriate to use a convenience method provided
559
+ by :class:`.RequestMethods`, such as :meth:`request`.
560
+
561
+ .. note::
562
+
563
+ `release_conn` will only behave as expected if
564
+ `preload_content=False` because we want to make
565
+ `preload_content=False` the default behaviour someday soon without
566
+ breaking backwards compatibility.
567
+
568
+ :param method:
569
+ HTTP request method (such as GET, POST, PUT, etc.)
570
+
571
+ :param url:
572
+ The URL to perform the request on.
573
+
574
+ :param body:
575
+ Data to send in the request body, either :class:`str`, :class:`bytes`,
576
+ an iterable of :class:`str`/:class:`bytes`, or a file-like object.
577
+
578
+ :param headers:
579
+ Dictionary of custom headers to send, such as User-Agent,
580
+ If-None-Match, etc. If None, pool headers are used. If provided,
581
+ these headers completely replace any pool-specific headers.
582
+
583
+ :param retries:
584
+ Configure the number of retries to allow before raising a
585
+ :class:`~urllib3.exceptions.MaxRetryError` exception.
586
+
587
+ Pass ``None`` to retry until you receive a response. Pass a
588
+ :class:`~urllib3.util.retry.Retry` object for fine-grained control
589
+ over different types of retries.
590
+ Pass an integer number to retry connection errors that many times,
591
+ but no other types of errors. Pass zero to never retry.
592
+
593
+ If ``False``, then retries are disabled and any exception is raised
594
+ immediately. Also, instead of raising a MaxRetryError on redirects,
595
+ the redirect response will be returned.
596
+
597
+ :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.
598
+
599
+ :param redirect:
600
+ If True, automatically handle redirects (status codes 301, 302,
601
+ 303, 307, 308). Each redirect counts as a retry. Disabling retries
602
+ will disable redirect, too.
603
+
604
+ :param assert_same_host:
605
+ If ``True``, will make sure that the host of the pool requests is
606
+ consistent else will raise HostChangedError. When ``False``, you can
607
+ use the pool on an HTTP proxy and request foreign hosts.
608
+
609
+ :param timeout:
610
+ If specified, overrides the default timeout for this one
611
+ request. It may be a float (in seconds) or an instance of
612
+ :class:`urllib3.util.Timeout`.
613
+
614
+ :param pool_timeout:
615
+ If set and the pool is set to block=True, then this method will
616
+ block for ``pool_timeout`` seconds and raise EmptyPoolError if no
617
+ connection is available within the time period.
618
+
619
+ :param release_conn:
620
+ If False, then the urlopen call will not release the connection
621
+ back into the pool once a response is received (but will release if
622
+ you read the entire contents of the response such as when
623
+ `preload_content=True`). This is useful if you're not preloading
624
+ the response's content immediately. You will need to call
625
+ ``r.release_conn()`` on the response ``r`` to return the connection
626
+ back into the pool. If None, it takes the value of
627
+ ``response_kw.get('preload_content', True)``.
628
+
629
+ :param chunked:
630
+ If True, urllib3 will send the body using chunked transfer
631
+ encoding. Otherwise, urllib3 will send the body using the standard
632
+ content-length form. Defaults to False.
633
+
634
+ :param int body_pos:
635
+ Position to seek to in file-like body in the event of a retry or
636
+ redirect. Typically this won't need to be set because urllib3 will
637
+ auto-populate the value when needed.
638
+
639
+ :param \\**response_kw:
640
+ Additional parameters are passed to
641
+ :meth:`urllib3.response.HTTPResponse.from_httplib`
642
+ """
643
+
644
+ parsed_url = parse_url(url)
645
+ destination_scheme = parsed_url.scheme
646
+
647
+ if headers is None:
648
+ headers = self.headers
649
+
650
+ if not isinstance(retries, Retry):
651
+ retries = Retry.from_int(retries, redirect=redirect, default=self.retries)
652
+
653
+ if release_conn is None:
654
+ release_conn = response_kw.get("preload_content", True)
655
+
656
+ # Check host
657
+ if assert_same_host and not self.is_same_host(url):
658
+ raise HostChangedError(self, url, retries)
659
+
660
+ # Ensure that the URL we're connecting to is properly encoded
661
+ if url.startswith("/"):
662
+ url = six.ensure_str(_encode_target(url))
663
+ else:
664
+ url = six.ensure_str(parsed_url.url)
665
+
666
+ conn = None
667
+
668
+ # Track whether `conn` needs to be released before
669
+ # returning/raising/recursing. Update this variable if necessary, and
670
+ # leave `release_conn` constant throughout the function. That way, if
671
+ # the function recurses, the original value of `release_conn` will be
672
+ # passed down into the recursive call, and its value will be respected.
673
+ #
674
+ # See issue #651 [1] for details.
675
+ #
676
+ # [1] <https://github.com/urllib3/urllib3/issues/651>
677
+ release_this_conn = release_conn
678
+
679
+ http_tunnel_required = connection_requires_http_tunnel(
680
+ self.proxy, self.proxy_config, destination_scheme
681
+ )
682
+
683
+ # Merge the proxy headers. Only done when not using HTTP CONNECT. We
684
+ # have to copy the headers dict so we can safely change it without those
685
+ # changes being reflected in anyone else's copy.
686
+ if not http_tunnel_required:
687
+ headers = headers.copy()
688
+ headers.update(self.proxy_headers)
689
+
690
+ # Must keep the exception bound to a separate variable or else Python 3
691
+ # complains about UnboundLocalError.
692
+ err = None
693
+
694
+ # Keep track of whether we cleanly exited the except block. This
695
+ # ensures we do proper cleanup in finally.
696
+ clean_exit = False
697
+
698
+ # Rewind body position, if needed. Record current position
699
+ # for future rewinds in the event of a redirect/retry.
700
+ body_pos = set_file_position(body, body_pos)
701
+
702
+ try:
703
+ # Request a connection from the queue.
704
+ timeout_obj = self._get_timeout(timeout)
705
+ conn = self._get_conn(timeout=pool_timeout)
706
+
707
+ conn.timeout = timeout_obj.connect_timeout
708
+
709
+ is_new_proxy_conn = self.proxy is not None and not getattr(
710
+ conn, "sock", None
711
+ )
712
+ if is_new_proxy_conn and http_tunnel_required:
713
+ self._prepare_proxy(conn)
714
+
715
+ # Make the request on the httplib connection object.
716
+ httplib_response = self._make_request(
717
+ conn,
718
+ method,
719
+ url,
720
+ timeout=timeout_obj,
721
+ body=body,
722
+ headers=headers,
723
+ chunked=chunked,
724
+ )
725
+
726
+ # If we're going to release the connection in ``finally:``, then
727
+ # the response doesn't need to know about the connection. Otherwise
728
+ # it will also try to release it and we'll have a double-release
729
+ # mess.
730
+ response_conn = conn if not release_conn else None
731
+
732
+ # Pass method to Response for length checking
733
+ response_kw["request_method"] = method
734
+
735
+ # Import httplib's response into our own wrapper object
736
+ response = self.ResponseCls.from_httplib(
737
+ httplib_response,
738
+ pool=self,
739
+ connection=response_conn,
740
+ retries=retries,
741
+ **response_kw
742
+ )
743
+
744
+ # Everything went great!
745
+ clean_exit = True
746
+
747
+ except EmptyPoolError:
748
+ # Didn't get a connection from the pool, no need to clean up
749
+ clean_exit = True
750
+ release_this_conn = False
751
+ raise
752
+
753
+ except (
754
+ TimeoutError,
755
+ HTTPException,
756
+ SocketError,
757
+ ProtocolError,
758
+ BaseSSLError,
759
+ SSLError,
760
+ CertificateError,
761
+ ) as e:
762
+ # Discard the connection for these exceptions. It will be
763
+ # replaced during the next _get_conn() call.
764
+ clean_exit = False
765
+
766
+ def _is_ssl_error_message_from_http_proxy(ssl_error):
767
+ # We're trying to detect the message 'WRONG_VERSION_NUMBER' but
768
+ # SSLErrors are kinda all over the place when it comes to the message,
769
+ # so we try to cover our bases here!
770
+ message = " ".join(re.split("[^a-z]", str(ssl_error).lower()))
771
+ return (
772
+ "wrong version number" in message
773
+ or "unknown protocol" in message
774
+ or "record layer failure" in message
775
+ )
776
+
777
+ # Try to detect a common user error with proxies which is to
778
+ # set an HTTP proxy to be HTTPS when it should be 'http://'
779
+ # (ie {'http': 'http://proxy', 'https': 'https://proxy'})
780
+ # Instead we add a nice error message and point to a URL.
781
+ if (
782
+ isinstance(e, BaseSSLError)
783
+ and self.proxy
784
+ and _is_ssl_error_message_from_http_proxy(e)
785
+ and conn.proxy
786
+ and conn.proxy.scheme == "https"
787
+ ):
788
+ e = ProxyError(
789
+ "Your proxy appears to only use HTTP and not HTTPS, "
790
+ "try changing your proxy URL to be HTTP. See: "
791
+ "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html"
792
+ "#https-proxy-error-http-proxy",
793
+ SSLError(e),
794
+ )
795
+ elif isinstance(e, (BaseSSLError, CertificateError)):
796
+ e = SSLError(e)
797
+ elif isinstance(e, (SocketError, NewConnectionError)) and self.proxy:
798
+ e = ProxyError("Cannot connect to proxy.", e)
799
+ elif isinstance(e, (SocketError, HTTPException)):
800
+ e = ProtocolError("Connection aborted.", e)
801
+
802
+ retries = retries.increment(
803
+ method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
804
+ )
805
+ retries.sleep()
806
+
807
+ # Keep track of the error for the retry warning.
808
+ err = e
809
+
810
+ finally:
811
+ if not clean_exit:
812
+ # We hit some kind of exception, handled or otherwise. We need
813
+ # to throw the connection away unless explicitly told not to.
814
+ # Close the connection, set the variable to None, and make sure
815
+ # we put the None back in the pool to avoid leaking it.
816
+ conn = conn and conn.close()
817
+ release_this_conn = True
818
+
819
+ if release_this_conn:
820
+ # Put the connection back to be reused. If the connection is
821
+ # expired then it will be None, which will get replaced with a
822
+ # fresh connection during _get_conn.
823
+ self._put_conn(conn)
824
+
825
+ if not conn:
826
+ # Try again
827
+ log.warning(
828
+ "Retrying (%r) after connection broken by '%r': %s", retries, err, url
829
+ )
830
+ return self.urlopen(
831
+ method,
832
+ url,
833
+ body,
834
+ headers,
835
+ retries,
836
+ redirect,
837
+ assert_same_host,
838
+ timeout=timeout,
839
+ pool_timeout=pool_timeout,
840
+ release_conn=release_conn,
841
+ chunked=chunked,
842
+ body_pos=body_pos,
843
+ **response_kw
844
+ )
845
+
846
+ # Handle redirect?
847
+ redirect_location = redirect and response.get_redirect_location()
848
+ if redirect_location:
849
+ if response.status == 303:
850
+ # Change the method according to RFC 9110, Section 15.4.4.
851
+ method = "GET"
852
+ # And lose the body not to transfer anything sensitive.
853
+ body = None
854
+ headers = HTTPHeaderDict(headers)._prepare_for_method_change()
855
+
856
+ try:
857
+ retries = retries.increment(method, url, response=response, _pool=self)
858
+ except MaxRetryError:
859
+ if retries.raise_on_redirect:
860
+ response.drain_conn()
861
+ raise
862
+ return response
863
+
864
+ response.drain_conn()
865
+ retries.sleep_for_retry(response)
866
+ log.debug("Redirecting %s -> %s", url, redirect_location)
867
+ return self.urlopen(
868
+ method,
869
+ redirect_location,
870
+ body,
871
+ headers,
872
+ retries=retries,
873
+ redirect=redirect,
874
+ assert_same_host=assert_same_host,
875
+ timeout=timeout,
876
+ pool_timeout=pool_timeout,
877
+ release_conn=release_conn,
878
+ chunked=chunked,
879
+ body_pos=body_pos,
880
+ **response_kw
881
+ )
882
+
883
+ # Check if we should retry the HTTP response.
884
+ has_retry_after = bool(response.headers.get("Retry-After"))
885
+ if retries.is_retry(method, response.status, has_retry_after):
886
+ try:
887
+ retries = retries.increment(method, url, response=response, _pool=self)
888
+ except MaxRetryError:
889
+ if retries.raise_on_status:
890
+ response.drain_conn()
891
+ raise
892
+ return response
893
+
894
+ response.drain_conn()
895
+ retries.sleep(response)
896
+ log.debug("Retry: %s", url)
897
+ return self.urlopen(
898
+ method,
899
+ url,
900
+ body,
901
+ headers,
902
+ retries=retries,
903
+ redirect=redirect,
904
+ assert_same_host=assert_same_host,
905
+ timeout=timeout,
906
+ pool_timeout=pool_timeout,
907
+ release_conn=release_conn,
908
+ chunked=chunked,
909
+ body_pos=body_pos,
910
+ **response_kw
911
+ )
912
+
913
+ return response
914
+
915
+
916
+ class HTTPSConnectionPool(HTTPConnectionPool):
917
+ """
918
+ Same as :class:`.HTTPConnectionPool`, but HTTPS.
919
+
920
+ :class:`.HTTPSConnection` uses one of ``assert_fingerprint``,
921
+ ``assert_hostname`` and ``host`` in this order to verify connections.
922
+ If ``assert_hostname`` is False, no verification is done.
923
+
924
+ The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``,
925
+ ``ca_cert_dir``, ``ssl_version``, ``key_password`` are only used if :mod:`ssl`
926
+ is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade
927
+ the connection socket into an SSL socket.
928
+ """
929
+
930
+ scheme = "https"
931
+ ConnectionCls = HTTPSConnection
932
+
933
+ def __init__(
934
+ self,
935
+ host,
936
+ port=None,
937
+ strict=False,
938
+ timeout=Timeout.DEFAULT_TIMEOUT,
939
+ maxsize=1,
940
+ block=False,
941
+ headers=None,
942
+ retries=None,
943
+ _proxy=None,
944
+ _proxy_headers=None,
945
+ key_file=None,
946
+ cert_file=None,
947
+ cert_reqs=None,
948
+ key_password=None,
949
+ ca_certs=None,
950
+ ssl_version=None,
951
+ assert_hostname=None,
952
+ assert_fingerprint=None,
953
+ ca_cert_dir=None,
954
+ **conn_kw
955
+ ):
956
+
957
+ HTTPConnectionPool.__init__(
958
+ self,
959
+ host,
960
+ port,
961
+ strict,
962
+ timeout,
963
+ maxsize,
964
+ block,
965
+ headers,
966
+ retries,
967
+ _proxy,
968
+ _proxy_headers,
969
+ **conn_kw
970
+ )
971
+
972
+ self.key_file = key_file
973
+ self.cert_file = cert_file
974
+ self.cert_reqs = cert_reqs
975
+ self.key_password = key_password
976
+ self.ca_certs = ca_certs
977
+ self.ca_cert_dir = ca_cert_dir
978
+ self.ssl_version = ssl_version
979
+ self.assert_hostname = assert_hostname
980
+ self.assert_fingerprint = assert_fingerprint
981
+
982
+ def _prepare_conn(self, conn):
983
+ """
984
+ Prepare the ``connection`` for :meth:`urllib3.util.ssl_wrap_socket`
985
+ and establish the tunnel if proxy is used.
986
+ """
987
+
988
+ if isinstance(conn, VerifiedHTTPSConnection):
989
+ conn.set_cert(
990
+ key_file=self.key_file,
991
+ key_password=self.key_password,
992
+ cert_file=self.cert_file,
993
+ cert_reqs=self.cert_reqs,
994
+ ca_certs=self.ca_certs,
995
+ ca_cert_dir=self.ca_cert_dir,
996
+ assert_hostname=self.assert_hostname,
997
+ assert_fingerprint=self.assert_fingerprint,
998
+ )
999
+ conn.ssl_version = self.ssl_version
1000
+ return conn
1001
+
1002
+ def _prepare_proxy(self, conn):
1003
+ """
1004
+ Establishes a tunnel connection through HTTP CONNECT.
1005
+
1006
+ Tunnel connection is established early because otherwise httplib would
1007
+ improperly set Host: header to proxy's IP:port.
1008
+ """
1009
+
1010
+ conn.set_tunnel(self._proxy_host, self.port, self.proxy_headers)
1011
+
1012
+ if self.proxy.scheme == "https":
1013
+ conn.tls_in_tls_required = True
1014
+
1015
+ conn.connect()
1016
+
1017
+ def _new_conn(self):
1018
+ """
1019
+ Return a fresh :class:`http.client.HTTPSConnection`.
1020
+ """
1021
+ self.num_connections += 1
1022
+ log.debug(
1023
+ "Starting new HTTPS connection (%d): %s:%s",
1024
+ self.num_connections,
1025
+ self.host,
1026
+ self.port or "443",
1027
+ )
1028
+
1029
+ if not self.ConnectionCls or self.ConnectionCls is DummyConnection:
1030
+ raise SSLError(
1031
+ "Can't connect to HTTPS URL because the SSL module is not available."
1032
+ )
1033
+
1034
+ actual_host = self.host
1035
+ actual_port = self.port
1036
+ if self.proxy is not None:
1037
+ actual_host = self.proxy.host
1038
+ actual_port = self.proxy.port
1039
+
1040
+ conn = self.ConnectionCls(
1041
+ host=actual_host,
1042
+ port=actual_port,
1043
+ timeout=self.timeout.connect_timeout,
1044
+ strict=self.strict,
1045
+ cert_file=self.cert_file,
1046
+ key_file=self.key_file,
1047
+ key_password=self.key_password,
1048
+ **self.conn_kw
1049
+ )
1050
+
1051
+ return self._prepare_conn(conn)
1052
+
1053
+ def _validate_conn(self, conn):
1054
+ """
1055
+ Called right before a request is made, after the socket is created.
1056
+ """
1057
+ super(HTTPSConnectionPool, self)._validate_conn(conn)
1058
+
1059
+ # Force connect early to allow us to validate the connection.
1060
+ if not getattr(conn, "sock", None): # AppEngine might not have `.sock`
1061
+ conn.connect()
1062
+
1063
+ if not conn.is_verified:
1064
+ warnings.warn(
1065
+ (
1066
+ "Unverified HTTPS request is being made to host '%s'. "
1067
+ "Adding certificate verification is strongly advised. See: "
1068
+ "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html"
1069
+ "#ssl-warnings" % conn.host
1070
+ ),
1071
+ InsecureRequestWarning,
1072
+ )
1073
+
1074
+ if getattr(conn, "proxy_is_verified", None) is False:
1075
+ warnings.warn(
1076
+ (
1077
+ "Unverified HTTPS connection done to an HTTPS proxy. "
1078
+ "Adding certificate verification is strongly advised. See: "
1079
+ "https://urllib3.readthedocs.io/en/1.26.x/advanced-usage.html"
1080
+ "#ssl-warnings"
1081
+ ),
1082
+ InsecureRequestWarning,
1083
+ )
1084
+
1085
+
1086
+ def connection_from_url(url, **kw):
1087
+ """
1088
+ Given a url, return an :class:`.ConnectionPool` instance of its host.
1089
+
1090
+ This is a shortcut for not having to parse out the scheme, host, and port
1091
+ of the url before creating an :class:`.ConnectionPool` instance.
1092
+
1093
+ :param url:
1094
+ Absolute URL string that must include the scheme. Port is optional.
1095
+
1096
+ :param \\**kw:
1097
+ Passes additional parameters to the constructor of the appropriate
1098
+ :class:`.ConnectionPool`. Useful for specifying things like
1099
+ timeout, maxsize, headers, etc.
1100
+
1101
+ Example::
1102
+
1103
+ >>> conn = connection_from_url('http://google.com/')
1104
+ >>> r = conn.request('GET', '/')
1105
+ """
1106
+ scheme, host, port = get_host(url)
1107
+ port = port or port_by_scheme.get(scheme, 80)
1108
+ if scheme == "https":
1109
+ return HTTPSConnectionPool(host, port=port, **kw)
1110
+ else:
1111
+ return HTTPConnectionPool(host, port=port, **kw)
1112
+
1113
+
1114
+ def _normalize_host(host, scheme):
1115
+ """
1116
+ Normalize hosts for comparisons and use with sockets.
1117
+ """
1118
+
1119
+ host = normalize_host(host, scheme)
1120
+
1121
+ # httplib doesn't like it when we include brackets in IPv6 addresses
1122
+ # Specifically, if we include brackets but also pass the port then
1123
+ # httplib crazily doubles up the square brackets on the Host header.
1124
+ # Instead, we need to make sure we never pass ``None`` as the port.
1125
+ # However, for backward compatibility reasons we can't actually
1126
+ # *assert* that. See http://bugs.python.org/issue28539
1127
+ if host.startswith("[") and host.endswith("]"):
1128
+ host = host[1:-1]
1129
+ return host
1130
+
1131
+
1132
+ def _close_pool_connections(pool):
1133
+ """Drains a queue of connections and closes each one."""
1134
+ try:
1135
+ while True:
1136
+ conn = pool.get(block=False)
1137
+ if conn:
1138
+ conn.close()
1139
+ except queue.Empty:
1140
+ pass # Done.
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__init__.py ADDED
File without changes
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (206 Bytes). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc ADDED
Binary file (1.86 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc ADDED
Binary file (11.6 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-312.pyc ADDED
Binary file (5.72 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc ADDED
Binary file (24.5 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-312.pyc ADDED
Binary file (35.5 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc ADDED
Binary file (7.52 kB). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This module provides means to detect the App Engine environment.
3
+ """
4
+
5
+ import os
6
+
7
+
8
+ def is_appengine():
9
+ return is_local_appengine() or is_prod_appengine()
10
+
11
+
12
+ def is_appengine_sandbox():
13
+ """Reports if the app is running in the first generation sandbox.
14
+
15
+ The second generation runtimes are technically still in a sandbox, but it
16
+ is much less restrictive, so generally you shouldn't need to check for it.
17
+ see https://cloud.google.com/appengine/docs/standard/runtimes
18
+ """
19
+ return is_appengine() and os.environ["APPENGINE_RUNTIME"] == "python27"
20
+
21
+
22
+ def is_local_appengine():
23
+ return "APPENGINE_RUNTIME" in os.environ and os.environ.get(
24
+ "SERVER_SOFTWARE", ""
25
+ ).startswith("Development/")
26
+
27
+
28
+ def is_prod_appengine():
29
+ return "APPENGINE_RUNTIME" in os.environ and os.environ.get(
30
+ "SERVER_SOFTWARE", ""
31
+ ).startswith("Google App Engine/")
32
+
33
+
34
+ def is_prod_appengine_mvms():
35
+ """Deprecated."""
36
+ return False
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py ADDED
File without changes
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (223 Bytes). View file
 
lib/python3.12/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc ADDED
Binary file (17.4 kB). View file