pip 25.1.1__py3-none-any.whl → 25.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. pip/__init__.py +3 -3
  2. pip/_internal/__init__.py +2 -2
  3. pip/_internal/build_env.py +186 -94
  4. pip/_internal/cache.py +17 -15
  5. pip/_internal/cli/autocompletion.py +13 -4
  6. pip/_internal/cli/base_command.py +18 -7
  7. pip/_internal/cli/cmdoptions.py +57 -80
  8. pip/_internal/cli/command_context.py +4 -3
  9. pip/_internal/cli/index_command.py +11 -9
  10. pip/_internal/cli/main.py +3 -2
  11. pip/_internal/cli/main_parser.py +4 -3
  12. pip/_internal/cli/parser.py +24 -20
  13. pip/_internal/cli/progress_bars.py +19 -12
  14. pip/_internal/cli/req_command.py +57 -33
  15. pip/_internal/cli/spinners.py +81 -5
  16. pip/_internal/commands/__init__.py +5 -3
  17. pip/_internal/commands/cache.py +18 -15
  18. pip/_internal/commands/check.py +1 -2
  19. pip/_internal/commands/completion.py +1 -2
  20. pip/_internal/commands/configuration.py +26 -18
  21. pip/_internal/commands/debug.py +8 -6
  22. pip/_internal/commands/download.py +6 -10
  23. pip/_internal/commands/freeze.py +2 -3
  24. pip/_internal/commands/hash.py +1 -2
  25. pip/_internal/commands/help.py +1 -2
  26. pip/_internal/commands/index.py +15 -9
  27. pip/_internal/commands/inspect.py +4 -4
  28. pip/_internal/commands/install.py +63 -53
  29. pip/_internal/commands/list.py +35 -26
  30. pip/_internal/commands/lock.py +4 -8
  31. pip/_internal/commands/search.py +14 -12
  32. pip/_internal/commands/show.py +14 -11
  33. pip/_internal/commands/uninstall.py +1 -2
  34. pip/_internal/commands/wheel.py +7 -13
  35. pip/_internal/configuration.py +40 -27
  36. pip/_internal/distributions/base.py +6 -4
  37. pip/_internal/distributions/installed.py +8 -4
  38. pip/_internal/distributions/sdist.py +33 -27
  39. pip/_internal/distributions/wheel.py +6 -4
  40. pip/_internal/exceptions.py +78 -42
  41. pip/_internal/index/collector.py +24 -29
  42. pip/_internal/index/package_finder.py +73 -64
  43. pip/_internal/index/sources.py +17 -14
  44. pip/_internal/locations/__init__.py +18 -16
  45. pip/_internal/locations/_distutils.py +12 -11
  46. pip/_internal/locations/_sysconfig.py +5 -4
  47. pip/_internal/locations/base.py +4 -3
  48. pip/_internal/main.py +2 -2
  49. pip/_internal/metadata/__init__.py +14 -7
  50. pip/_internal/metadata/_json.py +5 -4
  51. pip/_internal/metadata/base.py +22 -27
  52. pip/_internal/metadata/importlib/_compat.py +6 -4
  53. pip/_internal/metadata/importlib/_dists.py +20 -19
  54. pip/_internal/metadata/importlib/_envs.py +9 -6
  55. pip/_internal/metadata/pkg_resources.py +11 -14
  56. pip/_internal/models/direct_url.py +24 -21
  57. pip/_internal/models/format_control.py +5 -5
  58. pip/_internal/models/installation_report.py +4 -3
  59. pip/_internal/models/link.py +39 -34
  60. pip/_internal/models/pylock.py +27 -22
  61. pip/_internal/models/search_scope.py +6 -7
  62. pip/_internal/models/selection_prefs.py +3 -3
  63. pip/_internal/models/target_python.py +10 -9
  64. pip/_internal/models/wheel.py +12 -71
  65. pip/_internal/network/auth.py +20 -22
  66. pip/_internal/network/cache.py +28 -17
  67. pip/_internal/network/download.py +169 -141
  68. pip/_internal/network/lazy_wheel.py +15 -10
  69. pip/_internal/network/session.py +32 -27
  70. pip/_internal/network/utils.py +2 -2
  71. pip/_internal/network/xmlrpc.py +2 -2
  72. pip/_internal/operations/build/build_tracker.py +10 -8
  73. pip/_internal/operations/build/wheel.py +7 -6
  74. pip/_internal/operations/build/wheel_editable.py +7 -6
  75. pip/_internal/operations/check.py +21 -26
  76. pip/_internal/operations/freeze.py +12 -9
  77. pip/_internal/operations/install/wheel.py +49 -41
  78. pip/_internal/operations/prepare.py +42 -31
  79. pip/_internal/pyproject.py +7 -69
  80. pip/_internal/req/__init__.py +12 -12
  81. pip/_internal/req/constructors.py +68 -62
  82. pip/_internal/req/req_dependency_group.py +7 -11
  83. pip/_internal/req/req_file.py +32 -36
  84. pip/_internal/req/req_install.py +64 -170
  85. pip/_internal/req/req_set.py +4 -5
  86. pip/_internal/req/req_uninstall.py +20 -17
  87. pip/_internal/resolution/base.py +3 -3
  88. pip/_internal/resolution/legacy/resolver.py +21 -20
  89. pip/_internal/resolution/resolvelib/base.py +16 -13
  90. pip/_internal/resolution/resolvelib/candidates.py +49 -37
  91. pip/_internal/resolution/resolvelib/factory.py +72 -50
  92. pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
  93. pip/_internal/resolution/resolvelib/provider.py +24 -20
  94. pip/_internal/resolution/resolvelib/reporter.py +26 -11
  95. pip/_internal/resolution/resolvelib/requirements.py +8 -6
  96. pip/_internal/resolution/resolvelib/resolver.py +41 -29
  97. pip/_internal/self_outdated_check.py +19 -9
  98. pip/_internal/utils/appdirs.py +1 -2
  99. pip/_internal/utils/compat.py +7 -1
  100. pip/_internal/utils/compatibility_tags.py +17 -16
  101. pip/_internal/utils/deprecation.py +11 -9
  102. pip/_internal/utils/direct_url_helpers.py +2 -2
  103. pip/_internal/utils/egg_link.py +6 -5
  104. pip/_internal/utils/entrypoints.py +3 -2
  105. pip/_internal/utils/filesystem.py +20 -5
  106. pip/_internal/utils/filetypes.py +4 -6
  107. pip/_internal/utils/glibc.py +6 -5
  108. pip/_internal/utils/hashes.py +9 -6
  109. pip/_internal/utils/logging.py +8 -5
  110. pip/_internal/utils/misc.py +37 -45
  111. pip/_internal/utils/packaging.py +3 -2
  112. pip/_internal/utils/retry.py +7 -4
  113. pip/_internal/utils/subprocess.py +20 -17
  114. pip/_internal/utils/temp_dir.py +10 -12
  115. pip/_internal/utils/unpacking.py +31 -4
  116. pip/_internal/utils/urls.py +1 -1
  117. pip/_internal/utils/virtualenv.py +3 -2
  118. pip/_internal/utils/wheel.py +3 -4
  119. pip/_internal/vcs/bazaar.py +26 -8
  120. pip/_internal/vcs/git.py +59 -24
  121. pip/_internal/vcs/mercurial.py +34 -11
  122. pip/_internal/vcs/subversion.py +27 -16
  123. pip/_internal/vcs/versioncontrol.py +56 -51
  124. pip/_internal/wheel_builder.py +30 -101
  125. pip/_vendor/README.rst +180 -0
  126. pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  127. pip/_vendor/cachecontrol/__init__.py +1 -1
  128. pip/_vendor/certifi/LICENSE +20 -0
  129. pip/_vendor/certifi/__init__.py +1 -1
  130. pip/_vendor/certifi/cacert.pem +164 -261
  131. pip/_vendor/certifi/core.py +1 -32
  132. pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  133. pip/_vendor/distlib/LICENSE.txt +284 -0
  134. pip/_vendor/distlib/__init__.py +2 -2
  135. pip/_vendor/distlib/scripts.py +1 -1
  136. pip/_vendor/distro/LICENSE +202 -0
  137. pip/_vendor/idna/LICENSE.md +31 -0
  138. pip/_vendor/msgpack/COPYING +14 -0
  139. pip/_vendor/msgpack/__init__.py +2 -2
  140. pip/_vendor/packaging/LICENSE +3 -0
  141. pip/_vendor/packaging/LICENSE.APACHE +177 -0
  142. pip/_vendor/packaging/LICENSE.BSD +23 -0
  143. pip/_vendor/pkg_resources/LICENSE +17 -0
  144. pip/_vendor/pkg_resources/__init__.py +1 -1
  145. pip/_vendor/platformdirs/LICENSE +21 -0
  146. pip/_vendor/platformdirs/api.py +1 -1
  147. pip/_vendor/platformdirs/macos.py +10 -8
  148. pip/_vendor/platformdirs/version.py +16 -3
  149. pip/_vendor/pygments/LICENSE +25 -0
  150. pip/_vendor/pygments/__init__.py +1 -1
  151. pip/_vendor/pyproject_hooks/LICENSE +21 -0
  152. pip/_vendor/requests/LICENSE +175 -0
  153. pip/_vendor/requests/__version__.py +2 -2
  154. pip/_vendor/requests/adapters.py +17 -40
  155. pip/_vendor/requests/compat.py +12 -0
  156. pip/_vendor/requests/models.py +3 -1
  157. pip/_vendor/requests/sessions.py +1 -1
  158. pip/_vendor/requests/utils.py +6 -16
  159. pip/_vendor/resolvelib/LICENSE +13 -0
  160. pip/_vendor/resolvelib/__init__.py +3 -3
  161. pip/_vendor/resolvelib/reporters.py +1 -1
  162. pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
  163. pip/_vendor/resolvelib/resolvers/abstract.py +3 -3
  164. pip/_vendor/resolvelib/resolvers/resolution.py +96 -10
  165. pip/_vendor/rich/LICENSE +19 -0
  166. pip/_vendor/rich/__main__.py +12 -40
  167. pip/_vendor/rich/_inspect.py +1 -1
  168. pip/_vendor/rich/_ratio.py +1 -7
  169. pip/_vendor/rich/align.py +1 -7
  170. pip/_vendor/rich/box.py +1 -7
  171. pip/_vendor/rich/console.py +25 -20
  172. pip/_vendor/rich/control.py +1 -7
  173. pip/_vendor/rich/diagnose.py +1 -0
  174. pip/_vendor/rich/emoji.py +1 -6
  175. pip/_vendor/rich/live.py +32 -7
  176. pip/_vendor/rich/live_render.py +1 -7
  177. pip/_vendor/rich/logging.py +1 -1
  178. pip/_vendor/rich/panel.py +3 -4
  179. pip/_vendor/rich/progress.py +15 -15
  180. pip/_vendor/rich/spinner.py +7 -13
  181. pip/_vendor/rich/style.py +7 -11
  182. pip/_vendor/rich/syntax.py +24 -5
  183. pip/_vendor/rich/traceback.py +32 -17
  184. pip/_vendor/tomli/LICENSE +21 -0
  185. pip/_vendor/tomli/__init__.py +1 -1
  186. pip/_vendor/tomli/_parser.py +28 -21
  187. pip/_vendor/tomli/_re.py +8 -5
  188. pip/_vendor/tomli_w/LICENSE +21 -0
  189. pip/_vendor/truststore/LICENSE +21 -0
  190. pip/_vendor/truststore/__init__.py +1 -1
  191. pip/_vendor/truststore/_api.py +15 -7
  192. pip/_vendor/truststore/_openssl.py +3 -1
  193. pip/_vendor/urllib3/LICENSE.txt +21 -0
  194. pip/_vendor/vendor.txt +11 -12
  195. {pip-25.1.1.dist-info → pip-25.3.dist-info}/METADATA +32 -11
  196. {pip-25.1.1.dist-info → pip-25.3.dist-info}/RECORD +221 -192
  197. {pip-25.1.1.dist-info → pip-25.3.dist-info}/WHEEL +1 -2
  198. pip-25.3.dist-info/entry_points.txt +4 -0
  199. {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/AUTHORS.txt +21 -0
  200. pip-25.3.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  201. pip-25.3.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
  202. pip-25.3.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  203. pip-25.3.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
  204. pip-25.3.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
  205. pip-25.3.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
  206. pip-25.3.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
  207. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
  208. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
  209. pip-25.3.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
  210. pip-25.3.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
  211. pip-25.3.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
  212. pip-25.3.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
  213. pip-25.3.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
  214. pip-25.3.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
  215. pip-25.3.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
  216. pip-25.3.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
  217. pip-25.3.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
  218. pip-25.3.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
  219. pip-25.3.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
  220. pip-25.3.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
  221. pip/_internal/operations/build/metadata_legacy.py +0 -73
  222. pip/_internal/operations/build/wheel_legacy.py +0 -118
  223. pip/_internal/operations/install/editable_legacy.py +0 -46
  224. pip/_internal/utils/setuptools_build.py +0 -147
  225. pip/_vendor/distlib/database.py +0 -1329
  226. pip/_vendor/distlib/index.py +0 -508
  227. pip/_vendor/distlib/locators.py +0 -1295
  228. pip/_vendor/distlib/manifest.py +0 -384
  229. pip/_vendor/distlib/markers.py +0 -162
  230. pip/_vendor/distlib/metadata.py +0 -1031
  231. pip/_vendor/distlib/version.py +0 -750
  232. pip/_vendor/distlib/wheel.py +0 -1100
  233. pip/_vendor/typing_extensions.py +0 -4584
  234. pip-25.1.1.dist-info/entry_points.txt +0 -3
  235. pip-25.1.1.dist-info/top_level.txt +0 -1
  236. {pip-25.1.1.dist-info → pip-25.3.dist-info}/licenses/LICENSE.txt +0 -0
@@ -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,11 +43,11 @@ 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:
48
- path_no_extras = m.group(1)
50
+ path_no_extras = m.group(1).rstrip()
49
51
  extras = m.group(2)
50
52
  else:
51
53
  path_no_extras = path
@@ -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,17 +86,25 @@ 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]]:
88
- """Parses an editable requirement into:
89
- - a requirement name
90
- - an URL
91
- - extras
92
- - editable options
93
- Accepted requirements:
94
- svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
95
- .[some_extra]
96
- """
89
+ def _parse_direct_url_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
90
+ try:
91
+ req = Requirement(editable_req)
92
+ except InvalidRequirement:
93
+ pass
94
+ else:
95
+ if req.url:
96
+ # Join the marker back into the name part. This will be parsed out
97
+ # later into a Requirement again.
98
+ if req.marker:
99
+ name = f"{req.name} ; {req.marker}"
100
+ else:
101
+ name = req.name
102
+ return (name, req.url, req.extras)
103
+
104
+ raise ValueError
97
105
 
