docs/qapidoc: implement transmogrify() method

This is the true top-level processor for the new transmogrifier;
responsible both for generating the intermediate rST and then running
the nested parse on that generated document to produce the final
docutils tree that is then - very finally - postprocessed by sphinx for
final rendering to HTML &c.

Signed-off-by: John Snow <jsnow@redhat.com>
Message-ID: <20250311034303.75779-55-jsnow@redhat.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
[Use the opportunity to move the __version__ assignment to where
PEP 8 wants it]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
John Snow 2025-03-10 23:42:52 -04:00 committed by Markus Armbruster
parent c05de7235a
commit 5c1636f7cc

View file

@ -2,6 +2,7 @@
# #
# QEMU qapidoc QAPI file parsing extension # QEMU qapidoc QAPI file parsing extension
# #
# Copyright (c) 2024-2025 Red Hat
# Copyright (c) 2020 Linaro # Copyright (c) 2020 Linaro
# #
# This work is licensed under the terms of the GNU GPLv2 or later. # This work is licensed under the terms of the GNU GPLv2 or later.
@ -26,6 +27,8 @@ https://www.sphinx-doc.org/en/master/development/index.html
from __future__ import annotations from __future__ import annotations
__version__ = "2.0"
from contextlib import contextmanager from contextlib import contextmanager
import os import os
from pathlib import Path from pathlib import Path
@ -56,6 +59,7 @@ from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore
from sphinx import addnodes from sphinx import addnodes
from sphinx.directives.code import CodeBlock from sphinx.directives.code import CodeBlock
from sphinx.errors import ExtensionError from sphinx.errors import ExtensionError
from sphinx.util import logging
from sphinx.util.docutils import switch_source_input from sphinx.util.docutils import switch_source_input
from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.nodes import nested_parse_with_titles
@ -74,7 +78,7 @@ if TYPE_CHECKING:
from sphinx.util.typing import ExtensionMetadata from sphinx.util.typing import ExtensionMetadata
__version__ = "1.0" logger = logging.getLogger(__name__)
class Transmogrifier: class Transmogrifier:
@ -95,6 +99,10 @@ class Transmogrifier:
self._result = StringList() self._result = StringList()
self.indent = 0 self.indent = 0
@property
def result(self) -> StringList:
return self._result
@property @property
def entity(self) -> QAPISchemaDefinition: def entity(self) -> QAPISchemaDefinition:
assert self._curr_ent is not None assert self._curr_ent is not None
@ -438,7 +446,43 @@ class QAPIDocDirective(NestedDirective):
return "qapidoc-%d" % env.new_serialno("qapidoc") return "qapidoc-%d" % env.new_serialno("qapidoc")
def transmogrify(self, schema: QAPISchema) -> nodes.Element: def transmogrify(self, schema: QAPISchema) -> nodes.Element:
raise NotImplementedError logger.info("Transmogrifying QAPI to rST ...")
vis = Transmogrifier()
modules = set()
for doc in schema.docs:
module_source = doc.info.fname
if module_source not in modules:
vis.visit_module(module_source)
modules.add(module_source)
if doc.symbol:
ent = schema.lookup_entity(doc.symbol)
assert isinstance(ent, QAPISchemaDefinition)
vis.visit_entity(ent)
else:
vis.visit_freeform(doc)
logger.info("Transmogrification complete.")
contentnode = nodes.section()
content = vis.result
titles_allowed = True
logger.info("Transmogrifier running nested parse ...")
with switch_source_input(self.state, content):
if titles_allowed:
node: nodes.Element = nodes.section()
node.document = self.state.document
nested_parse_with_titles(self.state, content, contentnode)
else:
node = nodes.paragraph()
node.document = self.state.document
self.state.nested_parse(content, 0, contentnode)
logger.info("Transmogrifier's nested parse completed.")
sys.stdout.flush()
return contentnode
def legacy(self, schema: QAPISchema) -> nodes.Element: def legacy(self, schema: QAPISchema) -> nodes.Element:
vis = QAPISchemaGenRSTVisitor(self) vis = QAPISchemaGenRSTVisitor(self)
@ -572,6 +616,7 @@ class QMPExample(CodeBlock, NestedDirective):
def setup(app: Sphinx) -> ExtensionMetadata: def setup(app: Sphinx) -> ExtensionMetadata:
"""Register qapi-doc directive with Sphinx""" """Register qapi-doc directive with Sphinx"""
app.setup_extension("qapi_domain")
app.add_config_value("qapidoc_srctree", None, "env") app.add_config_value("qapidoc_srctree", None, "env")
app.add_directive("qapi-doc", QAPIDocDirective) app.add_directive("qapi-doc", QAPIDocDirective)
app.add_directive("qmp-example", QMPExample) app.add_directive("qmp-example", QMPExample)