pip 25.1.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 (202) 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 +44 -39
  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 +49 -41
  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 +7 -11
  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 +37 -45
  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/distlib/__init__.py +2 -2
  133. pip/_vendor/distlib/scripts.py +1 -1
  134. pip/_vendor/msgpack/__init__.py +2 -2
  135. pip/_vendor/pkg_resources/__init__.py +1 -1
  136. pip/_vendor/platformdirs/version.py +2 -2
  137. pip/_vendor/pygments/__init__.py +1 -1
  138. pip/_vendor/requests/__version__.py +2 -2
  139. pip/_vendor/requests/compat.py +12 -0
  140. pip/_vendor/requests/models.py +3 -1
  141. pip/_vendor/requests/utils.py +6 -16
  142. pip/_vendor/resolvelib/__init__.py +3 -3
  143. pip/_vendor/resolvelib/reporters.py +1 -1
  144. pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
  145. pip/_vendor/resolvelib/resolvers/resolution.py +91 -10
  146. pip/_vendor/rich/__main__.py +12 -40
  147. pip/_vendor/rich/_inspect.py +1 -1
  148. pip/_vendor/rich/_ratio.py +1 -7
  149. pip/_vendor/rich/align.py +1 -7
  150. pip/_vendor/rich/box.py +1 -7
  151. pip/_vendor/rich/console.py +25 -20
  152. pip/_vendor/rich/control.py +1 -7
  153. pip/_vendor/rich/diagnose.py +1 -0
  154. pip/_vendor/rich/emoji.py +1 -6
  155. pip/_vendor/rich/live.py +32 -7
  156. pip/_vendor/rich/live_render.py +1 -7
  157. pip/_vendor/rich/logging.py +1 -1
  158. pip/_vendor/rich/panel.py +3 -4
  159. pip/_vendor/rich/progress.py +15 -15
  160. pip/_vendor/rich/spinner.py +7 -13
  161. pip/_vendor/rich/syntax.py +24 -5
  162. pip/_vendor/rich/traceback.py +32 -17
  163. pip/_vendor/truststore/_api.py +1 -1
  164. pip/_vendor/vendor.txt +9 -10
  165. {pip-25.1.1.dist-info → pip-25.2.dist-info}/METADATA +26 -4
  166. {pip-25.1.1.dist-info → pip-25.2.dist-info}/RECORD +193 -180
  167. {pip-25.1.1.dist-info → pip-25.2.dist-info}/WHEEL +1 -1
  168. {pip-25.1.1.dist-info → pip-25.2.dist-info}/licenses/AUTHORS.txt +12 -0
  169. pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  170. pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
  171. pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  172. pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
  173. pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
  174. pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
  175. pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
  176. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
  177. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
  178. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
  179. pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
  180. pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
  181. pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
  182. pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
  183. pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
  184. pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
  185. pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
  186. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
  187. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +3 -0
  188. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
  189. pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
  190. pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
  191. pip/_vendor/distlib/database.py +0 -1329
  192. pip/_vendor/distlib/index.py +0 -508
  193. pip/_vendor/distlib/locators.py +0 -1295
  194. pip/_vendor/distlib/manifest.py +0 -384
  195. pip/_vendor/distlib/markers.py +0 -162
  196. pip/_vendor/distlib/metadata.py +0 -1031
  197. pip/_vendor/distlib/version.py +0 -750
  198. pip/_vendor/distlib/wheel.py +0 -1100
  199. pip/_vendor/typing_extensions.py +0 -4584
  200. {pip-25.1.1.dist-info → pip-25.2.dist-info}/entry_points.txt +0 -0
  201. {pip-25.1.1.dist-info → pip-25.2.dist-info}/licenses/LICENSE.txt +0 -0
  202. {pip-25.1.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,8 +12,10 @@ 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
20
  from itertools import chain, filterfalse, starmap
17
21
  from typing import (
@@ -19,17 +23,8 @@ from typing import (
19
23
  Any,
20
24
  BinaryIO,
21
25
  Callable,
22
- Dict,
23
- Generator,
24
- Iterable,
25
- Iterator,
26
- List,
27
26
  NewType,
28
- Optional,
29
27
  Protocol,
30
- Sequence,
31
- Set,
32
- Tuple,
33
28
  Union,
34
29
  cast,
35
30
  )
@@ -60,7 +55,7 @@ from pip._internal.utils.wheel import parse_wheel
60
55
 
61
56
 
62
57
  class File(Protocol):
63
- src_record_path: "RecordPath"
58
+ src_record_path: RecordPath
64
59
  dest_path: str
65
60
  changed: bool
66
61
 
@@ -71,17 +66,17 @@ class File(Protocol):
71
66
  logger = logging.getLogger(__name__)
72
67
 
73
68
  RecordPath = NewType("RecordPath", str)
74
- InstalledCSVRow = Tuple[RecordPath, str, Union[int, str]]
69
+ InstalledCSVRow = tuple[RecordPath, str, Union[int, str]]
75
70
 
76
71
 
77
- def rehash(path: str, blocksize: int = 1 << 20) -> Tuple[str, str]:
72
+ def rehash(path: str, blocksize: int = 1 << 20) -> tuple[str, str]:
78
73
  """Return (encoded_digest, length) for path using hashlib.sha256()"""
79
74
  h, length = hash_file(path, blocksize)
80
75
  digest = "sha256=" + urlsafe_b64encode(h.digest()).decode("latin1").rstrip("=")
81
76
  return (digest, str(length))
82
77
 
83
78
 
84
- def csv_io_kwargs(mode: str) -> Dict[str, Any]:
79
+ def csv_io_kwargs(mode: str) -> dict[str, Any]:
85
80
  """Return keyword arguments to properly open a CSV file
86
81
  in the given mode.
87
82
  """
@@ -112,7 +107,7 @@ def wheel_root_is_purelib(metadata: Message) -> bool:
112
107
  return metadata.get("Root-Is-Purelib", "").lower() == "true"
113
108
 
114
109
 
115
- 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]]:
116
111
  console_scripts = {}
