dbus-fast 2.46.1__cp311-cp311-musllinux_1_2_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dbus-fast might be problematic. Click here for more details.

Files changed (51) hide show
  1. dbus_fast/__init__.py +82 -0
  2. dbus_fast/__version__.py +10 -0
  3. dbus_fast/_private/__init__.py +1 -0
  4. dbus_fast/_private/_cython_compat.py +14 -0
  5. dbus_fast/_private/address.cpython-311-aarch64-linux-musl.so +0 -0
  6. dbus_fast/_private/address.pxd +15 -0
  7. dbus_fast/_private/address.py +117 -0
  8. dbus_fast/_private/constants.py +20 -0
  9. dbus_fast/_private/marshaller.cpython-311-aarch64-linux-musl.so +0 -0
  10. dbus_fast/_private/marshaller.pxd +110 -0
  11. dbus_fast/_private/marshaller.py +229 -0
  12. dbus_fast/_private/unmarshaller.cpython-311-aarch64-linux-musl.so +0 -0
  13. dbus_fast/_private/unmarshaller.pxd +261 -0
  14. dbus_fast/_private/unmarshaller.py +902 -0
  15. dbus_fast/_private/util.py +177 -0
  16. dbus_fast/aio/__init__.py +5 -0
  17. dbus_fast/aio/message_bus.py +579 -0
  18. dbus_fast/aio/message_reader.cpython-311-aarch64-linux-musl.so +0 -0
  19. dbus_fast/aio/message_reader.pxd +13 -0
  20. dbus_fast/aio/message_reader.py +49 -0
  21. dbus_fast/aio/proxy_object.py +207 -0
  22. dbus_fast/auth.py +125 -0
  23. dbus_fast/constants.py +152 -0
  24. dbus_fast/errors.py +81 -0
  25. dbus_fast/glib/__init__.py +3 -0
  26. dbus_fast/glib/message_bus.py +513 -0
  27. dbus_fast/glib/proxy_object.py +318 -0
  28. dbus_fast/introspection.py +682 -0
  29. dbus_fast/message.cpython-311-aarch64-linux-musl.so +0 -0
  30. dbus_fast/message.pxd +76 -0
  31. dbus_fast/message.py +387 -0
  32. dbus_fast/message_bus.cpython-311-aarch64-linux-musl.so +0 -0
  33. dbus_fast/message_bus.pxd +75 -0
  34. dbus_fast/message_bus.py +1326 -0
  35. dbus_fast/proxy_object.py +357 -0
  36. dbus_fast/py.typed +0 -0
  37. dbus_fast/send_reply.py +61 -0
  38. dbus_fast/service.cpython-311-aarch64-linux-musl.so +0 -0
  39. dbus_fast/service.pxd +50 -0
  40. dbus_fast/service.py +699 -0
  41. dbus_fast/signature.cpython-311-aarch64-linux-musl.so +0 -0
  42. dbus_fast/signature.pxd +31 -0
  43. dbus_fast/signature.py +482 -0
  44. dbus_fast/unpack.cpython-311-aarch64-linux-musl.so +0 -0
  45. dbus_fast/unpack.pxd +13 -0
  46. dbus_fast/unpack.py +24 -0
  47. dbus_fast/validators.py +199 -0
  48. dbus_fast-2.46.1.dist-info/METADATA +262 -0
  49. dbus_fast-2.46.1.dist-info/RECORD +51 -0
  50. dbus_fast-2.46.1.dist-info/WHEEL +4 -0
  51. dbus_fast-2.46.1.dist-info/licenses/LICENSE +22 -0
