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,11 +1,14 @@
1
+ from __future__ import annotations
2
+
1
3
  import json
2
4
  import logging
5
+ from collections.abc import Generator, Sequence
3
6
  from email.parser import Parser
4
7
  from optparse import Values
5
- from typing import TYPE_CHECKING, Generator, List, Optional, Sequence, Tuple, cast
8
+ from typing import TYPE_CHECKING, cast
6
9
 
7
10
  from pip._vendor.packaging.utils import canonicalize_name
8
- from pip._vendor.packaging.version import Version
11
+ from pip._vendor.packaging.version import InvalidVersion, Version
9
12
 
10
13
  from pip._internal.cli import cmdoptions
11
14
  from pip._internal.cli.index_command import IndexGroupCommand
@@ -140,8 +143,8 @@ class ListCommand(IndexGroupCommand):
140
143
  super().handle_pip_version_check(options)
141
144
 
142
145
  def _build_package_finder(
143
- self, options: Values, session: "PipSession"
144
- ) -> "PackageFinder":
146
+ self, options: Values, session: PipSession
147
+ ) -> PackageFinder:
145
148
  """
146
149
  Create a package finder appropriate to this list command.
147
150
  """
@@ -162,7 +165,7 @@ class ListCommand(IndexGroupCommand):
162
165
  selection_prefs=selection_prefs,
163
166
  )
164
167
 
165
- def run(self, options: Values, args: List[str]) -> int:
168
+ def run(self, options: Values, args: list[str]) -> int:
166
169
  if options.outdated and options.uptodate:
167
170
  raise CommandError("Options --outdated and --uptodate cannot be combined.")
168
171
 
@@ -204,8 +207,8 @@ class ListCommand(IndexGroupCommand):
204
207
  return SUCCESS
205
208
 
206
209
  def get_outdated(
207
- self, packages: "_ProcessedDists", options: Values
208
- ) -> "_ProcessedDists":
210
+ self, packages: _ProcessedDists, options: Values
211
+ ) -> _ProcessedDists:
209
212
  return [
210
213
  dist
211
214
  for dist in self.iter_packages_latest_infos(packages, options)
@@ -213,8 +216,8 @@ class ListCommand(IndexGroupCommand):
213
216
  ]
214
217
 
215
218
  def get_uptodate(
216
- self, packages: "_ProcessedDists", options: Values
217
- ) -> "_ProcessedDists":
219
+ self, packages: _ProcessedDists, options: Values
220
+ ) -> _ProcessedDists:
218
221
  return [
219
222
  dist
220
223
  for dist in self.iter_packages_latest_infos(packages, options)
@@ -222,8 +225,8 @@ class ListCommand(IndexGroupCommand):
222
225
  ]
223
226
 
224
227
  def get_not_required(
225
- self, packages: "_ProcessedDists", options: Values
226
- ) -> "_ProcessedDists":
228
+ self, packages: _ProcessedDists, options: Values
229
+ ) -> _ProcessedDists:
227
230
  dep_keys = {
228
231
  canonicalize_name(dep.name)
229
232
  for dist in packages
@@ -236,14 +239,14 @@ class ListCommand(IndexGroupCommand):
236
239
  return list({pkg for pkg in packages if pkg.canonical_name not in dep_keys})
237
240
 
238
241
  def iter_packages_latest_infos(
239
- self, packages: "_ProcessedDists", options: Values
240
- ) -> Generator["_DistWithLatestInfo", None, None]:
242
+ self, packages: _ProcessedDists, options: Values
243
+ ) -> Generator[_DistWithLatestInfo, None, None]:
241
244
  with self._build_session(options) as session:
242
245
  finder = self._build_package_finder(options, session)
243
246
 
244
247
  def latest_info(
245
- dist: "_DistWithLatestInfo",
246
- ) -> Optional["_DistWithLatestInfo"]:
248
+ dist: _DistWithLatestInfo,
249
+ ) -> _DistWithLatestInfo | None:
247
250
  all_candidates = finder.find_all_candidates(dist.canonical_name)
