qemu

Форк
0
/
dbusdomain.py 
410 строк · 12.6 Кб
1
# D-Bus sphinx domain extension
2
#
3
# Copyright (C) 2021, Red Hat Inc.
4
#
5
# SPDX-License-Identifier: LGPL-2.1-or-later
6
#
7
# Author: Marc-André Lureau <marcandre.lureau@redhat.com>
8

9
from typing import (
10
    Any,
11
    Dict,
12
    Iterable,
13
    Iterator,
14
    List,
15
    NamedTuple,
16
    Optional,
17
    Tuple,
18
    cast,
19
)
20

21
from docutils import nodes
22
from docutils.nodes import Element, Node
23
from docutils.parsers.rst import directives
24
from sphinx import addnodes
25
from sphinx.addnodes import desc_signature, pending_xref
26
from sphinx.directives import ObjectDescription
27
from sphinx.domains import Domain, Index, IndexEntry, ObjType
28
from sphinx.locale import _
29
from sphinx.roles import XRefRole
30
from sphinx.util import nodes as node_utils
31
from sphinx.util.docfields import Field, TypedField
32
from sphinx.util.typing import OptionSpec
33

34

35
class DBusDescription(ObjectDescription[str]):
36
    """Base class for DBus objects"""
37

38
    option_spec: OptionSpec = ObjectDescription.option_spec.copy()
39
    option_spec.update(
40
        {
41
            "deprecated": directives.flag,
42
        }
43
    )
44

45
    def get_index_text(self, modname: str, name: str) -> str:
46
        """Return the text for the index entry of the object."""
47
        raise NotImplementedError("must be implemented in subclasses")
48

49
    def add_target_and_index(
50
        self, name: str, sig: str, signode: desc_signature
51
    ) -> None:
52
        ifacename = self.env.ref_context.get("dbus:interface")
53
        node_id = name
54
        if ifacename:
55
            node_id = f"{ifacename}.{node_id}"
56

57
        signode["names"].append(name)
58
        signode["ids"].append(node_id)
59

60
        if "noindexentry" not in self.options:
61
            indextext = self.get_index_text(ifacename, name)
62
            if indextext:
63
                self.indexnode["entries"].append(
64
                    ("single", indextext, node_id, "", None)
65
                )
66

67
        domain = cast(DBusDomain, self.env.get_domain("dbus"))
68
        domain.note_object(name, self.objtype, node_id, location=signode)
69

70

71
class DBusInterface(DBusDescription):
72
    """
73
    Implementation of ``dbus:interface``.
74
    """
75

76
    def get_index_text(self, ifacename: str, name: str) -> str:
77
        return ifacename
78

79
    def before_content(self) -> None:
80
        self.env.ref_context["dbus:interface"] = self.arguments[0]
81

82
    def after_content(self) -> None:
83
        self.env.ref_context.pop("dbus:interface")
84

85
    def handle_signature(self, sig: str, signode: desc_signature) -> str:
86
        signode += addnodes.desc_annotation("interface ", "interface ")
87
        signode += addnodes.desc_name(sig, sig)
88
        return sig
89

90
    def run(self) -> List[Node]:
91
        _, node = super().run()
92
        name = self.arguments[0]
93
        section = nodes.section(ids=[name + "-section"])
94
        section += nodes.title(name, "%s interface" % name)
95
        section += node
96
        return [self.indexnode, section]
97

98

99
class DBusMember(DBusDescription):
100

101
    signal = False
102

103

104
class DBusMethod(DBusMember):
105
    """
106
    Implementation of ``dbus:method``.
107
    """
108

109
    option_spec: OptionSpec = DBusMember.option_spec.copy()
110
    option_spec.update(
111
        {
112
            "noreply": directives.flag,
113
        }
114
    )
115

116
    doc_field_types: List[Field] = [
117
        TypedField(
118
            "arg",
119
            label=_("Arguments"),
120
            names=("arg",),
121
            rolename="arg",
122
            typerolename=None,
123
            typenames=("argtype", "type"),
124
        ),
125
        TypedField(
126
            "ret",
127
            label=_("Returns"),
128
            names=("ret",),
129
            rolename="ret",
130
            typerolename=None,
131
            typenames=("rettype", "type"),
132
        ),
133
    ]
134

135
    def get_index_text(self, ifacename: str, name: str) -> str:
136
        return _("%s() (%s method)") % (name, ifacename)
137

138
    def handle_signature(self, sig: str, signode: desc_signature) -> str:
139
        params = addnodes.desc_parameterlist()
140
        returns = addnodes.desc_parameterlist()
141

142
        contentnode = addnodes.desc_content()
143
        self.state.nested_parse(self.content, self.content_offset, contentnode)
