pip 25.1.1__py3-none-any.whl → 25.3__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 (236) hide show
  1. pip/__init__.py +3 -3
  2. pip/_internal/__init__.py +2 -2
  3. pip/_internal/build_env.py +186 -94
  4. pip/_internal/cache.py +17 -15
  5. pip/_internal/cli/autocompletion.py +13 -4
  6. pip/_internal/cli/base_command.py +18 -7
  7. pip/_internal/cli/cmdoptions.py +57 -80
  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 +24 -20
  13. pip/_internal/cli/progress_bars.py +19 -12
  14. pip/_internal/cli/req_command.py +57 -33
  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 +6 -10
  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 +63 -53
  29. pip/_internal/commands/list.py +35 -26
  30. pip/_internal/commands/lock.py +4 -8
  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 +7 -13
  35. pip/_internal/configuration.py +40 -27
  36. pip/_internal/distributions/base.py +6 -4
  37. pip/_internal/distributions/installed.py +8 -4
  38. pip/_internal/distributions/sdist.py +33 -27
  39. pip/_internal/distributions/wheel.py +6 -4
  40. pip/_internal/exceptions.py +78 -42
  41. pip/_internal/index/collector.py +24 -29
  42. pip/_internal/index/package_finder.py +73 -64
  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 +14 -7
  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 +20 -19
  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 +12 -71
  65. pip/_internal/network/auth.py +20 -22
  66. pip/_internal/network/cache.py +28 -17
  67. pip/_internal/network/download.py +169 -141
  68. pip/_internal/network/lazy_wheel.py +15 -10
  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 +7 -6
  74. pip/_internal/operations/build/wheel_editable.py +7 -6
  75. pip/_internal/operations/check.py +21 -26
  76. pip/_internal/operations/freeze.py +12 -9
  77. pip/_internal/operations/install/wheel.py +49 -41
  78. pip/_internal/operations/prepare.py +42 -31
  79. pip/_internal/pyproject.py +7 -69
  80. pip/_internal/req/__init__.py +12 -12
  81. pip/_internal/req/constructors.py +68 -62
  82. pip/_internal/req/req_dependency_group.py +7 -11
  83. pip/_internal/req/req_file.py +32 -36
  84. pip/_internal/req/req_install.py +64 -170
  85. pip/_internal/req/req_set.py +4 -5
  86. pip/_internal/req/req_uninstall.py +20 -17
  87. pip/_internal/resolution/base.py +3 -3
  88. pip/_internal/resolution/legacy/resolver.py +21 -20
  89. pip/_internal/resolution/resolvelib/base.py +16 -13
  90. pip/_internal/resolution/resolvelib/candidates.py +49 -37
  91. pip/_internal/resolution/resolvelib/factory.py +72 -50
  92. pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
  93. pip/_internal/resolution/resolvelib/provider.py +24 -20
  94. pip/_internal/resolution/resolvelib/reporter.py +26 -11
  95. pip/_internal/resolution/resolvelib/requirements.py +8 -6
  96. pip/_internal/resolution/resolvelib/resolver.py +41 -29
  97. pip/_internal/self_outdated_check.py +19 -9
  98. pip/_internal/utils/appdirs.py +1 -2
  99. pip/_internal/utils/compat.py +7 -1
  100. pip/_internal/utils/compatibility_tags.py +17 -16
  101. pip/_internal/utils/deprecation.py +11 -9
  102. pip/_internal/utils/direct_url_helpers.py +2 -2
  103. pip/_internal/utils/egg_link.py +6 -5
  104. pip/_internal/utils/entrypoints.py +3 -2
  105. pip/_internal/utils/filesystem.py +20 -5
  106. pip/_internal/utils/filetypes.py +4 -6
  107. pip/_internal/utils/glibc.py +6 -5
  108. pip/_internal/utils/hashes.py +9 -6
  109. pip/_internal/utils/logging.py +8 -5
  110. pip/_internal/utils/misc.py +37 -45
  111. pip/_internal/utils/packaging.py +3 -2
  112. pip/_internal/utils/retry.py +7 -4
  113. pip/_internal/utils/subprocess.py +20 -17
  114. pip/_internal/utils/temp_dir.py +10 -12
  115. pip/_internal/utils/unpacking.py +31 -4
  116. pip/_internal/utils/urls.py +1 -1
  117. pip/_internal/utils/virtualenv.py +3 -2
  118. pip/_internal/utils/wheel.py +3 -4
  119. pip/_internal/vcs/bazaar.py +26 -8
  120. pip/_internal/vcs/git.py +59 -24
  121. pip/_internal/vcs/mercurial.py +34 -11
  122. pip/_internal/vcs/subversion.py +27 -16
  123. pip/_internal/vcs/versioncontrol.py +56 -51
  124. pip/_internal/wheel_builder.py +30 -101
  125. pip/_vendor/README.rst +180 -0
  126. pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  127. pip/_vendor/cachecontrol/__init__.py +1 -1
  128. pip/_vendor/certifi/LICENSE +20 -0
  129. pip/_vendor/certifi/__init__.py +1 -1
  130. pip/_vendor/certifi/cacert.pem +164 -261
  131. pip/_vendor/certifi/core.py +1 -32
  132. pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  133. pip/_vendor/distlib/LICENSE.txt +284 -0
  134. pip/_vendor/distlib/__init__.py +2 -2
  135. pip/_vendor/distlib/scripts.py +1 -1
  136. pip/_vendor/distro/LICENSE +202 -0
  137. pip/_vendor/idna/LICENSE.md +31 -0
  138. pip/_vendor/msgpack/COPYING +14 -0
  139. pip/_vendor/msgpack/__init__.py +2 -2
  140. pip/_vendor/packaging/LICENSE +3 -0
  141. pip/_vendor/packaging/LICENSE.APACHE +177 -0
  142. pip/_vendor/packaging/LICENSE.BSD +23 -0
  143. pip/_vendor/pkg_resources/LICENSE +17 -0
  144. pip/_vendor/pkg_resources/__init__.py +1 -1
  145. pip/_vendor/platformdirs/LICENSE +21 -0
  146. pip/_vendor/platformdirs/api.py +1 -1
  147. pip/_vendor/platformdirs/macos.py +10 -8
  148. pip/_vendor/platformdirs/version.py +16 -3
  149. pip/_vendor/pygments/LICENSE +25 -0
  150. pip/_vendor/pygments/__init__.py +1 -1
  151. pip/_vendor/pyproject_hooks/LICENSE +21 -0
  152. pip/_vendor/requests/LICENSE +175 -0
  153. pip/_vendor/requests/__version__.py +2 -2
  154. pip/_vendor/requests/adapters.py +17 -40
  155. pip/_vendor/requests/compat.py +12 -0
  156. pip/_vendor/requests/models.py +3 -1
  157. pip/_vendor/requests/sessions.py +1 -1
  158. pip/_vendor/requests/utils.py +6 -16
  159. pip/_vendor/resolvelib/LICENSE +13 -0
  160. pip/_vendor/resolvelib/__init__.py +3 -3
  161. pip/_vendor/resolvelib/reporters.py +1 -1
  162. pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
  163. pip/_vendor/resolvelib/resolvers/abstract.py +3 -3
  164. pip/_vendor/resolvelib/resolvers/resolution.py +96 -10
  165. pip/_vendor/rich/LICENSE +19 -0
  166. pip/_vendor/rich/__main__.py +12 -40
  167. pip/_vendor/rich/_inspect.py +1 -1
  168. pip/_vendor/rich/_ratio.py +1 -7
  169. pip/_vendor/rich/align.py +1 -7
  170. pip/_vendor/rich/box.py +1 -7
  171. pip/_vendor/rich/console.py +25 -20
  172. pip/_vendor/rich/control.py +1 -7
  173. pip/_vendor/rich/diagnose.py +1 -0
  174. pip/_vendor/rich/emoji.py +1 -6
  175. pip/_vendor/rich/live.py +32 -7
  176. pip/_vendor/rich/live_render.py +1 -7
  177. pip/_vendor/rich/logging.py +1 -1
  178. pip/_vendor/rich/panel.py +3 -4
  179. pip/_vendor/rich/progress.py +15 -15
  180. pip/_vendor/rich/spinner.py +7 -13
  181. pip/_vendor/rich/style.py +7 -11
  182. pip/_vendor/rich/syntax.py +24 -5
  183. pip/_vendor/rich/traceback.py +32 -17
  184. pip/_vendor/tomli/LICENSE +21 -0
  185. pip/_vendor/tomli/__init__.py +1 -1
  186. pip/_vendor/tomli/_parser.py +28 -21
  187. pip/_vendor/tomli/_re.py +8 -5
  188. pip/_vendor/tomli_w/LICENSE +21 -0
  189. pip/_vendor/truststore/LICENSE +21 -0
  190. pip/_vendor/truststore/__init__.py +1 -1
  191. pip/_vendor/truststore/_api.py +15 -7
  192. pip/_vendor/truststore/_openssl.py +3 -1
  193. pip/_vendor/urllib3/LICENSE.txt +21 -0
  194. pip/_vendor/vendor.txt +11 -12
  195. {pip-25.1.1.dist-info → pip-25.3.dist-info}/METADATA +32 -11
  196. {pip-25.1.1.dist-info → pip-25.3.dist-info}/RECORD +221 -192
  197. {pip-25.1.1.dist-info → pip-25.3.dist-info}/WHEEL +1 -2
  198. pip-25.3.dist-info/entry_points.txt +4 -0
  199. {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/AUTHORS.txt +21 -0
  200. pip-25.3.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  201. pip-25.3.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
  202. pip-25.3.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  203. pip-25.3.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
  204. pip-25.3.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
  205. pip-25.3.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
  206. pip-25.3.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
  207. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
  208. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
  209. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
  210. pip-25.3.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
  211. pip-25.3.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
  212. pip-25.3.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
  213. pip-25.3.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
  214. pip-25.3.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
  215. pip-25.3.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
  216. pip-25.3.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
  217. pip-25.3.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
  218. pip-25.3.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
  219. pip-25.3.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
  220. pip-25.3.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
  221. pip/_internal/operations/build/metadata_legacy.py +0 -73
  222. pip/_internal/operations/build/wheel_legacy.py +0 -118
  223. pip/_internal/operations/install/editable_legacy.py +0 -46
  224. pip/_internal/utils/setuptools_build.py +0 -147
  225. pip/_vendor/distlib/database.py +0 -1329
  226. pip/_vendor/distlib/index.py +0 -508
  227. pip/_vendor/distlib/locators.py +0 -1295
  228. pip/_vendor/distlib/manifest.py +0 -384
  229. pip/_vendor/distlib/markers.py +0 -162
  230. pip/_vendor/distlib/metadata.py +0 -1031
  231. pip/_vendor/distlib/version.py +0 -750
  232. pip/_vendor/distlib/wheel.py +0 -1100
  233. pip/_vendor/typing_extensions.py +0 -4584
  234. pip-25.1.1.dist-info/entry_points.txt +0 -3
  235. pip-25.1.1.dist-info/top_level.txt +0 -1
  236. {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,4 +1,4 @@
1
- from typing import Dict, Generator
1
+ from collections.abc import Generator
2
2
 
3
3
  from pip._vendor.requests.models import Response
4
4
 
@@ -23,7 +23,7 @@ from pip._internal.exceptions import NetworkConnectionError
23
23
  # you're not asking for a compressed file and will then decompress it
24
24
  # before sending because if that's the case I don't think it'll ever be
25
25
  # possible to make this work.
26
- HEADERS: Dict[str, str] = {"Accept-Encoding": "identity"}
26
+ HEADERS: dict[str, str] = {"Accept-Encoding": "identity"}
27
27
 
28
28
  DOWNLOAD_CHUNK_SIZE = 256 * 1024
29
29
 
@@ -3,7 +3,7 @@
3
3
  import logging
4
4
  import urllib.parse
5
5
  import xmlrpc.client
6
- from typing import TYPE_CHECKING, Tuple
6
+ from typing import TYPE_CHECKING
7
7
 
8
8
  from pip._internal.exceptions import NetworkConnectionError
9
9
  from pip._internal.network.session import PipSession
@@ -36,7 +36,7 @@ class PipXmlrpcTransport(xmlrpc.client.Transport):
36
36
  handler: str,
37
37
  request_body: "SizedBuffer",
38
38
  verbose: bool = False,
39
- ) -> Tuple["_Marshallable", ...]:
39
+ ) -> tuple["_Marshallable", ...]:
40
40
  assert isinstance(host, str)