248
251
  if not options.pre:
249
252
  # Remove prereleases
@@ -274,7 +277,7 @@ class ListCommand(IndexGroupCommand):
274
277
  yield dist
275
278
 
276
279
  def output_package_listing(
277
- self, packages: "_ProcessedDists", options: Values
280
+ self, packages: _ProcessedDists, options: Values
278
281
  ) -> None:
279
282
  packages = sorted(
280
283
  packages,
@@ -285,17 +288,19 @@ class ListCommand(IndexGroupCommand):
285
288
  self.output_package_listing_columns(data, header)
286
289
  elif options.list_format == "freeze":
287
290
  for dist in packages:
291
+ try:
292
+ req_string = f"{dist.raw_name}=={dist.version}"
293
+ except InvalidVersion:
294
+ req_string = f"{dist.raw_name}==={dist.raw_version}"
288
295
  if options.verbose >= 1:
289
- write_output(
290
- "%s==%s (%s)", dist.raw_name, dist.version, dist.location
291
- )
296
+ write_output("%s (%s)", req_string, dist.location)
292
297
  else:
293
- write_output("%s==%s", dist.raw_name, dist.version)
298
+ write_output(req_string)
294
299
  elif options.list_format == "json":
295
300
  write_output(format_for_json(packages, options))
296
301
 
297
302
  def output_package_listing_columns(
298
- self, data: List[List[str]], header: List[str]
303
+ self, data: list[list[str]], header: list[str]
299
304
  ) -> None:
300
305
  # insert the header first: we need to know the size of column names
301
306
  if len(data) > 0:
@@ -312,8 +317,8 @@ class ListCommand(IndexGroupCommand):
312
317
 
313
318
 
314
319
  def format_for_columns(
315
- pkgs: "_ProcessedDists", options: Values
316
- ) -> Tuple[List[List[str]], List[str]]:
320
+ pkgs: _ProcessedDists, options: Values
321
+ ) -> tuple[list[list[str]], list[str]]:
317
322
  """
318
323
  Convert the package data into something usable
319
324
  by output_package_listing_columns.
@@ -324,7 +329,7 @@ def format_for_columns(
324
329
  if running_outdated:
325
330
  header.extend(["Latest", "Type"])
326
331
 
327
- def wheel_build_tag(dist: BaseDistribution) -> Optional[str]:
332
+ def wheel_build_tag(dist: BaseDistribution) -> str | None:
328
333
  try:
329
334
  wheel_file = dist.read_text("WHEEL")
330
335
  except FileNotFoundError:
@@ -371,12 +376,16 @@ def format_for_columns(
371
376
  return data, header
372
377
 
373
378
 
374
- def format_for_json(packages: "_ProcessedDists", options: Values) -> str:
379
+ def format_for_json(packages: _ProcessedDists, options: Values) -> str:
375
380
  data = []
376
381
  for dist in packages:
382
+ try:
383
+ version = str(dist.version)
384
+ except InvalidVersion:
385
+ version = dist.raw_version
377
386
  info = {
378
387
  "name": dist.raw_name,
379
- "version": str(dist.version),
388
+ "version": version,
380
389
  }
381
390
  if options.verbose >= 1:
382
391
  info["location"] = dist.location or ""
@@ -1,7 +1,6 @@
1
1
  import sys
2
2
  from optparse import Values
3
3
  from pathlib import Path
4
- from typing import List
5
4
 
6
5
  from pip._internal.cache import WheelCache
7
6
  from pip._internal.cli import cmdoptions
@@ -90,7 +89,7 @@ class LockCommand(RequirementCommand):
90
89
  self.parser.insert_option_group(0, self.cmd_opts)
91
90
 
92
91
  @with_cleanup
93
- def run(self, options: Values, args: List[str]) -> int:
92
+ def run(self, options: Values, args: list[str]) -> int:
94
93
  logger.verbose("Using %s", get_pip_version())
95
94
 
96
95
  logger.warning(
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import shutil
3
5
  import sys
@@ -5,7 +7,7 @@ import textwrap
5
7
  import xmlrpc.client
6
8
  from collections import OrderedDict
7
9
  from optparse import Values
8
- from typing import Dict, List, Optional, TypedDict
10
+ from typing import TypedDict
9
11
 
10
12
  from pip._vendor.packaging.version import parse as parse_version
11
13
 
@@ -24,7 +26,7 @@ from pip._internal.utils.misc import write_output
24
26
  class TransformedHit(TypedDict):
25
27
  name: str
26
28
  summary: str
27
- versions: List[str]
29
+ versions: list[str]
28
30
 
29
31
 
30
32
  logger = logging.getLogger(__name__)
@@ -49,7 +51,7 @@ class SearchCommand(Command, SessionCommandMixin):
49
51
 
50
52
  self.parser.insert_option_group(0, self.cmd_opts)
51
53
 
52
- def run(self, options: Values, args: List[str]) -> int:
54
+ def run(self, options: Values, args: list[str]) -> int:
53
55
  if not args:
54
56
  raise CommandError("Missing required argument (search query).")
55
57
  query = args
@@ -65,7 +67,7 @@ class SearchCommand(Command, SessionCommandMixin):
65
67
  return SUCCESS
66
68
  return NO_MATCHES_FOUND
67
69
 
68
- def search(self, query: List[str], options: Values) -> List[Dict[str, str]]:
70
+ def search(self, query: list[str], options: Values) -> list[dict[str, str]]:
69
71
  index_url = options.index
70
72
 
71
73
  session = self.get_default_session(options)
@@ -83,13 +85,13 @@ class SearchCommand(Command, SessionCommandMixin):
83
85
  return hits
84
86
 
85
87
 
86
- def transform_hits(hits: List[Dict[str, str]]) -> List["TransformedHit"]:
88
+ def transform_hits(hits: list[dict[str, str]]) -> list[TransformedHit]:
87
89
  """
