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,9 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import functools
2
4
  import os
3
5
  import site
4
6
  import sys
5
7
  import sysconfig
6
- import typing
7
8
 
8
9
  from pip._internal.exceptions import InstallationError
9
10
  from pip._internal.utils import appdirs
@@ -71,11 +72,11 @@ def get_src_prefix() -> str:
71
72
  try:
72
73
  # Use getusersitepackages if this is present, as it ensures that the
73
74
  # value is initialised properly.
74
- user_site: typing.Optional[str] = site.getusersitepackages()
75
+ user_site: str | None = site.getusersitepackages()
75
76
  except AttributeError:
76
77
  user_site = site.USER_SITE
77
78
 
78
79
 
79
- @functools.lru_cache(maxsize=None)
80
+ @functools.cache
80
81
  def is_osx_framework() -> bool:
81
82
  return bool(sysconfig.get_config_var("PYTHONFRAMEWORK"))
pip/_internal/main.py CHANGED
@@ -1,7 +1,7 @@
1
- from typing import List, Optional
1
+ from __future__ import annotations
2
2
 
3
3
 
4
- def main(args: Optional[List[str]] = None) -> int:
4
+ def main(args: list[str] | None = None) -> int:
5
5
  """This is preserved for old console scripts that may still be referencing
6
6
  it.
7
7
 
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import contextlib
2
4
  import functools
3
5
  import os
4
6
  import sys
5
- from typing import List, Literal, Optional, Protocol, Type, cast
7
+ from typing import Literal, Protocol, cast
6
8
 
7
9
  from pip._internal.utils.deprecation import deprecated
8
10
  from pip._internal.utils.misc import strtobool
@@ -81,12 +83,12 @@ def _emit_pkg_resources_deprecation_if_needed() -> None:
81
83
 
82
84
 
83
85
  class Backend(Protocol):
84
- NAME: 'Literal["importlib", "pkg_resources"]'
85
- Distribution: Type[BaseDistribution]
86
- Environment: Type[BaseEnvironment]
86
+ NAME: Literal["importlib", "pkg_resources"]
87
+ Distribution: type[BaseDistribution]
88
+ Environment: type[BaseEnvironment]
87
89
 
88
90
 
89
- @functools.lru_cache(maxsize=None)
91
+ @functools.cache
90
92
  def select_backend() -> Backend:
91
93
  if _should_use_importlib_metadata():
92
94
  from . import importlib
@@ -110,7 +112,7 @@ def get_default_environment() -> BaseEnvironment:
110
112
  return select_backend().Environment.default()
111
113
 
112
114
 
113
- def get_environment(paths: Optional[List[str]]) -> BaseEnvironment:
115
+ def get_environment(paths: list[str] | None) -> BaseEnvironment:
114
116
  """Get a representation of the environment specified by ``paths``.
115
117
 
116
118
  This returns an Environment instance from the chosen backend based on the
@@ -1,8 +1,9 @@
1
1
  # Extracted from https://github.com/pfmoore/pkg_metadata
2
+ from __future__ import annotations
2
3
 
3
4
  from email.header import Header, decode_header, make_header
4
5
  from email.message import Message
5
- from typing import Any, Dict, List, Union, cast
6
+ from typing import Any, cast
6
7
 