144
        for child in contentnode:
145
            if isinstance(child, nodes.field_list):
146
                for field in child:
147
                    ty, sg, name = field[0].astext().split(None, 2)
148
                    param = addnodes.desc_parameter()
149
                    param += addnodes.desc_sig_keyword_type(sg, sg)
150
                    param += addnodes.desc_sig_space()
151
                    param += addnodes.desc_sig_name(name, name)
152
                    if ty == "arg":
153
                        params += param
154
                    elif ty == "ret":
155
                        returns += param
156

157
        anno = "signal " if self.signal else "method "
158
        signode += addnodes.desc_annotation(anno, anno)
159
        signode += addnodes.desc_name(sig, sig)
160
        signode += params
161
        if not self.signal and "noreply" not in self.options:
162
            ret = addnodes.desc_returns()
163
            ret += returns
164
            signode += ret
165

166
        return sig
167

168

169
class DBusSignal(DBusMethod):
170
    """
171
    Implementation of ``dbus:signal``.
172
    """
173

174
    doc_field_types: List[Field] = [
175
        TypedField(
176
            "arg",
177
            label=_("Arguments"),
178
            names=("arg",),
179
            rolename="arg",
180
            typerolename=None,
181
            typenames=("argtype", "type"),
182
        ),
183
    ]
184
    signal = True
185

186
    def get_index_text(self, ifacename: str, name: str) -> str:
187
        return _("%s() (%s signal)") % (name, ifacename)
188

189

190
class DBusProperty(DBusMember):
191
    """
192
    Implementation of ``dbus:property``.
193
    """
194

195
    option_spec: OptionSpec = DBusMember.option_spec.copy()
196
    option_spec.update(
197
        {
198
            "type": directives.unchanged,
199
            "readonly": directives.flag,
200
            "writeonly": directives.flag,
201
            "readwrite": directives.flag,
202
            "emits-changed": directives.unchanged,
203
        }
204
    )
205

206
    doc_field_types: List[Field] = []
207

208
    def get_index_text(self, ifacename: str, name: str) -> str:
209
        return _("%s (%s property)") % (name, ifacename)
210

211
    def transform_content(self, contentnode: addnodes.desc_content) -> None:
212
        fieldlist = nodes.field_list()
213
        access = None
214
        if "readonly" in self.options:
215
            access = _("read-only")
216
        if "writeonly" in self.options:
217
            access = _("write-only")
218
        if "readwrite" in self.options:
219
            access = _("read & write")
220
        if access:
221
            content = nodes.Text(access)
222
            fieldname = nodes.field_name("", _("Access"))
223
            fieldbody = nodes.field_body("", nodes.paragraph("", "", content))
224
            field = nodes.field("", fieldname, fieldbody)
225
            fieldlist += field
226
        emits = self.options.get("emits-changed", None)
227
        if emits:
228
            content = nodes.Text(emits)
229
            fieldname = nodes.field_name("", _("Emits Changed"))
230
            fieldbody = nodes.field_body("", nodes.paragraph("", "", content))
231
            field = nodes.field("", fieldname, fieldbody)
232
            fieldlist += field
233
        if len(fieldlist) > 0:
234
            contentnode.insert(0, fieldlist)
235

236
    def handle_signature(self, sig: str, signode: desc_signature) -> str:
237
        contentnode = addnodes.desc_content()
238
        self.state.nested_parse(self.content, self.content_offset, contentnode)
239
        ty = self.options.get("type")
240

241
        signode += addnodes.desc_annotation("property ", "property ")
242
        signode += addnodes.desc_name(sig, sig)
243
        signode += addnodes.desc_sig_punctuation("", ":")
244
        signode += addnodes.desc_sig_keyword_type(ty, ty)
245
        return sig
246

247
    def run(self) -> List[Node]:
248
        self.name = "dbus:member"
249
        return super().run()
250

251

252
class DBusXRef(XRefRole):
253
    def process_link(self, env, refnode, has_explicit_title, title, target):
254
        refnode["dbus:interface"] = env.ref_context.get("dbus:interface")
255
        if not has_explicit_title:
256
            title = title.lstrip(".")  # only has a meaning for the target
257
            target = target.lstrip("~")  # only has a meaning for the title
258
            # if the first character is a tilde, don't display the module/class
259
            # parts of the contents
260
            if title[0:1] == "~":
261
                title = title[1:]
262
                dot = title.rfind(".")
263
                if dot != -1:
264
                    title = title[dot + 1 :]
265
        # if the first character is a dot, search more specific namespaces first
266
        # else search builtins first
267
        if target[0:1] == ".":
268
            target = target[1:]
269
            refnode["refspecific"] = True
