pip 25.1__py3-none-any.whl → 25.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. pip/__init__.py +3 -3
  2. pip/_internal/__init__.py +2 -2
  3. pip/_internal/build_env.py +118 -94
  4. pip/_internal/cache.py +16 -14
  5. pip/_internal/cli/autocompletion.py +13 -4
  6. pip/_internal/cli/base_command.py +18 -7
  7. pip/_internal/cli/cmdoptions.py +14 -9
  8. pip/_internal/cli/command_context.py +4 -3
  9. pip/_internal/cli/index_command.py +11 -9
  10. pip/_internal/cli/main.py +3 -2
  11. pip/_internal/cli/main_parser.py +4 -3
  12. pip/_internal/cli/parser.py +26 -22
  13. pip/_internal/cli/progress_bars.py +19 -12
  14. pip/_internal/cli/req_command.py +16 -12
  15. pip/_internal/cli/spinners.py +81 -5
  16. pip/_internal/commands/__init__.py +5 -3
  17. pip/_internal/commands/cache.py +18 -15
  18. pip/_internal/commands/check.py +1 -2
  19. pip/_internal/commands/completion.py +1 -2
  20. pip/_internal/commands/configuration.py +26 -18
  21. pip/_internal/commands/debug.py +8 -6
  22. pip/_internal/commands/download.py +2 -3
  23. pip/_internal/commands/freeze.py +2 -3
  24. pip/_internal/commands/hash.py +1 -2
  25. pip/_internal/commands/help.py +1 -2
  26. pip/_internal/commands/index.py +15 -9
  27. pip/_internal/commands/inspect.py +4 -4
  28. pip/_internal/commands/install.py +45 -40
  29. pip/_internal/commands/list.py +35 -26
  30. pip/_internal/commands/lock.py +1 -2
  31. pip/_internal/commands/search.py +14 -12
  32. pip/_internal/commands/show.py +14 -11
  33. pip/_internal/commands/uninstall.py +1 -2
  34. pip/_internal/commands/wheel.py +2 -3
  35. pip/_internal/configuration.py +39 -25
  36. pip/_internal/distributions/base.py +6 -4
  37. pip/_internal/distributions/installed.py +8 -4
  38. pip/_internal/distributions/sdist.py +20 -13
  39. pip/_internal/distributions/wheel.py +6 -4
  40. pip/_internal/exceptions.py +58 -39
  41. pip/_internal/index/collector.py +24 -29
  42. pip/_internal/index/package_finder.py +70 -61
  43. pip/_internal/index/sources.py +17 -14
  44. pip/_internal/locations/__init__.py +18 -16
  45. pip/_internal/locations/_distutils.py +12 -11
  46. pip/_internal/locations/_sysconfig.py +5 -4
  47. pip/_internal/locations/base.py +4 -3
  48. pip/_internal/main.py +2 -2
  49. pip/_internal/metadata/__init__.py +8 -6
  50. pip/_internal/metadata/_json.py +5 -4
  51. pip/_internal/metadata/base.py +22 -27
  52. pip/_internal/metadata/importlib/_compat.py +6 -4
  53. pip/_internal/metadata/importlib/_dists.py +12 -17
  54. pip/_internal/metadata/importlib/_envs.py +9 -6
  55. pip/_internal/metadata/pkg_resources.py +11 -14
  56. pip/_internal/models/direct_url.py +24 -21
  57. pip/_internal/models/format_control.py +5 -5
  58. pip/_internal/models/installation_report.py +4 -3
  59. pip/_internal/models/link.py +39 -34
  60. pip/_internal/models/pylock.py +27 -22
  61. pip/_internal/models/search_scope.py +6 -7
  62. pip/_internal/models/selection_prefs.py +3 -3
  63. pip/_internal/models/target_python.py +10 -9
  64. pip/_internal/models/wheel.py +7 -5
  65. pip/_internal/network/auth.py +20 -22
  66. pip/_internal/network/cache.py +22 -6
  67. pip/_internal/network/download.py +169 -141
  68. pip/_internal/network/lazy_wheel.py +10 -7
  69. pip/_internal/network/session.py +32 -27
  70. pip/_internal/network/utils.py +2 -2
  71. pip/_internal/network/xmlrpc.py +2 -2
  72. pip/_internal/operations/build/build_tracker.py +10 -8
  73. pip/_internal/operations/build/wheel.py +3 -2
  74. pip/_internal/operations/build/wheel_editable.py +3 -2
  75. pip/_internal/operations/build/wheel_legacy.py +9 -8
  76. pip/_internal/operations/check.py +21 -26
  77. pip/_internal/operations/freeze.py +12 -9
  78. pip/_internal/operations/install/editable_legacy.py +5 -3
  79. pip/_internal/operations/install/wheel.py +53 -44
  80. pip/_internal/operations/prepare.py +35 -30
  81. pip/_internal/pyproject.py +7 -10
  82. pip/_internal/req/__init__.py +12 -10
  83. pip/_internal/req/constructors.py +33 -31
  84. pip/_internal/req/req_dependency_group.py +9 -8
  85. pip/_internal/req/req_file.py +32 -35
  86. pip/_internal/req/req_install.py +37 -34
  87. pip/_internal/req/req_set.py +4 -5
  88. pip/_internal/req/req_uninstall.py +20 -17
  89. pip/_internal/resolution/base.py +3 -3
  90. pip/_internal/resolution/legacy/resolver.py +21 -20
  91. pip/_internal/resolution/resolvelib/base.py +16 -13
  92. pip/_internal/resolution/resolvelib/candidates.py +29 -26
  93. pip/_internal/resolution/resolvelib/factory.py +41 -50
  94. pip/_internal/resolution/resolvelib/found_candidates.py +11 -9
  95. pip/_internal/resolution/resolvelib/provider.py +15 -20
  96. pip/_internal/resolution/resolvelib/reporter.py +5 -3
  97. pip/_internal/resolution/resolvelib/requirements.py +8 -6
  98. pip/_internal/resolution/resolvelib/resolver.py +39 -23
  99. pip/_internal/self_outdated_check.py +8 -6
  100. pip/_internal/utils/appdirs.py +1 -2
  101. pip/_internal/utils/compat.py +7 -1
  102. pip/_internal/utils/compatibility_tags.py +17 -16
  103. pip/_internal/utils/deprecation.py +11 -9
  104. pip/_internal/utils/direct_url_helpers.py +2 -2
  105. pip/_internal/utils/egg_link.py +6 -5
  106. pip/_internal/utils/entrypoints.py +3 -2
  107. pip/_internal/utils/filesystem.py +8 -5
  108. pip/_internal/utils/filetypes.py +4 -6
  109. pip/_internal/utils/glibc.py +6 -5
  110. pip/_internal/utils/hashes.py +9 -6
  111. pip/_internal/utils/logging.py +8 -5
  112. pip/_internal/utils/misc.py +54 -44
  113. pip/_internal/utils/packaging.py +3 -2
  114. pip/_internal/utils/retry.py +7 -4
  115. pip/_internal/utils/setuptools_build.py +12 -10
  116. pip/_internal/utils/subprocess.py +20 -17
  117. pip/_internal/utils/temp_dir.py +10 -12
  118. pip/_internal/utils/unpacking.py +6 -4
  119. pip/_internal/utils/urls.py +1 -1
  120. pip/_internal/utils/virtualenv.py +3 -2
  121. pip/_internal/utils/wheel.py +3 -4
  122. pip/_internal/vcs/bazaar.py +26 -8
  123. pip/_internal/vcs/git.py +59 -24
  124. pip/_internal/vcs/mercurial.py +34 -11
  125. pip/_internal/vcs/subversion.py +27 -16
  126. pip/_internal/vcs/versioncontrol.py +56 -51
  127. pip/_internal/wheel_builder.py +14 -12
  128. pip/_vendor/cachecontrol/__init__.py +1 -1
  129. pip/_vendor/certifi/__init__.py +1 -1
  130. pip/_vendor/certifi/cacert.pem +102 -221
  131. pip/_vendor/certifi/core.py +1 -32
  132. pip/_vendor/dependency_groups/_implementation.py +7 -11
  133. pip/_vendor/distlib/__init__.py +2 -2
  134. pip/_vendor/distlib/scripts.py +1 -1
  135. pip/_vendor/msgpack/__init__.py +2 -2
  136. pip/_vendor/pkg_resources/__init__.py +1 -1
  137. pip/_vendor/platformdirs/version.py +2 -2
  138. pip/_vendor/pygments/__init__.py +1 -1
  139. pip/_vendor/requests/__version__.py +2 -2
  140. pip/_vendor/requests/compat.py +12 -0
  141. pip/_vendor/requests/models.py +3 -1
  142. pip/_vendor/requests/utils.py +6 -16
  143. pip/_vendor/resolvelib/__init__.py +3 -3
  144. pip/_vendor/resolvelib/reporters.py +1 -1
  145. pip/_vendor/resolvelib/resolvers/__init__.py +4 -4
  146. pip/_vendor/resolvelib/resolvers/resolution.py +91 -10
  147. pip/_vendor/rich/__main__.py +12 -40
  148. pip/_vendor/rich/_inspect.py +1 -1
  149. pip/_vendor/rich/_ratio.py +1 -7
  150. pip/_vendor/rich/align.py +1 -7
  151. pip/_vendor/rich/box.py +1 -7
  152. pip/_vendor/rich/console.py +25 -20
  153. pip/_vendor/rich/control.py +1 -7
  154. pip/_vendor/rich/diagnose.py +1 -0
  155. pip/_vendor/rich/emoji.py +1 -6
  156. pip/_vendor/rich/live.py +32 -7
  157. pip/_vendor/rich/live_render.py +1 -7
  158. pip/_vendor/rich/logging.py +1 -1
  159. pip/_vendor/rich/panel.py +3 -4
  160. pip/_vendor/rich/progress.py +15 -15
  161. pip/_vendor/rich/spinner.py +7 -13
  162. pip/_vendor/rich/syntax.py +24 -5
  163. pip/_vendor/rich/traceback.py +32 -17
  164. pip/_vendor/truststore/_api.py +1 -1
  165. pip/_vendor/vendor.txt +10 -11
  166. {pip-25.1.dist-info → pip-25.2.dist-info}/METADATA +26 -4
  167. {pip-25.1.dist-info → pip-25.2.dist-info}/RECORD +194 -181
  168. {pip-25.1.dist-info → pip-25.2.dist-info}/WHEEL +1 -1
  169. {pip-25.1.dist-info → pip-25.2.dist-info}/licenses/AUTHORS.txt +12 -0
  170. pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  171. pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE +20 -0
  172. pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  173. pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt +284 -0
  174. pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE +202 -0
  175. pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md +31 -0
  176. pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING +14 -0
  177. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE +3 -0
  178. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +177 -0
  179. pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD +23 -0
  180. pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE +17 -0
  181. pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE +21 -0
  182. pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE +25 -0
  183. pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +21 -0
  184. pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE +175 -0
  185. pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE +13 -0
  186. pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE +19 -0
  187. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE +21 -0
  188. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +3 -0
  189. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE +21 -0
  190. pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE +21 -0
  191. pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt +21 -0
  192. pip/_vendor/distlib/database.py +0 -1329
  193. pip/_vendor/distlib/index.py +0 -508
  194. pip/_vendor/distlib/locators.py +0 -1295
  195. pip/_vendor/distlib/manifest.py +0 -384
  196. pip/_vendor/distlib/markers.py +0 -162
  197. pip/_vendor/distlib/metadata.py +0 -1031
  198. pip/_vendor/distlib/version.py +0 -750
  199. pip/_vendor/distlib/wheel.py +0 -1100
  200. pip/_vendor/typing_extensions.py +0 -4584
  201. {pip-25.1.dist-info → pip-25.2.dist-info}/entry_points.txt +0 -0
  202. {pip-25.1.dist-info → pip-25.2.dist-info}/licenses/LICENSE.txt +0 -0
  203. {pip-25.1.dist-info → pip-25.2.dist-info}/top_level.txt +0 -0
pip/__init__.py CHANGED
@@ -1,9 +1,9 @@
1
- from typing import List, Optional
1
+ from __future__ import annotations
2
2
 
3
- __version__ = "25.1"
3
+ __version__ = "25.2"
4
4
 
5
5
 
6
- def main(args: Optional[List[str]] = None) -> int:
6
+ def main(args: list[str] | None = None) -> int:
7
7
  """This is an internal API only meant for use by pip's own console scripts.
8
8
 
9
9
  For additional details, see https://github.com/pypa/pip/issues/7498.
pip/_internal/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import List, Optional
1
+ from __future__ import annotations
2
2
 
3
3
  from pip._internal.utils import _log
4
4
 
@@ -7,7 +7,7 @@ from pip._internal.utils import _log
7
7
  _log.init_logging()
8
8
 
9
9
 
10
- def main(args: Optional[List[str]] = None) -> int:
10
+ def main(args: list[str] | None = None) -> int:
11
11
  """This is preserved for old console scripts that may still be referencing
12
12
  it.
13
13
 
@@ -1,5 +1,7 @@
1
1
  """Build Environment used for isolation during sdist building"""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import logging
4
6
  import os
5
7
  import pathlib
@@ -7,8 +9,9 @@ import site
7
9
  import sys
8
10
  import textwrap
9
11
  from collections import OrderedDict
12
+ from collections.abc import Iterable
10
13
  from types import TracebackType
11
- from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type, Union
14
+ from typing import TYPE_CHECKING, Protocol
12
15
 
13
16
  from pip._vendor.packaging.version import Version
14
17
 
@@ -23,11 +26,12 @@ from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds
23
26
 
24
27
  if TYPE_CHECKING:
25
28
  from pip._internal.index.package_finder import PackageFinder
29
+ from pip._internal.req.req_install import InstallRequirement
26
30
 
27
31
  logger = logging.getLogger(__name__)
28
32
 
29
33
 
30
- def _dedup(a: str, b: str) -> Union[Tuple[str], Tuple[str, str]]:
34
+ def _dedup(a: str, b: str) -> tuple[str] | tuple[str, str]:
31
35
  return (a, b) if a != b else (a,)
32
36
 
33
37
 
@@ -56,7 +60,7 @@ def get_runnable_pip() -> str:
56
60
  return os.fsdecode(source / "__pip-runner__.py")
57
61
 
58
62
 
59
- def _get_system_sitepackages() -> Set[str]:
63
+ def _get_system_sitepackages() -> set[str]:
60
64
  """Get system site packages
61
65
 
62
66
  Usually from site.getsitepackages,
@@ -76,10 +80,108 @@ def _get_system_sitepackages() -> Set[str]:
76
80
  return {os.path.normcase(path) for path in system_sites}
77
81
 
78
82
 
83
+ class BuildEnvironmentInstaller(Protocol):
84
+ """
85
+ Interface for installing build dependencies into an isolated build
86
+ environment.
87
+ """
88
+
89
+ def install(
90
+ self,
91
+ requirements: Iterable[str],
92
+ prefix: _Prefix,
93
+ *,
94
+ kind: str,
95
+ for_req: InstallRequirement | None,
96
+ ) -> None: ...
97
+
98
+
99
+ class SubprocessBuildEnvironmentInstaller:
100
+ """
101
+ Install build dependencies by calling pip in a subprocess.
102
+ """
103
+
104
+ def __init__(self, finder: PackageFinder) -> None:
105
+ self.finder = finder
106
+
107
+ def install(
108
+ self,
109
+ requirements: Iterable[str],
110
+ prefix: _Prefix,
111
+ *,
112
+ kind: str,
113
+ for_req: InstallRequirement | None,
114
+ ) -> None:
115
+ finder = self.finder
116
+ args: list[str] = [
117
+ sys.executable,
118
+ get_runnable_pip(),
119
+ "install",
120
+ "--ignore-installed",
121
+ "--no-user",
122
+ "--prefix",
123
+ prefix.path,
124
+ "--no-warn-script-location",
125
+ "--disable-pip-version-check",
126
+ # As the build environment is ephemeral, it's wasteful to
127
+ # pre-compile everything, especially as not every Python
128
+ # module will be used/compiled in most cases.
129
+ "--no-compile",
130
+ # The prefix specified two lines above, thus
131
+ # target from config file or env var should be ignored
132
+ "--target",
133
+ "",
134
+ ]
135
+ if logger.getEffectiveLevel() <= logging.DEBUG:
136
+ args.append("-vv")
137
+ elif logger.getEffectiveLevel() <= VERBOSE:
138
+ args.append("-v")
139
+ for format_control in ("no_binary", "only_binary"):
140
+ formats = getattr(finder.format_control, format_control)
141
+ args.extend(
142
+ (
143
+ "--" + format_control.replace("_", "-"),
144
+ ",".join(sorted(formats or {":none:"})),
145
+ )
146
+ )
147
+
148
+ index_urls = finder.index_urls
149
+ if index_urls:
150
+ args.extend(["-i", index_urls[0]])
151
+ for extra_index in index_urls[1:]:
152
+ args.extend(["--extra-index-url", extra_index])
153
+ else:
154
+ args.append("--no-index")
155
+ for link in finder.find_links:
156
+ args.extend(["--find-links", link])
157
+
158
+ if finder.proxy:
159
+ args.extend(["--proxy", finder.proxy])
160
+ for host in finder.trusted_hosts:
161
+ args.extend(["--trusted-host", host])
162
+ if finder.custom_cert:
163
+ args.extend(["--cert", finder.custom_cert])
164
+ if finder.client_cert:
165
+ args.extend(["--client-cert", finder.client_cert])
166
+ if finder.allow_all_prereleases:
167
+ args.append("--pre")
168
+ if finder.prefer_binary:
169
+ args.append("--prefer-binary")
170
+ args.append("--")
171
+ args.extend(requirements)
172
+ with open_spinner(f"Installing {kind}") as spinner:
173
+ call_subprocess(
174
+ args,
175
+ command_desc=f"pip subprocess to install {kind}",
176
+ spinner=spinner,
177
+ )
178
+
179
+
79
180
  class BuildEnvironment:
80
181
  """Creates and manages an isolated environment to install build deps"""
81
182
 
82
- def __init__(self) -> None:
183
+ def __init__(self, installer: BuildEnvironmentInstaller) -> None:
184
+ self.installer = installer
83
185
  temp_dir = TempDirectory(kind=tempdir_kinds.BUILD_ENV, globally_managed=True)
84
186
 
85
187
  self._prefixes = OrderedDict(
@@ -87,8 +189,8 @@ class BuildEnvironment:
87
189
  for name in ("normal", "overlay")
88
190
  )
89
191
 
90
- self._bin_dirs: List[str] = []
91
- self._lib_dirs: List[str] = []
192
+ self._bin_dirs: list[str] = []
193
+ self._lib_dirs: list[str] = []
92
194
  for prefix in reversed(list(self._prefixes.values())):
93
195
  self._bin_dirs.append(prefix.bin_dir)
94
196
  self._lib_dirs.extend(prefix.lib_dirs)
@@ -156,9 +258,9 @@ class BuildEnvironment:
156
258
 
157
259
  def __exit__(
158
260
  self,
159
- exc_type: Optional[Type[BaseException]],
160
- exc_val: Optional[BaseException],
161
- exc_tb: Optional[TracebackType],
261
+ exc_type: type[BaseException] | None,
262
+ exc_val: BaseException | None,
263
+ exc_tb: TracebackType | None,
162
264
  ) -> None:
163
265
  for varname, old_value in self._save_env.items():
164
266
  if old_value is None:
@@ -168,7 +270,7 @@ class BuildEnvironment:
168
270
 
169
271
  def check_requirements(
170
272
  self, reqs: Iterable[str]
171
- ) -> Tuple[Set[Tuple[str, str]], Set[str]]:
273
+ ) -> tuple[set[tuple[str, str]], set[str]]:
172
274
  """Return 2 sets:
173
275
  - conflicting requirements: set of (installed, wanted) reqs tuples
174
276
  - missing requirements: set of reqs
@@ -202,96 +304,18 @@ class BuildEnvironment:
202
304
 
203
305
  def install_requirements(
204
306
  self,
205
- finder: "PackageFinder",
206
307
  requirements: Iterable[str],
207
308
  prefix_as_string: str,
208
309
  *,
209
310
  kind: str,
311
+ for_req: InstallRequirement | None = None,
210
312
  ) -> None:
211
313
  prefix = self._prefixes[prefix_as_string]
212
314
  assert not prefix.setup
213
315
  prefix.setup = True
214
316
  if not requirements:
215
317
  return
216
- self._install_requirements(
217
- get_runnable_pip(),
218
- finder,
219
- requirements,
220
- prefix,
221
- kind=kind,
222
- )
223
-
224
- @staticmethod
225
- def _install_requirements(
226
- pip_runnable: str,
227
- finder: "PackageFinder",
228
- requirements: Iterable[str],
229
- prefix: _Prefix,
230
- *,
231
- kind: str,
232
- ) -> None:
233
- args: List[str] = [
234
- sys.executable,
235
- pip_runnable,
236
- "install",
237
- "--ignore-installed",
238
- "--no-user",
239
- "--prefix",
240
- prefix.path,
241
- "--no-warn-script-location",
242
- "--disable-pip-version-check",
243
- # As the build environment is ephemeral, it's wasteful to
244
- # pre-compile everything, especially as not every Python
245
- # module will be used/compiled in most cases.
246
- "--no-compile",
247
- # The prefix specified two lines above, thus
248
- # target from config file or env var should be ignored
249
- "--target",
250
- "",
251
- ]
252
- if logger.getEffectiveLevel() <= logging.DEBUG:
253
- args.append("-vv")
254
- elif logger.getEffectiveLevel() <= VERBOSE:
255
- args.append("-v")
256
- for format_control in ("no_binary", "only_binary"):
257
- formats = getattr(finder.format_control, format_control)
258
- args.extend(
259
- (
260
- "--" + format_control.replace("_", "-"),
261
- ",".join(sorted(formats or {":none:"})),
262
- )
263
- )
264
-
265
- index_urls = finder.index_urls
266
- if index_urls:
267
- args.extend(["-i", index_urls[0]])
268
- for extra_index in index_urls[1:]:
269
- args.extend(["--extra-index-url", extra_index])
270
- else:
271
- args.append("--no-index")
272
- for link in finder.find_links:
273
- args.extend(["--find-links", link])
274
-
275
- if finder.proxy:
276
- args.extend(["--proxy", finder.proxy])
277
- for host in finder.trusted_hosts:
278
- args.extend(["--trusted-host", host])
279
- if finder.custom_cert:
280
- args.extend(["--cert", finder.custom_cert])
281
- if finder.client_cert:
282
- args.extend(["--client-cert", finder.client_cert])
283
- if finder.allow_all_prereleases:
284
- args.append("--pre")
285
- if finder.prefer_binary:
286
- args.append("--prefer-binary")
287
- args.append("--")
288
- args.extend(requirements)
289
- with open_spinner(f"Installing {kind}") as spinner:
290
- call_subprocess(
291
- args,
292
- command_desc=f"pip subprocess to install {kind}",
293
- spinner=spinner,
294
- )
318
+ self.installer.install(requirements, prefix, kind=kind, for_req=for_req)
295
319
 
296
320
 
297
321
  class NoOpBuildEnvironment(BuildEnvironment):
@@ -305,9 +329,9 @@ class NoOpBuildEnvironment(BuildEnvironment):
305
329
 
306
330
  def __exit__(
307
331
  self,
308
- exc_type: Optional[Type[BaseException]],
309
- exc_val: Optional[BaseException],
310
- exc_tb: Optional[TracebackType],
332
+ exc_type: type[BaseException] | None,
333
+ exc_val: BaseException | None,
334
+ exc_tb: TracebackType | None,
311
335
  ) -> None:
312
336
  pass
313
337
 
@@ -316,10 +340,10 @@ class NoOpBuildEnvironment(BuildEnvironment):
316
340
 
317
341
  def install_requirements(
318
342
  self,
319
- finder: "PackageFinder",
320
343
  requirements: Iterable[str],
321
344
  prefix_as_string: str,
322
345
  *,
323
346
  kind: str,
347
+ for_req: InstallRequirement | None = None,
324
348
  ) -> None:
325
349
  raise NotImplementedError()
pip/_internal/cache.py CHANGED
@@ -1,11 +1,13 @@
1
1
  """Cache Management"""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import hashlib
4
6
  import json
5
7
  import logging
6
8
  import os
7
9
  from pathlib import Path
8
- from typing import Any, Dict, List, Optional
10
+ from typing import Any
9
11
 
10
12
  from pip._vendor.packaging.tags import Tag, interpreter_name, interpreter_version
11
13
  from pip._vendor.packaging.utils import canonicalize_name
@@ -22,7 +24,7 @@ logger = logging.getLogger(__name__)
22
24
  ORIGIN_JSON_NAME = "origin.json"
23
25
 
24
26
 
25
- def _hash_dict(d: Dict[str, str]) -> str:
27
+ def _hash_dict(d: dict[str, str]) -> str:
26
28
  """Return a stable sha224 of a dictionary."""
27
29
  s = json.dumps(d, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
28
30
  return hashlib.sha224(s.encode("ascii")).hexdigest()
@@ -39,7 +41,7 @@ class Cache:
39
41
  assert not cache_dir or os.path.isabs(cache_dir)
40
42
  self.cache_dir = cache_dir or None
41
43
 
42
- def _get_cache_path_parts(self, link: Link) -> List[str]:
44
+ def _get_cache_path_parts(self, link: Link) -> list[str]:
43
45
  """Get parts of part that must be os.path.joined with cache_dir"""
44
46
 
45
47
  # We want to generate an url to use as our cache key, we don't want to
@@ -72,7 +74,7 @@ class Cache:
72
74
 
73
75
  return parts
74
76
 
75
- def _get_candidates(self, link: Link, canonical_package_name: str) -> List[Any]:
77
+ def _get_candidates(self, link: Link, canonical_package_name: str) -> list[Any]:
76
78
  can_not_cache = not self.cache_dir or not canonical_package_name or not link
77
79
  if can_not_cache:
78
80
  return []
@@ -89,8 +91,8 @@ class Cache:
89
91
  def get(
90
92
  self,
91
93
  link: Link,
92
- package_name: Optional[str],
93
- supported_tags: List[Tag],
94
+ package_name: str | None,
95
+ supported_tags: list[Tag],
94
96
  ) -> Link:
95
97
  """Returns a link to a cached item if it exists, otherwise returns the
96
98
  passed link.
@@ -127,8 +129,8 @@ class SimpleWheelCache(Cache):
127
129
  def get(
128
130
  self,
129
131
  link: Link,
130
- package_name: Optional[str],
131
- supported_tags: List[Tag],
132
+ package_name: str | None,
133
+ supported_tags: list[Tag],
132
134
  ) -> Link:
133
135
  candidates = []
134
136
 
@@ -188,7 +190,7 @@ class CacheEntry:
188
190
  ):
189
191
  self.link = link
190
192
  self.persistent = persistent
191
- self.origin: Optional[DirectUrl] = None
193
+ self.origin: DirectUrl | None = None
192
194
  origin_direct_url_path = Path(self.link.file_path).parent / ORIGIN_JSON_NAME
193
195
  if origin_direct_url_path.exists():
194
196
  try:
@@ -225,8 +227,8 @@ class WheelCache(Cache):
225
227
  def get(
226
228
  self,
227
229
  link: Link,
228
- package_name: Optional[str],
229
- supported_tags: List[Tag],
230
+ package_name: str | None,
231
+ supported_tags: list[Tag],
230
232
  ) -> Link:
231
233
  cache_entry = self.get_cache_entry(link, package_name, supported_tags)
232
234
  if cache_entry is None:
@@ -236,9 +238,9 @@ class WheelCache(Cache):
236
238
  def get_cache_entry(
237
239
  self,
238
240
  link: Link,
239
- package_name: Optional[str],
240
- supported_tags: List[Tag],
241
- ) -> Optional[CacheEntry]:
241
+ package_name: str | None,
242
+ supported_tags: list[Tag],
243
+ ) -> CacheEntry | None:
242
244
  """Returns a CacheEntry with a link to a cached item if it exists or
243
245
  None. The cache entry indicates if the item was found in the persistent
244
246
  or ephemeral cache.
@@ -1,10 +1,13 @@
1
1
  """Logic that powers autocompletion installed by ``pip completion``."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import optparse
4
6
  import os
5
7
  import sys
8
+ from collections.abc import Iterable
6
9
  from itertools import chain
7
- from typing import Any, Iterable, List, Optional
10
+ from typing import Any
8
11
 
9
12
  from pip._internal.cli.main_parser import create_main_parser
10
13
  from pip._internal.commands import commands_dict, create_command
@@ -32,7 +35,7 @@ def autocomplete() -> None:
32
35
  options = []
33
36
 
34
37
  # subcommand
35
- subcommand_name: Optional[str] = None
38
+ subcommand_name: str | None = None
36
39
  for word in cwords:
37
40
  if word in subcommands:
38
41
  subcommand_name = word
@@ -100,6 +103,12 @@ def autocomplete() -> None:
100
103
  if option[1] and option[0][:2] == "--":
101
104
  opt_label += "="
102
105
  print(opt_label)
106
+
107
+ # Complete sub-commands (unless one is already given).
108
+ if not any(name in cwords for name in subcommand.handler_map()):
109
+ for handler_name in subcommand.handler_map():
110
+ if handler_name.startswith(current):
111
+ print(handler_name)
103
112
  else:
104
113
  # show main parser options only when necessary
105
114
 
@@ -121,8 +130,8 @@ def autocomplete() -> None:
121
130
 
122
131
 
123
132
  def get_path_completion_type(
124
- cwords: List[str], cword: int, opts: Iterable[Any]
125
- ) -> Optional[str]:
133
+ cwords: list[str], cword: int, opts: Iterable[Any]
134
+ ) -> str | None:
126
135
  """Get the type of path completion (``file``, ``dir``, ``path`` or None)
127
136
 
128
137
  :param cwords: same as the environmental variable ``COMP_WORDS``
@@ -1,5 +1,7 @@
1
1
  """Base Command class, and related routines"""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import logging
4
6
  import logging.config
5
7
  import optparse
@@ -7,7 +9,7 @@ import os
7
9
  import sys
8
10
  import traceback
9
11
  from optparse import Values
10
- from typing import List, Optional, Tuple
12
+ from typing import Callable
11
13
 
12
14
  from pip._vendor.rich import reconfigure
13
15
  from pip._vendor.rich import traceback as rich_traceback
@@ -60,7 +62,7 @@ class Command(CommandContextMixIn):
60
62
  isolated=isolated,
61
63
  )
