summaryrefslogtreecommitdiffstats
path: root/python/service.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/service.py')
-rw-r--r--python/service.py234
1 files changed, 144 insertions, 90 deletions
diff --git a/python/service.py b/python/service.py
index ce251ed5..3809ca59 100644
--- a/python/service.py
+++ b/python/service.py
@@ -28,30 +28,72 @@ class BusName:
"""Get the name of this service"""
return self._named_service
-def _dispatch_dbus_method_call(target_methods, self, argument_list, message):
+def _dispatch_dbus_method_call(self, argument_list, message):
"""Calls method_to_call using argument_list, but handles
exceptions, etc, and generates a reply to the DBus Message message
"""
try:
- target_method = None
-
+ method_name = message.get_member()
dbus_interface = message.get_interface()
- if dbus_interface == None:
- if target_methods:
- target_method = target_methods[0]
- else:
- for dbus_method in target_methods:
- if dbus_method._dbus_interface == dbus_interface:
- target_method = dbus_method
+ candidate = None
+ successful = False
+
+ # split up the cases when we do and don't have an interface because the
+ # latter is much simpler
+ if dbus_interface:
+ # search through the class hierarchy in python MRO order
+ for cls in self.__class__.__mro__:
+ # if we haven't got a candidate yet, and we find a class with a
+ # suitably named member, save this as a candidate
+ if (not candidate and method_name in cls.__dict__):
+ if ("_dbus_is_method" in cls.__dict__[method_name].__dict__
+ and "_dbus_interface" in cls.__dict__[method_name].__dict__):
+ # however if it is annotated for a different interface
+ # than we are looking for, it cannot be a candidate
+ if cls.__dict__[method_name]._dbus_interface == dbus_interface:
+ candidate = cls
+ sucessful = True
+ target_parent = cls.__dict__[method_name]
+ break
+ else:
+ pass
+ else:
+ candidate = cls
+
+ # if we have a candidate, carry on checking this and all
+ # superclasses for a method annoated as a dbus method
+ # on the correct interface
+ if (candidate and method_name in cls.__dict__
+ and "_dbus_is_method" in cls.__dict__[method_name].__dict__
+ and "_dbus_interface" in cls.__dict__[method_name].__dict__
+ and cls.__dict__[method_name]._dbus_interface == dbus_interface):
+ # the candidate is a dbus method on the correct interface,
+ # or overrides a method that is, success!
+ target_parent = cls.__dict__[method_name]
+ sucessful = True
break
-
- if target_method:
- retval = target_method(self, *argument_list)
+
else:
+ # simpler version of above
+ for cls in self.__class__.__mro__:
+ if (not candidate and method_name in cls.__dict__):
+ candidate = cls
+
+ if (candidate and method_name in cls.__dict__
+ and "_dbus_is_method" in cls.__dict__[method_name].__dict__):
+ target_parent = cls.__dict__[method_name]
+ sucessful = True
+ break
+
+ retval = candidate.__dict__[method_name](self, *argument_list)
+ target_name = str(candidate.__module__) + '.' + candidate.__name__ + '.' + method_name
+
+ if not sucessful:
if not dbus_interface:
raise UnknownMethodException('%s is not a valid method'%(message.get_member()))
else:
raise UnknownMethodException('%s is not a valid method of interface %s'%(message.get_member(), dbus_interface))
+
except Exception, e:
if e.__module__ == '__main__':
# FIXME: is it right to use .__name__ here?
@@ -64,10 +106,6 @@ def _dispatch_dbus_method_call(target_methods, self, argument_list, message):
else:
reply = dbus_bindings.MethodReturn(message)
- # temporary - about to replace the method lookup code...
- target_parent = target_method
- target_name = str(target_method)
-
# do strict adding if an output signature was provided
if target_parent._dbus_out_signature != None:
# iterate signature into list of complete types
@@ -100,96 +138,101 @@ def _dispatch_dbus_method_call(target_methods, self, argument_list, message):
elif retval != None:
iter = reply.get_iter(append=True)
iter.append(retval)
+
return reply
-class ObjectType(type):
+class InterfaceType(type):
def __init__(cls, name, bases, dct):
-
- #generate out vtable
- method_vtable = getattr(cls, '_dbus_method_vtable', {})
- reflection_data = getattr(cls, '_dbus_reflection_data', "")
-
- reflection_interface_method_hash = {}
- reflection_interface_signal_hash = {}
-
+ # these attributes are shared between all instances of the Interface
+ # object, so this has to be a dictionary that maps class names to
+ # the per-class introspection/interface data
+ class_table = getattr(cls, '_dbus_class_table', {})
+ cls._dbus_class_table = class_table
+ interface_table = class_table[cls.__module__ + '.' + name] = {}
+
+ # merge all the name -> method tables for all the interfaces
+ # implemented by our base classes into our own
+ for b in bases:
+ base_name = b.__module__ + '.' + b.__name__
+ if getattr(b, '_dbus_class_table', False):
+ for (interface, method_table) in class_table[base_name].iteritems():
+ our_method_table = interface_table.setdefault(interface, {})
+ our_method_table.update(method_table)
+
+ # add in all the name -> method entries for our own methods/signals
for func in dct.values():
- if getattr(func, '_dbus_is_method', False):
- if method_vtable.has_key(func.__name__):
- method_vtable[func.__name__].append(func)
- else:
- method_vtable[func.__name__] = [func]
-
- #generate a hash of interfaces so we can group
- #methods in the xml data
- if reflection_interface_method_hash.has_key(func._dbus_interface):
- reflection_interface_method_hash[func._dbus_interface].append(func)
- else:
- reflection_interface_method_hash[func._dbus_interface] = [func]
+ if getattr(func, '_dbus_interface', False):
+ method_table = interface_table.setdefault(func._dbus_interface, {})
+ method_table[func.__name__] = func
- elif getattr(func, '_dbus_is_signal', False):
- if reflection_interface_signal_hash.has_key(func._dbus_interface):
- reflection_interface_signal_hash[func._dbus_interface].append(func)
- else:
- reflection_interface_signal_hash[func._dbus_interface] = [func]
+ super(InterfaceType, cls).__init__(name, bases, dct)
- for interface in reflection_interface_method_hash.keys():
- reflection_data = reflection_data + ' <interface name="%s">\n'%(interface)
- for func in reflection_interface_method_hash[interface]:
- reflection_data = reflection_data + cls._reflect_on_method(func)
+ # methods are different to signals, so we have two functions... :)
+ def _reflect_on_method(cls, func):
+ args = func._dbus_args
- if reflection_interface_signal_hash.has_key(interface):
- for func in reflection_interface_signal_hash[interface]:
- reflection_data = reflection_data + cls._reflect_on_signal(func)
+ if func._dbus_in_signature:
+ # convert signature into a tuple so length refers to number of
+ # types, not number of characters
+ in_sig = tuple(dbus_bindings.Signature(func._dbus_in_signature))
- del reflection_interface_signal_hash[interface]
-
- reflection_data = reflection_data + ' </interface>\n'
+ if len(in_sig) > len(args):
+ raise ValueError, 'input signature is longer than the number of arguments taken'
+ elif len(in_sig) < len(args):
+ raise ValueError, 'input signature is shorter than the number of arguments taken'
+ else:
+ # magic iterator which returns as many v's as we need
+ in_sig = dbus_bindings.VariantSignature()
- for interface in reflection_interface_signal_hash.keys():
- reflection_data = reflection_data + ' <interface name="%s">\n'%(interface)
-
- for func in reflection_interface_signal_hash[interface]:
- reflection_data = reflection_data + cls._reflect_on_signal(func)
+ if func._dbus_out_signature:
+ out_sig = dbus_bindings.Signature(func._dbus_out_signature)
+ else:
+ # its tempting to default to dbus_bindings.Signature('v'), but
+ # for methods that return nothing, providing incorrect
+ # introspection data is worse than providing none at all
+ out_sig = []
+
+ reflection_data = ' <method name="%s">\n' % (func.__name__)
+ for pair in zip(in_sig, args):
+ reflection_data += ' <arg direction="in" type="%s" name="%s" />\n' % pair
+ for type in out_sig:
+ reflection_data += ' <arg direction="out" type="%s" />\n' % type
+ reflection_data += ' </method>\n'
- reflection_data = reflection_data + ' </interface>\n'
+ return reflection_data
- cls._dbus_reflection_data = reflection_data
- cls._dbus_method_vtable = method_vtable
-
- super(ObjectType, cls).__init__(name, bases, dct)
+ def _reflect_on_signal(cls, func):
+ args = func._dbus_args
- #reflections on methods and signals may look like similar code but may in fact
- #diverge in the future so keep them seperate
- def _reflect_on_method(cls, func):
- reflection_data = ' <method name="%s">\n'%(func.__name__)
- for arg in func._dbus_args:
- reflection_data = reflection_data + ' <arg name="%s" type="v" />\n'%(arg)
+ if func._dbus_signature:
+ # convert signature into a tuple so length refers to number of
+ # types, not number of characters
+ sig = tuple(dbus_bindings.Signature(func._dbus_signature))
- #reclaim some memory
- del func._dbus_args
- reflection_data = reflection_data + ' </method>\n'
+ if len(sig) > len(args):
+ raise ValueError, 'signal signature is longer than the number of arguments provided'
+ elif len(sig) < len(args):
+ raise ValueError, 'signal signature is shorter than the number of arguments provided'
+ else:
+ # magic iterator which returns as many v's as we need
+ sig = dbus_bindings.VariantSignature()
- return reflection_data
-
- def _reflect_on_signal(cls, func):
- reflection_data = ' <signal name="%s">\n'%(func.__name__)
- for arg in func._dbus_args:
- reflection_data = reflection_data + ' <arg name="%s" type="v" />\n'%(arg)
-
- #reclaim some memory
- del func._dbus_args
+ reflection_data = ' <signal name="%s">\n' % (func.__name__)
+ for pair in zip(sig, args):
+ reflection_data = reflection_data + ' <arg type="%s" name="%s" />\n' % pair
reflection_data = reflection_data + ' </signal>\n'
return reflection_data
-class Object:
+class Interface(object):
+ __metaclass__ = InterfaceType
+
+class Object(Interface):
"""A base class for exporting your own Objects across the Bus.
Just inherit from Object and provide a list of methods to share
across the Bus
"""
- __metaclass__ = ObjectType
-
def __init__(self, bus_name, object_path):
self._object_path = object_path
self._name = bus_name
@@ -205,10 +248,9 @@ class Object:
def _message_cb(self, connection, message):
try:
target_method_name = message.get_member()
- target_methods = self._dbus_method_vtable[target_method_name]
args = message.get_args_list()
- reply = _dispatch_dbus_method_call(target_methods, self, args, message)
+ reply = _dispatch_dbus_method_call(self, args, message)
self._connection.send(reply)
except Exception, e:
@@ -217,12 +259,24 @@ class Object:
str(e))
self._connection.send(error_reply)
- @method('org.freedesktop.DBus.Introspectable')
+ @method('org.freedesktop.DBus.Introspectable', in_signature='', out_signature='s')
def Introspect(self):
reflection_data = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">\n'
- reflection_data = reflection_data + '<node name="%s">\n'%(self._object_path)
- reflection_data = reflection_data + self._dbus_reflection_data
- reflection_data = reflection_data + '</node>\n'
+ reflection_data += '<node name="%s">\n' % (self._object_path)
+
+ 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 += '</node>\n'
return reflection_data