117
112
  gui_scripts = {}
118
113
  for entry_point in dist.iter_entry_points():
@@ -123,7 +118,7 @@ def get_entrypoints(dist: BaseDistribution) -> Tuple[Dict[str, str], Dict[str, s
123
118
  return console_scripts, gui_scripts
124
119
 
125
120
 
126
- 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:
127
122
  """Determine if any scripts are not on PATH and format a warning.
128
123
  Returns a warning message if one or more scripts are not on PATH,
129
124
  otherwise None.
@@ -132,7 +127,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
132
127
  return None
133
128
 
134
129
  # Group scripts by the path they were installed in
135
- grouped_by_dir: Dict[str, Set[str]] = collections.defaultdict(set)
130
+ grouped_by_dir: dict[str, set[str]] = collections.defaultdict(set)
136
131
  for destfile in scripts:
137
132
  parent_dir = os.path.dirname(destfile)
138
133
  script_name = os.path.basename(destfile)
@@ -148,7 +143,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
148
143
  not_warn_dirs.append(
149
144
  os.path.normcase(os.path.normpath(os.path.dirname(sys.executable)))
150
145
  )
151
- warn_for: Dict[str, Set[str]] = {
146
+ warn_for: dict[str, set[str]] = {
152
147
  parent_dir: scripts
153
148
  for parent_dir, scripts in grouped_by_dir.items()
154
149
  if os.path.normcase(os.path.normpath(parent_dir)) not in not_warn_dirs
@@ -159,7 +154,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
159
154
  # Format a message
160
155
  msg_lines = []
161
156
  for parent_dir, dir_scripts in warn_for.items():
162
- sorted_scripts: List[str] = sorted(dir_scripts)
157
+ sorted_scripts: list[str] = sorted(dir_scripts)
163
158
  if len(sorted_scripts) == 1:
164
159
  start_text = f"script {sorted_scripts[0]} is"
165
160
  else:
@@ -197,7 +192,7 @@ def message_about_scripts_not_on_PATH(scripts: Sequence[str]) -> Optional[str]:
197
192
 
198
193
  def _normalized_outrows(
199
194
  outrows: Iterable[InstalledCSVRow],
200
- ) -> List[Tuple[str, str, str]]:
195
+ ) -> list[tuple[str, str, str]]:
201
196
  """Normalize the given rows of a RECORD file.
202
197
 
203
198
  Items in each row are converted into str. Rows are then sorted to make
@@ -236,17 +231,17 @@ def _fs_to_record_path(path: str, lib_dir: str) -> RecordPath:
236
231
 
237
232
 
238
233
  def get_csv_rows_for_installed(
239
- old_csv_rows: List[List[str]],
240
- installed: Dict[RecordPath, RecordPath],
241
- changed: Set[RecordPath],
242
- generated: List[str],
234
+ old_csv_rows: list[list[str]],
235
+ installed: dict[RecordPath, RecordPath],
236
+ changed: set[RecordPath],
237
+ generated: list[str],
243
238
  lib_dir: str,
244
- ) -> List[InstalledCSVRow]:
239
+ ) -> list[InstalledCSVRow]:
245
240
  """
246
241
  :param installed: A map from archive RECORD path to installation RECORD
247
242
  path.
248
243
  """
249
- installed_rows: List[InstalledCSVRow] = []
244
+ installed_rows: list[InstalledCSVRow] = []
250
245
  for row in old_csv_rows:
251
246
  if len(row) > 3:
252
247
  logger.warning("RECORD line has more than three elements: %s", row)
@@ -267,7 +262,7 @@ def get_csv_rows_for_installed(
267
262
  ]
268
263
 
269
264
 
270
- def get_console_script_specs(console: Dict[str, str]) -> List[str]:
265
+ def get_console_script_specs(console: dict[str, str]) -> list[str]:
271
266
  """
272
267
  Given the mapping from entrypoint name to callable, return the relevant
273
268
  console script specs.
@@ -381,7 +376,7 @@ class ZipBackedFile:
381
376
 
382
377
 
383
378
  class ScriptFile:
384
- def __init__(self, file: "File") -> None:
379
+ def __init__(self, file: File) -> None:
385
380
  self._file = file
386
381
  self.src_record_path = self._file.src_record_path
387
382
  self.dest_path = self._file.dest_path
@@ -396,7 +391,7 @@ class MissingCallableSuffix(InstallationError):
396
391
  def __init__(self, entry_point: str) -> None:
397
392
  super().__init__(
398
393
  f"Invalid script entry point: {entry_point} - A callable "
399
- "suffix is required. Cf https://packaging.python.org/"
394
+ "suffix is required. See https://packaging.python.org/"
400
395
  "specifications/entry-points/#use-for-scripts for more "
401
396
  "information."
402
397
  )
@@ -409,9 +404,22 @@ def _raise_for_invalid_entrypoint(specification: str) -> None:
409
404
 
410
405
 
411
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
+
412
420
  def make(
413
- self, specification: str, options: Optional[Dict[str, Any]] = None
414
- ) -> List[str]:
421
+ self, specification: str, options: dict[str, Any] | None = None
422
+ ) -> list[str]:
415
423
  _raise_for_invalid_entrypoint(specification)
416
424
  return super().make(specification, options)
417
425
 
@@ -423,7 +431,7 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
423
431
  scheme: Scheme,
424
432
  pycompile: bool = True,
425
433
  warn_script_location: bool = True,
426
- direct_url: Optional[DirectUrl] = None,
434
+ direct_url: DirectUrl | None = None,
427
435
  requested: bool = False,
428
436
  ) -> None:
429
437
  """Install a wheel.
@@ -452,9 +460,9 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
452
460
  # installed = files copied from the wheel to the destination
453
461
  # changed = files changed while installing (scripts #! line typically)
454
462
  # generated = files newly generated during the install (script wrappers)
455
- installed: Dict[RecordPath, RecordPath] = {}
456
- changed: Set[RecordPath] = set()
457
- generated: List[str] = []
463
+ installed: dict[RecordPath, RecordPath] = {}
464
+ changed: set[RecordPath] = set()
465
+ generated: list[str] = []
458
466
 
459
467
  def record_installed(
460
468
  srcfile: RecordPath, destfile: str, modified: bool = False
@@ -480,8 +488,8 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
480
488
 
481
489
  def root_scheme_file_maker(
482
490
  zip_file: ZipFile, dest: str
483
- ) -> Callable[[RecordPath], "File"]:
484
- def make_root_scheme_file(record_path: RecordPath) -> "File":
491
+ ) -> Callable[[RecordPath], File]:
492
+ def make_root_scheme_file(record_path: RecordPath) -> File:
485
493
  normed_path = os.path.normpath(record_path)
486
494
  dest_path = os.path.join(dest, normed_path)
487
495
  assert_no_path_traversal(dest, dest_path)
@@ -491,10 +499,10 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
491
499
 
492
500
  def data_scheme_file_maker(
493
501
  zip_file: ZipFile, scheme: Scheme
494
- ) -> Callable[[RecordPath], "File"]:
502
+ ) -> Callable[[RecordPath], File]:
495
503
  scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS}
