Skip to content

Commit f35b53e

Browse files
committed
Add --use-deprecated=backtrack-on-build-failures
This serves as an opt-out from build failures causing the entire installation to abort.
1 parent 5494275 commit f35b53e

File tree

6 files changed

+65
-0
lines changed

6 files changed

+65
-0
lines changed

src/pip/_internal/cli/req_command.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,30 @@ def determine_resolver_variant(options: Values) -> str:
227227

228228
return "2020-resolver"
229229

230+
@staticmethod
231+
def determine_build_failure_suppression(options: Values) -> bool:
232+
"""Determines whether build failures should be suppressed and backtracked on."""
233+
if "backtrack-on-build-failures" not in options.deprecated_features_enabled:
234+
return False
235+
236+
if "legacy-resolver" in options.deprecated_features_enabled:
237+
raise CommandError("Cannot backtrack with legacy resolver.")
238+
239+
deprecated(
240+
reason=(
241+
"Backtracking on build failures can mask issues related to how "
242+
"a package generates metadata or builds a wheel."
243+
),
244+
gone_in="22.2",
245+
replacement=(
246+
"avoiding known-bad versions by explicitly telling pip to ignore them "
247+
"(either directly as a requirements, or via a constraints file)."
248+
),
249+
feature_flag=None,
250+
issue=10655,
251+
)
252+
return True
253+
230254
@classmethod
231255
def make_requirement_preparer(
232256
cls,
@@ -321,6 +345,7 @@ def make_resolver(
321345
isolated=options.isolated_mode,
322346
use_pep517=use_pep517,
323347
)
348+
suppress_build_failures = cls.determine_build_failure_suppression(options)
324349
resolver_variant = cls.determine_resolver_variant(options)
325350
# The long import name and duplicated invocation is needed to convince
326351
# Mypy into correctly typechecking. Otherwise it would complain the
@@ -340,6 +365,7 @@ def make_resolver(
340365
force_reinstall=force_reinstall,
341366
upgrade_strategy=upgrade_strategy,
342367
py_version_info=py_version_info,
368+
suppress_build_failures=suppress_build_failures,
343369
)
344370
import pip._internal.resolution.legacy.resolver
345371

src/pip/_internal/resolution/resolvelib/factory.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from pip._internal.exceptions import (
2828
DistributionNotFound,
2929
InstallationError,
30+
InstallationSubprocessError,
3031
MetadataInconsistent,
3132
UnsupportedPythonVersion,
3233
UnsupportedWheel,
@@ -96,6 +97,7 @@ def __init__(
9697
force_reinstall: bool,
9798
ignore_installed: bool,
9899
ignore_requires_python: bool,
100+
suppress_build_failures: bool,
99101
py_version_info: Optional[Tuple[int, ...]] = None,
100102
) -> None:
101103
self._finder = finder
@@ -106,6 +108,7 @@ def __init__(
106108
self._use_user_site = use_user_site
107109
self._force_reinstall = force_reinstall
108110
self._ignore_requires_python = ignore_requires_python
111+
self._suppress_build_failures = suppress_build_failures
109112

110113
self._build_failures: Cache[InstallationError] = {}
111114
self._link_candidate_cache: Cache[LinkCandidate] = {}
@@ -193,6 +196,13 @@ def _make_candidate_from_link(
193196
logger.warning("Discarding %s. %s", link, e)
194197
self._build_failures[link] = e
195198
return None
199+
except InstallationSubprocessError as e:
200+
if not self._suppress_build_failures:
201+
raise
202+
logger.warning("Discarding %s due to build failure: %e", link, e)
203+
self._build_failures[link] = e
204+
return None
205+
196206
base: BaseCandidate = self._editable_candidate_cache[link]
197207
else:
198208
if link not in self._link_candidate_cache:
@@ -208,6 +218,12 @@ def _make_candidate_from_link(
208218
logger.warning("Discarding %s. %s", link, e)
209219
self._build_failures[link] = e
210220
return None
221+
except InstallationSubprocessError as e:
222+
if not self._suppress_build_failures:
223+
raise
224+
logger.warning("Discarding %s due to build failure: %e", link, e)
225+
self._build_failures[link] = e
226+
return None
211227
base = self._link_candidate_cache[link]
212228

213229
if not extras:

src/pip/_internal/resolution/resolvelib/resolver.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def __init__(
4747
ignore_requires_python: bool,
4848
force_reinstall: bool,
4949
upgrade_strategy: str,
50+
suppress_build_failures: bool,
5051
py_version_info: Optional[Tuple[int, ...]] = None,
5152
):
5253
super().__init__()
@@ -61,6 +62,7 @@ def __init__(
6162
force_reinstall=force_reinstall,
6263
ignore_installed=ignore_installed,
6364
ignore_requires_python=ignore_requires_python,
65+
suppress_build_failures=suppress_build_failures,
6466
py_version_info=py_version_info,
6567
)
6668
self.ignore_dependencies = ignore_dependencies

tests/functional/test_new_resolver.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,6 +2329,25 @@ def test_new_resolver_do_not_backtrack_on_build_failure(
23292329
assert "egg_info" in result.stderr
23302330

23312331

2332+
def test_new_resolver_flag_permits_backtracking_on_build_failure(
2333+
script: PipTestEnvironment,
2334+
) -> None:
2335+
create_basic_sdist_for_package(script, "pkg1", "2.0", fails_egg_info=True)
2336+
create_basic_wheel_for_package(script, "pkg1", "1.0")
2337+
2338+
script.pip(
2339+
"install",
2340+
"--use-deprecated=backtrack-on-build-failures",
2341+
"--no-cache-dir",
2342+
"--no-index",
2343+
"--find-links",
2344+
script.scratch_path,
2345+
"pkg1",
2346+
)
2347+
2348+
script.assert_installed(pkg1="1.0")
2349+
2350+
23322351
def test_new_resolver_works_when_failing_package_builds_are_disallowed(
23332352
script: PipTestEnvironment,
23342353
) -> None:

tests/unit/resolution_resolvelib/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def factory(finder: PackageFinder, preparer: RequirementPreparer) -> Iterator[Fa
6262
force_reinstall=False,
6363
ignore_installed=False,
6464
ignore_requires_python=False,
65+
suppress_build_failures=False,
6566
py_version_info=None,
6667
)
6768

tests/unit/resolution_resolvelib/test_resolver.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def resolver(preparer: RequirementPreparer, finder: PackageFinder) -> Resolver:
2929
ignore_requires_python=False,
3030
force_reinstall=False,
3131
upgrade_strategy="to-satisfy-only",
32+
suppress_build_failures=False,
3233
)
3334
return resolver
3435

0 commit comments

Comments
 (0)