Skip to content

DM-56680: Degrade autotypes enum-member references when a build reuses pickled doctrees - #555

Merged
jonathansick merged 2 commits into
mainfrom
tickets/DM-56680-autotypes-pickled-doctrees
Nov 10, 2026
Merged

jonathansick merged 2 commits into
mainfrom
tickets/DM-56680-autotypes-pickled-doctrees

Conversation

@jonathansick

Copy link
Copy Markdown
Member

Summary

Fixes #554. With Sphinx 9, a nitpicky build that reuses pickled doctrees (for example docs-linkcheck after docs over one doctree directory) warned py:class reference target not found: ExecutionPhase.PENDING for a model that inherits a field whose Annotated metadata names enum members.

Cause. The issue's working assumption is right, and is now checked against Sphinx's source: autodoc's attribute documenter (AttributeDocumenter.update_annotations) copies the source text of each MRO class's annotations onto the class it documents, for attributes that class doesn't annotate itself. documenteer.ext.autotypes.xrefs recognized the resulting references only by reading that copy back from the class's __annotations__. A doctree-reusing build never runs autodoc, so the copy isn't there.

Fix.

  • _class_annotation_values now also reads each MRO class's source annotations from Sphinx's ModuleAnalyzer, the same place autodoc gets them. This depends on nothing the read phase did.
  • _project_typing_registry and _mro_root_namespace (the third to-do in the issue) had the same kind of dependency: they scan sys.modules, which holds only what has been imported so far, and cache the result. Both now import the documented modules first, using the names stored in the pickled build environment. None of the existing test roots showed a failure from this in a two-pass build, because earlier rungs usually import the package anyway, so this part is a precaution covered by unit tests rather than a fix for an observed failure.
  • The per-class fragment cache now holds a reference to each class. It was keyed by id(cls) alone, so a garbage-collected class's entry could be served to a new class at the same address. The new tests exposed this as an order-dependent test failure.

Behavior change to be aware of. The fragment set for a class now always includes the names in the metadata positions of its MRO's source annotations, not only when autodoc happened to copy them. So ExecutionPhase.PENDING also degrades if it is referenced in the context of the base class that writes it. Names that no annotation of the class's MRO writes still warn.

Validation steps

  • nox -s "test-3.13(sphinx='8')" "test-3.13(sphinx='9')": 1180 passed on Sphinx 9, Sphinx 8 session also green.
  • tests/ext/autotypes_test.py on Python 3.14 with Sphinx 9: 50 passed.
  • nox -s lint and nox -s "typing-3.13(sphinx='8')" "typing-3.13(sphinx='9')": clean.
  • The four new tests fail without the xrefs.py change.
  • Reproduced outside pytest with two separate sphinx-build -n processes over the test-autotypes-annotated root sharing a doctree directory (-b dummy, then -b html): three ExecutionPhase.* warnings before the fix, none after.

Not done: I did not run Safir's nox -s docs docs-linkcheck against this branch. The test root models the Safir shape (uws.Job inheriting SerializedJob.phase), but confirming on Safir itself is still open.

References

A model that inherits a field whose Annotated metadata names enum
members renders that annotation from its source text, because autodoc's
attribute documenter copies each base class's source annotations onto
the class it documents. The xrefs sub-extension recognized the resulting
ExecutionPhase.PENDING references only by reading that copy back out of
the class's __annotations__. A build that reuses pickled doctrees never
runs autodoc, so the copy was missing and the references warned under
nitpicky mode.

Read the source annotations of every class in the MRO from Sphinx's
ModuleAnalyzer instead, which is where autodoc gets them and which
depends on nothing the read phase did.

The sys.modules scans behind the typing registry and the MRO root
namespace had the same kind of dependency: they saw only what autodoc
had imported. Import the documented modules, known from the pickled
build environment, before scanning.

Also keep each cached class alive in the fragment cache, so the id() an
entry is filed under cannot be reused by another class.
@github-actions

Copy link
Copy Markdown

Documentation preview

Edition URL
DM-56680-autotypes-pickled-doctrees https://documenteer.lsst.io/v/DM-56680-autotypes-pickled-doctrees/

Build 1xxz-qv07-1qqp-37 processed successfully.

@jonathansick
jonathansick merged commit 9d49d81 into main Nov 10, 2026
15 checks passed
@jonathansick
jonathansick deleted the tickets/DM-56680-autotypes-pickled-doctrees branch November 10, 2026 20:12
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.

autotypes: enum-member references are not degraded to plain text when a build reuses pickled doctrees

1 participant