Skip to content

Commit 62aa5e3

Browse files
authored
ci(branch-checks): align Python checks with pre-commit (NVIDIA#1908)
Signed-off-by: Taylor Mutch <taylormutch@gmail.com>
1 parent 8c01534 commit 62aa5e3

5 files changed

Lines changed: 104 additions & 32 deletions

File tree

‎.github/workflows/branch-checks.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,15 @@ jobs:
160160
- name: Install dependencies
161161
run: uv sync --frozen
162162

163+
- name: Format
164+
run: mise run python:format:check
165+
163166
- name: Lint
164167
run: mise run python:lint
165168

169+
- name: Typecheck
170+
run: mise run python:typecheck
171+
166172
- name: Test
167173
run: mise run test:python
168174

‎pyproject.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ dev = [
5454
"setuptools-scm>=8",
5555
"grpcio-tools>=1.60",
5656
"pyelftools>=0.30",
57+
"pyyaml==6.0.2",
58+
"types-pyyaml>=6.0.12.20260518",
5759
]
5860

5961
[tool.uv]

‎tasks/scripts/sync_docs_website.py‎

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
import shutil
1717
import sys
1818
import tempfile
19-
from collections.abc import MutableMapping, MutableSequence
2019
from dataclasses import dataclass
2120
from pathlib import Path
21+
from typing import cast
2222

2323
import yaml
2424

2525
SLUG_RE = re.compile(r"^[A-Za-z0-9._-]+$")
26+
YamlMapping = dict[str, object]
2627

2728

2829
@dataclass
@@ -39,7 +40,9 @@ def parse_args() -> argparse.Namespace:
3940
parser.add_argument("--operation", choices=["sync", "remove"], default="sync")
4041
parser.add_argument("--source-root", type=Path)
4142
parser.add_argument("--docs-website-root", required=True, type=Path)
42-
parser.add_argument("--channel", required=True, choices=["dev", "latest", "version"])
43+
parser.add_argument(
44+
"--channel", required=True, choices=["dev", "latest", "version"]
45+
)
4346
parser.add_argument("--source-ref", default="")
4447
parser.add_argument("--version-slug", default="")
4548
parser.add_argument("--display-name", default="")
@@ -58,11 +61,15 @@ def resolve_slug(channel: str, version_slug: str) -> str:
5861
if not version_slug:
5962
raise ValueError("--version-slug is required when --channel=version")
6063
if not SLUG_RE.fullmatch(version_slug):
61-
raise ValueError(f"version slug contains unsupported characters: {version_slug}")
64+
raise ValueError(
65+
f"version slug contains unsupported characters: {version_slug}"
66+
)
6267
return version_slug
6368

6469

65-
def resolve_display_name(channel: str, slug: str, source_ref: str, override: str) -> str:
70+
def resolve_display_name(
71+
channel: str, slug: str, source_ref: str, override: str
72+
) -> str:
6673
if override:
6774
return override
6875
if channel == "dev":
@@ -116,15 +123,15 @@ def copy_if_exists(src: Path, dst: Path) -> None:
116123
shutil.copy2(src, dst)
117124

118125

119-
def read_yaml(path: Path) -> dict:
126+
def read_yaml(path: Path) -> YamlMapping:
120127
ensure_existing(path, "YAML file")
121128
data = yaml.safe_load(path.read_text(encoding="utf-8"))
122129
if not isinstance(data, dict):
123130
raise ValueError(f"expected YAML mapping in {path}")
124-
return data
131+
return cast("YamlMapping", data)
125132

126133

127-
def write_yaml(path: Path, data: dict) -> None:
134+
def write_yaml(path: Path, data: YamlMapping) -> None:
128135
path.write_text(
129136
yaml.safe_dump(data, sort_keys=False, allow_unicode=True),
130137
encoding="utf-8",
@@ -140,20 +147,23 @@ def prefix_path(value: object, pages_dir: str) -> object:
140147

141148

142149
def prefix_navigation_paths(value: object, pages_dir: str) -> object:
143-
if isinstance(value, MutableMapping):
150+
if isinstance(value, dict):
151+
mapping = cast("YamlMapping", value)
144152
for key in ("path", "folder"):
145-
if key in value:
146-
value[key] = prefix_path(value[key], pages_dir)
147-
for child in value.values():
153+
if key in mapping:
154+
mapping[key] = prefix_path(mapping[key], pages_dir)
155+
for child in mapping.values():
148156
prefix_navigation_paths(child, pages_dir)
149-
elif isinstance(value, MutableSequence):
150-
for child in value:
157+
elif isinstance(value, list):
158+
for child in cast("list[object]", value):
151159
prefix_navigation_paths(child, pages_dir)
152160
return value
153161

154162

155-
def version_navigation(source_index: Path, pages_dir: str) -> dict:
156-
return prefix_navigation_paths(read_yaml(source_index), pages_dir)
163+
def version_navigation(source_index: Path, pages_dir: str) -> YamlMapping:
164+
data = read_yaml(source_index)
165+
prefix_navigation_paths(data, pages_dir)
166+
return data
157167

158168

159169
def parse_versions(raw_versions: object) -> list[VersionEntry]:
@@ -162,20 +172,27 @@ def parse_versions(raw_versions: object) -> list[VersionEntry]:
162172
if not isinstance(raw_versions, list):
163173
raise ValueError("docs.yml versions must be a list")
164174
entries: list[VersionEntry] = []
165-
for raw in raw_versions:
175+
for raw in cast("list[object]", raw_versions):
166176
if not isinstance(raw, dict):
167177
continue
168-
slug = raw.get("slug")
169-
display_name = raw.get("display-name")
170-
path = raw.get("path")
171-
if isinstance(slug, str) and isinstance(display_name, str) and isinstance(path, str):
178+
entry = cast("YamlMapping", raw)
179+
slug = entry.get("slug")
180+
display_name = entry.get("display-name")
181+
path = entry.get("path")
182+
if (
183+
isinstance(slug, str)
184+
and isinstance(display_name, str)
185+
and isinstance(path, str)
186+
):
172187
entries.append(
173188
VersionEntry(slug=slug, display_name=display_name, path=path)
174189
)
175190
return entries
176191

177192

178-
def ordered_entries(existing: list[VersionEntry], updated: VersionEntry) -> list[VersionEntry]:
193+
def ordered_entries(
194+
existing: list[VersionEntry], updated: VersionEntry
195+
) -> list[VersionEntry]:
179196
by_slug = {entry.slug: entry for entry in existing}
180197
by_slug[updated.slug] = updated
181198
existing_order = [entry.slug for entry in existing if entry.slug != updated.slug]
@@ -206,7 +223,9 @@ def render_versions(entries: list[VersionEntry]) -> list[dict[str, str]]:
206223
def component_dirs(fern_dir: Path) -> list[str]:
207224
dirs: list[str] = []
208225
preferred = ["pages-latest", "pages-dev"]
209-
all_page_dirs = sorted(path.name for path in fern_dir.glob("pages-*") if path.is_dir())
226+
all_page_dirs = sorted(
227+
path.name for path in fern_dir.glob("pages-*") if path.is_dir()
228+
)
210229
for name in preferred + all_page_dirs:
211230
path = fern_dir / name / "_components"
212231
component = f"./{name}/_components"
@@ -229,7 +248,9 @@ def update_docs_yml(docs_yml: Path, updated: VersionEntry, fern_dir: Path) -> No
229248

230249
def remove_docs_yml_entry(docs_yml: Path, slug: str, fern_dir: Path) -> None:
231250
data = read_yaml(docs_yml)
232-
entries = [entry for entry in parse_versions(data.get("versions")) if entry.slug != slug]
251+
entries = [
252+
entry for entry in parse_versions(data.get("versions")) if entry.slug != slug
253+
]
233254
data["experimental"] = {
234255
"mdx-components": component_dirs(fern_dir),
235256
}
@@ -274,7 +295,9 @@ def sync_docs(args: argparse.Namespace) -> None:
274295
)
275296
if refresh_shared:
276297
copy_if_exists(source_fern / "main.css", target_fern / "main.css")
277-
copy_if_exists(source_fern / "fern.config.json", target_fern / "fern.config.json")
298+
copy_if_exists(
299+
source_fern / "fern.config.json", target_fern / "fern.config.json"
300+
)
278301

279302
versions_dir = target_fern / "versions"
280303
versions_dir.mkdir(parents=True, exist_ok=True)
@@ -294,8 +317,7 @@ def sync_docs(args: argparse.Namespace) -> None:
294317
)
295318

