Skip to content

Commit 952b43d

Browse files
rd4398claude
andcommitted
fix(resolver): keep all candidates when all exceed max-release-age cutoff
Revert to keep-all-and-warn behavior when every candidate is older than the max-release-age cutoff, instead of raising ResolverException. This avoids empty resolution and lets the build proceed with a warning. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Rohan Devasthale <rdevasth@redhat.com>
1 parent ebd42b9 commit 952b43d

2 files changed

Lines changed: 21 additions & 18 deletions

File tree

src/fromager/resolver.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -237,15 +237,11 @@ def find_all_matching_from_provider(
237237
provider: The provider to query for candidates.
238238
req: The requirement to match.
239239
max_age_cutoff: If set, reject candidates published before this time.
240-
Raises ``ResolverException`` if all candidates are older than
241-
the cutoff.
240+
If all candidates are older than the cutoff, all are kept and
241+
a warning is emitted to avoid empty resolution.
242242
243243
Returns list of (url, version) tuples sorted by version (highest first).
244244
245-
Raises:
246-
ResolverException: If ``max_age_cutoff`` is set and every candidate
247-
is older than the cutoff.
248-
249245
IMPORTANT: This bypasses resolvelib's full resolver to collect all matching
250246
candidates. This is safe ONLY because BaseProvider.get_dependencies() returns
251247
an empty list (no transitive dependencies to resolve). The empty incompatibilities
@@ -301,12 +297,17 @@ def find_all_matching_from_provider(
301297
req,
302298
max_age_days,
303299
)
304-
if not filtered:
305-
raise resolvelib.resolvers.ResolverException(
306-
f"all {len(candidates_list)} candidate(s) for {req} were "
307-
f"published before the max-release-age cutoff"
300+
if filtered:
301+
candidates_list = filtered
302+
else:
303+
logger.warning(
304+
"%s: all %d candidate(s) of %s are older than %d days, "
305+
"keeping all to avoid empty resolution",
306+
req.name,
307+
len(candidates_list),
308+
req,
309+
max_age_days,
308310
)
309-
candidates_list = filtered
310311

311312
# Convert candidates to list of (url, version) tuples
312313
# Candidates are sorted by version (highest first) by BaseProvider.find_matches()

tests/test_cooldown.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -759,8 +759,10 @@ def test_max_release_age_disabled_returns_all() -> None:
759759
assert "1.2.2" in versions
760760

761761

762-
def test_max_release_age_all_too_old_raises() -> None:
763-
"""When all versions are older than cutoff, raise ResolverException."""
762+
def test_max_release_age_all_too_old_keeps_all(
763+
caplog: pytest.LogCaptureFixture,
764+
) -> None:
765+
"""When all versions are older than cutoff, keep all candidates and warn."""
764766
max_age_cutoff = _BOOTSTRAP_TIME + datetime.timedelta(days=1)
765767
with requests_mock.Mocker() as r:
766768
r.get(
@@ -769,13 +771,13 @@ def test_max_release_age_all_too_old_raises() -> None:
769771
headers={"Content-Type": _PYPI_SIMPLE_JSON_CONTENT_TYPE},
770772
)
771773
provider = resolver.PyPIProvider(include_sdists=True)
772-
with pytest.raises(
773-
resolvelib.resolvers.ResolverException,
774-
match=r"all 3 candidate.*max-release-age cutoff",
775-
):
776-
resolver.find_all_matching_from_provider(
774+
with caplog.at_level(logging.WARNING, logger="fromager.resolver"):
775+
results = resolver.find_all_matching_from_provider(
777776
provider, Requirement("test-pkg"), max_age_cutoff=max_age_cutoff
778777
)
778+
versions = [str(v) for _, v in results]
779+
assert versions == ["2.0.0", "1.3.2", "1.2.2"]
780+
assert "keeping all to avoid empty resolution" in caplog.text
779781

780782

781783
def test_max_release_age_candidates_without_upload_time_pass_through() -> None:

0 commit comments

Comments
 (0)