pip 25.2__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 (114) hide show
  1. pip/__init__.py +1 -1
  2. pip/_internal/__init__.py +0 -0
  3. pip/_internal/build_env.py +71 -3
  4. pip/_internal/cache.py +1 -1
  5. pip/_internal/cli/cmdoptions.py +43 -71
  6. pip/_internal/cli/parser.py +3 -3
  7. pip/_internal/cli/req_command.py +46 -26
  8. pip/_internal/commands/download.py +4 -7
  9. pip/_internal/commands/install.py +19 -14
  10. pip/_internal/commands/lock.py +3 -6
  11. pip/_internal/commands/wheel.py +5 -10
  12. pip/_internal/configuration.py +1 -2
  13. pip/_internal/distributions/sdist.py +13 -14
  14. pip/_internal/exceptions.py +20 -3
  15. pip/_internal/index/package_finder.py +3 -3
  16. pip/_internal/metadata/__init__.py +7 -2
  17. pip/_internal/metadata/importlib/_dists.py +8 -2
  18. pip/_internal/models/link.py +1 -1
  19. pip/_internal/models/wheel.py +5 -66
  20. pip/_internal/network/cache.py +6 -11
  21. pip/_internal/network/lazy_wheel.py +5 -3
  22. pip/_internal/operations/build/wheel.py +4 -4
  23. pip/_internal/operations/build/wheel_editable.py +4 -4
  24. pip/_internal/operations/prepare.py +7 -1
  25. pip/_internal/pyproject.py +2 -61
  26. pip/_internal/req/__init__.py +1 -3
  27. pip/_internal/req/constructors.py +42 -38
  28. pip/_internal/req/req_file.py +0 -1
  29. pip/_internal/req/req_install.py +32 -141
  30. pip/_internal/resolution/resolvelib/candidates.py +20 -11
  31. pip/_internal/resolution/resolvelib/factory.py +31 -0
  32. pip/_internal/resolution/resolvelib/provider.py +9 -0
  33. pip/_internal/resolution/resolvelib/reporter.py +21 -8
  34. pip/_internal/resolution/resolvelib/resolver.py +2 -6
  35. pip/_internal/self_outdated_check.py +11 -3
  36. pip/_internal/utils/filesystem.py +12 -0
  37. pip/_internal/utils/unpacking.py +25 -0
  38. pip/_internal/wheel_builder.py +23 -96
  39. pip/_vendor/README.rst +180 -0
  40. pip/_vendor/cachecontrol/LICENSE.txt +13 -0
  41. pip/_vendor/certifi/LICENSE +20 -0
  42. pip/_vendor/certifi/__init__.py +1 -1
  43. pip/_vendor/certifi/cacert.pem +62 -40
  44. pip/_vendor/dependency_groups/LICENSE.txt +9 -0
  45. pip/_vendor/distlib/LICENSE.txt +284 -0
  46. pip/_vendor/distro/LICENSE +202 -0
  47. pip/_vendor/idna/LICENSE.md +31 -0
  48. pip/_vendor/msgpack/COPYING +14 -0
  49. pip/_vendor/msgpack/__init__.py +2 -2
  50. pip/_vendor/packaging/LICENSE +3 -0
  51. pip/_vendor/packaging/LICENSE.APACHE +177 -0
  52. pip/_vendor/packaging/LICENSE.BSD +23 -0
  53. pip/_vendor/pkg_resources/LICENSE +17 -0
  54. pip/_vendor/platformdirs/LICENSE +21 -0
  55. pip/_vendor/platformdirs/api.py +1 -1
  56. pip/_vendor/platformdirs/macos.py +10 -8
  57. pip/_vendor/platformdirs/version.py +16 -3
  58. pip/_vendor/pygments/LICENSE +25 -0
  59. pip/_vendor/pyproject_hooks/LICENSE +21 -0
  60. pip/_vendor/requests/LICENSE +175 -0
  61. pip/_vendor/requests/__version__.py +2 -2
  62. pip/_vendor/requests/adapters.py +17 -40
  63. pip/_vendor/requests/sessions.py +1 -1
  64. pip/_vendor/resolvelib/LICENSE +13 -0
  65. pip/_vendor/resolvelib/__init__.py +1 -1
  66. pip/_vendor/resolvelib/resolvers/abstract.py +3 -3
  67. pip/_vendor/resolvelib/resolvers/resolution.py +5 -0
  68. pip/_vendor/rich/LICENSE +19 -0
  69. pip/_vendor/rich/style.py +7 -11
  70. pip/_vendor/tomli/LICENSE +21 -0
  71. pip/_vendor/tomli/__init__.py +1 -1
  72. pip/_vendor/tomli/_parser.py +28 -21
  73. pip/_vendor/tomli/_re.py +8 -5
  74. pip/_vendor/tomli_w/LICENSE +21 -0
  75. pip/_vendor/truststore/LICENSE +21 -0
  76. pip/_vendor/truststore/__init__.py +1 -1
  77. pip/_vendor/truststore/_api.py +14 -6
  78. pip/_vendor/truststore/_openssl.py +3 -1
  79. pip/_vendor/urllib3/LICENSE.txt +21 -0
  80. pip/_vendor/vendor.txt +8 -8
  81. {pip-25.2.dist-info → pip-25.3.dist-info}/METADATA +9 -10
  82. {pip-25.2.dist-info → pip-25.3.dist-info}/RECORD +106 -90
  83. {pip-25.2.dist-info → pip-25.3.dist-info}/WHEEL +1 -2
  84. pip-25.3.dist-info/entry_points.txt +4 -0
  85. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/AUTHORS.txt +9 -0
  86. pip/_internal/operations/build/metadata_legacy.py +0 -73
  87. pip/_internal/operations/build/wheel_legacy.py +0 -119
  88. pip/_internal/operations/install/editable_legacy.py +0 -48
  89. pip/_internal/utils/setuptools_build.py +0 -149
  90. pip-25.2.dist-info/entry_points.txt +0 -3
  91. pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER +0 -3
  92. pip-25.2.dist-info/top_level.txt +0 -1
  93. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/LICENSE.txt +0 -0
  94. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt +0 -0
  95. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/certifi/LICENSE +0 -0
  96. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt +0 -0
  97. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/distlib/LICENSE.txt +0 -0
  98. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/distro/LICENSE +0 -0
  99. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/idna/LICENSE.md +0 -0
  100. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/msgpack/COPYING +0 -0
  101. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE +0 -0
  102. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.APACHE +0 -0
  103. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/packaging/LICENSE.BSD +0 -0
  104. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/pkg_resources/LICENSE +0 -0
  105. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/platformdirs/LICENSE +0 -0
  106. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/pygments/LICENSE +0 -0
  107. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/pyproject_hooks/LICENSE +0 -0
  108. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/requests/LICENSE +0 -0
  109. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/resolvelib/LICENSE +0 -0
  110. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/rich/LICENSE +0 -0
  111. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/tomli/LICENSE +0 -0
  112. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/tomli_w/LICENSE +0 -0
  113. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/truststore/LICENSE +0 -0
  114. {pip-25.2.dist-info → pip-25.3.dist-info}/licenses/src/pip/_vendor/urllib3/LICENSE.txt +0 -0
@@ -11,9 +11,6 @@ from pip._internal.cli.req_command import (
11
11
  from pip._internal.cli.status_codes import SUCCESS
12
12
  from pip._internal.models.pylock import Pylock, is_valid_pylock_file_name
13
13
  from pip._internal.operations.build.build_tracker import get_build_tracker
14
- from pip._internal.req.req_install import (
15
- check_legacy_setup_py_options,
16
- )
17
14
  from pip._internal.utils.logging import getLogger
18
15
  from pip._internal.utils.misc import (
19
16
  get_pip_version,
@@ -59,6 +56,7 @@ class LockCommand(RequirementCommand):
59
56
  )
60
57
  self.cmd_opts.add_option(cmdoptions.requirements())
61
58
  self.cmd_opts.add_option(cmdoptions.constraints())
59
+ self.cmd_opts.add_option(cmdoptions.build_constraints())
62
60
  self.cmd_opts.add_option(cmdoptions.no_deps())
63
61
  self.cmd_opts.add_option(cmdoptions.pre())
64
62
 
@@ -69,7 +67,6 @@ class LockCommand(RequirementCommand):
69
67
  self.cmd_opts.add_option(cmdoptions.ignore_requires_python())
70
68
  self.cmd_opts.add_option(cmdoptions.no_build_isolation())
71
69
  self.cmd_opts.add_option(cmdoptions.use_pep517())
72
- self.cmd_opts.add_option(cmdoptions.no_use_pep517())
73
70
  self.cmd_opts.add_option(cmdoptions.check_build_deps())
74
71
 
75
72
  self.cmd_opts.add_option(cmdoptions.config_settings())
@@ -98,6 +95,8 @@ class LockCommand(RequirementCommand):
98
95
  "without prior warning."
99
96
  )
100
97
 
98
+ cmdoptions.check_build_constraints(options)
99
+
101
100
  session = self.get_default_session(options)
102
101
 
103
102
  finder = self._build_package_finder(
@@ -114,7 +113,6 @@ class LockCommand(RequirementCommand):
114
113
  )
115
114
 
116
115
  reqs = self.get_requirements(args, options, finder, session)
117
- check_legacy_setup_py_options(options, reqs)
118
116
 
119
117
  wheel_cache = WheelCache(options.cache_dir)
120
118
 
@@ -142,7 +140,6 @@ class LockCommand(RequirementCommand):
142
140
  ignore_installed=True,
143
141
  ignore_requires_python=options.ignore_requires_python,
144
142
  upgrade_strategy="to-satisfy-only",
145
- use_pep517=options.use_pep517,
146
143
  )
147
144
 
148
145
  self.trace_basic_info(finder)
@@ -11,7 +11,6 @@ from pip._internal.exceptions import CommandError
11
11
  from pip._internal.operations.build.build_tracker import get_build_tracker
12
12
  from pip._internal.req.req_install import (
13
13
  InstallRequirement,
14
- check_legacy_setup_py_options,
15
14
  )
16
15
  from pip._internal.utils.misc import ensure_dir, normalize_path
17
16
  from pip._internal.utils.temp_dir import TempDirectory
@@ -57,9 +56,9 @@ class WheelCommand(RequirementCommand):
57
56
  self.cmd_opts.add_option(cmdoptions.prefer_binary())
58
57
  self.cmd_opts.add_option(cmdoptions.no_build_isolation())
59
58
  self.cmd_opts.add_option(cmdoptions.use_pep517())
60
- self.cmd_opts.add_option(cmdoptions.no_use_pep517())
61
59
  self.cmd_opts.add_option(cmdoptions.check_build_deps())
62
60
  self.cmd_opts.add_option(cmdoptions.constraints())
61
+ self.cmd_opts.add_option(cmdoptions.build_constraints())
63
62
  self.cmd_opts.add_option(cmdoptions.editable())
64
63
  self.cmd_opts.add_option(cmdoptions.requirements())
65
64
  self.cmd_opts.add_option(cmdoptions.src())
@@ -76,8 +75,6 @@ class WheelCommand(RequirementCommand):
76
75
  )
77
76
 
78
77
  self.cmd_opts.add_option(cmdoptions.config_settings())
79
- self.cmd_opts.add_option(cmdoptions.build_options())
80
- self.cmd_opts.add_option(cmdoptions.global_options())
81
78
 
82
79
  self.cmd_opts.add_option(
83
80
  "--pre",
@@ -101,6 +98,8 @@ class WheelCommand(RequirementCommand):
101
98
 
102
99
  @with_cleanup
103
100
  def run(self, options: Values, args: list[str]) -> int:
101
+ cmdoptions.check_build_constraints(options)
102
+
104
103
  session = self.get_default_session(options)
105
104
 
106
105
  finder = self._build_package_finder(options, session)
@@ -117,7 +116,6 @@ class WheelCommand(RequirementCommand):
117
116
  )
118
117
 
119
118
  reqs = self.get_requirements(args, options, finder, session)
120
- check_legacy_setup_py_options(options, reqs)
121
119
 
122
120
  wheel_cache = WheelCache(options.cache_dir)
123
121
 
@@ -138,13 +136,14 @@ class WheelCommand(RequirementCommand):
138
136
  options=options,
139
137
  wheel_cache=wheel_cache,
140
138
  ignore_requires_python=options.ignore_requires_python,
141
- use_pep517=options.use_pep517,
142
139
  )
143
140
 
144
141
  self.trace_basic_info(finder)
145
142
 
146
143
  requirement_set = resolver.resolve(reqs, check_supported_wheels=True)
147
144
 
145
+ preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
146
+
148
147
  reqs_to_build: list[InstallRequirement] = []
149
148
  for req in requirement_set.requirements.values():
150
149
  if req.is_wheel:
@@ -152,15 +151,11 @@ class WheelCommand(RequirementCommand):
152
151
  else:
153
152
  reqs_to_build.append(req)
154
153
 
155
- preparer.prepare_linked_requirements_more(requirement_set.requirements.values())
156
-
157
154
  # build wheels
158
155
  build_successes, build_failures = build(
159
156
  reqs_to_build,
160
157
  wheel_cache=wheel_cache,
161
158
  verify=(not options.no_verify),
162
- build_options=options.build_options or [],
163
- global_options=options.global_options or [],
164
159
  )
165
160
  for req in build_successes:
166
161
  assert req.link and req.link.is_wheel
@@ -53,8 +53,7 @@ logger = getLogger(__name__)
53
53
  def _normalize_name(name: str) -> str:
54
54
  """Make a name consistent regardless of source (environment or file)"""
55
55
  name = name.lower().replace("_", "-")
56
- if name.startswith("--"):
57
- name = name[2:] # only prefer long opts
56
+ name = name.removeprefix("--") # only prefer long opts
58
57
  return name
59
58
 
60
59
 
@@ -20,7 +20,7 @@ class SourceDistribution(AbstractDistribution):
20
20
  """Represents a source distribution.
21
21
 
22
22
  The preparation step for these needs metadata for the packages to be
23
- generated, either using PEP 517 or using the legacy `setup.py egg_info`.
23
+ generated.
24
24
  """
25
25
 
26
26
  @property
@@ -38,28 +38,27 @@ class SourceDistribution(AbstractDistribution):
38
38
  build_isolation: bool,
39
39
  check_build_deps: bool,
40
40
  ) -> None:
41
- # Load pyproject.toml, to determine whether PEP 517 is to be used
41
+ # Load pyproject.toml
42
42
  self.req.load_pyproject_toml()
43
43
 
44
44
  # Set up the build isolation, if this requirement should be isolated
45
- should_isolate = self.req.use_pep517 and build_isolation
46
- if should_isolate:
45
+ if build_isolation:
47
46
  # Setup an isolated environment and install the build backend static
48
47
  # requirements in it.
49
48
  self._prepare_build_backend(build_env_installer)
50
- # Check that if the requirement is editable, it either supports PEP 660 or
51
- # has a setup.py or a setup.cfg. This cannot be done earlier because we need
52
- # to setup the build backend to verify it supports build_editable, nor can
53
- # it be done later, because we want to avoid installing build requirements
54
- # needlessly. Doing it here also works around setuptools generating
55
- # UNKNOWN.egg-info when running get_requires_for_build_wheel on a directory
56
- # without setup.py nor setup.cfg.
57
- self.req.isolated_editable_sanity_check()
49
+ # Check that the build backend supports PEP 660. This cannot be done
50
+ # earlier because we need to setup the build backend to verify it
51
+ # supports build_editable, nor can it be done later, because we want
52
+ # to avoid installing build requirements needlessly.
53
+ self.req.editable_sanity_check()
58
54
  # Install the dynamic build requirements.
59
55
  self._install_build_reqs(build_env_installer)
56
+ else:
57
+ # When not using build isolation, we still need to check that
58
+ # the build backend supports PEP 660.
59
+ self.req.editable_sanity_check()
60
60
  # Check if the current environment provides build dependencies
61
- should_check_deps = self.req.use_pep517 and check_build_deps
62
- if should_check_deps:
61
+ if check_build_deps:
63
62
  pyproject_requires = self.req.pyproject_requires
64
63
  assert pyproject_requires is not None
65
64
  conflicting, missing = self.req.build_env.check_requirements(
@@ -190,6 +190,23 @@ class InstallationError(PipError):
190
190
  """General exception during installation"""
191
191
 
192
192
 
193
+ class FailedToPrepareCandidate(InstallationError):
194
+ """Raised when we fail to prepare a candidate (i.e. fetch and generate metadata).
195
+
196
+ This is intentionally not a diagnostic error, since the output will be presented
197
+ above this error, when this occurs. This should instead present information to the
198
+ user.
199
+ """
200
+
201
+ def __init__(
202
+ self, *, package_name: str, requirement_chain: str, failed_step: str
203
+ ) -> None:
204
+ super().__init__(f"Failed to build '{package_name}' when {failed_step.lower()}")
205
+ self.package_name = package_name
206
+ self.requirement_chain = requirement_chain
207
+ self.failed_step = failed_step
208
+
209
+
193
210
  class MissingPyProjectBuildRequires(DiagnosticPipError):
194
211
  """Raised when pyproject.toml has `build-system`, but no `build-system.requires`."""
195
212
 
@@ -384,7 +401,7 @@ class InstallationSubprocessError(DiagnosticPipError, InstallationError):
384
401
  output_lines: list[str] | None,
385
402
  ) -> None:
386
403
  if output_lines is None:
387
- output_prompt = Text("See above for output.")
404
+ output_prompt = Text("No available output.")
388
405
  else:
389
406
  output_prompt = (
390
407
  Text.from_markup(f"[red][{len(output_lines)} lines of output][/]\n")
@@ -412,7 +429,7 @@ class InstallationSubprocessError(DiagnosticPipError, InstallationError):
412
429
  return f"{self.command_description} exited with {self.exit_code}"
413
430
 
414
431
 
415
- class MetadataGenerationFailed(InstallationSubprocessError, InstallationError):
432
+ class MetadataGenerationFailed(DiagnosticPipError, InstallationError):
416
433
  reference = "metadata-generation-failed"
417
434
 
418
435
  def __init__(
@@ -420,7 +437,7 @@ class MetadataGenerationFailed(InstallationSubprocessError, InstallationError):
420
437
  *,
421
438
  package_details: str,
422
439
  ) -> None:
423
- super(InstallationSubprocessError, self).__init__(
440
+ super().__init__(
424
441
  message="Encountered error while generating package metadata.",
425
442
  context=escape(package_details),
426
443
  hint_stmt="See above for details.",
@@ -17,7 +17,7 @@ from typing import (
17
17
 
18
18
  from pip._vendor.packaging import specifiers
19
19
  from pip._vendor.packaging.tags import Tag
20
- from pip._vendor.packaging.utils import canonicalize_name
20
+ from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
21
21
  from pip._vendor.packaging.version import InvalidVersion, _BaseVersion
22
22
  from pip._vendor.packaging.version import parse as parse_version
23
23
 
@@ -127,7 +127,7 @@ class LinkEvaluator:
127
127
  def __init__(
128
128
  self,
129
129
  project_name: str,
130
- canonical_name: str,
130
+ canonical_name: NormalizedName,
131
131
  formats: frozenset[str],
132
132
  target_python: TargetPython,
133
133
  allow_yanked: bool,
@@ -201,7 +201,7 @@ class LinkEvaluator:
201
201
  LinkType.format_invalid,
202
202
  "invalid wheel filename",
203
203
  )
204
- if canonicalize_name(wheel.name) != self._canonical_name:
204
+ if wheel.name != self._canonical_name:
205
205
  reason = f"wrong project name (not {self.project_name})"
206
206
  return (LinkType.different_project, reason)
207
207
 
@@ -4,13 +4,16 @@ import contextlib
4
4
  import functools
5
5
  import os
6
6
  import sys
7
- from typing import Literal, Protocol, cast
7
+ from typing import TYPE_CHECKING, Literal, Protocol, cast
8
8
 
9
9
  from pip._internal.utils.deprecation import deprecated
10
10
  from pip._internal.utils.misc import strtobool
11
11
 
12
12
  from .base import BaseDistribution, BaseEnvironment, FilesystemWheel, MemoryWheel, Wheel
13
13
 
14
+ if TYPE_CHECKING:
15
+ from pip._vendor.packaging.utils import NormalizedName
16
+
14
17
  __all__ = [
15
18
  "BaseDistribution",
16
19
  "BaseEnvironment",
@@ -131,7 +134,9 @@ def get_directory_distribution(directory: str) -> BaseDistribution:
131
134
  return select_backend().Distribution.from_directory(directory)
132
135
 
133
136
 
134
- def get_wheel_distribution(wheel: Wheel, canonical_name: str) -> BaseDistribution:
137
+ def get_wheel_distribution(
138
+ wheel: Wheel, canonical_name: NormalizedName
139
+ ) -> BaseDistribution:
135
140
  """Get the representation of the specified wheel's distribution metadata.
136
141
 
137
142
  This returns a Distribution instance from the chosen backend based on
@@ -28,6 +28,7 @@ from pip._internal.utils.temp_dir import TempDirectory
28
28
  from pip._internal.utils.wheel import parse_wheel, read_wheel_metadata_file
29
29
 
30
30
  from ._compat import (
31
+ BadMetadata,
31
32
  BasePath,
32
33
  get_dist_canonical_name,
33
34
  parse_name_and_version_from_info_directory,
@@ -165,9 +166,14 @@ class Distribution(BaseDistribution):
165
166
 
166
167
  @property
167
168
  def version(self) -> Version:
168
- 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
+ )
169
174
  return parse_version(version)
170
- return parse_version(self._dist.version)
175
+ except TypeError:
176
+ raise BadMetadata(self._dist, reason="invalid metadata entry `version`")
171
177
 
172
178
  @property
173
179
  def raw_version(self) -> str:
@@ -477,7 +477,7 @@ class Link:
477
477
  deprecated(
478
478
  reason=f"{self} contains an egg fragment with a non-PEP 508 name.",
479
479
  replacement="to use the req @ url syntax, and remove the egg fragment",
480
- gone_in="25.3",
480
+ gone_in="26.0",
481
481
  issue=13157,
482
482
  )
483
483
 
@@ -4,91 +4,30 @@ name that have meaning.
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- import re
8
7
  from collections.abc import Iterable
9
8
 
10
9
  from pip._vendor.packaging.tags import Tag
11
- from pip._vendor.packaging.utils import BuildTag, parse_wheel_filename
12
10
  from pip._vendor.packaging.utils import (
13
11
  InvalidWheelFilename as _PackagingInvalidWheelFilename,
14
12
  )
13
+ from pip._vendor.packaging.utils import parse_wheel_filename
15
14
 
16
15
  from pip._internal.exceptions import InvalidWheelFilename
17
- from pip._internal.utils.deprecation import deprecated
18
16
 
19
17
 
20
18
  class Wheel:
21
19
  """A wheel file"""
22
20
 
23
- legacy_wheel_file_re = re.compile(
24
- r"""^(?P<namever>(?P<name>[^\s-]+?)-(?P<ver>[^\s-]*?))
25
- ((-(?P<build>\d[^-]*?))?-(?P<pyver>[^\s-]+?)-(?P<abi>[^\s-]+?)-(?P<plat>[^\s-]+?)
26
- \.whl|\.dist-info)$""",
27
- re.VERBOSE,
28
- )
29
-
30
21
  def __init__(self, filename: str) -> None:
31
22
  self.filename = filename
32
23
 
33
- # To make mypy happy specify type hints that can come from either
34
- # parse_wheel_filename or the legacy_wheel_file_re match.
35
- self.name: str
36
- self._build_tag: BuildTag | None = None
37
-
38
24
  try:
39
25
  wheel_info = parse_wheel_filename(filename)
40
- self.name, _version, self._build_tag, self.file_tags = wheel_info
41
- self.version = str(_version)
42
26
  except _PackagingInvalidWheelFilename as e:
43
- # Check if the wheel filename is in the legacy format
44
- legacy_wheel_info = self.legacy_wheel_file_re.match(filename)
45
- if not legacy_wheel_info:
46
- raise InvalidWheelFilename(e.args[0]) from None
47
-
48
- deprecated(
49
- reason=(
50
- f"Wheel filename {filename!r} is not correctly normalised. "
51
- "Future versions of pip will raise the following error:\n"
52
- f"{e.args[0]}\n\n"
53
- ),
54
- replacement=(
55
- "to rename the wheel to use a correctly normalised "
56
- "name (this may require updating the version in "
57
- "the project metadata)"
58
- ),
59
- gone_in="25.3",
60
- issue=12938,
61
- )
62
-
63
- self.name = legacy_wheel_info.group("name").replace("_", "-")
64
- self.version = legacy_wheel_info.group("ver").replace("_", "-")
65
-
66
- # Generate the file tags from the legacy wheel filename
67
- pyversions = legacy_wheel_info.group("pyver").split(".")
68
- abis = legacy_wheel_info.group("abi").split(".")
69
- plats = legacy_wheel_info.group("plat").split(".")
70
- self.file_tags = frozenset(
71
- Tag(interpreter=py, abi=abi, platform=plat)
72
- for py in pyversions
73
- for abi in abis
74
- for plat in plats
75
- )
76
-
77
- @property
78
- def build_tag(self) -> BuildTag:
79
- if self._build_tag is not None:
80
- return self._build_tag
81
-
82
- # Parse the build tag from the legacy wheel filename
83
- legacy_wheel_info = self.legacy_wheel_file_re.match(self.filename)
84
- assert legacy_wheel_info is not None, "guaranteed by filename validation"
85
- build_tag = legacy_wheel_info.group("build")
86
- match = re.match(r"^(\d+)(.*)$", build_tag)
87
- assert match is not None, "guaranteed by filename validation"
88
- build_tag_groups = match.groups()
89
- self._build_tag = (int(build_tag_groups[0]), build_tag_groups[1])
90
-
91
- return self._build_tag
27
+ raise InvalidWheelFilename(e.args[0]) from None
28
+
29
+ self.name, _version, self.build_tag, self.file_tags = wheel_info
30
+ self.version = str(_version)
92
31
 
93
32
  def get_formatted_file_tags(self) -> list[str]:
94
33
  """Return the wheel's tags as a sorted list of strings."""
@@ -13,7 +13,11 @@ from pip._vendor.cachecontrol.cache import SeparateBodyBaseCache
13
13
  from pip._vendor.cachecontrol.caches import SeparateBodyFileCache
14
14
  from pip._vendor.requests.models import Response
15
15
 
16
- from pip._internal.utils.filesystem import adjacent_tmp_file, replace
16
+ from pip._internal.utils.filesystem import (
17
+ adjacent_tmp_file,
18
+ copy_directory_permissions,
19
+ replace,
20
+ )
17
21
  from pip._internal.utils.misc import ensure_dir
18
22
 
19
23
 
@@ -82,16 +86,7 @@ class SafeFileCache(SeparateBodyBaseCache):
82
86
  writer_func(f)
83
87
  # Inherit the read/write permissions of the cache directory
84
88
  # to enable multi-user cache use-cases.
85
- mode = (
86
- os.stat(self.directory).st_mode
87
- & 0o666 # select read/write permissions of cache directory
88
- | 0o600 # set owner read/write permissions
89
- )
90
- # Change permissions only if there is no risk of following a symlink.
91
- if os.chmod in os.supports_fd:
92
- os.chmod(f.fileno(), mode)
93
- elif os.chmod in os.supports_follow_symlinks:
94
- os.chmod(f.name, mode, follow_symlinks=False)
89
+ copy_directory_permissions(self.directory, f)
95
90
 
96
91
  replace(f.name, path)
97
92
 
@@ -11,7 +11,7 @@ from tempfile import NamedTemporaryFile
11
11
  from typing import Any
12
12
  from zipfile import BadZipFile, ZipFile
13
13
 
14
- from pip._vendor.packaging.utils import canonicalize_name
14
+ from pip._vendor.packaging.utils import NormalizedName
15
15
  from pip._vendor.requests.models import CONTENT_CHUNK_SIZE, Response
16
16
 
17
17
  from pip._internal.metadata import BaseDistribution, MemoryWheel, get_wheel_distribution
@@ -23,7 +23,9 @@ class HTTPRangeRequestUnsupported(Exception):
23
23
  pass
24
24
 
25
25
 
26
- def dist_from_wheel_url(name: str, url: str, session: PipSession) -> BaseDistribution:
26
+ def dist_from_wheel_url(
27
+ name: NormalizedName, url: str, session: PipSession
28
+ ) -> BaseDistribution:
27
29
  """Return a distribution object from the given wheel URL.
28
30
 
29
31
  This uses HTTP range requests to only fetch the portion of the wheel
@@ -37,7 +39,7 @@ def dist_from_wheel_url(name: str, url: str, session: PipSession) -> BaseDistrib
37
39
  wheel = MemoryWheel(zf.name, zf) # type: ignore
38
40
  # After context manager exit, wheel.name
39
41
  # is an invalid file by intention.
40
- return get_wheel_distribution(wheel, canonicalize_name(name))
42
+ return get_wheel_distribution(wheel, name)
41
43
 
42
44
 
43
45
  class LazyZipOverHTTP:
@@ -14,7 +14,7 @@ def build_wheel_pep517(
14
14
  name: str,
15
15
  backend: BuildBackendHookCaller,
16
16
  metadata_directory: str,
17
- tempd: str,
17
+ wheel_directory: str,
18
18
  ) -> str | None:
19
19
  """Build one InstallRequirement using the PEP 517 build process.
20
20
 
@@ -22,17 +22,17 @@ def build_wheel_pep517(
22
22
  """
23
23
  assert metadata_directory is not None
24
24
  try:
25
- logger.debug("Destination directory: %s", tempd)
25
+ logger.debug("Destination directory: %s", wheel_directory)
26
26
 
27
27
  runner = runner_with_spinner_message(
28
28
  f"Building wheel for {name} (pyproject.toml)"
29
29
  )
30
30
  with backend.subprocess_runner(runner):
31
31
  wheel_name = backend.build_wheel(
32
- tempd,
32
+ wheel_directory=wheel_directory,
33
33
  metadata_directory=metadata_directory,
34
34
  )
35
35
  except Exception:
36
36
  logger.error("Failed building wheel for %s", name)
37
37
  return None
38
- return os.path.join(tempd, wheel_name)
38
+ return os.path.join(wheel_directory, wheel_name)
@@ -14,7 +14,7 @@ def build_wheel_editable(
14
14
  name: str,
15
15
  backend: BuildBackendHookCaller,
16
16
  metadata_directory: str,
17
- tempd: str,
17
+ wheel_directory: str,
18
18
  ) -> str | None:
19
19
  """Build one InstallRequirement using the PEP 660 build process.
20
20
 
@@ -22,7 +22,7 @@ def build_wheel_editable(
22
22
  """
23
23
  assert metadata_directory is not None
24
24
  try:
25
- logger.debug("Destination directory: %s", tempd)
25
+ logger.debug("Destination directory: %s", wheel_directory)
26
26
 
27
27
  runner = runner_with_spinner_message(
28
28
  f"Building editable for {name} (pyproject.toml)"
@@ -30,7 +30,7 @@ def build_wheel_editable(
30
30
  with backend.subprocess_runner(runner):
31
31
  try:
32
32
  wheel_name = backend.build_editable(
33
- tempd,
33
+ wheel_directory=wheel_directory,
34
34
  metadata_directory=metadata_directory,
35
35
  )
36
36
  except HookMissing as e:
@@ -44,4 +44,4 @@ def build_wheel_editable(
44
44
  except Exception:
45
45
  logger.error("Failed building editable for %s", name)
46
46
  return None
47
- return os.path.join(tempd, wheel_name)
47
+ return os.path.join(wheel_directory, wheel_name)
@@ -444,7 +444,7 @@ class RequirementPreparer:
444
444
  return None
445
445
 
446
446
  wheel = Wheel(link.filename)
447
- name = canonicalize_name(wheel.name)
447
+ name = wheel.name
448
448
  logger.info(
449
449
  "Obtaining dependency information from %s %s",
450
450
  name,
@@ -531,6 +531,12 @@ class RequirementPreparer:
531
531
  metadata_dist = self._fetch_metadata_only(req)
532
532
  if metadata_dist is not None:
533
533
  req.needs_more_preparation = True
534
+ req.set_dist(metadata_dist)
535
+ # Ensure download_info is available even in dry-run mode
536
+ if req.download_info is None:
537
+ req.download_info = direct_url_from_link(
538
+ req.link, req.source_dir
539
+ )
534
540
  return metadata_dist
535
541
 
536
542
  # None of the optimizations worked, fully prepare the requirement
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import importlib.util
4
3
  import os
5
4
  from collections import namedtuple
6
5
  from typing import Any
@@ -30,13 +29,11 @@ BuildSystemDetails = namedtuple(
30
29
 
31
30
 
32
31
  def load_pyproject_toml(
33
- use_pep517: bool | None, pyproject_toml: str, setup_py: str, req_name: str
34
- ) -> BuildSystemDetails | None:
32
+ pyproject_toml: str, setup_py: str, req_name: str
33
+ ) -> BuildSystemDetails:
35
34
  """Load the pyproject.toml file.
36
35
 
37
36
  Parameters:
38
- use_pep517 - Has the user requested PEP 517 processing? None
39
- means the user hasn't explicitly specified.
40
37
  pyproject_toml - Location of the project's pyproject.toml file
41
38
  setup_py - Location of the project's setup.py file
42
39
  req_name - The name of the requirement we're processing (for
@@ -69,57 +66,7 @@ def load_pyproject_toml(
69
66
  else:
70
67
  build_system = None
71
68
 
72
- # The following cases must use PEP 517
73
- # We check for use_pep517 being non-None and falsy because that means
74
- # the user explicitly requested --no-use-pep517. The value 0 as
75
- # opposed to False can occur when the value is provided via an
76
- # environment variable or config file option (due to the quirk of
77
- # strtobool() returning an integer in pip's configuration code).
78
- if has_pyproject and not has_setup:
79
- if use_pep517 is not None and not use_pep517:
80
- raise InstallationError(
81
- "Disabling PEP 517 processing is invalid: "
82
- "project does not have a setup.py"
83
- )
84
- use_pep517 = True
85
- elif build_system and "build-backend" in build_system:
86
- if use_pep517 is not None and not use_pep517:
87
- raise InstallationError(
88
- "Disabling PEP 517 processing is invalid: "
89
- "project specifies a build backend of {} "
90
- "in pyproject.toml".format(build_system["build-backend"])
91
- )
92
- use_pep517 = True
93
-
94
- # If we haven't worked out whether to use PEP 517 yet,
95
- # and the user hasn't explicitly stated a preference,
96
- # we do so if the project has a pyproject.toml file
97
- # or if we cannot import setuptools or wheels.
98
-
99
- # We fallback to PEP 517 when without setuptools or without the wheel package,
100
- # so setuptools can be installed as a default build backend.
101
- # For more info see:
102
- # https://discuss.python.org/t/pip-without-setuptools-could-the-experience-be-improved/11810/9
103
- # https://github.com/pypa/pip/issues/8559
104
- elif use_pep517 is None:
105
- use_pep517 = (
106
- has_pyproject
107
- or not importlib.util.find_spec("setuptools")
108
- or not importlib.util.find_spec("wheel")
109
- )
110
-
111
- # At this point, we know whether we're going to use PEP 517.
112
- assert use_pep517 is not None
113
-
114
- # If we're using the legacy code path, there is nothing further
115
- # for us to do here.
116
- if not use_pep517:
117
- return None
118
-
119
69
  if build_system is None:
120
- # Either the user has a pyproject.toml with no build-system
121
- # section, or the user has no pyproject.toml, but has opted in
122
- # explicitly via --use-pep517.
123
70
  # In the absence of any explicit backend specification, we
124
71
  # assume the setuptools backend that most closely emulates the
125
72
  # traditional direct setup.py execution, and require wheel and
@@ -130,12 +77,6 @@ def load_pyproject_toml(
130
77
  "build-backend": "setuptools.build_meta:__legacy__",
131
78
  }
132
79
 
133
- # If we're using PEP 517, we have build system information (either
134
- # from pyproject.toml, or defaulted by the code above).
135
- # Note that at this point, we do not know if the user has actually
136
- # specified a backend, though.
137
- assert build_system is not None
138
-
139
80
  # Ensure that the build-system section in pyproject.toml conforms
140
81
  # to PEP 518.
141
82