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,20 +1,17 @@
1
1
  """Routines related to PyPI, indexes"""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import enum
4
6
  import functools
5
7
  import itertools
6
8
  import logging
7
9
  import re
10
+ from collections.abc import Iterable
8
11
  from dataclasses import dataclass
9
12
  from typing import (
10
13
  TYPE_CHECKING,
11
- Dict,
12
- FrozenSet,
13
- Iterable,
14
- List,
15
14
  Optional,
16
- Set,
17
- Tuple,
18
15
  Union,
19
16
  )
20
17
 
@@ -48,20 +45,20 @@ from pip._internal.utils.packaging import check_requires_python
48
45
  from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS
49
46
 
50
47
  if TYPE_CHECKING:
51
- from pip._vendor.typing_extensions import TypeGuard
48
+ from typing_extensions import TypeGuard
52
49
 
53
50
  __all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"]
54
51
 
55
52
 
56
53
  logger = getLogger(__name__)
57
54
 
58
- BuildTag = Union[Tuple[()], Tuple[int, str]]
59
- CandidateSortingKey = Tuple[int, int, int, _BaseVersion, Optional[int], BuildTag]
55
+ BuildTag = Union[tuple[()], tuple[int, str]]
56
+ CandidateSortingKey = tuple[int, int, int, _BaseVersion, Optional[int], BuildTag]
60
57
 
61
58
 
62
59
  def _check_link_requires_python(
63
60
  link: Link,
64
- version_info: Tuple[int, int, int],
61
+ version_info: tuple[int, int, int],
65
62
  ignore_requires_python: bool = False,
66
63
  ) -> bool:
67
64
  """
@@ -131,10 +128,10 @@ class LinkEvaluator:
131
128
  self,
132
129
  project_name: str,
133
130
  canonical_name: str,
134
- formats: FrozenSet[str],
131
+ formats: frozenset[str],
135
132
  target_python: TargetPython,
136
133
  allow_yanked: bool,
137
- ignore_requires_python: Optional[bool] = None,
134
+ ignore_requires_python: bool | None = None,
138
135
  ) -> None:
139
136
  """
140
137
  :param project_name: The user supplied package name.
@@ -164,7 +161,7 @@ class LinkEvaluator:
164
161
 
165
162
  self.project_name = project_name
166
163
 
167
- def evaluate_link(self, link: Link) -> Tuple[LinkType, str]:
164
+ def evaluate_link(self, link: Link) -> tuple[LinkType, str]:
168
165
  """
169
166
  Determine whether a link is a candidate for installation.
170
167
 
@@ -251,7 +248,19 @@ class LinkEvaluator:
251
248
  ignore_requires_python=self._ignore_requires_python,
252
249
  )
253
250
  if not supports_python:
254
- reason = f"{version} Requires-Python {link.requires_python}"
251
+ requires_python = link.requires_python
252
+ if requires_python:
253
+
254
+ def get_version_sort_key(v: str) -> tuple[int, ...]:
255
+ return tuple(int(s) for s in v.split(".") if s.isdigit())
256
+
257
+ requires_python = ",".join(
258
+ sorted(
259
+ (str(s) for s in specifiers.SpecifierSet(requires_python)),
260
+ key=get_version_sort_key,
261
+ )
262
+ )
263
+ reason = f"{version} Requires-Python {requires_python}"
255
264
  return (LinkType.requires_python_mismatch, reason)
256
265
 
257
266
  logger.debug("Found link %s, version: %s", link, version)
@@ -260,10 +269,10 @@ class LinkEvaluator:
260
269
 
261
270
 
262
271
  def filter_unallowed_hashes(
263
- candidates: List[InstallationCandidate],
264
- hashes: Optional[Hashes],
272
+ candidates: list[InstallationCandidate],
273
+ hashes: Hashes | None,
265
274
  project_name: str,
266
- ) -> List[InstallationCandidate]:
275
+ ) -> list[InstallationCandidate]:
267
276
  """
268
277
  Filter out candidates whose hashes aren't allowed, and return a new
269
278
  list of candidates.
@@ -357,9 +366,9 @@ class BestCandidateResult:
357
366
  if no applicable candidates were found.