41
41
  parts = (self._scheme, host, handler, None, None, None)
42
42
  url = urllib.parse.urlunparse(parts)
@@ -1,9 +1,11 @@
1
+ from __future__ import annotations
2
+
1
3
  import contextlib
2
4
  import hashlib
3
5
  import logging
4
6
  import os
7
+ from collections.abc import Generator
5
8
  from types import TracebackType
6
- from typing import Dict, Generator, Optional, Type, Union
7
9
 
8
10
  from pip._internal.req.req_install import InstallRequirement
9
11
  from pip._internal.utils.temp_dir import TempDirectory
@@ -17,7 +19,7 @@ def update_env_context_manager(**changes: str) -> Generator[None, None, None]:
17
19
 
18
20
  # Save values from the target and change them.
19
21
  non_existent_marker = object()
20
- saved_values: Dict[str, Union[object, str]] = {}
22
+ saved_values: dict[str, object | str] = {}
21
23
  for name, new_value in changes.items():
22
24
  try:
23
25
  saved_values[name] = target[name]
@@ -38,7 +40,7 @@ def update_env_context_manager(**changes: str) -> Generator[None, None, None]:
38
40
 
39
41
 
40
42
  @contextlib.contextmanager
41
- def get_build_tracker() -> Generator["BuildTracker", None, None]:
43
+ def get_build_tracker() -> Generator[BuildTracker, None, None]:
42
44
  root = os.environ.get("PIP_BUILD_TRACKER")
