You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
72 lines
2.7 KiB
72 lines
2.7 KiB
8 months ago
|
"""The metadata collector components for sphinx.environment."""
|
||
|
|
||
|
from __future__ import annotations
|
||
|
|
||
|
from typing import TYPE_CHECKING, cast
|
||
|
|
||
|
from docutils import nodes
|
||
|
|
||
|
from sphinx.environment.collectors import EnvironmentCollector
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from sphinx.application import Sphinx
|
||
|
from sphinx.environment import BuildEnvironment
|
||
|
from sphinx.util.typing import ExtensionMetadata
|
||
|
|
||
|
|
||
|
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) # type: ignore[arg-type]
|
||
|
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 == 'tocdepth':
|
||
|
try:
|
||
|
value = int(value)
|
||
|
except ValueError:
|
||
|
value = 0
|
||
|
md[name] = value
|
||
|
|
||
|
doctree.pop(index)
|
||
|
|
||
|
|
||
|
def setup(app: Sphinx) -> ExtensionMetadata:
|
||
|
app.add_env_collector(MetadataCollector)
|
||
|
|
||
|
return {
|
||
|
'version': 'builtin',
|
||
|
'parallel_read_safe': True,
|
||
|
'parallel_write_safe': True,
|
||
|
}
|