Skip to content

Commit 1d48098

Browse files
committed
typing(feat[py.typed]): Add PEP 561 markers and enforce strict typing across all packages
why: All 4 packages now ship inline types; type checkers (mypy, pyright) can verify downstream consumers without stubs. Removing loose overrides exposed real bugs. what: - Add py.typed to all 4 packages (gp-sphinx, sphinx-fonts, sphinx-gptheme, sphinx-argparse-neo) - Add Typing :: Typed classifier to all 4 pyproject.toml files - Remove broad ignore_errors=true overrides; replace with targeted disable_error_code - Add types-Pygments dev dep to satisfy import-untyped for Pygments stubs - directive.py: fix None-narrowing bug via temp variable (ArgumentParser | None) - directive.py: remove redundant cast (render() already returns list[nodes.Node]) - exemplar.py: remove stale type: ignore[misc] on CleanArgParseDirective subclass - tests: add isinstance(node, nodes.Element) narrowing before indexing Node attrs - tests: add list[dn.Node] annotation to fix list invariance in test_renderer.py - tests: cast _make_app() and SimpleNamespace mocks to Sphinx for arg-type errors - pyproject.toml: extend sphinx_fonts override to cover test_sphinx_fonts module
1 parent c2fe249 commit 1d48098

13 files changed

Lines changed: 85 additions & 50 deletions

File tree

packages/gp-sphinx/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ classifiers = [
2020
"Programming Language :: Python :: 3.14",
2121
"Topic :: Documentation",
2222
"Topic :: Documentation :: Sphinx",
23+
"Typing :: Typed",
2324
]
2425
readme = "README.md"
2526
keywords = ["sphinx", "documentation", "configuration"]

packages/sphinx-argparse-neo/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ classifiers = [
2222
"Topic :: Documentation",
2323
"Topic :: Documentation :: Sphinx",
2424
"Topic :: Software Development :: Documentation",
25+
"Typing :: Typed",
2526
]
2627
readme = "README.md"
2728
keywords = ["sphinx", "argparse", "cli", "documentation"]

packages/sphinx-argparse-neo/src/sphinx_argparse_neo/directive.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,14 @@ def run(self) -> list[nodes.Node]:
124124

125125
# Navigate to subparser if path specified
126126
if "path" in self.options:
127-
parser = self._navigate_to_subparser(parser, self.options["path"])
128-
if parser is None:
127+
maybe_parser = self._navigate_to_subparser(parser, self.options["path"])
128+
if maybe_parser is None:
129129
error = self.state_machine.reporter.error(
130130
f"Subparser path not found: {self.options['path']}",
131131
line=self.lineno,
132132
)
133133
return [error]
134+
parser = maybe_parser
134135

135136
# Build render config from directive options and Sphinx config
136137
config = self._build_render_config()
@@ -176,7 +177,7 @@ def run(self) -> list[nodes.Node]:
176177

177178
# Render to nodes
178179
renderer = ArgparseRenderer(config=config, state=self.state)
179-
return t.cast(list[nodes.Node], renderer.render(parser_info))
180+
return renderer.render(parser_info)
180181