43
45
  with contextlib.ExitStack() as ctx:
44
46
  if root is None:
@@ -65,18 +67,18 @@ class BuildTracker:
65
67
 
66
68
  def __init__(self, root: str) -> None:
67
69
  self._root = root
68
- self._entries: Dict[TrackerId, InstallRequirement] = {}
70
+ self._entries: dict[TrackerId, InstallRequirement] = {}
69
71
  logger.debug("Created build tracker: %s", self._root)
70
72
 
71
- def __enter__(self) -> "BuildTracker":
73
+ def __enter__(self) -> BuildTracker:
72
74
  logger.debug("Entered build tracker: %s", self._root)
73
75
  return self
74
76
 
75
77
  def __exit__(
76
78
  self,
77
- exc_type: Optional[Type[BaseException]],
78
- exc_val: Optional[BaseException],
79
- exc_tb: Optional[TracebackType],
79
+ exc_type: type[BaseException] | None,
80
+ exc_val: BaseException | None,
81
+ exc_tb: TracebackType | None,
80
82
  ) -> None:
81
83
  self.cleanup()
82
84
 
@@ -1,6 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import os
3
- from typing import Optional
4
5
 
5
6
  from pip._vendor.pyproject_hooks import BuildBackendHookCaller
6
7
 
@@ -13,25 +14,25 @@ def build_wheel_pep517(
13
14
  name: str,
14
15
  backend: BuildBackendHookCaller,
15
16
  metadata_directory: str,
16
- tempd: str,
17
- ) -> Optional[str]:
17
+ wheel_directory: str,
18
+ ) -> str | None:
18
19
  """Build one InstallRequirement using the PEP 517 build process.
19
20
 
20
21
  Returns path to wheel if successfully built. Otherwise, returns None.
21
22
  """
