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