7
8
  METADATA_FIELDS = [
8
9
  # Name, Multiple-Use
@@ -40,10 +41,10 @@ def json_name(field: str) -> str:
40
41
  return field.lower().replace("-", "_")
41
42
 
42
43
 
43
- def msg_to_json(msg: Message) -> Dict[str, Any]:
44
+ def msg_to_json(msg: Message) -> dict[str, Any]:
44
45
  """Convert a Message object into a JSON-compatible dictionary."""
45
46
 
46
- def sanitise_header(h: Union[Header, str]) -> str:
47
+ def sanitise_header(h: Header | str) -> str:
47
48
  if isinstance(h, Header):
48
49
  chunks = []
49
50
  for bytes, encoding in decode_header(h):
@@ -65,7 +66,7 @@ def msg_to_json(msg: Message) -> Dict[str, Any]:
65
66
  continue
66
67
  key = json_name(field)
67
68
  if multi:
68
- value: Union[str, List[str]] = [
69
+ value: str | list[str] = [
69
70
  sanitise_header(v) for v in msg.get_all(field) # type: ignore
70
71
  ]
71
72
  else:
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import csv
2
4
  import email.message
3
5
  import functools
@@ -6,19 +8,12 @@ import logging
6
8
  import pathlib
7
9
  import re
8
10
  import zipfile
11
+ from collections.abc import Collection, Container, Iterable, Iterator
9
12
  from typing import (
10
13
  IO,
11
14
  Any,
12
- Collection,
13
- Container,
14
- Dict,
15
- Iterable,
16
- Iterator,
17
- List,
18
15
  NamedTuple,
19
- Optional,
20
16
  Protocol,
21
- Tuple,
22
17
  Union,
23
18
  )
24
19
 
@@ -61,8 +56,8 @@ class BaseEntryPoint(Protocol):
61
56
 
62
57
 
63
58
  def _convert_installed_files_path(
64
- entry: Tuple[str, ...],
65
- info: Tuple[str, ...],
59
+ entry: tuple[str, ...],
60
+ info: tuple[str, ...],
66
61
  ) -> str:
67
62
  """Convert a legacy installed-files.txt path into modern RECORD path.
68
63
 
@@ -98,7 +93,7 @@ class RequiresEntry(NamedTuple):
98
93
 
99
94
  class BaseDistribution(Protocol):
100
95
  @classmethod
101
- def from_directory(cls, directory: str) -> "BaseDistribution":
96
+ def from_directory(cls, directory: str) -> BaseDistribution:
102
97
  """Load the distribution from a metadata directory.
103
98
 
104
99
  :param directory: Path to a metadata directory, e.g. ``.dist-info``.
@@ -111,7 +106,7 @@ class BaseDistribution(Protocol):
111
106
  metadata_contents: bytes,
112
107
  filename: str,
113
108
  project_name: str,
114
- ) -> "BaseDistribution":
109
+ ) -> BaseDistribution:
115
110
  """Load the distribution from the contents of a METADATA file.
116
111
 
117
112
  This is used to implement PEP 658 by generating a "shallow" dist object that can
@@ -124,7 +119,7 @@ class BaseDistribution(Protocol):
124
119
  raise NotImplementedError()
125
120
 
126
121
  @classmethod
127
- def from_wheel(cls, wheel: "Wheel", name: str) -> "BaseDistribution":
122
+ def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution:
128
123
  """Load the distribution from a given wheel.
129
124
 
130
125
  :param wheel: A concrete wheel definition.
@@ -144,7 +139,7 @@ class BaseDistribution(Protocol):
144
139
  return f"{self.raw_name} {self.raw_version}"
145
140
 
146
141
  @property
147
- def location(self) -> Optional[str]:
142
+ def location(self) -> str | None:
148
143
  """Where the distribution is loaded from.
149
144
 
150
145
  A string value is not necessarily a filesystem path, since distributions
@@ -158,7 +153,7 @@ class BaseDistribution(Protocol):
158
153
  raise NotImplementedError()
159
154
 
160
155
  @property
161
- def editable_project_location(self) -> Optional[str]:
156
+ def editable_project_location(self) -> str | None:
162
157
  """The project location for editable distributions.
163
158
 
164
159
  This is the directory where pyproject.toml or setup.py is located.
@@ -180,7 +175,7 @@ class BaseDistribution(Protocol):
180
175
  return None
181
176
 
182
177
  @property