88
90
  The list from pypi is really a list of versions. We want a list of
89
91
  packages with the list of versions stored inline. This converts the
90
92
  list from pypi into one we can use.
91
93
  """
92
- packages: Dict[str, TransformedHit] = OrderedDict()
94
+ packages: dict[str, TransformedHit] = OrderedDict()
93
95
  for hit in hits:
94
96
  name = hit["name"]
95
97
  summary = hit["summary"]
@@ -111,7 +113,7 @@ def transform_hits(hits: List[Dict[str, str]]) -> List["TransformedHit"]:
111
113
  return list(packages.values())
112
114
 
113
115
 
114
- def print_dist_installation_info(latest: str, dist: Optional[BaseDistribution]) -> None:
116
+ def print_dist_installation_info(latest: str, dist: BaseDistribution | None) -> None:
115
117
  if dist is not None:
116
118
  with indent_log():
117
119
  if dist.version == latest:
@@ -128,15 +130,15 @@ def print_dist_installation_info(latest: str, dist: Optional[BaseDistribution])
128
130
  write_output("LATEST: %s", latest)
129
131
 
130
132
 
131
- def get_installed_distribution(name: str) -> Optional[BaseDistribution]:
133
+ def get_installed_distribution(name: str) -> BaseDistribution | None:
132
134
  env = get_default_environment()
133
135
  return env.get_distribution(name)
134
136
 
135
137
 
136
138
  def print_results(
137
- hits: List["TransformedHit"],
138
- name_column_width: Optional[int] = None,
139
- terminal_width: Optional[int] = None,
139
+ hits: list[TransformedHit],
140
+ name_column_width: int | None = None,
141
+ terminal_width: int | None = None,
140
142
  ) -> None:
141
143
  if not hits:
142
144
  return
@@ -172,5 +174,5 @@ def print_results(
172
174
  pass
173
175
 
174
176
 
175
- def highest_version(versions: List[str]) -> str:
177
+ def highest_version(versions: list[str]) -> str:
176
178
  return max(versions, key=parse_version)
@@ -1,7 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import string
5
+ from collections.abc import Generator, Iterable, Iterator
3
6
  from optparse import Values
4
- from typing import Generator, Iterable, Iterator, List, NamedTuple, Optional
7
+ from typing import NamedTuple
5
8
 
6
9
  from pip._vendor.packaging.requirements import InvalidRequirement
7
10
  from pip._vendor.packaging.utils import canonicalize_name
@@ -44,7 +47,7 @@ class ShowCommand(Command):
44
47
 
45
48
  self.parser.insert_option_group(0, self.cmd_opts)
46
49
 
47
- def run(self, options: Values, args: List[str]) -> int:
50
+ def run(self, options: Values, args: list[str]) -> int:
48
51
  if not args:
49
52
  logger.warning("ERROR: Please provide a package name or names.")
50
53
  return ERROR
@@ -62,24 +65,24 @@ class _PackageInfo(NamedTuple):
62
65
  name: str
63
66
  version: str
64
67
  location: str
65
- editable_project_location: Optional[str]
66
- requires: List[str]
67
- required_by: List[str]
68
+ editable_project_location: str | None
69
+ requires: list[str]
70
+ required_by: list[str]
68
71
  installer: str
69
72
  metadata_version: str
70
- classifiers: List[str]
73
+ classifiers: list[str]
71
74
  summary: str
72
75
  homepage: str
73
- project_urls: List[str]
76
+ project_urls: list[str]
74
77
  author: str
75
78
  author_email: str
76
79
  license: str
77
80
  license_expression: str
78
- entry_points: List[str]
79
- files: Optional[List[str]]
81
+ entry_points: list[str]
82
+ files: list[str] | None
80
83
 
81
84
 
82
- def search_packages_info(query: List[str]) -> Generator[_PackageInfo, None, None]:
85
+ def search_packages_info(query: list[str]) -> Generator[_PackageInfo, None, None]:
83
86
  """
