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
@@ -2,6 +2,8 @@
2
2
  The main purpose of this module is to expose LinkCollector.collect_sources().
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  import collections
6
8
  import email.message
7
9
  import functools
@@ -11,21 +13,14 @@ import logging
11
13
  import os
12
14
  import urllib.parse
13
15
  import urllib.request
16
+ from collections.abc import Iterable, MutableMapping, Sequence
14
17
  from dataclasses import dataclass
15
18
  from html.parser import HTMLParser
16
19
  from optparse import Values
17
20
  from typing import (
18
21
  Callable,
19
- Dict,
20
- Iterable,
21
- List,
22
- MutableMapping,
23
22
  NamedTuple,
24
- Optional,
25
23
  Protocol,
26
- Sequence,
27
- Tuple,
28
- Union,
29
24
  )
30
25
 
31
26
  from pip._vendor import requests
@@ -48,7 +43,7 @@ logger = logging.getLogger(__name__)
48
43
  ResponseHeaders = MutableMapping[str, str]
49
44
 
50
45
 
51
- def _match_vcs_scheme(url: str) -> Optional[str]:
46
+ def _match_vcs_scheme(url: str) -> str | None:
52
47
  """Look for VCS schemes in the URL.
53
48
 
54
49
  Returns the matched VCS scheme, or None if there's no match.
@@ -173,7 +168,7 @@ def _get_simple_response(url: str, session: PipSession) -> Response:
173
168
  return resp
174
169
 
175
170
 
176
- def _get_encoding_from_headers(headers: ResponseHeaders) -> Optional[str]:
171
+ def _get_encoding_from_headers(headers: ResponseHeaders) -> str | None:
177
172
  """Determine if we have any encoding information in our headers."""
178
173
  if headers and "Content-Type" in headers:
179
174
  m = email.message.Message()
@@ -185,7 +180,7 @@ def _get_encoding_from_headers(headers: ResponseHeaders) -> Optional[str]:
185
180
 
186
181
 
187
182
  class CacheablePageContent:
188
- def __init__(self, page: "IndexContent") -> None:
183
+ def __init__(self, page: IndexContent) -> None:
189
184
  assert page.cache_link_parsing
190
185
  self.page = page
191
186
 
@@ -197,7 +192,7 @@ class CacheablePageContent:
197
192
 
198
193
 
199
194
  class ParseLinks(Protocol):
200
- def __call__(self, page: "IndexContent") -> Iterable[Link]: ...
195
+ def __call__(self, page: IndexContent) -> Iterable[Link]: ...
201
196
 
202
197
 
203
198
  def with_cached_index_content(fn: ParseLinks) -> ParseLinks:
@@ -207,12 +202,12 @@ def with_cached_index_content(fn: ParseLinks) -> ParseLinks:
207
202
  `page` has `page.cache_link_parsing == False`.
208
203
  """
209
204
 
210
- @functools.lru_cache(maxsize=None)
211
- def wrapper(cacheable_page: CacheablePageContent) -> List[Link]:
205
+ @functools.cache
206
+ def wrapper(cacheable_page: CacheablePageContent) -> list[Link]:
212
207
  return list(fn(cacheable_page.page))
213
208
 
214
209
  @functools.wraps(fn)
215
- def wrapper_wrapper(page: "IndexContent") -> List[Link]:
210
+ def wrapper_wrapper(page: IndexContent) -> list[Link]:
216
211
  if page.cache_link_parsing:
217
212
  return wrapper(CacheablePageContent(page))
218
213
  return list(fn(page))
@@ -221,7 +216,7 @@ def with_cached_index_content(fn: ParseLinks) -> ParseLinks:
221
216
 
222
217
 
223
218
  @with_cached_index_content
224
- def parse_links(page: "IndexContent") -> Iterable[Link]:
219
+ def parse_links(page: IndexContent) -> Iterable[Link]:
225
220
  """
226
221
  Parse a Simple API's Index Content, and yield its anchor elements as Link objects.
227
222
  """
@@ -262,7 +257,7 @@ class IndexContent:
262
257
 
263
258
  content: bytes
264
259
  content_type: str
265
- encoding: Optional[str]
260
+ encoding: str | None
266
261
  url: str
267
262
  cache_link_parsing: bool = True
268
263
 
@@ -280,10 +275,10 @@ class HTMLLinkParser(HTMLParser):
280
275
  super().__init__(convert_charrefs=True)
281
276
 
282
277
  self.url: str = url
283
- self.base_url: Optional[str] = None
284
- self.anchors: List[Dict[str, Optional[str]]] = []
278
+ self.base_url: str | None = None
279
+ self.anchors: list[dict[str, str | None]] = []
285
280
 
286
- def handle_starttag(self, tag: str, attrs: List[Tuple[str, Optional[str]]]) -> None:
281
+ def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
287
282
  if tag == "base" and self.base_url is None:
288
283
  href = self.get_href(attrs)
289
284
  if href is not None:
@@ -291,7 +286,7 @@ class HTMLLinkParser(HTMLParser):
291
286
  elif tag == "a":
292
287
  self.anchors.append(dict(attrs))
293
288
 
294
- def get_href(self, attrs: List[Tuple[str, Optional[str]]]) -> Optional[str]:
289
+ def get_href(self, attrs: list[tuple[str, str | None]]) -> str | None:
295
290
  for name, value in attrs:
296
291
  if name == "href":
297
292
  return value
@@ -300,8 +295,8 @@ class HTMLLinkParser(HTMLParser):
300
295
 
301
296
  def _handle_get_simple_fail(
302
297
  link: Link,
303
- reason: Union[str, Exception],
304
- meth: Optional[Callable[..., None]] = None,
298
+ reason: str | Exception,
299
+ meth: Callable[..., None] | None = None,
305
300
  ) -> None:
306
301
  if meth is None:
307
302
  meth = logger.debug
@@ -321,7 +316,7 @@ def _make_index_content(
321
316
  )
322
317
 
323
318
 
324
- def _get_index_content(link: Link, *, session: PipSession) -> Optional["IndexContent"]:
319
+ def _get_index_content(link: Link, *, session: PipSession) -> IndexContent | None:
325
320
  url = link.url.split("#", 1)[0]
326
321
 
327
322
  # Check for VCS schemes that do not support lookup as web pages.
@@ -383,8 +378,8 @@ def _get_index_content(link: Link, *, session: PipSession) -> Optional["IndexCon
383
378
 
384
379
 
385
380
  class CollectedSources(NamedTuple):
386
- find_links: Sequence[Optional[LinkSource]]
387
- index_urls: Sequence[Optional[LinkSource]]
381
+ find_links: Sequence[LinkSource | None]
382
+ index_urls: Sequence[LinkSource | None]
388
383
 
389
384
 
390
385
  class LinkCollector:
@@ -409,7 +404,7 @@ class LinkCollector:
409
404
  session: PipSession,
410
405
  options: Values,
411
406
  suppress_no_index: bool = False,
412
- ) -> "LinkCollector":
407
+ ) -> LinkCollector:
413
408
  """
414
409
  :param session: The Session to use to make requests.
415
410
  :param suppress_no_index: Whether to ignore the --no-index option
@@ -438,10 +433,10 @@ class LinkCollector:
438
433
  return link_collector
439
434
 
440
435
  @property
441
- def find_links(self) -> List[str]:
436
+ def find_links(self) -> list[str]:
442
437
  return self.search_scope.find_links
443
438
 
444
- def fetch_response(self, location: Link) -> Optional[IndexContent]:
439
+ def fetch_response(self, location: Link) -> IndexContent | None:
445
440
  """
446
441
  Fetch an HTML page containing package links.
447
442
  """
@@ -1,26 +1,23 @@
1
1
  """Routines related to PyPI, indexes"""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import enum
4
6
  import functools
5
7
  import itertools
6
8
  import logging
7
9
  import re
10
+ from collections.abc import Iterable
8
11
  from dataclasses import dataclass
9
12
  from typing import (
10
13
  TYPE_CHECKING,
11
- Dict,
12
- FrozenSet,
13
- Iterable,
14
- List,
15
14
  Optional,
16
- Set,
17
- Tuple,
18
15
  Union,
19
16
  )
20
17
 
21
18
  from pip._vendor.packaging import specifiers
22
19
  from pip._vendor.packaging.tags import Tag
23
- from pip._vendor.packaging.utils import canonicalize_name
20
+ from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
24
21
  from pip._vendor.packaging.version import InvalidVersion, _BaseVersion
25
22
  from pip._vendor.packaging.version import parse as parse_version
26
23
 
@@ -48,20 +45,20 @@ from pip._internal.utils.packaging import check_requires_python
48
45
  from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS
49
46
 
50
47
  if TYPE_CHECKING:
51
- from pip._vendor.typing_extensions import TypeGuard
48
+ from typing_extensions import TypeGuard
52
49
 
53
50
  __all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"]
54
51
 
55
52
 
56
53
  logger = getLogger(__name__)
57
54
 
58
- BuildTag = Union[Tuple[()], Tuple[int, str]]
59
- CandidateSortingKey = Tuple[int, int, int, _BaseVersion, Optional[int], BuildTag]
55
+ BuildTag = Union[tuple[()], tuple[int, str]]
56
+ CandidateSortingKey = tuple[int, int, int, _BaseVersion, Optional[int], BuildTag]
60
57
 
61
58
 
62
59
  def _check_link_requires_python(
63
60
  link: Link,
64
- version_info: Tuple[int, int, int],
61
+ version_info: tuple[int, int, int],
65
62
  ignore_requires_python: bool = False,
66
63
  ) -> bool:
67
64
  """
@@ -130,11 +127,11 @@ class LinkEvaluator:
130
127
  def __init__(
131
128
  self,
132
129
  project_name: str,
133
- canonical_name: str,
134
- formats: FrozenSet[str],
130
+ canonical_name: NormalizedName,
131
+ formats: frozenset[str],
135
132
  target_python: TargetPython,
136
133
  allow_yanked: bool,
137
- ignore_requires_python: Optional[bool] = None,
134
+ ignore_requires_python: bool | None = None,
138
135
  ) -> None:
139
136
  """
140
137
  :param project_name: The user supplied package name.
@@ -164,7 +161,7 @@ class LinkEvaluator:
164
161
 
165
162
  self.project_name = project_name
166
163
 
167
- def evaluate_link(self, link: Link) -> Tuple[LinkType, str]:
164
+ def evaluate_link(self, link: Link) -> tuple[LinkType, str]:
168
165
  """
169
166
  Determine whether a link is a candidate for installation.
170
167
 
@@ -204,7 +201,7 @@ class LinkEvaluator:
204
201
  LinkType.format_invalid,
205
202
  "invalid wheel filename",
206
203
  )
207
- if canonicalize_name(wheel.name) != self._canonical_name:
204
+ if wheel.name != self._canonical_name:
208
205
  reason = f"wrong project name (not {self.project_name})"
209
206
  return (LinkType.different_project, reason)
210
207
 
@@ -251,7 +248,19 @@ class LinkEvaluator:
251
248
  ignore_requires_python=self._ignore_requires_python,
252
249
  )
253
250
  if not supports_python:
254
- reason = f"{version} Requires-Python {link.requires_python}"
251
+ requires_python = link.requires_python
252
+ if requires_python:
253
+
254
+ def get_version_sort_key(v: str) -> tuple[int, ...]:
255
+ return tuple(int(s) for s in v.split(".") if s.isdigit())
256
+
257
+ requires_python = ",".join(
258
+ sorted(
259
+ (str(s) for s in specifiers.SpecifierSet(requires_python)),
260
+ key=get_version_sort_key,
261
+ )
262
+ )
263
+ reason = f"{version} Requires-Python {requires_python}"
255
264
  return (LinkType.requires_python_mismatch, reason)
256
265
 
257
266
  logger.debug("Found link %s, version: %s", link, version)
@@ -260,10 +269,10 @@ class LinkEvaluator:
260
269
 
261
270
 
262
271
  def filter_unallowed_hashes(
263
- candidates: List[InstallationCandidate],
264
- hashes: Optional[Hashes],
272
+ candidates: list[InstallationCandidate],
273
+ hashes: Hashes | None,
265
274
  project_name: str,
266
- ) -> List[InstallationCandidate]:
275
+ ) -> list[InstallationCandidate]:
267
276
  """
268
277
  Filter out candidates whose hashes aren't allowed, and return a new
269
278
  list of candidates.
@@ -357,9 +366,9 @@ class BestCandidateResult:
357
366
  if no applicable candidates were found.
358
367
  """
359
368
 
360
- all_candidates: List[InstallationCandidate]
361
- applicable_candidates: List[InstallationCandidate]
362
- best_candidate: Optional[InstallationCandidate]
369
+ all_candidates: list[InstallationCandidate]
370
+ applicable_candidates: list[InstallationCandidate]
371
+ best_candidate: InstallationCandidate | None
363
372
 
364
373
  def __post_init__(self) -> None:
365
374
  assert set(self.applicable_candidates) <= set(self.all_candidates)
@@ -380,12 +389,12 @@ class CandidateEvaluator:
380
389
  def create(
381
390
  cls,
382
391
  project_name: str,
383
- target_python: Optional[TargetPython] = None,
392
+ target_python: TargetPython | None = None,
384
393
  prefer_binary: bool = False,
385
394
  allow_all_prereleases: bool = False,
386
- specifier: Optional[specifiers.BaseSpecifier] = None,
387
- hashes: Optional[Hashes] = None,
388
- ) -> "CandidateEvaluator":
395
+ specifier: specifiers.BaseSpecifier | None = None,
396
+ hashes: Hashes | None = None,
397
+ ) -> CandidateEvaluator:
389
398
  """Create a CandidateEvaluator object.
390
399
 
391
400
  :param target_python: The target Python interpreter to use when
@@ -415,11 +424,11 @@ class CandidateEvaluator:
415
424
  def __init__(
416
425
  self,
417
426
  project_name: str,
418
- supported_tags: List[Tag],
427
+ supported_tags: list[Tag],
419
428
  specifier: specifiers.BaseSpecifier,
420
429
  prefer_binary: bool = False,
421
430
  allow_all_prereleases: bool = False,
422
- hashes: Optional[Hashes] = None,
431
+ hashes: Hashes | None = None,
423
432
  ) -> None:
424
433
  """
425
434
  :param supported_tags: The PEP 425 tags supported by the target
@@ -440,8 +449,8 @@ class CandidateEvaluator:
440
449
 
441
450
  def get_applicable_candidates(
442
451
  self,
443
- candidates: List[InstallationCandidate],
444
- ) -> List[InstallationCandidate]:
452
+ candidates: list[InstallationCandidate],
453
+ ) -> list[InstallationCandidate]:
445
454
  """
446
455
  Return the applicable candidates from a list of candidates.
447
456
  """
@@ -540,8 +549,8 @@ class CandidateEvaluator:
540
549
 
541
550
  def sort_best_candidate(
542
551
  self,
543
- candidates: List[InstallationCandidate],
544
- ) -> Optional[InstallationCandidate]:
552
+ candidates: list[InstallationCandidate],
553
+ ) -> InstallationCandidate | None:
545
554
  """
546
555
  Return the best candidate per the instance's sort order, or None if
547
556
  no candidate is acceptable.
@@ -553,7 +562,7 @@ class CandidateEvaluator:
553
562
 
554
563
  def compute_best_candidate(
555
564
  self,
556
- candidates: List[InstallationCandidate],
565
+ candidates: list[InstallationCandidate],
557
566
  ) -> BestCandidateResult:
558
567
  """
559
568
  Compute and return a `BestCandidateResult` instance.
@@ -581,9 +590,9 @@ class PackageFinder:
581
590
  link_collector: LinkCollector,
582
591
  target_python: TargetPython,
583
592
  allow_yanked: bool,
584
- format_control: Optional[FormatControl] = None,
585
- candidate_prefs: Optional[CandidatePreferences] = None,
586
- ignore_requires_python: Optional[bool] = None,
593
+ format_control: FormatControl | None = None,
594
+ candidate_prefs: CandidatePreferences | None = None,
595
+ ignore_requires_python: bool | None = None,
587
596
  ) -> None:
588
597
  """
589
598
  This constructor is primarily meant to be used by the create() class
@@ -609,12 +618,12 @@ class PackageFinder:
609
618
  self.format_control = format_control
610
619
 
611
620
  # These are boring links that have already been logged somehow.
612
- self._logged_links: Set[Tuple[Link, LinkType, str]] = set()
621
+ self._logged_links: set[tuple[Link, LinkType, str]] = set()
613
622
 
614
623
  # Cache of the result of finding candidates
615
- self._all_candidates: Dict[str, List[InstallationCandidate]] = {}
616
- self._best_candidates: Dict[
617
- Tuple[str, Optional[specifiers.BaseSpecifier], Optional[Hashes]],
624
+ self._all_candidates: dict[str, list[InstallationCandidate]] = {}
625
+ self._best_candidates: dict[
626
+ tuple[str, specifiers.BaseSpecifier | None, Hashes | None],
618
627
  BestCandidateResult,
619
628
  ] = {}
620
629
 
@@ -627,8 +636,8 @@ class PackageFinder:
627
636
  cls,
628
637
  link_collector: LinkCollector,
629
638
  selection_prefs: SelectionPreferences,
630
- target_python: Optional[TargetPython] = None,
631
- ) -> "PackageFinder":
639
+ target_python: TargetPython | None = None,
640
+ ) -> PackageFinder:
632
641
  """Create a PackageFinder.
633
642
 
634
643
  :param selection_prefs: The candidate selection preferences, as a
@@ -667,15 +676,15 @@ class PackageFinder:
667
676
  self._link_collector.search_scope = search_scope
668
677
 
669
678
  @property
670
- def find_links(self) -> List[str]:
679
+ def find_links(self) -> list[str]:
671
680
  return self._link_collector.find_links
672
681
 
673
682
  @property
674
- def index_urls(self) -> List[str]:
683
+ def index_urls(self) -> list[str]:
675
684
  return self.search_scope.index_urls
676
685
 
677
686
  @property
678
- def proxy(self) -> Optional[str]:
687
+ def proxy(self) -> str | None:
679
688
  return self._link_collector.session.pip_proxy
680
689
 
681
690
  @property
@@ -684,7 +693,7 @@ class PackageFinder:
684
693
  yield build_netloc(*host_port)
685
694
 
686
695
  @property
687
- def custom_cert(self) -> Optional[str]:
696
+ def custom_cert(self) -> str | None:
688
697
  # session.verify is either a boolean (use default bundle/no SSL
689
698
  # verification) or a string path to a custom CA bundle to use. We only
690
699
  # care about the latter.
@@ -692,7 +701,7 @@ class PackageFinder:
692
701
  return verify if isinstance(verify, str) else None
693
702
 
694
703
  @property
695
- def client_cert(self) -> Optional[str]:
704
+ def client_cert(self) -> str | None:
696
705
  cert = self._link_collector.session.cert
697
706
  assert not isinstance(cert, tuple), "pip only supports PEM client certs"
698
707
  return cert
@@ -711,7 +720,7 @@ class PackageFinder:
711
720
  def set_prefer_binary(self) -> None:
712
721
  self._candidate_prefs.prefer_binary = True
713
722
 
714
- def requires_python_skipped_reasons(self) -> List[str]:
723
+ def requires_python_skipped_reasons(self) -> list[str]:
715
724
  reasons = {
716
725
  detail
717
726
  for _, result, detail in self._logged_links
@@ -732,13 +741,13 @@ class PackageFinder:
732
741
  ignore_requires_python=self._ignore_requires_python,
733
742
  )
734
743
 
735
- def _sort_links(self, links: Iterable[Link]) -> List[Link]:
744
+ def _sort_links(self, links: Iterable[Link]) -> list[Link]:
736
745
  """
737
746
  Returns elements of links in order, non-egg links first, egg links
738
747
  second, while eliminating duplicates
739
748
  """
740
749
  eggs, no_eggs = [], []
741
- seen: Set[Link] = set()
750
+ seen: set[Link] = set()
742
751
  for link in links:
743
752
  if link not in seen:
744
753
  seen.add(link)
@@ -758,7 +767,7 @@ class PackageFinder:
758
767
 
759
768
  def get_install_candidate(
760
769
  self, link_evaluator: LinkEvaluator, link: Link
761
- ) -> Optional[InstallationCandidate]:
770
+ ) -> InstallationCandidate | None:
762
771
  """
763
772
  If the link is a candidate for install, convert it to an
764
773
  InstallationCandidate and return it. Otherwise, return None.
@@ -779,7 +788,7 @@ class PackageFinder:
779
788
 
780
789
  def evaluate_links(
781
790
  self, link_evaluator: LinkEvaluator, links: Iterable[Link]
782
- ) -> List[InstallationCandidate]:
791
+ ) -> list[InstallationCandidate]:
783
792
  """
