diff options
Diffstat (limited to 'obmc/dbuslib/introspection.py')
-rw-r--r-- | obmc/dbuslib/introspection.py | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/obmc/dbuslib/introspection.py b/obmc/dbuslib/introspection.py new file mode 100644 index 0000000..db83c6e --- /dev/null +++ b/obmc/dbuslib/introspection.py @@ -0,0 +1,136 @@ +# Contributors Listed Below - COPYRIGHT 2016 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. + +from xml.etree import ElementTree +import dbus + + +class IntrospectionNodeParser: + def __init__(self, data, tag_match=bool, intf_match=bool): + self.data = data + self.cache = {} + self.tag_match = tag_match + self.intf_match = intf_match + + def parse_args(self): + return [x.attrib for x in self.data.findall('arg')] + + def parse_children(self): + return [x.attrib['name'] for x in self.data.findall('node')] + + def parse_method_or_signal(self): + name = self.data.attrib['name'] + return name, self.parse_args() + + def parse_interface(self): + iface = {} + iface['method'] = {} + iface['signal'] = {} + + for node in self.data: + if node.tag not in ['method', 'signal']: + continue + if not self.tag_match(node.tag): + continue + p = IntrospectionNodeParser( + node, self.tag_match, self.intf_match) + n, element = p.parse_method_or_signal() + iface[node.tag][n] = element + + return iface + + def parse_node(self): + if self.cache: + return self.cache + + self.cache['interfaces'] = {} + self.cache['children'] = [] + + for node in self.data: + if node.tag == 'interface': + p = IntrospectionNodeParser( + node, self.tag_match, self.intf_match) + name = p.data.attrib['name'] + if not self.intf_match(name): + continue + self.cache['interfaces'][name] = p.parse_interface() + elif node.tag == 'node': + self.cache['children'] = self.parse_children() + + return self.cache + + def get_interfaces(self): + return self.parse_node()['interfaces'] + + def get_children(self): + return self.parse_node()['children'] + + def recursive_binding(self): + return any('/' in s for s in self.get_children()) + + +class IntrospectionParser: + def __init__(self, name, bus, tag_match=bool, intf_match=bool): + self.name = name + self.bus = bus + self.tag_match = tag_match + self.intf_match = intf_match + + def _introspect(self, path): + try: + obj = self.bus.get_object(self.name, path, introspect=False) + iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE) + data = iface.Introspect() + except dbus.DBusException: + return None + + return IntrospectionNodeParser( + ElementTree.fromstring(data), + self.tag_match, + self.intf_match) + + def _discover_flat(self, path, parser): + items = {} + interfaces = parser.get_interfaces().keys() + if interfaces: + items[path] = {} + items[path]['interfaces'] = interfaces + + return items + + def introspect(self, path='/', parser=None): + items = {} + if not parser: + parser = self._introspect(path) + if not parser: + return {} + items.update(self._discover_flat(path, parser)) + + if path != '/': + path += '/' + + if parser.recursive_binding(): + callback = self._discover_flat + else: + callback = self.introspect + + for k in parser.get_children(): + parser = self._introspect(path + k) + if not parser: + continue + items.update(callback(path + k, parser)) + + return items |