22
23
  assert metadata_directory is not None
23
24
  try:
24
- logger.debug("Destination directory: %s", tempd)
25
+ logger.debug("Destination directory: %s", wheel_directory)
25
26
 
26
27
  runner = runner_with_spinner_message(
27
28
  f"Building wheel for {name} (pyproject.toml)"
28
29
  )
29
30
  with backend.subprocess_runner(runner):
30
31
  wheel_name = backend.build_wheel(
31
- tempd,
32
+ wheel_directory=wheel_directory,
32
33
  metadata_directory=metadata_directory,
33
34
  )
34
35
  except Exception:
35
36
  logger.error("Failed building wheel for %s", name)
36
37
  return None
37
- return os.path.join(tempd, wheel_name)
38
+ return os.path.join(wheel_directory, wheel_name)
@@ -1,6 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import os
3
- from typing import Optional
4
5
 
5
6
  from pip._vendor.pyproject_hooks import BuildBackendHookCaller, HookMissing
6
7
 
@@ -13,15 +14,15 @@ def build_wheel_editable(
13
14
  name: str,
14
15
  backend: BuildBackendHookCaller,
15
16
  metadata_directory: str,
16
- tempd: str,
17
- ) -> Optional[str]:
17
+ wheel_directory: str,
18
+ ) -> str | None:
18
19
  """Build one InstallRequirement using the PEP 660 build process.
19
20
 
20
21
  Returns path to wheel if successfully built. Otherwise, returns None.
21
22
  """
22
23
  assert metadata_directory is not None
23
24
  try:
24
- logger.debug("Destination directory: %s", tempd)
25
+ logger.debug("Destination directory: %s", wheel_directory)
25
26
 
