]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
* python/dbus_bindings.pyx.in (send_with_reply_handlers): New send
authorJohn (J5) Palmieri <johnp@redhat.com>
Mon, 25 Apr 2005 22:54:28 +0000 (22:54 +0000)
committerJohn (J5) Palmieri <johnp@redhat.com>
Mon, 25 Apr 2005 22:54:28 +0000 (22:54 +0000)
method for doing async calls
(_pending_call_notification): New C function for handling pendning call
callbacks
(set_notify): New method for setting pending call notification

* python/dbus.py: new version tuple "version" is set at (0, 40, 0)
Async capabilities added to remote method calls
(Sender): class removed
(RemoteService): class removed
(ObjectTree): class removed for now
(RemoteObject): Renamed to ProxyObject
(RemoteMethod): Renamed to ProxyMethod
(method): Decorator added for decorating python methods as dbus methods
(signal): Decorator added for decorating python methods as signal emitters
(ObjectType): Metaclass added for generating introspection data and the
method callback vtable
(Interface): Wrapper class added to wrap objects in a dbus interface
(Object): Uses ObjectType as its metaclass and exports Introspect
of the org.freedesktop.DBus.Introspectable interface
(ValidationException, UnknownMethodException): new exceptions

* python/examples/*: Modified to fit with the new bindings

ChangeLog
python/dbus.py
python/dbus_bindings.pyx.in
python/examples/example-client.py
python/examples/example-service.py
python/examples/example-signal-emitter.py
python/examples/example-signal-recipient.py
python/examples/list-system-services.py

index 0368b119201124ee44d43a1c63289807a6bbdfe5..90c149e45e351974fa44e2eb7e409f38ba0f10c2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2005-04-25  John (J5) Palmieri  <johnp@redhat.com>
+
+       * python/dbus_bindings.pyx.in (send_with_reply_handlers): New send
+       method for doing async calls
+       (_pending_call_notification): New C function for handling pendning call
+       callbacks
+       (set_notify): New method for setting pending call notification
+       
+       * python/dbus.py: new version tuple "version" is set at (0, 40, 0)
+       Async capabilities added to remote method calls
+       (Sender): class removed
+       (RemoteService): class removed
+       (ObjectTree): class removed for now
+       (RemoteObject): Renamed to ProxyObject
+       (RemoteMethod): Renamed to ProxyMethod
+       (method): Decorator added for decorating python methods as dbus methods
+       (signal): Decorator added for decorating python methods as signal emitters
+       (ObjectType): Metaclass added for generating introspection data and the
+       method callback vtable
+       (Interface): Wrapper class added to wrap objects in a dbus interface
+       (Object): Uses ObjectType as its metaclass and exports Introspect
+       of the org.freedesktop.DBus.Introspectable interface
+       (ValidationException, UnknownMethodException): new exceptions
+
+       * python/examples/*: Modified to fit with the new bindings
+
 2005-04-23  Havoc Pennington  <hp@redhat.com>
 
        * dbus/dbus-message.c (dbus_message_append_args): fix doc comment,
index 72c0979738906dfb1a32c3ad410c29384437f6d6..6b1ea5580bd0b14ca280c033c540128f31c35c4c 100644 (file)
@@ -42,6 +42,11 @@ print(dbus_object.ListServices())
 """
 
 import dbus_bindings
+import re
+import inspect
+
+
+version = (0, 40, 0)
 
 _threads_initialized = 0
 def init_gthreads ():
@@ -50,13 +55,51 @@ def init_gthreads ():
         dbus_bindings.init_gthreads ()
         _threads_initialized = 1
 
-class Sender:
-    def __init__(self, interface, signal_name, service, path, message):
-        self.interface = interface
-        self.signal_name = signal_name 
-        self.service = service
-        self.path = path
-        self.message = message
+def _validate_interface_or_name(value):
+    elements = value.split('.')
+    if len(elements) <= 1:
+        raise ValidationException("%s must contain at least two elements seperated by a period ('.')"%(value))
+
+    validate = re.compile('[A-Za-z][\w_]*')
+    for element in elements:
+        if not validate.match(element):
+            raise ValidationException("Element %s of %s has invalid characters"%(element ,value))
+
+
+#Decorators
+def method(dbus_interface):
+    _validate_interface_or_name(dbus_interface)
+
+    def decorator(func):
+        func._dbus_is_method = True
+        func._dbus_interface = dbus_interface
+        func._dbus_args = inspect.getargspec(func)[0]
+        func._dbus_args.pop(0)
+        return func
+
+    return decorator
+
+def signal(dbus_interface):
+    _validate_interface_or_name(dbus_interface)
+    def decorator(func):
+        def emit_signal(self, *args, **keywords):
+           func(self, *args, **keywords)
+           message = dbus_bindings.Signal(self._object_path, dbus_interface, func.__name__)
+            iter = message.get_iter(True)
+            for arg in args:
+                iter.append(arg)
+        
+            self._connection.send(message)
+       
+        emit_signal._dbus_is_signal = True
+        emit_signal._dbus_interface = dbus_interface
+        emit_signal.__name__ = func.__name__
+        emit_signal._dbus_args = inspect.getargspec(func)[0]
+        emit_signal._dbus_args.pop(0)
+        return emit_signal
+
+    return decorator
+
 
 class Bus:
     """A connection to a DBus daemon.
@@ -85,50 +128,62 @@ class Bus:
     def get_connection(self):
         return self._connection
 
-    def get_service(self, service_name="org.freedesktop.Broadcast"):
-        """Get one of the RemoteServices connected to this Bus. service_name
-        is just a string of the form 'com.widgetcorp.MyService'
-        """
-        return RemoteService(self, service_name)
+    def get_session():
+        """Static method that returns the session bus"""
+        return SessionBus()
 
-    def add_signal_receiver(self, handler_function, signal_name=None, interface=None, service=None, path=None, expand_args=True):
-        match_rule = self._get_match_rule(signal_name, interface, service, path)
+    get_session = staticmethod(get_session)
 
-        if (not self._match_rule_to_receivers.has_key(match_rule)):
-            self._match_rule_to_receivers[match_rule] = {handler_function: True}
+    def get_system():
+        """Static method that returns the system bus"""
+        return SystemBus()
+
+    get_system = staticmethod(get_system)
+
+
+    def get_starter():
+        """Static method that returns the starter bus"""
+        return StarterBus()
+
+    get_starter = staticmethod(get_starter)
 
-        self._match_rule_to_receivers[match_rule][handler_function] = expand_args
+
+    def get_object(self, named_service, object_path):
+        """Get a proxy object to call over the bus"""
+        return ProxyObject(self, named_service, object_path)
+
+    def add_signal_receiver(self, handler_function, signal_name=None, dbus_interface=None, named_service=None, path=None):
+        match_rule = self._get_match_rule(signal_name, dbus_interface, named_service, path)
+
+        if (not self._match_rule_to_receivers.has_key(match_rule)):
+            self._match_rule_to_receivers[match_rule] = [handler_function]
+       else:
+           self._match_rule_to_receivers[match_rule].append(handler_function)
 
         dbus_bindings.bus_add_match(self._connection, match_rule)
 
-    def remove_signal_receiver(self, handler_function, signal_name=None, interface=None, service=None, path=None):
-        match_rule = self._get_match_rule(signal_name, interface, service, path)
+    def remove_signal_receiver(self, handler_function, signal_name=None, dbus_interface=None, named_service=None, path=None):
+        match_rule = self._get_match_rule(signal_name, dbus_interface, named_service, path)
 
         if self._match_rule_to_receivers.has_key(match_rule):
             if self._match_rule_to_receivers[match_rule].__contains__(handler_function):
                 self._match_rule_to_receivers[match_rule].pop(handler_function)
                 dbus_bindings.bus_remove_match(self._connection, match_rule)
 
-    def get_connection(self):
-        """Get the dbus_bindings.Connection object associated with this Bus"""
-        return self._connection
-    
-    def get_unix_user(self, service_name):
-        """Get the unix user for the given service_name on this Bus"""
-        return dbus_bindings.bus_get_unix_user(self._connection, service_name)
+    def get_unix_user(self, named_service):
+        """Get the unix user for the given named_service on this Bus"""
+        return dbus_bindings.bus_get_unix_user(self._connection, named_service)
 
-    def _get_match_rule(self, signal_name, interface, service, path):
+    def _get_match_rule(self, signal_name, dbus_interface, named_service, path):
         match_rule = "type='signal'"
-        if (interface):
-            match_rule = match_rule + ",interface='%s'" % (interface)
-        if (service):
-            if (service[0] != ':' and service != "org.freedesktop.DBus"):
-                bus_service = self.get_service("org.freedesktop.DBus")
-                bus_object = bus_service.get_object('/org/freedesktop/DBus',
-                                                     'org.freedesktop.DBus')
-                service = bus_object.GetNameOwner(service)
-
-            match_rule = match_rule + ",sender='%s'" % (service)
+        if (dbus_interface):
+            match_rule = match_rule + ",interface='%s'" % (dbus_interface)
+        if (named_service):
+            if (named_service[0] != ':' and named_service != "org.freedesktop.DBus"):
+                bus_object = self.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
+                named_service = bus_object.GetNameOwner(named_service, dbus_interface='org.freedesktop.DBus')
+
+            match_rule = match_rule + ",sender='%s'" % (named_service)
         if (path):
             match_rule = match_rule + ",path='%s'" % (path)
         if (signal_name):
@@ -139,28 +194,22 @@ class Bus:
         if (message.get_type() != dbus_bindings.MESSAGE_TYPE_SIGNAL):
             return dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
         
-        interface      = message.get_interface()
-        service        = message.get_sender()
+        dbus_interface      = message.get_interface()
+        named_service  = message.get_sender()
         path           = message.get_path()
         signal_name    = message.get_member()
 
-        match_rule = self._get_match_rule(signal_name, interface, service, path)
+        match_rule = self._get_match_rule(signal_name, dbus_interface, named_service, path)
 
         if (self._match_rule_to_receivers.has_key(match_rule)):
             receivers = self._match_rule_to_receivers[match_rule]
 
-            sender = Sender(interface, signal_name, service, path, message)
-            arg = [sender]
-            for receiver in receivers.iterkeys():
-               if receivers[receiver]:
-                    args = [sender]
-                   args.extend(message.get_args_list())
-                   receiver(*args)
-               else:
-                    receiver(*arg)
+            for receiver in receivers:
+               args = message.get_args_list()
+               receiver(*args)
 
-    def start_service_by_name(self, service):
-        return dbus_bindings.bus_start_service_by_name(self._connection, service)
+    def start_service_by_name(self, named_service):
+        return dbus_bindings.bus_start_service_by_name(self._connection, named_service)
 
 class SystemBus(Bus):
     """The system-wide message bus
@@ -182,59 +231,116 @@ class StarterBus(Bus):
         Bus.__init__(self, Bus.TYPE_STARTER)
 
 
-class RemoteObject:
-    """A remote Object.
+class Interface:
+    """An inteface into a remote object
+
+    An Interface can be used to wrap ProxyObjects
+    so that calls can be routed to their correct
+    dbus interface
+    """
 
-    A RemoteObject is provided by a RemoteService on a particular Bus. RemoteObjects
+    def __init__(self, object, dbus_interface):
+        self._obj = object
+        self._dbus_interface = dbus_interface
+
+    def connect_to_signal(self, signal_name, handler_function, dbus_interface = None):
+        if not dbus_interface:
+           dbus_interface = self._dbus_interface
+               
+        self._obj.connect_to_signal(signal_name, handler_function, dbus_interface)
+
+    def __getattr__(self, member, **keywords):
+        if (keywords.has_key('dbus_interface')):
+            _dbus_interface = keywords['dbus_interface']
+        else:
+            _dbus_interface = self._dbus_interface
+
+        if member == '__call__':
+            return object.__call__
+        else:
+            return self._obj.__getattr__(member, dbus_interface=_dbus_interface)
+
+class ProxyObject:
+    """A proxy to the remote Object.
+
+    A ProxyObject is provided by the Bus. ProxyObjects
     have member functions, and can be called like normal Python objects.
     """
-    def __init__(self, service, object_path, interface):
-        self._service      = service
+    def __init__(self, bus, named_service, object_path):
+        self._bus          = bus
+        self._named_service = named_service
         self._object_path  = object_path
-        self._interface    = interface
 
-    def connect_to_signal(self, signal_name, handler_function):
-        self._service.get_bus().add_signal_receiver(handler_function,
-                                                    signal_name=signal_name,
-                                                    interface=self._interface,
-                                                    service=self._service.get_service_name(),
-                                                    path=self._object_path)
+    def connect_to_signal(self, signal_name, handler_function, dbus_interface=None):
+        self._bus.add_signal_receiver(handler_function,
+                                      signal_name=signal_name,
+                                      dbus_interface=dbus_interface,
+                                      named_service=self._named_service,
+                                      path=self._object_path)
 
-    def __getattr__(self, member):
+
+
+    def __getattr__(self, member, **keywords):
         if member == '__call__':
             return object.__call__
         else:
-            return RemoteMethod(self._service.get_bus().get_connection(),
-                                self._service.get_service_name(),
-                                self._object_path, self._interface, member)
+            iface = None
+            if (keywords.has_key('dbus_interface')):
+                iface = keywords['dbus_interface']
+
+            return ProxyMethod(self._bus.get_connection(),
+                                self._named_service,
+                                self._object_path, iface, member)
 
 
-class RemoteMethod:
-    """A remote Method.
+class ProxyMethod:
+    """A proxy Method.
 
-    Typically a member of a RemoteObject. Calls to the
+    Typically a member of a ProxyObject. Calls to the
     method produce messages that travel over the Bus and are routed
-    to a specific Service.
+    to a specific named Service.
     """
-    def __init__(self, connection, service_name, object_path, interface, method_name):
+    def __init__(self, connection, named_service, object_path, dbus_interface, method_name):
         self._connection   = connection
-        self._service_name = service_name
+        self._named_service = named_service
         self._object_path  = object_path
-        self._interface    = interface
         self._method_name  = method_name
+        self._dbus_interface = dbus_interface
+
+    def __call__(self, *args, **keywords):
+        dbus_interface = self._dbus_interface
+        if (keywords.has_key('dbus_interface')):
+            dbus_interface = keywords['dbus_interface']
+
+        reply_handler = None
+        if (keywords.has_key('reply_handler')):
+            reply_handler = keywords['reply_handler']
 
-    def __call__(self, *args):
-        message = dbus_bindings.MethodCall(self._object_path, self._interface, self._method_name)
-        message.set_destination(self._service_name)
+        error_handler = None
+        if (keywords.has_key('error_handler')):
+            error_handler = keywords['error_handler']            
+
+        if not(reply_handler and error_handler):
+            if reply_handler:
+                raise MissingErrorself, HandlerException()
+            elif error_handler:
+                raise MissingReplyHandlerException()
+
+        message = dbus_bindings.MethodCall(self._object_path, dbus_interface, self._method_name)
+        message.set_destination(self._named_service)
         
         # Add the arguments to the function
         iter = message.get_iter(True)
         for arg in args:
             iter.append(arg)
 
-        reply_message = self._connection.send_with_reply_and_block(message, 5000)
-
-        args_tuple = reply_message.get_args_list()
+        if reply_handler:
+            result = self._connection.send_with_reply_handlers(message, -1, reply_handler, error_handler)
+            args_tuple = (result,)
+        else:
+            reply_message = self._connection.send_with_reply_and_block(message, -1)
+            args_tuple = reply_message.get_args_list()
+            
         if len(args_tuple) == 0:
             return
         elif len(args_tuple) == 1:
@@ -248,8 +354,8 @@ class Service:
     Just inherit from Service, providing the name of your service
     (e.g. org.designfu.SampleService).
     """
-    def __init__(self, service_name, bus=None):
-        self._service_name = service_name
+    def __init__(self, named_service, bus=None):
+        self._named_service = named_service
                              
         if bus == None:
             # Get the default bus
@@ -257,22 +363,40 @@ class Service:
         else:
             self._bus = bus
 
-        dbus_bindings.bus_request_name(self._bus.get_connection(), service_name)
+        dbus_bindings.bus_request_name(self._bus.get_connection(), named_service)
 
     def get_bus(self):
         """Get the Bus this Service is on"""
         return self._bus
 
-    def get_service_name(self):
+    def get_name(self):
         """Get the name of this service"""
-        return self._service_name
+        return self._named_service
 
-def _dispatch_dbus_method_call(target_method, argument_list, message):
+def _dispatch_dbus_method_call(target_methods, 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:
-        retval = target_method(message, *argument_list)
+        target_method = None
+        
+        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
+                    break
+        
+        if target_method:
+            retval = target_method(self, *argument_list)
+        else:
+            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?
@@ -289,13 +413,77 @@ def _dispatch_dbus_method_call(target_method, argument_list, message):
            
     return reply
 
-def _build_method_dictionary(methods):
-    method_dict = {}
-    for method in methods:
-        if method_dict.has_key(method.__name__):
-            print ('WARNING: registering DBus Object methods, already have a method named %s' % (method.__name__))
-        method_dict[method.__name__] = method
-    return method_dict
+class ObjectType(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 = {}
+
+        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]
+
+            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]
+
+       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 + '    <method 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
+                func._dbus_args = None
+                reflection_data = reflection_data + '    </method>\n'
+            if reflection_interface_signal_hash.has_key(interface):
+                for func in reflection_interface_signal_hash[interface]:
+                    reflection_data = 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
+                    func._dbus_args = None
+                    reflection_data = reflection_data + '    </signal>\n'
+
+                del reflection_interface_signal_hash[interface]
+            reflection_data = reflection_data + '  </interface>\n'
+
+       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 + '    <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
+                func._dbus_args = None
+                reflection_data = reflection_data + '    </signal>\n'
+
+            reflection_data = reflection_data + '  </interface>\n'
+
+        cls._dbus_reflection_data = reflection_data  
+       cls._dbus_method_vtable = method_vtable
+        
+        super(ObjectType, cls).__init__(name, bases, dct)
+
 
 class Object:
     """A base class for exporting your own Objects across the Bus.
@@ -304,129 +492,71 @@ class Object:
     across the Bus. These will appear as member functions of your
     ServiceObject.
     """
-    def __init__(self, object_path, service, dbus_methods=[]):
-        # Reversed constructor argument order. Add a temporary
-        # check to help people get things straightened out with minimal pain.
-        if type(service) == list:
-            raise TypeError, "dbus.Object.__init__(): the order of the 'service' and 'dbus_methods' arguments has been reversed (for consistency with dbus.ObjectTree)."
-        
+    __metaclass__ = ObjectType
+    
+    def __init__(self, object_path, service):
         self._object_path = object_path
         self._service = service
         self._bus = service.get_bus()
+            
         self._connection = self._bus.get_connection()
 
-        self._method_name_to_method = _build_method_dictionary(dbus_methods)
-        
         self._connection.register_object_path(object_path, self._unregister_cb, self._message_cb)
 
-    def emit_signal(self, interface, signal_name, *args):
-        message = dbus_bindings.Signal(self._object_path, interface, signal_name)
-        iter = message.get_iter(True)
-        for arg in args:
-            iter.append(arg)
-        
-        self._connection.send(message)
-
     def _unregister_cb(self, connection):
         print ("Unregister")
 
     def _message_cb(self, connection, message):
         target_method_name = message.get_member()
-        target_method = self._method_name_to_method[target_method_name]
+        target_methods = self._dbus_method_vtable[target_method_name]
         args = message.get_args_list()
-
-        reply = _dispatch_dbus_method_call(target_method, args, message)
+        
+        reply = _dispatch_dbus_method_call(target_methods, self, args, message)
 
         self._connection.send(reply)
 
-class ObjectTree:
-    """An object tree allows you to register a handler for a tree of object paths.
-    This means that literal Python objects do not need to be created for each object
-    over the bus, but you can have a virtual tree of objects handled by a single
-    Python object. There are two ways to handle method calls on virtual objects:
-
-    1) Pass a list of dbus_methods in to __init__. This works just like dbus.Object,
-    except an object_path is passed as the first argument to each method, denoting which
-    virtual object the call was made on. If all the objects in the tree support the same
-    methods, this is the best approach.
+    @method('org.freedesktop.DBus.Introspectable')
+    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'
 
-    2) Override object_method_called. This allows you to define the valid methods dynamically
-    on an object by object basis. For example, if providing an object tree that represented
-    a filesystem heirarchy, you'd only want an ls method on directory objects, not file objects.
-    """
+        return reflection_data
 
-    def __init__(self, base_path, service, dbus_methods=[]):
-        self._base_path = base_path
-        self._service = service
-        self._bus = service.get_bus()
-        self._connection = self._bus.get_connection()
+#Exceptions
+class MissingErrorHandlerException(Exception):
+    def __init__(self):
+        Exception.__init__(self)
 
-        self._method_name_to_method = _build_method_dictionary(dbus_methods)
-        
-        self._connection.register_fallback(base_path, self._unregister_cb, self._message_cb)
 
-    def relative_path_to_object_path(self, relative_path):
-        return ObjectPath(self._base_path + relative_path)
-        
-    def broadcast_signal(self, interface, signal_name, relative_path):
-        object_path = self.relative_path_to_object_path(relative_path)
-        message = dbus_bindings.Signal(object_path, interface, signal_name)
-        self._connection.send(message)
-        
-    def object_method_called(self, message, relative_path, method_name, argument_list):
-        """Override this method. Called with, object_path, the relative path of the object
-        under the base_path, the name of the method invoked, and a list of arguments
-        """
-        raise NotImplementedException, "object_method_called() must be overriden"
+    def __str__(self):
+        return "error_handler not defined: if you define a reply_handler you must also define an error_handler"
 
-    def _unregister_cb(self, connection):
-        print ("Unregister")
 
-    def _message_cb(self, connection, message):
-        target_object_full_path = message.get_path()
-        assert(self._base_path == target_object_full_path[:len(self._base_path)])
-        target_object_path = target_object_full_path[len(self._base_path):]
-        target_method_name = message.get_member()        
-        message_args = message.get_args_list()
+class MissingReplyHandlerException(Exception):
+    def __init__(self):
+        Exception.__init__(self)
 
-        try:
-            target_method = self._method_name_to_method[target_method_name]
-            args = [target_object_path] + message_args
-        except KeyError:
-            target_method = self.object_method_called
-            args = [target_object_path, target_method_name, message_args]
+    def __str__(self):
+        return "reply_handler not defined: if you define an error_handler you must also define a reply_handler"
 
-        reply = _dispatch_dbus_method_call(target_method, args, message)
+class ValidationException(Exception):
+    def __init__(self, msg=''):
+        self.msg = msg
+        Exception.__init__(self)
 
-        self._connection.send(reply)
-        
-class RemoteService:
-    """A remote service providing objects.
+    def __str__(self):
+        return "Error validating string: %s" % self.msg
 
-    A service is typically a process or application that provides
-    remote objects, but can also be the broadcast service that
-    receives signals from all applications on the Bus.
-    """
-    
-    def __init__(self, bus, service_name):
-        self._bus            = bus
-        self._service_name   = service_name
+class UnknownMethodException(Exception):
+    def __init__(self, msg=''):
+        self.msg = msg
+        Exception.__init__(self)
 
-    def get_bus(self):
-        return self._bus
+    def __str__(self):
+        return "Unknown method: %s" % self.msg
 
-    def get_service_name(self):
-        return self._service_name
-
-    def get_object(self, object_path, interface):
-        """Get an object provided by this Service that implements a
-        particular interface. object_path is a string of the form
-        '/com/widgetcorp/MyService/MyObject1'. interface looks a lot
-        like a service_name (they're often the same) and is of the form,
-        'com.widgetcorp.MyInterface', and mostly just defines the
-        set of member functions that will be present in the object.
-        """
-        return RemoteObject(self, object_path, interface)
-                             
 ObjectPath = dbus_bindings.ObjectPath
 ByteArray = dbus_bindings.ByteArray
+
index 6681c4587f64d78a681fd1f19c60dabfed5c93b3..0bfb9942ad206441955b1d6f53b05223fccd37c9 100644 (file)
@@ -222,6 +222,17 @@ cdef class Connection:
                                       NULL)
         return retval
 
+    def send_with_reply_handlers(self, Message message, timeout_milliseconds, reply_handler, error_handler):
+        retval = False
+        try:
+            (retval, pending_call) = self.send_with_reply(message, timeout_milliseconds)
+            if pending_call:
+                pending_call.set_notify(reply_handler, error_handler)
+        except Exception, e:
+            error_handler(e)
+            
+        return retval
+
     def send_with_reply(self, Message message, timeout_milliseconds):
         cdef dbus_bool_t retval
         cdef DBusPendingCall *cpending_call
@@ -376,7 +387,34 @@ cdef class Connection:
 
         return child_entries
 
-    
+cdef void _pending_call_notification(DBusPendingCall *pending_call, void *user_data):
+    cdef DBusMessage *dbus_message
+    cdef Message message
+   
+    (reply_handler, error_handler) = <object>user_data
+   
+    dbus_message = dbus_pending_call_steal_reply(pending_call)
+    message = Message(_create=0)
+    message._set_msg(dbus_message)
+
+    type = message.get_type()
+
+    if type == MESSAGE_TYPE_METHOD_RETURN:
+        args = message.get_args_list()
+        reply_handler(*args)
+    elif type == MESSAGE_TYPE_ERROR:
+        args = message.get_args_list()
+        error_handler(DBusException(args[0]))
+    else:
+        error_handler(DBusException('Unexpected Message Type: ' + message.type_to_name(type)))
+
+    dbus_message_unref(dbus_message)
+    dbus_pending_call_unref(pending_call)
+
+cdef void _pending_call_free_user_data(void *data):
+    call_tuple = <object>data
+    Py_XDECREF(call_tuple)
+
 cdef class PendingCall:
     cdef DBusPendingCall *pending_call
 
@@ -406,6 +444,13 @@ cdef class PendingCall:
     def block(self):
         dbus_pending_call_block(self.pending_call)
 
+    def set_notify(self, reply_handler, error_handler):
+        user_data = (reply_handler, error_handler)
+        Py_XINCREF(user_data)
+        dbus_pending_call_set_notify(self.pending_call, _pending_call_notification, 
+                                     <void *>user_data, _pending_call_free_user_data)
+        
+
 cdef class Watch:
     cdef DBusWatch* watch
 
@@ -914,29 +959,34 @@ cdef class Message:
     cdef DBusMessage *msg
 
     def __init__(self, message_type=MESSAGE_TYPE_INVALID,
-                 service=None, path=None, interface=None, method=None,
+                 service=None, path=None, dbus_interface=None, method=None,
                  Message method_call=None,
                  name=None,
                  Message reply_to=None, error_name=None, error_message=None,
                  _create=1):
+
         cdef char *cservice
+        cdef char *ciface
         cdef DBusMessage *cmsg
 
-        if (service == None):
-            cservice = NULL
-        else:
+        ciface = NULL
+        if (dbus_interface != None):
+            ciface = dbus_interface
+
+        cservice = NULL
+        if (service != None):
             cservice = service
             
         if not _create:
             return
 
         if message_type == MESSAGE_TYPE_METHOD_CALL:
-            self.msg = dbus_message_new_method_call(cservice, path, interface, method)
+            self.msg = dbus_message_new_method_call(cservice, path, ciface, method)
         elif message_type == MESSAGE_TYPE_METHOD_RETURN:
             cmsg = method_call._get_msg()
             self.msg = dbus_message_new_method_return(cmsg)
         elif message_type == MESSAGE_TYPE_SIGNAL:
-            self.msg = dbus_message_new_signal(path, interface, name)
+            self.msg = dbus_message_new_signal(path, ciface, name)
         elif message_type == MESSAGE_TYPE_ERROR:
             cmsg = reply_to._get_msg()
             self.msg = dbus_message_new_error(cmsg, error_name, error_message)
@@ -1105,11 +1155,11 @@ cdef class Message:
 
 class Signal(Message):
     def __init__(self, spath, sinterface, sname):
-        Message.__init__(self, MESSAGE_TYPE_SIGNAL, path=spath, interface=sinterface, name=sname)
+        Message.__init__(self, MESSAGE_TYPE_SIGNAL, path=spath, dbus_interface=sinterface, name=sname)
 
 class MethodCall(Message):
     def __init__(self, mpath, minterface, mmethod):
-        Message.__init__(self, MESSAGE_TYPE_METHOD_CALL, path=mpath, interface=minterface, method=mmethod)
+        Message.__init__(self, MESSAGE_TYPE_METHOD_CALL, path=mpath, dbus_interface=minterface, method=mmethod)
 
 class MethodReturn(Message):
     def __init__(self, method_call):
index 0270ed6cfd316734e4897b3a7c8ea0387a162f4e..7439e6bdd9ec5fdacc0be17e09f2b8370ac83365 100644 (file)
@@ -3,15 +3,14 @@
 import dbus
 
 bus = dbus.SessionBus()
-remote_service = bus.get_service("org.designfu.SampleService")
-remote_object = remote_service.get_object("/SomeObject",
-                                          "org.designfu.SampleInterface")
+remote_object = bus.get_object("org.designfu.SampleService", "/SomeObject")
+iface = dbus.Interface(remote_object, "org.designfu.SampleInterface")
 
-hello_reply_list = remote_object.HelloWorld("Hello from example-client.py!")
+hello_reply_list = remote_object.HelloWorld("Hello from example-client.py!", dbus_interface = "org.designfu.SampleInterface")
 
-hello_reply_tuple = remote_object.GetTuple()
+hello_reply_tuple = iface.GetTuple()
 
-hello_reply_dict = remote_object.GetDict()
+hello_reply_dict = iface.GetDict()
 
 print (hello_reply_list)
 
@@ -19,3 +18,4 @@ print str(hello_reply_tuple)
 
 print str(hello_reply_dict)
 
+print remote_object.Introspect(dbus_interface="org.freedesktop.DBus.Introspectable")
index e9f407f932719f81ed08826c0554f762299fbbe0..2209ff9f552cda69e35e94640adda247d7d0f096 100644 (file)
@@ -5,17 +5,19 @@ import gtk
 
 class SomeObject(dbus.Object):
     def __init__(self, service):
-        export = [self.HelloWorld, self.GetTuple, self.GetDict]
-        dbus.Object.__init__(self, "/SomeObject", service, export)
+        dbus.Object.__init__(self, "/SomeObject", service)
 
-    def HelloWorld(self, message, hello_message):
+    @dbus.method("org.designfu.SampleInterface")
+    def HelloWorld(self, hello_message):
         print (str(hello_message))
         return ["Hello", " from example-service.py"]
 
-    def GetTuple(self, message):
+    @dbus.method("org.designfu.SampleInterface")
+    def GetTuple(self):
         return ("Hello Tuple", " from example-service.py")
 
-    def GetDict(self, message):
+    @dbus.method("org.designfu.SampleInterface")
+    def GetDict(self):
         return {"first": "Hello Dict", "second": " from example-service.py"}
 
 session_bus = dbus.SessionBus()
index bcd5ad6f34df26214199b64b8af4423b8b58c3f5..8797e5f662a75f0cc754e7d93aef350c60635c09 100644 (file)
@@ -3,13 +3,20 @@ import gtk
 
 class TestObject(dbus.Object):
     def __init__(self, service):
-        dbus.Object.__init__(self, "/org/designfu/TestService/object", service, [self.emitHelloSignal])
+        dbus.Object.__init__(self, "/org/designfu/TestService/object", service)
 
-    def emitHelloSignal(self, message):
-        # Emit the signal
-        self.emit_signal(interface="org.designfu.TestService",
-                              signal_name="hello")
+    @dbus.signal('org.designfu.TestService')
+    def HelloSignal(self, message):
+        # The signal is emitted when this method exits
+       # You can have code here if you wish
+        pass
 
+    @dbus.method('org.designfu.TestService')
+    def emitHelloSignal(self):
+        #you emit signals by calling the signal's skeleton method
+       self.HelloSignal("Hello")
+       return "Signal emitted"
+       
 session_bus = dbus.SessionBus()
 service = dbus.Service("org.designfu.TestService", bus=session_bus)
 object = TestObject(service)
index b5306070caba7d00734a3edd31318b5f614e4a71..2da65ecb65ad663a2fb371a6aa5f6c5c657a85b8 100644 (file)
@@ -1,19 +1,30 @@
 import gtk
 import dbus
+import gobject
+
+def handle_reply(msg):
+    print msg
+
+def handle_error(e):
+    print str(e)
+
+def emit_signal():
+   #call the emitHelloSignal method async
+   object.emitHelloSignal(dbus_interface="org.designfu.TestService", 
+                          reply_handler = handle_reply, error_handler = handle_error)
+   return True
 
 bus = dbus.SessionBus()
-service = bus.get_service("org.designfu.TestService")
-object  = service.get_object("/org/designfu/TestService/object", "org.designfu.TestService")
+object  = bus.get_object("org.designfu.TestService","/org/designfu/TestService/object")
 
-def hello_signal_handler(sender):
-        print ("Received signal '%s.%s' from object '%s%s'"
-               % (sender.interface, sender.signal_name, sender.service, sender.path))
+def hello_signal_handler(hello_string):
+        print ("Received signal and it says: " + hello_string)
 
+object.connect_to_signal("HelloSignal", hello_signal_handler, dbus_interface="org.designfu.TestService")
 
-object.connect_to_signal("hello", hello_signal_handler)
+gobject.timeout_add(2000, emit_signal)
 
 # Tell the remote object to emit the signal
-object.emitHelloSignal()
 
 gtk.main()
 
index 9b114da95f794e8160b6f5452da9fc72b31d66ac..80f63f987bb36f4b3ff2ac50b50d626278426a28 100644 (file)
@@ -5,15 +5,14 @@ import dbus
 # Get a connection to the SYSTEM bus
 bus = dbus.SystemBus()
 
-# Get the service provided by the dbus-daemon named org.freedesktop.DBus
-dbus_service = bus.get_service('org.freedesktop.DBus')
-    
 # Get a reference to the desktop bus' standard object, denoted
-# by the path /org/freedesktop/DBus. The object /org/freedesktop/DBus
+# by the path /org/freedesktop/DBus. 
+dbus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
+
+# The object /org/freedesktop/DBus
 # implements the 'org.freedesktop.DBus' interface
-dbus_object = dbus_service.get_object('/org/freedesktop/DBus',
-                                      'org.freedesktop.DBus')
-            
+dbus_iface = dbus.Interface(dbus_object, 'org.freedesktop.DBus')
+
 # One of the member functions in the org.freedesktop.DBus interface
 # is ListServices(), which provides a list of all the other services
 # registered on this bus. Call it, and print the list.