784
793
  Convert links that are candidates to InstallationCandidate objects.
785
794
  """
@@ -793,7 +802,7 @@ class PackageFinder:
793
802
 
794
803
  def process_project_url(
795
804
  self, project_url: Link, link_evaluator: LinkEvaluator
796
- ) -> List[InstallationCandidate]:
805
+ ) -> list[InstallationCandidate]:
797
806
  logger.debug(
798
807
  "Fetching project page and analyzing links: %s",
799
808
  project_url,
@@ -812,7 +821,7 @@ class PackageFinder:
812
821
 
813
822
  return package_links
814
823
 
815
- def find_all_candidates(self, project_name: str) -> List[InstallationCandidate]:
824
+ def find_all_candidates(self, project_name: str) -> list[InstallationCandidate]:
816
825
  """Find all available InstallationCandidate for project_name
817
826
 
818
827
  This checks index_urls and find_links.
@@ -872,8 +881,8 @@ class PackageFinder:
872
881
  def make_candidate_evaluator(
873
882
  self,
874
883
  project_name: str,
875
- specifier: Optional[specifiers.BaseSpecifier] = None,
876
- hashes: Optional[Hashes] = None,
884
+ specifier: specifiers.BaseSpecifier | None = None,
885
+ hashes: Hashes | None = None,
877
886
  ) -> CandidateEvaluator:
878
887
  """Create a CandidateEvaluator object to use."""
879
888
  candidate_prefs = self._candidate_prefs
@@ -889,8 +898,8 @@ class PackageFinder:
889
898
  def find_best_candidate(
890
899
  self,
891
900
  project_name: str,
892
- specifier: Optional[specifiers.BaseSpecifier] = None,
893
- hashes: Optional[Hashes] = None,
901
+ specifier: specifiers.BaseSpecifier | None = None,
902
+ hashes: Hashes | None = None,
894
903
  ) -> BestCandidateResult:
895
904
  """Find matches for the given project and specifier.
896
905
 
@@ -917,7 +926,7 @@ class PackageFinder:
917
926
 
918
927
  def find_requirement(
919
928
  self, req: InstallRequirement, upgrade: bool
920
- ) -> Optional[InstallationCandidate]:
929
+ ) -> InstallationCandidate | None:
921
930
  """Try to find a Link matching req
922
931
 
923
932
  Expects req, an InstallRequirement and upgrade, a boolean
@@ -935,7 +944,7 @@ class PackageFinder:
935
944
  )
936
945
  best_candidate = best_candidate_result.best_candidate
937
946
 
938
- installed_version: Optional[_BaseVersion] = None
947
+ installed_version: _BaseVersion | None = None
939
948
  if req.satisfied_by is not None:
940
949
  installed_version = req.satisfied_by.version
941
950
 
@@ -965,8 +974,8 @@ class PackageFinder:
965
974
  raise DistributionNotFound(f"No matching distribution found for {req}")
966
975
 
967
976
  def _should_install_candidate(
968
- candidate: Optional[InstallationCandidate],
969
- ) -> "TypeGuard[InstallationCandidate]":
977
+ candidate: InstallationCandidate | None,
978
+ ) -> TypeGuard[InstallationCandidate]:
970
979
  if installed_version is None:
971
980
  return True
972
981
  if best_candidate is None:
@@ -1032,7 +1041,7 @@ def _find_name_version_sep(fragment: str, canonical_name: str) -> int:
1032
1041
  raise ValueError(f"{fragment} does not match {canonical_name}")
1033
1042
 
1034
1043
 