181182
def _build_render_config(self) -> RenderConfig:
182183
"""Build RenderConfig from directive and Sphinx config options.

packages/sphinx-argparse-neo/src/sphinx_argparse_neo/exemplar.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,7 @@ def _extract_sections_from_container(
11661166
return container, extracted_sections
11671167

11681168

1169-
class CleanArgParseDirective(ArgparseDirective): # type: ignore[misc]
1169+
class CleanArgParseDirective(ArgparseDirective):
11701170
"""ArgParse directive that strips ANSI codes and formats examples."""
11711171

11721172
def run(self) -> list[nodes.Node]:

packages/sphinx-fonts/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ classifiers = [
2121
"Programming Language :: Python :: 3.14",
2222
"Topic :: Documentation",
2323
"Topic :: Documentation :: Sphinx",
24+
"Typing :: Typed",
2425
]
2526
readme = "README.md"
2627
keywords = ["sphinx", "fonts", "fontsource", "self-hosted"]

packages/sphinx-gptheme/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ classifiers = [
2121
"Programming Language :: Python :: 3.14",
2222
"Topic :: Documentation",
2323
"Topic :: Documentation :: Sphinx",
24+
"Typing :: Typed",
2425
]
2526
readme = "README.md"
2627
keywords = ["sphinx", "theme", "furo", "documentation"]

pyproject.toml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dev = [
4242
"tomli; python_version < '3.11'",
4343
"typing-extensions",
4444
"types-docutils",
45+
"types-Pygments",
4546
]
4647

4748
[build-system]
@@ -60,19 +61,12 @@ files = [
6061
]
6162

6263
[[tool.mypy.overrides]]
63-
module = ["sphinx_fonts"]
64+
# sphinx_fonts sets private attributes on Sphinx's app object (app._font_preload_hrefs,
65+
# _font_faces, etc.) which aren't declared in sphinx's type stubs.
66+
# The test file also accesses these dynamically-added attrs, so it needs the same suppression.
67+
module = ["sphinx_fonts", "tests.ext.test_sphinx_fonts"]
6468
disable_error_code = ["attr-defined", "unused-ignore"]
6569

66-
[[tool.mypy.overrides]]
67-
module = ["sphinx_argparse_neo.*"]
68-
strict = false
69-
ignore_errors = true
70-
71-
[[tool.mypy.overrides]]
72-
module = ["tests.ext.*"]
73-
strict = false
74-
ignore_errors = true
75-
7670
[tool.ruff]
7771
target-version = "py310"
7872

tests/ext/argparse_neo/test_nodes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,8 @@ def render_argument_to_html(
433433
node["id_prefix"] = id_prefix
434434

435435
translator = MockTranslator()
436-
visit_argparse_argument_html(translator, node)
437-
depart_argparse_argument_html(translator, node)
436+
visit_argparse_argument_html(translator, node) # type: ignore[arg-type]
437+
depart_argparse_argument_html(translator, node) # type: ignore[arg-type]
438438

439439
return "".join(translator.body)
440440

tests/ext/argparse_neo/test_renderer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ def test_post_process_default() -> None:
471471

472472
from docutils import nodes as dn
473473

474-
input_nodes = [dn.paragraph(text="test")]
474+
input_nodes: list[dn.Node] = [dn.paragraph(text="test")]
475475

476476
result = renderer.post_process(input_nodes)
477477

@@ -481,7 +481,7 @@ def test_post_process_default() -> None:
481481
def test_post_process_custom() -> None:
482482
"""Test custom post_process implementation."""
483483

484-
class CustomRenderer(ArgparseRenderer): # type: ignore[misc]
484+
class CustomRenderer(ArgparseRenderer):
485485
def post_process(self, result_nodes: list[t.Any]) -> list[t.Any]:
486486
# Add a marker to each node
487487
for node in result_nodes:

tests/ext/test_argparse_exemplar.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import pytest
1616
from docutils import nodes
1717

18-
from sphinx_argparse_neo.exemplar import ( # type: ignore[import-not-found]
18+
from sphinx_argparse_neo.exemplar import (
1919
ExemplarConfig,
2020
_is_examples_section,
2121
_is_usage_block,
@@ -798,8 +798,11 @@ def test_reorder_nodes_multiple_examples_sections() -> None:
798798
assert len(result) == 5
799799
assert isinstance(result[0], nodes.paragraph)
800800
assert isinstance(result[1], nodes.literal_block)
801+
assert isinstance(result[2], nodes.Element)
801802
assert result[2]["ids"] == ["examples"]
803+
assert isinstance(result[3], nodes.Element)
802804
assert result[3]["ids"] == ["machine-readable-output-examples"]
805+
assert isinstance(result[4], nodes.Element)
803806
assert result[4]["ids"] == ["arguments"]
804807

805808

0 commit comments

Comments
 (0)