84
87
  Gather details from installed distributions. Print distribution name,
85
88
  version, location, and installed files. Installed files requires a
@@ -132,7 +135,7 @@ def search_packages_info(query: List[str]) -> Generator[_PackageInfo, None, None
132
135
 
133
136
  files_iter = dist.iter_declared_entries()
134
137
  if files_iter is None:
135
- files: Optional[List[str]] = None
138
+ files: list[str] | None = None
136
139
  else:
137
140
  files = sorted(files_iter)
138
141
 
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  from optparse import Values
3
- from typing import List
4
3
 
5
4
  from pip._vendor.packaging.utils import canonicalize_name
6
5
 
@@ -62,7 +61,7 @@ class UninstallCommand(Command, SessionCommandMixin):
62
61
  self.cmd_opts.add_option(cmdoptions.override_externally_managed())
63
62
  self.parser.insert_option_group(0, self.cmd_opts)
64
63
 
65
- def run(self, options: Values, args: List[str]) -> int:
64
+ def run(self, options: Values, args: list[str]) -> int:
66
65
  session = self.get_default_session(options)
67
66
 
68
67
  reqs_to_uninstall = {}
@@ -2,7 +2,6 @@ import logging
2
2
  import os
3
3
  import shutil
4
4
  from optparse import Values
5
- from typing import List
6
5
 
7
6
  from pip._internal.cache import WheelCache
8
7
  from pip._internal.cli import cmdoptions
@@ -101,7 +100,7 @@ class WheelCommand(RequirementCommand):
101
100
  self.parser.insert_option_group(0, self.cmd_opts)
102
101
 
103
102
  @with_cleanup
104
- def run(self, options: Values, args: List[str]) -> int:
103
+ def run(self, options: Values, args: list[str]) -> int:
105
104
  session = self.get_default_session(options)
106
105
 
107
106
  finder = self._build_package_finder(options, session)
@@ -146,7 +145,7 @@ class WheelCommand(RequirementCommand):
146
145
 
147
146
  requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
148
147
 
149
- reqs_to_build: List[InstallRequirement] = []
148
+ reqs_to_build: list[InstallRequirement] = []
150
149
  for req in requirement_set.requirements.values():
151
150
  if req.is_wheel:
152
151
  preparer.save_linked_requirement(req)
@@ -11,11 +11,14 @@ Some terminology:
11
11
  A single word describing where the configuration key-value pair came from
12
12
  """
13
13
 
14
+ from __future__ import annotations
15
+
14
16
  import configparser
15
17
  import locale
16
18
  import os
17
19
  import sys
18
- from typing import Any, Dict, Iterable, List, NewType, Optional, Tuple
20
+ from collections.abc import Iterable
21
+ from typing import Any, NewType
19
22
 
20
23
  from pip._internal.exceptions import (
21
24
  ConfigurationError,
@@ -55,7 +58,7 @@ def _normalize_name(name: str) -> str:
55
58
  return name
56
59
 
57
60
 
58
- def _disassemble_key(name: str) -> List[str]:
61
+ def _disassemble_key(name: str) -> list[str]:
59
62
  if "." not in name:
60
63
  error_message = (
61
64
  "Key does not contain dot separated section and key. "
@@ -65,7 +68,7 @@ def _disassemble_key(name: str) -> List[str]:
65
68
  return name.split(".", 1)
66
69
 
67
70
 
68
- def get_configuration_files() -> Dict[Kind, List[str]]:
71
+ def get_configuration_files() -> dict[Kind, list[str]]:
69
72
  global_config_files = [
70
73
  os.path.join(path, CONFIG_BASENAME) for path in appdirs.site_config_dirs("pip")
71
74
  ]
@@ -98,7 +101,7 @@ class Configuration:
98
101
  and the data stored is also nice.
99
102
  """
100
103
 
101
- def __init__(self, isolated: bool, load_only: Optional[Kind] = None) -> None:
104
+ def __init__(self, isolated: bool, load_only: Kind | None = None) -> None:
102
105
  super().__init__()
103
106
 
104
107
  if load_only is not None and load_only not in VALID_LOAD_ONLY:
@@ -111,13 +114,13 @@ class Configuration:
111
114
  self.load_only = load_only
112
115
 
113
116
  # Because we keep track of where we got the data from
114
- self._parsers: Dict[Kind, List[Tuple[str, RawConfigParser]]] = {
117
+ self._parsers: dict[Kind, list[tuple[str, RawConfigParser]]] = {
115
118
  variant: [] for variant in OVERRIDE_ORDER
116
119
  }
117
- self._config: Dict[Kind, Dict[str, Any]] = {
120
+ self._config: dict[Kind, dict[str, dict[str, Any]]] = {
118
121
  variant: {} for variant in OVERRIDE_ORDER
119
122
  }
120
- self._modified_parsers: List[Tuple[str, RawConfigParser]] = []
123
+ self._modified_parsers: list[tuple[str, RawConfigParser]] = []
121
124
 
122
125
  def load(self) -> None:
123
126
  """Loads configuration from configuration files and environment"""
@@ -125,7 +128,7 @@ class Configuration:
125
128
  if not self.isolated:
126
129
  self._load_environment_vars()
127
130
 
128
- def get_file_to_edit(self) -> Optional[str]:
131
+ def get_file_to_edit(self) -> str | None:
129
132
  """Returns the file with highest priority in configuration"""
130
133
  assert self.load_only is not None, "Need to be specified a file to be editing"
131
134
 
@@ -134,7 +137,7 @@ class Configuration:
134
137
  except IndexError:
135
138
  return None
136
139
 
137
- def items(self) -> Iterable[Tuple[str, Any]]:
140
+ def items(self) -> Iterable[tuple[str, Any]]:
138
141
  """Returns key-value pairs like dict.items() representing the loaded
139
142
  configuration
140
143
  """
@@ -145,7 +148,10 @@ class Configuration:
145
148
  orig_key = key
146
149
  key = _normalize_name(key)
147
150
  try:
148
- return self._dictionary[key]
151
+ clean_config: dict[str, Any] = {}
152
+ for file_values in self._dictionary.values():
153
+ clean_config.update(file_values)
154
+ return clean_config[key]
149
155
  except KeyError:
150
156
  # disassembling triggers a more useful error message than simply
151
157
  # "No such key" in the case that the key isn't in the form command.option
@@ -168,7 +174,8 @@ class Configuration:
168
174
  parser.add_section(section)
169
175
  parser.set(section, name, value)
170
176
 
171
- self._config[self.load_only][key] = value
177
+ self._config[self.load_only].setdefault(fname, {})
178
+ self._config[self.load_only][fname][key] = value
172
179
  self._mark_as_modified(fname, parser)
173
180
 
174
181
  def unset_value(self, key: str) -> None:
@@ -178,11 +185,14 @@ class Configuration:
178
185
  self._ensure_have_load_only()
179
186
 
180
187
  assert self.load_only
181
- if key not in self._config[self.load_only]:
182
- raise ConfigurationError(f"No such key - {orig_key}")
183
-
184
188
  fname, parser = self._get_parser_to_modify()
185
189
 
190
+ if (
191
+ key not in self._config[self.load_only][fname]
192
+ and key not in self._config[self.load_only]
193
+ ):
194
+ raise ConfigurationError(f"No such key - {orig_key}")
195
+
186
196
  if parser is not None:
187
197
  section, name = _disassemble_key(key)
188
198
  if not (
@@ -197,8 +207,10 @@ class Configuration:
197
207
  if not parser.items(section):
198
208
  parser.remove_section(section)
199
209
  self._mark_as_modified(fname, parser)
200
-
201
- del self._config[self.load_only][key]
210
+ try:
211
+ del self._config[self.load_only][fname][key]
212
+ except KeyError:
213
+ del self._config[self.load_only][key]
202
214
 
203
215
  def save(self) -> None:
204
216
  """Save the current in-memory state."""
@@ -230,7 +242,7 @@ class Configuration:
230
242
  logger.debug("Will be working with %s variant only", self.load_only)
231
243
 
232
244
  @property
233
- def _dictionary(self) -> Dict[str, Any]:
245
+ def _dictionary(self) -> dict[str, dict[str, Any]]:
234
246
  """A dictionary representing the loaded configuration."""
235
247
  # NOTE: Dictionaries are not populated if not loaded. So, conditionals
236
248
  # are not needed here.
@@ -270,7 +282,8 @@ class Configuration:
270
282
 
271
283
  for section in parser.sections():
272
284
  items = parser.items(section)
273
- self._config[variant].update(self._normalized_keys(section, items))
285
+ self._config[variant].setdefault(fname, {})
286
+ self._config[variant][fname].update(self._normalized_keys(section, items))
274
287
 
275
288
  return parser
276
289
 
@@ -297,13 +310,14 @@ class Configuration:
297
310
 
298
311
  def _load_environment_vars(self) -> None:
299
312
  """Loads configuration from environment variables"""
300
- self._config[kinds.ENV_VAR].update(
313
+ self._config[kinds.ENV_VAR].setdefault(":env:", {})
314
+ self._config[kinds.ENV_VAR][":env:"].update(
301
315
  self._normalized_keys(":env:", self.get_environ_vars())
302
316
  )
303
317
 
304
318
  def _normalized_keys(
305
- self, section: str, items: Iterable[Tuple[str, Any]]
306
- ) -> Dict[str, Any]:
319
+ self, section: str, items: Iterable[tuple[str, Any]]
320
+ ) -> dict[str, Any]:
307
321
  """Normalizes items to construct a dictionary with normalized keys.
308
322
 
309
323
  This routine is where the names become keys and are made the same
@@ -315,7 +329,7 @@ class Configuration:
315
329
  normalized[key] = val
316
330
  return normalized
317
331
 
318
- def get_environ_vars(self) -> Iterable[Tuple[str, str]]:
332
+ def get_environ_vars(self) -> Iterable[tuple[str, str]]:
319
333
  """Returns a generator with all environmental vars with prefix PIP_"""
320
334
  for key, val in os.environ.items():
321
335
  if key.startswith("PIP_"):
@@ -324,7 +338,7 @@ class Configuration:
324
338
  yield name, val
325
339
 
326
340
  # XXX: This is patched in the tests.
327
- def iter_config_files(self) -> Iterable[Tuple[Kind, List[str]]]:
341
+ def iter_config_files(self) -> Iterable[tuple[Kind, list[str]]]:
328
342
  """Yields variant and configuration files associated with it.
329
343
 
330
344
  This should be treated like items of a dictionary. The order
@@ -356,11 +370,11 @@ class Configuration:
356
370
  else:
357
371
  yield kinds.ENV, []
358
372
 
359
- def get_values_in_config(self, variant: Kind) -> Dict[str, Any]:
373
+ def get_values_in_config(self, variant: Kind) -> dict[str, Any]:
360
374
  """Get values present in a config file"""
361
375
  return self._config[variant]
362
376
 
363
- def _get_parser_to_modify(self) -> Tuple[str, RawConfigParser]:
377
+ def _get_parser_to_modify(self) -> tuple[str, RawConfigParser]:
364
378
  # Determine which parser to modify
365
379
  assert self.load_only
366
380
  parsers = self._parsers[self.load_only]
@@ -1,11 +1,13 @@
1
+ from __future__ import annotations
2
+
1
3
  import abc
2
- from typing import TYPE_CHECKING, Optional
4
+ from typing import TYPE_CHECKING
3
5
 
4
6
  from pip._internal.metadata.base import BaseDistribution
5
7
  from pip._internal.req import InstallRequirement
6
8
 
7
9
  if TYPE_CHECKING:
8
- from pip._internal.index.package_finder import PackageFinder
10
+ from pip._internal.build_env import BuildEnvironmentInstaller
9
11
 
10
12
 
11
13
  class AbstractDistribution(metaclass=abc.ABCMeta):
@@ -32,7 +34,7 @@ class AbstractDistribution(metaclass=abc.ABCMeta):
32
34
  self.req = req
33
35
 
34
36
  @abc.abstractproperty
35
- def build_tracker_id(self) -> Optional[str]:
37
+ def build_tracker_id(self) -> str | None:
36
38
  """A string that uniquely identifies this requirement to the build tracker.
37
39
 
38
40
  If None, then this dist has no work to do in the build tracker, and
@@ -46,7 +48,7 @@ class AbstractDistribution(metaclass=abc.ABCMeta):
46
48
  @abc.abstractmethod
47
49
  def prepare_distribution_metadata(
48
50
  self,
49
- finder: "PackageFinder",
51
+ build_env_installer: BuildEnvironmentInstaller,
50
52
  build_isolation: bool,
51
53
  check_build_deps: bool,
52
54
  ) -> None:
@@ -1,9 +1,13 @@
1
- from typing import Optional
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
2
4
 
3
5
  from pip._internal.distributions.base import AbstractDistribution
4
- from pip._internal.index.package_finder import PackageFinder
5
6
  from pip._internal.metadata import BaseDistribution
6
7
 
8
+ if TYPE_CHECKING:
9
+ from pip._internal.build_env import BuildEnvironmentInstaller
10
+
7
11
 
8
12
  class InstalledDistribution(AbstractDistribution):
9
13
  """Represents an installed package.
@@ -13,7 +17,7 @@ class InstalledDistribution(AbstractDistribution):
13
17
  """
14
18
 
15
19
  @property
16
- def build_tracker_id(self) -> Optional[str]:
20
+ def build_tracker_id(self) -> str | None:
17
21
  return None
18
22
 
19
23
  def get_metadata_distribution(self) -> BaseDistribution:
@@ -22,7 +26,7 @@ class InstalledDistribution(AbstractDistribution):
22
26
 
23
27
  def prepare_distribution_metadata(
24
28
  self,
25
- finder: PackageFinder,
29
+ build_env_installer: BuildEnvironmentInstaller,
26
30
  build_isolation: bool,
27
31
  check_build_deps: bool,
28
32
  ) -> None: