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,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import contextlib
2
4
  import functools
3
5
  import logging
4
6
  import os
5
- from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, cast
7
+ from typing import TYPE_CHECKING, cast
6
8
 
7
9
  from pip._vendor.packaging.utils import canonicalize_name
8
10
  from pip._vendor.resolvelib import BaseReporter, ResolutionImpossible, ResolutionTooDeep
@@ -43,7 +45,7 @@ class Resolver(BaseResolver):
43
45
  self,
44
46
  preparer: RequirementPreparer,
45
47
  finder: PackageFinder,
46
- wheel_cache: Optional[WheelCache],
48
+ wheel_cache: WheelCache | None,
47
49
  make_install_req: InstallRequirementProvider,
48
50
  use_user_site: bool,
49
51
  ignore_dependencies: bool,
@@ -51,7 +53,7 @@ class Resolver(BaseResolver):
51
53
  ignore_requires_python: bool,
52
54
  force_reinstall: bool,
53
55
  upgrade_strategy: str,
54
- py_version_info: Optional[Tuple[int, ...]] = None,
56
+ py_version_info: tuple[int, ...] | None = None,
55
57
  ):
56
58
  super().__init__()
57
59
  assert upgrade_strategy in self._allowed_strategies
@@ -69,10 +71,10 @@ class Resolver(BaseResolver):
69
71
  )
70
72
  self.ignore_dependencies = ignore_dependencies
71
73
  self.upgrade_strategy = upgrade_strategy
72
- self._result: Optional[Result] = None
74
+ self._result: Result | None = None
73
75
 
74
76
  def resolve(
75
- self, root_reqs: List[InstallRequirement], check_supported_wheels: bool
77
+ self, root_reqs: list[InstallRequirement], check_supported_wheels: bool
76
78
  ) -> RequirementSet:
77
79
  collected = self.factory.collect_root_requirements(root_reqs)
