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
@@ -1,10 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import functools
2
4
  import logging
3
5
  import os
4
6
  import pathlib
5
7
  import sys
6
8
  import sysconfig
7
- from typing import Any, Dict, Optional
9
+ from typing import Any
8
10
 
9
11
  from pip._internal.models.scheme import SCHEME_KEYS, Scheme
10
12
  from pip._internal.utils.compat import WINDOWS
@@ -87,7 +89,7 @@ def _looks_like_bpo_44860() -> bool:
87
89
  return unix_user_platlib == "$usersite"
88
90
 
89
91
 
90
- def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool:
92
+ def _looks_like_red_hat_patched_platlib_purelib(scheme: dict[str, str]) -> bool:
91
93
  platlib = scheme["platlib"]
92
94
  if "/$platlibdir/" in platlib:
93
95
  platlib = platlib.replace("/$platlibdir/", f"/{_PLATLIBDIR}/")
@@ -97,7 +99,7 @@ def _looks_like_red_hat_patched_platlib_purelib(scheme: Dict[str, str]) -> bool:
97
99
  return unpatched.replace("$platbase/", "$base/") == scheme["purelib"]
98
100
 
99
101
 
100
- @functools.lru_cache(maxsize=None)
102
+ @functools.cache
101
103
  def _looks_like_red_hat_lib() -> bool:
102
104
  """Red Hat patches platlib in unix_prefix and unix_home, but not purelib.
103
105
 
@@ -112,7 +114,7 @@ def _looks_like_red_hat_lib() -> bool:
112
114
  )
113
115
 
114
116
 
115
- @functools.lru_cache(maxsize=None)
117
+ @functools.cache
116
118
  def _looks_like_debian_scheme() -> bool:
117
119
  """Debian adds two additional schemes."""
118
120
  from distutils.command.install import INSTALL_SCHEMES
@@ -120,7 +122,7 @@ def _looks_like_debian_scheme() -> bool:
120
122
  return "deb_system" in INSTALL_SCHEMES and "unix_local" in INSTALL_SCHEMES
121
123
 
122
124
 
123
- @functools.lru_cache(maxsize=None)
125
+ @functools.cache
124
126
  def _looks_like_red_hat_scheme() -> bool:
125
127
  """Red Hat patches ``sys.prefix`` and ``sys.exec_prefix``.
126
128
 
@@ -140,7 +142,7 @@ def _looks_like_red_hat_scheme() -> bool:
140
142
  )
141
143
 
142
144
 
143
- @functools.lru_cache(maxsize=None)
145
+ @functools.cache
144
146
  def _looks_like_slackware_scheme() -> bool:
145
147
  """Slackware patches sysconfig but fails to patch distutils and site.
146
148
 
@@ -156,7 +158,7 @@ def _looks_like_slackware_scheme() -> bool:
156
158
  return "/lib64/" in paths["purelib"] and "/lib64/" not in user_site
157
159
 
158
160
 
159
- @functools.lru_cache(maxsize=None)
161
+ @functools.cache
160
162
  def _looks_like_msys2_mingw_scheme() -> bool:
161
163
  """MSYS2 patches distutils and sysconfig to use a UNIX-like scheme.
162
164
 
@@ -174,7 +176,7 @@ def _looks_like_msys2_mingw_scheme() -> bool:
174
176
  )
175
177
 
176
178
 
177
- @functools.lru_cache(maxsize=None)
179
+ @functools.cache
178
180
  def _warn_mismatched(old: pathlib.Path, new: pathlib.Path, *, key: str) -> None:
179
181
  issue_url = "https://github.com/pypa/pip/issues/10151"
