From 8d40569d8a5adcd793d544bef29634ba34bf1717 Mon Sep 17 00:00:00 2001 From: "John (J5) Palmieri" Date: Mon, 25 Apr 2005 22:54:28 +0000 Subject: * 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 --- ChangeLog | 26 ++ python/dbus.py | 510 +++++++++++++++++----------- python/dbus_bindings.pyx.in | 68 +++- python/examples/example-client.py | 12 +- python/examples/example-service.py | 12 +- python/examples/example-signal-emitter.py | 17 +- python/examples/example-signal-recipient.py | 25 +- python/examples/list-system-services.py | 13 +- 8 files changed, 454 insertions(+), 229 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0368b119..90c149e4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2005-04-25 John (J5) Palmieri + + * 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 * dbus/dbus-message.c (dbus_message_append_args): fix doc comment, diff --git a/python/dbus.py b/python/dbus.py index 72c09797..6b1ea558 100644 --- a/python/dbus.py +++ b/python/dbus.py @@ -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 + ' \n'%(interface) + for func in reflection_interface_method_hash[interface]: + reflection_data = reflection_data + ' \n'%(func.__name__) + for arg in func._dbus_args: + reflection_data = reflection_data + ' \n'%(arg) + + #reclaim some memory + func._dbus_args = None + reflection_data = reflection_data + ' \n' + if reflection_interface_signal_hash.has_key(interface): + for func in reflection_interface_signal_hash[interface]: + reflection_data = reflection_data + ' \n'%(func.__name__) + for arg in func._dbus_args: + reflection_data = reflection_data + ' \n'%(arg) + #reclaim some memory + func._dbus_args = None + reflection_data = reflection_data + ' \n' + + del reflection_interface_signal_hash[interface] + reflection_data = reflection_data + ' \n' + + for interface in reflection_interface_signal_hash.keys(): + reflection_data = reflection_data + ' \n'%(interface) + + for func in reflection_interface_signal_hash[interface]: + reflection_data = reflection_data + ' \n'%(func.__name__) + for arg in func._dbus_args: + reflection_data = reflection_data + ' \n'%(arg) + + #reclaim some memory + func._dbus_args = None + reflection_data = reflection_data + ' \n' + + reflection_data = reflection_data + ' \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 = '\n' + reflection_data = reflection_data + '\n'%(self._object_path) + reflection_data = reflection_data + self._dbus_reflection_data + reflection_data = reflection_data + '\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 + diff --git a/python/dbus_bindings.pyx.in b/python/dbus_bindings.pyx.in index 6681c458..0bfb9942 100644 --- a/python/dbus_bindings.pyx.in +++ b/python/dbus_bindings.pyx.in @@ -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) = 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 = 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, + 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): diff --git a/python/examples/example-client.py b/python/examples/example-client.py index 0270ed6c..7439e6bd 100644 --- a/python/examples/example-client.py +++ b/python/examples/example-client.py @@ -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") diff --git a/python/examples/example-service.py b/python/examples/example-service.py index e9f407f9..2209ff9f 100644 --- a/python/examples/example-service.py +++ b/python/examples/example-service.py @@ -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() diff --git a/python/examples/example-signal-emitter.py b/python/examples/example-signal-emitter.py index bcd5ad6f..8797e5f6 100644 --- a/python/examples/example-signal-emitter.py +++ b/python/examples/example-signal-emitter.py @@ -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) diff --git a/python/examples/example-signal-recipient.py b/python/examples/example-signal-recipient.py index b5306070..2da65ecb 100644 --- a/python/examples/example-signal-recipient.py +++ b/python/examples/example-signal-recipient.py @@ -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() diff --git a/python/examples/list-system-services.py b/python/examples/list-system-services.py index 9b114da9..80f63f98 100644 --- a/python/examples/list-system-services.py +++ b/python/examples/list-system-services.py @@ -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. -- cgit