78
80
  provider = PipProvider(
@@ -85,7 +87,8 @@ class Resolver(BaseResolver):
85
87
  if "PIP_RESOLVER_DEBUG" in os.environ:
86
88
  reporter: BaseReporter[Requirement, Candidate, str] = PipDebuggingReporter()
87
89
  else:
88
- reporter = PipReporter()
90
+ reporter = PipReporter(constraints=provider.constraints)
91
+
89
92
  resolver: RLResolver[Requirement, Candidate, str] = RLResolver(
90
93
  provider,
91
94
  reporter,
@@ -178,16 +181,11 @@ class Resolver(BaseResolver):
178
181
 
179
182
  req_set.add_named_requirement(ireq)
180
183
 
181
- reqs = req_set.all_requirements
182
- self.factory.preparer.prepare_linked_requirements_more(reqs)
183
- for req in reqs:
184
- req.prepared = True
185
- req.needs_more_preparation = False
186
184
  return req_set
187
185
 
188
186
  def get_installation_order(
189
187
  self, req_set: RequirementSet
190
- ) -> List[InstallRequirement]:
188
+ ) -> list[InstallRequirement]:
191
189
  """Get order for installation of requirements in RequirementSet.
192
190
 
193
191
  The returned list contains a requirement before another that depends on
@@ -218,8 +216,8 @@ class Resolver(BaseResolver):
218
216
 
219
217
 
220
218
  def get_topological_weights(
221
- graph: "DirectedGraph[Optional[str]]", requirement_keys: Set[str]
222
- ) -> Dict[Optional[str], int]:
219
+ graph: DirectedGraph[str | None], requirement_keys: set[str]
220
+ ) -> dict[str | None, int]:
223
221
  """Assign weights to each node based on how "deep" they are.
224
222
 
225
223
  This implementation may change at any point in the future without prior
@@ -245,14 +243,25 @@ def get_topological_weights(
245
243
  We are only interested in the weights of packages that are in the
246
244
  requirement_keys.
247
245
  """
248
- path: Set[Optional[str]] = set()
249
- weights: Dict[Optional[str], int] = {}
246
+ path: set[str | None] = set()
247
+ weights: dict[str | None, list[int]] = {}
250
248
 
251
- def visit(node: Optional[str]) -> None:
249
+ def visit(node: str | None) -> None:
252
250
  if node in path:
253
251
  # We hit a cycle, so we'll break it here.
254
252
  return
255
253
 
254
+ # The walk is exponential and for pathologically connected graphs (which
255
+ # are the ones most likely to contain cycles in the first place) it can
256
+ # take until the heat-death of the universe. To counter this we limit
257
+ # the number of attempts to visit (i.e. traverse through) any given
258
+ # node. We choose a value here which gives decent enough coverage for
259
+ # fairly well behaved graphs, and still limits the walk complexity to be
260
+ # linear in nature.
261
+ cur_weights = weights.get(node, [])
262
+ if len(cur_weights) >= 5:
263
+ return
264
+
256
265
  # Time to visit the children!
257
266
  path.add(node)
258
267
  for child in graph.iter_children(node):
@@ -262,14 +271,14 @@ def get_topological_weights(
262
271
  if node not in requirement_keys:
263
272
  return
264
273
 
265
- last_known_parent_count = weights.get(node, 0)
266
- weights[node] = max(last_known_parent_count, len(path))
274
+ cur_weights.append(len(path))
275
+ weights[node] = cur_weights
267
276
 
268
- # Simplify the graph, pruning leaves that have no dependencies.
269
- # This is needed for large graphs (say over 200 packages) because the
270
- # `visit` function is exponentially slower then, taking minutes.
277
+ # Simplify the graph, pruning leaves that have no dependencies. This is
278
+ # needed for large graphs (say over 200 packages) because the `visit`
279
+ # function is slower for large/densely connected graphs, taking minutes.
271
280
  # See https://github.com/pypa/pip/issues/10557
272
- # We will loop until we explicitly break the loop.
281
+ # We repeat the pruning step until we have no more leaves to remove.
273
282
  while True:
274
283
  leaves = set()
275
284
  for key in graph:
@@ -289,12 +298,13 @@ def get_topological_weights(
289
298
  for leaf in leaves:
290
299
  if leaf not in requirement_keys:
291
300
  continue
292
- weights[leaf] = weight
301
+ weights[leaf] = [weight]
293
302
  # Remove the leaves from the graph, making it simpler.
294
303
  for leaf in leaves:
295
304
  graph.remove(leaf)
296
305
 
297
- # Visit the remaining graph.
306
+ # Visit the remaining graph, this will only have nodes to handle if the
307
+ # graph had a cycle in it, which the pruning step above could not handle.
298
308
  # `None` is guaranteed to be the root node by resolvelib.
299
309
  visit(None)
300
310
 
@@ -303,13 +313,15 @@ def get_topological_weights(
303
313
  difference = set(weights.keys()).difference(requirement_keys)
304
314
  assert not difference, difference
305
315
 
306
- return weights
316
+ # Now give back all the weights, choosing the largest ones from what we
317
+ # accumulated.
318
+ return {node: max(wgts) for (node, wgts) in weights.items()}
307
319
 
308
320
 
309
321
  def _req_set_item_sorter(
310
- item: Tuple[str, InstallRequirement],
311
- weights: Dict[Optional[str], int],
312
- ) -> Tuple[int, str]:
322
+ item: tuple[str, InstallRequirement],
323
+ weights: dict[str | None, int],
324
+ ) -> tuple[int, str]:
313
325
  """Key function used to sort install requirements for installation.
314
326
 
315
327
  Based on the "weight" mapping calculated in ``get_installation_order()``.
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import datetime
2
4
  import functools
3
5
  import hashlib
@@ -7,7 +9,7 @@ import optparse
7
9
  import os.path
8
10
  import sys
9
11
  from dataclasses import dataclass
10
- from typing import Any, Callable, Dict, Optional
12
+ from typing import Any, Callable
11
13
 
12
14
  from pip._vendor.packaging.version import Version
13
15
  from pip._vendor.packaging.version import parse as parse_version
@@ -25,7 +27,12 @@ from pip._internal.utils.entrypoints import (
25
27
  get_best_invocation_for_this_pip,
26
28
  get_best_invocation_for_this_python,
27
29
  )
28
- from pip._internal.utils.filesystem import adjacent_tmp_file, check_path_owner, replace
30
+ from pip._internal.utils.filesystem import (
31
+ adjacent_tmp_file,
32
+ check_path_owner,
33
+ copy_directory_permissions,
34
+ replace,
35
+ )
29
36
  from pip._internal.utils.misc import (
30
37
  ExternallyManagedEnvironment,
31
38
  check_externally_managed,
@@ -54,7 +61,7 @@ def _convert_date(isodate: str) -> datetime.datetime:
54
61
 
55
62
  class SelfCheckState:
56
63
  def __init__(self, cache_dir: str) -> None:
57
- self._state: Dict[str, Any] = {}
64
+ self._state: dict[str, Any] = {}
58
65
  self._statefile_path = None
59
66
 
60
67
  # Try to load the existing state
@@ -74,7 +81,7 @@ class SelfCheckState:
74
81
  def key(self) -> str:
75
82
  return sys.prefix
76
83
 
77
- def get(self, current_time: datetime.datetime) -> Optional[str]:
84
+ def get(self, current_time: datetime.datetime) -> str | None:
78
85
  """Check if we have a not-outdated version loaded already."""
79
86
  if not self._state:
80
87
  return None
@@ -98,13 +105,15 @@ class SelfCheckState:
98
105
  if not self._statefile_path:
99
106
  return
100
107
 
108
+ statefile_directory = os.path.dirname(self._statefile_path)
109
+
101
110
  # Check to make sure that we own the directory
102
- if not check_path_owner(os.path.dirname(self._statefile_path)):
111
+ if not check_path_owner(statefile_directory):
103
112
  return
104
113
 
105
114
  # Now that we've ensured the directory is owned by this user, we'll go
106
115
  # ahead and make sure that all our directories are created.
107
- ensure_dir(os.path.dirname(self._statefile_path))
116
+ ensure_dir(statefile_directory)
108
117
 
109
118
  state = {
110
119
  # Include the key so it's easy to tell which pip wrote the
@@ -118,6 +127,7 @@ class SelfCheckState:
118
127
 
119
128
  with adjacent_tmp_file(self._statefile_path) as f:
120
129
  f.write(text.encode())
130
+ copy_directory_permissions(statefile_directory, f)
121
131
 
122
132
  try:
123
133
  # Since we have a prefix-specific state file, we can just
@@ -165,7 +175,7 @@ def was_installed_by_pip(pkg: str) -> bool:
165
175
 
166
176
  def _get_current_remote_pip_version(
167
177
  session: PipSession, options: optparse.Values
168
- ) -> Optional[str]:
178
+ ) -> str | None:
169
179
  # Lets use PackageFinder to see what the latest pip version is
170
180
  link_collector = LinkCollector.create(
171
181
  session,
@@ -196,8 +206,8 @@ def _self_version_check_logic(
196
206
  state: SelfCheckState,
197
207
  current_time: datetime.datetime,
198
208
  local_version: Version,
199
- get_remote_version: Callable[[], Optional[str]],
200
- ) -> Optional[UpgradePrompt]:
209
+ get_remote_version: Callable[[], str | None],
210
+ ) -> UpgradePrompt | None:
201
211
  remote_version_str = state.get(current_time)
202
212
  if remote_version_str is None:
203
213
  remote_version_str = get_remote_version()
@@ -8,7 +8,6 @@ and eventually drop this after all usages are changed.
8
8
 
9
9
  import os
10
10
  import sys
11
- from typing import List
12
11
 
13
12
  from pip._vendor import platformdirs as _appdirs
14
13
 
@@ -40,7 +39,7 @@ def user_config_dir(appname: str, roaming: bool = True) -> str:
40
39
 
41
40
  # for the discussion regarding site_config_dir locations
42
41
  # see <https://github.com/pypa/pip/issues/1733>
43
- def site_config_dirs(appname: str) -> List[str]:
42
+ def site_config_dirs(appname: str) -> list[str]:
44
43
  if sys.platform == "darwin":
45
44
  dirval = _appdirs.site_data_dir(appname, appauthor=False, multipath=True)
46
45
  return dirval.split(os.pathsep)
@@ -7,7 +7,7 @@ import os
7
7
  import sys
8
8
  from typing import IO
9
9
 
10
- __all__ = ["get_path_uid", "stdlib_pkgs", "WINDOWS"]
10
+ __all__ = ["get_path_uid", "stdlib_pkgs", "tomllib", "WINDOWS"]
11
11
 
12
12
 
13
13
  logger = logging.getLogger(__name__)
@@ -67,6 +67,12 @@ else:
67
67
  )
68
68
 
69
69
 
70
+ if sys.version_info >= (3, 11):
71
+ import tomllib
72
+ else:
73
+ from pip._vendor import tomli as tomllib
74
+
75
+
70
76
  # packages in the stdlib that may have installation metadata, but should not be
71
77
  # considered 'installed'. this theoretically could be determined based on
72
78
  # dist.location (py27:`sysconfig.get_paths()['stdlib']`,
@@ -1,7 +1,8 @@
1
1
  """Generate and work with PEP 425 Compatibility Tags."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import re
4
- from typing import List, Optional, Tuple
5
6
 
6
7
  from pip._vendor.packaging.tags import (
7
8
  PythonVersion,
@@ -19,12 +20,12 @@ from pip._vendor.packaging.tags import (
19
20
  _apple_arch_pat = re.compile(r"(.+)_(\d+)_(\d+)_(.+)")
20
21
 
21
22
 
22
- def version_info_to_nodot(version_info: Tuple[int, ...]) -> str:
23
+ def version_info_to_nodot(version_info: tuple[int, ...]) -> str:
23
24
  # Only use up to the first two numbers.
24
25
  return "".join(map(str, version_info[:2]))
25
26
 
26
27
 
27
- def _mac_platforms(arch: str) -> List[str]:
28
+ def _mac_platforms(arch: str) -> list[str]:
28
29
  match = _apple_arch_pat.match(arch)
29
30
  if match:
30
31
  name, major, minor, actual_arch = match.groups()
@@ -44,7 +45,7 @@ def _mac_platforms(arch: str) -> List[str]:
44
45
  return arches
45
46
 
46
47
 
47
- def _ios_platforms(arch: str) -> List[str]:
48
+ def _ios_platforms(arch: str) -> list[str]:
48
49
  match = _apple_arch_pat.match(arch)
49
50
  if match:
50
51
  name, major, minor, actual_multiarch = match.groups()
@@ -64,7 +65,7 @@ def _ios_platforms(arch: str) -> List[str]:
64
65
  return arches
65
66
 
66
67
 
67
- def _android_platforms(arch: str) -> List[str]:
68
+ def _android_platforms(arch: str) -> list[str]:
68
69
  match = re.fullmatch(r"android_(\d+)_(.+)", arch)
69
70
  if match:
70
71
  api_level, abi = match.groups()
@@ -74,7 +75,7 @@ def _android_platforms(arch: str) -> List[str]:
74
75
  return [arch]
75
76
 
76
77
 
77
- def _custom_manylinux_platforms(arch: str) -> List[str]:
78
+ def _custom_manylinux_platforms(arch: str) -> list[str]:
78
79
  arches = [arch]
79
80
  arch_prefix, arch_sep, arch_suffix = arch.partition("_")
80
81
  if arch_prefix == "manylinux2014":
@@ -95,7 +96,7 @@ def _custom_manylinux_platforms(arch: str) -> List[str]:
95
96
  return arches
96
97
 
97
98
 
98
- def _get_custom_platforms(arch: str) -> List[str]:
99
+ def _get_custom_platforms(arch: str) -> list[str]:
99
100
  arch_prefix, arch_sep, arch_suffix = arch.partition("_")
100
101
  if arch.startswith("macosx"):
101
102
  arches = _mac_platforms(arch)
@@ -110,7 +111,7 @@ def _get_custom_platforms(arch: str) -> List[str]:
110
111
  return arches
111
112
 
112
113
 
113
- def _expand_allowed_platforms(platforms: Optional[List[str]]) -> Optional[List[str]]:
114
+ def _expand_allowed_platforms(platforms: list[str] | None) -> list[str] | None:
114
115
  if not platforms:
115
116
  return None
116
117
 
@@ -135,7 +136,7 @@ def _get_python_version(version: str) -> PythonVersion:
135
136
 
136
137
 
137
138
  def _get_custom_interpreter(
138
- implementation: Optional[str] = None, version: Optional[str] = None
139
+ implementation: str | None = None, version: str | None = None
139
140
  ) -> str:
140
141
  if implementation is None:
141
142
  implementation = interpreter_name()
@@ -145,11 +146,11 @@ def _get_custom_interpreter(
145
146
 
146
147
 
147
148
  def get_supported(
148
- version: Optional[str] = None,
149
- platforms: Optional[List[str]] = None,
150
- impl: Optional[str] = None,
151
- abis: Optional[List[str]] = None,
152
- ) -> List[Tag]:
149
+ version: str | None = None,
150
+ platforms: list[str] | None = None,
151
+ impl: str | None = None,
152
+ abis: list[str] | None = None,
153
+ ) -> list[Tag]:
153
154
  """Return a list of supported tags for each version specified in
154
155
  `versions`.
155
156
 
@@ -162,9 +163,9 @@ def get_supported(
162
163
  :param abis: specify a list of abis you want valid
163
164
  tags for, or None. If None, use the local interpreter abi.
164
165
  """
165
- supported: List[Tag] = []
166
+ supported: list[Tag] = []
166
167
 
167
- python_version: Optional[PythonVersion] = None
168
+ python_version: PythonVersion | None = None
168
169
  if version is not None:
169
170
  python_version = _get_python_version(version)
170
171
 
@@ -2,9 +2,11 @@
2
2
  A module that implements tooling to enable easy warnings about deprecations.
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  import logging
6
8
  import warnings
7
- from typing import Any, Optional, TextIO, Type, Union
9
+ from typing import Any, TextIO
8
10
 
9
11
  from pip._vendor.packaging.version import parse
10
12
 
@@ -22,12 +24,12 @@ _original_showwarning: Any = None
22
24
 
23
25
  # Warnings <-> Logging Integration
24
26
  def _showwarning(
25
- message: Union[Warning, str],
26
- category: Type[Warning],
27
+ message: Warning | str,
28
+ category: type[Warning],
27
29
  filename: str,
28
30
  lineno: int,
29
- file: Optional[TextIO] = None,
30
- line: Optional[str] = None,
31
+ file: TextIO | None = None,
32
+ line: str | None = None,
31
33
  ) -> None:
32
34
  if file is not None:
33
35
  if _original_showwarning is not None:
@@ -55,10 +57,10 @@ def install_warning_logger() -> None:
55
57
  def deprecated(
56
58
  *,
57
59
  reason: str,
58
- replacement: Optional[str],
59
- gone_in: Optional[str],
60
- feature_flag: Optional[str] = None,
61
- issue: Optional[int] = None,
60
+ replacement: str | None,
61
+ gone_in: str | None,
62
+ feature_flag: str | None = None,
63
+ issue: int | None = None,
62
64
  ) -> None:
63
65
  """Helper to deprecate existing functionality.
64
66
 
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from __future__ import annotations
2
2
 
3
3
  from pip._internal.models.direct_url import ArchiveInfo, DirectUrl, DirInfo, VcsInfo
4
4
  from pip._internal.models.link import Link
@@ -37,7 +37,7 @@ def direct_url_for_editable(source_dir: str) -> DirectUrl:
37
37
 
38
38
 
39
39
  def direct_url_from_link(
40
- link: Link, source_dir: Optional[str] = None, link_is_in_wheel_cache: bool = False
40
+ link: Link, source_dir: str | None = None, link_is_in_wheel_cache: bool = False
41
41
  ) -> DirectUrl:
42
42
  if link.is_vcs:
43
43
  vcs_backend = vcs.get_backend_for_scheme(link.scheme)
@@ -1,7 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import re
3
5
  import sys
4
- from typing import List, Optional
5
6
 
6
7
  from pip._internal.locations import site_packages, user_site
7
8
  from pip._internal.utils.virtualenv import (
@@ -15,7 +16,7 @@ __all__ = [
15
16
  ]
16
17
 
17
18
 
18
- def _egg_link_names(raw_name: str) -> List[str]:
19
+ def _egg_link_names(raw_name: str) -> list[str]:
19
20
  """
20
21
  Convert a Name metadata value to a .egg-link name, by applying
21
22
  the same substitution as pkg_resources's safe_name function.
@@ -30,7 +31,7 @@ def _egg_link_names(raw_name: str) -> List[str]:
30
31
  ]
31
32
 
32
33
 
33
- def egg_link_path_from_sys_path(raw_name: str) -> Optional[str]:
34
+ def egg_link_path_from_sys_path(raw_name: str) -> str | None:
34
35
  """
35
36
  Look for a .egg-link file for project name, by walking sys.path.
36
37
  """
@@ -43,7 +44,7 @@ def egg_link_path_from_sys_path(raw_name: str) -> Optional[str]:
43
44
  return None
44
45
 
45
46
 
46
- def egg_link_path_from_location(raw_name: str) -> Optional[str]:
47
+ def egg_link_path_from_location(raw_name: str) -> str | None:
47
48
  """
48
49
  Return the path for the .egg-link file if it exists, otherwise, None.
49
50
 
@@ -61,7 +62,7 @@ def egg_link_path_from_location(raw_name: str) -> Optional[str]:
61
62
 
62
63
  This method will just return the first one found.
63
64
  """
64
- sites: List[str] = []
65
+ sites: list[str] = []
65
66
  if running_under_virtualenv():
66
67
  sites.append(site_packages)
67
68
  if not virtualenv_no_global() and user_site:
@@ -1,8 +1,9 @@
1
+ from __future__ import annotations
2
+
1
3
  import itertools
2
4
  import os
3
5
  import shutil
4
6
  import sys
5
- from typing import List, Optional
6
7
 
7
8
  from pip._internal.cli.main import main
8
9
  from pip._internal.utils.compat import WINDOWS
@@ -20,7 +21,7 @@ if WINDOWS:
20
21
  ]
21
22
 
22
23
 
23
- def _wrapper(args: Optional[List[str]] = None) -> int:
24
+ def _wrapper(args: list[str] | None = None) -> int:
24
25
  """Central wrapper for all old entrypoints.
25
26
 
26
27
  Historically pip has had several entrypoints defined. Because of issues
@@ -1,11 +1,14 @@
1
+ from __future__ import annotations
2
+
1
3
  import fnmatch
2
4
  import os
3
5
  import os.path
4
6
  import random
5
7
  import sys
8
+ from collections.abc import Generator
6
9
  from contextlib import contextmanager
7
10
  from tempfile import NamedTemporaryFile
8
- from typing import Any, BinaryIO, Generator, List, Union, cast
11
+ from typing import Any, BinaryIO, cast
9
12
 
10
13
  from pip._internal.utils.compat import get_path_uid
11
14
  from pip._internal.utils.misc import format_size
@@ -115,17 +118,17 @@ def _test_writable_dir_win(path: str) -> bool:
115
118
  raise OSError("Unexpected condition testing for writable directory")
116
119
 
117
120
 
118
- def find_files(path: str, pattern: str) -> List[str]:
121
+ def find_files(path: str, pattern: str) -> list[str]:
119
122
  """Returns a list of absolute paths of files beneath path, recursively,
120
123
  with filenames which match the UNIX-style shell glob pattern."""
121
- result: List[str] = []
124
+ result: list[str] = []
122
125
  for root, _, files in os.walk(path):
123
126
  matches = fnmatch.filter(files, pattern)
124
127
  result.extend(os.path.join(root, f) for f in matches)
125
128
  return result
126
129
 
127
130
 
128
- def file_size(path: str) -> Union[int, float]:
131
+ def file_size(path: str) -> int | float:
129
132
  # If it's a symlink, return 0.
130
133
  if os.path.islink(path):
131
134
  return 0
@@ -136,7 +139,7 @@ def format_file_size(path: str) -> str:
136
139
  return format_size(file_size(path))
137
140
 
138
141
 
139
- def directory_size(path: str) -> Union[int, float]:
142
+ def directory_size(path: str) -> int | float:
140
143
  size = 0.0
141
144
  for root, _dirs, files in os.walk(path):
142
145
  for filename in files:
@@ -147,3 +150,15 @@ def directory_size(path: str) -> Union[int, float]:
147
150
 
148
151
  def format_directory_size(path: str) -> str:
149
152
  return format_size(directory_size(path))
153
+
154
+
155
+ def copy_directory_permissions(directory: str, target_file: BinaryIO) -> None:
156
+ mode = (
157
+ os.stat(directory).st_mode & 0o666 # select read/write permissions of directory
158
+ | 0o600 # set owner read/write permissions
159
+ )
160
+ # Change permissions only if there is no risk of following a symlink.
161
+ if os.chmod in os.supports_fd:
162
+ os.chmod(target_file.fileno(), mode)
163
+ elif os.chmod in os.supports_follow_symlinks:
164
+ os.chmod(target_file.name, mode, follow_symlinks=False)
@@ -1,20 +1,18 @@
1
1
  """Filetype information."""
2
2
 
3
- from typing import Tuple
4
-
5
3
  from pip._internal.utils.misc import splitext
6
4
 
7
5
  WHEEL_EXTENSION = ".whl"
8
- BZ2_EXTENSIONS: Tuple[str, ...] = (".tar.bz2", ".tbz")
9
- XZ_EXTENSIONS: Tuple[str, ...] = (
6
+ BZ2_EXTENSIONS: tuple[str, ...] = (".tar.bz2", ".tbz")
7
+ XZ_EXTENSIONS: tuple[str, ...] = (
10
8
  ".tar.xz",
11
9
  ".txz",
12
10
  ".tlz",
13
11
  ".tar.lz",
14
12
  ".tar.lzma",
15
13
  )
16
- ZIP_EXTENSIONS: Tuple[str, ...] = (".zip", WHEEL_EXTENSION)
17
- TAR_EXTENSIONS: Tuple[str, ...] = (".tar.gz", ".tgz", ".tar")
14
+ ZIP_EXTENSIONS: tuple[str, ...] = (".zip", WHEEL_EXTENSION)
15
+ TAR_EXTENSIONS: tuple[str, ...] = (".tar.gz", ".tgz", ".tar")
18
16
  ARCHIVE_EXTENSIONS = ZIP_EXTENSIONS + BZ2_EXTENSIONS + TAR_EXTENSIONS + XZ_EXTENSIONS
19
17
 
20
18
 
@@ -1,14 +1,15 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
4
  import sys
3
- from typing import Optional, Tuple
4
5
 
5
6
 
6
- def glibc_version_string() -> Optional[str]:
7
+ def glibc_version_string() -> str | None:
7
8
  "Returns glibc version string, or None if not using glibc."
8
9
  return glibc_version_string_confstr() or glibc_version_string_ctypes()
9
10
 
10
11
 
11
- def glibc_version_string_confstr() -> Optional[str]:
12
+ def glibc_version_string_confstr() -> str | None:
12
13
  "Primary implementation of glibc_version_string using os.confstr."
13
14
  # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
14
15
  # to be broken or missing. This strategy is used in the standard library
@@ -28,7 +29,7 @@ def glibc_version_string_confstr() -> Optional[str]:
28
29
  return version
29
30
 
30
31
 
31
- def glibc_version_string_ctypes() -> Optional[str]:
32
+ def glibc_version_string_ctypes() -> str | None:
32
33
  "Fallback implementation of glibc_version_string using ctypes."
33
34
 
34
35
  try:
@@ -88,7 +89,7 @@ def glibc_version_string_ctypes() -> Optional[str]:
88
89
  # versions that was generated by pip 8.1.2 and earlier is useless and
89
90
  # misleading. Solution: instead of using platform, use our code that actually
90
91
  # works.
91
- def libc_ver() -> Tuple[str, str]:
92
+ def libc_ver() -> tuple[str, str]:
92
93
  """Try to determine the glibc version
93
94
 
94
95
  Returns a tuple of strings (lib, version) which default to empty strings