Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 20 additions & 6 deletions stub_uploader/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ def verify_typeshed_req(req: Requirement) -> None:
"urllib3",
}

# Runtime requirements corresponding to the external requirements list above.
EXTERNAL_RUNTIME_REQ_MAP = {
"django-stubs": "django",
"djangorestframework-stubs": "djangorestframework",
"pandas-stubs": "pandas",
}


def validate_response(resp: requests.Response, req: Requirement) -> None:
if resp.status_code != 200:
Expand Down Expand Up @@ -294,9 +301,13 @@ def verify_external_req(
)

if req.name not in EXTERNAL_REQ_ALLOWLIST and not _unsafe_ignore_allowlist:
raise InvalidRequires(
f"Expected dependency {req.name} to be present in the stub_uploader allowlist"
)
msg = f"Expected dependency {req.name} to be present in the stub_uploader allowlist"
if req.name in EXTERNAL_RUNTIME_REQ_MAP.values():
maybe = next(
k for k, v in EXTERNAL_RUNTIME_REQ_MAP.items() if v == req.name
)
msg += f". Did you mean {maybe}?"
raise InvalidRequires(msg)

resp = requests.get(f"https://pypi.org/pypi/{upstream_distribution}/json")
validate_response(resp, req)
Expand All @@ -306,7 +317,10 @@ def verify_external_req(
# broken by new releases of upstream packages, even if they do not match the version spec we
# have for the upstream distribution.

if req_canonical_name in [
runtime_req_name = EXTERNAL_RUNTIME_REQ_MAP.get(req.name, req.name)
runtime_req_canonical_name = canonical_name(runtime_req_name)

if runtime_req_canonical_name in [
canonical_name(Requirement(r).name)
for r in (data["info"].get("requires_dist") or [])
]:
Expand All @@ -322,11 +336,11 @@ def verify_external_req(
)
if not (
sdist_data
and req_canonical_name
and runtime_req_canonical_name
in [canonical_name(r.name) for r in extract_sdist_requires(sdist_data, req)]
):
raise InvalidRequires(
f"Expected dependency {req} to be listed in {upstream_distribution}'s "
f"Expected dependency {runtime_req_name} to be listed in {upstream_distribution}'s "
+ "requires_dist or the sdist's *.egg-info/requires.txt"
)

Expand Down
11 changes: 11 additions & 0 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@ def test_verify_external_req() -> None:
with pytest.raises(InvalidRequires, match="no upstream distribution on PyPI"):
m.requires_external

# Check differing runtime and stub dependencies
verify_external_req(Requirement("pandas-stubs"), "geopandas")
with pytest.raises(
InvalidRequires,
match=(
r"Expected dependency pandas to be present in the stub_uploader allowlist"
r"\. Did you mean pandas-stubs\?"
),
):
verify_external_req(Requirement("pandas"), "geopandas")


def test_dependency_order() -> None:
"""Test sort_by_dependency correctly sorts all packages by dependency."""
Expand Down