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
2 changes: 1 addition & 1 deletion data/test_typeshed/stubs/multifilepkg/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version = "0.1"
requires = []
dependencies = []
2 changes: 1 addition & 1 deletion data/test_typeshed/stubs/nspkg/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version = "0.1"
requires = []
dependencies = []
7 changes: 4 additions & 3 deletions stub_uploader/build_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"Typing :: Stubs Only",
]
requires-python = "{requires_python}"
dependencies = {requires}
dependencies = {dependencies}

[project.urls]
"Homepage" = "https://github.com/python/typeshed"
Expand Down Expand Up @@ -339,7 +339,8 @@ def generate_pyproject_file(
) -> str:
"""Auto-generate a setup.py file for given distribution using a template."""
all_requirements = [
str(req) for req in metadata.requires_typeshed + metadata.requires_external
str(req)
for req in metadata.dependencies_typeshed + metadata.dependencies_external
]
requires_python = (
metadata.requires_python
Expand All @@ -354,7 +355,7 @@ def generate_pyproject_file(
build_data.distribution, ts_data, metadata
),
version=version,
requires=all_requirements,
dependencies=all_requirements,
packages=pkg_data.top_level_packages,
requires_python=requires_python,
package_data="\n".join(package_data),
Expand Down
40 changes: 23 additions & 17 deletions stub_uploader/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,35 +59,41 @@ def version_spec(self) -> Specifier:
return spec

@property
def _unvalidated_requires(self) -> list[Requirement]:
return [Requirement(req) for req in self.data.get("requires", [])]
def _unvalidated_dependencies(self) -> list[Requirement]:
return [
Requirement(req)
# TODO: once typeshed migrates from requires to dependencies too, remove fallback on `requires`
for req in self.data.get("dependencies", self.data.get("requires", []))
]

@property
def _unvalidated_requires_typeshed(self) -> list[Requirement]:
def _unvalidated_dependencies_typeshed(self) -> list[Requirement]:
typeshed = uploaded_packages.read()
return [
r for r in self._unvalidated_requires if canonical_name(r.name) in typeshed
r
for r in self._unvalidated_dependencies
if canonical_name(r.name) in typeshed
]

@functools.cached_property
def requires_typeshed(self) -> list[Requirement]:
reqs = self._unvalidated_requires_typeshed
def dependencies_typeshed(self) -> list[Requirement]:
reqs = self._unvalidated_dependencies_typeshed
for req in reqs:
verify_typeshed_req(req)
return reqs

@property
def _unvalidated_requires_external(self) -> list[Requirement]:
def _unvalidated_dependencies_external(self) -> list[Requirement]:
typeshed = uploaded_packages.read()
return [
r
for r in self._unvalidated_requires
for r in self._unvalidated_dependencies
if canonical_name(r.name) not in typeshed
]

@functools.cached_property
def requires_external(self) -> list[Requirement]:
reqs = self._unvalidated_requires_external
def dependencies_external(self) -> list[Requirement]:
reqs = self._unvalidated_dependencies_external
for req in reqs:
verify_external_req(req, self.upstream_distribution)
return reqs
Expand Down Expand Up @@ -472,11 +478,11 @@ def sort_by_dependency(
metadata = read_metadata(typeshed_dir, dist)
ts.add(
metadata.stub_distribution,
# Use _unvalidated_requires instead of requires_typeshed, in case we're uploading
# Use _unvalidated_dependencies instead of dependencies_typeshed, in case we're uploading
# a new package B that depends on another new package A. Sorting topologically means
# that the A will be in uploaded_packages.txt by the time it comes to verify and
# upload B.
*[r.name for r in metadata._unvalidated_requires],
*[r.name for r in metadata._unvalidated_dependencies],
)
dist_map[metadata.stub_distribution] = dist

Expand All @@ -495,7 +501,7 @@ def sort_by_dependency(


def recursive_verify(metadata: Metadata, typeshed_dir: str) -> set[str]:
# While metadata.requires_typeshed and metadata.requires_external will perform validation on the
# While metadata.dependencies_typeshed and metadata.dependencies_external will perform validation on the
# stub distribution itself, it seems useful to be able to validate the transitive typeshed
# dependency graph for a stub distribution
_verified: set[str] = set()
Expand All @@ -505,12 +511,12 @@ def _verify(metadata: Metadata) -> None:
return
_verified.add(metadata.stub_distribution)

# calling these checks metadata's requires
assert isinstance(metadata.requires_typeshed, list)
assert isinstance(metadata.requires_external, list)
# calling these checks metadata's dependencies
assert isinstance(metadata.dependencies_typeshed, list)
assert isinstance(metadata.dependencies_external, list)

# and recursively verify all our internal dependencies as well
for req in metadata.requires_typeshed:
for req in metadata.dependencies_typeshed:
_verify(read_metadata(typeshed_dir, strip_types_prefix(req.name)))

_verify(metadata)
Expand Down
32 changes: 17 additions & 15 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ def test_version_increment(distribution: str) -> None:


def test_unvalidated_properties() -> None:
m = Metadata("fake", {"version": "0.1", "requires": ["numpy", "types-six>=0.1"]})
assert [r.name for r in m._unvalidated_requires] == ["numpy", "types-six"]
assert [r.name for r in m._unvalidated_requires_external] == ["numpy"]
assert [r.name for r in m._unvalidated_requires_typeshed] == ["types-six"]
m = Metadata(
"fake", {"version": "0.1", "dependencies": ["numpy", "types-six>=0.1"]}
)
assert [r.name for r in m._unvalidated_dependencies] == ["numpy", "types-six"]
assert [r.name for r in m._unvalidated_dependencies_external] == ["numpy"]
assert [r.name for r in m._unvalidated_dependencies_typeshed] == ["types-six"]


def test_verify_typeshed_req() -> None:
Expand All @@ -96,8 +98,8 @@ def test_verify_typeshed_req() -> None:
with pytest.raises(InvalidRequires, match="to be uploaded from stub_uploader"):
verify_typeshed_req(Requirement("types-unknown-xxx"))

m = Metadata("mypy", {"version": "0.1", "requires": ["types-unknown-xxx"]})
assert m.requires_typeshed == []
m = Metadata("mypy", {"version": "0.1", "dependencies": ["types-unknown-xxx"]})
assert m.dependencies_typeshed == []


def test_verify_external_req() -> None:
Expand All @@ -116,8 +118,8 @@ def test_verify_external_req() -> None:
):
verify_external_req(Requirement("typing-extensions"), "mypy")

m = Metadata("pandas", {"version": "0.1", "requires": ["numpy"]})
assert [r.name for r in m.requires_external] == ["numpy"]
m = Metadata("pandas", {"version": "0.1", "dependencies": ["numpy"]})
assert [r.name for r in m.dependencies_external] == ["numpy"]

with pytest.raises(InvalidRequires, match="to be listed in mypy's requires_dist"):
verify_external_req(Requirement("numpy"), "mypy")
Expand All @@ -128,19 +130,19 @@ def test_verify_external_req() -> None:
with pytest.raises(InvalidRequires, match="to not start with types-"):
verify_external_req(Requirement("types-unknown-xxx"), "mypy")

m = Metadata("mypy", {"version": "0.1", "requires": ["numpy"]})
m = Metadata("mypy", {"version": "0.1", "dependencies": ["numpy"]})
with pytest.raises(InvalidRequires, match="to be listed in mypy's requires_dist"):
m.requires_external
m.dependencies_external
with pytest.raises(InvalidRequires, match="to be listed in mypy's requires_dist"):
recursive_verify(m, TYPESHED)

# TODO: change tests once METADATA.toml specifies whether a dist is on PyPI
m = Metadata("gdb", {"version": "0.1", "requires": []})
assert m.requires_external == []
m = Metadata("gdb", {"version": "0.1", "dependencies": []})
assert m.dependencies_external == []

m = Metadata("gdb", {"version": "0.1", "requires": ["cryptography"]})
m = Metadata("gdb", {"version": "0.1", "dependencies": ["cryptography"]})
with pytest.raises(InvalidRequires, match="no upstream distribution on PyPI"):
m.requires_external
m.dependencies_external

# Check differing runtime and stub dependencies
verify_external_req(Requirement("pandas-stubs"), "geopandas")
Expand All @@ -160,7 +162,7 @@ def test_dependency_order() -> None:
to_upload = list(sort_by_dependency(TYPESHED, distributions))
assert len(set(to_upload)) == len(to_upload)
for distribution in distributions:
for req in read_metadata(TYPESHED, distribution).requires_typeshed:
for req in read_metadata(TYPESHED, distribution).dependencies_typeshed:
assert to_upload.index(strip_types_prefix(req.name)) < to_upload.index(
distribution
)
Expand Down
Loading