pip 25.1__py3-none-any.whl → 25.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. pip/__init__.py +3 -3
  2. pip/_internal/__init__.py +2 -2
  3. pip/_internal/build_env.py +118 -94
  4. pip/_internal/cache.py +16 -14
  5. pip/_internal/cli/autocompletion.py +13 -4
  6. pip/_internal/cli/base_command.py +18 -7
  7. pip/_internal/cli/cmdoptions.py +14 -9
  8. pip/_internal/cli/command_context.py +4 -3
  9. pip/_internal/cli/index_command.py +11 -9
  10. pip/_internal/cli/main.py +3 -2
  11. pip/_internal/cli/main_parser.py +4 -3
  12. pip/_internal/cli/parser.py +26 -22
  13. pip/_internal/cli/progress_bars.py +19 -12
  14. pip/_internal/cli/req_command.py +16 -12
  15. pip/_internal/cli/spinners.py +81 -5
  16. pip/_internal/commands/__init__.py +5 -3
  17. pip/_internal/commands/cache.py +18 -15
  18. pip/_internal/commands/check.py +1 -2
  19. pip/_internal/commands/completion.py +1 -2
  20. pip/_internal/commands/configuration.py +26 -18
  21. pip/_internal/commands/debug.py +8 -6
  22. pip/_internal/commands/download.py +2 -3
  23. pip/_internal/commands/freeze.py +2 -3
  24. pip/_internal/commands/hash.py +1 -2
  25. pip/_internal/commands/help.py +1 -2
  26. pip/_internal/commands/index.py +15 -9
  27. pip/_internal/commands/inspect.py +4 -4
  28. pip/_internal/commands/install.py +45 -40
  29. pip/_internal/commands/list.py +35 -26
  30. pip/_internal/commands/lock.py +1 -2
  31. pip/_internal/commands/search.py +14 -12
  32. pip/_internal/commands/show.py +14 -11
  33. pip/_internal/commands/uninstall.py +1 -2
  34. pip/_internal/commands/wheel.py +2 -3
  35. pip/_internal/configuration.py +39 -25
  36. pip/_internal/distributions/base.py +6 -4
  37. pip/_internal/distributions/installed.py +8 -4
  38. pip/_internal/distributions/sdist.py +20 -13
  39. pip/_internal/distributions/wheel.py +6 -4
  40. pip/_internal/exceptions.py +58 -39
  41. pip/_internal/index/collector.py +24 -29
  42. pip/_internal/index/package_finder.py +70 -61
  43. pip/_internal/index/sources.py +17 -14
  44. pip/_internal/locations/__init__.py +18 -16
  45. pip/_internal/locations/_distutils.py +12 -11
  46. pip/_internal/locations/_sysconfig.py +5 -4
  47. pip/_internal/locations/base.py +4 -3
  48. pip/_internal/main.py +2 -2
  49. pip/_internal/metadata/__init__.py +8 -6
  50. pip/_internal/metadata/_json.py +5 -4
  51. pip/_internal/metadata/base.py +22 -27
  52. pip/_internal/metadata/importlib/_compat.py +6 -4
  53. pip/_internal/metadata/importlib/_dists.py +12 -17
  54. pip/_internal/metadata/importlib/_envs.py +9 -6
  55. pip/_internal/metadata/pkg_resources.py +11 -14
  56. pip/_internal/models/direct_url.py +24 -21
  57. pip/_internal/models/format_control.py +5 -5
  58. pip/_internal/models/installation_report.py +4 -3
  59. pip/_internal/models/link.py +39 -34
  60. pip/_internal/models/pylock.py +27 -22
  61. pip/_internal/models/search_scope.py +6 -7
  62. pip/_internal/models/selection_prefs.py +3 -3
  63. pip/_internal/models/target_python.py +10 -9
  64. pip/_internal/models/wheel.py +7 -5
  65. pip/_internal/network/auth.py +20 -22
  66. pip/_internal/network/cache.py +22 -6
  67. pip/_internal/network/download.py +169 -141
  68. pip/_internal/network/lazy_wheel.py +10 -7
  69. pip/_internal/network/session.py +32 -27
  70. pip/_internal/network/utils.py +2 -2
  71. pip/_internal/network/xmlrpc.py +2 -2
  72. pip/_internal/operations/build/build_tracker.py +10 -8
  73. pip/_internal/operations/build/wheel.py +3 -2
  74. pip/_internal/operations/build/wheel_editable.py +3 -2
  75. pip/_internal/operations/build/wheel_legacy.py +9 -8
  76. pip/_internal/operations/check.py +21 -26
  77. pip/_internal/operations/freeze.py +12 -9
  78. pip/_internal/operations/install/editable_legacy.py +5 -3
  79. pip/_internal/operations/install/wheel.py +53 -44
  80. pip/_internal/operations/prepare.py +35 -30
  81. pip/_internal/pyproject.py +7 -10
  82. pip/_internal/req/__init__.py +12 -10
  83. pip/_internal/req/constructors.py +33 -31
  84. pip/_internal/req/req_dependency_group.py +9 -8
  85. pip/_internal/req/req_file.py +32 -35
  86. pip/_internal/req/req_install.py +37 -34
  87. pip/_internal/req/req_set.py +4 -5
  88. pip/_internal/req/req_uninstall.py +20 -17
  89. pip/_internal/resolution/base.py +3 -3
  90. pip/_internal/resolution/legacy/resolver.py +21 -20
  91. pip/_internal/resolution/resolvelib/base.py +16 -13
  92. pip/_internal/resolution/resolvelib/candidates.py +29 -26
  93. pip/_internal/resolution/resolvelib/factory.py +41 -50
  94. pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
  95. pip/_internal/resolution/resolvelib/provider.py +15 -20
  96. pip/_internal/resolution/resolvelib/reporter.py +5 -3
  97. pip/_internal/resolution/resolvelib/requirements.py +8 -6
  98. pip/_internal/resolution/resolvelib/resolver.py +39 -23
  99. pip/_internal/self_outdated_check.py +8 -6
  100. pip/_internal/utils/appdirs.py +1 -2
  101. pip/_internal/utils/compat.py +7 -1
  102. pip/_internal/utils/compatibility_tags.py +17 -16
  103. pip/_internal/utils/deprecation.py +11 -9
  104. pip/_internal/utils/direct_url_helpers.py +2 -2
  105. pip/_internal/utils/egg_link.py +6 -5
  106. pip/_internal/utils/entrypoints.py +3 -2
  107. pip/_internal/utils/filesystem.py +8 -5
  108. pip/_internal/utils/filetypes.py +4 -6
  109. pip/_internal/utils/glibc.py +6 -5
  110. pip/_internal/utils/hashes.py +9 -6
  111. pip/_internal/utils/logging.py +8 -5
  112. pip/_internal/utils/misc.py +54 -44
  113. pip/_internal/utils/packaging.py +3 -2
  114. pip/_internal/utils/retry.py +7 -4
  115. pip/_internal/utils/setuptools_build.py +12 -10
  116. pip/_internal/utils/subprocess.py +20 -17
  117. pip/_internal/utils/temp_dir.py +10 -12
  118. pip/_internal/utils/unpacking.py +6 -4
  119. pip/_internal/utils/urls.py +1 -1
  120. pip/_internal/utils/virtualenv.py +3 -2
  121. pip/_internal/utils/wheel.py +3 -4
  122. pip/_internal/vcs/bazaar.py +26 -8
  123. pip/_internal/vcs/git.py +59 -24
  124. pip/_internal/vcs/mercurial.py +34 -11
  125. pip/_internal/vcs/subversion.py +27 -16
  126. pip/_internal/vcs/versioncontrol.py +56 -51
  127. pip/_internal/wheel_builder.py +14 -12
  128. pip/_vendor/cachecontrol/__init__.py +1 -1
  129. pip/_vendor/certifi/__init__.py +1 -1
  130. pip/_vendor/certifi/cacert.pem +102 -221
  131. pip/_vendor/certifi/core.py +1 -32
  132. pip/_vendor/dependency_groups/_implementation.py +7 -11
  133. pip/_vendor/distlib/__init__.py +2 -2
  134. pip/_vendor/distlib/scripts.py +1 -1
  135. pip/_vendor/msgpack/__init__.py +2 -2
  136. pip/_vendor/pkg_resources/__init__.py +1 -1
  137. pip/_vendor/platformdirs/version.py +2 -2
  138. pip/_vendor/pygments/__init__.py +1 -1
  139. pip/_vendor/requests/__version__.py +2 -2
  140. pip/_vendor/requests/compat.py +12 -0
  141. pip/_vendor/requests/models.py +3 -1
  142. pip/_vendor/requests/utils.py +6 -16
  143. pip/_vendor/resolvelib/__init__.py +3 -3
  144. pip/_vendor/resolvelib/reporters.py +1 -1
  145. pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
  146. pip/_vendor/resolvelib/resolvers/resolution.py +91 -10
  147. pip/_vendor/rich/__main__.py +12 -40
  148. pip/_vendor/rich/_inspect.py +1 -1
  149. pip/_vendor/rich/_ratio.py +1 -7
  150. pip/_vendor/rich/align.py +1 -7
  151. pip/_vendor/rich/box.py +1 -7
  152. pip/_vendor/rich/console.py +25 -20
  153. pip/_vendor/rich/control.py +1 -7
  154. pip/_vendor/rich/diagnose.py +1 -0
  155. pip/_vendor/rich/emoji.py +1 -6
  156. pip/_vendor/rich/live.py +32 -7
  157. pip/_vendor/rich/live_render.py +1 -7
  158. pip/_vendor/rich/logging.py +1 -1
  159. pip/_vendor/rich/panel.py +3 -4
  160. pip/_vendor/rich/progress.py +15 -15
  161. pip/_vendor/rich/spinner.py +7 -13
  162. pip/_vendor/rich/syntax.py +24 -5
  163. pip/_vendor/rich/traceback.py +32 -17
  164. pip/_vendor/truststore/_api.py +1 -1
  165. pip/_vendor/vendor.txt +10 -11
  166. {pip-25.1.dist-info → pip-25.2.dist-info}/METADATA +26 -4
  167. {pip-25.1.dist-info → pip-25.2.dist-info}/RECORD +194 -181
  168. {pip-25.1.dist-info → pip-25.2.dist-info}/WHEEL +1 -1
  169. {pip-25.1.dist-info → pip-25.2.dist-info}/licenses/AUTHORS.txt +12 -0
  170. pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  171. pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
  172. pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  173. pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
  174. pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
  175. pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
  176. pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
  177. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
  178. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
  179. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
  180. pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
  181. pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
  182. pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
  183. pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
  184. pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
  185. pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
  186. pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
  187. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
  188. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +3 -0
  189. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
  190. pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
  191. pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
  192. pip/_vendor/distlib/database.py +0 -1329
  193. pip/_vendor/distlib/index.py +0 -508
  194. pip/_vendor/distlib/locators.py +0 -1295
  195. pip/_vendor/distlib/manifest.py +0 -384
  196. pip/_vendor/distlib/markers.py +0 -162
  197. pip/_vendor/distlib/metadata.py +0 -1031
  198. pip/_vendor/distlib/version.py +0 -750
  199. pip/_vendor/distlib/wheel.py +0 -1100
  200. pip/_vendor/typing_extensions.py +0 -4584
  201. {pip-25.1.dist-info → pip-25.2.dist-info}/entry_points.txt +0 -0
  202. {pip-25.1.dist-info → pip-25.2.dist-info}/licenses/LICENSE.txt +0 -0
  203. {pip-25.1.dist-info → pip-25.2.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,7 @@
1
1
  """Support for installing and building the "wheel" binary package format."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import collections
4
6
  import compileall
5
7
  import contextlib
@@ -10,27 +12,19 @@ import os.path
10
12
  import re
11
13
  import shutil
12
14
  import sys
15
+ import textwrap
13
16
  import warnings
14
17
  from base64 import urlsafe_b64encode
18
+ from collections.abc import Generator, Iterable, Iterator, Sequence
15
19
  from email.message import Message
16
- from io import StringIO
17
20
  from itertools import chain, filterfalse, starmap
18
21
  from typing import (
19
22
  IO,
20
23
  Any,
21
24
  BinaryIO,
22
25
  Callable,
23
- Dict,
24
- Generator,
25
- Iterable,
26
- Iterator,
27
- List,
28
26
  NewType,
29
- Optional,
30
27
  Protocol,
31
- Sequence,
32
- Set,
33
- Tuple,
34
28
  Union,
35
29
  cast,
36
30
  )
@@ -50,7 +44,7 @@ from pip._internal.metadata import (
50
44
  from pip._internal.models.direct_url import DIRECT_URL_METADATA_NAME, DirectUrl
51
45
  from pip._internal.models.scheme import SCHEME_KEYS, Scheme
52
46
  from pip._internal.utils.filesystem import adjacent_tmp_file, replace
53
- from pip._internal.utils.misc import ensure_dir, hash_file, partition
47
+ from pip._internal.utils.misc import StreamWrapper, ensure_dir, hash_file, partition
54
48
  from pip._internal.utils.unpacking import (
55
49
  current_umask,
56
50
  is_within_directory,
@@ -61,7 +55,7 @@ from pip._internal.utils.wheel import parse_wheel
61
55
 
62
56
 
63
57
  class File(Protocol):
64
- src_record_path: "RecordPath"
58
+ src_record_path: RecordPath
65
59
  dest_path: str
66
60
  changed: bool
67
61
 
@@ -72,17 +66,17 @@ class File(Protocol):
72
66
  logger = logging.getLogger(__name__)
73
67
 
74
68
  RecordPath = NewType("RecordPath", str)
75
- InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]]
69
+ InstalledCSVRow = tuple[RecordPath, str, Union[int, str]]
76
70
 
77
71
 
78
- def rehash(path: str, blocksize: int = 1 << 20) -> Tuple[str, str]:
72
+ def rehash(path: str, blocksize: int = 1 << 20) -> tuple[str, str]:
79
73
  """Return (encoded_digest, length) for path using hashlib.sha256()"""
80
74
  h, length = hash_file(path, blocksize)
81
75
  digest = "sha256=" + urlsafe_b64encode(h.digest()).decode("latin1").rstrip("=")
82
76
  return (digest, str(length))
83
77
 
84
78
 
85
- def csv_io_kwargs(mode: str) -> Dict[str, Any]:
79
+ def csv_io_kwargs(mode: str) -> dict[str, Any]:
86
80
  """Return keyword arguments to properly open a CSV file
87
81
  in the given mode.
88
82
  """
@@ -113,7 +107,7 @@ def wheel_root_is_purelib(metadata: Message) -> bool:
113
107
  return metadata.get("Root-Is-Purelib", "").lower() == "true"
114
108
 
115
109
 
116
- def get_entrypoints(dist: BaseDistribution) -> Tuple[Dict[str, str], Dict[str, str]]:
110
+ def get_entrypoints(dist: BaseDistribution) -> tuple[dict[str, str], dict[str, str]]:
117
111
  console_scripts = {}
118
112
  gui_scripts = {}
119
113
  for entry_point in dist.iter_entry_points():
@@ -124,7 +118,7 @@ def get_entrypoints(dist: BaseDistribution) -> Tuple[Dict[str, str], Dict[str, s
124
118
  return console_scripts, gui_scripts
125
119
 
126
120
 
127
- def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
121
+ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> str | None:
128
122
  """Determine if any scripts are not on PATH and format a warning.
129
123
  Returns a warning message if one or more scripts are not on PATH,
130
124
  otherwise None.
@@ -133,7 +127,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
133
127
  return None
134
128
 
135
129
  # Group scripts by the path they were installed in
136
- grouped_by_dir: Dict[str, Set[str]] = collections.defaultdict(set)
130
+ grouped_by_dir: dict[str, set[str]] = collections.defaultdict(set)
137
131
  for destfile in scripts:
138
132
  parent_dir = os.path.dirname(destfile)
139
133
  script_name = os.path.basename(destfile)
@@ -149,7 +143,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
149
143
  not_warn_dirs.append(
150
144
  os.path.normcase(os.path.normpath(os.path.dirname(sys.executable)))
151
145
  )
152
- warn_for: Dict[str, Set[str]] = {
146
+ warn_for: dict[str, set[str]] = {
153
147
  parent_dir: scripts
154
148
  for parent_dir, scripts in grouped_by_dir.items()
155
149
  if os.path.normcase(os.path.normpath(parent_dir)) not in not_warn_dirs
@@ -160,7 +154,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
160
154
  # Format a message
161
155
  msg_lines = []
162
156
  for parent_dir, dir_scripts in warn_for.items():
163
- sorted_scripts: List[str] = sorted(dir_scripts)
157
+ sorted_scripts: list[str] = sorted(dir_scripts)
164
158
  if len(sorted_scripts) == 1:
165
159
  start_text = f"script {sorted_scripts[0]} is"
166
160
  else:
@@ -198,7 +192,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
198
192
 
199
193
  def _normalized_outrows(
200
194
  outrows: Iterable[InstalledCSVRow],
201
- ) -> List[Tuple[str, str, str]]:
195
+ ) -> list[tuple[str, str, str]]:
202
196
  """Normalize the given rows of a RECORD file.
203
197
 
204
198
  Items in each row are converted into str. Rows are then sorted to make
@@ -237,17 +231,17 @@ def _fs_to_record_path(path: str, lib_dir: str) -> RecordPath:
237
231
 
238
232
 
239
233
  def get_csv_rows_for_installed(
240
- old_csv_rows: List[List[str]],
241
- installed: Dict[RecordPath, RecordPath],
242
- changed: Set[RecordPath],
243
- generated: List[str],
234
+ old_csv_rows: list[list[str]],
235
+ installed: dict[RecordPath, RecordPath],
236
+ changed: set[RecordPath],
237
+ generated: list[str],
244
238
  lib_dir: str,
245
- ) -> List[InstalledCSVRow]:
239
+ ) -> list[InstalledCSVRow]:
246
240
  """
247
241
  :param installed: A map from archive RECORD path to installation RECORD
248
242
  path.
249
243
  """
250
- installed_rows: List[InstalledCSVRow] = []
244
+ installed_rows: list[InstalledCSVRow] = []
251
245
  for row in old_csv_rows:
252
246
  if len(row) > 3:
253
247
  logger.warning("RECORD line has more than three elements: %s", row)
@@ -268,7 +262,7 @@ def get_csv_rows_for_installed(
268
262
  ]
269
263
 
270
264
 
271
- def get_console_script_specs(console: Dict[str, str]) -> List[str]:
265
+ def get_console_script_specs(console: dict[str, str]) -> list[str]:
272
266
  """
273
267
  Given the mapping from entrypoint name to callable, return the relevant
274
268
  console script specs.
@@ -382,7 +376,7 @@ class ZipBackedFile:
382
376
 
383
377
 
384
378
  class ScriptFile:
385
- def __init__(self, file: "File") -> None:
379
+ def __init__(self, file: File) -> None:
386
380
  self._file = file
387
381
  self.src_record_path = self._file.src_record_path
388
382
  self.dest_path = self._file.dest_path
@@ -397,7 +391,7 @@ class MissingCallableSuffix(InstallationError):
397
391
  def __init__(self, entry_point: str) -> None:
398
392
  super().__init__(
399
393
  f"Invalid script entry point: {entry_point} - A callable "
400
- "suffix is required. Cf https://packaging.python.org/"
394
+ "suffix is required. See https://packaging.python.org/"
401
395
  "specifications/entry-points/#use-for-scripts for more "
402
396
  "information."
403
397
  )
@@ -410,9 +404,22 @@ def _raise_for_invalid_entrypoint(specification: str) -> None:
410
404
 
411
405
 
412
406
  class PipScriptMaker(ScriptMaker):
407
+ # Override distlib's default script template with one that
408
+ # doesn't import `re` module, allowing scripts to load faster.
409
+ script_template = textwrap.dedent(
410
+ """\
411
+ import sys
412
+ from %(module)s import %(import_name)s
413
+ if __name__ == '__main__':
414
+ if sys.argv[0].endswith('.exe'):
415
+ sys.argv[0] = sys.argv[0][:-4]
416
+ sys.exit(%(func)s())
417
+ """
418
+ )
419
+
413
420
  def make(
414
- self, specification: str, options: Optional[Dict[str, Any]] = None
415
- ) -> List[str]:
421
+ self, specification: str, options: dict[str, Any] | None = None
422
+ ) -> list[str]:
416
423
  _raise_for_invalid_entrypoint(specification)
417
424
  return super().make(specification, options)
418
425
 
@@ -424,7 +431,7 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
424
431
  scheme: Scheme,
425
432
  pycompile: bool = True,
426
433
  warn_script_location: bool = True,
427
- direct_url: Optional[DirectUrl] = None,
434
+ direct_url: DirectUrl | None = None,
428
435
  requested: bool = False,
429
436
  ) -> None:
430
437
  """Install a wheel.
@@ -453,9 +460,9 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
453
460
  # installed = files copied from the wheel to the destination
454
461
  # changed = files changed while installing (scripts #! line typically)
455
462
  # generated = files newly generated during the install (script wrappers)
456
- installed: Dict[RecordPath, RecordPath] = {}
457
- changed: Set[RecordPath] = set()
458
- generated: List[str] = []
463
+ installed: dict[RecordPath, RecordPath] = {}
464
+ changed: set[RecordPath] = set()
465
+ generated: list[str] = []
459
466
 
460
467
  def record_installed(
461
468
  srcfile: RecordPath, destfile: str, modified: bool = False
@@ -481,8 +488,8 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
481
488
 
482
489
  def root_scheme_file_maker(
483
490
  zip_file: ZipFile, dest: str
484
- ) -> Callable[[RecordPath], "File"]:
485
- def make_root_scheme_file(record_path: RecordPath) -> "File":
491
+ ) -> Callable[[RecordPath], File]:
492
+ def make_root_scheme_file(record_path: RecordPath) -> File:
486
493
  normed_path = os.path.normpath(record_path)
487
494
  dest_path = os.path.join(dest, normed_path)
488
495
  assert_no_path_traversal(dest, dest_path)
@@ -492,10 +499,10 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
492
499
 
493
500
  def data_scheme_file_maker(
494
501
  zip_file: ZipFile, scheme: Scheme
495
- ) -> Callable[[RecordPath], "File"]:
502
+ ) -> Callable[[RecordPath], File]:
496
503
  scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS}
497
504
 
498
- def make_data_scheme_file(record_path: RecordPath) -> "File":
505
+ def make_data_scheme_file(record_path: RecordPath) -> File:
499
506
  normed_path = os.path.normpath(record_path)
500
507
  try:
501
508
  _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2)
@@ -527,7 +534,7 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
527
534
  def is_data_scheme_path(path: RecordPath) -> bool:
528
535
  return path.split("/", 1)[0].endswith(".data")
529
536
 
530
- paths = cast(List[RecordPath], wheel_zip.namelist())
537
+ paths = cast(list[RecordPath], wheel_zip.namelist())
531
538
  file_paths = filterfalse(is_dir_path, paths)
532
539
  root_scheme_paths, data_scheme_paths = partition(is_data_scheme_path, file_paths)
533
540
 
@@ -553,7 +560,7 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
553
560
  )
554
561
  console, gui = get_entrypoints(distribution)
555
562
 
556
- def is_entrypoint_wrapper(file: "File") -> bool:
563
+ def is_entrypoint_wrapper(file: File) -> bool:
557
564
  # EP, EP.exe and EP-script.py are scripts generated for
558
565
  # entry point EP by setuptools
559
566
  path = file.dest_path
@@ -607,7 +614,9 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
607
614
 
608
615
  # Compile all of the pyc files for the installed files
609
616
  if pycompile:
610
- with contextlib.redirect_stdout(StringIO()) as stdout:
617
+ with contextlib.redirect_stdout(
618
+ StreamWrapper.from_stream(sys.stdout)
619
+ ) as stdout:
611
620
  with warnings.catch_warnings():
612
621
  warnings.filterwarnings("ignore")
613
622
  for path in pyc_source_file_paths():
@@ -720,7 +729,7 @@ def install_wheel(
720
729
  req_description: str,
721
730
  pycompile: bool = True,
722
731
  warn_script_location: bool = True,
723
- direct_url: Optional[DirectUrl] = None,
732
+ direct_url: DirectUrl | None = None,
724
733
  requested: bool = False,
725
734
  ) -> None:
726
735
  with ZipFile(wheel_path, allowZip64=True) as z:
@@ -2,16 +2,19 @@
2
2
 
3
3
  # The following comment should be removed at some point in the future.
4
4
  # mypy: strict-optional=False
5
+ from __future__ import annotations
5
6
 
6
7
  import mimetypes
7
8
  import os
8
9
  import shutil
10
+ from collections.abc import Iterable
9
11
  from dataclasses import dataclass
10
12
  from pathlib import Path
11
- from typing import Dict, Iterable, List, Optional
13
+ from typing import TYPE_CHECKING
12
14
 
13
15
  from pip._vendor.packaging.utils import canonicalize_name
14
16
 
17
+ from pip._internal.build_env import BuildEnvironmentInstaller
15
18
  from pip._internal.distributions import make_distribution_for_install_requirement
16
19
  from pip._internal.distributions.installed import InstalledDistribution
17
20
  from pip._internal.exceptions import (
@@ -28,7 +31,7 @@ from pip._internal.metadata import BaseDistribution, get_metadata_distribution
28
31
  from pip._internal.models.direct_url import ArchiveInfo
29
32
  from pip._internal.models.link import Link
30
33
  from pip._internal.models.wheel import Wheel
31
- from pip._internal.network.download import BatchDownloader, Downloader
34
+ from pip._internal.network.download import Downloader
32
35
  from pip._internal.network.lazy_wheel import (
33
36
  HTTPRangeRequestUnsupported,
34
37
  dist_from_wheel_url,
@@ -53,13 +56,16 @@ from pip._internal.utils.temp_dir import TempDirectory
53
56
  from pip._internal.utils.unpacking import unpack_file
54
57
  from pip._internal.vcs import vcs
55
58
 
59
+ if TYPE_CHECKING:
60
+ from pip._internal.cli.progress_bars import BarType
61
+
56
62
  logger = getLogger(__name__)
57
63
 
58
64
 
59
65
  def _get_prepared_distribution(
60
66
  req: InstallRequirement,
61
67
  build_tracker: BuildTracker,
62
- finder: PackageFinder,
68
+ build_env_installer: BuildEnvironmentInstaller,
63
69
  build_isolation: bool,
64
70
  check_build_deps: bool,
65
71
  ) -> BaseDistribution:
@@ -69,7 +75,7 @@ def _get_prepared_distribution(
69
75
  if tracker_id is not None:
70
76
  with build_tracker.track(req, tracker_id):
71
77
  abstract_dist.prepare_distribution_metadata(
72
- finder, build_isolation, check_build_deps
78
+ build_env_installer, build_isolation, check_build_deps
73
79
  )
74
80
  return abstract_dist.get_metadata_distribution()
75
81
 
@@ -83,7 +89,7 @@ def unpack_vcs_link(link: Link, location: str, verbosity: int) -> None:
83
89
  @dataclass
84
90
  class File:
85
91
  path: str
86
- content_type: Optional[str] = None
92
+ content_type: str | None = None
87
93
 
88
94
  def __post_init__(self) -> None:
89
95
  if self.content_type is None:
@@ -98,8 +104,8 @@ class File:
98
104
  def get_http_url(
99
105
  link: Link,
100
106
  download: Downloader,
101
- download_dir: Optional[str] = None,
102
- hashes: Optional[Hashes] = None,
107
+ download_dir: str | None = None,
108
+ hashes: Hashes | None = None,
103
109
  ) -> File:
104
110
  temp_dir = TempDirectory(kind="unpack", globally_managed=True)
105
111
  # If a download dir is specified, is the file already downloaded there?
@@ -120,7 +126,7 @@ def get_http_url(
120
126
 
121
127
 
122
128
  def get_file_url(
123
- link: Link, download_dir: Optional[str] = None, hashes: Optional[Hashes] = None
129
+ link: Link, download_dir: str | None = None, hashes: Hashes | None = None
124
130
  ) -> File:
125
131
  """Get file and optionally check its hash."""
126
132
  # If a download dir is specified, is the file already there and valid?
@@ -148,9 +154,9 @@ def unpack_url(
148
154
  location: str,
149
155
  download: Downloader,
150
156
  verbosity: int,
151
- download_dir: Optional[str] = None,
152
- hashes: Optional[Hashes] = None,
153
- ) -> Optional[File]:
157
+ download_dir: str | None = None,
158
+ hashes: Hashes | None = None,
159
+ ) -> File | None:
154
160
  """Unpack link into location, downloading if required.