183
- def installed_location(self) -> Optional[str]:
178
+ def installed_location(self) -> str | None:
184
179
  """The distribution's "installed" location.
185
180
 
186
181
  This should generally be a ``site-packages`` directory. This is
@@ -193,7 +188,7 @@ class BaseDistribution(Protocol):
193
188
  raise NotImplementedError()
194
189
 
195
190
  @property
196
- def info_location(self) -> Optional[str]:
191
+ def info_location(self) -> str | None:
197
192
  """Location of the .[egg|dist]-info directory or file.
198
193
 
199
194
  Similarly to ``location``, a string value is not necessarily a
@@ -290,7 +285,7 @@ class BaseDistribution(Protocol):
290
285
  return self.raw_name.replace("-", "_")
291
286
 
292
287
  @property
293
- def direct_url(self) -> Optional[DirectUrl]:
288
+ def direct_url(self) -> DirectUrl | None:
294
289
  """Obtain a DirectUrl from this distribution.
295
290
 
296
291
  Returns None if the distribution has no `direct_url.json` metadata,
@@ -398,7 +393,7 @@ class BaseDistribution(Protocol):
398
393
  return metadata
399
394
 
400
395
  @property
401
- def metadata_dict(self) -> Dict[str, Any]:
396
+ def metadata_dict(self) -> dict[str, Any]:
402
397
  """PEP 566 compliant JSON-serializable representation of METADATA or PKG-INFO.
403
398
 
404
399
  This should return an empty dict if the metadata file is unavailable.
@@ -409,7 +404,7 @@ class BaseDistribution(Protocol):
409
404
  return msg_to_json(self.metadata)
410
405
 
411
406
  @property
412
- def metadata_version(self) -> Optional[str]:
407
+ def metadata_version(self) -> str | None:
413
408
  """Value of "Metadata-Version:" in distribution metadata, if available."""
414
409
  return self.metadata.get("Metadata-Version")
415
410
 
@@ -463,7 +458,7 @@ class BaseDistribution(Protocol):
463
458
  """
464
459
  raise NotImplementedError()
465
460
 
466
- def _iter_declared_entries_from_record(self) -> Optional[Iterator[str]]:
461
+ def _iter_declared_entries_from_record(self) -> Iterator[str] | None:
467
462
  try:
468
463
  text = self.read_text("RECORD")
469
464
  except FileNotFoundError:
@@ -471,7 +466,7 @@ class BaseDistribution(Protocol):
471
466
  # This extra Path-str cast normalizes entries.
472
467
  return (str(pathlib.Path(row[0])) for row in csv.reader(text.splitlines()))
473
468
 
474
- def _iter_declared_entries_from_legacy(self) -> Optional[Iterator[str]]:
469
+ def _iter_declared_entries_from_legacy(self) -> Iterator[str] | None:
475
470
  try:
476
471
  text = self.read_text("installed-files.txt")
477
472
  except FileNotFoundError:
@@ -492,7 +487,7 @@ class BaseDistribution(Protocol):
492
487
  for p in paths
493
488
  )
494
489
 
495
- def iter_declared_entries(self) -> Optional[Iterator[str]]:
490
+ def iter_declared_entries(self) -> Iterator[str] | None:
496
491
  """Iterate through file entries declared in this distribution.
497
492
 
498
493
  For modern .dist-info distributions, this is the files listed in the
@@ -585,14 +580,14 @@ class BaseEnvironment:
585
580
  """An environment containing distributions to introspect."""
586
581
 
587
582
  @classmethod
588
- def default(cls) -> "BaseEnvironment":
583
+ def default(cls) -> BaseEnvironment:
589
584
  raise NotImplementedError()
590
585
 
591
586
  @classmethod
592
- def from_paths(cls, paths: Optional[List[str]]) -> "BaseEnvironment":
587
+ def from_paths(cls, paths: list[str] | None) -> BaseEnvironment:
593
588
  raise NotImplementedError()
594
589
 