180
182
  message = (
@@ -192,13 +194,13 @@ def _warn_if_mismatch(old: pathlib.Path, new: pathlib.Path, *, key: str) -> bool
192
194
  return True
193
195
 
194
196
 
195
- @functools.lru_cache(maxsize=None)
197
+ @functools.cache
196
198
  def _log_context(
197
199
  *,
198
200
  user: bool = False,
199
- home: Optional[str] = None,
200
- root: Optional[str] = None,
201
- prefix: Optional[str] = None,
201
+ home: str | None = None,
202
+ root: str | None = None,
203
+ prefix: str | None = None,
202
204
  ) -> None:
203
205
  parts = [
204
206
  "Additional context:",
@@ -214,10 +216,10 @@ def _log_context(
214
216
  def get_scheme(
215
217
  dist_name: str,
216
218
  user: bool = False,
217
- home: Optional[str] = None,
218
- root: Optional[str] = None,
219
+ home: str | None = None,
220
+ root: str | None = None,
219
221
  isolated: bool = False,
220
- prefix: Optional[str] = None,
222
+ prefix: str | None = None,
221
223
  ) -> Scheme:
222
224
  new = _sysconfig.get_scheme(
223
225
  dist_name,
@@ -281,7 +283,7 @@ def get_scheme(
281
283
  continue
282
284
 
283
285
  # On Python 3.9+, sysconfig's posix_user scheme sets platlib against
284
- # sys.platlibdir, but distutils's unix_user incorrectly coninutes
286
+ # sys.platlibdir, but distutils's unix_user incorrectly continues
285
287
  # using the same $usersite for both platlib and purelib. This creates a
286
288
  # mismatch when sys.platlibdir is not "lib".
287
289
  skip_bpo_44860 = (
@@ -9,6 +9,8 @@
9
9
  #
10
10
  # See https://github.com/pypa/pip/issues/8761 for the original discussion and
11
11
  # rationale for why this is done within pip.
12
+ from __future__ import annotations
13
+
12
14
  try:
13
15
  __import__("_distutils_hack").remove_shim()
14
16
  except (ImportError, AttributeError):
@@ -21,7 +23,6 @@ from distutils.cmd import Command as DistutilsCommand
21
23
  from distutils.command.install import SCHEME_KEYS
22
24
  from distutils.command.install import install as distutils_install_command
23
25
  from distutils.sysconfig import get_python_lib
24
- from typing import Dict, List, Optional, Union
25
26
 
26
27
  from pip._internal.models.scheme import Scheme
27
28
  from pip._internal.utils.compat import WINDOWS
@@ -35,19 +36,19 @@ logger = logging.getLogger(__name__)
35
36
  def distutils_scheme(
36
37
  dist_name: str,
37
38
  user: bool = False,
38
- home: Optional[str] = None,
39
- root: Optional[str] = None,
39
+ home: str | None = None,
40
+ root: str | None = None,
40
41
  isolated: bool = False,
41
- prefix: Optional[str] = None,
42
+ prefix: str | None = None,
42
43
  *,
43
44
  ignore_config_files: bool = False,
44
- ) -> Dict[str, str]:
45
+ ) -> dict[str, str]:
45
46
  """
46
47
  Return a distutils install scheme
47
48
  """
48
49
  from distutils.dist import Distribution
49
50
 
50
- dist_args: Dict[str, Union[str, List[str]]] = {"name": dist_name}
51
+ dist_args: dict[str, str | list[str]] = {"name": dist_name}
51
52
  if isolated:
52
53
  dist_args["script_args"] = ["--no-user-cfg"]
53
54
 
@@ -61,7 +62,7 @@ def distutils_scheme(
61
62
  "Ignore distutils configs in %s due to encoding errors.",
62
63
  ", ".join(os.path.basename(p) for p in paths),
63
64
  )
64
- obj: Optional[DistutilsCommand] = None
65
+ obj: DistutilsCommand | None = None
65
66
  obj = d.get_command_obj("install", create=True)
66
67
  assert obj is not None
67
68
  i: distutils_install_command = obj
@@ -78,7 +79,7 @@ def distutils_scheme(
78
79
  i.root = root or i.root
79
80
  i.finalize_options()
80
81
 
81
- scheme: Dict[str, str] = {}
82
+ scheme: dict[str, str] = {}
82
83
  for key in SCHEME_KEYS:
83
84
  scheme[key] = getattr(i, "install_" + key)
84
85
 
@@ -115,10 +116,10 @@ def distutils_scheme(
115
116
  def get_scheme(
116
117
  dist_name: str,
117
118
  user: bool = False,
118
- home: Optional[str] = None,
119
- root: Optional[str] = None,
119
+ home: str | None = None,
120
+ root: str | None = None,
120
121
  isolated: bool = False,
121
- prefix: Optional[str] = None,
122
+ prefix: str | None = None,
122
123
  ) -> Scheme:
123
124
  """
124
125
  Get the "scheme" corresponding to the input parameters. The distutils
@@ -1,8 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  import os
3
5
  import sys
4
6
  import sysconfig
5
- import typing
6
7
 
7
8
  from pip._internal.exceptions import InvalidSchemeCombination, UserInstallationInvalid
8
9
  from pip._internal.models.scheme import SCHEME_KEYS, Scheme
@@ -124,10 +125,10 @@ if sysconfig.get_config_var("userbase") is not None:
124
125
  def get_scheme(
125
126
  dist_name: str,
126
127
  user: bool = False,
127
- home: typing.Optional[str] = None,
128
- root: typing.Optional[str] = None,
128
+ home: str | None = None,
129
+ root: str | None = None,
129
130
  isolated: bool = False,
130
- prefix: typing.Optional[str] = None,
131
+ prefix: str | None = None,
131
132
  ) -> Scheme:
132
133
  """
133
134
  Get the "scheme" corresponding to the input parameters.
@@ -1,9 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import functools
2
4
  import os
3
5
  import site
4
6
  import sys
5
7
  import sysconfig
6
- import typing
7
8
 
8
9
  from pip._internal.exceptions import InstallationError
9
10
  from pip._internal.utils import appdirs
@@ -71,11 +72,11 @@ def get_src_prefix() -> str:
71
72
  try:
72
73
  # Use getusersitepackages if this is present, as it ensures that the
73
74
  # value is initialised properly.
74
- user_site: typing.Optional[str] = site.getusersitepackages()
75
+ user_site: str | None = site.getusersitepackages()
75
76
  except AttributeError:
76
77
  user_site = site.USER_SITE
77
78
 
78
79
 
79
- @functools.lru_cache(maxsize=None)
80
+ @functools.cache
80
81
  def is_osx_framework() -> bool:
81
82
  return bool(sysconfig.get_config_var("PYTHONFRAMEWORK"))
pip/_internal/main.py CHANGED
@@ -1,7 +1,7 @@
1
- from typing import List, Optional
1
+ from __future__ import annotations
2
2
 
3
3
 
4
- def main(args: Optional[List[str]] = None) -> int:
4
+ def main(args: list[str] | None = None) -> int:
5
5
  """This is preserved for old console scripts that may still be referencing
6
6
  it.
7
7
 
@@ -1,14 +1,19 @@
1
+ from __future__ import annotations
2
+
1
3
  import contextlib
2
4
  import functools
3
5
  import os
4
6
  import sys
5
- from typing import List, Literal, Optional, Protocol, Type, cast
7
+ from typing import TYPE_CHECKING, Literal, Protocol, cast
6
8
 
7
9
  from pip._internal.utils.deprecation import deprecated
8
10
  from pip._internal.utils.misc import strtobool
9
11
 
10
12
  from .base import BaseDistribution, BaseEnvironment, FilesystemWheel, MemoryWheel, Wheel
11
13
 
14
+ if TYPE_CHECKING:
15
+ from pip._vendor.packaging.utils import NormalizedName
16
+
12
17
  __all__ = [
13
18
  "BaseDistribution",
14
19
  "BaseEnvironment",
@@ -81,12 +86,12 @@ def _emit_pkg_resources_deprecation_if_needed() -> None:
81
86
 
82
87
 
83
88
  class Backend(Protocol):
84
- NAME: 'Literal["importlib", "pkg_resources"]'
85
- Distribution: Type[BaseDistribution]
86
- Environment: Type[BaseEnvironment]
89
+ NAME: Literal["importlib", "pkg_resources"]
90
+ Distribution: type[BaseDistribution]
91
+ Environment: type[BaseEnvironment]
87
92
 
88
93
 
89
- @functools.lru_cache(maxsize=None)
94
+ @functools.cache
90
95
  def select_backend() -> Backend:
91
96
  if _should_use_importlib_metadata():
92
97
  from . import importlib
@@ -110,7 +115,7 @@ def get_default_environment() -> BaseEnvironment:
110
115
  return select_backend().Environment.default()
111
116
 
112
117
 
113
- def get_environment(paths: Optional[List[str]]) -> BaseEnvironment:
118
+ def get_environment(paths: list[str] | None) -> BaseEnvironment:
114
119
  """Get a representation of the environment specified by ``paths``.
115
120
 
116
121
  This returns an Environment instance from the chosen backend based on the
@@ -129,7 +134,9 @@ def get_directory_distribution(directory: str) -> BaseDistribution:
129
134
  return select_backend().Distribution.from_directory(directory)
130
135
 
131
136
 
132
- def get_wheel_distribution(wheel: Wheel, canonical_name: str) -> BaseDistribution:
137
+ def get_wheel_distribution(
138
+ wheel: Wheel, canonical_name: NormalizedName
139
+ ) -> BaseDistribution:
133
140
  """Get the representation of the specified wheel's distribution metadata.
134
141
 
135
142
  This returns a Distribution instance from the chosen backend based on
@@ -1,8 +1,9 @@
1
1
  # Extracted from https://github.com/pfmoore/pkg_metadata
2
+ from __future__ import annotations
2
3
 
3
4
  from email.header import Header, decode_header, make_header
4
5
  from email.message import Message
5
- from typing import Any, Dict, List, Union, cast
6
+ from typing import Any, cast
6
7
 
7
8
  METADATA_FIELDS = [
8
9
  # Name, Multiple-Use
@@ -40,10 +41,10 @@ def json_name(field: str) -> str:
40
41
  return field.lower().replace("-", "_")
41
42
 
42
43
 
43
- def msg_to_json(msg: Message) -> Dict[str, Any]:
44
+ def msg_to_json(msg: Message) -> dict[str, Any]:
44
45
  """Convert a Message object into a JSON-compatible dictionary."""
45
46
 
46
- def sanitise_header(h: Union[Header, str]) -> str:
47
+ def sanitise_header(h: Header | str) -> str:
47
48
  if isinstance(h, Header):
48
49
  chunks = []
49
50
  for bytes, encoding in decode_header(h):
@@ -65,7 +66,7 @@ def msg_to_json(msg: Message) -> Dict[str, Any]:
65
66
  continue
66
67
  key = json_name(field)
67
68
  if multi:
68
- value: Union[str, List[str]] = [
69
+ value: str | list[str] = [
69
70
  sanitise_header(v) for v in msg.get_all(field) # type: ignore
70
71
  ]
71
72
  else:
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import csv
2
4
  import email.message
3
5
  import functools
@@ -6,19 +8,12 @@ import logging
6
8
  import pathlib
7
9
  import re
8
10
  import zipfile
11
+ from collections.abc import Collection, Container, Iterable, Iterator
9
12
  from typing import (
10
13
  IO,
11
14
  Any,
12
- Collection,
13
- Container,
14
- Dict,
15
- Iterable,
16
- Iterator,
17
- List,
18
15
  NamedTuple,
19
- Optional,
20
16
  Protocol,
21
- Tuple,
22
17
  Union,
23
18
  )
24
19
 
@@ -61,8 +56,8 @@ class BaseEntryPoint(Protocol):
61
56
 
62
57
 
63
58
  def _convert_installed_files_path(
64
- entry: Tuple[str, ...],
65
- info: Tuple[str, ...],
59
+ entry: tuple[str, ...],
60
+ info: tuple[str, ...],
66
61
  ) -> str:
67
62
  """Convert a legacy installed-files.txt path into modern RECORD path.
68
63
 
@@ -98,7 +93,7 @@ class RequiresEntry(NamedTuple):
98
93
 
99
94
  class BaseDistribution(Protocol):
100
95
  @classmethod
101
- def from_directory(cls, directory: str) -> "BaseDistribution":
96
+ def from_directory(cls, directory: str) -> BaseDistribution:
102
97
  """Load the distribution from a metadata directory.
103
98
 
104
99
  :param directory: Path to a metadata directory, e.g. ``.dist-info``.
@@ -111,7 +106,7 @@ class BaseDistribution(Protocol):
111
106
  metadata_contents: bytes,
112
107
  filename: str,
113
108
  project_name: str,
114
- ) -> "BaseDistribution":
109
+ ) -> BaseDistribution:
115
110
  """Load the distribution from the contents of a METADATA file.
116
111
 
117
112
  This is used to implement PEP 658 by generating a "shallow" dist object that can
@@ -124,7 +119,7 @@ class BaseDistribution(Protocol):
124
119
  raise NotImplementedError()
125
120
 
126
121
  @classmethod
127
- def from_wheel(cls, wheel: "Wheel", name: str) -> "BaseDistribution":
122
+ def from_wheel(cls, wheel: Wheel, name: str) -> BaseDistribution:
128
123
  """Load the distribution from a given wheel.
129
124
 
130
125
  :param wheel: A concrete wheel definition.
@@ -144,7 +139,7 @@ class BaseDistribution(Protocol):
144
139
  return f"{self.raw_name} {self.raw_version}"
145
140
 
146
141
  @property
147
- def location(self) -> Optional[str]:
142
+ def location(self) -> str | None:
148
143
  """Where the distribution is loaded from.
149
144
 
150
145
  A string value is not necessarily a filesystem path, since distributions
@@ -158,7 +153,7 @@ class BaseDistribution(Protocol):
158
153
  raise NotImplementedError()
159
154
 
160
155
  @property
161
- def editable_project_location(self) -> Optional[str]:
156
+ def editable_project_location(self) -> str | None:
162
157
  """The project location for editable distributions.
163
158
 
164
159
  This is the directory where pyproject.toml or setup.py is located.
@@ -180,7 +175,7 @@ class BaseDistribution(Protocol):
180
175
  return None
181
176
 
182
177
  @property
183
- def installed_location(self) -> Optional[str]:
178
+ def installed_location(self) -> str | None:
184
179
  """The distribution's "installed" location.
185
180
 
186
181
  This should generally be a ``site-packages`` directory. This is
@@ -193,7 +188,7 @@ class BaseDistribution(Protocol):
193
188
  raise NotImplementedError()
194
189
 
195
190
  @property
196
- def info_location(self) -> Optional[str]:
191
+ def info_location(self) -> str | None:
197
192
  """Location of the .[egg|dist]-info directory or file.
198
193
 
199
194
  Similarly to ``location``, a string value is not necessarily a
@@ -290,7 +285,7 @@ class BaseDistribution(Protocol):
290
285
  return self.raw_name.replace("-", "_")
291
286
 
292
287
  @property
293
- def direct_url(self) -> Optional[DirectUrl]:
288
+ def direct_url(self) -> DirectUrl | None:
294
289
  """Obtain a DirectUrl from this distribution.
295
290
 
296
291
  Returns None if the distribution has no `direct_url.json` metadata,
@@ -398,7 +393,7 @@ class BaseDistribution(Protocol):
398
393
  return metadata
399
394
 
400
395
  @property
401
- def metadata_dict(self) -> Dict[str, Any]:
396
+ def metadata_dict(self) -> dict[str, Any]:
402
397
  """PEP 566 compliant JSON-serializable representation of METADATA or PKG-INFO.
403
398
 
404
399
  This should return an empty dict if the metadata file is unavailable.
@@ -409,7 +404,7 @@ class BaseDistribution(Protocol):
409
404
  return msg_to_json(self.metadata)
410
405
 
411
406
  @property
412
- def metadata_version(self) -> Optional[str]:
407
+ def metadata_version(self) -> str | None:
413
408
  """Value of "Metadata-Version:" in distribution metadata, if available."""
414
409
  return self.metadata.get("Metadata-Version")
415
410
 
@@ -463,7 +458,7 @@ class BaseDistribution(Protocol):
463
458
  """
464
459
  raise NotImplementedError()
465
460
 
466
- def _iter_declared_entries_from_record(self) -> Optional[Iterator[str]]:
461
+ def _iter_declared_entries_from_record(self) -> Iterator[str] | None:
467
462
  try:
468
463
  text = self.read_text("RECORD")
469
464
  except FileNotFoundError:
@@ -471,7 +466,7 @@ class BaseDistribution(Protocol):
471
466
  # This extra Path-str cast normalizes entries.
472
467
  return (str(pathlib.Path(row[0])) for row in csv.reader(text.splitlines()))
473
468
 
474
- def _iter_declared_entries_from_legacy(self) -> Optional[Iterator[str]]:
469
+ def _iter_declared_entries_from_legacy(self) -> Iterator[str] | None:
475
470
  try:
476
471
  text = self.read_text("installed-files.txt")
477
472
  except FileNotFoundError:
@@ -492,7 +487,7 @@ class BaseDistribution(Protocol):
492
487
  for p in paths
493
488
  )
494
489
 
495
- def iter_declared_entries(self) -> Optional[Iterator[str]]:
490
+ def iter_declared_entries(self) -> Iterator[str] | None:
496
491
  """Iterate through file entries declared in this distribution.
497
492
 
498
493
  For modern .dist-info distributions, this is the files listed in the
@@ -585,14 +580,14 @@ class BaseEnvironment:
585
580
  """An environment containing distributions to introspect."""
586
581
 
587
582
  @classmethod
588
- def default(cls) -> "BaseEnvironment":
583
+ def default(cls) -> BaseEnvironment:
589
584
  raise NotImplementedError()
590
585
 
591
586
  @classmethod
592
- def from_paths(cls, paths: Optional[List[str]]) -> "BaseEnvironment":
587
+ def from_paths(cls, paths: list[str] | None) -> BaseEnvironment:
593
588
  raise NotImplementedError()
594
589
 
595
- def get_distribution(self, name: str) -> Optional["BaseDistribution"]:
590
+ def get_distribution(self, name: str) -> BaseDistribution | None:
596
591
  """Given a requirement name, return the installed distributions.
597
592
 
598
593
  The name may not be normalized. The implementation must canonicalize
@@ -600,7 +595,7 @@ class BaseEnvironment:
600
595
  """
601
596
  raise NotImplementedError()
602
597
 
603
- def _iter_distributions(self) -> Iterator["BaseDistribution"]:
598
+ def _iter_distributions(self) -> Iterator[BaseDistribution]:
604
599
  """Iterate through installed distributions.
605
600
 
606
601
  This function should be implemented by subclass, but never called
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import importlib.metadata
2
4
  import os
3
- from typing import Any, Optional, Protocol, Tuple, cast
5
+ from typing import Any, Protocol, cast
4
6
 
5
7
  from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
6
8
 
@@ -30,11 +32,11 @@ class BasePath(Protocol):
30
32
  raise NotImplementedError()
31
33
 
32
34
  @property
33
- def parent(self) -> "BasePath":
35
+ def parent(self) -> BasePath:
34
36
  raise NotImplementedError()
35
37
 
36
38
 
37
- def get_info_location(d: importlib.metadata.Distribution) -> Optional[BasePath]:
39
+ def get_info_location(d: importlib.metadata.Distribution) -> BasePath | None:
38
40
  """Find the path to the distribution's metadata directory.
39
41
 
40
42
  HACK: This relies on importlib.metadata's private ``_path`` attribute. Not
@@ -48,7 +50,7 @@ def get_info_location(d: importlib.metadata.Distribution) -> Optional[BasePath]:
48
50
 
49
51
  def parse_name_and_version_from_info_directory(
50
52
  dist: importlib.metadata.Distribution,
51
- ) -> Tuple[Optional[str], Optional[str]]:
53
+ ) -> tuple[str | None, str | None]:
52
54
  """Get a name and version from the metadata directory name.
53
55
 
54
56
  This is much faster than reading distribution metadata.
@@ -1,17 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import email.message
2
4
  import importlib.metadata
3
5
  import pathlib
4
6
  import zipfile
7
+ from collections.abc import Collection, Iterable, Iterator, Mapping, Sequence
5
8
  from os import PathLike
6
9
  from typing import (
7
- Collection,
8
- Dict,
9
- Iterable,
10
- Iterator,
11
- Mapping,
12
- Optional,
13
- Sequence,
14
- Union,
15
10
  cast,
16
11
  )
17
12
 
@@ -33,6 +28,7 @@ from pip._internal.utils.temp_dir import TempDirectory
33
28
  from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file
34
29
 
35
30
  from ._compat import (
31
+ BadMetadata,
36
32
  BasePath,
37
33
  get_dist_canonical_name,
38
34
  parse_name_and_version_from_info_directory,
@@ -64,7 +60,7 @@ class WheelDistribution(importlib.metadata.Distribution):
64
60
  zf: zipfile.ZipFile,
65
61
  name: str,
66
62
  location: str,
67
- ) -> "WheelDistribution":
63
+ ) -> WheelDistribution:
68
64
  info_dir, _ = parse_wheel(zf, name)
69
65
  paths = (
70
66
  (name, pathlib.PurePosixPath(name.split("/", 1)[-1]))
@@ -84,7 +80,7 @@ class WheelDistribution(importlib.metadata.Distribution):
84
80
  return iter(self._files)
85
81
  raise FileNotFoundError(path)
86
82
 
87
- def read_text(self, filename: str) -> Optional[str]:
83
+ def read_text(self, filename: str) -> str | None:
88
84
  try:
89
85
  data = self._files[pathlib.PurePosixPath(filename)]
90
86
  except KeyError:
@@ -97,7 +93,7 @@ class WheelDistribution(importlib.metadata.Distribution):
97
93
  raise UnsupportedWheel(error)
98
94
  return text
99
95
 
100
- def locate_file(self, path: Union[str, "PathLike[str]"]) -> pathlib.Path:
96
+ def locate_file(self, path: str | PathLike[str]) -> pathlib.Path:
101
97
  # This method doesn't make sense for our in-memory wheel, but the API
102
98
  # requires us to define it.
103
99
  raise NotImplementedError
@@ -107,8 +103,8 @@ class Distribution(BaseDistribution):
107
103
  def __init__(
108
104
  self,
109
105
  dist: importlib.metadata.Distribution,
110
- info_location: Optional[BasePath],
111
- installed_location: Optional[BasePath],
106
+ info_location: BasePath | None,
107
+ installed_location: BasePath | None,
112
108
  ) -> None:
113
109
  self._dist = dist
114
110
  self._info_location = info_location
@@ -147,19 +143,19 @@ class Distribution(BaseDistribution):
147
143
  return cls(dist, dist.info_location, pathlib.PurePosixPath(wheel.location))
148
144
 
149
145
  @property
150
- def location(self) -> Optional[str]:
146
+ def location(self) -> str | None:
151
147
  if self._info_location is None:
152
148
  return None
153
149
  return str(self._info_location.parent)
154
150
 
155
151
  @property
156
- def info_location(self) -> Optional[str]:
152
+ def info_location(self) -> str | None:
157
153
  if self._info_location is None:
158
154
  return None
159
155
  return str(self._info_location)
160
156
 
161
157
  @property
162
- def installed_location(self) -> Optional[str]:
158
+ def installed_location(self) -> str | None:
163
159
  if self._installed_location is None:
164
160
  return None
165
161
  return normalize_path(str(self._installed_location))
@@ -170,9 +166,14 @@ class Distribution(BaseDistribution):
170
166
 
171
167
  @property
172
168
  def version(self) -> Version:
173
- if version := parse_name_and_version_from_info_directory(self._dist)[1]:
169
+ try:
170
+ version = (
171
+ parse_name_and_version_from_info_directory(self._dist)[1]
172
+ or self._dist.version
173
+ )
174
174
  return parse_version(version)
175
- return parse_version(self._dist.version)
175
+ except TypeError:
176
+ raise BadMetadata(self._dist, reason="invalid metadata entry `version`")
176
177
 
177
178
  @property
178
179
  def raw_version(self) -> str:
@@ -215,7 +216,7 @@ class Distribution(BaseDistribution):
215
216
  ]
216
217
 
217
218
  def iter_dependencies(self, extras: Collection[str] = ()) -> Iterable[Requirement]:
218
- contexts: Sequence[Dict[str, str]] = [{"extra": e} for e in extras]
219
+ contexts: Sequence[dict[str, str]] = [{"extra": e} for e in extras]
219
220
  for req_string in self.metadata.get_all("Requires-Dist", []):
220
221
  # strip() because email.message.Message.get_all() may return a leading \n
221
222
  # in case a long header was wrapped.