1035
- def _extract_version_from_fragment(fragment: str, canonical_name: str) -> Optional[str]:
1044
+ def _extract_version_from_fragment(fragment: str, canonical_name: str) -> str | None:
1036
1045
  """Parse the version string from a <package>+<version> filename
1037
1046
  "fragment" (stem) or egg fragment.
1038
1047
 
@@ -1,8 +1,11 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import mimetypes
3
5
  import os
4
6
  from collections import defaultdict
5
- from typing import Callable, Dict, Iterable, List, Optional, Tuple
7
+ from collections.abc import Iterable
8
+ from typing import Callable
6
9
 
7
10
  from pip._vendor.packaging.utils import (
8
11
  InvalidSdistFilename,
@@ -27,7 +30,7 @@ PageValidator = Callable[[Link], bool]
27
30
 
28
31
  class LinkSource:
29
32
  @property
30
- def link(self) -> Optional[Link]:
33
+ def link(self) -> Link | None:
31
34
  """Returns the underlying link, if there's one."""
32
35
  raise NotImplementedError()
33
36
 
@@ -49,8 +52,8 @@ class _FlatDirectoryToUrls:
49
52
 
50
53
  def __init__(self, path: str) -> None:
51
54
  self._path = path
52
- self._page_candidates: List[str] = []
53
- self._project_name_to_urls: Dict[str, List[str]] = defaultdict(list)
55
+ self._page_candidates: list[str] = []
56
+ self._project_name_to_urls: dict[str, list[str]] = defaultdict(list)
54
57
  self._scanned_directory = False
55
58
 
56
59
  def _scan_directory(self) -> None:
@@ -77,14 +80,14 @@ class _FlatDirectoryToUrls:
77
80
  self._scanned_directory = True
78
81
 
79
82
  @property
80
- def page_candidates(self) -> List[str]:
83
+ def page_candidates(self) -> list[str]:
81
84
  if not self._scanned_directory:
82
85
  self._scan_directory()
83
86
 
84
87
  return self._page_candidates
85
88
 
86
89
  @property
87
- def project_name_to_urls(self) -> Dict[str, List[str]]:
90
+ def project_name_to_urls(self) -> dict[str, list[str]]:
88
91
  if not self._scanned_directory:
89
92
  self._scan_directory()
90
93
 
@@ -100,7 +103,7 @@ class _FlatDirectorySource(LinkSource):
100
103
  * ``file_candidates``: Archives in the directory.
101
104
  """
102
105
 
103
- _paths_to_urls: Dict[str, _FlatDirectoryToUrls] = {}
106
+ _paths_to_urls: dict[str, _FlatDirectoryToUrls] = {}
104
107
 
105
108
  def __init__(
106
109
  self,
@@ -119,7 +122,7 @@ class _FlatDirectorySource(LinkSource):
119
122
  self._paths_to_urls[path] = self._path_to_urls
120
123
 
121
124
  @property
122
- def link(self) -> Optional[Link]:
125
+ def link(self) -> Link | None:
123
126
  return None
124
127
 
125
128
  def page_candidates(self) -> FoundCandidates:
@@ -150,7 +153,7 @@ class _LocalFileSource(LinkSource):
150
153
  self._link = link
151
154
 
152
155
  @property
153
- def link(self) -> Optional[Link]:
156
+ def link(self) -> Link | None:
154
157
  return self._link
155
158
 
156
159
  def page_candidates(self) -> FoundCandidates:
@@ -184,7 +187,7 @@ class _RemoteFileSource(LinkSource):
184
187
  self._link = link
185
188
 
186
189
  @property
187
- def link(self) -> Optional[Link]:
190
+ def link(self) -> Link | None:
188
191
  return self._link
189
192
 
190
193
  def page_candidates(self) -> FoundCandidates:
@@ -212,7 +215,7 @@ class _IndexDirectorySource(LinkSource):
212
215
  self._link = link
213
216
 
214
217
  @property
215
- def link(self) -> Optional[Link]:
218
+ def link(self) -> Link | None:
216
219
  return self._link
217
220
 
218
221
  def page_candidates(self) -> FoundCandidates:
@@ -230,9 +233,9 @@ def build_source(
230
233
  expand_dir: bool,
231
234
  cache_link_parsing: bool,
232
235
  project_name: str,
233
- ) -> Tuple[Optional[str], Optional[LinkSource]]:
234
- path: Optional[str] = None
235
- url: Optional[str] = None
236
+ ) -> tuple[str | None, LinkSource | None]:
237
+ path: str | None = None
238
+ url: str | None = None
236
239
  if os.path.exists(location): # Is a local path.
237
240
  url = path_to_url(location)
238
241
  path = location