595
- def get_distribution(self, name: str) -> Optional["BaseDistribution"]:
590
+ def get_distribution(self, name: str) -> BaseDistribution | None:
596
591
  """Given a requirement name, return the installed distributions.
597
592
 
598
593
  The name may not be normalized. The implementation must canonicalize
@@ -600,7 +595,7 @@ class BaseEnvironment:
600
595
  """
601
596
  raise NotImplementedError()
602
597
 
603
- def _iter_distributions(self) -> Iterator["BaseDistribution"]:
598
+ def _iter_distributions(self) -> Iterator[BaseDistribution]:
604
599
  """Iterate through installed distributions.
605
600
 
606
601
  This function should be implemented by subclass, but never called
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import importlib.metadata
2
4
  import os
3
- from typing import Any, Optional, Protocol, Tuple, cast
5
+ from typing import Any, Protocol, cast
4
6
 
5
7
  from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
6
8
 
@@ -30,11 +32,11 @@ class BasePath(Protocol):
30
32
  raise NotImplementedError()
31
33
 
32
34
  @property
33
- def parent(self) -> "BasePath":
35
+ def parent(self) -> BasePath:
34
36
  raise NotImplementedError()
35
37
 
36
38
 
37
- def get_info_location(d: importlib.metadata.Distribution) -> Optional[BasePath]:
39
+ def get_info_location(d: importlib.metadata.Distribution) -> BasePath | None:
38
40
  """Find the path to the distribution's metadata directory.
39
41
 
40
42
  HACK: This relies on importlib.metadata's private ``_path`` attribute. Not
@@ -48,7 +50,7 @@ def get_info_location(d: importlib.metadata.Distribution) -> Optional[BasePath]:
48
50
 
