From ce84a7761a567f5afaca03d0fb5c0378f293da80 Mon Sep 17 00:00:00 2001 From: "John (J5) Palmieri" Date: Tue, 16 Aug 2005 22:54:04 +0000 Subject: * python/__init__.py: Version updated (0, 43, 0) * python/dbus_bindings.pyx: - Fixed type objects to have self passed into __init__ - Added the Variant type - Add the ability to specify types or signatures for Array, Variant and Dictionary - (Connection::send_with_reply_handlers): return a PendingCall object - (_pending_call_notification): handle the case when an error is returned without an error message in the body - (MessageIter::get_boolean): return True or False instead of an integer - (MessageIter::python_value_to_dbus_sig): add direct checking of types and add checks for objects with embeded signatures or types (Array, Variant and Dictionary) - (MessageIter::append_byte): handle case when the value is a dbus.Byte - (MessageIter::append_dict): handle embeded types or signatures - (MessageIter::append_array): handle embeded types or signatures - (MessageIter::append_variant): new method * python/proxies.py: - (DeferedMethod): New. Dummy executable object used when queuing calls blocking on introspection data - (ProxyMethod::__call__): add the timeout keyword for specifying longer or shorter timeouts for method calls - (ProxyObject): Add first pass at an introspection state machine - (ProxyObject::__init__): Add introspect keyword for turing off an on introspection. - (ProxyObject::_Introspect): Internal Introspect call that bypasses the usual mechanisms for sending messages. This is to avoid a deadlock where the Intospect call would be queued waiting for the Introspect call to finish ;-) - (ProxyObject::_introspect_reply_handler): New. This method is called when introspection returns with no error - (ProxyObject::_introspect_error_handler): New. This method is called when introspection encounters an error - (ProxyObject::__getattr__): Code to handle different introspection states. Queue async calls or block blocking calls if we are introspecting. Pass through as normal if we are not or are done with introspecting. * python/service.py: Import signal and method from decorators.py * python/types.py: Add Variant type --- ChangeLog | 43 ++++++ python/__init__.py | 2 +- python/_dbus.py | 5 +- python/dbus_bindings.pyx | 223 +++++++++++++++++++++++++------- python/examples/example-client.py | 1 + python/examples/list-system-services.py | 4 +- python/proxies.py | 97 ++++++++++++-- python/service.py | 2 + python/types.py | 1 + 9 files changed, 312 insertions(+), 66 deletions(-) diff --git a/ChangeLog b/ChangeLog index c7fc00f1..dfceda32 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,46 @@ +2005-08-16 John (J5) Palmieri + + * python/__init__.py: Version updated (0, 43, 0) + + * python/dbus_bindings.pyx: + - Fixed type objects to have self passed into __init__ + - Added the Variant type + - Add the ability to specify types or signatures for Array, Variant and Dictionary + - (Connection::send_with_reply_handlers): return a PendingCall object + - (_pending_call_notification): handle the case when an error is returned + without an error message in the body + - (MessageIter::get_boolean): return True or False instead of an integer + - (MessageIter::python_value_to_dbus_sig): add direct checking of types and + add checks for objects with embeded signatures or types (Array, Variant and + Dictionary) + - (MessageIter::append_byte): handle case when the value is a dbus.Byte + - (MessageIter::append_dict): handle embeded types or signatures + - (MessageIter::append_array): handle embeded types or signatures + - (MessageIter::append_variant): new method + + * python/proxies.py: + - (DeferedMethod): New. Dummy executable object used when queuing calls blocking on + introspection data + - (ProxyMethod::__call__): add the timeout keyword for specifying longer or + shorter timeouts for method calls + - (ProxyObject): Add first pass at an introspection state machine + - (ProxyObject::__init__): Add introspect keyword for turing off an on + introspection. + - (ProxyObject::_Introspect): Internal Introspect call that bypasses the usual + mechanisms for sending messages. This is to avoid a deadlock where the Intospect + call would be queued waiting for the Introspect call to finish ;-) + - (ProxyObject::_introspect_reply_handler): New. This method is called when + introspection returns with no error + - (ProxyObject::_introspect_error_handler): New. This method is called when + introspection encounters an error + - (ProxyObject::__getattr__): Code to handle different introspection states. + Queue async calls or block blocking calls if we are introspecting. Pass through + as normal if we are not or are done with introspecting. + + * python/service.py: Import signal and method from decorators.py + + * python/types.py: Add Variant type + 2005-08-16 Colin Walters * glib/dbus-gobject.c (dbus_set_g_error): Don't lose if the diff --git a/python/__init__.py b/python/__init__.py index 945ef67e..a1ff407a 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -1,5 +1,5 @@ from _dbus import * from types import * -version = (0, 42, 0) +version = (0, 43, 0) _dbus_main_loop_setup_function = None diff --git a/python/_dbus.py b/python/_dbus.py index f0c7b069..6074be16 100644 --- a/python/_dbus.py +++ b/python/_dbus.py @@ -196,8 +196,9 @@ class Interface: if member == '__call__': return object.__call__ else: - return self._obj.__getattr__(member, dbus_interface=_dbus_interface) - + ret = self._obj.__getattr__(member, dbus_interface=_dbus_interface) + return ret + def __repr__(self): return ''%( self._obj, self._dbus_interface, id(self)) diff --git a/python/dbus_bindings.pyx b/python/dbus_bindings.pyx index 608ef544..a083f504 100644 --- a/python/dbus_bindings.pyx +++ b/python/dbus_bindings.pyx @@ -74,53 +74,53 @@ class ConnectionError(Exception): class ObjectPath(str): def __init__(self, value): - str.__init__(value) + str.__init__(self, value) class ByteArray(str): def __init__(self, value): - str.__init__(value) + str.__init__(self, value) class Signature(str): def __init__(self, value): - str.__init__(value) + str.__init__(self, value) class Byte(int): def __init__(self, value): - int.__init__(value) + int.__init__(self, value) class Boolean(int): def __init__(self, value): - int.__init__(value) + int.__init__(self, value) class Int16(int): def __init__(self, value): - int.__init__(value) + int.__init__(self, value) class UInt16(int): def __init__(self, value): if value < 0: raise TypeError('Unsigned integers must not have a negitive value') - int.__init__(value) + int.__init__(self, value) class Int32(int): def __init__(self, value): - int.__init__(value) + int.__init__(self, value) class UInt32(long): def __init__(self, value): if value < 0: raise TypeError('Unsigned integers must not have a negitive value') - long.__init__(value) + long.__init__(self, value) class Int64(long): def __init__(self, value): - long.__init__(value) + long.__init__(self, value) class UInt64(long): def __init__(self, value): if value < 0: raise TypeError('Unsigned integers must not have a negitive value') - long.__init__(value) + long.__init__(self, value) class Double(float): def __init__(self, value): @@ -128,19 +128,49 @@ class Double(float): class String(str): def __init__(self, value): - str.__init__(value) + str.__init__(self, value) class Array(list): - def __init__(self, value): - list.__init__(value) + def __init__(self, value, type=None, signature=None): + if signature and type: + raise TypeError('Can not mix type and signature arguments in a D-BUS Array') + + self.type = type + self.signature = signature + list.__init__(self, value) + +class Variant: + def __init__(self, value, type=None, signature=None): + self.value = value + if signature and type: + raise TypeError('Can not mix type and signature arguments in a D-BUS Variant') + + self.type = type + self.signature = signature + + def __repr__(self): + return repr(self.value) + + def __str__(self): + return str(self.value) class Struct(tuple): def __init__(self, value): - tuple.__init__(value) + tuple.__init__(self, value) class Dictionary(dict): - def __init__(self, value): - dict.__init__(value) + def __init__(self, value, key_type=None, value_type=None, signature=None): + if key_type and not value_type: + raise TypeError('When specifying a key_type you must also have a value_type in a D-BUS Dictionary') + elif value_type and not key_type: + raise TypeError('When specifying a value_type you must also have a key_type in a D-BUS Dictionary') + elif key_type and signature: + raise TypeError('Can not mix type arguments with signature arguments in a D-BUS Dictionary') + + self.key_type = key_type + self.value_type = value_type + self.signature = signature + dict.__init__(self, value) #forward delcerations cdef class Message @@ -190,7 +220,7 @@ cdef DBusHandlerResult cmessage_function_handler (DBusConnection *connection, return retval finally: PyGILState_Release(gil) - + cdef class Connection: def __init__(self, address=None, Connection _conn=None): cdef DBusConnection *c_conn @@ -297,7 +327,7 @@ cdef class Connection: except Exception, e: error_handler(e) - return retval + return (retval, pending_call) def send_with_reply(self, Message message, timeout_milliseconds): cdef dbus_bool_t retval @@ -470,7 +500,10 @@ cdef void _pending_call_notification(DBusPendingCall *pending_call, void *user_d reply_handler(*args) elif type == MESSAGE_TYPE_ERROR: args = message.get_args_list() - error_handler(DBusException(args[0])) + if len(args) > 0: + error_handler(DBusException(args[0])) + else: + error_handler(DBusException("")) else: error_handler(DBusException('Unexpected Message Type: ' + message.type_to_name(type))) @@ -628,7 +661,11 @@ cdef class MessageIter: def get_boolean(self): cdef dbus_bool_t c_val dbus_message_iter_get_basic(self.iter, &c_val) - return c_val + + if c_val: + return True + else: + return False def get_signature(self): signature_string = self.get_string() @@ -637,6 +674,7 @@ cdef class MessageIter: def get_int16(self): cdef dbus_int16_t c_val dbus_message_iter_get_basic(self.iter, &c_val) + return c_val def get_uint16(self): @@ -672,6 +710,7 @@ cdef class MessageIter: def get_string(self): cdef char *c_str dbus_message_iter_get_basic(self.iter, &c_str) + return c_str def get_object_path(self): @@ -797,60 +836,78 @@ cdef class MessageIter: elif ptype == list: ret = str(chr(TYPE_ARRAY)) ret = ret + self.python_value_to_dbus_sig(value[0], level) - elif isinstance(value, ObjectPath): + elif isinstance(value, ObjectPath) or value == ObjectPath: + ret = TYPE_OBJECT_PATH ret = str(chr(ret)) - elif isinstance(ByteArray): + elif isinstance(value, ByteArray) or value == ByteArray: ret = str(chr(TYPE_ARRAY)) + str(chr(TYPE_BYTE)) - elif isinstance(Signature): + elif isinstance(value, Signature) or value == Signature: ret = TYPE_SIGNATURE ret = str(chr(ret)) - elif isinstance(value, Byte): + elif isinstance(value, Byte) or value == Byte: ret = TYPE_BYTE ret = str(chr(ret)) - elif isinstance(value, Boolean): + elif isinstance(value, Boolean) or value == Boolean: ret = TYPE_BOOL ret = str(chr(ret)) - elif isinstance(value, Int16): + elif isinstance(value, Int16) or value == Int16: ret = TYPE_INT16 ret = str(chr(ret)) - elif isinstance(value, UInt16): + elif isinstance(value, UInt16) or value == UInt16: ret = TYPE_UINT16 ret = str(chr(ret)) - elif isinstance(value, Int32): + elif isinstance(value, Int32) or value == Int32: ret = TYPE_INT32 ret = str(chr(ret)) - elif isinstance(value, UInt32): + elif isinstance(value, UInt32) or value == UInt32: ret = TYPE_UINT32 ret = str(chr(ret)) - elif isinstance(value, Int64): + elif isinstance(value, Int64) or value == Int64: ret = TYPE_INT64 ret = str(chr(ret)) - elif isinstance(value, UInt64): + elif isinstance(value, UInt64) or value == UInt64: ret = TYPE_UINT64 ret = str(chr(ret)) - elif isinstance(value, Double): + elif isinstance(value, Double) or value == Double: ret = TYPE_DOUBLE ret = str(chr(ret)) - elif isinstance(value, String): + elif isinstance(value, String) or value == String: ret = TYPE_STRING ret = str(chr(ret)) elif isinstance(value, Array): ret = str(chr(TYPE_ARRAY)) - ret = ret + self.python_value_to_dbus_sig(value[0], level) - elif isinstance(value, Struct): + if value.type == None: + if value.signature: + ret = ret + value.signature + else: + ret = ret + self.python_value_to_dbus_sig(value[0], level) + else: + ret = ret + self.python_value_to_dbus_sig(value.type, level) + + elif isinstance(value, Struct) or value == Struct: ret = str(chr(STRUCT_BEGIN)) for item in value: ret = ret + self.python_value_to_dbus_sig(item, level) ret = ret + str(chr(STRUCT_END)) elif isinstance(value, Dictionary): - dict_list = value.items() - key, value = dict_list[0] - ret = str(chr(TYPE_ARRAY)) + str(chr(DICT_ENTRY_BEGIN)) - ret = ret + self.python_value_to_dbus_sig(key, level) - ret = ret + self.python_value_to_dbus_sig(value, level) + + if value.key_type and value.value_type: + ret = ret + self.python_value_to_dbus_sig(value.key_type, level) + ret = ret + self.python_value_to_dbus_sig(value.value_type, level) + elif value.signature: + ret = ret + value.signature + else: + dict_list = value.items() + + key, val = dict_list[0] + ret = ret + self.python_value_to_dbus_sig(key, level) + ret = ret + self.python_value_to_dbus_sig(val, level) + ret = ret + str(chr(DICT_ENTRY_END)) + elif isinstance(value, Variant) or value == Variant: + ret = ret + str(chr(TYPE_VARIANT)) else: raise TypeError, "Argument of unknown type '%s'" % (ptype) @@ -910,6 +967,8 @@ cdef class MessageIter: retval = self.append_struct(value) elif isinstance(value, Dictionary): retval = self.append_dict(value) + elif isinstance(value, Variant): + retval = self.append_variant(value) else: raise TypeError, "Argument of unknown type '%s'" % (value_type) @@ -922,10 +981,13 @@ cdef class MessageIter: def append_byte(self, value): cdef char b - if type(value) != str or len(value) != 1: + if type(value) == str and len(value) == 1: + b = ord(value) + elif type(value) == Byte: + b = value + else: raise TypeError - b = ord(value) return dbus_message_iter_append_basic(self.iter, TYPE_BYTE, &b) def append_int16(self, value): @@ -985,12 +1047,27 @@ cdef class MessageIter: level = self.level + 1 - dict_list = python_dict.items() - key, value = dict_list[0] + key = None + value = None sig = str(chr(DICT_ENTRY_BEGIN)) - sig = sig + self.python_value_to_dbus_sig(key) - sig = sig + self.python_value_to_dbus_sig(value) + + if isinstance(python_dict, Dictionary): + key = python_dict.key_type + value = python_dict.value_type + signature = python_dict.signature + + dict_list = python_dict.items() + + if signature: + sig = sig + signature + else: + if not (key and value): + key, value = dict_list[0] + + sig = sig + self.python_value_to_dbus_sig(key) + sig = sig + self.python_value_to_dbus_sig(value) + sig = sig + str(chr(DICT_ENTRY_END)) dbus_message_iter_open_container(self.iter, TYPE_ARRAY, sig, &c_dict_iter) @@ -1002,13 +1079,22 @@ cdef class MessageIter: dict_entry_iter = MessageIter(level) dict_entry_iter.__cinit__(&c_dict_entry_iter) - dict_entry_iter.append(key) - dict_entry_iter.append(value) + if not dict_entry_iter.append(key): + dbus_message_iter_close_container(dict_iter.iter, dict_entry_iter.iter) + dbus_message_iter_close_container(self.iter, dict_iter.iter) + return False + + if not dict_entry_iter.append(value): + dbus_message_iter_close_container(dict_iter.iter, dict_entry_iter.iter) + dbus_message_iter_close_container(self.iter, dict_iter.iter) + return False dbus_message_iter_close_container(dict_iter.iter, dict_entry_iter.iter) dbus_message_iter_close_container(self.iter, dict_iter.iter) + return True + def append_struct(self, python_struct): cdef DBusMessageIter c_struct_iter cdef MessageIter struct_iter @@ -1025,12 +1111,24 @@ cdef class MessageIter: dbus_message_iter_close_container(self.iter, struct_iter.iter) + return True + def append_array(self, python_list): cdef DBusMessageIter c_array_iter cdef MessageIter array_iter level = self.level + 1 - sig = self.python_value_to_dbus_sig(python_list[0]) + + sig = None + if isinstance(python_list, Array): + if python_list.type: + sig = self.python_value_to_dbus_sig(python_list.type) + elif python_list.signature: + sig = python_list.signature + else: + sig = self.python_value_to_dbus_sig(python_list[0]) + else: + sig = self.python_value_to_dbus_sig(python_list[0]) dbus_message_iter_open_container(self.iter, TYPE_ARRAY, sig, &c_array_iter) array_iter = MessageIter(level) @@ -1046,6 +1144,31 @@ cdef class MessageIter: return True + def append_variant(self, value): + cdef DBusMessageIter c_variant_iter + cdef MessageIter variant_iter + + level = self.level + 1 + + if value.signature: + sig = value.signature + elif value.type: + sig = self.python_value_to_dbus_sig(value.type) + else: + sig = self.python_value_to_dbus_sig(value.value) + + dbus_message_iter_open_container(self.iter, TYPE_VARIANT, sig, &c_variant_iter) + + variant_iter = MessageIter(level) + variant_iter.__cinit__(&c_variant_iter) + + if not variant_iter.append(value.value): + dbus_message_iter_close_container(self.iter, variant_iter.iter) + return False + + dbus_message_iter_close_container(self.iter, variant_iter.iter) + return True + def __str__(self): cdef DBusMessageIter c_array_iter cdef MessageIter array_iter diff --git a/python/examples/example-client.py b/python/examples/example-client.py index 7439e6bd..3d170bd4 100644 --- a/python/examples/example-client.py +++ b/python/examples/example-client.py @@ -19,3 +19,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/list-system-services.py b/python/examples/list-system-services.py index d4280551..4cad8717 100644 --- a/python/examples/list-system-services.py +++ b/python/examples/list-system-services.py @@ -2,6 +2,7 @@ """Lists services on the system bus """ + import dbus # Get a connection to the SYSTEM bus @@ -18,5 +19,4 @@ 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. -system_service_list = dbus_object.ListNames() -print str(system_service_list) +print dbus_object.ListNames() diff --git a/python/proxies.py b/python/proxies.py index 95c98a9c..c7dc02be 100644 --- a/python/proxies.py +++ b/python/proxies.py @@ -1,6 +1,17 @@ import dbus_bindings from exceptions import MissingReplyHandlerException, MissingErrorHandlerException +class DeferedMethod: + """A DeferedMethod + + This is returned instead of ProxyMethod when we are defering DBus calls + while waiting for introspection data to be returned + + This class can be used for debugging purposes + """ + def __call__(self, *args, **keywords): + return None + class ProxyMethod: """A proxy Method. @@ -17,15 +28,19 @@ class ProxyMethod: def __call__(self, *args, **keywords): dbus_interface = self._dbus_interface - if (keywords.has_key('dbus_interface')): + if keywords.has_key('dbus_interface'): dbus_interface = keywords['dbus_interface'] + timeout = -1 + if keywords.has_key('timeout'): + timeout = keywords['timeout'] + reply_handler = None - if (keywords.has_key('reply_handler')): + if keywords.has_key('reply_handler'): reply_handler = keywords['reply_handler'] error_handler = None - if (keywords.has_key('error_handler')): + if keywords.has_key('error_handler'): error_handler = keywords['error_handler'] if not(reply_handler and error_handler): @@ -43,10 +58,10 @@ class ProxyMethod: iter.append(arg) if reply_handler: - result = self._connection.send_with_reply_handlers(message, -1, reply_handler, error_handler) - args_tuple = (result,) + result = self._connection.send_with_reply_handlers(message, timeout, reply_handler, error_handler) + args_tuple = result else: - reply_message = self._connection.send_with_reply_and_block(message, -1) + reply_message = self._connection.send_with_reply_and_block(message, timeout) args_tuple = reply_message.get_args_list() if len(args_tuple) == 0: @@ -64,11 +79,31 @@ class ProxyObject: have member functions, and can be called like normal Python objects. """ ProxyMethodClass = ProxyMethod + DeferedMethodClass = DeferedMethod - def __init__(self, bus, named_service, object_path): - self._bus = bus + INTROSPECT_STATE_DONT_INTROSPECT = 0 + INTROSPECT_STATE_INTROSPECT_IN_PROGRESS = 1 + INTROSPECT_STATE_INTROSPECT_DONE = 2 + + #TODO: default introspect to False right now because it is not done yet + # make sure to default to True later + def __init__(self, bus, named_service, object_path, introspect=False): + self._bus = bus self._named_service = named_service - self._object_path = object_path + self._object_path = object_path + + #PendingCall object for Introspect call + self._pending_introspect = None + #queue of async calls waiting on the Introspect to return + self._pending_introspect_queue = [] + + if not introspect: + self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT + else: + self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS + + (result, self._pending_introspect) = self._Introspect() + def connect_to_signal(self, signal_name, handler_function, dbus_interface=None): self._bus.add_signal_receiver(handler_function, @@ -77,7 +112,28 @@ class ProxyObject: named_service=self._named_service, path=self._object_path) + def _Introspect(self): + message = dbus_bindings.MethodCall(self._object_path, 'org.freedesktop.DBus.Introspectable', 'Introspect') + message.set_destination(self._named_service) + + result = self._bus.get_connection().send_with_reply_handlers(message, -1, + self._introspect_reply_handler, + self._introspect_error_handler) + return result + + def _introspect_reply_handler(self, data): + self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_DONE + + for call in self._pending_introspect_queue: + (member, iface, args, keywords) = call + call_object = self.ProxyMethodClass(self._bus.get_connection(), + self._named_service, + self._object_path, iface, member) + + call_object(args, keywords) + def _introspect_error_handler(self, error): + self._introspect_state = self.INTROSPECT_STATE_DONT_INTROSPECT def __getattr__(self, member, **keywords): if member == '__call__': @@ -86,12 +142,31 @@ class ProxyObject: raise AttributeError(member) else: iface = None - if (keywords.has_key('dbus_interface')): + if keywords.has_key('dbus_interface'): iface = keywords['dbus_interface'] - return self.ProxyMethodClass(self._bus.get_connection(), + if self._introspect_state == self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS: + reply_handler = None + if keywords.has_key('reply_handler'): + reply_handler = keywords['reply_handler'] + + error_handler = None + if keywords.has_key('error_handler'): + error_handler = keywords['error_handler'] + + if not reply_handler: + self._pending_introspect.block() + else: + call = (memeber, iface, args, keywords) + self._pending_introspect_queue.append(call) + + ret = self.DeferedMethodClass() + return ret + + ret = self.ProxyMethodClass(self._bus.get_connection(), self._named_service, self._object_path, iface, member) + return ret def __repr__(self): return ''%( diff --git a/python/service.py b/python/service.py index edaef63f..bfe8fa5f 100644 --- a/python/service.py +++ b/python/service.py @@ -2,6 +2,8 @@ import dbus_bindings import _dbus from exceptions import UnknownMethodException +from decorators import method +from decorators import signal class BusName: """A base class for exporting your own Named Services across the Bus diff --git a/python/types.py b/python/types.py index 1a01d8e4..f3d348c6 100644 --- a/python/types.py +++ b/python/types.py @@ -16,3 +16,4 @@ String = dbus_bindings.String Array = dbus_bindings.Array Struct = dbus_bindings.Struct Dictionary = dbus_bindings.Dictionary +Variant = dbus_bindings.Variant -- cgit