358
367
  """
359
368
 
360
- all_candidates: List[InstallationCandidate]
361
- applicable_candidates: List[InstallationCandidate]
362
- best_candidate: Optional[InstallationCandidate]
369
+ all_candidates: list[InstallationCandidate]
370
+ applicable_candidates: list[InstallationCandidate]
371
+ best_candidate: InstallationCandidate | None
363
372
 
364
373
  def __post_init__(self) -> None:
365
374
  assert set(self.applicable_candidates) <= set(self.all_candidates)
@@ -380,12 +389,12 @@ class CandidateEvaluator:
380
389
  def create(
381
390
  cls,
382
391
  project_name: str,
383
- target_python: Optional[TargetPython] = None,
392
+ target_python: TargetPython | None = None,
384
393
  prefer_binary: bool = False,
385
394
  allow_all_prereleases: bool = False,
386
- specifier: Optional[specifiers.BaseSpecifier] = None,
387
- hashes: Optional[Hashes] = None,
388
- ) -> "CandidateEvaluator":
395
+ specifier: specifiers.BaseSpecifier | None = None,
396
+ hashes: Hashes | None = None,
397
+ ) -> CandidateEvaluator:
389
398
  """Create a CandidateEvaluator object.
390
399
 
391
400
  :param target_python: The target Python interpreter to use when
@@ -415,11 +424,11 @@ class CandidateEvaluator:
415
424
  def __init__(
416
425
  self,
417
426
  project_name: str,
418
- supported_tags: List[Tag],
427
+ supported_tags: list[Tag],
419
428
  specifier: specifiers.BaseSpecifier,
420
429
  prefer_binary: bool = False,
421
430
  allow_all_prereleases: bool = False,
422
- hashes: Optional[Hashes] = None,
431
+ hashes: Hashes | None = None,
423
432
  ) -> None:
424
433
  """
425
434
  :param supported_tags: The PEP 425 tags supported by the target
@@ -440,8 +449,8 @@ class CandidateEvaluator:
440
449
 
441
450
  def get_applicable_candidates(
442
451
  self,
443
- candidates: List[InstallationCandidate],
444
- ) -> List[InstallationCandidate]:
452
+ candidates: list[InstallationCandidate],
453
+ ) -> list[InstallationCandidate]:
445
454
  """
446
455
  Return the applicable candidates from a list of candidates.
447
456
  """
@@ -540,8 +549,8 @@ class CandidateEvaluator:
540
549
 
541
550
  def sort_best_candidate(
542
551
  self,
543
- candidates: List[InstallationCandidate],
544
- ) -> Optional[InstallationCandidate]:
552
+ candidates: list[InstallationCandidate],
553
+ ) -> InstallationCandidate | None:
545
554
  """
546
555
  Return the best candidate per the instance's sort order, or None if
547
556
  no candidate is acceptable.
@@ -553,7 +562,7 @@ class CandidateEvaluator:
553
562
 
554
563
  def compute_best_candidate(
555
564
  self,
556
- candidates: List[InstallationCandidate],
565
+ candidates: list[InstallationCandidate],
557
566
  ) -> BestCandidateResult:
558
567
  """
559
568
  Compute and return a `BestCandidateResult` instance.
@@ -581,9 +590,9 @@ class PackageFinder:
581
590
  link_collector: LinkCollector,
582
591
  target_python: TargetPython,
583
592
  allow_yanked: bool,
584
- format_control: Optional[FormatControl] = None,
585
- candidate_prefs: Optional[CandidatePreferences] = None,
586
- ignore_requires_python: Optional[bool] = None,
593
+ format_control: FormatControl | None = None,
594
+ candidate_prefs: CandidatePreferences | None = None,
595
+ ignore_requires_python: bool | None = None,
587
596
  ) -> None:
588
597
  """
589
598
  This constructor is primarily meant to be used by the create() class
@@ -609,12 +618,12 @@ class PackageFinder:
609
618
  self.format_control = format_control
610
619
 
611
620
  # These are boring links that have already been logged somehow.
612
- self._logged_links: Set[Tuple[Link, LinkType, str]] = set()
621
+ self._logged_links: set[tuple[Link, LinkType, str]] = set()
613
622
 
614
623
  # Cache of the result of finding candidates