496
504
 
497
- def make_data_scheme_file(record_path: RecordPath) -> "File":
505
+ def make_data_scheme_file(record_path: RecordPath) -> File:
498
506
  normed_path = os.path.normpath(record_path)
499
507
  try:
500
508
  _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2)
@@ -526,7 +534,7 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
526
534
  def is_data_scheme_path(path: RecordPath) -> bool:
527
535
  return path.split("/", 1)[0].endswith(".data")
528
536
 
529
- paths = cast(List[RecordPath], wheel_zip.namelist())
537
+ paths = cast(list[RecordPath], wheel_zip.namelist())
530
538
  file_paths = filterfalse(is_dir_path, paths)
531
539
  root_scheme_paths, data_scheme_paths = partition(is_data_scheme_path, file_paths)
532
540
 
@@ -552,7 +560,7 @@ def _install_wheel( # noqa: C901, PLR0915 function is too long
552
560
  )
553
561
  console, gui = get_entrypoints(distribution)
554
562
 
555
- def is_entrypoint_wrapper(file: "File") -> bool:
563
+ def is_entrypoint_wrapper(file: File) -> bool:
556
564
  # EP, EP.exe and EP-script.py are scripts generated for
557
565
  # entry point EP by setuptools
558
566
  path = file.dest_path
@@ -721,7 +729,7 @@ def install_wheel(
721
729
  req_description: str,
722
730
  pycompile: bool = True,
723
731
  warn_script_location: bool = True,
724
- direct_url: Optional[DirectUrl] = None,
732
+ direct_url: DirectUrl | None = None,
725
733
  requested: bool = False,
726
734
  ) -> None:
727
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