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
#
# Copyright (c) 2024-2025 Red Hat
# Copyright (c) 2020 Linaro
#
# 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
__version__ = "2.0"
from contextlib import contextmanager
import os
from pathlib import Path
@ -56,6 +59,7 @@ from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore
from sphinx import addnodes
from sphinx.directives.code import CodeBlock
from sphinx.errors import ExtensionError
from sphinx.util import logging
from sphinx.util.docutils import switch_source_input
from sphinx.util.nodes import nested_parse_with_titles
@ -74,7 +78,7 @@ if TYPE_CHECKING:
from sphinx.util.typing import ExtensionMetadata
__version__ = "1.0"
logger = logging.getLogger(__name__)
class Transmogrifier:
@ -95,6 +99,10 @@ class Transmogrifier:
self._result = StringList()
self.indent = 0
@property
def result(self) -> StringList:
return self._result
@property
def entity(self) -> QAPISchemaDefinition:
assert self._curr_ent is not None
@ -438,7 +446,43 @@ class QAPIDocDirective(NestedDirective):
return "qapidoc-%d" % env.new_serialno("qapidoc")
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:
vis = QAPISchemaGenRSTVisitor(self)
@ -572,6 +616,7 @@ class QMPExample(CodeBlock, NestedDirective):
def setup(app: Sphinx) -> ExtensionMetadata:
"""Register qapi-doc directive with Sphinx"""
app.setup_extension("qapi_domain")
app.add_config_value("qapidoc_srctree", None, "env")
app.add_directive("qapi-doc", QAPIDocDirective)
app.add_directive("qmp-example", QMPExample)