8
"""dbus-doc is a Sphinx extension that provides documentation from D-Bus XML."""
29
from docutils import nodes
30
from docutils.nodes import Element, Node
31
from docutils.parsers.rst import Directive, directives
32
from docutils.parsers.rst.states import RSTState
33
from docutils.statemachine import StringList, ViewList
34
from sphinx.application import Sphinx
35
from sphinx.errors import ExtensionError
36
from sphinx.util import logging
37
from sphinx.util.docstrings import prepare_docstring
38
from sphinx.util.docutils import SphinxDirective, switch_source_input
39
from sphinx.util.nodes import nested_parse_with_titles
42
from dbusparser import parse_dbus_xml
44
logger = logging.getLogger(__name__)
50
def __init__(self, sphinx_directive, dbusfile):
52
self._sphinx_directive = sphinx_directive
53
self._dbusfile = dbusfile
54
self._top_node = nodes.section()
55
self.result = StringList()
58
def add_line(self, line: str, *lineno: int) -> None:
59
"""Append one line of generated reST to the output."""
61
self.result.append(self.indent + line, self._dbusfile, *lineno)
63
self.result.append("", self._dbusfile, *lineno)
65
def add_method(self, method):
66
self.add_line(f".. dbus:method:: {method.name}")
69
for arg in method.in_args:
70
self.add_line(f":arg {arg.signature} {arg.name}: {arg.doc_string}")
71
for arg in method.out_args:
72
self.add_line(f":ret {arg.signature} {arg.name}: {arg.doc_string}")
74
for line in prepare_docstring("\n" + method.doc_string):
76
self.indent = self.indent[:-3]
78
def add_signal(self, signal):
79
self.add_line(f".. dbus:signal:: {signal.name}")
82
for arg in signal.args:
83
self.add_line(f":arg {arg.signature} {arg.name}: {arg.doc_string}")
85
for line in prepare_docstring("\n" + signal.doc_string):
87
self.indent = self.indent[:-3]
89
def add_property(self, prop):
90
self.add_line(f".. dbus:property:: {prop.name}")
92
self.add_line(f":type: {prop.signature}")
93
access = {"read": "readonly", "write": "writeonly", "readwrite": "readwrite"}[
96
self.add_line(f":{access}:")
97
if prop.emits_changed_signal:
98
self.add_line(f":emits-changed: yes")
100
for line in prepare_docstring("\n" + prop.doc_string):
102
self.indent = self.indent[:-3]
104
def add_interface(self, iface):
105
self.add_line(f".. dbus:interface:: {iface.name}")
108
for line in prepare_docstring("\n" + iface.doc_string):
110
for method in iface.methods:
111
self.add_method(method)
112
for sig in iface.signals:
114
for prop in iface.properties:
115
self.add_property(prop)
116
self.indent = self.indent[:-3]
119
def parse_generated_content(state: RSTState, content: StringList) -> List[Node]:
120
"""Parse a generated content by Documenter."""
121
with switch_source_input(state, content):
122
node = nodes.paragraph()
123
node.document = state.document
124
state.nested_parse(content, 0, node)
129
class DBusDocDirective(SphinxDirective):
130
"""Extract documentation from the specified D-Bus XML file"""
133
required_arguments = 1
134
optional_arguments = 0
135
final_argument_whitespace = True
138
reporter = self.state.document.reporter
141
source, lineno = reporter.get_source_and_line(self.lineno)
142
except AttributeError:
143
source, lineno = (None, None)
145
logger.debug("[dbusdoc] %s:%s: input:\n%s", source, lineno, self.block_text)
147
env = self.state.document.settings.env
148
dbusfile = env.config.qapidoc_srctree + "/" + self.arguments[0]
149
with open(dbusfile, "rb") as f:
151
xml = parse_dbus_xml(xml_data)
152
doc = DBusDoc(self, dbusfile)
154
doc.add_interface(iface)
156
result = parse_generated_content(self.state, doc.result)
160
def setup(app: Sphinx) -> Dict[str, Any]:
161
"""Register dbus-doc directive with Sphinx"""
162
app.add_config_value("dbusdoc_srctree", None, "env")
163
app.add_directive("dbus-doc", DBusDocDirective)
164
dbusdomain.setup(app)
166
return dict(version=__version__, parallel_read_safe=True, parallel_write_safe=True)