26
27
  runner = runner_with_spinner_message(
27
28
  f"Building editable for {name} (pyproject.toml)"
@@ -29,7 +30,7 @@ def build_wheel_editable(
29
30
  with backend.subprocess_runner(runner):
30
31
  try:
31
32
  wheel_name = backend.build_editable(
32
- tempd,
33
+ wheel_directory=wheel_directory,
33
34
  metadata_directory=metadata_directory,
34
35
  )
35
36
  except HookMissing as e:
@@ -43,4 +44,4 @@ def build_wheel_editable(
43
44
  except Exception:
44
45
  logger.error("Failed building editable for %s", name)
45
46
  return None
46
- return os.path.join(tempd, wheel_name)
47
+ return os.path.join(wheel_directory, wheel_name)
@@ -1,20 +1,15 @@
1
1
  """Validation of dependencies of packages"""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import logging
6
+ from collections.abc import Generator, Iterable
4
7
  from contextlib import suppress
5
8
  from email.parser import Parser
6
9
  from functools import reduce
7
10
  from typing import (
8
11
  Callable,
9
- Dict,
10
- FrozenSet,
11
- Generator,
12
- Iterable,
13
- List,
14
12
  NamedTuple,
15
- Optional,
16
- Set,
17
- Tuple,
18
13
  )
19
14
 
20
15
  from pip._vendor.packaging.requirements import Requirement
@@ -32,21 +27,21 @@ logger = logging.getLogger(__name__)
32
27
 
33
28
  class PackageDetails(NamedTuple):
34
29
  version: Version
35
- dependencies: List[Requirement]
30
+ dependencies: list[Requirement]
36
31
 
37
32
 
38
33
  # Shorthands
39
- PackageSet = Dict[NormalizedName, PackageDetails]
40
- Missing = Tuple[NormalizedName, Requirement]
41
- Conflicting = Tuple[NormalizedName, Version, Requirement]
34
+ PackageSet = dict[NormalizedName, PackageDetails]
35
+ Missing = tuple[NormalizedName, Requirement]
36
+ Conflicting = tuple[NormalizedName, Version, Requirement]
42
37
 
43
- MissingDict = Dict[NormalizedName, List[Missing]]
44
- ConflictingDict = Dict[NormalizedName, List[Conflicting]]
45
- CheckResult = Tuple[MissingDict, ConflictingDict]
46
- ConflictDetails = Tuple[PackageSet, CheckResult]
38
+ MissingDict = dict[NormalizedName, list[Missing]]
39
+ ConflictingDict = dict[NormalizedName, list[Conflicting]]
40
+ CheckResult = tuple[MissingDict, ConflictingDict]
41
+ ConflictDetails = tuple[PackageSet, CheckResult]
47
42
 
48
43
 
49
- def create_package_set_from_installed() -> Tuple[PackageSet, bool]:
44
+ def create_package_set_from_installed() -> tuple[PackageSet, bool]:
50
45
  """Converts a list of distributions into a PackageSet."""
51
46
  package_set = {}
52
47
  problems = False
@@ -64,7 +59,7 @@ def create_package_set_from_installed() -> Tuple[PackageSet, bool]:
64
59
 
65
60
 
66
61
  def check_package_set(
67
- package_set: PackageSet, should_ignore: Optional[Callable[[str], bool]] = None
62
+ package_set: PackageSet, should_ignore: Callable[[str], bool] | None = None
68
63
  ) -> CheckResult:
69
64
  """Check if a package set is consistent
70
65
 
@@ -77,8 +72,8 @@ def check_package_set(
77
72
 
78
73
  for package_name, package_detail in package_set.items():
79
74
  # Info about dependencies of package_name
80
- missing_deps: Set[Missing] = set()
81
- conflicting_deps: Set[Conflicting] = set()
75
+ missing_deps: set[Missing] = set()
76
+ conflicting_deps: set[Conflicting] = set()
82
77
 
83
78
  if should_ignore and should_ignore(package_name):
84
79
  continue
@@ -108,7 +103,7 @@ def check_package_set(
108
103
  return missing, conflicting
109
104
 
110
105
 
111
- def check_install_conflicts(to_install: List[InstallRequirement]) -> ConflictDetails:
106
+ def check_install_conflicts(to_install: list[InstallRequirement]) -> ConflictDetails:
112
107
  """For checking if the dependency graph would be consistent after \
113
108
  installing given requirements
114
109
  """
@@ -135,7 +130,7 @@ def check_unsupported(
135
130
  for p in packages:
136
131
  with suppress(FileNotFoundError):
137
132
  wheel_file = p.read_text("WHEEL")
138
- wheel_tags: FrozenSet[Tag] = reduce(
133
+ wheel_tags: frozenset[Tag] = reduce(
139
134
  frozenset.union,
140
135
  map(parse_tag, Parser().parsestr(wheel_file).get_all("Tag", [])),
141
136
  frozenset(),
@@ -145,8 +140,8 @@ def check_unsupported(
145
140
 
146
141
 
147
142
  def _simulate_installation_of(
148
- to_install: List[InstallRequirement], package_set: PackageSet
149
- ) -> Set[NormalizedName]:
143
+ to_install: list[InstallRequirement], package_set: PackageSet
144
+ ) -> set[NormalizedName]:
150
145
  """Computes the version of packages after installing to_install."""
151
146
  # Keep track of packages that were installed
152
147
  installed = set()
@@ -164,8 +159,8 @@ def _simulate_installation_of(
164
159
 
165
160
 
166
161
  def _create_whitelist(
167
- would_be_installed: Set[NormalizedName], package_set: PackageSet
168
- ) -> Set[NormalizedName]:
162
+ would_be_installed: set[NormalizedName], package_set: PackageSet
163
+ ) -> set[NormalizedName]:
169
164
  packages_affected = set(would_be_installed)
170
165
 
171
166
  for package_name in package_set:
@@ -1,8 +1,11 @@
1
+ from __future__ import annotations
2
+
1
3
  import collections
2
4
  import logging
3
5
  import os
6
+ from collections.abc import Container, Generator, Iterable
4
7
  from dataclasses import dataclass, field
5
- from typing import Container, Dict, Generator, Iterable, List, NamedTuple, Optional, Set
8
+ from typing import NamedTuple
6
9
 
7
10
  from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
8
11
  from pip._vendor.packaging.version import InvalidVersion
@@ -21,19 +24,19 @@ logger = logging.getLogger(__name__)
21
24
 
22
25
  class _EditableInfo(NamedTuple):
23
26
  requirement: str
24
- comments: List[str]
27
+ comments: list[str]
25
28
 
26
29
 
27
30
  def freeze(
28
- requirement: Optional[List[str]] = None,
31
+ requirement: list[str] | None = None,
29
32
  local_only: bool = False,
30
33
  user_only: bool = False,
31
- paths: Optional[List[str]] = None,
34
+ paths: list[str] | None = None,
32
35
  isolated: bool = False,
33
36
  exclude_editable: bool = False,
34
37
  skip: Container[str] = (),
35
38
  ) -> Generator[str, None, None]:
36
- installations: Dict[str, FrozenRequirement] = {}
39
+ installations: dict[str, FrozenRequirement] = {}
37
40
 
38
41
  dists = get_environment(paths).iter_installed_distributions(
39
42
  local_only=local_only,
@@ -51,10 +54,10 @@ def freeze(
51
54
  # should only be emitted once, even if the same option is in multiple
52
55
  # requirements files, so we need to keep track of what has been emitted
53
56
  # so that we don't emit it again if it's seen again
54
- emitted_options: Set[str] = set()
57
+ emitted_options: set[str] = set()
55
58
  # keep track of which files a requirement is in so that we can
56
59
  # give an accurate warning if a requirement appears multiple times.
57
- req_files: Dict[str, List[str]] = collections.defaultdict(list)
60
+ req_files: dict[str, list[str]] = collections.defaultdict(list)
58
61
  for req_file_path in requirement:
59
62
  with open(req_file_path) as req_file:
60
63
  for line in req_file:
@@ -83,7 +86,7 @@ def freeze(
83
86
  yield line
84
87
  continue
85
88
 
86
- if line.startswith("-e") or line.startswith("--editable"):
89
+ if line.startswith(("-e", "--editable")):
87
90
  if line.startswith("-e"):
88
91
  line = line[2:].strip()
89
92
  else:
@@ -233,7 +236,7 @@ class FrozenRequirement:
233
236
  return canonicalize_name(self.name)
234
237
 
235
238
  @classmethod
236
- def from_dist(cls, dist: BaseDistribution) -> "FrozenRequirement":
239
+ def from_dist(cls, dist: BaseDistribution) -> FrozenRequirement:
237
240
  editable = dist.editable
238
241
  if editable:
239
242
  req, comments = _get_editable_info(dist)
@@ -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: