Skip to content

autodoc: Execute .pyi stubs with deferred annotation semantics - #14729

Open
bfs2021 wants to merge 4 commits into
sphinx-doc:masterfrom
bfs2021:fix/autodoc-stub-deferred-annotations
Open

bfs2021 wants to merge 4 commits into
sphinx-doc:masterfrom
bfs2021:fix/autodoc-stub-deferred-annotations

Conversation

@bfs2021

@bfs2021 bfs2021 commented Oct 9, 2026

Copy link
Copy Markdown

Resolves #14354

What

When autodoc imports a PEP 561 .pyi stub file for a native module (via _find_type_stub_spec/_StubFileLoader), it now executes the stub as if it began with from __future__ import annotations — which is exactly how PEP 484 defines stub-file semantics ("The type checker is required to treat annotations in stub files as if they were always evaluated with from __future__ import annotations"). Previously the stub was compiled and executed as a plain module, so a forward reference in an annotation raised NameError at import time:

File ".../coconext/types.pyi", line 17, in <module>
    class Logic:
File ".../types.pyi", line 19, in Logic
    def __init__(self, arg: Logic) -> None: ...
NameError: name 'Logic' is not defined

The implementation compiles the stub source with the CO_FUTURE_ANNOTATIONS compiler flag instead of spec.loader.exec_module, so annotations stay lazily-evaluated strings and the module namespace is populated exactly as before otherwise.

Testing

  • tests/roots/test-ext-apidoc-duplicates/fish_licence/halibut.pyi (previously empty) now contains a class with a self-referencing annotation, which reproduces the reported NameError on main.
  • test_import_native_module_stubs_defer_annotations asserts the import succeeds and the annotation is preserved as a deferred string ('Fish').
  • Verified the new test fails on main with the exact reported error (NameError: name 'Fish' is not defined) and passes with this change.

ruff check and ruff format --check (pinned 0.14.9) are clean. The remaining test_ext_autodoc failures in my local run (test_overload3, test_final, test_autodoc_pep695_type_alias, test_cfunction) are identical on unmodified main in this environment and unrelated to this change.

@bfs2021

bfs2021 commented Oct 9, 2026

Copy link
Copy Markdown
Author

Heads-up on the red CI: the failing jobs here (Docutils HEAD, LaTeX, Python 3.13/3.14 — all failing on test_cfunction, where time.asctime's parameter renders as time_tuple instead of tuple) appear to be runner/dependency drift rather than anything from this change. The exact same job set fails on unrelated open PRs right now, e.g. #14726 (Docutils HEAD, LaTeX, Python 3.13t/3.14t) and #14724 (same).

The checks that exercise this change all pass: ruff, mypy, ty, Windows, macOS, docs-lint, and the ReadTheDocs build. The regression test also fails on main without this patch and passes with it (details in the description).

…ction

The parameter name differs between CPython builds and patch releases
('tuple' vs 'time_tuple'), so a version-range check cannot be reliable.
Derive the expected name from the running interpreter's docstring,
which is exactly what autodoc renders.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Typing stubs not being analyzed with deferred type evaluation

1 participant