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
@@ -8,12 +8,14 @@ These are meant to be used elsewhere within pip to create instances of
8
8
  InstallRequirement.
9
9
  """
10
10
 
11
+ from __future__ import annotations
12
+
11
13
  import copy
12
14
  import logging
13
15
  import os
14
16
  import re
17
+ from collections.abc import Collection
15
18
  from dataclasses import dataclass
16
- from typing import Collection, Dict, List, Optional, Set, Tuple, Union
17
19
 
18
20
  from pip._vendor.packaging.markers import Marker
19
21
  from pip._vendor.packaging.requirements import InvalidRequirement, Requirement
@@ -41,7 +43,7 @@ logger = logging.getLogger(__name__)
41
43
  operators = Specifier._operators.keys()
42
44
 
43
45
 
44
- def _strip_extras(path: str) -> Tuple[str, Optional[str]]:
46
+ def _strip_extras(path: str) -> tuple[str, str | None]:
45
47
  m = re.match(r"^(.+)(\[[^\]]+\])$", path)
46
48
  extras = None
47
49
  if m:
@@ -53,19 +55,19 @@ def _strip_extras(path: str) -> Tuple[str, Optional[str]]:
53
55
  return path_no_extras, extras
54
56
 
55
57
 
56
- def convert_extras(extras: Optional[str]) -> Set[str]:
58
+ def convert_extras(extras: str | None) -> set[str]:
57
59
  if not extras:
58
60
  return set()
59
61
  return get_requirement("placeholder" + extras.lower()).extras
60
62
 
61
63
 
62
- def _set_requirement_extras(req: Requirement, new_extras: Set[str]) -> Requirement:
64
+ def _set_requirement_extras(req: Requirement, new_extras: set[str]) -> Requirement:
63
65
  """
64
66
  Returns a new requirement based on the given one, with the supplied extras. If the
65
67
  given requirement already has extras those are replaced (or dropped if no new extras
66
68
  are given).
67
69
  """
68
- match: Optional[re.Match[str]] = re.fullmatch(
70
+ match: re.Match[str] | None = re.fullmatch(
69
71
  # see https://peps.python.org/pep-0508/#complete-grammar
70
72
  r"([\w\t .-]+)(\[[^\]]*\])?(.*)",
71
73
  str(req),
@@ -75,8 +77,8 @@ def _set_requirement_extras(req: Requirement, new_extras: Set[str]) -> Requireme
75
77
  assert (
76
78
  match is not None
77
79
  ), f"regex match on requirement {req} failed, this should never happen"
78
- pre: Optional[str] = match.group(1)
79
- post: Optional[str] = match.group(3)
80
+ pre: str | None = match.group(1)
81
+ post: str | None = match.group(3)
80
82
  assert (
81
83
  pre is not None and post is not None
82
84
  ), f"regex group selection for requirement {req} failed, this should never happen"
@@ -84,7 +86,7 @@ def _set_requirement_extras(req: Requirement, new_extras: Set[str]) -> Requireme
84
86
  return get_requirement(f"{pre}{extras}{post}")
85
87
 
86
88
 
87
- def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]:
89
+ def parse_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
88
90
  """Parses an editable requirement into:
89
91
  - a requirement name
90
92
  - an URL
@@ -194,10 +196,10 @@ def deduce_helpful_msg(req: str) -> str:
194
196
 
195
197
  @dataclass(frozen=True)
196
198
  class RequirementParts:
197
- requirement: Optional[Requirement]
198
- link: Optional[Link]
199
- markers: Optional[Marker]
200
- extras: Set[str]
199
+ requirement: Requirement | None
200
+ link: Link | None
201
+ markers: Marker | None
202
+ extras: set[str]
201
203
 
202
204
 
203
205
  def parse_req_from_editable(editable_req: str) -> RequirementParts:
@@ -205,7 +207,7 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts:
205
207
 
206
208
  if name is not None:
207
209
  try:
208
- req: Optional[Requirement] = get_requirement(name)
210
+ req: Requirement | None = get_requirement(name)
209
211
  except InvalidRequirement as exc:
210
212
  raise InstallationError(f"Invalid requirement: {name!r}: {exc}")
211
213
  else:
@@ -221,16 +223,16 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts:
221
223
 
222
224
  def install_req_from_editable(
223
225
  editable_req: str,
224
- comes_from: Optional[Union[InstallRequirement, str]] = None,
226
+ comes_from: InstallRequirement | str | None = None,
225
227
  *,
226
- use_pep517: Optional[bool] = None,
228
+ use_pep517: bool | None = None,
227
229
  isolated: bool = False,
228
- global_options: Optional[List[str]] = None,
229
- hash_options: Optional[Dict[str, List[str]]] = None,
230
+ global_options: list[str] | None = None,
231
+ hash_options: dict[str, list[str]] | None = None,
230
232
  constraint: bool = False,
231
233
  user_supplied: bool = False,
232
234
  permit_editable_wheels: bool = False,
233
- config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
235
+ config_settings: dict[str, str | list[str]] | None = None,
234
236
  ) -> InstallRequirement:
235
237
  parts = parse_req_from_editable(editable_req)
236
238
 
@@ -270,7 +272,7 @@ def _looks_like_path(name: str) -> bool:
270
272
  return False
271
273
 
272
274
 
273
- def _get_url_from_path(path: str, name: str) -> Optional[str]:
275
+ def _get_url_from_path(path: str, name: str) -> str | None:
274
276
  """
275
277
  First, it checks whether a provided path is an installable directory. If it
276
278
  is, returns the path.
@@ -304,7 +306,7 @@ def _get_url_from_path(path: str, name: str) -> Optional[str]:
304
306
  return path_to_url(path)
305
307
 
306
308
 
307
- def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementParts:
309
+ def parse_req_from_line(name: str, line_source: str | None) -> RequirementParts:
308
310
  if is_url(name):
309
311
  marker_sep = "; "
310
312
  else:
@@ -376,7 +378,7 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar
376
378
  raise InstallationError(msg)
377
379
 
378
380
  if req_as_string is not None:
379
- req: Optional[Requirement] = _parse_req_string(req_as_string)
381
+ req: Requirement | None = _parse_req_string(req_as_string)
380
382
  else:
381
383
  req = None
382
384
 
@@ -385,16 +387,16 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar
385
387
 
386
388
  def install_req_from_line(
387
389
  name: str,
388
- comes_from: Optional[Union[str, InstallRequirement]] = None,
390
+ comes_from: str | InstallRequirement | None = None,
389
391
  *,
390
- use_pep517: Optional[bool] = None,
392
+ use_pep517: bool | None = None,
391
393
  isolated: bool = False,
392
- global_options: Optional[List[str]] = None,
393
- hash_options: Optional[Dict[str, List[str]]] = None,
394
+ global_options: list[str] | None = None,
395
+ hash_options: dict[str, list[str]] | None = None,
394
396
  constraint: bool = False,
395
- line_source: Optional[str] = None,
397
+ line_source: str | None = None,
396
398
  user_supplied: bool = False,
397
- config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
399
+ config_settings: dict[str, str | list[str]] | None = None,
398
400
  ) -> InstallRequirement:
399
401
  """Creates an InstallRequirement from a name, which might be a
400
402
  requirement, directory containing 'setup.py', filename, or URL.
