Source

pida-patches / core-pdbus-pyxml-introspect

Full commit
# HG changeset patch
# Parent c85ba1bff5e6b1cb2bc93802ec1155ded8d6282e
use py.xml to generate the dbus introspection metadata

diff --git a/pida/core/pdbus.py b/pida/core/pdbus.py
--- a/pida/core/pdbus.py
+++ b/pida/core/pdbus.py
@@ -11,6 +11,8 @@
 
 
 import os
+
+
 from pango import Font
 from pygtkhelpers.gthreads import gcall
 from pida.core.environment import workspace_name
@@ -162,21 +164,20 @@ class DbusOptionsManagerReal(Object):
             raise ValueError, "No object type found for %s" % type_
 
     def dbus_custom_introspect(self):
-        rv = '  <interface name="%s">\n' % (self.dbus_ns)
+        rv = ns.interface(name=self.dbus_ns)
         for option in self._options.itervalues():
             try:
-                typ = self.object_to_dbus(option.type)
+                type_sig = self.object_to_dbus(option.type)
             except ValueError, e:
                 print "Can't find conversation dbus conversation for ", option
                 continue
-            rv += '    <property name="%s" type="%s" access="readwrite"/>\n' % (option.name, typ)
+            rv.append(ns.property(name=option.name, type=type_sig, access="readwrite"))
 
         if hasattr(self, '_actions'):
             for action in self._actions.list_actions():
                 if action.get_name() not in self.dbus_no_activate:
-                    rv += '    <method name="activate_%s" />\n' % (action.get_name())
+                    rv.append(ns.method(name='activate_%s' % action.get_name()))
 
-        rv += '  </interface>\n'
         return rv
 
     def _message_cb(self, connection, message):
@@ -238,29 +239,10 @@ class DbusOptionsManagerReal(Object):
         """Return a string of XML encoding this object's supported interfaces,
         methods and signals.
         """
-        reflection_data = _dbus_bindings.DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
-        reflection_data += '<node name="%s">\n' % object_path
+        #reflection_data = _dbus_bindings.DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+        data = pdbus.reflect_service(self, object_path, connection)
+        return data.unicode(indent=2)
 
-        interfaces = self._dbus_class_table[self.__class__.__module__ + '.' + self.__class__.__name__]
-        for (name, funcs) in interfaces.iteritems():
-            reflection_data += '  <interface name="%s">\n' % (name)
-
-            for func in funcs.values():
-                if getattr(func, '_dbus_is_method', False):
-                    reflection_data += self.__class__._reflect_on_method(func)
-                elif getattr(func, '_dbus_is_signal', False):
-                    reflection_data += self.__class__._reflect_on_signal(func)
-
-            reflection_data += '  </interface>\n'
-
-        reflection_data += self.dbus_custom_introspect()
-
-        for name in connection.list_exported_child_objects(object_path):
-            reflection_data += '  <node name="%s"/>\n' % name
-
-        reflection_data += '</node>\n'
-
-        return reflection_data
 
 
 class DbusOptionsManagerNoop(object):
diff --git a/pida/utils/pdbus.py b/pida/utils/pdbus.py
--- a/pida/utils/pdbus.py
+++ b/pida/utils/pdbus.py
@@ -7,6 +7,9 @@
 from functools import partial, wraps
 import os
 
+import py
+
+
 import dbus
 from dbus.service import BusName
 from dbus.lowlevel import MethodCallMessage
@@ -24,6 +27,8 @@ def DBUS_NS(*path):
 def DBUS_PATH(*path, **kwargs):
     return "/".join(DBUS_PATH_PREFIX + path)
 
+class ns(py.xml.Namespace):
+    "namespace for introspection"
 
 def _dbus_decorator(f, ns=None, suffix=None):
     @wraps(f)
@@ -81,7 +86,53 @@ def list_pida_instances(include_this=Fal
             print 'failed to aks state of', name
     return result
 
+def reflect_service(obj, object_path, connection):
 
+    reflection_data = ns.node(name=object_path)
+    typename = '%s.%s' % (type(obj).__module__, type(obj).__name)
+    interfaces = obj._dbus_class_table[typename]
+
+    for (name, funcs) in interfaces.iteritems():
+        interface = ns.interface(name=name)
+        reflection_data.append(interface)
+
+        def sig_or_var(sig, var=True):
+            if sig:
+                return dbus.service.Signature(sig)
+            elif var:
+                return dbus.service._VariantSignature()
+            else:
+                return ()
+
+        for func in funcs.values():
+            args = func._dbus_args
+
+            if getattr(func, '_dbus_is_method', False):
+                in_sig = sig_or_var(func._dbus_in_signature)
+                out_sig = sig_or_var(func._dbus_out_signature, True)
+                method = ns.method(name=func.__name__)
+                for fname, fargs in zip(args, in_sig):
+                     method.append(ns.arg(direction="in", type=fargs, name=fname))
+                for sig in out_sig:
+                    method.append(ns.arg(direction="out", type=str(sig)))
+
+                interface.append(method)
+
+            elif getattr(func, '_dbus_is_signal', False):
+                sig = sig_or_var(func._dbus_signature)
+                signal = ns.signal(name=func.__name__)
+                for pname, pargs in zip(args, sig):
+                    signal.append(ns.arg(name=pname, type=pargs))
+
+                interface.append(signal)
+
+
+    reflection_data.append(obj.dbus_custom_introspect())
+
+    for name in connection.list_exported_child_objects(object_path):
+        reflection_data.append(ns.node(name=name))
+
+    return reflection_data
 
 
 class PidaRemote(object):