615
- self._all_candidates: Dict[str, List[InstallationCandidate]] = {}
616
- self._best_candidates: Dict[
617
- Tuple[str, Optional[specifiers.BaseSpecifier], Optional[Hashes]],
624
+ self._all_candidates: dict[str, list[InstallationCandidate]] = {}
625
+ self._best_candidates: dict[
626
+ tuple[str, specifiers.BaseSpecifier | None, Hashes | None],
618
627
  BestCandidateResult,
619
628
  ] = {}
620
629
 
@@ -627,8 +636,8 @@ class PackageFinder:
627
636
  cls,
628
637
  link_collector: LinkCollector,
629
638
  selection_prefs: SelectionPreferences,
630
- target_python: Optional[TargetPython] = None,
631
- ) -> "PackageFinder":
639
+ target_python: TargetPython | None = None,
640
+ ) -> PackageFinder:
632
641
  """Create a PackageFinder.
633
642
 
634
643
  :param selection_prefs: The candidate selection preferences, as a
@@ -667,15 +676,15 @@ class PackageFinder:
667
676
  self._link_collector.search_scope = search_scope
668
677
 
669
678
  @property
670
- def find_links(self) -> List[str]:
679
+ def find_links(self) -> list[str]:
671
680
  return self._link_collector.find_links
672
681
 
673
682
  @property
674
- def index_urls(self) -> List[str]:
683
+ def index_urls(self) -> list[str]:
675
684
  return self.search_scope.index_urls
676
685
 
677
686
  @property
678
- def proxy(self) -> Optional[str]:
687
+ def proxy(self) -> str | None:
679
688
  return self._link_collector.session.pip_proxy
680
689
 
681
690
  @property
@@ -684,7 +693,7 @@ class PackageFinder:
684
693
  yield build_netloc(*host_port)
685
694
 
686
695
  @property
687
- def custom_cert(self) -> Optional[str]:
696
+ def custom_cert(self) -> str | None:
688
697
  # session.verify is either a boolean (use default bundle/no SSL
689
698
  # verification) or a string path to a custom CA bundle to use. We only
690
699
  # care about the latter.
@@ -692,7 +701,7 @@ class PackageFinder:
692
701
  return verify if isinstance(verify, str) else None
693
702
 
694
703
  @property
695
- def client_cert(self) -> Optional[str]:
704
+ def client_cert(self) -> str | None:
696
705
  cert = self._link_collector.session.cert
697
706
  assert not isinstance(cert, tuple), "pip only supports PEM client certs"
698
707
  return cert
@@ -711,7 +720,7 @@ class PackageFinder:
711
720
  def set_prefer_binary(self) -> None:
712
721
  self._candidate_prefs.prefer_binary = True
713
722
 
714
- def requires_python_skipped_reasons(self) -> List[str]:
723
+ def requires_python_skipped_reasons(self) -> list[str]:
715
724
  reasons = {
716
725
  detail
717
726
  for _, result, detail in self._logged_links
@@ -732,13 +741,13 @@ class PackageFinder:
732
741
  ignore_requires_python=self._ignore_requires_python,
733
742
  )
734
743
 
735
- def _sort_links(self, links: Iterable[Link]) -> List[Link]:
744
+ def _sort_links(self, links: Iterable[Link]) -> list[Link]:
736
745
  """
737
746
  Returns elements of links in order, non-egg links first, egg links
738
747
  second, while eliminating duplicates
739
748
  """
740
749
  eggs, no_eggs = [], []
741
- seen: Set[Link] = set()
750
+ seen: set[Link] = set()
742
751
  for link in links:
743
752
  if link not in seen:
744
753
  seen.add(link)
@@ -758,7 +767,7 @@ class PackageFinder:
758
767
 
759
768
  def get_install_candidate(
760
769
  self, link_evaluator: LinkEvaluator, link: Link
761
- ) -> Optional[InstallationCandidate]:
770
+ ) -> InstallationCandidate | None:
762
771
  """
763
772
  If the link is a candidate for install, convert it to an
764
773
  InstallationCandidate and return it. Otherwise, return None.
@@ -779,7 +788,7 @@ class PackageFinder:
779
788
 
780
789
  def evaluate_links(
781
790
  self, link_evaluator: LinkEvaluator, links: Iterable[Link]
782
- ) -> List[InstallationCandidate]:
791
+ ) -> list[InstallationCandidate]:
783
792
  """
784
793
  Convert links that are candidates to InstallationCandidate objects.
785
794
  """
@@ -793,7 +802,7 @@ class PackageFinder:
793
802
 