106
+
107
+ def _parse_pip_syntax_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
98
108
  url = editable_req
99
109
 
100
110
  # If a file path is specified with extras, strip off the extras.
@@ -120,9 +130,27 @@ def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]:
120
130
  url = f"{version_control}+{url}"
121
131
  break
122
132
 
133
+ return Link(url).egg_fragment, url, set()
134
+
135
+
136
+ def parse_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
137
+ """Parses an editable requirement into:
138
+ - a requirement name with environment markers
139
+ - an URL
140
+ - extras
141
+ Accepted requirements:
142
+ - svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
143
+ - local_path[some_extra]
144
+ - Foobar[extra] @ svn+http://blahblah@rev#subdirectory=subdir ; markers
145
+ """
146
+ try:
147
+ package_name, url, extras = _parse_direct_url_editable(editable_req)
148
+ except ValueError:
149
+ package_name, url, extras = _parse_pip_syntax_editable(editable_req)
150
+
123
151
  link = Link(url)
124
152
 
125
- if not link.is_vcs:
153
+ if not link.is_vcs and not link.url.startswith("file:"):
126
154
  backends = ", ".join(vcs.all_schemes)
127
155
  raise InstallationError(
128
156
  f"{editable_req} is not a valid editable requirement. "
@@ -130,13 +158,13 @@ def parse_editable(editable_req: str) -> Tuple[Optional[str], str, Set[str]]:
130
158
  f"(beginning with {backends})."
131
159
  )