@@ -422,9 +424,9 @@ def install_req_from_line(
422
424
 
423
425
  def install_req_from_req_string(
424
426
  req_string: str,
425
- comes_from: Optional[InstallRequirement] = None,
427
+ comes_from: InstallRequirement | None = None,
426
428
  isolated: bool = False,
427
- use_pep517: Optional[bool] = None,
429
+ use_pep517: bool | None = None,
428
430
  user_supplied: bool = False,
429
431
  ) -> InstallRequirement:
430
432
  try:
@@ -461,9 +463,9 @@ def install_req_from_req_string(
461
463
  def install_req_from_parsed_requirement(
462
464
  parsed_req: ParsedRequirement,
463
465
  isolated: bool = False,
464
- use_pep517: Optional[bool] = None,
466
+ use_pep517: bool | None = None,
465
467
  user_supplied: bool = False,
466
- config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
468
+ config_settings: dict[str, str | list[str]] | None = None,
467
469
  ) -> InstallRequirement:
468
470
  if parsed_req.is_editable:
469
471
  req = install_req_from_editable(
@@ -1,12 +1,13 @@
1
- from typing import Any, Dict, Iterable, Iterator, List, Tuple
1
+ from collections.abc import Iterable, Iterator
2
+ from typing import Any
2
3
 
3
- from pip._vendor import tomli
4
4
  from pip._vendor.dependency_groups import DependencyGroupResolver
5
5
 
6
6
  from pip._internal.exceptions import InstallationError
7
+ from pip._internal.utils.compat import tomllib
7
8
 
8
9
 
9
- def parse_dependency_groups(groups: List[Tuple[str, str]]) -> List[str]:
10
+ def parse_dependency_groups(groups: list[tuple[str, str]]) -> list[str]:
10
11
  """
11
12
  Parse dependency groups data as provided via the CLI, in a `[path:]group` syntax.
12
13
 
@@ -17,7 +18,7 @@ def parse_dependency_groups(groups: List[Tuple[str, str]]) -> List[str]:
17
18
 
18
19
 
19
20
  def _resolve_all_groups(
20
- resolvers: Dict[str, DependencyGroupResolver], groups: List[Tuple[str, str]]
21
+ resolvers: dict[str, DependencyGroupResolver], groups: list[tuple[str, str]]
21
22
  ) -> Iterator[str]:
22
23
  """
23
24
  Run all resolution, converting any error from `DependencyGroupResolver` into
@@ -34,7 +35,7 @@ def _resolve_all_groups(
34
35
  ) from e
35
36
 
36
37
 
37
- def _build_resolvers(paths: Iterable[str]) -> Dict[str, Any]:
38
+ def _build_resolvers(paths: Iterable[str]) -> dict[str, Any]:
38
39
  resolvers = {}
39
40
  for path in paths:
40
41
  if path in resolvers:
@@ -57,7 +58,7 @@ def _build_resolvers(paths: Iterable[str]) -> Dict[str, Any]:
57
58
  return resolvers
58
59
 
59
60
 
60
- def _load_pyproject(path: str) -> Dict[str, Any]:
61
+ def _load_pyproject(path: str) -> dict[str, Any]:
61
62
  """
62
63
  This helper loads a pyproject.toml as TOML.
63
64
 
@@ -65,10 +66,10 @@ def _load_pyproject(path: str) -> Dict[str, Any]:
65
66
  """
66
67
  try:
67
68
  with open(path, "rb") as fp:
68
- return tomli.load(fp)
69
+ return tomllib.load(fp)
69
70
  except FileNotFoundError:
70
71
  raise InstallationError(f"{path} not found. Cannot resolve '--group' option.")
71
- except tomli.TOMLDecodeError as e:
72
+ except tomllib.TOMLDecodeError as e:
72
73
  raise InstallationError(f"Error parsing {path}: {e}") from e
73
74
  except OSError as e:
74
75
  raise InstallationError(f"Error reading {path}: {e}") from e
@@ -2,6 +2,8 @@
2
2
  Requirements file parsing
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  import codecs
6
8
  import locale
7
9
  import logging
@@ -11,19 +13,14 @@ import re
11
13
  import shlex
12
14
  import sys
13
15
  import urllib.parse
16
+ from collections.abc import Generator, Iterable
14
17
  from dataclasses import dataclass
15
18
  from optparse import Values
16
19
  from typing import (
17
20
  TYPE_CHECKING,
18
21
  Any,
19
22
  Callable,
20
- Dict,
21
- Generator,
22
- Iterable,
23
- List,
24
23
  NoReturn,
25
- Optional,
26
- Tuple,
27
24
  )
28
25
 
29
26
  from pip._internal.cli import cmdoptions
@@ -36,9 +33,9 @@ if TYPE_CHECKING:
36
33
 
37
34
  __all__ = ["parse_requirements"]
38
35
 
39
- ReqFileLines = Iterable[Tuple[int, str]]
36
+ ReqFileLines = Iterable[tuple[int, str]]
40
37
 
41
- LineParser = Callable[[str], Tuple[str, Values]]
38
+ LineParser = Callable[[str], tuple[str, Values]]
42
39
 
43
40
  SCHEME_RE = re.compile(r"^(http|https|file):", re.I)
44
41
  COMMENT_RE = re.compile(r"(^|\s+)#.*$")
@@ -49,7 +46,7 @@ COMMENT_RE = re.compile(r"(^|\s+)#.*$")
49
46
  # 2013 Edition.
50
47
  ENV_VAR_RE = re.compile(r"(?P<var>\$\{(?P<name>[A-Z0-9_]+)\})")
51
48
 
52
- SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [
49
+ SUPPORTED_OPTIONS: list[Callable[..., optparse.Option]] = [
53
50
  cmdoptions.index_url,
54
51
  cmdoptions.extra_index_url,
55
52
  cmdoptions.no_index,
@@ -67,13 +64,13 @@ SUPPORTED_OPTIONS: List[Callable[..., optparse.Option]] = [
67
64
  ]
68
65
 
69
66
  # options to be passed to requirements
70
- SUPPORTED_OPTIONS_REQ: List[Callable[..., optparse.Option]] = [
67
+ SUPPORTED_OPTIONS_REQ: list[Callable[..., optparse.Option]] = [
71
68
  cmdoptions.global_options,
72
69
  cmdoptions.hash,
73
70
  cmdoptions.config_settings,
74
71
  ]
75
72
 
76
- SUPPORTED_OPTIONS_EDITABLE_REQ: List[Callable[..., optparse.Option]] = [
73
+ SUPPORTED_OPTIONS_EDITABLE_REQ: list[Callable[..., optparse.Option]] = [
77
74
  cmdoptions.config_settings,
78
75
  ]
79
76
 
@@ -86,7 +83,7 @@ SUPPORTED_OPTIONS_EDITABLE_REQ_DEST = [
86
83
 
87
84
  # order of BOMS is important: codecs.BOM_UTF16_LE is a prefix of codecs.BOM_UTF32_LE
88
85
  # so data.startswith(BOM_UTF16_LE) would be true for UTF32_LE data
89
- BOMS: List[Tuple[bytes, str]] = [
86
+ BOMS: list[tuple[bytes, str]] = [
90
87
  (codecs.BOM_UTF8, "utf-8"),
91
88
  (codecs.BOM_UTF32, "utf-32"),
92
89
  (codecs.BOM_UTF32_BE, "utf-32-be"),
@@ -118,8 +115,8 @@ class ParsedRequirement:
118
115
  is_editable: bool
119
116
  comes_from: str
120
117
  constraint: bool
121
- options: Optional[Dict[str, Any]]
122
- line_source: Optional[str]
118
+ options: dict[str, Any] | None
119
+ line_source: str | None
123
120
 
124
121
 
125
122
  @dataclass(frozen=True)
@@ -137,7 +134,7 @@ class ParsedLine:
137
134
  return bool(self.opts.editables)
138
135
 
139
136
  @property
140
- def requirement(self) -> Optional[str]:
137
+ def requirement(self) -> str | None:
141
138
  if self.args:
142
139
  return self.args
143
140
  elif self.is_editable:
@@ -148,9 +145,9 @@ class ParsedLine:
148
145
 
149
146
  def parse_requirements(
150
147
  filename: str,
151
- session: "PipSession",
152
- finder: Optional["PackageFinder"] = None,
153
- options: Optional[optparse.Values] = None,
148
+ session: PipSession,
149
+ finder: PackageFinder | None = None,
150
+ options: optparse.Values | None = None,
154
151
  constraint: bool = False,
155
152
  ) -> Generator[ParsedRequirement, None, None]:
156
153
  """Parse a requirements file and yield ParsedRequirement instances.
@@ -187,7 +184,7 @@ def preprocess(content: str) -> ReqFileLines:
187
184
 
188
185
  def handle_requirement_line(
189
186
  line: ParsedLine,
190
- options: Optional[optparse.Values] = None,
187
+ options: optparse.Values | None = None,
191
188
  ) -> ParsedRequirement:
192
189
  # preserve for the nested code path
193
190
  line_comes_from = "{} {} (line {})".format(
@@ -223,9 +220,9 @@ def handle_option_line(
223
220
  opts: Values,
224
221
  filename: str,
225
222
  lineno: int,
226
- finder: Optional["PackageFinder"] = None,
227
- options: Optional[optparse.Values] = None,
228
- session: Optional["PipSession"] = None,
223
+ finder: PackageFinder | None = None,
224
+ options: optparse.Values | None = None,
225
+ session: PipSession | None = None,
229
226
  ) -> None:
230
227
  if opts.hashes:
231
228
  logger.warning(
@@ -291,10 +288,10 @@ def handle_option_line(
291
288
 
292
289
  def handle_line(
293
290
  line: ParsedLine,
294
- options: Optional[optparse.Values] = None,
295
- finder: Optional["PackageFinder"] = None,
296
- session: Optional["PipSession"] = None,
297
- ) -> Optional[ParsedRequirement]:
291
+ options: optparse.Values | None = None,
292
+ finder: PackageFinder | None = None,
293
+ session: PipSession | None = None,
294
+ ) -> ParsedRequirement | None:
298
295
  """Handle a single parsed requirements line; This can result in
299
296
  creating/yielding requirements, or updating the finder.
300
297
 
@@ -336,7 +333,7 @@ def handle_line(
336
333
  class RequirementsFileParser:
337
334
  def __init__(
338
335
  self,
339
- session: "PipSession",
336
+ session: PipSession,
340
337
  line_parser: LineParser,
341
338
  ) -> None:
342
339
  self._session = session
@@ -354,7 +351,7 @@ class RequirementsFileParser:
354
351
  self,
355
352
  filename: str,
356
353
  constraint: bool,
357
- parsed_files_stack: List[Dict[str, Optional[str]]],
354
+ parsed_files_stack: list[dict[str, str | None]],
358
355
  ) -> Generator[ParsedLine, None, None]:
359
356
  for line in self._parse_file(filename, constraint):
360
357
  if line.requirement is None and (
@@ -426,8 +423,8 @@ class RequirementsFileParser:
426
423
  )
427
424
 
428
425
 
429
- def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser:
430
- def parse_line(line: str) -> Tuple[str, Values]:
426
+ def get_line_parser(finder: PackageFinder | None) -> LineParser:
427
+ def parse_line(line: str) -> tuple[str, Values]:
431
428
  # Build new parser for each line since it accumulates appendable
432
429
  # options.
433
430
  parser = build_parser()
@@ -450,7 +447,7 @@ def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser:
450
447
  return parse_line
451
448
 
452
449
 
453
- def break_args_options(line: str) -> Tuple[str, str]:
450
+ def break_args_options(line: str) -> tuple[str, str]:
454
451
  """Break up the line into an args and options string. We only want to shlex
455
452
  (and then optparse) the options, not the args. args can contain markers
456
453
  which are corrupted by shlex.
@@ -459,7 +456,7 @@ def break_args_options(line: str) -> Tuple[str, str]:
459
456
  args = []
460
457
  options = tokens[:]
461
458
  for token in tokens:
462
- if token.startswith("-") or token.startswith("--"):
459
+ if token.startswith(("-", "--")):
463
460
  break
464
461
  else:
465
462
  args.append(token)
@@ -485,7 +482,7 @@ def build_parser() -> optparse.OptionParser:
485
482
 
486
483
  # By default optparse sys.exits on parsing errors. We want to wrap
487
484
  # that in our own exception.
488
- def parser_exit(self: Any, msg: str) -> "NoReturn":
485
+ def parser_exit(self: Any, msg: str) -> NoReturn:
489
486
  raise OptionParsingError(msg)
490
487
 
491
488
  # NOTE: mypy disallows assigning to a method
@@ -500,7 +497,7 @@ def join_lines(lines_enum: ReqFileLines) -> ReqFileLines:
500
497
  comments). The joined line takes on the index of the first line.
501
498
  """
502
499
  primary_line_number = None
503
- new_line: List[str] = []
500
+ new_line: list[str] = []
504
501
  for line_number, line in lines_enum:
505
502
  if not line.endswith("\\") or COMMENT_RE.match(line):
506
503
  if COMMENT_RE.match(line):
@@ -564,7 +561,7 @@ def expand_env_variables(lines_enum: ReqFileLines) -> ReqFileLines:
564
561
  yield line_number, line
565
562
 
566
563
 
567
- def get_file_content(url: str, session: "PipSession") -> Tuple[str, str]:
564
+ def get_file_content(url: str, session: PipSession) -> tuple[str, str]:
568
565
  """Gets the content of a file; it may be a filename, file: URL, or
569
566
  http: URL. Returns (location, content). Content is unicode.
570
567
  Respects # -*- coding: declarations on the retrieved files.
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import functools
2
4
  import logging
3
5
  import os
@@ -5,9 +7,10 @@ import shutil
5
7
  import sys
6
8
  import uuid
7
9
  import zipfile
10
+ from collections.abc import Collection, Iterable, Sequence
8
11
  from optparse import Values
9
12
  from pathlib import Path
10
- from typing import Any, Collection, Dict, Iterable, List, Optional, Sequence, Union
13
+ from typing import Any
11
14
 
12
15
  from pip._vendor.packaging.markers import Marker
13
16
  from pip._vendor.packaging.requirements import Requirement
@@ -71,17 +74,17 @@ class InstallRequirement:
71
74
 
72
75
  def __init__(
73
76
  self,
74
- req: Optional[Requirement],
75
- comes_from: Optional[Union[str, "InstallRequirement"]],
77
+ req: Requirement | None,
78
+ comes_from: str | InstallRequirement | None,
76
79
  editable: bool = False,
77
- link: Optional[Link] = None,
78
- markers: Optional[Marker] = None,
79
- use_pep517: Optional[bool] = None,
80
+ link: Link | None = None,
81
+ markers: Marker | None = None,
82
+ use_pep517: bool | None = None,
80
83
  isolated: bool = False,
81
84
  *,
82
- global_options: Optional[List[str]] = None,
83
- hash_options: Optional[Dict[str, List[str]]] = None,
84
- config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
85
+ global_options: list[str] | None = None,
86
+ hash_options: dict[str, list[str]] | None = None,
87
+ config_settings: dict[str, str | list[str]] | None = None,
85
88
  constraint: bool = False,
86
89
  extras: Collection[str] = (),
87
90
  user_supplied: bool = False,
@@ -99,7 +102,7 @@ class InstallRequirement:
99
102
  # populating source_dir is done by the RequirementPreparer. Note this
100
103
  # is not necessarily the directory where pyproject.toml or setup.py is
101
104
  # located - that one is obtained via unpacked_source_directory.
102
- self.source_dir: Optional[str] = None
105
+ self.source_dir: str | None = None
103
106
  if self.editable:
104
107
  assert link
105
108
  if link.is_file:
@@ -115,14 +118,14 @@ class InstallRequirement:
115
118
  # When this InstallRequirement is a wheel obtained from the cache of locally
116
119
  # built wheels, this is the source link corresponding to the cache entry, which
117
120
  # was used to download and build the cached wheel.
118
- self.cached_wheel_source_link: Optional[Link] = None
121
+ self.cached_wheel_source_link: Link | None = None
119
122
 
120
123
  # Information about the location of the artifact that was downloaded . This
121
124
  # property is guaranteed to be set in resolver results.
122
- self.download_info: Optional[DirectUrl] = None
125
+ self.download_info: DirectUrl | None = None
123
126
 
124
127
  # Path to any downloaded or already-existing package.
125
- self.local_file_path: Optional[str] = None
128
+ self.local_file_path: str | None = None
126
129
  if self.link and self.link.is_file:
127
130
  self.local_file_path = self.link.file_path
128
131
 
@@ -137,14 +140,14 @@ class InstallRequirement:
137
140
  self.markers = markers
138
141
 
139
142
  # This holds the Distribution object if this requirement is already installed.
140
- self.satisfied_by: Optional[BaseDistribution] = None
143
+ self.satisfied_by: BaseDistribution | None = None
141
144
  # Whether the installation process should try to uninstall an existing
142
145
  # distribution before installing this requirement.
143
146
  self.should_reinstall = False
144
147
  # Temporary build location
145
- self._temp_build_dir: Optional[TempDirectory] = None
148
+ self._temp_build_dir: TempDirectory | None = None
146
149
  # Set to True after successful installation
147
- self.install_succeeded: Optional[bool] = None
150
+ self.install_succeeded: bool | None = None
148
151
  # Supplied options
149
152
  self.global_options = global_options if global_options else []
150
153
  self.hash_options = hash_options if hash_options else {}
@@ -163,16 +166,16 @@ class InstallRequirement:
163
166
  # gets stored. We need this to pass to build_wheel, so the backend
164
167
  # can ensure that the wheel matches the metadata (see the PEP for
165
168
  # details).
166
- self.metadata_directory: Optional[str] = None
169
+ self.metadata_directory: str | None = None
167
170
 
168
171
  # The static build requirements (from pyproject.toml)
169
- self.pyproject_requires: Optional[List[str]] = None
172
+ self.pyproject_requires: list[str] | None = None
170
173
 
171
174
  # Build requirements that we will check are available
172
- self.requirements_to_check: List[str] = []
175
+ self.requirements_to_check: list[str] = []
173
176
 
174
177
  # The PEP 517 backend we should use to build the project
175
- self.pep517_backend: Optional[BuildBackendHookCaller] = None
178
+ self.pep517_backend: BuildBackendHookCaller | None = None
176
179
 
177
180
  # Are we using PEP 517 for this requirement?
178
181
  # After pyproject.toml has been loaded, the only valid values are True
@@ -195,7 +198,7 @@ class InstallRequirement:
195
198
  self.needs_more_preparation = False
196
199
 
197
200
  # This requirement needs to be unpacked before it can be installed.
198
- self._archive_source: Optional[Path] = None
201
+ self._archive_source: Path | None = None
199
202
 
200
203
  def __str__(self) -> str:
201
204
  if self.req:
@@ -214,7 +217,7 @@ class InstallRequirement:
214
217
  s += f" in {location}"
215
218
  if self.comes_from:
216
219
  if isinstance(self.comes_from, str):
217
- comes_from: Optional[str] = self.comes_from
220
+ comes_from: str | None = self.comes_from
218
221
  else:
219
222
  comes_from = self.comes_from.from_path()
220
223
  if comes_from:
@@ -240,7 +243,7 @@ class InstallRequirement:
240
243
 
241
244
  # Things that are valid for all kinds of requirements?
242
245
  @property
243
- def name(self) -> Optional[str]:
246
+ def name(self) -> str | None:
244
247
  if self.req is None:
245
248
  return None
246
249
  return self.req.name
@@ -277,7 +280,7 @@ class InstallRequirement:
277
280
  specifiers = self.req.specifier
278
281
  return len(specifiers) == 1 and next(iter(specifiers)).operator in {"==", "==="}
279
282
 
280
- def match_markers(self, extras_requested: Optional[Iterable[str]] = None) -> bool:
283
+ def match_markers(self, extras_requested: Iterable[str] | None = None) -> bool:
281
284
  if not extras_requested:
282
285
  # Provide an extra to safely evaluate the markers
283
286
  # without matching any extra
@@ -326,13 +329,13 @@ class InstallRequirement:
326
329
  good_hashes.setdefault(link.hash_name, []).append(link.hash)
327
330
  return Hashes(good_hashes)
328
331
 
329
- def from_path(self) -> Optional[str]:
332
+ def from_path(self) -> str | None:
330
333
  """Format a nice indicator to show where this "comes from" """
331
334
  if self.req is None:
332
335
  return None
333
336
  s = str(self.req)
334
337
  if self.comes_from:
335
- comes_from: Optional[str]
338
+ comes_from: str | None
336
339
  if isinstance(self.comes_from, str):
337
340
  comes_from = self.comes_from
338
341
  else:
@@ -699,7 +702,7 @@ class InstallRequirement:
699
702
  # Top-level Actions
700
703
  def uninstall(
701
704
  self, auto_confirm: bool = False, verbose: bool = False
702
- ) -> Optional[UninstallPathSet]:
705
+ ) -> UninstallPathSet | None:
703
706
  """
704
707
  Uninstall the distribution currently satisfying this requirement.
705
708
 
@@ -737,7 +740,7 @@ class InstallRequirement:
737
740
  name = _clean_zip_name(path, rootdir)
738
741
  return self.req.name + "/" + name
739
742
 
740
- def archive(self, build_dir: Optional[str]) -> None:
743
+ def archive(self, build_dir: str | None) -> None:
741
744
  """Saves archive to provided build_dir.
742
745
 
743
746
  Used for saving downloaded VCS requirements as part of `pip download`.
@@ -806,10 +809,10 @@ class InstallRequirement:
806
809
 
807
810
  def install(
808
811
  self,
809
- global_options: Optional[Sequence[str]] = None,
810
- root: Optional[str] = None,
811
- home: Optional[str] = None,
812
- prefix: Optional[str] = None,
812
+ global_options: Sequence[str] | None = None,
813
+ root: str | None = None,
814
+ home: str | None = None,
815
+ prefix: str | None = None,
813
816
  warn_script_location: bool = True,
814
817
  use_user_site: bool = False,
815
818
  pycompile: bool = True,
@@ -905,7 +908,7 @@ def check_invalid_constraint_type(req: InstallRequirement) -> str:
905
908
  return problem
906
909
 
907
910
 
908
- def _has_option(options: Values, reqs: List[InstallRequirement], option: str) -> bool:
911
+ def _has_option(options: Values, reqs: list[InstallRequirement], option: str) -> bool:
909
912
  if getattr(options, option, None):
910
913
  return True
911
914
  for req in reqs:
@@ -916,7 +919,7 @@ def _has_option(options: Values, reqs: List[InstallRequirement], option: str) ->
916
919
 
917
920
  def check_legacy_setup_py_options(
918
921
  options: Values,
919
- reqs: List[InstallRequirement],
922
+ reqs: list[InstallRequirement],
920
923
  ) -> None:
921
924
  has_build_options = _has_option(options, reqs, "build_options")
922
925
  has_global_options = _has_option(options, reqs, "global_options")