794
803
  def process_project_url(
795
804
  self, project_url: Link, link_evaluator: LinkEvaluator
796
- ) -> List[InstallationCandidate]:
805
+ ) -> list[InstallationCandidate]:
797
806
  logger.debug(
798
807
  "Fetching project page and analyzing links: %s",
799
808
  project_url,
@@ -812,7 +821,7 @@ class PackageFinder:
812
821
 
813
822
  return package_links
814
823
 
815
- def find_all_candidates(self, project_name: str) -> List[InstallationCandidate]:
824
+ def find_all_candidates(self, project_name: str) -> list[InstallationCandidate]:
816
825
  """Find all available InstallationCandidate for project_name
817
826
 
818
827
  This checks index_urls and find_links.
@@ -872,8 +881,8 @@ class PackageFinder:
872
881
  def make_candidate_evaluator(
873
882
  self,
874
883
  project_name: str,
875
- specifier: Optional[specifiers.BaseSpecifier] = None,
876
- hashes: Optional[Hashes] = None,
884
+ specifier: specifiers.BaseSpecifier | None = None,
885
+ hashes: Hashes | None = None,
877
886
  ) -> CandidateEvaluator:
878
887
  """Create a CandidateEvaluator object to use."""
879
888
  candidate_prefs = self._candidate_prefs
@@ -889,8 +898,8 @@ class PackageFinder:
889
898
  def find_best_candidate(
890
899
  self,
891
900
  project_name: str,
892
- specifier: Optional[specifiers.BaseSpecifier] = None,
893
- hashes: Optional[Hashes] = None,
901
+ specifier: specifiers.BaseSpecifier | None = None,
902
+ hashes: Hashes | None = None,
894
903
  ) -> BestCandidateResult:
895
904
  """Find matches for the given project and specifier.
896
905
 
@@ -917,7 +926,7 @@ class PackageFinder:
917
926
 
918
927
  def find_requirement(
919
928
  self, req: InstallRequirement, upgrade: bool
920
- ) -> Optional[InstallationCandidate]:
929
+ ) -> InstallationCandidate | None:
921
930
  """Try to find a Link matching req
922
931
 
923
932
  Expects req, an InstallRequirement and upgrade, a boolean
@@ -935,7 +944,7 @@ class PackageFinder:
935
944
  )
936
945
  best_candidate = best_candidate_result.best_candidate
937
946
 
938
- installed_version: Optional[_BaseVersion] = None
947
+ installed_version: _BaseVersion | None = None
939
948
  if req.satisfied_by is not None:
940
949
  installed_version = req.satisfied_by.version
941
950
 
@@ -965,8 +974,8 @@ class PackageFinder:
965
974
  raise DistributionNotFound(f"No matching distribution found for {req}")
966
975
 
967
976
  def _should_install_candidate(
968
- candidate: Optional[InstallationCandidate],
969
- ) -> "TypeGuard[InstallationCandidate]":
977
+ candidate: InstallationCandidate | None,
978
+ ) -> TypeGuard[InstallationCandidate]:
970
979
  if installed_version is None:
971
980
  return True
972
981
  if best_candidate is None:
@@ -1032,7 +1041,7 @@ def _find_name_version_sep(fragment: str, canonical_name: str) -> int:
1032
1041
  raise ValueError(f"{fragment} does not match {canonical_name}")
1033
1042
 
1034
1043
 
1035
- def _extract_version_from_fragment(fragment: str, canonical_name: str) -> Optional[str]:
1044
+ def _extract_version_from_fragment(fragment: str, canonical_name: str) -> str | None:
1036
1045
  """Parse the version string from a <package>+<version> filename
1037
1046
  "fragment" (stem) or egg fragment.
1038
1047
 
@@ -1,8 +1,11 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import mimetypes
3
5
  import os
4
6
  from collections import defaultdict
5
- from typing import Callable, Dict, Iterable, List, Optional, Tuple
7
+ from collections.abc import Iterable
8
+ from typing import Callable
6
9
 