132
160
 
133
- package_name = link.egg_fragment
134
- if not package_name:
161
+ # The project name can be inferred from local file URIs easily.
162
+ if not package_name and not link.url.startswith("file:"):
135
163
  raise InstallationError(
136
164
  f"Could not detect requirement name for '{editable_req}', "
137
- "please specify one with #egg=your_package_name"
165
+ "please specify one with your_package_name @ URL"
138
166
  )
139
- return package_name, url, set()
167
+ return package_name, url, extras
140
168
 
141
169
 
142
170
  def check_first_requirement_in_file(filename: str) -> None:
@@ -194,10 +222,10 @@ def deduce_helpful_msg(req: str) -> str:
194
222
 
195
223
  @dataclass(frozen=True)
196
224
  class RequirementParts:
197
- requirement: Optional[Requirement]
198
- link: Optional[Link]
199
- markers: Optional[Marker]
200
- extras: Set[str]
225
+ requirement: Requirement | None
226
+ link: Link | None
227
+ markers: Marker | None
228
+ extras: set[str]
201
229
 
202
230
 
203
231
  def parse_req_from_editable(editable_req: str) -> RequirementParts:
@@ -205,7 +233,7 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts:
205
233
 
206
234
  if name is not None:
207
235
  try:
208
- req: Optional[Requirement] = get_requirement(name)
236
+ req: Requirement | None = get_requirement(name)
209
237
  except InvalidRequirement as exc:
210
238
  raise InstallationError(f"Invalid requirement: {name!r}: {exc}")
211
239
  else:
@@ -221,16 +249,14 @@ def parse_req_from_editable(editable_req: str) -> RequirementParts:
221
249
 
222
250
  def install_req_from_editable(
223
251
  editable_req: str,
224
- comes_from: Optional[Union[InstallRequirement, str]] = None,
252
+ comes_from: InstallRequirement | str | None = None,
225
253
  *,
226
- use_pep517: Optional[bool] = None,
227
254
  isolated: bool = False,
228
- global_options: Optional[List[str]] = None,
229
- hash_options: Optional[Dict[str, List[str]]] = None,
255
+ hash_options: dict[str, list[str]] | None = None,
230
256
  constraint: bool = False,
231
257
  user_supplied: bool = False,
232
258
  permit_editable_wheels: bool = False,
233
- config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
259
+ config_settings: dict[str, str | list[str]] | None = None,
234
260
  ) -> InstallRequirement:
235
261
  parts = parse_req_from_editable(editable_req)
236
262
 
@@ -242,9 +268,7 @@ def install_req_from_editable(
242
268
  permit_editable_wheels=permit_editable_wheels,
243
269
  link=parts.link,
244
270
  constraint=constraint,
245
- use_pep517=use_pep517,
246
271
  isolated=isolated,
247
- global_options=global_options,
248
272
  hash_options=hash_options,
249
273
  config_settings=config_settings,
250
274
  extras=parts.extras,
@@ -270,7 +294,7 @@ def _looks_like_path(name: str) -> bool:
270
294
  return False
271
295
 
272
296
 
273
- def _get_url_from_path(path: str, name: str) -> Optional[str]:
297
+ def _get_url_from_path(path: str, name: str) -> str | None:
274
298
  """
275
299
  First, it checks whether a provided path is an installable directory. If it
276
300
  is, returns the path.
@@ -304,7 +328,7 @@ def _get_url_from_path(path: str, name: str) -> Optional[str]:
304
328
  return path_to_url(path)
305
329
 
306
330
 
307
- def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementParts:
331
+ def parse_req_from_line(name: str, line_source: str | None) -> RequirementParts:
308
332
  if is_url(name):
309
333
  marker_sep = "; "
310
334
  else:
@@ -376,7 +400,7 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar
376
400
  raise InstallationError(msg)
377
401
 
378
402
  if req_as_string is not None:
379
- req: Optional[Requirement] = _parse_req_string(req_as_string)
403
+ req: Requirement | None = _parse_req_string(req_as_string)
380
404
  else:
381
405
  req = None
382
406
 
@@ -385,16 +409,14 @@ def parse_req_from_line(name: str, line_source: Optional[str]) -> RequirementPar
385
409
 
386
410
  def install_req_from_line(
387
411
  name: str,
388
- comes_from: Optional[Union[str, InstallRequirement]] = None,
412
+ comes_from: str | InstallRequirement | None = None,
389
413
  *,
390
- use_pep517: Optional[bool] = None,
391
414
  isolated: bool = False,
392
- global_options: Optional[List[str]] = None,
393
- hash_options: Optional[Dict[str, List[str]]] = None,
415
+ hash_options: dict[str, list[str]] | None = None,
394
416
  constraint: bool = False,
395
- line_source: Optional[str] = None,
417
+ line_source: str | None = None,
396
418
  user_supplied: bool = False,
397
- config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
419
+ config_settings: dict[str, str | list[str]] | None = None,
398
420
  ) -> InstallRequirement:
399
421
  """Creates an InstallRequirement from a name, which might be a
400
422
  requirement, directory containing 'setup.py', filename, or URL.
@@ -409,9 +431,7 @@ def install_req_from_line(
409
431
  comes_from,
410
432
  link=parts.link,
411
433
  markers=parts.markers,
412
- use_pep517=use_pep517,
413
434
  isolated=isolated,
414
- global_options=global_options,
415
435
  hash_options=hash_options,
416
436
  config_settings=config_settings,
417
437
  constraint=constraint,
@@ -422,9 +442,8 @@ def install_req_from_line(
422
442
 
423
443
  def install_req_from_req_string(
424
444
  req_string: str,
425
- comes_from: Optional[InstallRequirement] = None,
445
+ comes_from: InstallRequirement | None = None,
426
446
  isolated: bool = False,
427
- use_pep517: Optional[bool] = None,
428
447
  user_supplied: bool = False,
429
448
  ) -> InstallRequirement:
430
449
  try:
@@ -453,7 +472,6 @@ def install_req_from_req_string(
453
472
  req,
454
473
  comes_from,
455
474
  isolated=isolated,
456
- use_pep517=use_pep517,
457
475
  user_supplied=user_supplied,
458
476
  )
459
477
 
@@ -461,15 +479,13 @@ def install_req_from_req_string(
461
479
  def install_req_from_parsed_requirement(
462
480
  parsed_req: ParsedRequirement,
463
481
  isolated: bool = False,
464
- use_pep517: Optional[bool] = None,
465
482
  user_supplied: bool = False,
466
- config_settings: Optional[Dict[str, Union[str, List[str]]]] = None,
483
+ config_settings: dict[str, str | list[str]] | None = None,
467
484
  ) -> InstallRequirement:
468
485
  if parsed_req.is_editable:
469
486
  req = install_req_from_editable(
470
487
  parsed_req.requirement,
471
488
  comes_from=parsed_req.comes_from,
472
- use_pep517=use_pep517,
473
489
  constraint=parsed_req.constraint,
474
490
  isolated=isolated,
475
491
  user_supplied=user_supplied,
@@ -480,13 +496,7 @@ def install_req_from_parsed_requirement(
480
496
  req = install_req_from_line(
481
497
  parsed_req.requirement,
482
498
  comes_from=parsed_req.comes_from,
483
- use_pep517=use_pep517,
484
499
  isolated=isolated,
485
- global_options=(
486
- parsed_req.options.get("global_options", [])
487
- if parsed_req.options
488
- else []
489
- ),
490
500
  hash_options=(
491
501
  parsed_req.options.get("hashes", {}) if parsed_req.options else {}
492
502
  ),
@@ -507,9 +517,7 @@ def install_req_from_link_and_ireq(
507
517
  editable=ireq.editable,
508
518
  link=link,
509
519
  markers=ireq.markers,
510
- use_pep517=ireq.use_pep517,
511
520
  isolated=ireq.isolated,
512
- global_options=ireq.global_options,
513
521
  hash_options=ireq.hash_options,
514
522
  config_settings=ireq.config_settings,
515
523
  user_supplied=ireq.user_supplied,
@@ -530,9 +538,7 @@ def install_req_drop_extras(ireq: InstallRequirement) -> InstallRequirement:
530
538
  editable=ireq.editable,
531
539
  link=ireq.link,
532
540
  markers=ireq.markers,
533
- use_pep517=ireq.use_pep517,
534
541
  isolated=ireq.isolated,
535
- global_options=ireq.global_options,
536
542
  hash_options=ireq.hash_options,
537
543
  constraint=ireq.constraint,
538
544
  extras=[],
@@ -1,17 +1,13 @@
1
- import sys
2
- from typing import Any, Dict, Iterable, Iterator, List, Tuple
3
-
4
- if sys.version_info >= (3, 11):
5
- import tomllib
6
- else:
7
- from pip._vendor import tomli as tomllib
1
+ from collections.abc import Iterable, Iterator
2
+ from typing import Any
8
3
 
9
4
  from pip._vendor.dependency_groups import DependencyGroupResolver
10
5
 
11
6
  from pip._internal.exceptions import InstallationError
7
+ from pip._internal.utils.compat import tomllib
12
8
 
13
9
 
14
- def parse_dependency_groups(groups: List[Tuple[str, str]]) -> List[str]:
10
+ def parse_dependency_groups(groups: list[tuple[str, str]]) -> list[str]:
15
11
  """
16
12
  Parse dependency groups data as provided via the CLI, in a `[path:]group` syntax.
17
13
 
@@ -22,7 +18,7 @@ def parse_dependency_groups(groups: List[Tuple[str, str]]) -> List[str]:
22
18
 
23
19
 
24
20
  def _resolve_all_groups(
25
- resolvers: Dict[str, DependencyGroupResolver], groups: List[Tuple[str, str]]
21
+ resolvers: dict[str, DependencyGroupResolver], groups: list[tuple[str, str]]
26
22
  ) -> Iterator[str]:
27
23
  """
28
24
  Run all resolution, converting any error from `DependencyGroupResolver` into
@@ -39,7 +35,7 @@ def _resolve_all_groups(
39
35
  ) from e
40
36
 
41
37
 
42
- def _build_resolvers(paths: Iterable[str]) -> Dict[str, Any]:
38
+ def _build_resolvers(paths: Iterable[str]) -> dict[str, Any]:
43
39
  resolvers = {}
44
40
  for path in paths:
45
41
  if path in resolvers:
@@ -62,7 +58,7 @@ def _build_resolvers(paths: Iterable[str]) -> Dict[str, Any]:
62
58
  return resolvers
63
59
 
64
60
 
65
- def _load_pyproject(path: str) -> Dict[str, Any]:
61
+ def _load_pyproject(path: str) -> dict[str, Any]:
66
62
  """
67
63
  This helper loads a pyproject.toml as TOML.
68
64
 
@@ -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,12 @@ 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]] = [
71
- cmdoptions.global_options,
67
+ SUPPORTED_OPTIONS_REQ: list[Callable[..., optparse.Option]] = [
72
68
  cmdoptions.hash,
73
69
  cmdoptions.config_settings,
74
70
  ]
75
71
 
76
- SUPPORTED_OPTIONS_EDITABLE_REQ: List[Callable[..., optparse.Option]] = [
72
+ SUPPORTED_OPTIONS_EDITABLE_REQ: list[Callable[..., optparse.Option]] = [
77
73
  cmdoptions.config_settings,
78
74
  ]
79
75
 
@@ -86,7 +82,7 @@ SUPPORTED_OPTIONS_EDITABLE_REQ_DEST = [
86
82
 
87
83
  # order of BOMS is important: codecs.BOM_UTF16_LE is a prefix of codecs.BOM_UTF32_LE
88
84
  # so data.startswith(BOM_UTF16_LE) would be true for UTF32_LE data
89
- BOMS: List[Tuple[bytes, str]] = [
85
+ BOMS: list[tuple[bytes, str]] = [
90
86
  (codecs.BOM_UTF8, "utf-8"),
91
87
  (codecs.BOM_UTF32, "utf-32"),
92
88
  (codecs.BOM_UTF32_BE, "utf-32-be"),
@@ -118,8 +114,8 @@ class ParsedRequirement:
118
114
  is_editable: bool
119
115
  comes_from: str
120
116
  constraint: bool
121
- options: Optional[Dict[str, Any]]
122
- line_source: Optional[str]
117
+ options: dict[str, Any] | None
118
+ line_source: str | None
123
119
 
124
120
 
125
121
  @dataclass(frozen=True)
@@ -137,7 +133,7 @@ class ParsedLine:
137
133
  return bool(self.opts.editables)
138
134
 
139
135
  @property
140
- def requirement(self) -> Optional[str]:
136
+ def requirement(self) -> str | None:
141
137
  if self.args:
142
138
  return self.args
143
139
  elif self.is_editable:
@@ -148,9 +144,9 @@ class ParsedLine:
148
144
 
149
145
  def parse_requirements(
150
146
  filename: str,
151
- session: "PipSession",
152
- finder: Optional["PackageFinder"] = None,
153
- options: Optional[optparse.Values] = None,
147
+ session: PipSession,
148
+ finder: PackageFinder | None = None,
149
+ options: optparse.Values | None = None,
154
150
  constraint: bool = False,
155
151
  ) -> Generator[ParsedRequirement, None, None]:
156
152
  """Parse a requirements file and yield ParsedRequirement instances.
@@ -187,7 +183,7 @@ def preprocess(content: str) -> ReqFileLines:
187
183
 
188
184
  def handle_requirement_line(
189
185
  line: ParsedLine,
190
- options: Optional[optparse.Values] = None,
186
+ options: optparse.Values | None = None,
191
187
  ) -> ParsedRequirement:
192
188
  # preserve for the nested code path
193
189
  line_comes_from = "{} {} (line {})".format(
@@ -223,9 +219,9 @@ def handle_option_line(
223
219
  opts: Values,
224
220
  filename: str,
225
221
  lineno: int,
226
- finder: Optional["PackageFinder"] = None,
227
- options: Optional[optparse.Values] = None,
228
- session: Optional["PipSession"] = None,
222
+ finder: PackageFinder | None = None,
223
+ options: optparse.Values | None = None,
224
+ session: PipSession | None = None,
229
225
  ) -> None:
230
226
  if opts.hashes:
231
227
  logger.warning(
@@ -291,10 +287,10 @@ def handle_option_line(
291
287
 
292
288
  def handle_line(
293
289
  line: ParsedLine,
294
- options: Optional[optparse.Values] = None,
295
- finder: Optional["PackageFinder"] = None,
296
- session: Optional["PipSession"] = None,
297
- ) -> Optional[ParsedRequirement]:
290
+ options: optparse.Values | None = None,
291
+ finder: PackageFinder | None = None,
292
+ session: PipSession | None = None,
293
+ ) -> ParsedRequirement | None:
298
294
  """Handle a single parsed requirements line; This can result in
299
295
  creating/yielding requirements, or updating the finder.
300
296
 
@@ -336,7 +332,7 @@ def handle_line(
336
332
  class RequirementsFileParser:
337
333
  def __init__(
338
334
  self,
339
- session: "PipSession",
335
+ session: PipSession,
340
336
  line_parser: LineParser,
341
337
  ) -> None:
342
338
  self._session = session
@@ -354,7 +350,7 @@ class RequirementsFileParser:
354
350
  self,
355
351
  filename: str,
356
352
  constraint: bool,
357
- parsed_files_stack: List[Dict[str, Optional[str]]],
353
+ parsed_files_stack: list[dict[str, str | None]],
358
354
  ) -> Generator[ParsedLine, None, None]:
359
355
  for line in self._parse_file(filename, constraint):
360
356
  if line.requirement is None and (
@@ -426,8 +422,8 @@ class RequirementsFileParser:
426
422
  )
427
423
 
428
424
 
429
- def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser:
430
- def parse_line(line: str) -> Tuple[str, Values]:
425
+ def get_line_parser(finder: PackageFinder | None) -> LineParser:
426
+ def parse_line(line: str) -> tuple[str, Values]:
431
427
  # Build new parser for each line since it accumulates appendable
432
428
  # options.
433
429
  parser = build_parser()
@@ -450,7 +446,7 @@ def get_line_parser(finder: Optional["PackageFinder"]) -> LineParser:
450
446
  return parse_line
451
447
 
452
448
 
453
- def break_args_options(line: str) -> Tuple[str, str]:
449
+ def break_args_options(line: str) -> tuple[str, str]:
454
450
  """Break up the line into an args and options string. We only want to shlex
455
451
  (and then optparse) the options, not the args. args can contain markers
456
452
  which are corrupted by shlex.
@@ -459,7 +455,7 @@ def break_args_options(line: str) -> Tuple[str, str]:
459
455
  args = []
460
456
  options = tokens[:]
461
457
  for token in tokens:
462
- if token.startswith("-") or token.startswith("--"):
458
+ if token.startswith(("-", "--")):
463
459
  break
464
460
  else:
465
461
  args.append(token)
@@ -485,7 +481,7 @@ def build_parser() -> optparse.OptionParser:
485
481
 
486
482
  # By default optparse sys.exits on parsing errors. We want to wrap
487
483
  # that in our own exception.
488
- def parser_exit(self: Any, msg: str) -> "NoReturn":
484
+ def parser_exit(self: Any, msg: str) -> NoReturn:
489
485
  raise OptionParsingError(msg)
490
486
 
491
487
  # NOTE: mypy disallows assigning to a method
@@ -500,7 +496,7 @@ def join_lines(lines_enum: ReqFileLines) -> ReqFileLines:
500
496
  comments). The joined line takes on the index of the first line.
501
497
  """
502
498
  primary_line_number = None
503
- new_line: List[str] = []
499
+ new_line: list[str] = []
504
500
  for line_number, line in lines_enum:
505
501
  if not line.endswith("\\") or COMMENT_RE.match(line):
506
502
  if COMMENT_RE.match(line):
@@ -564,7 +560,7 @@ def expand_env_variables(lines_enum: ReqFileLines) -> ReqFileLines:
564
560
  yield line_number, line
565
561
 
566
562
 
567
- def get_file_content(url: str, session: "PipSession") -> Tuple[str, str]:
563
+ def get_file_content(url: str, session: PipSession) -> tuple[str, str]:
568
564
  """Gets the content of a file; it may be a filename, file: URL, or
569
565
  http: URL. Returns (location, content). Content is unicode.
570
566
  Respects # -*- coding: declarations on the retrieved files.