diff --git a/src/fromager/resolver.py b/src/fromager/resolver.py index 1b6754fd..2242852d 100644 --- a/src/fromager/resolver.py +++ b/src/fromager/resolver.py @@ -77,6 +77,30 @@ def match_py_req(py_req: str, *, python_version: Version = PYTHON_VERSION) -> bo return python_version in SpecifierSet(py_req) +def check_pypi_quarantine_status(project: str) -> None: + """Check if a project is quarantined on PyPI (PEP 792). + + Raises ValueError if the project is quarantined. + """ + client = pypi_simple.PyPISimple( + endpoint=PYPI_SERVER_URL, + session=session, + accept=pypi_simple.ACCEPT_JSON_PREFERRED, + ) + try: + package = client.get_project_page(project) + except Exception: + logger.debug( + "failed to check quarantine status for %s on PyPI, skipping check", + project, + ) + return + if package.status == pypi_simple.ProjectStatus.QUARANTINED: + raise ValueError( + f"project {project!r} is quarantined on PyPI: {package.status_reason}" + ) + + def resolve( *, ctx: context.WorkContext, @@ -104,6 +128,7 @@ def resolve( req_type=req_type, ignore_platform=ignore_platform, ) + check_pypi_quarantine_status(req.name) provider.cooldown = resolve_package_cooldown(ctx, req) max_age_cutoff = _compute_max_age_cutoff(ctx) results = find_all_matching_from_provider( @@ -349,7 +374,8 @@ def get_project_from_pypi( ) raise - # PEP 792 package status + # PEP 792 package status (quarantine is checked separately + # via check_pypi_quarantine_status at the resolution entry points) match package.status: case None: logger.debug("no package status") @@ -362,10 +388,6 @@ def get_project_from_pypi( package.status, package.status_reason, ) - case pypi_simple.ProjectStatus.QUARANTINED: - raise ValueError( - f"project {project!r} is quarantined: {package.status_reason}" - ) case _: logger.warning( "project %r has unknown status %r: %s", diff --git a/src/fromager/sources.py b/src/fromager/sources.py index 094ffef7..fb051140 100644 --- a/src/fromager/sources.py +++ b/src/fromager/sources.py @@ -180,6 +180,9 @@ def resolve_source( ctx=ctx, req=req, sdist_server_url=sdist_server_url, req_type=req_type ) + # PEP 792: check quarantine status on PyPI regardless of resolver type. + resolver.check_pypi_quarantine_status(req.name) + # Get all matching candidates from provider max_age_cutoff = resolver._compute_max_age_cutoff(ctx) results = resolver.find_all_matching_from_provider( diff --git a/tests/test_resolver.py b/tests/test_resolver.py index 690a1288..1955d41d 100644 --- a/tests/test_resolver.py +++ b/tests/test_resolver.py @@ -1278,3 +1278,61 @@ def test_cli_package_resolver( assert "- PyPI versions: 1.2.2, 1.3.1+local, 1.3.2, 2.0.0a1" in result.stdout assert "- only wheels on PyPI: 1.3.1+local, 2.0.0a1" in result.stdout assert "- missing from Fromager: 1.3.1+local, 2.0.0a1" in result.stdout + + +_quarantined_simple_response = """ + + +
+ + + +