296319
print(
297-
f"Synced {channel} docs from {source_ref} to fern/{pages_dir} "
298-
f"({display_name})"
320+
f"Synced {channel} docs from {source_ref} to fern/{pages_dir} ({display_name})"
299321
)
300322

301323

‎tasks/scripts/sync_docs_website_test.py‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from __future__ import annotations
1212

1313
from argparse import Namespace
14-
from typing import TYPE_CHECKING
14+
from typing import TYPE_CHECKING, cast
1515

1616
import pytest
1717
import sync_docs_website as sdw
@@ -66,7 +66,7 @@ def test_ordered_entries_pins_latest_then_dev() -> None:
6666

6767

6868
def test_prefix_navigation_paths() -> None:
69-
nav = {
69+
nav: dict[str, object] = {
7070
"navigation": [
7171
{"page": "Intro", "path": "intro.mdx"},
7272
{
@@ -78,11 +78,14 @@ def test_prefix_navigation_paths() -> None:
7878
]
7979
}
8080
sdw.prefix_navigation_paths(nav, "pages-dev")
81-
assert nav["navigation"][0]["path"] == "../pages-dev/intro.mdx"
82-
assert nav["navigation"][1]["folder"] == "../pages-dev/guide"
83-
assert nav["navigation"][1]["contents"][0]["path"] == "../pages-dev/guide/a.mdx"
81+
navigation = cast("list[dict[str, object]]", nav["navigation"])
82+
guide = navigation[1]
83+
contents = cast("list[dict[str, object]]", guide["contents"])
84+
assert navigation[0]["path"] == "../pages-dev/intro.mdx"
85+
assert guide["folder"] == "../pages-dev/guide"
86+
assert contents[0]["path"] == "../pages-dev/guide/a.mdx"
8487
# Absolute URLs are left untouched.
85-
assert nav["navigation"][2]["path"] == "https://example.com"
88+
assert navigation[2]["path"] == "https://example.com"
8689

8790

8891
def _make_source_tree(root: Path) -> None:

‎uv.lock‎

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)