7
10
  from pip._vendor.packaging.utils import (
8
11
  InvalidSdistFilename,
@@ -27,7 +30,7 @@ PageValidator = Callable[[Link], bool]
27
30
 
28
31
  class LinkSource:
29
32
  @property
30
- def link(self) -> Optional[Link]:
33
+ def link(self) -> Link | None:
31
34
  """Returns the underlying link, if there's one."""
32
35
  raise NotImplementedError()
33
36
 
@@ -49,8 +52,8 @@ class _FlatDirectoryToUrls:
49
52
 
50
53
  def __init__(self, path: str) -> None:
51
54
  self._path = path
52
- self._page_candidates: List[str] = []
53
- self._project_name_to_urls: Dict[str, List[str]] = defaultdict(list)
55
+ self._page_candidates: list[str] = []
56
+ self._project_name_to_urls: dict[str, list[str]] = defaultdict(list)
54
57
  self._scanned_directory = False
55
58
 
56
59
  def _scan_directory(self) -> None:
@@ -77,14 +80,14 @@ class _FlatDirectoryToUrls:
77
80
  self._scanned_directory = True
78
81
 
79
82
  @property
80
- def page_candidates(self) -> List[str]:
83
+ def page_candidates(self) -> list[str]:
81
84
  if not self._scanned_directory:
82
85
  self._scan_directory()
83
86
 
84
87
  return self._page_candidates
85
88
 
86
89
  @property
87
- def project_name_to_urls(self) -> Dict[str, List[str]]:
90
+ def project_name_to_urls(self) -> dict[str, list[str]]:
88
91
  if not self._scanned_directory:
89
92
  self._scan_directory()
90
93
 
@@ -100,7 +103,7 @@ class _FlatDirectorySource(LinkSource):
100
103
  * ``file_candidates``: Archives in the directory.
101
104
  """
102
105
 
103
- _paths_to_urls: Dict[str, _FlatDirectoryToUrls] = {}
106
+ _paths_to_urls: dict[str, _FlatDirectoryToUrls] = {}
104
107
 
105
108
  def __init__(
106
109
  self,
@@ -119,7 +122,7 @@ class _FlatDirectorySource(LinkSource):
119
122
  self._paths_to_urls[path] = self._path_to_urls
120
123
 
121
124
  @property
122
- def link(self) -> Optional[Link]:
125
+ def link(self) -> Link | None:
123
126
  return None
124
127
 
125
128
  def page_candidates(self) -> FoundCandidates:
@@ -150,7 +153,7 @@ class _LocalFileSource(LinkSource):
150
153
  self._link = link
151
154
 
152
155
  @property
153
- def link(self) -> Optional[Link]:
156
+ def link(self) -> Link | None:
154
157
  return self._link
155
158
 
156
159
  def page_candidates(self) -> FoundCandidates:
@@ -184,7 +187,7 @@ class _RemoteFileSource(LinkSource):
184
187
  self._link = link
185
188
 
186
189
  @property
187
- def link(self) -> Optional[Link]:
190
+ def link(self) -> Link | None:
188
191
  return self._link
189
192
 
190
193
  def page_candidates(self) -> FoundCandidates:
@@ -212,7 +215,7 @@ class _IndexDirectorySource(LinkSource):
212
215
  self._link = link
213
216
 
214
217
  @property
215
- def link(self) -> Optional[Link]:
218
+ def link(self) -> Link | None:
216
219
  return self._link
217
220
 
218
221
  def page_candidates(self) -> FoundCandidates:
@@ -230,9 +233,9 @@ def build_source(
230
233
  expand_dir: bool,
231
234
  cache_link_parsing: bool,
232
235
  project_name: str,
233
- ) -> Tuple[Optional[str], Optional[LinkSource]]:
234
- path: Optional[str] = None
235
- url: Optional[str] = None
236
+ ) -> tuple[str | None, LinkSource | None]:
237
+ path: str | None = None
238
+ url: str | None = None
236
239
  if os.path.exists(location): # Is a local path.
237
240
  url = path_to_url(location)
238
241
  path = location
@@ -1,10 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import functools
2
4
  import logging
3
5
  import os
4
6
  import pathlib
5
7
  import sys
6
8
  import sysconfig
7
- from typing import Any, Dict, Optional
9
+ from typing import Any
8
10
 
9
11
  from pip._internal.models.scheme import SCHEME_KEYS, Scheme
10
12
  from pip._internal.utils.compat import WINDOWS
@@ -87,7 +89,7 @@ def _looks_like_bpo_44860() -> bool:
87
89
  return unix_user_platlib == "$usersite"
88
90
 
89
91
 
90
- def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool:
92
+ def _looks_like_red_hat_patched_platlib_purelib(scheme: dict[str, str]) -> bool:
91
93
  platlib = scheme["platlib"]
92
94
  if "/$platlibdir/" in platlib:
93
95
  platlib = platlib.replace("/$platlibdir/", f"/{_PLATLIBDIR}/")
@@ -97,7 +99,7 @@ def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool:
97
99
  return unpatched.replace("$platbase/", "$base/") == scheme["purelib"]
98
100
 
99
101
 
100
- @functools.lru_cache(maxsize=None)
102
+ @functools.cache
101
103
  def _looks_like_red_hat_lib() -> bool:
102
104
  """Red Hat patches platlib in unix_prefix and unix_home, but not purelib.
103
105
 
@@ -112,7 +114,7 @@ def _looks_like_red_hat_lib() -> bool:
112
114
  )