49
51
  def parse_name_and_version_from_info_directory(
50
52
  dist: importlib.metadata.Distribution,
51
- ) -> Tuple[Optional[str], Optional[str]]:
53
+ ) -> tuple[str | None, str | None]:
52
54
  """Get a name and version from the metadata directory name.
53
55
 
54
56
  This is much faster than reading distribution metadata.
@@ -1,17 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import email.message
2
4
  import importlib.metadata
3
5
  import pathlib
4
6
  import zipfile
7
+ from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence
5
8
  from os import PathLike
6
9
  from typing import (
7
- Collection,
8
- Dict,
9
- Iterable,
10
- Iterator,
11
- Mapping,
12
- Optional,
13
- Sequence,
14
- Union,
15
10
  cast,
16
11
  )
17
12
 
@@ -64,7 +59,7 @@ class WheelDistribution(importlib.metadata.Distribution):
64
59
  zf: zipfile.ZipFile,
65
60
  name: str,
66
61
  location: str,
67
- ) -> "WheelDistribution":
62
+ ) -> WheelDistribution:
68
63
  info_dir, _ = parse_wheel(zf, name)
69
64
  paths = (
70
65
  (name, pathlib.PurePosixPath(name.split("/", 1)[-1]))
@@ -84,7 +79,7 @@ class WheelDistribution(importlib.metadata.Distribution):
84
79
  return iter(self._files)
85
80
  raise FileNotFoundError(path)
86
81
 
87
- def read_text(self, filename: str) -> Optional[str]:
82
+ def read_text(self, filename: str) -> str | None:
88
83
  try:
89
84
  data = self._files[pathlib.PurePosixPath(filename)]
90
85
  except KeyError:
@@ -97,7 +92,7 @@ class WheelDistribution(importlib.metadata.Distribution):
97
92
  raise UnsupportedWheel(error)
98
93
  return text
99
94
 
100
- def locate_file(self, path: Union[str, "PathLike[str]"]) -> pathlib.Path:
95
+ def locate_file(self, path: str | PathLike[str]) -> pathlib.Path:
101
96
  # This method doesn't make sense for our in-memory wheel, but the API
102
97
  # requires us to define it.
103
98
  raise NotImplementedError
@@ -107,8 +102,8 @@ class Distribution(BaseDistribution):
107
102
  def __init__(
108
103
  self,
109
104
  dist: importlib.metadata.Distribution,
110
- info_location: Optional[BasePath],
111
- installed_location: Optional[BasePath],
105
+ info_location: BasePath | None,
106
+ installed_location: BasePath | None,
112
107
  ) -> None:
113
108
  self._dist = dist
114
109
  self._info_location = info_location
@@ -147,19 +142,19 @@ class Distribution(BaseDistribution):
147
142
  return cls(dist, dist.info_location, pathlib.PurePosixPath(wheel.location))
148
143
 
149
144
  @property
150
- def location(self) -> Optional[str]:
145
+ def location(self) -> str | None:
151
146
  if self._info_location is None:
152
147
  return None
153
148
  return str(self._info_location.parent)
154
149
 
155
150
  @property
156
- def info_location(self) -> Optional[str]:
151
+ def info_location(self) -> str | None:
157
152
  if self._info_location is None:
158
153
  return None
159
154
  return str(self._info_location)
160
155
 
161
156
  @property
162
- def installed_location(self) -> Optional[str]:
157
+ def installed_location(self) -> str | None:
163
158
  if self._installed_location is None:
164
159
  return None
165
160
  return normalize_path(str(self._installed_location))
@@ -215,7 +210,7 @@ class Distribution(BaseDistribution):
215
210
  ]
216
211
 
217
212
  def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]:
218
- contexts: Sequence[Dict[str, str]] = [{"extra": e} for e in extras]
213
+ contexts: Sequence[dict[str, str]] = [{"extra": e} for e in extras]
219
214
  for req_string in self.metadata.get_all("Requires-Dist", []):
220
215
  # strip() because email.message.Message.get_all() may return a leading \n
221
216
  # in case a long header was wrapped.
@@ -1,10 +1,13 @@
1
+ from __future__ import annotations
2
+
1
3
  import importlib.metadata
2
4
  import logging
3
5
  import os
4
6
  import pathlib
5
7
  import sys
6
8
  import zipfile
7
- from typing import Iterator, List, Optional, Sequence, Set, Tuple
9
+ from collections.abc import Iterator, Sequence
10
+ from typing import Optional
8
11
 
9
12
  from pip._vendor.packaging.utils import (
10
13
  InvalidWheelFilename,
@@ -47,10 +50,10 @@ class _DistributionFinder:
47
50
  installations as well. It's useful feature, after all.
48
51
  """
49
52
 
50
- FoundResult = Tuple[importlib.metadata.Distribution, Optional[BasePath]]
53
+ FoundResult = tuple[importlib.metadata.Distribution, Optional[BasePath]]
51
54
 
52
55
  def __init__(self) -> None:
53
- self._found_names: Set[NormalizedName] = set()
56
+ self._found_names: set[NormalizedName] = set()
54
57
 
55
58
  def _find_impl(self, location: str) -> Iterator[FoundResult]:
56
59
  """Find distributions in a location."""