62
64
 
63
- self.tempdir_registry: Optional[TempDirRegistry] = None
65
+ self.tempdir_registry: TempDirRegistry | None = None
64
66
 
65
67
  # Commands should add options to this option group
66
68
  optgroup_name = f"{self.name.capitalize()} Options"
@@ -87,10 +89,10 @@ class Command(CommandContextMixIn):
87
89
  # are present.
88
90
  assert not hasattr(options, "no_index")
89
91
 
90
- def run(self, options: Values, args: List[str]) -> int:
92
+ def run(self, options: Values, args: list[str]) -> int:
91
93
  raise NotImplementedError
92
94
 
93
- def _run_wrapper(self, level_number: int, options: Values, args: List[str]) -> int:
95
+ def _run_wrapper(self, level_number: int, options: Values, args: list[str]) -> int:
94
96
  def _inner_run() -> int:
95
97
  try:
96
98
  return self.run(options, args)
@@ -147,18 +149,18 @@ class Command(CommandContextMixIn):
147
149
 
148
150
  return UNKNOWN_ERROR
149
151
 
150
- def parse_args(self, args: List[str]) -> Tuple[Values, List[str]]:
152
+ def parse_args(self, args: list[str]) -> tuple[Values, list[str]]:
151
153
  # factored out for testability
152
154
  return self.parser.parse_args(args)
153
155
 
154
- def main(self, args: List[str]) -> int:
156
+ def main(self, args: list[str]) -> int:
155
157
  try:
156
158
  with self.main_context():
157
159
  return self._main(args)
158
160
  finally:
159
161
  logging.shutdown()
160
162
 
161
- def _main(self, args: List[str]) -> int:
163
+ def _main(self, args: list[str]) -> int:
162
164
  # We must initialize this before the tempdir manager, otherwise the
163
165
  # configuration would not be accessible by the time we clean up the
164
166
  # tempdir manager.
@@ -174,6 +176,9 @@ class Command(CommandContextMixIn):
174
176
  if options.debug_mode:
175
177
  self.verbosity = 2
176
178
 
179
+ if hasattr(options, "progress_bar") and options.progress_bar == "auto":
180
+ options.progress_bar = "on" if self.verbosity >= 0 else "off"
181
+
177
182
  reconfigure(no_color=options.no_color)
