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
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import collections
4
4
  import logging
5
- from collections.abc import Generator, Sequence
5
+ from collections.abc import Generator
6
6
  from dataclasses import dataclass
7
7
 
8
8
  from pip._internal.cli.progress_bars import BarType, get_install_progress_renderer
@@ -37,7 +37,6 @@ def _validate_requirements(
37
37
 
38
38
  def install_given_reqs(
39
39
  requirements: list[InstallRequirement],
40
- global_options: Sequence[str],
41
40
  root: str | None,
42
41
  home: str | None,
43
42
  prefix: str | None,
@@ -83,7 +82,6 @@ def install_given_reqs(
83
82
 
84
83
  try:
85
84
  requirement.install(
86
- global_options,
87
85
  root=root,
88
86
  home=home,
89
87
  prefix=prefix,
@@ -47,7 +47,7 @@ def _strip_extras(path: str) -> tuple[str, str | None]:
47
47
  m = re.match(r"^(.+)(\[[^\]]+\])$", path)
48
48
  extras = None
49
49
  if m:
50
- path_no_extras = m.group(1)
50
+ path_no_extras = m.group(1).rstrip()
51
51
  extras = m.group(2)
52
52
  else:
53
53
  path_no_extras = path
@@ -86,17 +86,25 @@ def _set_requirement_extras(req: Requirement, new_extras: set[str]) -> Requireme
86
86
  return get_requirement(f"{pre}{extras}{post}")
87
87
 
88
88
 
89
- def parse_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
90
- """Parses an editable requirement into:
91
- - a requirement name
92
- - an URL
93
- - extras
94
- - editable options
95
- Accepted requirements:
96
- svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
97
- .[some_extra]
98
- """
89
+ def _parse_direct_url_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
90
+ try:
91
+ req = Requirement(editable_req)
92
+ except InvalidRequirement:
93
+ pass
94
+ else:
95
+ if req.url:
96
+ # Join the marker back into the name part. This will be parsed out
97
+ # later into a Requirement again.
98
+ if req.marker:
99
+ name = f"{req.name} ; {req.marker}"
100
+ else:
101
+ name = req.name
102
+ return (name, req.url, req.extras)
103
+
104
+ raise ValueError
105
+
99
106
 
107
+ def _parse_pip_syntax_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
100
108
  url = editable_req
101
109
 
102
110
  # If a file path is specified with extras, strip off the extras.
@@ -122,9 +130,27 @@ def parse_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
122
130
  url = f"{version_control}+{url}"
123
131
  break
124
132
 
133
+ return Link(url).egg_fragment, url, set()
134
+
135
+
136
+ def parse_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
137
+ """Parses an editable requirement into:
138
+ - a requirement name with environment markers
139
+ - an URL
140
+ - extras
141
+ Accepted requirements:
142
+ - svn+http://blahblah@rev#egg=Foobar[baz]&subdirectory=version_subdir
143
+ - local_path[some_extra]
144
+ - Foobar[extra] @ svn+http://blahblah@rev#subdirectory=subdir ; markers
145
+ """
146
+ try:
147
+ package_name, url, extras = _parse_direct_url_editable(editable_req)
148
+ except ValueError:
149
+ package_name, url, extras = _parse_pip_syntax_editable(editable_req)
150
+
125
151
  link = Link(url)
126
152
 
127
- if not link.is_vcs:
153
+ if not link.is_vcs and not link.url.startswith("file:"):
128
154
  backends = ", ".join(vcs.all_schemes)
129
155
  raise InstallationError(
130
156
  f"{editable_req} is not a valid editable requirement. "
@@ -132,13 +158,13 @@ def parse_editable(editable_req: str) -> tuple[str | None, str, set[str]]:
132
158
  f"(beginning with {backends})."
133
159
  )
134
160
 
135
- package_name = link.egg_fragment
136
- if not package_name:
161
+ # The project name can be inferred from local file URIs easily.
162
+ if not package_name and not link.url.startswith("file:"):
137
163
  raise InstallationError(
138
164
  f"Could not detect requirement name for '{editable_req}', "
139
- "please specify one with #egg=your_package_name"
165
+ "please specify one with your_package_name @ URL"
140
166
  )
141
- return package_name, url, set()
167
+ return package_name, url, extras
142
168
 
143
169
 
144
170
  def check_first_requirement_in_file(filename: str) -> None:
@@ -225,9 +251,7 @@ def install_req_from_editable(
225
251
  editable_req: str,
226
252
  comes_from: InstallRequirement | str | None = None,
227
253
  *,
228
- use_pep517: bool | None = None,
229
254
  isolated: bool = False,
230
- global_options: list[str] | None = None,
231
255
  hash_options: dict[str, list[str]] | None = None,
232
256
  constraint: bool = False,
233
257
  user_supplied: bool = False,
@@ -244,9 +268,7 @@ def install_req_from_editable(
244
268
  permit_editable_wheels=permit_editable_wheels,
245
269
  link=parts.link,
246
270
  constraint=constraint,
247
- use_pep517=use_pep517,
248
271
  isolated=isolated,
249
- global_options=global_options,
250
272
  hash_options=hash_options,
251
273
  config_settings=config_settings,
252
274
  extras=parts.extras,
@@ -389,9 +411,7 @@ def install_req_from_line(
389
411
  name: str,
390
412
  comes_from: str | InstallRequirement | None = None,
391
413
  *,
392
- use_pep517: bool | None = None,
393
414
  isolated: bool = False,
394
- global_options: list[str] | None = None,
395
415
  hash_options: dict[str, list[str]] | None = None,
396
416
  constraint: bool = False,
397
417
  line_source: str | None = None,
@@ -411,9 +431,7 @@ def install_req_from_line(
411
431
  comes_from,
412
432
  link=parts.link,
413
433
  markers=parts.markers,
414
- use_pep517=use_pep517,
415
434
  isolated=isolated,
416
- global_options=global_options,
417
435
  hash_options=hash_options,
418
436
  config_settings=config_settings,
419
437
  constraint=constraint,
@@ -426,7 +444,6 @@ def install_req_from_req_string(
426
444
  req_string: str,
427
445
  comes_from: InstallRequirement | None = None,
428
446
  isolated: bool = False,
429
- use_pep517: bool | None = None,
430
447
  user_supplied: bool = False,
431
448
  ) -> InstallRequirement:
432
449
  try:
@@ -455,7 +472,6 @@ def install_req_from_req_string(
455
472
  req,
456
473
  comes_from,
457
474
  isolated=isolated,
458
- use_pep517=use_pep517,
459
475
  user_supplied=user_supplied,
460
476
  )
461
477
 
@@ -463,7 +479,6 @@ def install_req_from_req_string(
463
479
  def install_req_from_parsed_requirement(
464
480
  parsed_req: ParsedRequirement,
465
481
  isolated: bool = False,
466
- use_pep517: bool | None = None,
467
482
  user_supplied: bool = False,
468
483
  config_settings: dict[str, str | list[str]] | None = None,
469
484
  ) -> InstallRequirement:
@@ -471,7 +486,6 @@ def install_req_from_parsed_requirement(
471
486
  req = install_req_from_editable(
472
487
  parsed_req.requirement,
473
488
  comes_from=parsed_req.comes_from,
474
- use_pep517=use_pep517,
475
489
  constraint=parsed_req.constraint,
476
490
  isolated=isolated,
477
491
  user_supplied=user_supplied,
@@ -482,13 +496,7 @@ def install_req_from_parsed_requirement(
482
496
  req = install_req_from_line(
483
497
  parsed_req.requirement,
484
498
  comes_from=parsed_req.comes_from,
485
- use_pep517=use_pep517,
486
499
  isolated=isolated,
487
- global_options=(
488
- parsed_req.options.get("global_options", [])
489
- if parsed_req.options
490
- else []
491
- ),
492
500
  hash_options=(
493
501
  parsed_req.options.get("hashes", {}) if parsed_req.options else {}
494
502
  ),
@@ -509,9 +517,7 @@ def install_req_from_link_and_ireq(
509
517
  editable=ireq.editable,
510
518
  link=link,
511
519
  markers=ireq.markers,
512
- use_pep517=ireq.use_pep517,
513
520
  isolated=ireq.isolated,
514
- global_options=ireq.global_options,
515
521
  hash_options=ireq.hash_options,
516
522
  config_settings=ireq.config_settings,
517
523
  user_supplied=ireq.user_supplied,
@@ -532,9 +538,7 @@ def install_req_drop_extras(ireq: InstallRequirement) -> InstallRequirement:
532
538
  editable=ireq.editable,
533
539
  link=ireq.link,
534
540
  markers=ireq.markers,
535
- use_pep517=ireq.use_pep517,
536
541
  isolated=ireq.isolated,
537
- global_options=ireq.global_options,
538
542
  hash_options=ireq.hash_options,
539
543
  constraint=ireq.constraint,
540
544
  extras=[],
@@ -65,7 +65,6 @@ SUPPORTED_OPTIONS: list[Callable[..., optparse.Option]] = [
65
65
 
66
66
  # options to be passed to requirements
67
67
  SUPPORTED_OPTIONS_REQ: list[Callable[..., optparse.Option]] = [
68
- cmdoptions.global_options,
69
68
  cmdoptions.hash,
70
69
  cmdoptions.config_settings,
71
70
  ]
@@ -7,7 +7,7 @@ import shutil
7
7
  import sys
8
8
  import uuid
9
9
  import zipfile
10
- from collections.abc import Collection, Iterable, Sequence
10
+ from collections.abc import Collection, Iterable
11
11
  from optparse import Values
12
12
  from pathlib import Path
13
13
  from typing import Any
@@ -34,12 +34,6 @@ from pip._internal.models.direct_url import DirectUrl
34
34
  from pip._internal.models.link import Link
35
35
  from pip._internal.operations.build.metadata import generate_metadata
36
36
  from pip._internal.operations.build.metadata_editable import generate_editable_metadata
37
- from pip._internal.operations.build.metadata_legacy import (
38
- generate_metadata as generate_metadata_legacy,
39
- )
40
- from pip._internal.operations.install.editable_legacy import (
41
- install_editable as install_editable_legacy,
42
- )
43
37
  from pip._internal.operations.install.wheel import install_wheel
44
38
  from pip._internal.pyproject import load_pyproject_toml, make_pyproject_path
45
39
  from pip._internal.req.req_uninstall import UninstallPathSet
@@ -79,10 +73,8 @@ class InstallRequirement:
79
73
  editable: bool = False,
80
74
  link: Link | None = None,
81
75
  markers: Marker | None = None,
82
- use_pep517: bool | None = None,
83
76
  isolated: bool = False,
84
77
  *,
85
- global_options: list[str] | None = None,
86
78
  hash_options: dict[str, list[str]] | None = None,
87
79
  config_settings: dict[str, str | list[str]] | None = None,
88
80
  constraint: bool = False,
@@ -149,7 +141,6 @@ class InstallRequirement:
149
141
  # Set to True after successful installation
150
142
  self.install_succeeded: bool | None = None
151
143
  # Supplied options
152
- self.global_options = global_options if global_options else []
153
144
  self.hash_options = hash_options if hash_options else {}
154
145
  self.config_settings = config_settings
155
146
  # Set to True after successful preparation of this requirement
@@ -168,6 +159,10 @@ class InstallRequirement:
168
159
  # details).
169
160
  self.metadata_directory: str | None = None
170
161
 
162
+ # The cached metadata distribution that this requirement represents.
163
+ # See get_dist / set_dist.
164
+ self._distribution: BaseDistribution | None = None
165
+
171
166
  # The static build requirements (from pyproject.toml)
172
167
  self.pyproject_requires: list[str] | None = None
173
168
 
@@ -177,23 +172,6 @@ class InstallRequirement:
177
172
  # The PEP 517 backend we should use to build the project
178
173
  self.pep517_backend: BuildBackendHookCaller | None = None
179
174
 
180
- # Are we using PEP 517 for this requirement?
181
- # After pyproject.toml has been loaded, the only valid values are True
182
- # and False. Before loading, None is valid (meaning "use the default").
183
- # Setting an explicit value before loading pyproject.toml is supported,
184
- # but after loading this flag should be treated as read only.
185
- self.use_pep517 = use_pep517
186
-
187
- # If config settings are provided, enforce PEP 517.
188
- if self.config_settings:
189
- if self.use_pep517 is False:
190
- logger.warning(
191
- "--no-use-pep517 ignored for %s "
192
- "because --config-settings are specified.",
193
- self,
194
- )
195
- self.use_pep517 = True
196
-
197
175
  # This requirement needs more preparation before it can be built
198
176
  self.needs_more_preparation = False
199
177
 
@@ -250,8 +228,6 @@ class InstallRequirement:
250
228
 
251
229
  @functools.cached_property
252
230
  def supports_pyproject_editable(self) -> bool:
253
- if not self.use_pep517:
254
- return False
255
231
  assert self.pep517_backend
256
232
  with self.build_env:
257
233
  runner = runner_with_spinner_message(
@@ -492,13 +468,6 @@ class InstallRequirement:
492
468
 
493
469
  return setup_py
494
470
 
495
- @property
496
- def setup_cfg_path(self) -> str:
497
- assert self.source_dir, f"No source dir for {self}"
498
- setup_cfg = os.path.join(self.unpacked_source_directory, "setup.cfg")
499
-
500
- return setup_cfg
501
-
502
471
  @property
503
472
  def pyproject_toml_path(self) -> str:
504
473
  assert self.source_dir, f"No source dir for {self}"
@@ -508,20 +477,12 @@ class InstallRequirement:
508
477
  """Load the pyproject.toml file.
509
478
 
510
479
  After calling this routine, all of the attributes related to PEP 517
511
- processing for this requirement have been set. In particular, the
512
- use_pep517 attribute can be used to determine whether we should
513
- follow the PEP 517 or legacy (setup.py) code path.
480
+ processing for this requirement have been set.
514
481
  """
515
482
  pyproject_toml_data = load_pyproject_toml(
516
- self.use_pep517, self.pyproject_toml_path, self.setup_py_path, str(self)
483
+ self.pyproject_toml_path, self.setup_py_path, str(self)
517
484
  )
518
-
519
- if pyproject_toml_data is None:
520
- assert not self.config_settings
521
- self.use_pep517 = False
522
- return
523
-
524
- self.use_pep517 = True
485
+ assert pyproject_toml_data
525
486
  requires, backend, check, backend_path = pyproject_toml_data
526
487
  self.requirements_to_check = check
527
488
  self.pyproject_requires = requires
@@ -532,23 +493,15 @@ class InstallRequirement:
532
493
  backend_path=backend_path,
533
494
  )
534
495
 
535
- def isolated_editable_sanity_check(self) -> None:
496
+ def editable_sanity_check(self) -> None:
536
497
  """Check that an editable requirement if valid for use with PEP 517/518.
537
498
 
538
- This verifies that an editable that has a pyproject.toml either supports PEP 660
539
- or as a setup.py or a setup.cfg
499
+ This verifies that an editable has a build backend that supports PEP 660.
540
500
  """
541
- if (
542
- self.editable
543
- and self.use_pep517
544
- and not self.supports_pyproject_editable
545
- and not os.path.isfile(self.setup_py_path)
546
- and not os.path.isfile(self.setup_cfg_path)
547
- ):
501
+ if self.editable and not self.supports_pyproject_editable:
548
502
  raise InstallationError(
549
- f"Project {self} has a 'pyproject.toml' and its build "
550
- f"backend is missing the 'build_editable' hook. Since it does not "
551
- f"have a 'setup.py' nor a 'setup.cfg', "
503
+ f"Project {self} uses a build backend "
504
+ f"that is missing the 'build_editable' hook, so "
552
505
  f"it cannot be installed in editable mode. "
553
506
  f"Consider using a build backend that supports PEP 660."
554
507
  )
@@ -562,30 +515,21 @@ class InstallRequirement:
562
515
  assert self.source_dir, f"No source dir for {self}"
563
516
  details = self.name or f"from {self.link}"
564
517
 
565
- if self.use_pep517:
566
- assert self.pep517_backend is not None
567
- if (
568
- self.editable
569
- and self.permit_editable_wheels
570
- and self.supports_pyproject_editable
571
- ):
572
- self.metadata_directory = generate_editable_metadata(
573
- build_env=self.build_env,
574
- backend=self.pep517_backend,
575
- details=details,
576
- )
577
- else:
578
- self.metadata_directory = generate_metadata(
579
- build_env=self.build_env,
580
- backend=self.pep517_backend,
581
- details=details,
582
- )
518
+ assert self.pep517_backend is not None
519
+ if (
520
+ self.editable
521
+ and self.permit_editable_wheels
522
+ and self.supports_pyproject_editable
523
+ ):
524
+ self.metadata_directory = generate_editable_metadata(
525
+ build_env=self.build_env,
526
+ backend=self.pep517_backend,
527
+ details=details,
528
+ )
583
529
  else:
584
- self.metadata_directory = generate_metadata_legacy(
530
+ self.metadata_directory = generate_metadata(
585
531
  build_env=self.build_env,
586
- setup_py_path=self.setup_py_path,
587
- source_dir=self.unpacked_source_directory,
588
- isolated=self.isolated,
532
+ backend=self.pep517_backend,
589
533
  details=details,
590
534
  )
591
535
 
@@ -604,8 +548,13 @@ class InstallRequirement:
604
548
 
605
549
  return self._metadata
606
550
 
551
+ def set_dist(self, distribution: BaseDistribution) -> None:
552
+ self._distribution = distribution
553
+
607
554
  def get_dist(self) -> BaseDistribution:
608
- if self.metadata_directory:
555
+ if self._distribution is not None:
556
+ return self._distribution
557
+ elif self.metadata_directory:
609
558
  return get_directory_distribution(self.metadata_directory)
610
559
  elif self.local_file_path and self.is_wheel:
611
560
  assert self.req is not None
@@ -809,7 +758,6 @@ class InstallRequirement:
809
758
 
810
759
  def install(
811
760
  self,
812
- global_options: Sequence[str] | None = None,
813
761
  root: str | None = None,
814
762
  home: str | None = None,
815
763
  prefix: str | None = None,
@@ -827,43 +775,6 @@ class InstallRequirement:
827
775
  prefix=prefix,
828
776
  )
829
777
 
830
- if self.editable and not self.is_wheel:
831
- deprecated(
832
- reason=(
833
- f"Legacy editable install of {self} (setup.py develop) "
834
- "is deprecated."
835
- ),
836
- replacement=(
837
- "to add a pyproject.toml or enable --use-pep517, "
838
- "and use setuptools >= 64. "
839
- "If the resulting installation is not behaving as expected, "
840
- "try using --config-settings editable_mode=compat. "
841
- "Please consult the setuptools documentation for more information"
842
- ),
843
- gone_in="25.3",
844
- issue=11457,
845
- )
846
- if self.config_settings:
847
- logger.warning(
848
- "--config-settings ignored for legacy editable install of %s. "
849
- "Consider upgrading to a version of setuptools "
850
- "that supports PEP 660 (>= 64).",
851
- self,
852
- )
853
- install_editable_legacy(
854
- global_options=global_options if global_options is not None else [],
855
- prefix=prefix,
856
- home=home,
857
- use_user_site=use_user_site,
858
- name=self.req.name,
859
- setup_py_path=self.setup_py_path,
860
- isolated=self.isolated,
861
- build_env=self.build_env,
862
- unpacked_source_directory=self.unpacked_source_directory,
863
- )
864
- self.install_succeeded = True
865
- return
866
-
867
778
  assert self.is_wheel
868
779
  assert self.local_file_path
869
780
 
@@ -915,23 +826,3 @@ def _has_option(options: Values, reqs: list[InstallRequirement], option: str) ->
915
826
  if getattr(req, option, None):
916
827
  return True
917
828
  return False
918
-
919
-
920
- def check_legacy_setup_py_options(
921
- options: Values,
922
- reqs: list[InstallRequirement],
923
- ) -> None:
924
- has_build_options = _has_option(options, reqs, "build_options")
925
- has_global_options = _has_option(options, reqs, "global_options")
926
- if has_build_options or has_global_options:
927
- deprecated(
928
- reason="--build-option and --global-option are deprecated.",
929
- issue=11859,
930
- replacement="to use --config-settings",
931
- gone_in="25.3",
932
- )
933
- logger.warning(
934
- "Implying --no-binary=:all: due to the presence of "
935
- "--build-option / --global-option. "
936
- )
937
- options.format_control.disallow_binaries()
@@ -10,6 +10,7 @@ from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
10
10
  from pip._vendor.packaging.version import Version
11
11
 
12
12
  from pip._internal.exceptions import (
13
+ FailedToPrepareCandidate,
13
14
  HashError,
14
15
  InstallationSubprocessError,
15
16
  InvalidInstalledPackage,
@@ -68,10 +69,8 @@ def make_install_req_from_link(
68
69
  line,
69
70
  user_supplied=template.user_supplied,
70
71
  comes_from=template.comes_from,
71
- use_pep517=template.use_pep517,
72
72
  isolated=template.isolated,
73
73
  constraint=template.constraint,
74
- global_options=template.global_options,
75
74
  hash_options=template.hash_options,
76
75
  config_settings=template.config_settings,
77
76
  )
@@ -85,15 +84,17 @@ def make_install_req_from_editable(
85
84
  link: Link, template: InstallRequirement
86
85
  ) -> InstallRequirement:
87
86
  assert template.editable, "template not editable"
87
+ if template.name:
88
+ req_string = f"{template.name} @ {link.url}"
89
+ else:
90
+ req_string = link.url
88
91
  ireq = install_req_from_editable(
89
- link.url,
92
+ req_string,
90
93
  user_supplied=template.user_supplied,
91
94
  comes_from=template.comes_from,
92
- use_pep517=template.use_pep517,
93
95
  isolated=template.isolated,
94
96
  constraint=template.constraint,
95
97
  permit_editable_wheels=template.permit_editable_wheels,
96
- global_options=template.global_options,
97
98
  hash_options=template.hash_options,
98
99
  config_settings=template.config_settings,
99
100
  )
@@ -114,10 +115,8 @@ def _make_install_req_from_dist(
114
115
  line,
115
116
  user_supplied=template.user_supplied,
116
117
  comes_from=template.comes_from,
117
- use_pep517=template.use_pep517,
118
118
  isolated=template.isolated,
119
119
  constraint=template.constraint,
120
- global_options=template.global_options,
121
120
  hash_options=template.hash_options,
122
121
  config_settings=template.config_settings,
123
122
  )
@@ -244,9 +243,19 @@ class _InstallRequirementBackedCandidate(Candidate):
244
243
  e.req = self._ireq
245
244
  raise
246
245
  except InstallationSubprocessError as exc:
247
- # The output has been presented already, so don't duplicate it.
248
- exc.context = "See above for output."
249
- raise
246
+ if isinstance(self._ireq.comes_from, InstallRequirement):
247
+ request_chain = self._ireq.comes_from.from_path()
248
+ else:
249
+ request_chain = self._ireq.comes_from
250
+
251
+ if request_chain is None:
252
+ request_chain = "directly requested"
253
+
254
+ raise FailedToPrepareCandidate(
255
+ package_name=self._ireq.name or str(self._link),
256
+ requirement_chain=request_chain,
257
+ failed_step=exc.command_description,
258
+ )
250
259
 
251
260
  self._check_metadata_consistency(dist)
252
261
  return dist
@@ -283,7 +292,7 @@ class LinkCandidate(_InstallRequirementBackedCandidate):
283
292
  assert ireq.link == link
284
293
  if ireq.link.is_wheel and not ireq.link.is_file:
285
294
  wheel = Wheel(ireq.link.filename)
286
- wheel_name = canonicalize_name(wheel.name)
295
+ wheel_name = wheel.name
287
296
  assert name == wheel_name, f"{name!r} != {wheel_name!r} for wheel"
288
297
  # Version may not be present for PEP 508 direct URLs
289
298
  if version is not None:
@@ -711,6 +711,21 @@ class Factory:
711
711
 
712
712
  return DistributionNotFound(f"No matching distribution found for {req}")
713
713
 
714
+ def _has_any_candidates(self, project_name: str) -> bool:
715
+ """
716
+ Check if there are any candidates available for the project name.
717
+ """
718
+ return any(
719
+ self.find_candidates(
720
+ project_name,
721
+ requirements={project_name: []},
722
+ incompatibilities={},
723
+ constraint=Constraint.empty(),
724
+ prefers_installed=True,
725
+ is_satisfied_by=lambda r, c: True,
726
+ )
727
+ )
728
+
714
729
  def get_installation_error(
715
730
  self,
716
731
  e: ResolutionImpossible[Requirement, Candidate],
@@ -796,6 +811,22 @@ class Factory:
796
811
  spec = constraints[key].specifier
797
812
  msg += f"\n The user requested (constraint) {key}{spec}"
798
813
 
814
+ # Check for causes that had no candidates
815
+ causes = set()
816
+ for req, _ in e.causes:
817
+ causes.add(req.name)
818
+
819
+ no_candidates = {c for c in causes if not self._has_any_candidates(c)}
820
+ if no_candidates:
821
+ msg = (
822
+ msg
823
+ + "\n\n"
824
+ + "Additionally, some packages in these conflicts have no "
825
+ + "matching distributions available for your environment:"
826
+ + "\n "
827
+ + "\n ".join(sorted(no_candidates))
828
+ )
829
+
799
830
  msg = (
800
831
  msg
801
832
  + "\n\n"
@@ -100,6 +100,15 @@ class PipProvider(_ProviderBase):
100
100
  self._upgrade_strategy = upgrade_strategy
101
101
  self._user_requested = user_requested
102
102
 
103
+ @property
104
+ def constraints(self) -> dict[str, Constraint]:
105
+ """Public view of user-specified constraints.
106
+
107
+ Exposes the provider's constraints mapping without encouraging
108
+ external callers to reach into private attributes.
109
+ """
110
+ return self._constraints
111
+
103
112
  def identify(self, requirement_or_candidate: Requirement | Candidate) -> str:
104
113
  return requirement_or_candidate.name
105
114