Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a2d6d53

Browse files
authored
[stubsabot] Use common functions from ts_utils (#12772)
1 parent 42f6a21 commit a2d6d53

2 files changed

Lines changed: 41 additions & 48 deletions

File tree

lib/ts_utils/metadata.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ def _get_oldest_supported_python() -> str:
5050
return val
5151

5252

53+
def stubs_path(distribution: str) -> Path:
54+
"""Return the path to the directory of a third-party distribution."""
55+
return Path("stubs", distribution)
56+
57+
58+
def metadata_path(distribution: str) -> Path:
59+
"""Return the path to the METADATA.toml file of a third-party distribution."""
60+
return stubs_path(distribution) / "METADATA.toml"
61+
62+
5363
@final
5464
@dataclass(frozen=True)
5565
class StubtestSettings:
@@ -77,7 +87,7 @@ def system_requirements_for_platform(self, platform: str) -> list[str]:
7787
@cache
7888
def read_stubtest_settings(distribution: str) -> StubtestSettings:
7989
"""Return an object describing the stubtest settings for a single stubs distribution."""
80-
with Path("stubs", distribution, "METADATA.toml").open("rb") as f:
90+
with metadata_path(distribution).open("rb") as f:
8191
data: dict[str, object] = tomli.load(f).get("tool", {}).get("stubtest", {})
8292

8393
skip: object = data.get("skip", False)
@@ -130,6 +140,7 @@ class StubMetadata:
130140
Don't construct instances directly; use the `read_metadata` function.
131141
"""
132142

143+
distribution: Annotated[str, "The name of the distribution on PyPI"]
133144
version: str
134145
requires: Annotated[list[Requirement], "The parsed requirements as listed in METADATA.toml"]
135146
extra_description: str | None
@@ -142,6 +153,10 @@ class StubMetadata:
142153
stubtest_settings: StubtestSettings
143154
requires_python: Annotated[Specifier, "Versions of Python supported by the stub package"]
144155

156+
@property
157+
def is_obsolete(self) -> bool:
158+
return self.obsolete_since is not None
159+
145160

146161
_KNOWN_METADATA_FIELDS: Final = frozenset(
147162
{
@@ -187,7 +202,7 @@ def read_metadata(distribution: str) -> StubMetadata:
187202
given in the `requires` field, for example.
188203
"""
189204
try:
190-
with Path("stubs", distribution, "METADATA.toml").open("rb") as f:
205+
with metadata_path(distribution).open("rb") as f:
191206
data: dict[str, object] = tomli.load(f)
192207
except FileNotFoundError:
193208
raise NoSuchStubError(f"Typeshed has no stubs for {distribution!r}!") from None
@@ -273,6 +288,7 @@ def read_metadata(distribution: str) -> StubMetadata:
273288
assert key in tk, f"Unrecognised {tool} key {key!r} for {distribution!r}"
274289

275290
return StubMetadata(
291+
distribution=distribution,
276292
version=version,
277293
requires=requires,
278294
extra_description=extra_description,

scripts/stubsabot.py

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@
2626
import aiohttp
2727
import packaging.specifiers
2828
import packaging.version
29-
import tomli
3029
import tomlkit
3130
from termcolor import colored
3231

32+
from ts_utils.metadata import StubMetadata, metadata_path, read_metadata, stubs_path
33+
3334
TYPESHED_OWNER = "python"
3435
TYPESHED_API_URL = f"https://api.github.com/repos/{TYPESHED_OWNER}/typeshed"
3536

@@ -56,27 +57,6 @@ def from_cmd_arg(cls, cmd_arg: str) -> ActionLevel:
5657
everything = 3, "do everything, e.g. open PRs"
5758

5859

59-
@dataclass
60-
class StubInfo:
61-
distribution: str
62-
version_spec: str
63-
upstream_repository: str | None
64-
obsolete: bool
65-
no_longer_updated: bool
66-
67-
68-
def read_typeshed_stub_metadata(stub_path: Path) -> StubInfo:
69-
with (stub_path / "METADATA.toml").open("rb") as f:
70-
meta = tomli.load(f)
71-
return StubInfo(
72-
distribution=stub_path.name,
73-
version_spec=meta["version"],
74-
upstream_repository=meta.get("upstream_repository"),
75-
obsolete="obsolete_since" in meta,
76-
no_longer_updated=meta.get("no_longer_updated", False),
77-
)
78-
79-
8060
@dataclass
8161
class PypiReleaseDownload:
8262
distribution: str
@@ -141,7 +121,6 @@ async def fetch_pypi_info(distribution: str, session: aiohttp.ClientSession) ->
141121
@dataclass
142122
class Update:
143123
distribution: str
144-
stub_path: Path
145124
old_version_spec: str
146125
new_version_spec: str
147126
links: dict[str, str]
@@ -154,7 +133,6 @@ def __str__(self) -> str:
154133
@dataclass
155134
class Obsolete:
156135
distribution: str
157-
stub_path: Path
158136
obsolete_since_version: str
159137
obsolete_since_date: datetime.datetime
160138
links: dict[str, str]
@@ -304,7 +282,7 @@ class GitHubInfo:
304282
tags: list[dict[str, Any]] = field(repr=False)
305283

306284

307-
async def get_github_repo_info(session: aiohttp.ClientSession, stub_info: StubInfo) -> GitHubInfo | None:
285+
async def get_github_repo_info(session: aiohttp.ClientSession, stub_info: StubMetadata) -> GitHubInfo | None:
308286
"""
309287
If the project represented by `stub_info` is hosted on GitHub,
310288
return information regarding the project as it exists on GitHub.
@@ -335,7 +313,7 @@ class GitHubDiffInfo(NamedTuple):
335313

336314

337315
async def get_diff_info(
338-
session: aiohttp.ClientSession, stub_info: StubInfo, pypi_version: packaging.version.Version
316+
session: aiohttp.ClientSession, stub_info: StubMetadata, pypi_version: packaging.version.Version
339317
) -> GitHubDiffInfo | None:
340318
"""Return a tuple giving info about the diff between two releases, if possible.
341319
@@ -355,7 +333,7 @@ async def get_diff_info(
355333
with contextlib.suppress(packaging.version.InvalidVersion):
356334
versions_to_tags[packaging.version.Version(tag_name)] = tag_name
357335

358-
curr_specifier = packaging.specifiers.SpecifierSet(f"=={stub_info.version_spec}")
336+
curr_specifier = packaging.specifiers.SpecifierSet(f"=={stub_info.version}")
359337

360338
try:
361339
new_tag = versions_to_tags[pypi_version]
@@ -469,7 +447,7 @@ def __str__(self) -> str:
469447

470448

471449
async def analyze_diff(
472-
github_repo_path: str, stub_path: Path, old_tag: str, new_tag: str, *, session: aiohttp.ClientSession
450+
github_repo_path: str, distribution: str, old_tag: str, new_tag: str, *, session: aiohttp.ClientSession
473451
) -> DiffAnalysis | None:
474452
url = f"https://api.github.com/repos/{github_repo_path}/compare/{old_tag}...{new_tag}"
475453
async with session.get(url, headers=get_github_api_headers()) as response:
@@ -478,22 +456,23 @@ async def analyze_diff(
478456
assert isinstance(json_resp, dict)
479457
# https://docs.github.com/en/rest/commits/commits#compare-two-commits
480458
py_files: list[FileInfo] = [file for file in json_resp["files"] if Path(file["filename"]).suffix == ".py"]
459+
stub_path = stubs_path(distribution)
481460
files_in_typeshed = set(stub_path.rglob("*.pyi"))
482461
py_files_stubbed_in_typeshed = [file for file in py_files if (stub_path / f"{file['filename']}i") in files_in_typeshed]
483462
return DiffAnalysis(py_files=py_files, py_files_stubbed_in_typeshed=py_files_stubbed_in_typeshed)
484463

485464

486-
async def determine_action(stub_path: Path, session: aiohttp.ClientSession) -> Update | NoUpdate | Obsolete:
487-
stub_info = read_typeshed_stub_metadata(stub_path)
488-
if stub_info.obsolete:
465+
async def determine_action(distribution: str, session: aiohttp.ClientSession) -> Update | NoUpdate | Obsolete:
466+
stub_info = read_metadata(distribution)
467+
if stub_info.is_obsolete:
489468
return NoUpdate(stub_info.distribution, "obsolete")
490469
if stub_info.no_longer_updated:
491470
return NoUpdate(stub_info.distribution, "no longer updated")
492471

493472
pypi_info = await fetch_pypi_info(stub_info.distribution, session)
494473
latest_release = pypi_info.get_latest_release()
495474
latest_version = latest_release.version
496-
spec = packaging.specifiers.SpecifierSet(f"=={stub_info.version_spec}")
475+
spec = packaging.specifiers.SpecifierSet(f"=={stub_info.version}")
497476
obsolete_since = await find_first_release_with_py_typed(pypi_info, session=session)
498477
if obsolete_since is None and latest_version in spec:
499478
return NoUpdate(stub_info.distribution, "up to date")
@@ -517,7 +496,6 @@ async def determine_action(stub_path: Path, session: aiohttp.ClientSession) -> U
517496
if obsolete_since:
518497
return Obsolete(
519498
stub_info.distribution,
520-
stub_path,
521499
obsolete_since_version=str(obsolete_since.version),
522500
obsolete_since_date=obsolete_since.upload_date,
523501
links=links,
@@ -528,17 +506,16 @@ async def determine_action(stub_path: Path, session: aiohttp.ClientSession) -> U
528506
else:
529507
diff_analysis = await analyze_diff(
530508
github_repo_path=diff_info.repo_path,
531-
stub_path=stub_path,
509+
distribution=distribution,
532510
old_tag=diff_info.old_tag,
533511
new_tag=diff_info.new_tag,
534512
session=session,
535513
)
536514

537515
return Update(
538516
distribution=stub_info.distribution,
539-
stub_path=stub_path,
540-
old_version_spec=stub_info.version_spec,
541-
new_version_spec=get_updated_version_spec(stub_info.version_spec, latest_version),
517+
old_version_spec=stub_info.version,
518+
new_version_spec=get_updated_version_spec(stub_info.version, latest_version),
542519
links=links,
543520
diff_analysis=diff_analysis,
544521
)
@@ -705,10 +682,10 @@ async def suggest_typeshed_update(update: Update, session: aiohttp.ClientSession
705682
async with _repo_lock:
706683
branch_name = f"{BRANCH_PREFIX}/{normalize(update.distribution)}"
707684
subprocess.check_call(["git", "checkout", "-B", branch_name, "origin/main"])
708-
with open(update.stub_path / "METADATA.toml", "rb") as f:
685+
with metadata_path(update.distribution).open("rb") as f:
709686
meta = tomlkit.load(f)
710687
meta["version"] = update.new_version_spec
711-
with open(update.stub_path / "METADATA.toml", "w", encoding="UTF-8") as f:
688+
with metadata_path(update.distribution).open("w", encoding="UTF-8") as f:
712689
# tomlkit.dump has partially unknown IO type
713690
tomlkit.dump(meta, f) # pyright: ignore[reportUnknownMemberType]
714691
body = get_update_pr_body(update, meta)
@@ -732,12 +709,12 @@ async def suggest_typeshed_obsolete(obsolete: Obsolete, session: aiohttp.ClientS
732709
async with _repo_lock:
733710
branch_name = f"{BRANCH_PREFIX}/{normalize(obsolete.distribution)}"
734711
subprocess.check_call(["git", "checkout", "-B", branch_name, "origin/main"])
735-
with open(obsolete.stub_path / "METADATA.toml", "rb") as f:
712+
with metadata_path(obsolete.distribution).open("rb") as f:
736713
meta = tomlkit.load(f)
737714
obs_string = tomlkit.string(obsolete.obsolete_since_version)
738715
obs_string.comment(f"Released on {obsolete.obsolete_since_date.date().isoformat()}")
739716
meta["obsolete_since"] = obs_string
740-
with open(obsolete.stub_path / "METADATA.toml", "w", encoding="UTF-8") as f:
717+
with metadata_path(obsolete.distribution).open("w", encoding="UTF-8") as f:
741718
# tomlkit.dump has partially unknown Mapping type
742719
tomlkit.dump(meta, f) # pyright: ignore[reportUnknownMemberType]
743720
body = "\n".join(f"{k}: {v}" for k, v in obsolete.links.items())
@@ -774,9 +751,9 @@ async def main() -> None:
774751
args = parser.parse_args()
775752

776753
if args.distributions:
777-
paths_to_update = [Path("stubs") / distribution for distribution in args.distributions]
754+
dists_to_update = args.distributions
778755
else:
779-
paths_to_update = list(Path("stubs").iterdir())
756+
dists_to_update = [path.name for path in Path("stubs").iterdir()]
780757

781758
if args.action_level > ActionLevel.nothing:
782759
subprocess.run(["git", "update-index", "--refresh"], capture_output=True)
@@ -808,9 +785,9 @@ async def main() -> None:
808785
conn = aiohttp.TCPConnector(limit_per_host=10)
809786
async with aiohttp.ClientSession(connector=conn) as session:
810787
tasks = [
811-
asyncio.create_task(determine_action(stubs_path, session))
812-
for stubs_path in paths_to_update
813-
if stubs_path.name not in denylist
788+
asyncio.create_task(determine_action(distribution, session))
789+
for distribution in dists_to_update
790+
if distribution not in denylist
814791
]
815792

816793
action_count = 0

0 commit comments

Comments
 (0)