178
183
  level_number = setup_logging(
179
184
  verbosity=self.verbosity,
@@ -231,3 +236,9 @@ class Command(CommandContextMixIn):
231
236
  options.cache_dir = None
232
237
 
233
238
  return self._run_wrapper(level_number, options, args)
239
+
240
+ def handler_map(self) -> dict[str, Callable[[Values, list[str]], None]]:
241
+ """
242
+ map of names to handler actions for commands with sub-actions
243
+ """
244
+ return {}
@@ -9,6 +9,7 @@ pass on state. To be consistent, all options will follow this design.
9
9
 
10
10
  # The following comment should be removed at some point in the future.
11
11
  # mypy: strict-optional=False
12
+ from __future__ import annotations
12
13
 
13
14
  import importlib.util
14
15
  import logging
@@ -18,7 +19,7 @@ import textwrap
18
19
  from functools import partial
19
20
  from optparse import SUPPRESS_HELP, Option, OptionGroup, OptionParser, Values
20
21
  from textwrap import dedent
21
- from typing import Any, Callable, Dict, Optional, Tuple
22
+ from typing import Any, Callable
22
23
 
23
24
  from pip._vendor.packaging.utils import canonicalize_name
24
25
 
@@ -48,7 +49,7 @@ def raise_option_error(parser: OptionParser, option: Option, msg: str) -> None:
48
49
  parser.error(msg)
49
50
 
50
51
 
51
- def make_option_group(group: Dict[str, Any], parser: ConfigOptionParser) -> OptionGroup:
52
+ def make_option_group(group: dict[str, Any], parser: ConfigOptionParser) -> OptionGroup:
52
53
  """
53
54
  Return an OptionGroup object
54
55
  group -- assumed to be dict with 'name' and 'options' keys
@@ -227,9 +228,13 @@ progress_bar: Callable[..., Option] = partial(
227
228
  "--progress-bar",
228
229
  dest="progress_bar",
229
230
  type="choice",
230
- choices=["on", "off", "raw"],
231
- default="on",
232
- help="Specify whether the progress bar should be used [on, off, raw] (default: on)",
231
+ choices=["auto", "on", "off", "raw"],
232
+ default="auto",
233
+ help=(
234
+ "Specify whether the progress bar should be used. In 'auto'"
235
+ " mode, --quiet will suppress all progress bars."
236
+ " [auto, on, off, raw] (default: auto)"
237
+ ),
233
238
  )
234
239
 
235
240
  log: Callable[..., Option] = partial(
@@ -289,7 +294,7 @@ resume_retries: Callable[..., Option] = partial(
289
294
  "--resume-retries",
290
295
  dest="resume_retries",
291
296
  type="int",
292
- default=0,
297
+ default=5,
293
298
  help="Maximum attempts to resume or restart an incomplete download. "
294
299
  "(default: %default)",
295
300
  )
@@ -555,7 +560,7 @@ platforms: Callable[..., Option] = partial(
555
560
 
556
561
 
557
562
  # This was made a separate function for unit-testing purposes.
558
- def _convert_python_version(value: str) -> Tuple[Tuple[int, ...], Optional[str]]:
563
+ def _convert_python_version(value: str) -> tuple[tuple[int, ...], str | None]:
559
564
  """
560
565
  Convert a version string like "3", "37", or "3.7.3" into a tuple of ints.
561
566
 
@@ -1090,7 +1095,7 @@ use_deprecated_feature: Callable[..., Option] = partial(
1090
1095
  # groups #
1091
1096
  ##########
1092
1097
 
1093
- general_group: Dict[str, Any] = {
1098
+ general_group: dict[str, Any] = {
1094
1099
  "name": "General Options",
1095
1100
  "options": [
1096
1101
  help_,
@@ -1122,7 +1127,7 @@ general_group: Dict[str, Any] = {
1122
1127
  ],
1123
1128
  }
1124
1129
 
1125
- index_group: Dict[str, Any] = {
1130
+ index_group: dict[str, Any] = {
1126
1131
  "name": "Package Index Options",
1127
1132
  "options": [
1128
1133
  index_url,
@@ -1,5 +1,6 @@
1
- from contextlib import ExitStack, contextmanager
2
- from typing import ContextManager, Generator, TypeVar
1
+ from collections.abc import Generator
2
+ from contextlib import AbstractContextManager, ExitStack, contextmanager
3
+ from typing import TypeVar
3
4
 
4
5
  _T = TypeVar("_T", covariant=True)
5
6
 
@@ -21,7 +22,7 @@ class CommandContextMixIn:
21
22
  finally:
22
23
  self._in_main_context = False
23
24
 
24
- def enter_context(self, context_provider: ContextManager[_T]) -> _T:
25
+ def enter_context(self, context_provider: AbstractContextManager[_T]) -> _T:
25
26
  assert self._in_main_context
26
27
 
27
28
  return self._main_context.enter_context(context_provider)