summaryrefslogtreecommitdiffstats
path: root/sphinx/environment/collectors/metadata.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/environment/collectors/metadata.py')
-rw-r--r--sphinx/environment/collectors/metadata.py70
1 files changed, 70 insertions, 0 deletions
diff --git a/sphinx/environment/collectors/metadata.py b/sphinx/environment/collectors/metadata.py
new file mode 100644
index 0000000..5f737a9
--- /dev/null
+++ b/sphinx/environment/collectors/metadata.py
@@ -0,0 +1,70 @@
+"""The metadata collector components for sphinx.environment."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, cast
+
+from docutils import nodes
+
+from sphinx.environment.collectors import EnvironmentCollector
+
+if TYPE_CHECKING:
+ from sphinx.application import Sphinx
+ from sphinx.environment import BuildEnvironment
+
+
+class MetadataCollector(EnvironmentCollector):
+ """metadata collector for sphinx.environment."""
+
+ def clear_doc(self, app: Sphinx, env: BuildEnvironment, docname: str) -> None:
+ env.metadata.pop(docname, None)
+
+ def merge_other(self, app: Sphinx, env: BuildEnvironment,
+ docnames: set[str], other: BuildEnvironment) -> None:
+ for docname in docnames:
+ env.metadata[docname] = other.metadata[docname]
+
+ def process_doc(self, app: Sphinx, doctree: nodes.document) -> None:
+ """Process the docinfo part of the doctree as metadata.
+
+ Keep processing minimal -- just return what docutils says.
+ """
+ index = doctree.first_child_not_matching_class(nodes.PreBibliographic)
+ if index is None:
+ return
+ elif isinstance(doctree[index], nodes.docinfo):
+ md = app.env.metadata[app.env.docname]
+ for node in doctree[index]: # type: ignore[attr-defined]
+ # nodes are multiply inherited...
+ if isinstance(node, nodes.authors):
+ authors = cast(list[nodes.author], node)
+ md['authors'] = [author.astext() for author in authors]
+ elif isinstance(node, nodes.field):
+ assert len(node) == 2
+ field_name = cast(nodes.field_name, node[0])
+ field_body = cast(nodes.field_body, node[1])
+ md[field_name.astext()] = field_body.astext()
+ elif isinstance(node, nodes.TextElement):
+ # other children must be TextElement
+ # see: https://docutils.sourceforge.io/docs/ref/doctree.html#bibliographic-elements # noqa: E501
+ md[node.__class__.__name__] = node.astext()
+
+ for name, value in md.items():
+ if name in ('tocdepth',):
+ try:
+ value = int(value)
+ except ValueError:
+ value = 0
+ md[name] = value
+
+ doctree.pop(index)
+
+
+def setup(app: Sphinx) -> dict[str, Any]:
+ app.add_env_collector(MetadataCollector)
+
+ return {
+ 'version': 'builtin',
+ 'parallel_read_safe': True,
+ 'parallel_write_safe': True,
+ }