1 # D-Bus XML documentation extension
3 # Copyright (C) 2021, Red Hat Inc.
5 # SPDX-License-Identifier: LGPL-2.1-or-later
7 # Author: Marc-André Lureau <marcandre.lureau@redhat.com>
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."""
60 if line
.strip(): # not a blank line
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
) # type: ignore
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)