Skip to content

Commit 1feee28

Browse files
authored
Improve PyPI READMEs for stubs packages (#105)
1 parent 6b6864b commit 1feee28

File tree

3 files changed

+58
-5
lines changed

3 files changed

+58
-5
lines changed

stub_uploader/build_wheel.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,18 @@
9898
DESCRIPTION_INTRO_TEMPLATE = """
9999
## Typing stubs for {distribution}
100100
101-
This is a PEP 561 type stub package for the `{distribution}` package. It
102-
can be used by type-checking tools like
101+
This is a [PEP 561](https://peps.python.org/pep-0561/)
102+
type stub package for the {formatted_distribution} package.
103+
It can be used by type-checking tools like
103104
[mypy](https://github.com/python/mypy/),
104105
[pyright](https://github.com/microsoft/pyright),
105106
[pytype](https://github.com/google/pytype/),
106107
PyCharm, etc. to check code that uses
107-
`{distribution}`. The source for this package can be found at
108+
`{distribution}`.
109+
110+
This version of `{stub_distribution}` aims to provide accurate annotations
111+
for `{distribution}=={typeshed_version_pin}`.
112+
The source for this package can be found at
108113
https://github.com/python/typeshed/tree/main/stubs/{distribution}. All fixes for
109114
types and metadata should be contributed there.
110115
""".strip()
@@ -276,7 +281,20 @@ def generate_long_description(
276281
) -> str:
277282
extra_description = metadata.extra_description.strip()
278283
parts: list[str] = []
279-
parts.append(DESCRIPTION_INTRO_TEMPLATE.format(distribution=distribution))
284+
285+
if metadata.upstream_repository is not None:
286+
formatted_distribution = f"[`{distribution}`]({metadata.upstream_repository})"
287+
else:
288+
formatted_distribution = f"`{distribution}`"
289+
290+
parts.append(
291+
DESCRIPTION_INTRO_TEMPLATE.format(
292+
distribution=distribution,
293+
formatted_distribution=formatted_distribution,
294+
stub_distribution=metadata.stub_distribution,
295+
typeshed_version_pin=metadata.version_spec,
296+
)
297+
)
280298
if extra_description:
281299
parts.append(extra_description)
282300
if metadata.obsolete_since:

stub_uploader/metadata.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import re
77
import tarfile
8+
import urllib.parse
89
from collections.abc import Generator, Iterable
910
from glob import glob
1011
from pathlib import Path
@@ -112,6 +113,21 @@ def requires_python(self) -> str | None:
112113
verify_requires_python(req)
113114
return req
114115

116+
@functools.cached_property
117+
def upstream_repository(self) -> str | None:
118+
ts_upstream_repo = self.data.get("upstream_repository")
119+
if not isinstance(ts_upstream_repo, str):
120+
# either typeshed doesn't list it for these stubs,
121+
# or it gives a non-str for the field (bad!)
122+
return None
123+
try:
124+
parsed_url = urllib.parse.urlsplit(ts_upstream_repo)
125+
except ValueError:
126+
return None
127+
if parsed_url.scheme != "https":
128+
return None
129+
return ts_upstream_repo
130+
115131

116132
def read_metadata(typeshed_dir: str, distribution: str) -> Metadata:
117133
"""Parse metadata from file."""

tests/test_unit.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
from io import StringIO
44
import os
55
import tempfile
6+
from typing import Any
67

78
import pytest
89
from packaging.version import Version
910

1011
from stub_uploader.build_wheel import collect_setup_entries
1112
from stub_uploader.get_version import compute_incremented_version, ensure_specificity
12-
from stub_uploader.metadata import _UploadedPackages, strip_types_prefix
13+
from stub_uploader.metadata import _UploadedPackages, strip_types_prefix, Metadata
1314
from stub_uploader.ts_data import parse_requirements
1415

1516

@@ -179,3 +180,21 @@ def test_parse_requirements__parsed_packages(name: str, version: str) -> None:
179180
def test_parse_requirements__skipped_packages(name: str) -> None:
180181
requirements = parse_requirements(StringIO(_REQUIREMENTS_TXT))
181182
assert name not in requirements, f"package {name} was not skipped"
183+
184+
185+
@pytest.mark.parametrize(
186+
"data,expected",
187+
[
188+
({}, None),
189+
({"upstream_repository": 12345}, None),
190+
({"upstream_repository": "https://[].foo.com"}, None),
191+
(
192+
{"upstream_repository": "https://github.com/psf/requests"},
193+
"https://github.com/psf/requests",
194+
),
195+
],
196+
)
197+
def test_upstream_repo_validation(data: dict[str, Any], expected: str | None) -> None:
198+
m = Metadata("foo", data)
199+
assert m.upstream_repository == expected
200+
assert type(m.upstream_repository) is type(expected)

0 commit comments

Comments
 (0)