- commit
- dbeaf690edddd3b5dc1c3084ff67278dbe53e806
- parent
- d423f854ed683902f690d454b67cfa74adf934eb
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2026-02-17 05:46
add Schema.to_xml()
Diffstat
| A | tests/test_schema.py | 37 | +++++++++++++++++++++++++++++++++++++ |
| M | xibus/schema.py | 80 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 117 insertions, 0 deletions
diff --git a/tests/test_schema.py b/tests/test_schema.py
@@ -0,0 +1,37 @@
-1 1 import unittest
-1 2
-1 3 from xibus.schema import Schema
-1 4
-1 5 SCHEMA = """<?xml version='1.0' encoding='utf-8'?>
-1 6 <node>
-1 7 <interface name="org.freedesktop.DBus">
-1 8 <method name="RequestName">
-1 9 <arg direction="in" type="s" />
-1 10 <arg direction="in" type="u" />
-1 11 <arg direction="out" type="u" />
-1 12 </method>
-1 13 <method name="ReloadConfig" />
-1 14 <property name="Features" type="as" access="read" />
-1 15 <signal name="NameLost">
-1 16 <arg type="s" />
-1 17 </signal>
-1 18 </interface>
-1 19 <node name="foo" />
-1 20 </node>"""
-1 21
-1 22
-1 23 class TestSchema(unittest.TestCase):
-1 24 maxDiff = 1000
-1 25
-1 26 def test_from_xml(self):
-1 27 schema = Schema.from_xml(SCHEMA)
-1 28 self.assertEqual(schema.to_xml(), SCHEMA)
-1 29
-1 30 def test_construct(self):
-1 31 schema = Schema()
-1 32 schema.add_method('org.freedesktop.DBus', 'RequestName', ['s', 'u'], ['u'])
-1 33 schema.add_method('org.freedesktop.DBus', 'ReloadConfig', [], [])
-1 34 schema.add_property('org.freedesktop.DBus', 'Features', 'as', 'read')
-1 35 schema.add_signal('org.freedesktop.DBus', 'NameLost', ['s'])
-1 36 schema.nodes.append('foo')
-1 37 self.assertEqual(schema.to_xml(), SCHEMA)
diff --git a/xibus/schema.py b/xibus/schema.py
@@ -7,6 +7,14 @@ _Signal = namedtuple('Signal', ['args'])
7 7 _Interface = namedtuple('Interface', ['methods', 'properties', 'signals'])
8 8
9 9
-1 10 def el(tag, attrs):
-1 11 node = ET.Element(tag)
-1 12 for key, value in attrs.items():
-1 13 if value is not None:
-1 14 node.attrib[key] = value
-1 15 return node
-1 16
-1 17
10 18 def get_all_ordered(node, tag, parse):
11 19 return [(n.get('name'), parse(n)) for n in node.findall(tag)]
12 20
@@ -15,10 +23,22 @@ def get_all(node, tag, parse):
15 23 return dict(get_all_ordered(node, tag, parse))
16 24
17 25
-1 26 def normalize_args(args):
-1 27 return [(None, arg) if isinstance(arg, str) else arg for arg in args]
-1 28
-1 29
18 30 def parse_arg(node):
19 31 return node.get('type')
20 32
21 33
-1 34 def unparse_arg(name, typ, direction=None):
-1 35 return el('arg', {
-1 36 'name': name,
-1 37 'direction': direction,
-1 38 'type': typ,
-1 39 })
-1 40
-1 41
22 42 class Method(_Method):
23 43 @classmethod
24 44 def parse(cls, node):
@@ -28,6 +48,14 @@ class Method(_Method):
28 48 returns=get_all_ordered(node, './/arg[@direction!="in"]', parse_arg),
29 49 )
30 50
-1 51 def unparse(self, name):
-1 52 node = el('method', {'name': name})
-1 53 for _name, typ in self.args:
-1 54 node.append(unparse_arg(_name, typ, 'in'))
-1 55 for _name, typ in self.returns:
-1 56 node.append(unparse_arg(_name, typ, 'out'))
-1 57 return node
-1 58
31 59
32 60 class Property(_Property):
33 61 @classmethod
@@ -37,6 +65,13 @@ class Property(_Property):
37 65 access=node.get('access'),
38 66 )
39 67
-1 68 def unparse(self, name):
-1 69 return el('property', {
-1 70 'name': name,
-1 71 'type': self.type,
-1 72 'access': self.access,
-1 73 })
-1 74
40 75
41 76 class Signal(_Signal):
42 77 @classmethod
@@ -45,6 +80,12 @@ class Signal(_Signal):
45 80 args=get_all_ordered(node, 'arg', parse_arg),
46 81 )
47 82
-1 83 def unparse(self, name):
-1 84 node = el('signal', {'name': name})
-1 85 for _name, typ in self.args:
-1 86 node.append(unparse_arg(_name, typ))
-1 87 return node
-1 88
48 89
49 90 class Interface(_Interface):
50 91 @classmethod
@@ -55,12 +96,42 @@ class Interface(_Interface):
55 96 signals=get_all(node, 'signal', Signal.parse),
56 97 )
57 98
-1 99 def unparse(self, name):
-1 100 node = el('interface', {'name': name})
-1 101 for _name, method in self.methods.items():
-1 102 node.append(method.unparse(_name))
-1 103 for _name, prop in self.properties.items():
-1 104 node.append(prop.unparse(_name))
-1 105 for _name, signal in self.signals.items():
-1 106 node.append(signal.unparse(_name))
-1 107 return node
-1 108
58 109
59 110 class Schema:
60 111 def __init__(self, interfaces=None, nodes=None):
61 112 self.interfaces = interfaces or {}
62 113 self.nodes = nodes or []
63 114
-1 115 def add_property(self, iface, prop, typ, access):
-1 116 iface_data = self.interfaces.setdefault(iface, Interface({}, {}, {}))
-1 117 iface_data.properties[prop] = Property(typ, access)
-1 118
-1 119 def add_method(self, iface, method, args, returns):
-1 120 iface_data = self.interfaces.setdefault(iface, Interface({}, {}, {}))
-1 121 iface_data.methods[method] = Method(
-1 122 normalize_args(args), normalize_args(returns)
-1 123 )
-1 124
-1 125 def add_signal(self, iface, signal, args):
-1 126 iface_data = self.interfaces.setdefault(iface, Interface({}, {}, {}))
-1 127 iface_data.signals[signal] = Signal(normalize_args(args))
-1 128
-1 129 def add_defaults(self):
-1 130 self.add_method('org.freedesktop.DBus.Introspectable', 'Introspect', [], ['s'])
-1 131 self.add_method('org.freedesktop.DBus.Properties', 'Get', ['s', 's'], ['v'])
-1 132 self.add_method('org.freedesktop.DBus.Properties', 'Set', ['s', 's', 'v'], [])
-1 133 self.add_method('org.freedesktop.DBus.Properties', 'GetAll', ['s'], ['a{sv}'])
-1 134
64 135 @classmethod
65 136 def from_xml(cls, s):
66 137 tree = ET.fromstring(s)
@@ -68,3 +139,12 @@ class Schema:
68 139 interfaces=get_all(tree, 'interface', Interface.parse),
69 140 nodes=[n.get('name') for n in tree.findall('node')],
70 141 )
-1 142
-1 143 def to_xml(self):
-1 144 tree = el('node', {})
-1 145 for _name, iface in self.interfaces.items():
-1 146 tree.append(iface.unparse(_name))
-1 147 for _name in self.nodes:
-1 148 tree.append(el('node', {'name': _name}))
-1 149 ET.indent(tree)
-1 150 return ET.tostring(tree, encoding='unicode', xml_declaration=True)