blob: 9ec9c05cbc7780f570c947ac7c79227ec42c06f4 [file] [log] [blame]
Brad Bishop8ffe1e42016-02-11 16:15:40 -05001# Contributors Listed Below - COPYRIGHT 2016
2# [+] International Business Machines Corp.
3#
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14# implied. See the License for the specific language governing
15# permissions and limitations under the License.
16
Brad Bishopc66de762016-06-28 16:47:42 -040017import xml.etree.ElementTree as ET
Brad Bishop8ffe1e42016-02-11 16:15:40 -050018import dbus
19
20
21class IntrospectionNodeParser:
22 def __init__(self, data, tag_match=bool, intf_match=bool):
23 self.data = data
24 self.cache = {}
25 self.tag_match = tag_match
26 self.intf_match = intf_match
27
28 def parse_args(self):
29 return [x.attrib for x in self.data.findall('arg')]
30
31 def parse_children(self):
32 return [x.attrib['name'] for x in self.data.findall('node')]
33
34 def parse_method_or_signal(self):
35 name = self.data.attrib['name']
36 return name, self.parse_args()
37
38 def parse_interface(self):
39 iface = {}
40 iface['method'] = {}
41 iface['signal'] = {}
42
43 for node in self.data:
44 if node.tag not in ['method', 'signal']:
45 continue
46 if not self.tag_match(node.tag):
47 continue
48 p = IntrospectionNodeParser(
49 node, self.tag_match, self.intf_match)
50 n, element = p.parse_method_or_signal()
51 iface[node.tag][n] = element
52
53 return iface
54
55 def parse_node(self):
56 if self.cache:
57 return self.cache
58
59 self.cache['interfaces'] = {}
60 self.cache['children'] = []
61
62 for node in self.data:
63 if node.tag == 'interface':
64 p = IntrospectionNodeParser(
65 node, self.tag_match, self.intf_match)
66 name = p.data.attrib['name']
67 if not self.intf_match(name):
68 continue
69 self.cache['interfaces'][name] = p.parse_interface()
70 elif node.tag == 'node':
71 self.cache['children'] = self.parse_children()
72
73 return self.cache
74
75 def get_interfaces(self):
76 return self.parse_node()['interfaces']
77
78 def get_children(self):
79 return self.parse_node()['children']
80
81 def recursive_binding(self):
82 return any('/' in s for s in self.get_children())
83
84
85class IntrospectionParser:
86 def __init__(self, name, bus, tag_match=bool, intf_match=bool):
87 self.name = name
88 self.bus = bus
89 self.tag_match = tag_match
90 self.intf_match = intf_match
91
92 def _introspect(self, path):
93 try:
94 obj = self.bus.get_object(self.name, path, introspect=False)
95 iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)
96 data = iface.Introspect()
97 except dbus.DBusException:
98 return None
99
100 return IntrospectionNodeParser(
Brad Bishopc66de762016-06-28 16:47:42 -0400101 ET.fromstring(data),
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500102 self.tag_match,
103 self.intf_match)
104
105 def _discover_flat(self, path, parser):
106 items = {}
Brad Bishopd641c082018-01-31 15:55:58 -0500107 interfaces = parser.get_interfaces().keys()
Brad Bishop8ffe1e42016-02-11 16:15:40 -0500108 if interfaces:
109 items[path] = {}
110 items[path]['interfaces'] = interfaces
111
112 return items
113
114 def introspect(self, path='/', parser=None):
115 items = {}
116 if not parser:
117 parser = self._introspect(path)
118 if not parser:
119 return {}
120 items.update(self._discover_flat(path, parser))
121
122 if path != '/':
123 path += '/'
124
125 if parser.recursive_binding():
126 callback = self._discover_flat
127 else:
128 callback = self.introspect
129
130 for k in parser.get_children():
131 parser = self._introspect(path + k)
132 if not parser:
133 continue
134 items.update(callback(path + k, parser))
135
136 return items