Mingke977 commited on
Commit
418881f
·
verified ·
1 Parent(s): 81d78fb

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. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-310.pyc +0 -0
  2. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/_log.cpython-310.pyc +0 -0
  3. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-310.pyc +0 -0
  4. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/compat.cpython-310.pyc +0 -0
  5. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-310.pyc +0 -0
  6. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-310.pyc +0 -0
  7. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-310.pyc +0 -0
  8. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-310.pyc +0 -0
  9. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-310.pyc +0 -0
  10. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-310.pyc +0 -0
  11. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-310.pyc +0 -0
  12. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-310.pyc +0 -0
  13. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-310.pyc +0 -0
  14. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-310.pyc +0 -0
  15. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-310.pyc +0 -0
  16. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-310.pyc +0 -0
  17. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-310.pyc +0 -0
  18. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/logging.cpython-310.pyc +0 -0
  19. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/misc.cpython-310.pyc +0 -0
  20. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/models.cpython-310.pyc +0 -0
  21. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-310.pyc +0 -0
  22. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-310.pyc +0 -0
  23. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-310.pyc +0 -0
  24. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-310.pyc +0 -0
  25. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-310.pyc +0 -0
  26. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/urls.cpython-310.pyc +0 -0
  27. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-310.pyc +0 -0
  28. venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-310.pyc +0 -0
  29. venv/lib/python3.10/site-packages/pip/_internal/utils/encoding.py +36 -0
  30. venv/lib/python3.10/site-packages/pip/_internal/utils/entrypoints.py +27 -0
  31. venv/lib/python3.10/site-packages/pip/_internal/utils/filesystem.py +182 -0
  32. venv/lib/python3.10/site-packages/pip/_internal/utils/filetypes.py +27 -0
  33. venv/lib/python3.10/site-packages/pip/_internal/utils/glibc.py +88 -0
  34. venv/lib/python3.10/site-packages/pip/_internal/utils/hashes.py +144 -0
  35. venv/lib/python3.10/site-packages/pip/_internal/utils/inject_securetransport.py +35 -0
  36. venv/lib/python3.10/site-packages/pip/_internal/utils/logging.py +343 -0
  37. venv/lib/python3.10/site-packages/pip/_internal/utils/misc.py +653 -0
  38. venv/lib/python3.10/site-packages/pip/_internal/utils/models.py +39 -0
  39. venv/lib/python3.10/site-packages/pip/_internal/utils/packaging.py +57 -0
  40. venv/lib/python3.10/site-packages/pip/_internal/utils/setuptools_build.py +195 -0
  41. venv/lib/python3.10/site-packages/pip/_internal/utils/subprocess.py +260 -0
  42. venv/lib/python3.10/site-packages/pip/_internal/utils/temp_dir.py +246 -0
  43. venv/lib/python3.10/site-packages/pip/_internal/utils/unpacking.py +258 -0
  44. venv/lib/python3.10/site-packages/pip/_internal/utils/urls.py +62 -0
  45. venv/lib/python3.10/site-packages/pip/_internal/utils/virtualenv.py +104 -0
  46. venv/lib/python3.10/site-packages/pip/_internal/utils/wheel.py +136 -0
  47. venv/lib/python3.10/site-packages/pip/_internal/vcs/__init__.py +15 -0
  48. venv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-310.pyc +0 -0
  49. venv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-310.pyc +0 -0
  50. venv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/git.cpython-310.pyc +0 -0
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (290 Bytes). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/_log.cpython-310.pyc ADDED
Binary file (1.62 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-310.pyc ADDED
Binary file (1.72 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/compat.cpython-310.pyc ADDED
Binary file (1.61 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/compatibility_tags.cpython-310.pyc ADDED
Binary file (4.18 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/datetime.cpython-310.pyc ADDED
Binary file (613 Bytes). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-310.pyc ADDED
Binary file (3.41 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/direct_url_helpers.cpython-310.pyc ADDED
Binary file (2.18 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/distutils_args.cpython-310.pyc ADDED
Binary file (1.2 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/egg_link.cpython-310.pyc ADDED
Binary file (2.25 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-310.pyc ADDED
Binary file (1.4 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/entrypoints.cpython-310.pyc ADDED
Binary file (1.4 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-310.pyc ADDED
Binary file (5.26 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/filetypes.cpython-310.pyc ADDED
Binary file (1.04 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-310.pyc ADDED
Binary file (1.77 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-310.pyc ADDED
Binary file (5.29 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/inject_securetransport.cpython-310.pyc ADDED
Binary file (1.09 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/logging.cpython-310.pyc ADDED
Binary file (9.73 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/misc.cpython-310.pyc ADDED
Binary file (19.5 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/models.cpython-310.pyc ADDED
Binary file (2.09 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-310.pyc ADDED
Binary file (2.18 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-310.pyc ADDED
Binary file (4.69 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/subprocess.cpython-310.pyc ADDED
Binary file (5.87 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-310.pyc ADDED
Binary file (7.4 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/unpacking.cpython-310.pyc ADDED
Binary file (6.75 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/urls.cpython-310.pyc ADDED
Binary file (1.68 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-310.pyc ADDED
Binary file (3.39 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/__pycache__/wheel.cpython-310.pyc ADDED
Binary file (4.51 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/utils/encoding.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import codecs
2
+ import locale
3
+ import re
4
+ import sys
5
+ from typing import List, Tuple
6
+
7
+ BOMS: List[Tuple[bytes, str]] = [
8
+ (codecs.BOM_UTF8, "utf-8"),
9
+ (codecs.BOM_UTF16, "utf-16"),
10
+ (codecs.BOM_UTF16_BE, "utf-16-be"),
11
+ (codecs.BOM_UTF16_LE, "utf-16-le"),
12
+ (codecs.BOM_UTF32, "utf-32"),
13
+ (codecs.BOM_UTF32_BE, "utf-32-be"),
14
+ (codecs.BOM_UTF32_LE, "utf-32-le"),
15
+ ]
16
+
17
+ ENCODING_RE = re.compile(br"coding[:=]\s*([-\w.]+)")
18
+
19
+
20
+ def auto_decode(data: bytes) -> str:
21
+ """Check a bytes string for a BOM to correctly detect the encoding
22
+
23
+ Fallback to locale.getpreferredencoding(False) like open() on Python3"""
24
+ for bom, encoding in BOMS:
25
+ if data.startswith(bom):
26
+ return data[len(bom) :].decode(encoding)
27
+ # Lets check the first two lines as in PEP263
28
+ for line in data.split(b"\n")[:2]:
29
+ if line[0:1] == b"#" and ENCODING_RE.search(line):
30
+ result = ENCODING_RE.search(line)
31
+ assert result is not None
32
+ encoding = result.groups()[0].decode("ascii")
33
+ return data.decode(encoding)
34
+ return data.decode(
35
+ locale.getpreferredencoding(False) or sys.getdefaultencoding(),
36
+ )
venv/lib/python3.10/site-packages/pip/_internal/utils/entrypoints.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ from typing import List, Optional
3
+
4
+ from pip._internal.cli.main import main
5
+
6
+
7
+ def _wrapper(args: Optional[List[str]] = None) -> int:
8
+ """Central wrapper for all old entrypoints.
9
+
10
+ Historically pip has had several entrypoints defined. Because of issues
11
+ arising from PATH, sys.path, multiple Pythons, their interactions, and most
12
+ of them having a pip installed, users suffer every time an entrypoint gets
13
+ moved.
14
+
15
+ To alleviate this pain, and provide a mechanism for warning users and
16
+ directing them to an appropriate place for help, we now define all of
17
+ our old entrypoints as wrappers for the current one.
18
+ """
19
+ sys.stderr.write(
20
+ "WARNING: pip is being invoked by an old script wrapper. This will "
21
+ "fail in a future version of pip.\n"
22
+ "Please see https://github.com/pypa/pip/issues/5599 for advice on "
23
+ "fixing the underlying issue.\n"
24
+ "To avoid this problem you can invoke Python with '-m pip' instead of "
25
+ "running pip directly.\n"
26
+ )
27
+ return main(args)
venv/lib/python3.10/site-packages/pip/_internal/utils/filesystem.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fnmatch
2
+ import os
3
+ import os.path
4
+ import random
5
+ import shutil
6
+ import stat
7
+ import sys
8
+ from contextlib import contextmanager
9
+ from tempfile import NamedTemporaryFile
10
+ from typing import Any, BinaryIO, Iterator, List, Union, cast
11
+
12
+ from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed
13
+
14
+ from pip._internal.utils.compat import get_path_uid
15
+ from pip._internal.utils.misc import format_size
16
+
17
+
18
+ def check_path_owner(path: str) -> bool:
19
+ # If we don't have a way to check the effective uid of this process, then
20
+ # we'll just assume that we own the directory.
21
+ if sys.platform == "win32" or not hasattr(os, "geteuid"):
22
+ return True
23
+
24
+ assert os.path.isabs(path)
25
+
26
+ previous = None
27
+ while path != previous:
28
+ if os.path.lexists(path):
29
+ # Check if path is writable by current user.
30
+ if os.geteuid() == 0:
31
+ # Special handling for root user in order to handle properly
32
+ # cases where users use sudo without -H flag.
33
+ try:
34
+ path_uid = get_path_uid(path)
35
+ except OSError:
36
+ return False
37
+ return path_uid == 0
38
+ else:
39
+ return os.access(path, os.W_OK)
40
+ else:
41
+ previous, path = path, os.path.dirname(path)
42
+ return False # assume we don't own the path
43
+
44
+
45
+ def copy2_fixed(src: str, dest: str) -> None:
46
+ """Wrap shutil.copy2() but map errors copying socket files to
47
+ SpecialFileError as expected.
48
+
49
+ See also https://bugs.python.org/issue37700.
50
+ """
51
+ try:
52
+ shutil.copy2(src, dest)
53
+ except OSError:
54
+ for f in [src, dest]:
55
+ try:
56
+ is_socket_file = is_socket(f)
57
+ except OSError:
58
+ # An error has already occurred. Another error here is not
59
+ # a problem and we can ignore it.
60
+ pass
61
+ else:
62
+ if is_socket_file:
63
+ raise shutil.SpecialFileError(f"`{f}` is a socket")
64
+
65
+ raise
66
+
67
+
68
+ def is_socket(path: str) -> bool:
69
+ return stat.S_ISSOCK(os.lstat(path).st_mode)
70
+
71
+
72
+ @contextmanager
73
+ def adjacent_tmp_file(path: str, **kwargs: Any) -> Iterator[BinaryIO]:
74
+ """Return a file-like object pointing to a tmp file next to path.
75
+
76
+ The file is created securely and is ensured to be written to disk
77
+ after the context reaches its end.
78
+
79
+ kwargs will be passed to tempfile.NamedTemporaryFile to control
80
+ the way the temporary file will be opened.
81
+ """
82
+ with NamedTemporaryFile(
83
+ delete=False,
84
+ dir=os.path.dirname(path),
85
+ prefix=os.path.basename(path),
86
+ suffix=".tmp",
87
+ **kwargs,
88
+ ) as f:
89
+ result = cast(BinaryIO, f)
90
+ try:
91
+ yield result
92
+ finally:
93
+ result.flush()
94
+ os.fsync(result.fileno())
95
+
96
+
97
+ # Tenacity raises RetryError by default, explicitly raise the original exception
98
+ _replace_retry = retry(reraise=True, stop=stop_after_delay(1), wait=wait_fixed(0.25))
99
+
100
+ replace = _replace_retry(os.replace)
101
+
102
+
103
+ # test_writable_dir and _test_writable_dir_win are copied from Flit,
104
+ # with the author's agreement to also place them under pip's license.
105
+ def test_writable_dir(path: str) -> bool:
106
+ """Check if a directory is writable.
107
+
108
+ Uses os.access() on POSIX, tries creating files on Windows.
109
+ """
110
+ # If the directory doesn't exist, find the closest parent that does.
111
+ while not os.path.isdir(path):
112
+ parent = os.path.dirname(path)
113
+ if parent == path:
114
+ break # Should never get here, but infinite loops are bad
115
+ path = parent
116
+
117
+ if os.name == "posix":
118
+ return os.access(path, os.W_OK)
119
+
120
+ return _test_writable_dir_win(path)
121
+
122
+
123
+ def _test_writable_dir_win(path: str) -> bool:
124
+ # os.access doesn't work on Windows: http://bugs.python.org/issue2528
125
+ # and we can't use tempfile: http://bugs.python.org/issue22107
126
+ basename = "accesstest_deleteme_fishfingers_custard_"
127
+ alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"
128
+ for _ in range(10):
129
+ name = basename + "".join(random.choice(alphabet) for _ in range(6))
130
+ file = os.path.join(path, name)
131
+ try:
132
+ fd = os.open(file, os.O_RDWR | os.O_CREAT | os.O_EXCL)
133
+ except FileExistsError:
134
+ pass
135
+ except PermissionError:
136
+ # This could be because there's a directory with the same name.
137
+ # But it's highly unlikely there's a directory called that,
138
+ # so we'll assume it's because the parent dir is not writable.
139
+ # This could as well be because the parent dir is not readable,
140
+ # due to non-privileged user access.
141
+ return False
142
+ else:
143
+ os.close(fd)
144
+ os.unlink(file)
145
+ return True
146
+
147
+ # This should never be reached
148
+ raise OSError("Unexpected condition testing for writable directory")
149
+
150
+
151
+ def find_files(path: str, pattern: str) -> List[str]:
152
+ """Returns a list of absolute paths of files beneath path, recursively,
153
+ with filenames which match the UNIX-style shell glob pattern."""
154
+ result: List[str] = []
155
+ for root, _, files in os.walk(path):
156
+ matches = fnmatch.filter(files, pattern)
157
+ result.extend(os.path.join(root, f) for f in matches)
158
+ return result
159
+
160
+
161
+ def file_size(path: str) -> Union[int, float]:
162
+ # If it's a symlink, return 0.
163
+ if os.path.islink(path):
164
+ return 0
165
+ return os.path.getsize(path)
166
+
167
+
168
+ def format_file_size(path: str) -> str:
169
+ return format_size(file_size(path))
170
+
171
+
172
+ def directory_size(path: str) -> Union[int, float]:
173
+ size = 0.0
174
+ for root, _dirs, files in os.walk(path):
175
+ for filename in files:
176
+ file_path = os.path.join(root, filename)
177
+ size += file_size(file_path)
178
+ return size
179
+
180
+
181
+ def format_directory_size(path: str) -> str:
182
+ return format_size(directory_size(path))
venv/lib/python3.10/site-packages/pip/_internal/utils/filetypes.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Filetype information.
2
+ """
3
+
4
+ from typing import Tuple
5
+
6
+ from pip._internal.utils.misc import splitext
7
+
8
+ WHEEL_EXTENSION = ".whl"
9
+ BZ2_EXTENSIONS: Tuple[str, ...] = (".tar.bz2", ".tbz")
10
+ XZ_EXTENSIONS: Tuple[str, ...] = (
11
+ ".tar.xz",
12
+ ".txz",
13
+ ".tlz",
14
+ ".tar.lz",
15
+ ".tar.lzma",
16
+ )
17
+ ZIP_EXTENSIONS: Tuple[str, ...] = (".zip", WHEEL_EXTENSION)
18
+ TAR_EXTENSIONS: Tuple[str, ...] = (".tar.gz", ".tgz", ".tar")
19
+ ARCHIVE_EXTENSIONS = ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS
20
+
21
+
22
+ def is_archive_file(name: str) -> bool:
23
+ """Return True if `name` is a considered as an archive file."""
24
+ ext = splitext(name)[1].lower()
25
+ if ext in ARCHIVE_EXTENSIONS:
26
+ return True
27
+ return False
venv/lib/python3.10/site-packages/pip/_internal/utils/glibc.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # The following comment should be removed at some point in the future.
2
+ # mypy: strict-optional=False
3
+
4
+ import os
5
+ import sys
6
+ from typing import Optional, Tuple
7
+
8
+
9
+ def glibc_version_string() -> Optional[str]:
10
+ "Returns glibc version string, or None if not using glibc."
11
+ return glibc_version_string_confstr() or glibc_version_string_ctypes()
12
+
13
+
14
+ def glibc_version_string_confstr() -> Optional[str]:
15
+ "Primary implementation of glibc_version_string using os.confstr."
16
+ # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
17
+ # to be broken or missing. This strategy is used in the standard library
18
+ # platform module:
19
+ # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183
20
+ if sys.platform == "win32":
21
+ return None
22
+ try:
23
+ # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17":
24
+ _, version = os.confstr("CS_GNU_LIBC_VERSION").split()
25
+ except (AttributeError, OSError, ValueError):
26
+ # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
27
+ return None
28
+ return version
29
+
30
+
31
+ def glibc_version_string_ctypes() -> Optional[str]:
32
+ "Fallback implementation of glibc_version_string using ctypes."
33
+
34
+ try:
35
+ import ctypes
36
+ except ImportError:
37
+ return None
38
+
39
+ # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
40
+ # manpage says, "If filename is NULL, then the returned handle is for the
41
+ # main program". This way we can let the linker do the work to figure out
42
+ # which libc our process is actually using.
43
+ process_namespace = ctypes.CDLL(None)
44
+ try:
45
+ gnu_get_libc_version = process_namespace.gnu_get_libc_version
46
+ except AttributeError:
47
+ # Symbol doesn't exist -> therefore, we are not linked to
48
+ # glibc.
49
+ return None
50
+
51
+ # Call gnu_get_libc_version, which returns a string like "2.5"
52
+ gnu_get_libc_version.restype = ctypes.c_char_p
53
+ version_str = gnu_get_libc_version()
54
+ # py2 / py3 compatibility:
55
+ if not isinstance(version_str, str):
56
+ version_str = version_str.decode("ascii")
57
+
58
+ return version_str
59
+
60
+
61
+ # platform.libc_ver regularly returns completely nonsensical glibc
62
+ # versions. E.g. on my computer, platform says:
63
+ #
64
+ # ~$ python2.7 -c 'import platform; print(platform.libc_ver())'
65
+ # ('glibc', '2.7')
66
+ # ~$ python3.5 -c 'import platform; print(platform.libc_ver())'
67
+ # ('glibc', '2.9')
68
+ #
69
+ # But the truth is:
70
+ #
71
+ # ~$ ldd --version
72
+ # ldd (Debian GLIBC 2.22-11) 2.22
73
+ #
74
+ # This is unfortunate, because it means that the linehaul data on libc
75
+ # versions that was generated by pip 8.1.2 and earlier is useless and
76
+ # misleading. Solution: instead of using platform, use our code that actually
77
+ # works.
78
+ def libc_ver() -> Tuple[str, str]:
79
+ """Try to determine the glibc version
80
+
81
+ Returns a tuple of strings (lib, version) which default to empty strings
82
+ in case the lookup fails.
83
+ """
84
+ glibc_version = glibc_version_string()
85
+ if glibc_version is None:
86
+ return ("", "")
87
+ else:
88
+ return ("glibc", glibc_version)
venv/lib/python3.10/site-packages/pip/_internal/utils/hashes.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib
2
+ from typing import TYPE_CHECKING, BinaryIO, Dict, Iterator, List
3
+
4
+ from pip._internal.exceptions import HashMismatch, HashMissing, InstallationError
5
+ from pip._internal.utils.misc import read_chunks
6
+
7
+ if TYPE_CHECKING:
8
+ from hashlib import _Hash
9
+
10
+ # NoReturn introduced in 3.6.2; imported only for type checking to maintain
11
+ # pip compatibility with older patch versions of Python 3.6
12
+ from typing import NoReturn
13
+
14
+
15
+ # The recommended hash algo of the moment. Change this whenever the state of
16
+ # the art changes; it won't hurt backward compatibility.
17
+ FAVORITE_HASH = "sha256"
18
+
19
+
20
+ # Names of hashlib algorithms allowed by the --hash option and ``pip hash``
21
+ # Currently, those are the ones at least as collision-resistant as sha256.
22
+ STRONG_HASHES = ["sha256", "sha384", "sha512"]
23
+
24
+
25
+ class Hashes:
26
+ """A wrapper that builds multiple hashes at once and checks them against
27
+ known-good values
28
+
29
+ """
30
+
31
+ def __init__(self, hashes: Dict[str, List[str]] = None) -> None:
32
+ """
33
+ :param hashes: A dict of algorithm names pointing to lists of allowed
34
+ hex digests
35
+ """
36
+ allowed = {}
37
+ if hashes is not None:
38
+ for alg, keys in hashes.items():
39
+ # Make sure values are always sorted (to ease equality checks)
40
+ allowed[alg] = sorted(keys)
41
+ self._allowed = allowed
42
+
43
+ def __and__(self, other: "Hashes") -> "Hashes":
44
+ if not isinstance(other, Hashes):
45
+ return NotImplemented
46
+
47
+ # If either of the Hashes object is entirely empty (i.e. no hash
48
+ # specified at all), all hashes from the other object are allowed.
49
+ if not other:
50
+ return self
51
+ if not self:
52
+ return other
53
+
54
+ # Otherwise only hashes that present in both objects are allowed.
55
+ new = {}
56
+ for alg, values in other._allowed.items():
57
+ if alg not in self._allowed:
58
+ continue
59
+ new[alg] = [v for v in values if v in self._allowed[alg]]
60
+ return Hashes(new)
61
+
62
+ @property
63
+ def digest_count(self) -> int:
64
+ return sum(len(digests) for digests in self._allowed.values())
65
+
66
+ def is_hash_allowed(self, hash_name: str, hex_digest: str) -> bool:
67
+ """Return whether the given hex digest is allowed."""
68
+ return hex_digest in self._allowed.get(hash_name, [])
69
+
70
+ def check_against_chunks(self, chunks: Iterator[bytes]) -> None:
71
+ """Check good hashes against ones built from iterable of chunks of
72
+ data.
73
+
74
+ Raise HashMismatch if none match.
75
+
76
+ """
77
+ gots = {}
78
+ for hash_name in self._allowed.keys():
79
+ try:
80
+ gots[hash_name] = hashlib.new(hash_name)
81
+ except (ValueError, TypeError):
82
+ raise InstallationError(f"Unknown hash name: {hash_name}")
83
+
84
+ for chunk in chunks:
85
+ for hash in gots.values():
86
+ hash.update(chunk)
87
+
88
+ for hash_name, got in gots.items():
89
+ if got.hexdigest() in self._allowed[hash_name]:
90
+ return
91
+ self._raise(gots)
92
+
93
+ def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn":
94
+ raise HashMismatch(self._allowed, gots)
95
+
96
+ def check_against_file(self, file: BinaryIO) -> None:
97
+ """Check good hashes against a file-like object
98
+
99
+ Raise HashMismatch if none match.
100
+
101
+ """
102
+ return self.check_against_chunks(read_chunks(file))
103
+
104
+ def check_against_path(self, path: str) -> None:
105
+ with open(path, "rb") as file:
106
+ return self.check_against_file(file)
107
+
108
+ def __bool__(self) -> bool:
109
+ """Return whether I know any known-good hashes."""
110
+ return bool(self._allowed)
111
+
112
+ def __eq__(self, other: object) -> bool:
113
+ if not isinstance(other, Hashes):
114
+ return NotImplemented
115
+ return self._allowed == other._allowed
116
+
117
+ def __hash__(self) -> int:
118
+ return hash(
119
+ ",".join(
120
+ sorted(
121
+ ":".join((alg, digest))
122
+ for alg, digest_list in self._allowed.items()
123
+ for digest in digest_list
124
+ )
125
+ )
126
+ )
127
+
128
+
129
+ class MissingHashes(Hashes):
130
+ """A workalike for Hashes used when we're missing a hash for a requirement
131
+
132
+ It computes the actual hash of the requirement and raises a HashMissing
133
+ exception showing it to the user.
134
+
135
+ """
136
+
137
+ def __init__(self) -> None:
138
+ """Don't offer the ``hashes`` kwarg."""
139
+ # Pass our favorite hash in to generate a "gotten hash". With the
140
+ # empty list, it will never match, so an error will always raise.
141
+ super().__init__(hashes={FAVORITE_HASH: []})
142
+
143
+ def _raise(self, gots: Dict[str, "_Hash"]) -> "NoReturn":
144
+ raise HashMissing(gots[FAVORITE_HASH].hexdigest())
venv/lib/python3.10/site-packages/pip/_internal/utils/inject_securetransport.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """A helper module that injects SecureTransport, on import.
2
+
3
+ The import should be done as early as possible, to ensure all requests and
4
+ sessions (or whatever) are created after injecting SecureTransport.
5
+
6
+ Note that we only do the injection on macOS, when the linked OpenSSL is too
7
+ old to handle TLSv1.2.
8
+ """
9
+
10
+ import sys
11
+
12
+
13
+ def inject_securetransport() -> None:
14
+ # Only relevant on macOS
15
+ if sys.platform != "darwin":
16
+ return
17
+
18
+ try:
19
+ import ssl
20
+ except ImportError:
21
+ return
22
+
23
+ # Checks for OpenSSL 1.0.1
24
+ if ssl.OPENSSL_VERSION_NUMBER >= 0x1000100F:
25
+ return
26
+
27
+ try:
28
+ from pip._vendor.urllib3.contrib import securetransport
29
+ except (ImportError, OSError):
30
+ return
31
+
32
+ securetransport.inject_into_urllib3()
33
+
34
+
35
+ inject_securetransport()
venv/lib/python3.10/site-packages/pip/_internal/utils/logging.py ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import contextlib
2
+ import errno
3
+ import logging
4
+ import logging.handlers
5
+ import os
6
+ import sys
7
+ import threading
8
+ from dataclasses import dataclass
9
+ from logging import Filter
10
+ from typing import IO, Any, ClassVar, Iterator, List, Optional, TextIO, Type
11
+
12
+ from pip._vendor.rich.console import (
13
+ Console,
14
+ ConsoleOptions,
15
+ ConsoleRenderable,
16
+ RenderResult,
17
+ )
18
+ from pip._vendor.rich.highlighter import NullHighlighter
19
+ from pip._vendor.rich.logging import RichHandler
20
+ from pip._vendor.rich.segment import Segment
21
+ from pip._vendor.rich.style import Style
22
+
23
+ from pip._internal.exceptions import DiagnosticPipError
24
+ from pip._internal.utils._log import VERBOSE, getLogger
25
+ from pip._internal.utils.compat import WINDOWS
26
+ from pip._internal.utils.deprecation import DEPRECATION_MSG_PREFIX
27
+ from pip._internal.utils.misc import ensure_dir
28
+
29
+ _log_state = threading.local()
30
+ subprocess_logger = getLogger("pip.subprocessor")
31
+
32
+
33
+ class BrokenStdoutLoggingError(Exception):
34
+ """
35
+ Raised if BrokenPipeError occurs for the stdout stream while logging.
36
+ """
37
+
38
+
39
+ def _is_broken_pipe_error(exc_class: Type[BaseException], exc: BaseException) -> bool:
40
+ if exc_class is BrokenPipeError:
41
+ return True
42
+
43
+ # On Windows, a broken pipe can show up as EINVAL rather than EPIPE:
44
+ # https://bugs.python.org/issue19612
45
+ # https://bugs.python.org/issue30418
46
+ if not WINDOWS:
47
+ return False
48
+
49
+ return isinstance(exc, OSError) and exc.errno in (errno.EINVAL, errno.EPIPE)
50
+
51
+
52
+ @contextlib.contextmanager
53
+ def indent_log(num: int = 2) -> Iterator[None]:
54
+ """
55
+ A context manager which will cause the log output to be indented for any
56
+ log messages emitted inside it.
57
+ """
58
+ # For thread-safety
59
+ _log_state.indentation = get_indentation()
60
+ _log_state.indentation += num
61
+ try:
62
+ yield
63
+ finally:
64
+ _log_state.indentation -= num
65
+
66
+
67
+ def get_indentation() -> int:
68
+ return getattr(_log_state, "indentation", 0)
69
+
70
+
71
+ class IndentingFormatter(logging.Formatter):
72
+ default_time_format = "%Y-%m-%dT%H:%M:%S"
73
+
74
+ def __init__(
75
+ self,
76
+ *args: Any,
77
+ add_timestamp: bool = False,
78
+ **kwargs: Any,
79
+ ) -> None:
80
+ """
81
+ A logging.Formatter that obeys the indent_log() context manager.
82
+
83
+ :param add_timestamp: A bool indicating output lines should be prefixed
84
+ with their record's timestamp.
85
+ """
86
+ self.add_timestamp = add_timestamp
87
+ super().__init__(*args, **kwargs)
88
+
89
+ def get_message_start(self, formatted: str, levelno: int) -> str:
90
+ """
91
+ Return the start of the formatted log message (not counting the
92
+ prefix to add to each line).
93
+ """
94
+ if levelno < logging.WARNING:
95
+ return ""
96
+ if formatted.startswith(DEPRECATION_MSG_PREFIX):
97
+ # Then the message already has a prefix. We don't want it to
98
+ # look like "WARNING: DEPRECATION: ...."
99
+ return ""
100
+ if levelno < logging.ERROR:
101
+ return "WARNING: "
102
+
103
+ return "ERROR: "
104
+
105
+ def format(self, record: logging.LogRecord) -> str:
106
+ """
107
+ Calls the standard formatter, but will indent all of the log message
108
+ lines by our current indentation level.
109
+ """
110
+ formatted = super().format(record)
111
+ message_start = self.get_message_start(formatted, record.levelno)
112
+ formatted = message_start + formatted
113
+
114
+ prefix = ""
115
+ if self.add_timestamp:
116
+ prefix = f"{self.formatTime(record)} "
117
+ prefix += " " * get_indentation()
118
+ formatted = "".join([prefix + line for line in formatted.splitlines(True)])
119
+ return formatted
120
+
121
+
122
+ @dataclass
123
+ class IndentedRenderable:
124
+ renderable: ConsoleRenderable
125
+ indent: int
126
+
127
+ def __rich_console__(
128
+ self, console: Console, options: ConsoleOptions
129
+ ) -> RenderResult:
130
+ segments = console.render(self.renderable, options)
131
+ lines = Segment.split_lines(segments)
132
+ for line in lines:
133
+ yield Segment(" " * self.indent)
134
+ yield from line
135
+ yield Segment("\n")
136
+
137
+
138
+ class RichPipStreamHandler(RichHandler):
139
+ KEYWORDS: ClassVar[Optional[List[str]]] = []
140
+
141
+ def __init__(self, stream: Optional[TextIO], no_color: bool) -> None:
142
+ super().__init__(
143
+ console=Console(file=stream, no_color=no_color, soft_wrap=True),
144
+ show_time=False,
145
+ show_level=False,
146
+ show_path=False,
147
+ highlighter=NullHighlighter(),
148
+ )
149
+
150
+ # Our custom override on Rich's logger, to make things work as we need them to.
151
+ def emit(self, record: logging.LogRecord) -> None:
152
+ style: Optional[Style] = None
153
+
154
+ # If we are given a diagnostic error to present, present it with indentation.
155
+ if record.msg == "[present-diagnostic] %s" and len(record.args) == 1:
156
+ diagnostic_error: DiagnosticPipError = record.args[0] # type: ignore[index]
157
+ assert isinstance(diagnostic_error, DiagnosticPipError)
158
+
159
+ renderable: ConsoleRenderable = IndentedRenderable(
160
+ diagnostic_error, indent=get_indentation()
161
+ )
162
+ else:
163
+ message = self.format(record)
164
+ renderable = self.render_message(record, message)
165
+ if record.levelno is not None:
166
+ if record.levelno >= logging.ERROR:
167
+ style = Style(color="red")
168
+ elif record.levelno >= logging.WARNING:
169
+ style = Style(color="yellow")
170
+
171
+ try:
172
+ self.console.print(renderable, overflow="ignore", crop=False, style=style)
173
+ except Exception:
174
+ self.handleError(record)
175
+
176
+ def handleError(self, record: logging.LogRecord) -> None:
177
+ """Called when logging is unable to log some output."""
178
+
179
+ exc_class, exc = sys.exc_info()[:2]
180
+ # If a broken pipe occurred while calling write() or flush() on the
181
+ # stdout stream in logging's Handler.emit(), then raise our special
182
+ # exception so we can handle it in main() instead of logging the
183
+ # broken pipe error and continuing.
184
+ if (
185
+ exc_class
186
+ and exc
187
+ and self.console.file is sys.stdout
188
+ and _is_broken_pipe_error(exc_class, exc)
189
+ ):
190
+ raise BrokenStdoutLoggingError()
191
+
192
+ return super().handleError(record)
193
+
194
+
195
+ class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler):
196
+ def _open(self) -> IO[Any]:
197
+ ensure_dir(os.path.dirname(self.baseFilename))
198
+ return super()._open()
199
+
200
+
201
+ class MaxLevelFilter(Filter):
202
+ def __init__(self, level: int) -> None:
203
+ self.level = level
204
+
205
+ def filter(self, record: logging.LogRecord) -> bool:
206
+ return record.levelno < self.level
207
+
208
+
209
+ class ExcludeLoggerFilter(Filter):
210
+
211
+ """
212
+ A logging Filter that excludes records from a logger (or its children).
213
+ """
214
+
215
+ def filter(self, record: logging.LogRecord) -> bool:
216
+ # The base Filter class allows only records from a logger (or its
217
+ # children).
218
+ return not super().filter(record)
219
+
220
+
221
+ def setup_logging(verbosity: int, no_color: bool, user_log_file: Optional[str]) -> int:
222
+ """Configures and sets up all of the logging
223
+
224
+ Returns the requested logging level, as its integer value.
225
+ """
226
+
227
+ # Determine the level to be logging at.
228
+ if verbosity >= 2:
229
+ level_number = logging.DEBUG
230
+ elif verbosity == 1:
231
+ level_number = VERBOSE
232
+ elif verbosity == -1:
233
+ level_number = logging.WARNING
234
+ elif verbosity == -2:
235
+ level_number = logging.ERROR
236
+ elif verbosity <= -3:
237
+ level_number = logging.CRITICAL
238
+ else:
239
+ level_number = logging.INFO
240
+
241
+ level = logging.getLevelName(level_number)
242
+
243
+ # The "root" logger should match the "console" level *unless* we also need
244
+ # to log to a user log file.
245
+ include_user_log = user_log_file is not None
246
+ if include_user_log:
247
+ additional_log_file = user_log_file
248
+ root_level = "DEBUG"
249
+ else:
250
+ additional_log_file = "/dev/null"
251
+ root_level = level
252
+
253
+ # Disable any logging besides WARNING unless we have DEBUG level logging
254
+ # enabled for vendored libraries.
255
+ vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG"
256
+
257
+ # Shorthands for clarity
258
+ log_streams = {
259
+ "stdout": "ext://sys.stdout",
260
+ "stderr": "ext://sys.stderr",
261
+ }
262
+ handler_classes = {
263
+ "stream": "pip._internal.utils.logging.RichPipStreamHandler",
264
+ "file": "pip._internal.utils.logging.BetterRotatingFileHandler",
265
+ }
266
+ handlers = ["console", "console_errors", "console_subprocess"] + (
267
+ ["user_log"] if include_user_log else []
268
+ )
269
+
270
+ logging.config.dictConfig(
271
+ {
272
+ "version": 1,
273
+ "disable_existing_loggers": False,
274
+ "filters": {
275
+ "exclude_warnings": {
276
+ "()": "pip._internal.utils.logging.MaxLevelFilter",
277
+ "level": logging.WARNING,
278
+ },
279
+ "restrict_to_subprocess": {
280
+ "()": "logging.Filter",
281
+ "name": subprocess_logger.name,
282
+ },
283
+ "exclude_subprocess": {
284
+ "()": "pip._internal.utils.logging.ExcludeLoggerFilter",
285
+ "name": subprocess_logger.name,
286
+ },
287
+ },
288
+ "formatters": {
289
+ "indent": {
290
+ "()": IndentingFormatter,
291
+ "format": "%(message)s",
292
+ },
293
+ "indent_with_timestamp": {
294
+ "()": IndentingFormatter,
295
+ "format": "%(message)s",
296
+ "add_timestamp": True,
297
+ },
298
+ },
299
+ "handlers": {
300
+ "console": {
301
+ "level": level,
302
+ "class": handler_classes["stream"],
303
+ "no_color": no_color,
304
+ "stream": log_streams["stdout"],
305
+ "filters": ["exclude_subprocess", "exclude_warnings"],
306
+ "formatter": "indent",
307
+ },
308
+ "console_errors": {
309
+ "level": "WARNING",
310
+ "class": handler_classes["stream"],
311
+ "no_color": no_color,
312
+ "stream": log_streams["stderr"],
313
+ "filters": ["exclude_subprocess"],
314
+ "formatter": "indent",
315
+ },
316
+ # A handler responsible for logging to the console messages
317
+ # from the "subprocessor" logger.
318
+ "console_subprocess": {
319
+ "level": level,
320
+ "class": handler_classes["stream"],
321
+ "stream": log_streams["stderr"],
322
+ "no_color": no_color,
323
+ "filters": ["restrict_to_subprocess"],
324
+ "formatter": "indent",
325
+ },
326
+ "user_log": {
327
+ "level": "DEBUG",
328
+ "class": handler_classes["file"],
329
+ "filename": additional_log_file,
330
+ "encoding": "utf-8",
331
+ "delay": True,
332
+ "formatter": "indent_with_timestamp",
333
+ },
334
+ },
335
+ "root": {
336
+ "level": root_level,
337
+ "handlers": handlers,
338
+ },
339
+ "loggers": {"pip._vendor": {"level": vendored_log_level}},
340
+ }
341
+ )
342
+
343
+ return level_number
venv/lib/python3.10/site-packages/pip/_internal/utils/misc.py ADDED
@@ -0,0 +1,653 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # The following comment should be removed at some point in the future.
2
+ # mypy: strict-optional=False
3
+
4
+ import contextlib
5
+ import errno
6
+ import getpass
7
+ import hashlib
8
+ import io
9
+ import logging
10
+ import os
11
+ import posixpath
12
+ import shutil
13
+ import stat
14
+ import sys
15
+ import urllib.parse
16
+ from io import StringIO
17
+ from itertools import filterfalse, tee, zip_longest
18
+ from types import TracebackType
19
+ from typing import (
20
+ Any,
21
+ BinaryIO,
22
+ Callable,
23
+ ContextManager,
24
+ Iterable,
25
+ Iterator,
26
+ List,
27
+ Optional,
28
+ TextIO,
29
+ Tuple,
30
+ Type,
31
+ TypeVar,
32
+ cast,
33
+ )
34
+
35
+ from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed
36
+
37
+ from pip import __version__
38
+ from pip._internal.exceptions import CommandError
39
+ from pip._internal.locations import get_major_minor_version
40
+ from pip._internal.utils.compat import WINDOWS
41
+ from pip._internal.utils.virtualenv import running_under_virtualenv
42
+
43
+ __all__ = [
44
+ "rmtree",
45
+ "display_path",
46
+ "backup_dir",
47
+ "ask",
48
+ "splitext",
49
+ "format_size",
50
+ "is_installable_dir",
51
+ "normalize_path",
52
+ "renames",
53
+ "get_prog",
54
+ "captured_stdout",
55
+ "ensure_dir",
56
+ "remove_auth_from_url",
57
+ ]
58
+
59
+
60
+ logger = logging.getLogger(__name__)
61
+
62
+ T = TypeVar("T")
63
+ ExcInfo = Tuple[Type[BaseException], BaseException, TracebackType]
64
+ VersionInfo = Tuple[int, int, int]
65
+ NetlocTuple = Tuple[str, Tuple[Optional[str], Optional[str]]]
66
+
67
+
68
+ def get_pip_version() -> str:
69
+ pip_pkg_dir = os.path.join(os.path.dirname(__file__), "..", "..")
70
+ pip_pkg_dir = os.path.abspath(pip_pkg_dir)
71
+
72
+ return "pip {} from {} (python {})".format(
73
+ __version__,
74
+ pip_pkg_dir,
75
+ get_major_minor_version(),
76
+ )
77
+
78
+
79
+ def normalize_version_info(py_version_info: Tuple[int, ...]) -> Tuple[int, int, int]:
80
+ """
81
+ Convert a tuple of ints representing a Python version to one of length
82
+ three.
83
+
84
+ :param py_version_info: a tuple of ints representing a Python version,
85
+ or None to specify no version. The tuple can have any length.
86
+
87
+ :return: a tuple of length three if `py_version_info` is non-None.
88
+ Otherwise, return `py_version_info` unchanged (i.e. None).
89
+ """
90
+ if len(py_version_info) < 3:
91
+ py_version_info += (3 - len(py_version_info)) * (0,)
92
+ elif len(py_version_info) > 3:
93
+ py_version_info = py_version_info[:3]
94
+
95
+ return cast("VersionInfo", py_version_info)
96
+
97
+
98
+ def ensure_dir(path: str) -> None:
99
+ """os.path.makedirs without EEXIST."""
100
+ try:
101
+ os.makedirs(path)
102
+ except OSError as e:
103
+ # Windows can raise spurious ENOTEMPTY errors. See #6426.
104
+ if e.errno != errno.EEXIST and e.errno != errno.ENOTEMPTY:
105
+ raise
106
+
107
+
108
+ def get_prog() -> str:
109
+ try:
110
+ prog = os.path.basename(sys.argv[0])
111
+ if prog in ("__main__.py", "-c"):
112
+ return f"{sys.executable} -m pip"
113
+ else:
114
+ return prog
115
+ except (AttributeError, TypeError, IndexError):
116
+ pass
117
+ return "pip"
118
+
119
+
120
+ # Retry every half second for up to 3 seconds
121
+ # Tenacity raises RetryError by default, explicitly raise the original exception
122
+ @retry(reraise=True, stop=stop_after_delay(3), wait=wait_fixed(0.5))
123
+ def rmtree(dir: str, ignore_errors: bool = False) -> None:
124
+ shutil.rmtree(dir, ignore_errors=ignore_errors, onerror=rmtree_errorhandler)
125
+
126
+
127
+ def rmtree_errorhandler(func: Callable[..., Any], path: str, exc_info: ExcInfo) -> None:
128
+ """On Windows, the files in .svn are read-only, so when rmtree() tries to
129
+ remove them, an exception is thrown. We catch that here, remove the
130
+ read-only attribute, and hopefully continue without problems."""
131
+ try:
132
+ has_attr_readonly = not (os.stat(path).st_mode & stat.S_IWRITE)
133
+ except OSError:
134
+ # it's equivalent to os.path.exists
135
+ return
136
+
137
+ if has_attr_readonly:
138
+ # convert to read/write
139
+ os.chmod(path, stat.S_IWRITE)
140
+ # use the original function to repeat the operation
141
+ func(path)
142
+ return
143
+ else:
144
+ raise
145
+
146
+
147
+ def display_path(path: str) -> str:
148
+ """Gives the display value for a given path, making it relative to cwd
149
+ if possible."""
150
+ path = os.path.normcase(os.path.abspath(path))
151
+ if path.startswith(os.getcwd() + os.path.sep):
152
+ path = "." + path[len(os.getcwd()) :]
153
+ return path
154
+
155
+
156
+ def backup_dir(dir: str, ext: str = ".bak") -> str:
157
+ """Figure out the name of a directory to back up the given dir to
158
+ (adding .bak, .bak2, etc)"""
159
+ n = 1
160
+ extension = ext
161
+ while os.path.exists(dir + extension):
162
+ n += 1
163
+ extension = ext + str(n)
164
+ return dir + extension
165
+
166
+
167
+ def ask_path_exists(message: str, options: Iterable[str]) -> str:
168
+ for action in os.environ.get("PIP_EXISTS_ACTION", "").split():
169
+ if action in options:
170
+ return action
171
+ return ask(message, options)
172
+
173
+
174
+ def _check_no_input(message: str) -> None:
175
+ """Raise an error if no input is allowed."""
176
+ if os.environ.get("PIP_NO_INPUT"):
177
+ raise Exception(
178
+ f"No input was expected ($PIP_NO_INPUT set); question: {message}"
179
+ )
180
+
181
+
182
+ def ask(message: str, options: Iterable[str]) -> str:
183
+ """Ask the message interactively, with the given possible responses"""
184
+ while 1:
185
+ _check_no_input(message)
186
+ response = input(message)
187
+ response = response.strip().lower()
188
+ if response not in options:
189
+ print(
190
+ "Your response ({!r}) was not one of the expected responses: "
191
+ "{}".format(response, ", ".join(options))
192
+ )
193
+ else:
194
+ return response
195
+
196
+
197
+ def ask_input(message: str) -> str:
198
+ """Ask for input interactively."""
199
+ _check_no_input(message)
200
+ return input(message)
201
+
202
+
203
+ def ask_password(message: str) -> str:
204
+ """Ask for a password interactively."""
205
+ _check_no_input(message)
206
+ return getpass.getpass(message)
207
+
208
+
209
+ def strtobool(val: str) -> int:
210
+ """Convert a string representation of truth to true (1) or false (0).
211
+
212
+ True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
213
+ are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
214
+ 'val' is anything else.
215
+ """
216
+ val = val.lower()
217
+ if val in ("y", "yes", "t", "true", "on", "1"):
218
+ return 1
219
+ elif val in ("n", "no", "f", "false", "off", "0"):
220
+ return 0
221
+ else:
222
+ raise ValueError(f"invalid truth value {val!r}")
223
+
224
+
225
+ def format_size(bytes: float) -> str:
226
+ if bytes > 1000 * 1000:
227
+ return "{:.1f} MB".format(bytes / 1000.0 / 1000)
228
+ elif bytes > 10 * 1000:
229
+ return "{} kB".format(int(bytes / 1000))
230
+ elif bytes > 1000:
231
+ return "{:.1f} kB".format(bytes / 1000.0)
232
+ else:
233
+ return "{} bytes".format(int(bytes))
234
+
235
+
236
+ def tabulate(rows: Iterable[Iterable[Any]]) -> Tuple[List[str], List[int]]:
237
+ """Return a list of formatted rows and a list of column sizes.
238
+
239
+ For example::
240
+
241
+ >>> tabulate([['foobar', 2000], [0xdeadbeef]])
242
+ (['foobar 2000', '3735928559'], [10, 4])
243
+ """
244
+ rows = [tuple(map(str, row)) for row in rows]
245
+ sizes = [max(map(len, col)) for col in zip_longest(*rows, fillvalue="")]
246
+ table = [" ".join(map(str.ljust, row, sizes)).rstrip() for row in rows]
247
+ return table, sizes
248
+
249
+
250
+ def is_installable_dir(path: str) -> bool:
251
+ """Is path is a directory containing pyproject.toml or setup.py?
252
+
253
+ If pyproject.toml exists, this is a PEP 517 project. Otherwise we look for
254
+ a legacy setuptools layout by identifying setup.py. We don't check for the
255
+ setup.cfg because using it without setup.py is only available for PEP 517
256
+ projects, which are already covered by the pyproject.toml check.
257
+ """
258
+ if not os.path.isdir(path):
259
+ return False
260
+ if os.path.isfile(os.path.join(path, "pyproject.toml")):
261
+ return True
262
+ if os.path.isfile(os.path.join(path, "setup.py")):
263
+ return True
264
+ return False
265
+
266
+
267
+ def read_chunks(file: BinaryIO, size: int = io.DEFAULT_BUFFER_SIZE) -> Iterator[bytes]:
268
+ """Yield pieces of data from a file-like object until EOF."""
269
+ while True:
270
+ chunk = file.read(size)
271
+ if not chunk:
272
+ break
273
+ yield chunk
274
+
275
+
276
+ def normalize_path(path: str, resolve_symlinks: bool = True) -> str:
277
+ """
278
+ Convert a path to its canonical, case-normalized, absolute version.
279
+
280
+ """
281
+ path = os.path.expanduser(path)
282
+ if resolve_symlinks:
283
+ path = os.path.realpath(path)
284
+ else:
285
+ path = os.path.abspath(path)
286
+ return os.path.normcase(path)
287
+
288
+
289
+ def splitext(path: str) -> Tuple[str, str]:
290
+ """Like os.path.splitext, but take off .tar too"""
291
+ base, ext = posixpath.splitext(path)
292
+ if base.lower().endswith(".tar"):
293
+ ext = base[-4:] + ext
294
+ base = base[:-4]
295
+ return base, ext
296
+
297
+
298
+ def renames(old: str, new: str) -> None:
299
+ """Like os.renames(), but handles renaming across devices."""
300
+ # Implementation borrowed from os.renames().
301
+ head, tail = os.path.split(new)
302
+ if head and tail and not os.path.exists(head):
303
+ os.makedirs(head)
304
+
305
+ shutil.move(old, new)
306
+
307
+ head, tail = os.path.split(old)
308
+ if head and tail:
309
+ try:
310
+ os.removedirs(head)
311
+ except OSError:
312
+ pass
313
+
314
+
315
+ def is_local(path: str) -> bool:
316
+ """
317
+ Return True if this is a path pip is allowed to modify.
318
+
319
+ If we're in a virtualenv, sys.prefix points to the virtualenv's
320
+ prefix; only sys.prefix is considered local.
321
+
322
+ If we're not in a virtualenv, in general we can modify anything.
323
+ However, if the OS vendor has configured distutils to install
324
+ somewhere other than sys.prefix (which could be a subdirectory of
325
+ sys.prefix, e.g. /usr/local), we consider sys.prefix itself nonlocal
326
+ and the domain of the OS vendor. (In other words, everything _other
327
+ than_ sys.prefix is considered local.)
328
+
329
+ Caution: this function assumes the head of path has been normalized
330
+ with normalize_path.
331
+ """
332
+
333
+ path = normalize_path(path)
334
+ # Hard-coded becouse PyPy uses a different sys.prefix on Debian
335
+ prefix = '/usr'
336
+
337
+ if running_under_virtualenv():
338
+ return path.startswith(normalize_path(sys.prefix))
339
+ else:
340
+ from pip._internal.locations import get_scheme
341
+ from pip._internal.models.scheme import SCHEME_KEYS
342
+ if path.startswith(prefix):
343
+ scheme = get_scheme("")
344
+ for key in SCHEME_KEYS:
345
+ local_path = getattr(scheme, key)
346
+ if path.startswith(normalize_path(local_path)):
347
+ return True
348
+ return False
349
+ else:
350
+ return True
351
+
352
+
353
+ def write_output(msg: Any, *args: Any) -> None:
354
+ logger.info(msg, *args)
355
+
356
+
357
+ class StreamWrapper(StringIO):
358
+ orig_stream: TextIO = None
359
+
360
+ @classmethod
361
+ def from_stream(cls, orig_stream: TextIO) -> "StreamWrapper":
362
+ cls.orig_stream = orig_stream
363
+ return cls()
364
+
365
+ # compileall.compile_dir() needs stdout.encoding to print to stdout
366
+ # https://github.com/python/mypy/issues/4125
367
+ @property
368
+ def encoding(self): # type: ignore
369
+ return self.orig_stream.encoding
370
+
371
+
372
+ @contextlib.contextmanager
373
+ def captured_output(stream_name: str) -> Iterator[StreamWrapper]:
374
+ """Return a context manager used by captured_stdout/stdin/stderr
375
+ that temporarily replaces the sys stream *stream_name* with a StringIO.
376
+
377
+ Taken from Lib/support/__init__.py in the CPython repo.
378
+ """
379
+ orig_stdout = getattr(sys, stream_name)
380
+ setattr(sys, stream_name, StreamWrapper.from_stream(orig_stdout))
381
+ try:
382
+ yield getattr(sys, stream_name)
383
+ finally:
384
+ setattr(sys, stream_name, orig_stdout)
385
+
386
+
387
+ def captured_stdout() -> ContextManager[StreamWrapper]:
388
+ """Capture the output of sys.stdout:
389
+
390
+ with captured_stdout() as stdout:
391
+ print('hello')
392
+ self.assertEqual(stdout.getvalue(), 'hello\n')
393
+
394
+ Taken from Lib/support/__init__.py in the CPython repo.
395
+ """
396
+ return captured_output("stdout")
397
+
398
+
399
+ def captured_stderr() -> ContextManager[StreamWrapper]:
400
+ """
401
+ See captured_stdout().
402
+ """
403
+ return captured_output("stderr")
404
+
405
+
406
+ # Simulates an enum
407
+ def enum(*sequential: Any, **named: Any) -> Type[Any]:
408
+ enums = dict(zip(sequential, range(len(sequential))), **named)
409
+ reverse = {value: key for key, value in enums.items()}
410
+ enums["reverse_mapping"] = reverse
411
+ return type("Enum", (), enums)
412
+
413
+
414
+ def build_netloc(host: str, port: Optional[int]) -> str:
415
+ """
416
+ Build a netloc from a host-port pair
417
+ """
418
+ if port is None:
419
+ return host
420
+ if ":" in host:
421
+ # Only wrap host with square brackets when it is IPv6
422
+ host = f"[{host}]"
423
+ return f"{host}:{port}"
424
+
425
+
426
+ def build_url_from_netloc(netloc: str, scheme: str = "https") -> str:
427
+ """
428
+ Build a full URL from a netloc.
429
+ """
430
+ if netloc.count(":") >= 2 and "@" not in netloc and "[" not in netloc:
431
+ # It must be a bare IPv6 address, so wrap it with brackets.
432
+ netloc = f"[{netloc}]"
433
+ return f"{scheme}://{netloc}"
434
+
435
+
436
+ def parse_netloc(netloc: str) -> Tuple[str, Optional[int]]:
437
+ """
438
+ Return the host-port pair from a netloc.
439
+ """
440
+ url = build_url_from_netloc(netloc)
441
+ parsed = urllib.parse.urlparse(url)
442
+ return parsed.hostname, parsed.port
443
+
444
+
445
+ def split_auth_from_netloc(netloc: str) -> NetlocTuple:
446
+ """
447
+ Parse out and remove the auth information from a netloc.
448
+
449
+ Returns: (netloc, (username, password)).
450
+ """
451
+ if "@" not in netloc:
452
+ return netloc, (None, None)
453
+
454
+ # Split from the right because that's how urllib.parse.urlsplit()
455
+ # behaves if more than one @ is present (which can be checked using
456
+ # the password attribute of urlsplit()'s return value).
457
+ auth, netloc = netloc.rsplit("@", 1)
458
+ pw: Optional[str] = None
459
+ if ":" in auth:
460
+ # Split from the left because that's how urllib.parse.urlsplit()
461
+ # behaves if more than one : is present (which again can be checked
462
+ # using the password attribute of the return value)
463
+ user, pw = auth.split(":", 1)
464
+ else:
465
+ user, pw = auth, None
466
+
467
+ user = urllib.parse.unquote(user)
468
+ if pw is not None:
469
+ pw = urllib.parse.unquote(pw)
470
+
471
+ return netloc, (user, pw)
472
+
473
+
474
+ def redact_netloc(netloc: str) -> str:
475
+ """
476
+ Replace the sensitive data in a netloc with "****", if it exists.
477
+
478
+ For example:
479
+ - "user:pass@example.com" returns "user:****@example.com"
480
+ - "accesstoken@example.com" returns "****@example.com"
481
+ """
482
+ netloc, (user, password) = split_auth_from_netloc(netloc)
483
+ if user is None:
484
+ return netloc
485
+ if password is None:
486
+ user = "****"
487
+ password = ""
488
+ else:
489
+ user = urllib.parse.quote(user)
490
+ password = ":****"
491
+ return "{user}{password}@{netloc}".format(
492
+ user=user, password=password, netloc=netloc
493
+ )
494
+
495
+
496
+ def _transform_url(
497
+ url: str, transform_netloc: Callable[[str], Tuple[Any, ...]]
498
+ ) -> Tuple[str, NetlocTuple]:
499
+ """Transform and replace netloc in a url.
500
+
501
+ transform_netloc is a function taking the netloc and returning a
502
+ tuple. The first element of this tuple is the new netloc. The
503
+ entire tuple is returned.
504
+
505
+ Returns a tuple containing the transformed url as item 0 and the
506
+ original tuple returned by transform_netloc as item 1.
507
+ """
508
+ purl = urllib.parse.urlsplit(url)
509
+ netloc_tuple = transform_netloc(purl.netloc)
510
+ # stripped url
511
+ url_pieces = (purl.scheme, netloc_tuple[0], purl.path, purl.query, purl.fragment)
512
+ surl = urllib.parse.urlunsplit(url_pieces)
513
+ return surl, cast("NetlocTuple", netloc_tuple)
514
+
515
+
516
+ def _get_netloc(netloc: str) -> NetlocTuple:
517
+ return split_auth_from_netloc(netloc)
518
+
519
+
520
+ def _redact_netloc(netloc: str) -> Tuple[str]:
521
+ return (redact_netloc(netloc),)
522
+
523
+
524
+ def split_auth_netloc_from_url(url: str) -> Tuple[str, str, Tuple[str, str]]:
525
+ """
526
+ Parse a url into separate netloc, auth, and url with no auth.
527
+
528
+ Returns: (url_without_auth, netloc, (username, password))
529
+ """
530
+ url_without_auth, (netloc, auth) = _transform_url(url, _get_netloc)
531
+ return url_without_auth, netloc, auth
532
+
533
+
534
+ def remove_auth_from_url(url: str) -> str:
535
+ """Return a copy of url with 'username:password@' removed."""
536
+ # username/pass params are passed to subversion through flags
537
+ # and are not recognized in the url.
538
+ return _transform_url(url, _get_netloc)[0]
539
+
540
+
541
+ def redact_auth_from_url(url: str) -> str:
542
+ """Replace the password in a given url with ****."""
543
+ return _transform_url(url, _redact_netloc)[0]
544
+
545
+
546
+ class HiddenText:
547
+ def __init__(self, secret: str, redacted: str) -> None:
548
+ self.secret = secret
549
+ self.redacted = redacted
550
+
551
+ def __repr__(self) -> str:
552
+ return "<HiddenText {!r}>".format(str(self))
553
+
554
+ def __str__(self) -> str:
555
+ return self.redacted
556
+
557
+ # This is useful for testing.
558
+ def __eq__(self, other: Any) -> bool:
559
+ if type(self) != type(other):
560
+ return False
561
+
562
+ # The string being used for redaction doesn't also have to match,
563
+ # just the raw, original string.
564
+ return self.secret == other.secret
565
+
566
+
567
+ def hide_value(value: str) -> HiddenText:
568
+ return HiddenText(value, redacted="****")
569
+
570
+
571
+ def hide_url(url: str) -> HiddenText:
572
+ redacted = redact_auth_from_url(url)
573
+ return HiddenText(url, redacted=redacted)
574
+
575
+
576
+ def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None:
577
+ """Protection of pip.exe from modification on Windows
578
+
579
+ On Windows, any operation modifying pip should be run as:
580
+ python -m pip ...
581
+ """
582
+ pip_names = [
583
+ "pip.exe",
584
+ "pip{}.exe".format(sys.version_info[0]),
585
+ "pip{}.{}.exe".format(*sys.version_info[:2]),
586
+ ]
587
+
588
+ # See https://github.com/pypa/pip/issues/1299 for more discussion
589
+ should_show_use_python_msg = (
590
+ modifying_pip and WINDOWS and os.path.basename(sys.argv[0]) in pip_names
591
+ )
592
+
593
+ if should_show_use_python_msg:
594
+ new_command = [sys.executable, "-m", "pip"] + sys.argv[1:]
595
+ raise CommandError(
596
+ "To modify pip, please run the following command:\n{}".format(
597
+ " ".join(new_command)
598
+ )
599
+ )
600
+
601
+
602
+ def is_console_interactive() -> bool:
603
+ """Is this console interactive?"""
604
+ return sys.stdin is not None and sys.stdin.isatty()
605
+
606
+
607
+ def hash_file(path: str, blocksize: int = 1 << 20) -> Tuple[Any, int]:
608
+ """Return (hash, length) for path using hashlib.sha256()"""
609
+
610
+ h = hashlib.sha256()
611
+ length = 0
612
+ with open(path, "rb") as f:
613
+ for block in read_chunks(f, size=blocksize):
614
+ length += len(block)
615
+ h.update(block)
616
+ return h, length
617
+
618
+
619
+ def is_wheel_installed() -> bool:
620
+ """
621
+ Return whether the wheel package is installed.
622
+ """
623
+ try:
624
+ import wheel # noqa: F401
625
+ except ImportError:
626
+ return False
627
+
628
+ return True
629
+
630
+
631
+ def pairwise(iterable: Iterable[Any]) -> Iterator[Tuple[Any, Any]]:
632
+ """
633
+ Return paired elements.
634
+
635
+ For example:
636
+ s -> (s0, s1), (s2, s3), (s4, s5), ...
637
+ """
638
+ iterable = iter(iterable)
639
+ return zip_longest(iterable, iterable)
640
+
641
+
642
+ def partition(
643
+ pred: Callable[[T], bool],
644
+ iterable: Iterable[T],
645
+ ) -> Tuple[Iterable[T], Iterable[T]]:
646
+ """
647
+ Use a predicate to partition entries into false entries and true entries,
648
+ like
649
+
650
+ partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9
651
+ """
652
+ t1, t2 = tee(iterable)
653
+ return filterfalse(pred, t1), filter(pred, t2)
venv/lib/python3.10/site-packages/pip/_internal/utils/models.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utilities for defining models
2
+ """
3
+
4
+ import operator
5
+ from typing import Any, Callable, Type
6
+
7
+
8
+ class KeyBasedCompareMixin:
9
+ """Provides comparison capabilities that is based on a key"""
10
+
11
+ __slots__ = ["_compare_key", "_defining_class"]
12
+
13
+ def __init__(self, key: Any, defining_class: Type["KeyBasedCompareMixin"]) -> None:
14
+ self._compare_key = key
15
+ self._defining_class = defining_class
16
+
17
+ def __hash__(self) -> int:
18
+ return hash(self._compare_key)
19
+
20
+ def __lt__(self, other: Any) -> bool:
21
+ return self._compare(other, operator.__lt__)
22
+
23
+ def __le__(self, other: Any) -> bool:
24
+ return self._compare(other, operator.__le__)
25
+
26
+ def __gt__(self, other: Any) -> bool:
27
+ return self._compare(other, operator.__gt__)
28
+
29
+ def __ge__(self, other: Any) -> bool:
30
+ return self._compare(other, operator.__ge__)
31
+
32
+ def __eq__(self, other: Any) -> bool:
33
+ return self._compare(other, operator.__eq__)
34
+
35
+ def _compare(self, other: Any, method: Callable[[Any, Any], bool]) -> bool:
36
+ if not isinstance(other, self._defining_class):
37
+ return NotImplemented
38
+
39
+ return method(self._compare_key, other._compare_key)
venv/lib/python3.10/site-packages/pip/_internal/utils/packaging.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+ import logging
3
+ import re
4
+ from typing import NewType, Optional, Tuple, cast
5
+
6
+ from pip._vendor.packaging import specifiers, version
7
+ from pip._vendor.packaging.requirements import Requirement
8
+
9
+ NormalizedExtra = NewType("NormalizedExtra", str)
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def check_requires_python(
15
+ requires_python: Optional[str], version_info: Tuple[int, ...]
16
+ ) -> bool:
17
+ """
18
+ Check if the given Python version matches a "Requires-Python" specifier.
19
+
20
+ :param version_info: A 3-tuple of ints representing a Python
21
+ major-minor-micro version to check (e.g. `sys.version_info[:3]`).
22
+
23
+ :return: `True` if the given Python version satisfies the requirement.
24
+ Otherwise, return `False`.
25
+
26
+ :raises InvalidSpecifier: If `requires_python` has an invalid format.
27
+ """
28
+ if requires_python is None:
29
+ # The package provides no information
30
+ return True
31
+ requires_python_specifier = specifiers.SpecifierSet(requires_python)
32
+
33
+ python_version = version.parse(".".join(map(str, version_info)))
34
+ return python_version in requires_python_specifier
35
+
36
+
37
+ @functools.lru_cache(maxsize=512)
38
+ def get_requirement(req_string: str) -> Requirement:
39
+ """Construct a packaging.Requirement object with caching"""
40
+ # Parsing requirement strings is expensive, and is also expected to happen
41
+ # with a low diversity of different arguments (at least relative the number
42
+ # constructed). This method adds a cache to requirement object creation to
43
+ # minimize repeated parsing of the same string to construct equivalent
44
+ # Requirement objects.
45
+ return Requirement(req_string)
46
+
47
+
48
+ def safe_extra(extra: str) -> NormalizedExtra:
49
+ """Convert an arbitrary string to a standard 'extra' name
50
+
51
+ Any runs of non-alphanumeric characters are replaced with a single '_',
52
+ and the result is always lowercased.
53
+
54
+ This function is duplicated from ``pkg_resources``. Note that this is not
55
+ the same to either ``canonicalize_name`` or ``_egg_link_name``.
56
+ """
57
+ return cast(NormalizedExtra, re.sub("[^A-Za-z0-9.-]+", "_", extra).lower())
venv/lib/python3.10/site-packages/pip/_internal/utils/setuptools_build.py ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import textwrap
3
+ from typing import List, Optional, Sequence
4
+
5
+ # Shim to wrap setup.py invocation with setuptools
6
+ # Note that __file__ is handled via two {!r} *and* %r, to ensure that paths on
7
+ # Windows are correctly handled (it should be "C:\\Users" not "C:\Users").
8
+ _SETUPTOOLS_SHIM = textwrap.dedent(
9
+ """
10
+ exec(compile('''
11
+ # This is <pip-setuptools-caller> -- a caller that pip uses to run setup.py
12
+ #
13
+ # - It imports setuptools before invoking setup.py, to enable projects that directly
14
+ # import from `distutils.core` to work with newer packaging standards.
15
+ # - It provides a clear error message when setuptools is not installed.
16
+ # - It sets `sys.argv[0]` to the underlying `setup.py`, when invoking `setup.py` so
17
+ # setuptools doesn't think the script is `-c`. This avoids the following warning:
18
+ # manifest_maker: standard file '-c' not found".
19
+ # - It generates a shim setup.py, for handling setup.cfg-only projects.
20
+ import os, sys, tokenize
21
+
22
+ try:
23
+ import setuptools
24
+ except ImportError as error:
25
+ print(
26
+ "ERROR: Can not execute `setup.py` since setuptools is not available in "
27
+ "the build environment.",
28
+ file=sys.stderr,
29
+ )
30
+ sys.exit(1)
31
+
32
+ __file__ = %r
33
+ sys.argv[0] = __file__
34
+
35
+ if os.path.exists(__file__):
36
+ filename = __file__
37
+ with tokenize.open(__file__) as f:
38
+ setup_py_code = f.read()
39
+ else:
40
+ filename = "<auto-generated setuptools caller>"
41
+ setup_py_code = "from setuptools import setup; setup()"
42
+
43
+ exec(compile(setup_py_code, filename, "exec"))
44
+ ''' % ({!r},), "<pip-setuptools-caller>", "exec"))
45
+ """
46
+ ).rstrip()
47
+
48
+
49
+ def make_setuptools_shim_args(
50
+ setup_py_path: str,
51
+ global_options: Sequence[str] = None,
52
+ no_user_config: bool = False,
53
+ unbuffered_output: bool = False,
54
+ ) -> List[str]:
55
+ """
56
+ Get setuptools command arguments with shim wrapped setup file invocation.
57
+
58
+ :param setup_py_path: The path to setup.py to be wrapped.
59
+ :param global_options: Additional global options.
60
+ :param no_user_config: If True, disables personal user configuration.
61
+ :param unbuffered_output: If True, adds the unbuffered switch to the
62
+ argument list.
63
+ """
64
+ args = [sys.executable]
65
+ if unbuffered_output:
66
+ args += ["-u"]
67
+ args += ["-c", _SETUPTOOLS_SHIM.format(setup_py_path)]
68
+ if global_options:
69
+ args += global_options
70
+ if no_user_config:
71
+ args += ["--no-user-cfg"]
72
+ return args
73
+
74
+
75
+ def make_setuptools_bdist_wheel_args(
76
+ setup_py_path: str,
77
+ global_options: Sequence[str],
78
+ build_options: Sequence[str],
79
+ destination_dir: str,
80
+ ) -> List[str]:
81
+ # NOTE: Eventually, we'd want to also -S to the flags here, when we're
82
+ # isolating. Currently, it breaks Python in virtualenvs, because it
83
+ # relies on site.py to find parts of the standard library outside the
84
+ # virtualenv.
85
+ args = make_setuptools_shim_args(
86
+ setup_py_path, global_options=global_options, unbuffered_output=True
87
+ )
88
+ args += ["bdist_wheel", "-d", destination_dir]
89
+ args += build_options
90
+ return args
91
+
92
+
93
+ def make_setuptools_clean_args(
94
+ setup_py_path: str,
95
+ global_options: Sequence[str],
96
+ ) -> List[str]:
97
+ args = make_setuptools_shim_args(
98
+ setup_py_path, global_options=global_options, unbuffered_output=True
99
+ )
100
+ args += ["clean", "--all"]
101
+ return args
102
+
103
+
104
+ def make_setuptools_develop_args(
105
+ setup_py_path: str,
106
+ global_options: Sequence[str],
107
+ install_options: Sequence[str],
108
+ no_user_config: bool,
109
+ prefix: Optional[str],
110
+ home: Optional[str],
111
+ use_user_site: bool,
112
+ ) -> List[str]:
113
+ assert not (use_user_site and prefix)
114
+
115
+ args = make_setuptools_shim_args(
116
+ setup_py_path,
117
+ global_options=global_options,
118
+ no_user_config=no_user_config,
119
+ )
120
+
121
+ args += ["develop", "--no-deps"]
122
+
123
+ args += install_options
124
+
125
+ if prefix:
126
+ args += ["--prefix", prefix]
127
+ if home is not None:
128
+ args += ["--install-dir", home]
129
+
130
+ if use_user_site:
131
+ args += ["--user", "--prefix="]
132
+
133
+ return args
134
+
135
+
136
+ def make_setuptools_egg_info_args(
137
+ setup_py_path: str,
138
+ egg_info_dir: Optional[str],
139
+ no_user_config: bool,
140
+ ) -> List[str]:
141
+ args = make_setuptools_shim_args(setup_py_path, no_user_config=no_user_config)
142
+
143
+ args += ["egg_info"]
144
+
145
+ if egg_info_dir:
146
+ args += ["--egg-base", egg_info_dir]
147
+
148
+ return args
149
+
150
+
151
+ def make_setuptools_install_args(
152
+ setup_py_path: str,
153
+ global_options: Sequence[str],
154
+ install_options: Sequence[str],
155
+ record_filename: str,
156
+ root: Optional[str],
157
+ prefix: Optional[str],
158
+ header_dir: Optional[str],
159
+ home: Optional[str],
160
+ use_user_site: bool,
161
+ no_user_config: bool,
162
+ pycompile: bool,
163
+ ) -> List[str]:
164
+ assert not (use_user_site and prefix)
165
+ assert not (use_user_site and root)
166
+
167
+ args = make_setuptools_shim_args(
168
+ setup_py_path,
169
+ global_options=global_options,
170
+ no_user_config=no_user_config,
171
+ unbuffered_output=True,
172
+ )
173
+ args += ["install", "--record", record_filename]
174
+ args += ["--single-version-externally-managed"]
175
+
176
+ if root is not None:
177
+ args += ["--root", root]
178
+ if prefix is not None:
179
+ args += ["--prefix", prefix]
180
+ if home is not None:
181
+ args += ["--home", home]
182
+ if use_user_site:
183
+ args += ["--user", "--prefix="]
184
+
185
+ if pycompile:
186
+ args += ["--compile"]
187
+ else:
188
+ args += ["--no-compile"]
189
+
190
+ if header_dir:
191
+ args += ["--install-headers", header_dir]
192
+
193
+ args += install_options
194
+
195
+ return args
venv/lib/python3.10/site-packages/pip/_internal/utils/subprocess.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ import shlex
4
+ import subprocess
5
+ from typing import (
6
+ TYPE_CHECKING,
7
+ Any,
8
+ Callable,
9
+ Iterable,
10
+ List,
11
+ Mapping,
12
+ Optional,
13
+ Union,
14
+ )
15
+
16
+ from pip._vendor.rich.markup import escape
17
+
18
+ from pip._internal.cli.spinners import SpinnerInterface, open_spinner
19
+ from pip._internal.exceptions import InstallationSubprocessError
20
+ from pip._internal.utils.logging import VERBOSE, subprocess_logger
21
+ from pip._internal.utils.misc import HiddenText
22
+
23
+ if TYPE_CHECKING:
24
+ # Literal was introduced in Python 3.8.
25
+ #
26
+ # TODO: Remove `if TYPE_CHECKING` when dropping support for Python 3.7.
27
+ from typing import Literal
28
+
29
+ CommandArgs = List[Union[str, HiddenText]]
30
+
31
+
32
+ def make_command(*args: Union[str, HiddenText, CommandArgs]) -> CommandArgs:
33
+ """
34
+ Create a CommandArgs object.
35
+ """
36
+ command_args: CommandArgs = []
37
+ for arg in args:
38
+ # Check for list instead of CommandArgs since CommandArgs is
39
+ # only known during type-checking.
40
+ if isinstance(arg, list):
41
+ command_args.extend(arg)
42
+ else:
43
+ # Otherwise, arg is str or HiddenText.
44
+ command_args.append(arg)
45
+
46
+ return command_args
47
+
48
+
49
+ def format_command_args(args: Union[List[str], CommandArgs]) -> str:
50
+ """
51
+ Format command arguments for display.
52
+ """
53
+ # For HiddenText arguments, display the redacted form by calling str().
54
+ # Also, we don't apply str() to arguments that aren't HiddenText since
55
+ # this can trigger a UnicodeDecodeError in Python 2 if the argument
56
+ # has type unicode and includes a non-ascii character. (The type
57
+ # checker doesn't ensure the annotations are correct in all cases.)
58
+ return " ".join(
59
+ shlex.quote(str(arg)) if isinstance(arg, HiddenText) else shlex.quote(arg)
60
+ for arg in args
61
+ )
62
+
63
+
64
+ def reveal_command_args(args: Union[List[str], CommandArgs]) -> List[str]:
65
+ """
66
+ Return the arguments in their raw, unredacted form.
67
+ """
68
+ return [arg.secret if isinstance(arg, HiddenText) else arg for arg in args]
69
+
70
+
71
+ def call_subprocess(
72
+ cmd: Union[List[str], CommandArgs],
73
+ show_stdout: bool = False,
74
+ cwd: Optional[str] = None,
75
+ on_returncode: 'Literal["raise", "warn", "ignore"]' = "raise",
76
+ extra_ok_returncodes: Optional[Iterable[int]] = None,
77
+ extra_environ: Optional[Mapping[str, Any]] = None,
78
+ unset_environ: Optional[Iterable[str]] = None,
79
+ spinner: Optional[SpinnerInterface] = None,
80
+ log_failed_cmd: Optional[bool] = True,
81
+ stdout_only: Optional[bool] = False,
82
+ *,
83
+ command_desc: str,
84
+ ) -> str:
85
+ """
86
+ Args:
87
+ show_stdout: if true, use INFO to log the subprocess's stderr and
88
+ stdout streams. Otherwise, use DEBUG. Defaults to False.
89
+ extra_ok_returncodes: an iterable of integer return codes that are
90
+ acceptable, in addition to 0. Defaults to None, which means [].
91
+ unset_environ: an iterable of environment variable names to unset
92
+ prior to calling subprocess.Popen().
93
+ log_failed_cmd: if false, failed commands are not logged, only raised.
94
+ stdout_only: if true, return only stdout, else return both. When true,
95
+ logging of both stdout and stderr occurs when the subprocess has
96
+ terminated, else logging occurs as subprocess output is produced.
97
+ """
98
+ if extra_ok_returncodes is None:
99
+ extra_ok_returncodes = []
100
+ if unset_environ is None:
101
+ unset_environ = []
102
+ # Most places in pip use show_stdout=False. What this means is--
103
+ #
104
+ # - We connect the child's output (combined stderr and stdout) to a
105
+ # single pipe, which we read.
106
+ # - We log this output to stderr at DEBUG level as it is received.
107
+ # - If DEBUG logging isn't enabled (e.g. if --verbose logging wasn't
108
+ # requested), then we show a spinner so the user can still see the
109
+ # subprocess is in progress.
110
+ # - If the subprocess exits with an error, we log the output to stderr
111
+ # at ERROR level if it hasn't already been displayed to the console
112
+ # (e.g. if --verbose logging wasn't enabled). This way we don't log
113
+ # the output to the console twice.
114
+ #
115
+ # If show_stdout=True, then the above is still done, but with DEBUG
116
+ # replaced by INFO.
117
+ if show_stdout:
118
+ # Then log the subprocess output at INFO level.
119
+ log_subprocess = subprocess_logger.info
120
+ used_level = logging.INFO
121
+ else:
122
+ # Then log the subprocess output using VERBOSE. This also ensures
123
+ # it will be logged to the log file (aka user_log), if enabled.
124
+ log_subprocess = subprocess_logger.verbose
125
+ used_level = VERBOSE
126
+
127
+ # Whether the subprocess will be visible in the console.
128
+ showing_subprocess = subprocess_logger.getEffectiveLevel() <= used_level
129
+
130
+ # Only use the spinner if we're not showing the subprocess output
131
+ # and we have a spinner.
132
+ use_spinner = not showing_subprocess and spinner is not None
133
+
134
+ log_subprocess("Running command %s", command_desc)
135
+ env = os.environ.copy()
136
+ if extra_environ:
137
+ env.update(extra_environ)
138
+ for name in unset_environ:
139
+ env.pop(name, None)
140
+ try:
141
+ proc = subprocess.Popen(
142
+ # Convert HiddenText objects to the underlying str.
143
+ reveal_command_args(cmd),
144
+ stdin=subprocess.PIPE,
145
+ stdout=subprocess.PIPE,
146
+ stderr=subprocess.STDOUT if not stdout_only else subprocess.PIPE,
147
+ cwd=cwd,
148
+ env=env,
149
+ errors="backslashreplace",
150
+ )
151
+ except Exception as exc:
152
+ if log_failed_cmd:
153
+ subprocess_logger.critical(
154
+ "Error %s while executing command %s",
155
+ exc,
156
+ command_desc,
157
+ )
158
+ raise
159
+ all_output = []
160
+ if not stdout_only:
161
+ assert proc.stdout
162
+ assert proc.stdin
163
+ proc.stdin.close()
164
+ # In this mode, stdout and stderr are in the same pipe.
165
+ while True:
166
+ line: str = proc.stdout.readline()
167
+ if not line:
168
+ break
169
+ line = line.rstrip()
170
+ all_output.append(line + "\n")
171
+
172
+ # Show the line immediately.
173
+ log_subprocess(line)
174
+ # Update the spinner.
175
+ if use_spinner:
176
+ assert spinner
177
+ spinner.spin()
178
+ try:
179
+ proc.wait()
180
+ finally:
181
+ if proc.stdout:
182
+ proc.stdout.close()
183
+ output = "".join(all_output)
184
+ else:
185
+ # In this mode, stdout and stderr are in different pipes.
186
+ # We must use communicate() which is the only safe way to read both.
187
+ out, err = proc.communicate()
188
+ # log line by line to preserve pip log indenting
189
+ for out_line in out.splitlines():
190
+ log_subprocess(out_line)
191
+ all_output.append(out)
192
+ for err_line in err.splitlines():
193
+ log_subprocess(err_line)
194
+ all_output.append(err)
195
+ output = out
196
+
197
+ proc_had_error = proc.returncode and proc.returncode not in extra_ok_returncodes
198
+ if use_spinner:
199
+ assert spinner
200
+ if proc_had_error:
201
+ spinner.finish("error")
202
+ else:
203
+ spinner.finish("done")
204
+ if proc_had_error:
205
+ if on_returncode == "raise":
206
+ error = InstallationSubprocessError(
207
+ command_description=command_desc,
208
+ exit_code=proc.returncode,
209
+ output_lines=all_output if not showing_subprocess else None,
210
+ )
211
+ if log_failed_cmd:
212
+ subprocess_logger.error("[present-diagnostic] %s", error)
213
+ subprocess_logger.verbose(
214
+ "[bold magenta]full command[/]: [blue]%s[/]",
215
+ escape(format_command_args(cmd)),
216
+ extra={"markup": True},
217
+ )
218
+ subprocess_logger.verbose(
219
+ "[bold magenta]cwd[/]: %s",
220
+ escape(cwd or "[inherit]"),
221
+ extra={"markup": True},
222
+ )
223
+
224
+ raise error
225
+ elif on_returncode == "warn":
226
+ subprocess_logger.warning(
227
+ 'Command "%s" had error code %s in %s',
228
+ command_desc,
229
+ proc.returncode,
230
+ cwd,
231
+ )
232
+ elif on_returncode == "ignore":
233
+ pass
234
+ else:
235
+ raise ValueError(f"Invalid value: on_returncode={on_returncode!r}")
236
+ return output
237
+
238
+
239
+ def runner_with_spinner_message(message: str) -> Callable[..., None]:
240
+ """Provide a subprocess_runner that shows a spinner message.
241
+
242
+ Intended for use with for pep517's Pep517HookCaller. Thus, the runner has
243
+ an API that matches what's expected by Pep517HookCaller.subprocess_runner.
244
+ """
245
+
246
+ def runner(
247
+ cmd: List[str],
248
+ cwd: Optional[str] = None,
249
+ extra_environ: Optional[Mapping[str, Any]] = None,
250
+ ) -> None:
251
+ with open_spinner(message) as spinner:
252
+ call_subprocess(
253
+ cmd,
254
+ command_desc=message,
255
+ cwd=cwd,
256
+ extra_environ=extra_environ,
257
+ spinner=spinner,
258
+ )
259
+
260
+ return runner
venv/lib/python3.10/site-packages/pip/_internal/utils/temp_dir.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import errno
2
+ import itertools
3
+ import logging
4
+ import os.path
5
+ import tempfile
6
+ from contextlib import ExitStack, contextmanager
7
+ from typing import Any, Dict, Iterator, Optional, TypeVar, Union
8
+
9
+ from pip._internal.utils.misc import enum, rmtree
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ _T = TypeVar("_T", bound="TempDirectory")
14
+
15
+
16
+ # Kinds of temporary directories. Only needed for ones that are
17
+ # globally-managed.
18
+ tempdir_kinds = enum(
19
+ BUILD_ENV="build-env",
20
+ EPHEM_WHEEL_CACHE="ephem-wheel-cache",
21
+ REQ_BUILD="req-build",
22
+ )
23
+
24
+
25
+ _tempdir_manager: Optional[ExitStack] = None
26
+
27
+
28
+ @contextmanager
29
+ def global_tempdir_manager() -> Iterator[None]:
30
+ global _tempdir_manager
31
+ with ExitStack() as stack:
32
+ old_tempdir_manager, _tempdir_manager = _tempdir_manager, stack
33
+ try:
34
+ yield
35
+ finally:
36
+ _tempdir_manager = old_tempdir_manager
37
+
38
+
39
+ class TempDirectoryTypeRegistry:
40
+ """Manages temp directory behavior"""
41
+
42
+ def __init__(self) -> None:
43
+ self._should_delete: Dict[str, bool] = {}
44
+
45
+ def set_delete(self, kind: str, value: bool) -> None:
46
+ """Indicate whether a TempDirectory of the given kind should be
47
+ auto-deleted.
48
+ """
49
+ self._should_delete[kind] = value
50
+
51
+ def get_delete(self, kind: str) -> bool:
52
+ """Get configured auto-delete flag for a given TempDirectory type,
53
+ default True.
54
+ """
55
+ return self._should_delete.get(kind, True)
56
+
57
+
58
+ _tempdir_registry: Optional[TempDirectoryTypeRegistry] = None
59
+
60
+
61
+ @contextmanager
62
+ def tempdir_registry() -> Iterator[TempDirectoryTypeRegistry]:
63
+ """Provides a scoped global tempdir registry that can be used to dictate
64
+ whether directories should be deleted.
65
+ """
66
+ global _tempdir_registry
67
+ old_tempdir_registry = _tempdir_registry
68
+ _tempdir_registry = TempDirectoryTypeRegistry()
69
+ try:
70
+ yield _tempdir_registry
71
+ finally:
72
+ _tempdir_registry = old_tempdir_registry
73
+
74
+
75
+ class _Default:
76
+ pass
77
+
78
+
79
+ _default = _Default()
80
+
81
+
82
+ class TempDirectory:
83
+ """Helper class that owns and cleans up a temporary directory.
84
+
85
+ This class can be used as a context manager or as an OO representation of a
86
+ temporary directory.
87
+
88
+ Attributes:
89
+ path
90
+ Location to the created temporary directory
91
+ delete
92
+ Whether the directory should be deleted when exiting
93
+ (when used as a contextmanager)
94
+
95
+ Methods:
96
+ cleanup()
97
+ Deletes the temporary directory
98
+
99
+ When used as a context manager, if the delete attribute is True, on
100
+ exiting the context the temporary directory is deleted.
101
+ """
102
+
103
+ def __init__(
104
+ self,
105
+ path: Optional[str] = None,
106
+ delete: Union[bool, None, _Default] = _default,
107
+ kind: str = "temp",
108
+ globally_managed: bool = False,
109
+ ):
110
+ super().__init__()
111
+
112
+ if delete is _default:
113
+ if path is not None:
114
+ # If we were given an explicit directory, resolve delete option
115
+ # now.
116
+ delete = False
117
+ else:
118
+ # Otherwise, we wait until cleanup and see what
119
+ # tempdir_registry says.
120
+ delete = None
121
+
122
+ # The only time we specify path is in for editables where it
123
+ # is the value of the --src option.
124
+ if path is None:
125
+ path = self._create(kind)
126
+
127
+ self._path = path
128
+ self._deleted = False
129
+ self.delete = delete
130
+ self.kind = kind
131
+
132
+ if globally_managed:
133
+ assert _tempdir_manager is not None
134
+ _tempdir_manager.enter_context(self)
135
+
136
+ @property
137
+ def path(self) -> str:
138
+ assert not self._deleted, f"Attempted to access deleted path: {self._path}"
139
+ return self._path
140
+
141
+ def __repr__(self) -> str:
142
+ return f"<{self.__class__.__name__} {self.path!r}>"
143
+
144
+ def __enter__(self: _T) -> _T:
145
+ return self
146
+
147
+ def __exit__(self, exc: Any, value: Any, tb: Any) -> None:
148
+ if self.delete is not None:
149
+ delete = self.delete
150
+ elif _tempdir_registry:
151
+ delete = _tempdir_registry.get_delete(self.kind)
152
+ else:
153
+ delete = True
154
+
155
+ if delete:
156
+ self.cleanup()
157
+
158
+ def _create(self, kind: str) -> str:
159
+ """Create a temporary directory and store its path in self.path"""
160
+ # We realpath here because some systems have their default tmpdir
161
+ # symlinked to another directory. This tends to confuse build
162
+ # scripts, so we canonicalize the path by traversing potential
163
+ # symlinks here.
164
+ path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-"))
165
+ logger.debug("Created temporary directory: %s", path)
166
+ return path
167
+
168
+ def cleanup(self) -> None:
169
+ """Remove the temporary directory created and reset state"""
170
+ self._deleted = True
171
+ if not os.path.exists(self._path):
172
+ return
173
+ rmtree(self._path)
174
+
175
+
176
+ class AdjacentTempDirectory(TempDirectory):
177
+ """Helper class that creates a temporary directory adjacent to a real one.
178
+
179
+ Attributes:
180
+ original
181
+ The original directory to create a temp directory for.
182
+ path
183
+ After calling create() or entering, contains the full
184
+ path to the temporary directory.
185
+ delete
186
+ Whether the directory should be deleted when exiting
187
+ (when used as a contextmanager)
188
+
189
+ """
190
+
191
+ # The characters that may be used to name the temp directory
192
+ # We always prepend a ~ and then rotate through these until
193
+ # a usable name is found.
194
+ # pkg_resources raises a different error for .dist-info folder
195
+ # with leading '-' and invalid metadata
196
+ LEADING_CHARS = "-~.=%0123456789"
197
+
198
+ def __init__(self, original: str, delete: Optional[bool] = None) -> None:
199
+ self.original = original.rstrip("/\\")
200
+ super().__init__(delete=delete)
201
+
202
+ @classmethod
203
+ def _generate_names(cls, name: str) -> Iterator[str]:
204
+ """Generates a series of temporary names.
205
+
206
+ The algorithm replaces the leading characters in the name
207
+ with ones that are valid filesystem characters, but are not
208
+ valid package names (for both Python and pip definitions of
209
+ package).
210
+ """
211
+ for i in range(1, len(name)):
212
+ for candidate in itertools.combinations_with_replacement(
213
+ cls.LEADING_CHARS, i - 1
214
+ ):
215
+ new_name = "~" + "".join(candidate) + name[i:]
216
+ if new_name != name:
217
+ yield new_name
218
+
219
+ # If we make it this far, we will have to make a longer name
220
+ for i in range(len(cls.LEADING_CHARS)):
221
+ for candidate in itertools.combinations_with_replacement(
222
+ cls.LEADING_CHARS, i
223
+ ):
224
+ new_name = "~" + "".join(candidate) + name
225
+ if new_name != name:
226
+ yield new_name
227
+
228
+ def _create(self, kind: str) -> str:
229
+ root, name = os.path.split(self.original)
230
+ for candidate in self._generate_names(name):
231
+ path = os.path.join(root, candidate)
232
+ try:
233
+ os.mkdir(path)
234
+ except OSError as ex:
235
+ # Continue if the name exists already
236
+ if ex.errno != errno.EEXIST:
237
+ raise
238
+ else:
239
+ path = os.path.realpath(path)
240
+ break
241
+ else:
242
+ # Final fallback on the default behavior.
243
+ path = os.path.realpath(tempfile.mkdtemp(prefix=f"pip-{kind}-"))
244
+
245
+ logger.debug("Created temporary directory: %s", path)
246
+ return path
venv/lib/python3.10/site-packages/pip/_internal/utils/unpacking.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Utilities related archives.
2
+ """
3
+
4
+ import logging
5
+ import os
6
+ import shutil
7
+ import stat
8
+ import tarfile
9
+ import zipfile
10
+ from typing import Iterable, List, Optional
11
+ from zipfile import ZipInfo
12
+
13
+ from pip._internal.exceptions import InstallationError
14
+ from pip._internal.utils.filetypes import (
15
+ BZ2_EXTENSIONS,
16
+ TAR_EXTENSIONS,
17
+ XZ_EXTENSIONS,
18
+ ZIP_EXTENSIONS,
19
+ )
20
+ from pip._internal.utils.misc import ensure_dir
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ SUPPORTED_EXTENSIONS = ZIP_EXTENSIONS + TAR_EXTENSIONS
26
+
27
+ try:
28
+ import bz2 # noqa
29
+
30
+ SUPPORTED_EXTENSIONS += BZ2_EXTENSIONS
31
+ except ImportError:
32
+ logger.debug("bz2 module is not available")
33
+
34
+ try:
35
+ # Only for Python 3.3+
36
+ import lzma # noqa
37
+
38
+ SUPPORTED_EXTENSIONS += XZ_EXTENSIONS
39
+ except ImportError:
40
+ logger.debug("lzma module is not available")
41
+
42
+
43
+ def current_umask() -> int:
44
+ """Get the current umask which involves having to set it temporarily."""
45
+ mask = os.umask(0)
46
+ os.umask(mask)
47
+ return mask
48
+
49
+
50
+ def split_leading_dir(path: str) -> List[str]:
51
+ path = path.lstrip("/").lstrip("\\")
52
+ if "/" in path and (
53
+ ("\\" in path and path.find("/") < path.find("\\")) or "\\" not in path
54
+ ):
55
+ return path.split("/", 1)
56
+ elif "\\" in path:
57
+ return path.split("\\", 1)
58
+ else:
59
+ return [path, ""]
60
+
61
+
62
+ def has_leading_dir(paths: Iterable[str]) -> bool:
63
+ """Returns true if all the paths have the same leading path name
64
+ (i.e., everything is in one subdirectory in an archive)"""
65
+ common_prefix = None
66
+ for path in paths:
67
+ prefix, rest = split_leading_dir(path)
68
+ if not prefix:
69
+ return False
70
+ elif common_prefix is None:
71
+ common_prefix = prefix
72
+ elif prefix != common_prefix:
73
+ return False
74
+ return True
75
+
76
+
77
+ def is_within_directory(directory: str, target: str) -> bool:
78
+ """
79
+ Return true if the absolute path of target is within the directory
80
+ """
81
+ abs_directory = os.path.abspath(directory)
82
+ abs_target = os.path.abspath(target)
83
+
84
+ prefix = os.path.commonprefix([abs_directory, abs_target])
85
+ return prefix == abs_directory
86
+
87
+
88
+ def set_extracted_file_to_default_mode_plus_executable(path: str) -> None:
89
+ """
90
+ Make file present at path have execute for user/group/world
91
+ (chmod +x) is no-op on windows per python docs
92
+ """
93
+ os.chmod(path, (0o777 & ~current_umask() | 0o111))
94
+
95
+
96
+ def zip_item_is_executable(info: ZipInfo) -> bool:
97
+ mode = info.external_attr >> 16
98
+ # if mode and regular file and any execute permissions for
99
+ # user/group/world?
100
+ return bool(mode and stat.S_ISREG(mode) and mode & 0o111)
101
+
102
+
103
+ def unzip_file(filename: str, location: str, flatten: bool = True) -> None:
104
+ """
105
+ Unzip the file (with path `filename`) to the destination `location`. All
106
+ files are written based on system defaults and umask (i.e. permissions are
107
+ not preserved), except that regular file members with any execute
108
+ permissions (user, group, or world) have "chmod +x" applied after being
109
+ written. Note that for windows, any execute changes using os.chmod are
110
+ no-ops per the python docs.
111
+ """
112
+ ensure_dir(location)
113
+ zipfp = open(filename, "rb")
114
+ try:
115
+ zip = zipfile.ZipFile(zipfp, allowZip64=True)
116
+ leading = has_leading_dir(zip.namelist()) and flatten
117
+ for info in zip.infolist():
118
+ name = info.filename
119
+ fn = name
120
+ if leading:
121
+ fn = split_leading_dir(name)[1]
122
+ fn = os.path.join(location, fn)
123
+ dir = os.path.dirname(fn)
124
+ if not is_within_directory(location, fn):
125
+ message = (
126
+ "The zip file ({}) has a file ({}) trying to install "
127
+ "outside target directory ({})"
128
+ )
129
+ raise InstallationError(message.format(filename, fn, location))
130
+ if fn.endswith("/") or fn.endswith("\\"):
131
+ # A directory
132
+ ensure_dir(fn)
133
+ else:
134
+ ensure_dir(dir)
135
+ # Don't use read() to avoid allocating an arbitrarily large
136
+ # chunk of memory for the file's content
137
+ fp = zip.open(name)
138
+ try:
139
+ with open(fn, "wb") as destfp:
140
+ shutil.copyfileobj(fp, destfp)
141
+ finally:
142
+ fp.close()
143
+ if zip_item_is_executable(info):
144
+ set_extracted_file_to_default_mode_plus_executable(fn)
145
+ finally:
146
+ zipfp.close()
147
+
148
+
149
+ def untar_file(filename: str, location: str) -> None:
150
+ """
151
+ Untar the file (with path `filename`) to the destination `location`.
152
+ All files are written based on system defaults and umask (i.e. permissions
153
+ are not preserved), except that regular file members with any execute
154
+ permissions (user, group, or world) have "chmod +x" applied after being
155
+ written. Note that for windows, any execute changes using os.chmod are
156
+ no-ops per the python docs.
157
+ """
158
+ ensure_dir(location)
159
+ if filename.lower().endswith(".gz") or filename.lower().endswith(".tgz"):
160
+ mode = "r:gz"
161
+ elif filename.lower().endswith(BZ2_EXTENSIONS):
162
+ mode = "r:bz2"
163
+ elif filename.lower().endswith(XZ_EXTENSIONS):
164
+ mode = "r:xz"
165
+ elif filename.lower().endswith(".tar"):
166
+ mode = "r"
167
+ else:
168
+ logger.warning(
169
+ "Cannot determine compression type for file %s",
170
+ filename,
171
+ )
172
+ mode = "r:*"
173
+ tar = tarfile.open(filename, mode, encoding="utf-8")
174
+ try:
175
+ leading = has_leading_dir([member.name for member in tar.getmembers()])
176
+ for member in tar.getmembers():
177
+ fn = member.name
178
+ if leading:
179
+ fn = split_leading_dir(fn)[1]
180
+ path = os.path.join(location, fn)
181
+ if not is_within_directory(location, path):
182
+ message = (
183
+ "The tar file ({}) has a file ({}) trying to install "
184
+ "outside target directory ({})"
185
+ )
186
+ raise InstallationError(message.format(filename, path, location))
187
+ if member.isdir():
188
+ ensure_dir(path)
189
+ elif member.issym():
190
+ try:
191
+ # https://github.com/python/typeshed/issues/2673
192
+ tar._extract_member(member, path) # type: ignore
193
+ except Exception as exc:
194
+ # Some corrupt tar files seem to produce this
195
+ # (specifically bad symlinks)
196
+ logger.warning(
197
+ "In the tar file %s the member %s is invalid: %s",
198
+ filename,
199
+ member.name,
200
+ exc,
201
+ )
202
+ continue
203
+ else:
204
+ try:
205
+ fp = tar.extractfile(member)
206
+ except (KeyError, AttributeError) as exc:
207
+ # Some corrupt tar files seem to produce this
208
+ # (specifically bad symlinks)
209
+ logger.warning(
210
+ "In the tar file %s the member %s is invalid: %s",
211
+ filename,
212
+ member.name,
213
+ exc,
214
+ )
215
+ continue
216
+ ensure_dir(os.path.dirname(path))
217
+ assert fp is not None
218
+ with open(path, "wb") as destfp:
219
+ shutil.copyfileobj(fp, destfp)
220
+ fp.close()
221
+ # Update the timestamp (useful for cython compiled files)
222
+ tar.utime(member, path)
223
+ # member have any execute permissions for user/group/world?
224
+ if member.mode & 0o111:
225
+ set_extracted_file_to_default_mode_plus_executable(path)
226
+ finally:
227
+ tar.close()
228
+
229
+
230
+ def unpack_file(
231
+ filename: str,
232
+ location: str,
233
+ content_type: Optional[str] = None,
234
+ ) -> None:
235
+ filename = os.path.realpath(filename)
236
+ if (
237
+ content_type == "application/zip"
238
+ or filename.lower().endswith(ZIP_EXTENSIONS)
239
+ or zipfile.is_zipfile(filename)
240
+ ):
241
+ unzip_file(filename, location, flatten=not filename.endswith(".whl"))
242
+ elif (
243
+ content_type == "application/x-gzip"
244
+ or tarfile.is_tarfile(filename)
245
+ or filename.lower().endswith(TAR_EXTENSIONS + BZ2_EXTENSIONS + XZ_EXTENSIONS)
246
+ ):
247
+ untar_file(filename, location)
248
+ else:
249
+ # FIXME: handle?
250
+ # FIXME: magic signatures?
251
+ logger.critical(
252
+ "Cannot unpack file %s (downloaded from %s, content-type: %s); "
253
+ "cannot detect archive format",
254
+ filename,
255
+ location,
256
+ content_type,
257
+ )
258
+ raise InstallationError(f"Cannot determine archive format of {location}")
venv/lib/python3.10/site-packages/pip/_internal/utils/urls.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import string
3
+ import urllib.parse
4
+ import urllib.request
5
+ from typing import Optional
6
+
7
+ from .compat import WINDOWS
8
+
9
+
10
+ def get_url_scheme(url: str) -> Optional[str]:
11
+ if ":" not in url:
12
+ return None
13
+ return url.split(":", 1)[0].lower()
14
+
15
+
16
+ def path_to_url(path: str) -> str:
17
+ """
18
+ Convert a path to a file: URL. The path will be made absolute and have
19
+ quoted path parts.
20
+ """
21
+ path = os.path.normpath(os.path.abspath(path))
22
+ url = urllib.parse.urljoin("file:", urllib.request.pathname2url(path))
23
+ return url
24
+
25
+
26
+ def url_to_path(url: str) -> str:
27
+ """
28
+ Convert a file: URL to a path.
29
+ """
30
+ assert url.startswith(
31
+ "file:"
32
+ ), f"You can only turn file: urls into filenames (not {url!r})"
33
+
34
+ _, netloc, path, _, _ = urllib.parse.urlsplit(url)
35
+
36
+ if not netloc or netloc == "localhost":
37
+ # According to RFC 8089, same as empty authority.
38
+ netloc = ""
39
+ elif WINDOWS:
40
+ # If we have a UNC path, prepend UNC share notation.
41
+ netloc = "\\\\" + netloc
42
+ else:
43
+ raise ValueError(
44
+ f"non-local file URIs are not supported on this platform: {url!r}"
45
+ )
46
+
47
+ path = urllib.request.url2pathname(netloc + path)
48
+
49
+ # On Windows, urlsplit parses the path as something like "/C:/Users/foo".
50
+ # This creates issues for path-related functions like io.open(), so we try
51
+ # to detect and strip the leading slash.
52
+ if (
53
+ WINDOWS
54
+ and not netloc # Not UNC.
55
+ and len(path) >= 3
56
+ and path[0] == "/" # Leading slash to strip.
57
+ and path[1] in string.ascii_letters # Drive letter.
58
+ and path[2:4] in (":", ":/") # Colon + end of string, or colon + absolute path.
59
+ ):
60
+ path = path[1:]
61
+
62
+ return path
venv/lib/python3.10/site-packages/pip/_internal/utils/virtualenv.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import os
3
+ import re
4
+ import site
5
+ import sys
6
+ from typing import List, Optional
7
+
8
+ logger = logging.getLogger(__name__)
9
+ _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX = re.compile(
10
+ r"include-system-site-packages\s*=\s*(?P<value>true|false)"
11
+ )
12
+
13
+
14
+ def _running_under_venv() -> bool:
15
+ """Checks if sys.base_prefix and sys.prefix match.
16
+
17
+ This handles PEP 405 compliant virtual environments.
18
+ """
19
+ return sys.prefix != getattr(sys, "base_prefix", sys.prefix)
20
+
21
+
22
+ def _running_under_regular_virtualenv() -> bool:
23
+ """Checks if sys.real_prefix is set.
24
+
25
+ This handles virtual environments created with pypa's virtualenv.
26
+ """
27
+ # pypa/virtualenv case
28
+ return hasattr(sys, "real_prefix")
29
+
30
+
31
+ def running_under_virtualenv() -> bool:
32
+ """Return True if we're running inside a virtualenv, False otherwise."""
33
+ return _running_under_venv() or _running_under_regular_virtualenv()
34
+
35
+
36
+ def _get_pyvenv_cfg_lines() -> Optional[List[str]]:
37
+ """Reads {sys.prefix}/pyvenv.cfg and returns its contents as list of lines
38
+
39
+ Returns None, if it could not read/access the file.
40
+ """
41
+ pyvenv_cfg_file = os.path.join(sys.prefix, "pyvenv.cfg")
42
+ try:
43
+ # Although PEP 405 does not specify, the built-in venv module always
44
+ # writes with UTF-8. (pypa/pip#8717)
45
+ with open(pyvenv_cfg_file, encoding="utf-8") as f:
46
+ return f.read().splitlines() # avoids trailing newlines
47
+ except OSError:
48
+ return None
49
+
50
+
51
+ def _no_global_under_venv() -> bool:
52
+ """Check `{sys.prefix}/pyvenv.cfg` for system site-packages inclusion
53
+
54
+ PEP 405 specifies that when system site-packages are not supposed to be
55
+ visible from a virtual environment, `pyvenv.cfg` must contain the following
56
+ line:
57
+
58
+ include-system-site-packages = false
59
+
60
+ Additionally, log a warning if accessing the file fails.
61
+ """
62
+ cfg_lines = _get_pyvenv_cfg_lines()
63
+ if cfg_lines is None:
64
+ # We're not in a "sane" venv, so assume there is no system
65
+ # site-packages access (since that's PEP 405's default state).
66
+ logger.warning(
67
+ "Could not access 'pyvenv.cfg' despite a virtual environment "
68
+ "being active. Assuming global site-packages is not accessible "
69
+ "in this environment."
70
+ )
71
+ return True
72
+
73
+ for line in cfg_lines:
74
+ match = _INCLUDE_SYSTEM_SITE_PACKAGES_REGEX.match(line)
75
+ if match is not None and match.group("value") == "false":
76
+ return True
77
+ return False
78
+
79
+
80
+ def _no_global_under_regular_virtualenv() -> bool:
81
+ """Check if "no-global-site-packages.txt" exists beside site.py
82
+
83
+ This mirrors logic in pypa/virtualenv for determining whether system
84
+ site-packages are visible in the virtual environment.
85
+ """
86
+ site_mod_dir = os.path.dirname(os.path.abspath(site.__file__))
87
+ no_global_site_packages_file = os.path.join(
88
+ site_mod_dir,
89
+ "no-global-site-packages.txt",
90
+ )
91
+ return os.path.exists(no_global_site_packages_file)
92
+
93
+
94
+ def virtualenv_no_global() -> bool:
95
+ """Returns a boolean, whether running in venv with no system site-packages."""
96
+ # PEP 405 compliance needs to be checked first since virtualenv >=20 would
97
+ # return True for both checks, but is only able to use the PEP 405 config.
98
+ if _running_under_venv():
99
+ return _no_global_under_venv()
100
+
101
+ if _running_under_regular_virtualenv():
102
+ return _no_global_under_regular_virtualenv()
103
+
104
+ return False
venv/lib/python3.10/site-packages/pip/_internal/utils/wheel.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Support functions for working with wheel files.
2
+ """
3
+
4
+ import logging
5
+ from email.message import Message
6
+ from email.parser import Parser
7
+ from typing import Tuple
8
+ from zipfile import BadZipFile, ZipFile
9
+
10
+ from pip._vendor.packaging.utils import canonicalize_name
11
+
12
+ from pip._internal.exceptions import UnsupportedWheel
13
+
14
+ VERSION_COMPATIBLE = (1, 0)
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def parse_wheel(wheel_zip: ZipFile, name: str) -> Tuple[str, Message]:
21
+ """Extract information from the provided wheel, ensuring it meets basic
22
+ standards.
23
+
24
+ Returns the name of the .dist-info directory and the parsed WHEEL metadata.
25
+ """
26
+ try:
27
+ info_dir = wheel_dist_info_dir(wheel_zip, name)
28
+ metadata = wheel_metadata(wheel_zip, info_dir)
29
+ version = wheel_version(metadata)
30
+ except UnsupportedWheel as e:
31
+ raise UnsupportedWheel("{} has an invalid wheel, {}".format(name, str(e)))
32
+
33
+ check_compatibility(version, name)
34
+
35
+ return info_dir, metadata
36
+
37
+
38
+ def wheel_dist_info_dir(source: ZipFile, name: str) -> str:
39
+ """Returns the name of the contained .dist-info directory.
40
+
41
+ Raises AssertionError or UnsupportedWheel if not found, >1 found, or
42
+ it doesn't match the provided name.
43
+ """
44
+ # Zip file path separators must be /
45
+ subdirs = {p.split("/", 1)[0] for p in source.namelist()}
46
+
47
+ info_dirs = [s for s in subdirs if s.endswith(".dist-info")]
48
+
49
+ if not info_dirs:
50
+ raise UnsupportedWheel(".dist-info directory not found")
51
+
52
+ if len(info_dirs) > 1:
53
+ raise UnsupportedWheel(
54
+ "multiple .dist-info directories found: {}".format(", ".join(info_dirs))
55
+ )
56
+
57
+ info_dir = info_dirs[0]
58
+
59
+ info_dir_name = canonicalize_name(info_dir)
60
+ canonical_name = canonicalize_name(name)
61
+ if not info_dir_name.startswith(canonical_name):
62
+ raise UnsupportedWheel(
63
+ ".dist-info directory {!r} does not start with {!r}".format(
64
+ info_dir, canonical_name
65
+ )
66
+ )
67
+
68
+ return info_dir
69
+
70
+
71
+ def read_wheel_metadata_file(source: ZipFile, path: str) -> bytes:
72
+ try:
73
+ return source.read(path)
74
+ # BadZipFile for general corruption, KeyError for missing entry,
75
+ # and RuntimeError for password-protected files
76
+ except (BadZipFile, KeyError, RuntimeError) as e:
77
+ raise UnsupportedWheel(f"could not read {path!r} file: {e!r}")
78
+
79
+
80
+ def wheel_metadata(source: ZipFile, dist_info_dir: str) -> Message:
81
+ """Return the WHEEL metadata of an extracted wheel, if possible.
82
+ Otherwise, raise UnsupportedWheel.
83
+ """
84
+ path = f"{dist_info_dir}/WHEEL"
85
+ # Zip file path separators must be /
86
+ wheel_contents = read_wheel_metadata_file(source, path)
87
+
88
+ try:
89
+ wheel_text = wheel_contents.decode()
90
+ except UnicodeDecodeError as e:
91
+ raise UnsupportedWheel(f"error decoding {path!r}: {e!r}")
92
+
93
+ # FeedParser (used by Parser) does not raise any exceptions. The returned
94
+ # message may have .defects populated, but for backwards-compatibility we
95
+ # currently ignore them.
96
+ return Parser().parsestr(wheel_text)
97
+
98
+
99
+ def wheel_version(wheel_data: Message) -> Tuple[int, ...]:
100
+ """Given WHEEL metadata, return the parsed Wheel-Version.
101
+ Otherwise, raise UnsupportedWheel.
102
+ """
103
+ version_text = wheel_data["Wheel-Version"]
104
+ if version_text is None:
105
+ raise UnsupportedWheel("WHEEL is missing Wheel-Version")
106
+
107
+ version = version_text.strip()
108
+
109
+ try:
110
+ return tuple(map(int, version.split(".")))
111
+ except ValueError:
112
+ raise UnsupportedWheel(f"invalid Wheel-Version: {version!r}")
113
+
114
+
115
+ def check_compatibility(version: Tuple[int, ...], name: str) -> None:
116
+ """Raises errors or warns if called with an incompatible Wheel-Version.
117
+
118
+ pip should refuse to install a Wheel-Version that's a major series
119
+ ahead of what it's compatible with (e.g 2.0 > 1.1); and warn when
120
+ installing a version only minor version ahead (e.g 1.2 > 1.1).
121
+
122
+ version: a 2-tuple representing a Wheel-Version (Major, Minor)
123
+ name: name of wheel or package to raise exception about
124
+
125
+ :raises UnsupportedWheel: when an incompatible Wheel-Version is given
126
+ """
127
+ if version[0] > VERSION_COMPATIBLE[0]:
128
+ raise UnsupportedWheel(
129
+ "{}'s Wheel-Version ({}) is not compatible with this version "
130
+ "of pip".format(name, ".".join(map(str, version)))
131
+ )
132
+ elif version > VERSION_COMPATIBLE:
133
+ logger.warning(
134
+ "Installing from a newer Wheel-Version (%s)",
135
+ ".".join(map(str, version)),
136
+ )
venv/lib/python3.10/site-packages/pip/_internal/vcs/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Expose a limited set of classes and functions so callers outside of
2
+ # the vcs package don't need to import deeper than `pip._internal.vcs`.
3
+ # (The test directory may still need to import from a vcs sub-package.)
4
+ # Import all vcs modules to register each VCS in the VcsSupport object.
5
+ import pip._internal.vcs.bazaar
6
+ import pip._internal.vcs.git
7
+ import pip._internal.vcs.mercurial
8
+ import pip._internal.vcs.subversion # noqa: F401
9
+ from pip._internal.vcs.versioncontrol import ( # noqa: F401
10
+ RemoteNotFoundError,
11
+ RemoteNotValidError,
12
+ is_url,
13
+ make_vcs_requirement_url,
14
+ vcs,
15
+ )
venv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (613 Bytes). View file
 
venv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-310.pyc ADDED
Binary file (3.44 kB). View file
 
venv/lib/python3.10/site-packages/pip/_internal/vcs/__pycache__/git.cpython-310.pyc ADDED
Binary file (12.6 kB). View file