270
        return title, target
271

272

273
class DBusIndex(Index):
274
    """
275
    Index subclass to provide a D-Bus interfaces index.
276
    """
277

278
    name = "dbusindex"
279
    localname = _("D-Bus Interfaces Index")
280
    shortname = _("dbus")
281

282
    def generate(
283
        self, docnames: Iterable[str] = None
284
    ) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
285
        content: Dict[str, List[IndexEntry]] = {}
286
        # list of prefixes to ignore
287
        ignores: List[str] = self.domain.env.config["dbus_index_common_prefix"]
288
        ignores = sorted(ignores, key=len, reverse=True)
289

290
        ifaces = sorted(
291
            [
292
                x
293
                for x in self.domain.data["objects"].items()
294
                if x[1].objtype == "interface"
295
            ],
296
            key=lambda x: x[0].lower(),
297
        )
298
        for name, (docname, node_id, _) in ifaces:
299
            if docnames and docname not in docnames:
300
                continue
301

302
            for ignore in ignores:
303
                if name.startswith(ignore):
304
                    name = name[len(ignore) :]
305
                    stripped = ignore
306
                    break
307
            else:
308
                stripped = ""
309

310
            entries = content.setdefault(name[0].lower(), [])
311
            entries.append(IndexEntry(stripped + name, 0, docname, node_id, "", "", ""))
312

313
        # sort by first letter
314
        sorted_content = sorted(content.items())
315

316
        return sorted_content, False
317

318

319
class ObjectEntry(NamedTuple):
320
    docname: str
321
    node_id: str
322
    objtype: str
323

324

325
class DBusDomain(Domain):
326
    """
327
    Implementation of the D-Bus domain.
328
    """
329

330
    name = "dbus"
331
    label = "D-Bus"
332
    object_types: Dict[str, ObjType] = {
333
        "interface": ObjType(_("interface"), "iface", "obj"),
334
        "method": ObjType(_("method"), "meth", "obj"),
335
        "signal": ObjType(_("signal"), "sig", "obj"),
336
        "property": ObjType(_("property"), "attr", "_prop", "obj"),
337
    }
338
    directives = {
339
        "interface": DBusInterface,
340
        "method": DBusMethod,
341
        "signal": DBusSignal,
342
        "property": DBusProperty,
343
    }
344
    roles = {
345
        "iface": DBusXRef(),
346
        "meth": DBusXRef(),
347
        "sig": DBusXRef(),
348
        "prop": DBusXRef(),
349
    }
350
    initial_data: Dict[str, Dict[str, Tuple[Any]]] = {
351
        "objects": {},  # fullname -> ObjectEntry
352
    }
353
    indices = [
354
        DBusIndex,
355
    ]
356

357
    @property
358
    def objects(self) -> Dict[str, ObjectEntry]:
359
        return self.data.setdefault("objects", {})  # fullname -> ObjectEntry
360

361
    def note_object(
362
        self, name: str, objtype: str, node_id: str, location: Any = None
363
    ) -> None:
364
        self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype)
365

366
    def clear_doc(self, docname: str) -> None:
367
        for fullname, obj in list(self.objects.items()):
368
            if obj.docname == docname:
369
                del self.objects[fullname]
370

371
    def find_obj(self, typ: str, name: str) -> Optional[Tuple[str, ObjectEntry]]:
372
        # skip parens
373
        if name[-2:] == "()":
374
            name = name[:-2]
375
        if typ in ("meth", "sig", "prop"):
376
            try:
377
                ifacename, name = name.rsplit(".", 1)
378
            except ValueError:
379
                pass
380
        return self.objects.get(name)
381

382
    def resolve_xref(
383
        self,
384
        env: "BuildEnvironment",
385
        fromdocname: str,
386
        builder: "Builder",
387
        typ: str,
388
        target: str,
389
        node: pending_xref,
390
        contnode: Element,
391
    ) -> Optional[Element]:
392
        """Resolve the pending_xref *node* with the given *typ* and *target*."""
393
        objdef = self.find_obj(typ, target)
394
        if objdef:
395
            return node_utils.make_refnode(
396
                builder, fromdocname, objdef.docname, objdef.node_id, contnode
397
            )
398

399
    def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
400
        for refname, obj in self.objects.items():
401
            yield (refname, refname, obj.objtype, obj.docname, obj.node_id, 1)
402

403
    def merge_domaindata(self, docnames, otherdata):
404
        for name, obj in otherdata['objects'].items():
405
            if obj.docname in docnames:
406
                self.data['objects'][name] = obj
407

408
def setup(app):
409
    app.add_domain(DBusDomain)
410
    app.add_config_value("dbus_index_common_prefix", [], "env")
411

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.