155
161
 
156
162
  :param hashes: A Hashes object, one of whose embedded hashes must match,
@@ -189,9 +195,9 @@ def unpack_url(
189
195
  def _check_download_dir(
190
196
  link: Link,
191
197
  download_dir: str,
192
- hashes: Optional[Hashes],
198
+ hashes: Hashes | None,
193
199
  warn_on_hash_mismatch: bool = True,
194
- ) -> Optional[str]:
200
+ ) -> str | None:
195
201
  """Check download_dir for previously downloaded file with correct hash
196
202
  If a correct file is found return its path else None
197
203
  """
@@ -219,16 +225,18 @@ def _check_download_dir(
219
225
  class RequirementPreparer:
220
226
  """Prepares a Requirement"""
221
227
 
222
- def __init__(
228
+ def __init__( # noqa: PLR0913 (too many parameters)
223
229
  self,
230
+ *,
224
231
  build_dir: str,
225
- download_dir: Optional[str],
232
+ download_dir: str | None,
226
233
  src_dir: str,
227
234
  build_isolation: bool,
235
+ build_isolation_installer: BuildEnvironmentInstaller,
228
236
  check_build_deps: bool,
229
237
  build_tracker: BuildTracker,
230
238
  session: PipSession,
231
- progress_bar: str,
239
+ progress_bar: BarType,
232
240
  finder: PackageFinder,
233
241
  require_hashes: bool,
234
242
  use_user_site: bool,
@@ -244,7 +252,6 @@ class RequirementPreparer:
244
252
  self.build_tracker = build_tracker
245
253
  self._session = session
246
254
  self._download = Downloader(session, progress_bar, resume_retries)
247
- self._batch_download = BatchDownloader(session, progress_bar, resume_retries)
248
255
  self.finder = finder
249
256
 
250
257
  # Where still-packed archives should be written to. If None, they are
@@ -253,6 +260,7 @@ class RequirementPreparer:
253
260
 
254
261
  # Is build isolation allowed?
255
262
  self.build_isolation = build_isolation
263
+ self.build_env_installer = build_isolation_installer
256
264
 
257
265
  # Should check build dependencies?
258
266
  self.check_build_deps = check_build_deps
@@ -273,7 +281,7 @@ class RequirementPreparer:
273
281
  self.legacy_resolver = legacy_resolver
274
282
 
275
283
  # Memoized downloaded files, as mapping of url: path.
276
- self._downloaded: Dict[str, str] = {}
284
+ self._downloaded: dict[str, str] = {}
277
285
 
278
286
  # Previous "header" printed for a link-based InstallRequirement
279
287
  self._previous_requirement_header = ("", "")
@@ -291,7 +299,7 @@ class RequirementPreparer:
291
299
  # would already be included if we used req directly)
292
300
  if req.req and req.comes_from:
293
301
  if isinstance(req.comes_from, str):
294
- comes_from: Optional[str] = req.comes_from
302
+ comes_from: str | None = req.comes_from
295
303
  else:
296
304
  comes_from = req.comes_from.from_path()
297
305
  if comes_from:
@@ -363,7 +371,7 @@ class RequirementPreparer:
363
371
  def _fetch_metadata_only(
364
372
  self,
365
373
  req: InstallRequirement,
366
- ) -> Optional[BaseDistribution]:
374
+ ) -> BaseDistribution | None:
367
375
  if self.legacy_resolver:
368
376
  logger.debug(
369
377
  "Metadata-only fetching is not used in the legacy resolver",
@@ -382,7 +390,7 @@ class RequirementPreparer:
382
390
  def _fetch_metadata_using_link_data_attr(
383
391
  self,
384
392
  req: InstallRequirement,
385
- ) -> Optional[BaseDistribution]:
393
+ ) -> BaseDistribution | None:
386
394
  """Fetch metadata from the data-dist-info-metadata attribute, if possible."""
387
395
  # (1) Get the link to the metadata file, if provided by the backend.
388
396
  metadata_link = req.link.metadata_link()
@@ -423,7 +431,7 @@ class RequirementPreparer:
423
431
  def _fetch_metadata_using_lazy_wheel(
424
432
  self,
425
433
  link: Link,
426
- ) -> Optional[BaseDistribution]:
434
+ ) -> BaseDistribution | None:
427
435
  """Fetch metadata using lazy wheel, if possible."""
428
436
  # --use-feature=fast-deps must be provided.
429
437
  if not self.use_lazy_wheel:
@@ -462,15 +470,12 @@ class RequirementPreparer:
462
470
  # Map each link to the requirement that owns it. This allows us to set
463
471
  # `req.local_file_path` on the appropriate requirement after passing
464
472
  # all the links at once into BatchDownloader.
465
- links_to_fully_download: Dict[Link, InstallRequirement] = {}
473
+ links_to_fully_download: dict[Link, InstallRequirement] = {}
466
474
  for req in partially_downloaded_reqs:
467
475
  assert req.link
468
476
  links_to_fully_download[req.link] = req
469
477
 
470
- batch_download = self._batch_download(
471
- links_to_fully_download.keys(),
472
- temp_dir,
473
- )
478
+ batch_download = self._download.batch(links_to_fully_download.keys(), temp_dir)
474
479
  for link, (filepath, _) in batch_download:
475
480
  logger.debug("Downloading link %s to %s", link, filepath)
476
481
  req = links_to_fully_download[link]
@@ -547,7 +552,7 @@ class RequirementPreparer:
547
552
 
548
553
  # Prepare requirements we found were already downloaded for some
549
554
  # reason. The other downloads will be completed separately.
550
- partially_downloaded_reqs: List[InstallRequirement] = []
555
+ partially_downloaded_reqs: list[InstallRequirement] = []
551
556
  for req in reqs:
552
557
  if req.needs_more_preparation:
553
558
  partially_downloaded_reqs.append(req)
@@ -647,7 +652,7 @@ class RequirementPreparer:
647
652
  dist = _get_prepared_distribution(
648
653
  req,
649
654
  self.build_tracker,
650
- self.finder,
655
+ self.build_env_installer,
651
656
  self.build_isolation,
652
657
  self.check_build_deps,
653
658
  )
@@ -703,7 +708,7 @@ class RequirementPreparer:
703
708
  dist = _get_prepared_distribution(
704
709
  req,
705
710
  self.build_tracker,
706
- self.finder,
711
+ self.build_env_installer,
707
712
  self.build_isolation,
708
713
  self.check_build_deps,
709
714
  )
@@ -1,13 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import importlib.util
2
4
  import os
3
- import sys
4
5
  from collections import namedtuple
5
- from typing import Any, List, Optional
6
-
7
- if sys.version_info >= (3, 11):
8
- import tomllib
9
- else:
10
- from pip._vendor import tomli as tomllib
6
+ from typing import Any
11
7
 
12
8
  from pip._vendor.packaging.requirements import InvalidRequirement
13
9
 
@@ -16,6 +12,7 @@ from pip._internal.exceptions import (
16
12
  InvalidPyProjectBuildRequires,
17
13
  MissingPyProjectBuildRequires,
18
14
  )
15
+ from pip._internal.utils.compat import tomllib
19
16
  from pip._internal.utils.packaging import get_requirement
20
17
 
21
18
 
@@ -33,8 +30,8 @@ BuildSystemDetails = namedtuple(
33
30
 
34
31
 
35
32
  def load_pyproject_toml(
36
- use_pep517: Optional[bool], pyproject_toml: str, setup_py: str, req_name: str
37
- ) -> Optional[BuildSystemDetails]:
33
+ use_pep517: bool | None, pyproject_toml: str, setup_py: str, req_name: str
34
+ ) -> BuildSystemDetails | None:
38
35
  """Load the pyproject.toml file.
39
36
 
40
37
  Parameters:
@@ -166,7 +163,7 @@ def load_pyproject_toml(
166
163
 
167
164
  backend = build_system.get("build-backend")
168
165
  backend_path = build_system.get("backend-path", [])
169
- check: List[str] = []
166
+ check: list[str] = []
170
167
  if backend is None:
171
168
  # If the user didn't specify a backend, we assume they want to use
172
169
  # the setuptools backend. But we can't be sure they have included
@@ -1,9 +1,11 @@
1
+ from __future__ import annotations
2
+
1
3
  import collections
2
4
  import logging
5
+ from collections.abc import Generator, Sequence
3
6
  from dataclasses import dataclass
4
- from typing import Generator, List, Optional, Sequence, Tuple
5
7
 
6
- from pip._internal.cli.progress_bars import get_install_progress_renderer
8
+ from pip._internal.cli.progress_bars import BarType, get_install_progress_renderer
7
9
  from pip._internal.utils.logging import indent_log
8
10
 
9
11
  from .req_file import parse_requirements
@@ -26,24 +28,24 @@ class InstallationResult:
26
28
 
27
29
 
28
30
  def _validate_requirements(
29
- requirements: List[InstallRequirement],
30
- ) -> Generator[Tuple[str, InstallRequirement], None, None]:
31
+ requirements: list[InstallRequirement],
32
+ ) -> Generator[tuple[str, InstallRequirement], None, None]:
31
33
  for req in requirements:
32
34
  assert req.name, f"invalid to-be-installed requirement: {req}"
33
35
  yield req.name, req
34
36
 
35
37
 
36
38
  def install_given_reqs(
37
- requirements: List[InstallRequirement],
39
+ requirements: list[InstallRequirement],
38
40
  global_options: Sequence[str],
39
- root: Optional[str],
40
- home: Optional[str],
41
- prefix: Optional[str],
41
+ root: str | None,
42
+ home: str | None,
43
+ prefix: str | None,
42
44
  warn_script_location: bool,
43
45
  use_user_site: bool,
44
46
  pycompile: bool,
45
- progress_bar: str,
46
- ) -> List[InstallationResult]:
47
+ progress_bar: BarType,
48
+ ) -> list[InstallationResult]:
47
49
  """
48
50
  Install everything in the given list.
49
51