113
115
 
114
116
 
115
- @functools.lru_cache(maxsize=None)
117
+ @functools.cache
116
118
  def _looks_like_debian_scheme() -> bool:
117
119
  """Debian adds two additional schemes."""
118
120
  from distutils.command.install import INSTALL_SCHEMES
@@ -120,7 +122,7 @@ def _looks_like_debian_scheme() -> bool:
120
122
  return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES
121
123
 
122
124
 
123
- @functools.lru_cache(maxsize=None)
125
+ @functools.cache
124
126
  def _looks_like_red_hat_scheme() -> bool:
125
127
  """Red Hat patches ``sys.prefix`` and ``sys.exec_prefix``.
126
128
 
@@ -140,7 +142,7 @@ def _looks_like_red_hat_scheme() -> bool:
140
142
  )
141
143
 
142
144
 
143
- @functools.lru_cache(maxsize=None)
145
+ @functools.cache
144
146
  def _looks_like_slackware_scheme() -> bool:
145
147
  """Slackware patches sysconfig but fails to patch distutils and site.
146
148
 
@@ -156,7 +158,7 @@ def _looks_like_slackware_scheme() -> bool:
156
158
  return "/lib64/" in paths["purelib"] and "/lib64/" not in user_site
157
159
 
158
160
 
159
- @functools.lru_cache(maxsize=None)
161
+ @functools.cache
160
162
  def _looks_like_msys2_mingw_scheme() -> bool:
161
163
  """MSYS2 patches distutils and sysconfig to use a UNIX-like scheme.
162
164
 
@@ -174,7 +176,7 @@ def _looks_like_msys2_mingw_scheme() -> bool:
174
176
  )
175
177
 
176
178
 
177
- @functools.lru_cache(maxsize=None)
179
+ @functools.cache
178
180
  def _warn_mismatched(old: pathlib.Path, new: pathlib.Path, *, key: str) -> None:
179
181
  issue_url = "https://github.com/pypa/pip/issues/10151"
180
182
  message = (
@@ -192,13 +194,13 @@ def _warn_if_mismatch(old: pathlib.Path, new: pathlib.Path, *, key: str) -> bool
192
194
  return True
193
195
 
194
196
 
195
- @functools.lru_cache(maxsize=None)
197
+ @functools.cache
196
198
  def _log_context(
197
199
  *,
198
200
  user: bool = False,
199
- home: Optional[str] = None,
200
- root: Optional[str] = None,
201
- prefix: Optional[str] = None,
201
+ home: str | None = None,
202
+ root: str | None = None,
203
+ prefix: str | None = None,
202
204
  ) -> None:
203
205
  parts = [
204
206
  "Additional context:",
@@ -214,10 +216,10 @@ def _log_context(
214
216
  def get_scheme(
215
217
  dist_name: str,
216
218
  user: bool = False,
217
- home: Optional[str] = None,
218
- root: Optional[str] = None,
219
+ home: str | None = None,
220
+ root: str | None = None,
219
221
  isolated: bool = False,
220
- prefix: Optional[str] = None,
222
+ prefix: str | None = None,
221
223
  ) -> Scheme:
222
224
  new = _sysconfig.get_scheme(
223
225
  dist_name,
@@ -281,7 +283,7 @@ def get_scheme(
281
283
  continue
282
284
 
283
285
  # On Python 3.9+, sysconfig's posix_user scheme sets platlib against
284
- # sys.platlibdir, but distutils's unix_user incorrectly coninutes
286
+ # sys.platlibdir, but distutils's unix_user incorrectly continues
285
287
  # using the same $usersite for both platlib and purelib. This creates a
286
288
  # mismatch when sys.platlibdir is not "lib".
287
289
  skip_bpo_44860 = (
@@ -9,6 +9,8 @@
9
9
  #
10
10
  # See https://github.com/pypa/pip/issues/8761 for the original discussion and
11
11
  # rationale for why this is done within pip.
12
+ from __future__ import annotations
13
+
12
14
  try:
13
15
  __import__("_distutils_hack").remove_shim()
14
16
  except (ImportError, AttributeError):
@@ -21,7 +23,6 @@ from distutils.cmd import Command as DistutilsCommand
21
23
  from distutils.command.install import SCHEME_KEYS
22
24
  from distutils.command.install import install as distutils_install_command
23
25
  from distutils.sysconfig import get_python_lib
24
- from typing import Dict, List, Optional, Union
25
26
 
26
27
  from pip._internal.models.scheme import Scheme
27
28
  from pip._internal.utils.compat import WINDOWS
@@ -35,19 +36,19 @@ logger = logging.getLogger(__name__)
35
36
  def distutils_scheme(
36
37
  dist_name: str,
37
38
  user: bool = False,
38
- home: Optional[str] = None,
39
- root: Optional[str] = None,
39
+ home: str | None = None,
40
+ root: str | None = None,
40
41
  isolated: bool = False,
41
- prefix: Optional[str] = None,
42
+ prefix: str | None = None,
42
43
  *,
43
44
  ignore_config_files: bool = False,
44
- ) -> Dict[str, str]:
45
+ ) -> dict[str, str]:
45
46
  """