@@ -80,7 +83,7 @@ class _DistributionFinder:
80
83
  """
81
84
  for dist, info_location in self._find_impl(location):
82
85
  if info_location is None:
83
- installed_location: Optional[BasePath] = None
86
+ installed_location: BasePath | None = None
84
87
  else:
85
88
  installed_location = info_location.parent
86
89
  yield Distribution(dist, info_location, installed_location)
@@ -119,7 +122,7 @@ class Environment(BaseEnvironment):
119
122
  return cls(sys.path)
120
123
 
121
124
  @classmethod
122
- def from_paths(cls, paths: Optional[List[str]]) -> BaseEnvironment:
125
+ def from_paths(cls, paths: list[str] | None) -> BaseEnvironment:
123
126
  if paths is None:
124
127
  return cls(sys.path)
125
128
  return cls(paths)
@@ -130,7 +133,7 @@ class Environment(BaseEnvironment):
130
133
  yield from finder.find(location)
131
134
  yield from finder.find_legacy_editables(location)
132
135
 
133
- def get_distribution(self, name: str) -> Optional[BaseDistribution]:
136
+ def get_distribution(self, name: str) -> BaseDistribution | None:
134
137
  canonical_name = canonicalize_name(name)
135
138
  matches = (
136
139
  distribution
@@ -1,16 +1,13 @@
1
+ from __future__ import annotations
2
+
1
3
  import email.message
2
4
  import email.parser
3
5
  import logging
4
6
  import os
5
7
  import zipfile
8
+ from collections.abc import Collection, Iterable, Iterator, Mapping
6
9
  from typing import (
7
- Collection,
8
- Iterable,
9
- Iterator,
10
- List,
11
- Mapping,
12
10
  NamedTuple,
13
- Optional,
14
11
  )
15
12
 
16
13
  from pip._vendor import pkg_resources
@@ -73,7 +70,7 @@ class InMemoryMetadata:
73
70
  def metadata_isdir(self, name: str) -> bool:
74
71
  return False
75
72
 
76
- def metadata_listdir(self, name: str) -> List[str]:
73
+ def metadata_listdir(self, name: str) -> list[str]:
77
74
  return []
78
75
 
79
76
  def run_script(self, script_name: str, namespace: str) -> None:
@@ -85,7 +82,7 @@ class Distribution(BaseDistribution):
85
82
  self._dist = dist
86
83
  # This is populated lazily, to avoid loading metadata for all possible
87
84
  # distributions eagerly.
88
- self.__extra_mapping: Optional[Mapping[NormalizedName, str]] = None
85
+ self.__extra_mapping: Mapping[NormalizedName, str] | None = None
89
86
 
90
87
  @property
91
88
  def _extra_mapping(self) -> Mapping[NormalizedName, str]:
@@ -155,11 +152,11 @@ class Distribution(BaseDistribution):
155
152
  return cls(dist)
156
153
 
157
154
  @property
158
- def location(self) -> Optional[str]:
155
+ def location(self) -> str | None:
159
156
  return self._dist.location
160
157
 
161
158
  @property
162
- def installed_location(self) -> Optional[str]:
159
+ def installed_location(self) -> str | None:
163
160
  egg_link = egg_link_path_from_location(self.raw_name)
164
161
  if egg_link:
165
162
  location = egg_link
@@ -170,7 +167,7 @@ class Distribution(BaseDistribution):
170
167
  return normalize_path(location)
171
168
 
172
169
  @property
173
- def info_location(self) -> Optional[str]:
170
+ def info_location(self) -> str | None:
174
171
  return self._dist.egg_info
175
172
 
176
173
  @property
@@ -259,14 +256,14 @@ class Environment(BaseEnvironment):
259
256
  return cls(pkg_resources.working_set)
260
257
 
261
258
  @classmethod
262
- def from_paths(cls, paths: Optional[List[str]]) -> BaseEnvironment:
259
+ def from_paths(cls, paths: list[str] | None) -> BaseEnvironment:
263
260
  return cls(pkg_resources.WorkingSet(paths))
264
261
 
265
262
  def _iter_distributions(self) -> Iterator[BaseDistribution]:
266
263
  for dist in self._ws:
267
264
  yield Distribution(dist)
268
265
 
269
- def _search_distribution(self, name: str) -> Optional[BaseDistribution]:
266
+ def _search_distribution(self, name: str) -> BaseDistribution | None:
270
267
  """Find a distribution matching the ``name`` in the environment.
271
268
 
272
269
  This searches from *all* distributions available in the environment, to
@@ -278,7 +275,7 @@ class Environment(BaseEnvironment):
278
275
  return dist
279
276
  return None
280
277
 
281
- def get_distribution(self, name: str) -> Optional[BaseDistribution]:
278
+ def get_distribution(self, name: str) -> BaseDistribution | None:
282
279
  # Search the distribution by looking through the working set.
283
280
  dist = self._search_distribution(name)
284
281
  if dist: