summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn (J5) Palmieri <johnp@redhat.com>2005-04-25 22:54:28 +0000
committerJohn (J5) Palmieri <johnp@redhat.com>2005-04-25 22:54:28 +0000
commit8d40569d8a5adcd793d544bef29634ba34bf1717 (patch)
treeb7c2a6edc4ebf8e08f7f857b9d32e8e3ca9d7b99
parent64f3d8f67db09e0c84ed3ff009b86d0127fe82b4 (diff)
* 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
-rw-r--r--ChangeLog26
-rw-r--r--python/dbus.py510
-rw-r--r--python/dbus_bindings.pyx.in68
-rw-r--r--python/examples/example-client.py12
-rw-r--r--python/examples/example-service.py12
-rw-r--r--python/examples/example-signal-emitter.py17
-rw-r--r--python/examples/example-signal-recipient.py25
-rw-r--r--python/examples/list-system-services.py13
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 <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,
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 + ' <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
+
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) = <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):
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.