fix(docs): let the build resolve yaml pages and directory links

The gettext build emitted 23251 warnings. Three causes carried 22424 of
them, and each is answered with the mechanism sphinx supplies.

conf.py read .yml and .yaml through the reStructuredText parser, where
yaml indentation is a block quote: 16107 warnings, and the pages had no
title either. A parser renders them as the literal block they are, and
marks them orphan because a yaml file is reference material rather than a
chapter, which drops another 3062 toctree warnings.

myst builds a document reference only when the link target is_file(), so
every link to a directory fell through as unresolvable. A post-transform
ahead of myst's resolver points such a link at the directory's README,
but only where that README exists, which leaves the genuinely dangling
ones reported. Together with myst_heading_anchors, which was never
configured, that clears 2921 missing cross-references and 320 python
references.

Measured per step over four full builds: 23251 -> 7130 -> 4068 -> 827.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-09-25 17:47:07 +02:00
1 parent 3f212febcb
commit bd15eae9fa
3 files changed
+111 -2

No files matched your search

@@ -44,8 +44,8 @@ html_theme_options = {
source_suffix = {
".md": "markdown",
".rst": "restructuredtext",
".yml": "restructuredtext",
".yaml": "restructuredtext",
".yml": "yaml",
".yaml": "yaml",
}
extensions = [
@@ -56,6 +56,8 @@ extensions = [
"infinito_docs.extensions.markdown_include",
"infinito_docs.extensions.parallel_postprocess",
"infinito_docs.extensions.untranslated_yaml",
"infinito_docs.extensions.yaml_source",
"infinito_docs.extensions.directory_readme",
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
]
@@ -67,6 +69,7 @@ gettext_compact = "docs"
gettext_location = False
myst_enable_extensions = ["colon_fence"]
myst_heading_anchors = 6
def replace_assets_in_doctree(app, doctree, docname):
@@ -0,0 +1,53 @@
"""Resolve a markdown link to a directory against that directory's README.
GitHub renders ``[when](when/)`` by opening the folder and showing its README.
myst only builds a document reference when the target ``is_file()``, so a folder
falls through as an unresolvable reference and reported 2512 of these. Rewriting
the links in the sources instead would have made them uglier in the place they
already work.
"""
from __future__ import annotations
from pathlib import Path
from sphinx import addnodes
from sphinx.transforms.post_transforms import SphinxPostTransform
MYST_RESOLVER_PRIORITY = 9
README = "README.md"
class DirectoryReadme(SphinxPostTransform):
"""Point a link to a directory at the document its README became."""
default_priority = MYST_RESOLVER_PRIORITY - 1
def _docname(self, target: str, refdoc: str) -> str:
"""Return the docname of the README backing ``target``, empty when none.
Args:
target: the link destination, without its anchor.
refdoc: the document the link was written in.
"""
_, path = self.env.relfn2path(target, refdoc)
readme = Path(path) / README
return self.env.path2doc(str(readme)) or "" if readme.is_file() else ""
def run(self, **kwargs) -> None:
"""Rewrite every directory reference that a README backs."""
for node in self.document.findall(addnodes.pending_xref):
if node.get("reftype") != "myst" or node.get("refdomain") is not None:
continue
target, _, anchor = node["reftarget"].partition("#")
docname = self._docname(target, node.get("refdoc", self.env.docname))
if not docname:
continue
node["refdomain"] = "doc"
node["reftarget"] = docname
node["reftargetid"] = anchor or None
def setup(app):
app.add_post_transform(DirectoryReadme)
return {"version": "1.0", "parallel_read_safe": True}
@@ -0,0 +1,53 @@
"""Render a YAML source file as the literal block it is.
Sphinx read ``.yml`` and ``.yaml`` through the reStructuredText parser, where
YAML indentation reads as a block quote: 9650 of the build's ``Unexpected
indentation`` errors came from that one mapping, and the pages carried no title
either, because a YAML file has no heading to find.
"""
from __future__ import annotations
from pathlib import Path
from docutils import nodes
from sphinx.parsers import Parser
def _orphan() -> nodes.docinfo:
"""Return the docinfo Sphinx reads the ``orphan`` metadata flag from."""
field = nodes.field()
field += nodes.field_name(text="orphan")
field += nodes.field_body()
info = nodes.docinfo()
info += field
return info
class YamlParser(Parser):
"""Parse a YAML file into a page titled after it, holding its source."""
supported = ("yaml",)
def parse(self, inputstring: str, document: nodes.document) -> None:
"""Fill ``document`` with the source of the YAML file it was read from.
The page is marked ``orphan``: a YAML file is reference material reached
from a link in prose, not a chapter of the manual, and every one of them
would otherwise report itself missing from the toctree.
Args:
inputstring: the file's content.
document: the docutils document to populate.
"""
title = Path(str(document["source"])).name
document += _orphan()
section = nodes.section(ids=[nodes.make_id(title)], names=[title])
section += nodes.title(text=title)
section += nodes.literal_block(inputstring, inputstring, language="yaml")
document += section
def setup(app):
app.add_source_parser(YamlParser)
return {"version": "1.0", "parallel_read_safe": True}