@@ -0,0 +1,682 @@
1
+ import xml.etree.ElementTree as ET
2
+
3
+ from .constants import ArgDirection, PropertyAccess
4
+ from .errors import InvalidIntrospectionError
5
+ from .signature import SignatureType, get_signature_tree
6
+ from .validators import assert_interface_name_valid, assert_member_name_valid
7
+
8
+ # https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
9
+
10
+
11
+ def _fetch_annotations(element: ET.Element) -> dict[str, str]:
12
+ annotations: dict[str, str] = {}
13
+
14
+ for child in element:
15
+ if child.tag != "annotation":
16
+ continue
17
+ annotation_name = child.attrib["name"]
18
+ annotation_value = child.attrib["value"]
19
+ annotations[annotation_name] = annotation_value
20
+
21
+ return annotations
22
+
23
+
24
+ def _extract_annotations(element: ET.Element, annotations: dict[str, str]) -> None:
25
+ for key, value in annotations.items():
26
+ annotation = ET.Element("annotation", {"name": key, "value": value})
27
+ element.append(annotation)
28
+
29
+
30
+ class Arg:
31
+ """A class that represents an input or output argument to a signal or a method.
32
+
33
+ :ivar name: The name of this arg.
34
+ :vartype name: str
35
+ :ivar direction: Whether this is an input or an output argument.
36
+ :vartype direction: :class:`ArgDirection <dbus_fast.ArgDirection>`
37
+ :ivar type: The parsed signature type of this argument.
38
+ :vartype type: :class:`SignatureType <dbus_fast.SignatureType>`
39
+ :ivar signature: The signature string of this argument.
40
+ :vartype signature: str
41
+ :ivar annotations: The annotations of this arg.
42
+ :vartype annotations: dict[str, str]
43
+
44
+ :raises:
45
+ - :class:`InvalidMemberNameError <dbus_fast.InvalidMemberNameError>` - If the name of the arg is not valid.
46
+ - :class:`InvalidSignatureError <dbus_fast.InvalidSignatureError>` - If the signature is not valid.
47
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the signature is not a single complete type.
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ signature: SignatureType | str,
53
+ direction: ArgDirection | None = None,
54
+ name: str | None = None,
55
+ annotations: dict[str, str] | None = None,
56
+ ):
57
+ type_ = None
58
+ if type(signature) is SignatureType:
59
+ type_ = signature
60
+ signature = signature.signature
61
+ else:
62
+ tree = get_signature_tree(signature)
63
+ if len(tree.types) != 1:
64
+ raise InvalidIntrospectionError(
65
+ f"an argument must have a single complete type. (has {len(tree.types)} types)"
66
+ )
67
+ type_ = tree.types[0]
68
+
69
+ self.type = type_
70
+ self.signature = signature
71
+ self.name = name
72
+ self.direction = direction
73
+ self.annotations = annotations or {}
74
+
75
+ def from_xml(element: ET.Element, direction: ArgDirection) -> "Arg":
76
+ """Convert a :class:`xml.etree.ElementTree.Element` into a
77
+ :class:`Arg`.
78
+
79
+ The element must be valid DBus introspection XML for an ``arg``.
80
+
81
+ :param element: The parsed XML element.
82
+ :type element: :class:`xml.etree.ElementTree.Element`
83
+ :param direction: The direction of this arg. Must be specified because it can default to different values depending on if it's in a method or signal.
84
+ :type direction: :class:`ArgDirection <dbus_fast.ArgDirection>`
85
+
86
+ :raises:
87
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the XML tree is not valid introspection data.
88
+ """
89
+ name = element.attrib.get("name")
90
+ signature = element.attrib.get("type")
91
+
92
+ if not signature:
93
+ raise InvalidIntrospectionError(
94
+ 'a method argument must have a "type" attribute'
95
+ )
96
+
97
+ annotations = _fetch_annotations(element)
98
+
99
+ return Arg(signature, direction, name, annotations)
100
+
101
+ def to_xml(self) -> ET.Element:
102
+ """Convert this :class:`Arg` into an :class:`xml.etree.ElementTree.Element`."""
103
+ element = ET.Element("arg")
104
+ if self.name:
105
+ element.set("name", self.name)
106
+
107
+ if self.direction:
108
+ element.set("direction", self.direction.value)
109
+ element.set("type", self.signature)
110
+
111
+ _extract_annotations(element, self.annotations)
112
+
113
+ return element
114
+
115
+
116
+ class Signal:
117
+ """A class that represents a signal exposed on an interface.
118
+
119
+ :ivar name: The name of this signal
120
+ :vartype name: str
121
+ :ivar args: A list of output arguments for this signal.
122
+ :vartype args: list(Arg)
123
+ :ivar signature: The collected signature of the output arguments.
124
+ :vartype signature: str
125
+ :ivar annotations: The annotations of this signal.
126
+ :vartype annotations: dict[str, str]
127
+
128
+ :raises:
129
+ - :class:`InvalidMemberNameError <dbus_fast.InvalidMemberNameError>` - If the name of the signal is not a valid member name.
130
+ """
131
+
132
+ def __init__(
133
+ self,
134
+ name: str,
135
+ args: list[Arg] | None = None,
136
+ annotations: dict[str, str] | None = None,
137
+ ):
138
+ if name is not None:
139
+ assert_member_name_valid(name)
140
+
141
+ self.name = name
142
+ self.args = args or []
143
+ self.signature = "".join(arg.signature for arg in self.args)
144
+ self.annotations = annotations or {}
145
+
146
+ def from_xml(element):
147
+ """Convert an :class:`xml.etree.ElementTree.Element` to a :class:`Signal`.
148
+
149
+ The element must be valid DBus introspection XML for a ``signal``.
150
+
151
+ :param element: The parsed XML element.
152
+ :type element: :class:`xml.etree.ElementTree.Element`
153
+ :param is_root: Whether this is the root node
154
+ :type is_root: bool
155
+
156
+ :raises:
157
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the XML tree is not valid introspection data.
158
+ """
159
+ name = element.attrib.get("name")
160
+ if not name:
161
+ raise InvalidIntrospectionError('signals must have a "name" attribute')
162
+
163
+ args = []
164
+ for child in element:
165
+ if child.tag == "arg":
166
+ args.append(Arg.from_xml(child, ArgDirection.OUT))
167
+
168
+ annotations = _fetch_annotations(element)
169
+
170
+ signal = Signal(name, args, annotations)
171
+
172
+ return signal
173
+
174
+ def to_xml(self) -> ET.Element:
175
+ """Convert this :class:`Signal` into an :class:`xml.etree.ElementTree.Element`."""
176
+ element = ET.Element("signal")
177
+ element.set("name", self.name)
178
+
179
+ for arg in self.args:
180
+ element.append(arg.to_xml())
181
+
182
+ _extract_annotations(element, self.annotations)
183
+
184
+ return element
185
+
186
+
187
+ class Method:
188
+ """A class that represents a method exposed on an :class:`Interface`.
189
+
190
+ :ivar name: The name of this method.
191
+ :vartype name: str
192
+ :ivar in_args: A list of input arguments to this method.
193
+ :vartype in_args: list(Arg)
194
+ :ivar out_args: A list of output arguments to this method.
195
+ :vartype out_args: list(Arg)
196
+ :ivar in_signature: The collected signature string of the input arguments.
197
+ :vartype in_signature: str
198
+ :ivar out_signature: The collected signature string of the output arguments.
199
+ :vartype out_signature: str
200
+ :ivar annotations: The annotations of this method.
201
+ :vartype annotations: dict[str, str]
202
+
203
+ :raises:
204
+ - :class:`InvalidMemberNameError <dbus_fast.InvalidMemberNameError>` - If the name of this method is not valid.
205
+ """
206
+
207
+ def __init__(
208
+ self,
209
+ name: str,
210
+ in_args: list[Arg] = [],
211
+ out_args: list[Arg] = [],
212
+ annotations: dict[str, str] | None = None,
213
+ ):
214
+ assert_member_name_valid(name)
215
+
216
+ self.name = name
217
+ self.in_args = in_args
218
+ self.out_args = out_args
219
+ self.in_signature = "".join(arg.signature for arg in in_args)
220
+ self.out_signature = "".join(arg.signature for arg in out_args)
221
+ self.annotations = annotations or {}
222
+
223
+ def from_xml(element: ET.Element) -> "Method":
224
+ """Convert an :class:`xml.etree.ElementTree.Element` to a :class:`Method`.
225
+
226
+ The element must be valid DBus introspection XML for a ``method``.
227
+
228
+ :param element: The parsed XML element.
229
+ :type element: :class:`xml.etree.ElementTree.Element`
230
+ :param is_root: Whether this is the root node
231
+ :type is_root: bool
232
+
233
+ :raises:
234
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the XML tree is not valid introspection data.
235
+ """
236
+ name = element.attrib.get("name")
237
+ if not name:
238
+ raise InvalidIntrospectionError('interfaces must have a "name" attribute')
239
+
240
+ in_args = []
241
+ out_args = []
242
+
243
+ for child in element:
244
+ if child.tag == "arg":
245
+ direction = ArgDirection(child.attrib.get("direction", "in"))
246
+ arg = Arg.from_xml(child, direction)
247
+ if direction == ArgDirection.IN:
248
+ in_args.append(arg)
249
+ elif direction == ArgDirection.OUT:
250
+ out_args.append(arg)
251
+
252
+ annotations = _fetch_annotations(element)
253
+
254
+ return Method(name, in_args, out_args, annotations)
255
+
256
+ def to_xml(self) -> ET.Element:
257
+ """Convert this :class:`Method` into an :class:`xml.etree.ElementTree.Element`."""
258
+ element = ET.Element("method")
259
+ element.set("name", self.name)
260
+
261
+ for arg in self.in_args:
262
+ element.append(arg.to_xml())
263
+ for arg in self.out_args:
264
+ element.append(arg.to_xml())
265
+
266
+ _extract_annotations(element, self.annotations)
267
+
268
+ return element
269
+
270
+
271
+ class Property:
272
+ """A class that represents a DBus property exposed on an
273
+ :class:`Interface`.
274
+
275
+ :ivar name: The name of this property.
276
+ :vartype name: str
277
+ :ivar signature: The signature string for this property. Must be a single complete type.
278
+ :vartype signature: str
279
+ :ivar access: Whether this property is readable and writable.
280
+ :vartype access: :class:`PropertyAccess <dbus_fast.PropertyAccess>`
281
+ :ivar type: The parsed type of this property.
282
+ :vartype type: :class:`SignatureType <dbus_fast.SignatureType>`
283
+ :ivar annotations: The annotations of this property.
284
+ :vartype annotations: dict[str, str]
285
+
286
+ :raises:
287
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the property is not a single complete type.
288
+ - :class `InvalidSignatureError <dbus_fast.InvalidSignatureError>` - If the given signature is not valid.
289
+ - :class: `InvalidMemberNameError <dbus_fast.InvalidMemberNameError>` - If the member name is not valid.
290
+ """
291
+
292
+ def __init__(
293
+ self,
294
+ name: str,
295
+ signature: str,
296
+ access: PropertyAccess = PropertyAccess.READWRITE,
297
+ annotations: dict[str, str] | None = None,
298
+ validate: bool = True,
299
+ ):
300
+ if validate:
301
+ assert_member_name_valid(name)
302
+
303
+ tree = get_signature_tree(signature)
304
+ if len(tree.types) != 1:
305
+ raise InvalidIntrospectionError(
306
+ f"properties must have a single complete type. (has {len(tree.types)} types)"
307
+ )
308
+
309
+ self.name = name
310
+ self.signature = signature
311
+ self.access = access
312
+ self.type = tree.types[0]
313
+ self.annotations = annotations or {}
314
+
315
+ def from_xml(element, validate: bool = True):
316
+ """Convert an :class:`xml.etree.ElementTree.Element` to a :class:`Property`.
317
+
318
+ The element must be valid DBus introspection XML for a ``property``.
319
+
320
+ :param element: The parsed XML element.
321
+ :type element: :class:`xml.etree.ElementTree.Element`
322
+
323
+ :raises:
324
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the XML tree is not valid introspection data.
325
+ """
326
+ name = element.attrib.get("name")
327
+ signature = element.attrib.get("type")
328
+ access = PropertyAccess(element.attrib.get("access", "readwrite"))
329
+
330
+ if not name:
331
+ raise InvalidIntrospectionError('properties must have a "name" attribute')
332
+ if not signature:
333
+ raise InvalidIntrospectionError('properties must have a "type" attribute')
334
+
335
+ annotations = _fetch_annotations(element)
336
+
337
+ return Property(
338
+ name, signature, access, annotations=annotations, validate=validate
339
+ )
340
+
341
+ def to_xml(self) -> ET.Element:
342
+ """Convert this :class:`Property` into an :class:`xml.etree.ElementTree.Element`."""
343
+ element = ET.Element("property")
344
+ element.set("name", self.name)
345
+ element.set("type", self.signature)
346
+ element.set("access", self.access.value)
347
+ _extract_annotations(element, self.annotations)
348
+ return element
349
+
350
+
351
+ class Interface:
352
+ """A class that represents a DBus interface exported on on object path.
353
+
354
+ Contains information about the methods, signals, and properties exposed on
355
+ this interface.
356
+
357
+ :ivar name: The name of this interface.
358
+ :vartype name: str
359
+ :ivar methods: A list of methods exposed on this interface.
360
+ :vartype methods: list(:class:`Method`)
361
+ :ivar signals: A list of signals exposed on this interface.
362
+ :vartype signals: list(:class:`Signal`)
363
+ :ivar properties: A list of properties exposed on this interface.
364
+ :vartype properties: list(:class:`Property`)
365
+ :ivar annotations: The annotations of this interface.
366
+ :vartype annotations: dict[str, str]
367
+
368
+ :raises:
369
+ - :class:`InvalidInterfaceNameError <dbus_fast.InvalidInterfaceNameError>` - If the name is not a valid interface name.
370
+ """
371
+
372
+ def __init__(
373
+ self,
374
+ name: str,
375
+ methods: list[Method] | None = None,
376
+ signals: list[Signal] | None = None,
377
+ properties: list[Property] | None = None,
378
+ annotations: dict[str, str] | None = None,
379
+ ):
380
+ assert_interface_name_valid(name)
381
+
382
+ self.name = name
383
+ self.methods = methods if methods is not None else []
384
+ self.signals = signals if signals is not None else []
385
+ self.properties = properties if properties is not None else []
386
+ self.annotations = annotations or {}
387
+
388
+ @staticmethod
389
+ def from_xml(
390
+ element: ET.Element, validate_property_names: bool = True
391
+ ) -> "Interface":
392
+ """Convert a :class:`xml.etree.ElementTree.Element` into a
393
+ :class:`Interface`.
394
+
395
+ The element must be valid DBus introspection XML for an ``interface``.
396
+
397
+ :param element: The parsed XML element.
398
+ :type element: :class:`xml.etree.ElementTree.Element`
399
+
400
+ :raises:
401
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the XML tree is not valid introspection data.
402
+ """
403
+ name = element.attrib.get("name")
404
+ if not name:
405
+ raise InvalidIntrospectionError('interfaces must have a "name" attribute')
406
+
407
+ interface = Interface(name)
408
+
409
+ for child in element:
410
+ if child.tag == "method":
411
+ interface.methods.append(Method.from_xml(child))
412
+ elif child.tag == "signal":
413
+ interface.signals.append(Signal.from_xml(child))
414
+ elif child.tag == "property":
415
+ interface.properties.append(
416
+ Property.from_xml(child, validate=validate_property_names)
417
+ )
418
+
419
+ interface.annotations = _fetch_annotations(element)
420
+
421
+ return interface
422
+
423
+ def to_xml(self) -> ET.Element:
424
+ """Convert this :class:`Interface` into an :class:`xml.etree.ElementTree.Element`."""
425
+ element = ET.Element("interface")
426
+ element.set("name", self.name)
427
+
428
+ for method in self.methods:
429
+ element.append(method.to_xml())
430
+ for signal in self.signals:
431
+ element.append(signal.to_xml())
432
+ for prop in self.properties:
433
+ element.append(prop.to_xml())
434
+
435
+ _extract_annotations(element, self.annotations)
436
+
437
+ return element
438
+
439
+
440
+ class Node:
441
+ """A class that represents a node in an object path in introspection data.
442
+
443
+ A node contains information about interfaces exported on this path and
444
+ child nodes. A node can be converted to and from introspection XML exposed
445
+ through the ``org.freedesktop.DBus.Introspectable`` standard DBus
446
+ interface.
447
+
448
+ This class is an essential building block for a high-level DBus interface.
449
+ This is the underlying data structure for the :class:`ProxyObject
450
+ <dbus_fast.proxy_object.BaseProxyInterface>`. A :class:`ServiceInterface
451
+ <dbus_fast.service.ServiceInterface>` definition is converted to this class
452
+ to expose XML on the introspectable interface.
453
+
454
+ :ivar interfaces: A list of interfaces exposed on this node.
455
+ :vartype interfaces: list(:class:`Interface <dbus_fast.introspection.Interface>`)
456
+ :ivar nodes: A list of child nodes.
457
+ :vartype nodes: list(:class:`Node`)
458
+ :ivar name: The object path of this node.
459
+ :vartype name: str
460
+ :ivar is_root: Whether this is the root node. False if it is a child node.
461
+ :vartype is_root: bool
462
+
463
+ :raises:
464
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the name is not a valid node name.
465
+ """
466
+
467
+ def __init__(
468
+ self,
469
+ name: str | None = None,
470
+ interfaces: list[Interface] | None = None,
471
+ is_root: bool = True,
472
+ ):
473
+ if not is_root and not name:
474
+ raise InvalidIntrospectionError('child nodes must have a "name" attribute')
475
+
476
+ self.interfaces = interfaces if interfaces is not None else []
477
+ self.nodes: list[Node] = []
478
+ self.name = name
479
+ self.is_root = is_root
480
+
481
+ @staticmethod
482
+ def from_xml(
483
+ element: ET.Element, is_root: bool = False, validate_property_names: bool = True
484
+ ) -> "Node":
485
+ """Convert an :class:`xml.etree.ElementTree.Element` to a :class:`Node`.
486
+
487
+ The element must be valid DBus introspection XML for a ``node``.
488
+
489
+ :param element: The parsed XML element.
490
+ :type element: :class:`xml.etree.ElementTree.Element`
491
+ :param is_root: Whether this is the root node
492
+ :type is_root: bool
493
+ :param validate_property_names: Whether to validate property names or not
494
+ :type validate_property_names: bool
495
+
496
+ :raises:
497
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the XML tree is not valid introspection data.
498
+ """
499
+ node = Node(element.attrib.get("name"), is_root=is_root)
500
+
501
+ for child in element:
502
+ if child.tag == "interface":
503
+ node.interfaces.append(
504
+ Interface.from_xml(
505
+ child, validate_property_names=validate_property_names
506
+ )
507
+ )
508
+ elif child.tag == "node":
509
+ node.nodes.append(
510
+ Node.from_xml(
511
+ child, validate_property_names=validate_property_names
512
+ )
513
+ )
514
+
515
+ return node
516
+
517
+ @staticmethod
518
+ def parse(data: str, validate_property_names: bool = True) -> "Node":
519
+ """Parse XML data as a string into a :class:`Node`.
520
+
521
+ The string must be valid DBus introspection XML.
522
+
523
+ :param data: The XMl string.
524
+ :type data: str
525
+ :param validate_property_names: Whether to validate property names or not
526
+ :type validate_property_names: bool
527
+
528
+ :raises:
529
+ - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the string is not valid introspection data.
530
+ """
531
+ element = ET.fromstring(data)
532
+ if element.tag != "node":
533
+ raise InvalidIntrospectionError(
534
+ 'introspection data must have a "node" for the root element'
535
+ )
536
+
537
+ return Node.from_xml(
538
+ element, is_root=True, validate_property_names=validate_property_names
539
+ )
540
+
541
+ def to_xml(self) -> ET.Element:
542
+ """Convert this :class:`Node` into an :class:`xml.etree.ElementTree.Element`."""
543
+ element = ET.Element("node")
544
+
545
+ if self.name:
546
+ element.set("name", self.name)
547
+
548
+ for interface in self.interfaces:
549
+ element.append(interface.to_xml())
550
+ for node in self.nodes:
551
+ element.append(node.to_xml())
552
+
553
+ return element
554
+
555
+ def tostring(self) -> str:
556
+ """Convert this :class:`Node` into a DBus introspection XML string."""
557
+ header = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"\n"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">\n'
558
+
559
+ def indent(elem, level=0):
560
+ i = "\n" + level * " "
561
+ if len(elem):
562
+ if not elem.text or not elem.text.strip():
563
+ elem.text = i + " "
564
+ if not elem.tail or not elem.tail.strip():
565
+ elem.tail = i
566
+ for elem_ in elem:
567
+ indent(elem_, level + 1)
568
+ if not elem.tail or not elem.tail.strip():
569
+ elem.tail = i
570
+ elif level and (not elem.tail or not elem.tail.strip()):
571
+ elem.tail = i
572
+
573
+ xml = self.to_xml()
574
+ indent(xml)
575
+ return header + ET.tostring(xml, encoding="unicode").rstrip()
576
+
577
+ @staticmethod
578
+ def default(name: str | None = None) -> "Node":
579
+ """Create a :class:`Node` with the default interfaces supported by this library.
580
+
581
+ The default interfaces include:
582
+
583
+ * ``org.freedesktop.DBus.Introspectable``
584
+ * ``org.freedesktop.DBus.Peer``
585
+ * ``org.freedesktop.DBus.Properties``
586
+ * ``org.freedesktop.DBus.ObjectManager``
587
+ """
588
+ return Node(
589
+ name,
590
+ is_root=True,
591
+ interfaces=[
592
+ Interface(
593
+ "org.freedesktop.DBus.Introspectable",
594
+ methods=[
595
+ Method(
596
+ "Introspect", out_args=[Arg("s", ArgDirection.OUT, "data")]
597
+ )
598
+ ],
599
+ ),
600
+ Interface(
601
+ "org.freedesktop.DBus.Peer",
602
+ methods=[
603
+ Method(
604
+ "GetMachineId",
605
+ out_args=[Arg("s", ArgDirection.OUT, "machine_uuid")],
606
+ ),
607
+ Method("Ping"),
608
+ ],
609
+ ),
610
+ Interface(
611
+ "org.freedesktop.DBus.Properties",
612
+ methods=[
613
+ Method(
614
+ "Get",
615
+ in_args=[
616
+ Arg("s", ArgDirection.IN, "interface_name"),
617
+ Arg("s", ArgDirection.IN, "property_name"),
618
+ ],
619
+ out_args=[Arg("v", ArgDirection.OUT, "value")],
620
+ ),
621
+ Method(
622
+ "Set",
623
+ in_args=[
624
+ Arg("s", ArgDirection.IN, "interface_name"),
625
+ Arg("s", ArgDirection.IN, "property_name"),
626
+ Arg("v", ArgDirection.IN, "value"),
627
+ ],
628
+ ),
629
+ Method(
630
+ "GetAll",
631
+ in_args=[Arg("s", ArgDirection.IN, "interface_name")],
632
+ out_args=[Arg("a{sv}", ArgDirection.OUT, "props")],
633
+ ),
634
+ ],
635
+ signals=[
636
+ Signal(
637
+ "PropertiesChanged",
638
+ args=[
639
+ Arg("s", ArgDirection.OUT, "interface_name"),
640
+ Arg("a{sv}", ArgDirection.OUT, "changed_properties"),
641
+ Arg("as", ArgDirection.OUT, "invalidated_properties"),
642
+ ],
643
+ )
644
+ ],
645
+ ),
646
+ Interface(
647
+ "org.freedesktop.DBus.ObjectManager",
648
+ methods=[
649
+ Method(
650
+ "GetManagedObjects",
651
+ out_args=[
652
+ Arg(
653
+ "a{oa{sa{sv}}}",
654
+ ArgDirection.OUT,
655
+ "objpath_interfaces_and_properties",
656
+ )
657
+ ],
658
+ ),
659
+ ],
660
+ signals=[
661
+ Signal(
662
+ "InterfacesAdded",
663
+ args=[
664
+ Arg("o", ArgDirection.OUT, "object_path"),
665
+ Arg(
666
+ "a{sa{sv}}",
667
+ ArgDirection.OUT,
668
+ "interfaces_and_properties",
669
+ ),
670
+ ],
671
+ ),
672
+ Signal(
673
+ "InterfacesRemoved",
674
+ args=[
675
+ Arg("o", ArgDirection.OUT, "object_path"),
676
+ Arg("as", ArgDirection.OUT, "interfaces"),
677
+ ],
678
+ ),
679
+ ],
680
+ ),
681
+ ],
682
+ )