46
47
  Return a distutils install scheme
47
48
  """
48
49
  from distutils.dist import Distribution
49
50
 
50
- dist_args: Dict[str, Union[str, List[str]]] = {"name": dist_name}
51
+ dist_args: dict[str, str | list[str]] = {"name": dist_name}
51
52
  if isolated:
52
53
  dist_args["script_args"] = ["--no-user-cfg"]
53
54
 
@@ -61,7 +62,7 @@ def distutils_scheme(
61
62
  "Ignore distutils configs in %s due to encoding errors.",
62
63
  ", ".join(os.path.basename(p) for p in paths),
63
64
  )
64
- obj: Optional[DistutilsCommand] = None
65
+ obj: DistutilsCommand | None = None
65
66
  obj = d.get_command_obj("install", create=True)
66
67
  assert obj is not None
67
68
  i: distutils_install_command = obj
@@ -78,7 +79,7 @@ def distutils_scheme(
78
79
  i.root = root or i.root
79
80
  i.finalize_options()
80
81
 
81
- scheme: Dict[str, str] = {}
82
+ scheme: dict[str, str] = {}
82
83
  for key in SCHEME_KEYS:
83
84
  scheme[key] = getattr(i, "install_" + key)
84
85
 
@@ -115,10 +116,10 @@ def distutils_scheme(
115
116
  def get_scheme(
116
117
  dist_name: str,
117
118
  user: bool = False,
118
- home: Optional[str] = None,
119
- root: Optional[str] = None,
119
+ home: str | None = None,
120
+ root: str | None = None,
120
121
  isolated: bool = False,
121
- prefix: Optional[str] = None,
122
+ prefix: str | None = None,
122
123
  ) -> Scheme:
123
124
  """
124
125
  Get the "scheme" corresponding to the input parameters. The distutils
@@ -1,8 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import os
3
5
  import sys
4
6
  import sysconfig
5
- import typing
6
7
 
7
8
  from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid
8
9
  from pip._internal.models.scheme import SCHEME_KEYS, Scheme
@@ -124,10 +125,10 @@ if sysconfig.get_config_var("userbase") is not None:
124
125
  def get_scheme(
125
126
  dist_name: str,
126
127
  user: bool = False,
127
- home: typing.Optional[str] = None,
128
- root: typing.Optional[str] = None,
128
+ home: str | None = None,
129
+ root: str | None = None,
129
130
  isolated: bool = False,
130
- prefix: typing.Optional[str] = None,
131
+ prefix: str | None = None,
131
132
  ) -> Scheme:
132
133
  """
133
134
  Get the "scheme" corresponding to the input parameters.