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