summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--API_CHANGES.txt84
-rw-r--r--dbus/_dbus.py56
-rw-r--r--dbus/decorators.py13
-rw-r--r--dbus/exceptions.py3
-rw-r--r--dbus/proxies.py90
-rw-r--r--dbus/service.py82
-rw-r--r--dbus/types.py26
-rw-r--r--setup.py25
-rw-r--r--test/cross-test-client.py55
-rw-r--r--test/cross-test-server.py11
-rwxr-xr-xtest/run-test.sh3
-rwxr-xr-xtest/test-client.py33
-rwxr-xr-xtest/test-service.py16
-rwxr-xr-xtest/test-standalone.py126
14 files changed, 413 insertions, 210 deletions
diff --git a/API_CHANGES.txt b/API_CHANGES.txt
new file mode 100644
index 0000000..bffa85f
--- /dev/null
+++ b/API_CHANGES.txt
@@ -0,0 +1,84 @@
+===============================
+API changes in dbus-python-in-c
+===============================
+
+:Author: Simon McVittie
+:Contact: simon.mcvittie@collabora.co.uk
+:Organization: `Collabora Ltd`_
+:Date: 2006-09-26
+
+.. _ Collabora Ltd: http://www.collabora.co.uk/
+
+* Byte is a subtype of str rather than of int, which better matches
+ Python's conventions for dealing with byte streams. Its constructor
+ accepts either single-byte strings or integers in the range 0 to 255.
+
+* In method parameters, method returns from proxy methods, etc.,
+ integers arrive as instances of dbus.Int32 etc., bytes arrive as
+ Byte, and so on, rather than everything being converted to an
+ appropriate built-in Python type. This means you can tell exactly
+ what arguments went over the bus, and their types.
+
+* Variants arrive as method parameters, returns from proxy methods, etc.
+ as Variant objects. This is a bit of a big change, but makes things
+ completely unambiguous. To unwrap variants like the old Pyrex
+ implementation did, you can write::
+
+ while isinstance(myparam, dbus.Variant):
+ myparam = myparam.object
+
+ This should also work (and do nothing) under the Pyrex implementation.
+
+* The D-Bus integer types (dbus.Int32, etc.) are properly range-checked.
+
+* Variants are now immutable "value objects".
+
+* Variants are somewhat more useful: they can be cast using int(),
+ str(), long(), float(), you can iterate over them with iter() if they
+ contain an iterable, you can compare them with ``==`` and ``!=``,
+ and you can hash them if the underlying object supports it.
+
+* Array constructor takes arguments (iterable[, signature])
+ rather than (iterable[, type][, signature]); ditto Variant, Dict
+
+* Proxy methods with multiple return values return a tuple rather than
+ a list.
+
+* Calling a proxy method with reply ignored, or with async
+ handlers, returns None
+
+* The Boolean, String, Double and Struct types are now aliases for the
+ built-in bool, unicode, float and tuple types. Their use is vaguely
+ deprecated.
+
+* ConnectionError no longer exists (it was never raised)
+
+* dbus_bindings is now called _dbus_bindings, and is considerably
+ different internally:
+
+ * connections are private at the libdbus level: shared connections
+ are only shared among Python code
+
+ * The MessageIter stuff is now done in C: there's a much simpler
+ Python API, ``Message.append(...)`` where positional arguments are
+ the things to be appended, and the keyword argument ``signature``
+ controls how objects are interpreted
+
+ * The signature-guessing algorithm used if there is no proper
+ signature is exposed as a static method,
+ ``Message.guess_signature(*args)``
+
+ * Bus is a subclass of Connection rather than being a wrapper object
+ which has-a Connection
+
+ * Some relatively internal methods have been renamed starting with
+ an underscore - most Python code shouldn't need to use them, and
+ they expose the full complexity of Messages etc.
+
+ * The timeouts in _send_with_reply and in _send_with_reply_and_block
+ are in (possibly fractional) seconds, as is conventional in Python
+
+ * The specialized Message subclasses have names ending with Message
+
+..
+ vim:set sw=2 sts=2 et ft=rst tw=72:
diff --git a/dbus/_dbus.py b/dbus/_dbus.py
index f82d643..84acf01 100644
--- a/dbus/_dbus.py
+++ b/dbus/_dbus.py
@@ -41,8 +41,8 @@ For example, the dbus-daemon itself provides a service and some objects::
"""
__all__ = ('Bus', 'SystemBus', 'SessionBus', 'StarterBus', 'Interface',
- # From exceptions (some originally from _dbus_bindings)
- 'DBusException', 'ConnectionError', 'MissingErrorHandlerException',
+ # From exceptions (DBusException originally from _dbus_bindings)
+ 'DBusException', 'MissingErrorHandlerException',
'MissingReplyHandlerException', 'ValidationException',
'IntrospectionParserException', 'UnknownMethodException',
'NameExistsException',
@@ -58,7 +58,7 @@ from proxies import *
from exceptions import *
from matchrules import *
-class Bus(object):
+class Bus(_dbus_bindings._Bus):
"""A connection to a DBus daemon.
One of three possible standard buses, the SESSION, SYSTEM,
@@ -111,17 +111,13 @@ class Bus(object):
else:
raise ValueError('invalid bus_type %s' % bus_type)
- bus = object.__new__(subclass)
+ bus = _dbus_bindings._Bus.__new__(subclass, bus_type)
bus._bus_type = bus_type
bus._bus_names = weakref.WeakValueDictionary()
bus._match_rule_tree = SignalMatchTree()
- # FIXME: if you get a starter and a system/session bus connection
- # in the same process, it's the same underlying connection that
- # is returned by bus_get, but we initialise it twice
- bus._connection = _dbus_bindings.bus_get(bus_type, private)
- bus._connection.add_filter(bus._signal_func)
+ bus._add_filter(bus._signal_func)
if use_default_mainloop:
func = getattr(dbus, "_dbus_mainloop_setup_function", None)
@@ -138,13 +134,10 @@ class Bus(object):
# same object if __new__ returns a shared instance
pass
- def close(self):
- """Close the connection."""
- self._connection.close()
-
def get_connection(self):
- """Return the underlying `_dbus_bindings.Connection`."""
- return self._connection
+ return self
+
+ _connection = property(get_connection)
def get_session(private=False):
"""Static method that returns a connection to the session bus.
@@ -277,7 +270,7 @@ class Bus(object):
self._match_rule_tree.add(match_rule)
- _dbus_bindings.bus_add_match(self._connection, repr(match_rule))
+ self.add_match_string(repr(match_rule))
def remove_signal_receiver(self, handler_function,
signal_name=None,
@@ -302,19 +295,13 @@ class Bus(object):
self._match_rule_tree.remove(match_rule)
- #TODO we leak match rules in the lower level bindings. We need to ref count them
+ # TODO: remove the match string at the libdbus level
- def get_unix_user(self, named_service):
- """Get the numeric uid of the process which owns the given bus name
- on the connected bus daemon.
-
- :Parameters:
- `named_service` : str
- A bus name (may be either a unique name or a well-known name)
- """
- return _dbus_bindings.bus_get_unix_user(self._connection, named_service)
-
def _signal_func(self, connection, message):
+ """D-Bus filter function. Handle signals by dispatching to Python
+ callbacks kept in the match-rule tree.
+ """
+
if (message.get_type() != _dbus_bindings.MESSAGE_TYPE_SIGNAL):
return _dbus_bindings.HANDLER_RESULT_NOT_YET_HANDLED
@@ -327,21 +314,6 @@ class Bus(object):
self._match_rule_tree.exec_matches(match_rule, message)
- def start_service_by_name(self, named_service):
- """Start a service which will implement the given bus name on this
- Bus.
-
- :Parameters:
- `named_service` : str
- The well-known bus name for which an implementation is required
-
- :Returns: A tuple of 2 elements. The first is always True, the second is
- either START_REPLY_SUCCESS or START_REPLY_ALREADY_RUNNING.
-
- :Raises DBusException: if the service could not be started.
- """
- return _dbus_bindings.bus_start_service_by_name(self._connection, named_service)
-
def __repr__(self):
if self._bus_type == self.TYPE_SESSION:
name = 'SESSION'
diff --git a/dbus/decorators.py b/dbus/decorators.py
index 6dc2176..5486bb7 100644
--- a/dbus/decorators.py
+++ b/dbus/decorators.py
@@ -104,18 +104,15 @@ def signal(dbus_interface, signature=None):
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)
+ message = _dbus_bindings.SignalMessage(self._object_path, dbus_interface, func.__name__)
if emit_signal._dbus_signature:
- signature = tuple(_dbus_bindings.Signature(emit_signal._dbus_signature))
- for (arg, sig) in zip(args, signature):
- iter.append_strict(arg, sig)
+ message.append(signature=emit_signal._dbus_signature,
+ *args)
else:
- for arg in args:
- iter.append(arg)
+ message.append(*args)
- self._connection.send(message)
+ self._connection._send(message)
args = inspect.getargspec(func)[0]
args.pop(0)
diff --git a/dbus/exceptions.py b/dbus/exceptions.py
index 60ba3fd..801ede6 100644
--- a/dbus/exceptions.py
+++ b/dbus/exceptions.py
@@ -1,6 +1,6 @@
"""D-Bus exceptions."""
-__all__ = ('DBusException', 'ConnectionError', 'MissingErrorHandlerException',
+__all__ = ('DBusException', 'MissingErrorHandlerException',
'MissingReplyHandlerException', 'ValidationException',
'IntrospectionParserException', 'UnknownMethodException',
'NameExistsException')
@@ -8,7 +8,6 @@ __all__ = ('DBusException', 'ConnectionError', 'MissingErrorHandlerException',
import _dbus_bindings
DBusException = _dbus_bindings.DBusException
-ConnectionError = _dbus_bindings.ConnectionError
class MissingErrorHandlerException(DBusException):
def __init__(self):
diff --git a/dbus/proxies.py b/dbus/proxies.py
index d9cfb2a..9c06485 100644
--- a/dbus/proxies.py
+++ b/dbus/proxies.py
@@ -1,11 +1,36 @@
-import _dbus_bindings
-import introspect_parser
import sys
-from exceptions import MissingReplyHandlerException, MissingErrorHandlerException, IntrospectionParserException
+import logging
+
+import _dbus_bindings
+import dbus.introspect_parser as introspect_parser
+from dbus.exceptions import MissingReplyHandlerException, MissingErrorHandlerException, IntrospectionParserException, DBusException
__docformat__ = 'restructuredtext'
+_logger = logging.getLogger('dbus.proxies')
+
+
+class _ReplyHandler(object):
+ __slots__ = ('_on_error', '_on_reply')
+ def __init__(self, on_reply, on_error):
+ self._on_error = on_error
+ self._on_reply = on_reply
+
+ def __call__(self, message):
+ if isinstance(message, _dbus_bindings.MethodReturnMessage):
+ self._on_reply(*message.get_args_list())
+ elif isinstance(message, _dbus_bindings.ErrorMessage):
+ args = message.get_args_list()
+ if len(args) > 0:
+ self._on_error(DBusException(args[0]))
+ else:
+ self._on_error(DBusException())
+ else:
+ self._on_error(DBusException('Unexpected reply message type: %s'
+ % message))
+
+
class DeferedMethod:
"""A DeferedMethod
@@ -24,7 +49,7 @@ class DeferedMethod:
#block for now even on async
# FIXME: put ret in async queue in future if we have a reply handler
- self._proxy_method._proxy._pending_introspect.block()
+ self._proxy_method._proxy._pending_introspect._block()
ret = self._proxy_method (*args, **keywords)
return ret
@@ -82,35 +107,38 @@ class ProxyMethod:
if self._proxy._introspect_method_map.has_key (key):
introspect_sig = self._proxy._introspect_method_map[key]
- message = _dbus_bindings.MethodCall(self._object_path, dbus_interface, self._method_name)
+ message = _dbus_bindings.MethodCallMessage(destination=None,
+ path=self._object_path,
+ interface=dbus_interface,
+ method=self._method_name)
message.set_destination(self._named_service)
# Add the arguments to the function
- iter = message.get_iter(True)
-
- if introspect_sig:
- for (arg, sig) in zip(args, _dbus_bindings.Signature(introspect_sig)):
- iter.append_strict(arg, sig)
- else:
- for arg in args:
- iter.append(arg)
-
+ try:
+ message.append(signature=introspect_sig, *args)
+ except Exception, e:
+ _logger.error('Unable to set arguments %r according to '
+ 'introspected signature %r: %s: %s',
+ args, introspect_sig, e.__class__, e)
+ raise
+
+ # FIXME: using private API on Connection while I decide whether to
+ # make it public or what
if ignore_reply:
- result = self._connection.send(message)
- args_tuple = (result,)
+ result = self._connection._send(message)
+ return None
elif reply_handler:
- 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, timeout)
- args_tuple = reply_message.get_args_list()
-
- if len(args_tuple) == 0:
- return
- elif len(args_tuple) == 1:
- return args_tuple[0]
+ result = self._connection._send_with_reply(message, _ReplyHandler(reply_handler, error_handler), timeout/1000.0)
+ return None
else:
- return args_tuple
+ reply_message = self._connection._send_with_reply_and_block(message, timeout)
+ args_list = reply_message.get_args_list()
+ if len(args_list) == 0:
+ return None
+ elif len(args_list) == 1:
+ return args_list[0]
+ else:
+ return tuple(args_list)
class ProxyObject:
@@ -157,7 +185,7 @@ class ProxyObject:
else:
self._introspect_state = self.INTROSPECT_STATE_INTROSPECT_IN_PROGRESS
- (result, self._pending_introspect) = self._Introspect()
+ self._pending_introspect = self._Introspect()
def connect_to_signal(self, signal_name, handler_function, dbus_interface=None, **keywords):
@@ -194,12 +222,10 @@ class ProxyObject:
**keywords)
def _Introspect(self):
- message = _dbus_bindings.MethodCall(self._object_path, 'org.freedesktop.DBus.Introspectable', 'Introspect')
+ message = _dbus_bindings.MethodCallMessage(None, 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)
+ result = self._bus.get_connection()._send_with_reply(message, _ReplyHandler(self._introspect_reply_handler, self._introspect_error_handler), -1)
return result
def _introspect_execute_queue(self):
diff --git a/dbus/service.py b/dbus/service.py
index 32a3a28..75c0c1a 100644
--- a/dbus/service.py
+++ b/dbus/service.py
@@ -1,15 +1,21 @@
__all__ = ('BusName', 'Object', 'method', 'signal')
__docformat__ = 'restructuredtext'
-import _dbus_bindings
-import _dbus
+import sys
+import logging
import operator
import traceback
-from exceptions import NameExistsException
-from exceptions import UnknownMethodException
-from decorators import method
-from decorators import signal
+import _dbus_bindings
+import dbus._dbus as _dbus
+from dbus.exceptions import NameExistsException
+from dbus.exceptions import UnknownMethodException
+from dbus.decorators import method
+from dbus.decorators import signal
+
+
+_logger = logging.getLogger('dbus.service')
+
class _VariantSignature(object):
"""A fake method signature which, when iterated, yields an endless stream
@@ -77,7 +83,7 @@ class BusName(object):
_dbus_bindings.NAME_FLAG_REPLACE_EXISTING * replace_existing + \
_dbus_bindings.NAME_FLAG_DO_NOT_QUEUE * do_not_queue
- retval = _dbus_bindings.bus_request_name(bus.get_connection(), name, name_flags)
+ retval = bus.request_name(name, name_flags)
# TODO: more intelligent tracking of bus name states?
if retval == _dbus_bindings.REQUEST_NAME_REPLY_PRIMARY_OWNER:
@@ -115,7 +121,7 @@ class BusName(object):
# we can delete the low-level name here because these objects
# are guaranteed to exist only once for each bus name
def __del__(self):
- _dbus_bindings.bus_release_name(self._bus.get_connection(), self._name)
+ self._bus.release_name(self._name)
pass
def get_bus(self):
@@ -197,28 +203,25 @@ def _method_lookup(self, method_name, dbus_interface):
raise UnknownMethodException('%s is not a valid method' % method_name)
+# FIXME: if signature is '', we used to accept anything, but now insist on
+# zero args. Which was desired? -smcv
def _method_reply_return(connection, message, method_name, signature, *retval):
- reply = _dbus_bindings.MethodReturn(message)
- iter = reply.get_iter(append=True)
-
- # do strict adding if an output signature was provided
- if signature:
- if len(signature) > len(retval):
- raise TypeError('output signature %s is longer than the number of values returned by %s' %
- (signature, method_name))
- elif len(retval) > len(signature):
- raise TypeError('output signature %s is shorter than the number of values returned by %s' %
- (signature, method_name))
- else:
- for (value, sig) in zip(retval, signature):
- iter.append_strict(value, sig)
-
- # no signature, try and guess the return type by inspection
- else:
- for value in retval:
- iter.append(value)
-
- connection.send(reply)
+ reply = _dbus_bindings.MethodReturnMessage(message)
+ try:
+ reply.append(signature=signature, *retval)
+ except Exception, e:
+ if signature is None:
+ try:
+ signature = reply.guess_signature(retval) + ' (guessed)'
+ except Exception, e:
+ _logger.error('Unable to guess signature for arguments %r: '
+ '%s: %s', retval, e.__class__, e)
+ raise
+ _logger.error('Unable to append %r to message with signature %s: '
+ '%s: %s', retval, signature, e.__class__, e)
+ raise
+
+ connection._send(reply)
def _method_reply_error(connection, message, exception):
@@ -230,9 +233,9 @@ def _method_reply_error(connection, message, exception):
name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__)
contents = traceback.format_exc()
- reply = _dbus_bindings.Error(message, name, contents)
+ reply = _dbus_bindings.ErrorMessage(message, name, contents)
- connection.send(reply)
+ connection._send(reply)
class InterfaceType(type):
@@ -342,10 +345,10 @@ class Object(Interface):
self._connection = self._bus.get_connection()
- self._connection.register_object_path(object_path, self._unregister_cb, self._message_cb)
+ self._connection._register_object_path(object_path, self._message_cb, self._unregister_cb)
def _unregister_cb(self, connection):
- print ("Unregister")
+ _logger.info('Unregistering exported object %r', self)
def _message_cb(self, connection, message):
try:
@@ -358,9 +361,8 @@ class Object(Interface):
args = message.get_args_list()
keywords = {}
- # iterate signature into list of complete types
- if parent_method._dbus_out_signature:
- signature = tuple(_dbus_bindings.Signature(parent_method._dbus_out_signature))
+ if parent_method._dbus_out_signature is not None:
+ signature = _dbus_bindings.Signature(parent_method._dbus_out_signature)
else:
signature = None
@@ -384,17 +386,18 @@ class Object(Interface):
# otherwise we send the return values in a reply. if we have a
# signature, use it to turn the return value into a tuple as
# appropriate
- if parent_method._dbus_out_signature:
+ if signature is not None:
+ signature_tuple = tuple(signature)
# if we have zero or one return values we want make a tuple
# for the _method_reply_return function, otherwise we need
# to check we're passing it a sequence
- if len(signature) == 0:
+ if len(signature_tuple) == 0:
if retval == None:
retval = ()
else:
raise TypeError('%s has an empty output signature but did not return None' %
method_name)
- elif len(signature) == 1:
+ elif len(signature_tuple) == 1:
retval = (retval,)
else:
if operator.isSequenceType(retval):
@@ -406,7 +409,6 @@ class Object(Interface):
# no signature, so just turn the return into a tuple and send it as normal
else:
- signature = None
if retval == None:
retval = ()
else:
diff --git a/dbus/types.py b/dbus/types.py
index 0e04f42..8be55d7 100644
--- a/dbus/types.py
+++ b/dbus/types.py
@@ -2,22 +2,10 @@ __all__ = ('ObjectPath', 'ByteArray', 'Signature', 'Byte', 'Boolean',
'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64',
'Double', 'String', 'Array', 'Struct', 'Dictionary', 'Variant')
-import _dbus_bindings
-
-ObjectPath = _dbus_bindings.ObjectPath
-ByteArray = _dbus_bindings.ByteArray
-Signature = _dbus_bindings.Signature
-Byte = _dbus_bindings.Byte
-Boolean = _dbus_bindings.Boolean
-Int16 = _dbus_bindings.Int16
-UInt16 = _dbus_bindings.UInt16
-Int32 = _dbus_bindings.Int32
-UInt32 = _dbus_bindings.UInt32
-Int64 = _dbus_bindings.Int64
-UInt64 = _dbus_bindings.UInt64
-Double = _dbus_bindings.Double
-String = _dbus_bindings.String
-Array = _dbus_bindings.Array
-Struct = _dbus_bindings.Struct
-Dictionary = _dbus_bindings.Dictionary
-Variant = _dbus_bindings.Variant
+from _dbus_bindings import ObjectPath, ByteArray, Signature, Byte,\
+ Int16, UInt16, Int32, UInt32,\
+ Int64, UInt64, Variant, Dictionary, Array
+Boolean = bool
+String = unicode
+Double = float
+Struct = tuple
diff --git a/setup.py b/setup.py
index a295c6a..084eb57 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,6 @@ sys.path.append("dbus")
from distutils.core import setup
from distutils.extension import Extension
from distutils.command.clean import clean
-from Pyrex.Distutils import build_ext
import extract
@@ -20,15 +19,11 @@ def remove(filename):
class full_clean(clean):
def run(self):
clean.run(self)
- remove("dbus/extract.pyo")
- remove("dbus/_dbus_bindings.pxd")
- remove("dbus/_dbus_bindings.c")
- remove("dbus/_dbus_glib_bindings.c")
remove("ChangeLog")
includedirs_flag = ['-I.']
-dbus_includes = ['.']
-dbus_glib_includes = ['.']
+dbus_includes = ['.', 'include']
+dbus_glib_includes = ['.', 'include']
pipe = os.popen3("pkg-config --cflags dbus-1")
output = pipe[1].read().strip()
@@ -102,11 +97,6 @@ if error:
raise SystemExit
dbus_glib_libs.extend([ x.replace("-L", "") for x in output.split() ])
-output = open("dbus/_dbus_bindings.pxd", 'w')
-includedirs_flag.append('-Idbus/')
-extract.main("dbus/_dbus_bindings.pxd.in", includedirs_flag, output)
-output.close()
-
long_desc = '''D-BUS is a message bus system, a simple way for applications to
talk to one another.
@@ -143,22 +133,19 @@ setup(
"dbus/_util",
],
ext_modules=[
- Extension("_dbus_bindings", ["dbus/_dbus_bindings.pyx"],
+ Extension("_dbus_bindings", ["_dbus_bindings/module.c"],
+ extra_compile_args=['-O0'],
include_dirs=dbus_includes,
library_dirs=dbus_libs,
libraries=["dbus-1"],
-
),
- Extension("_dbus_glib_bindings", ["dbus/_dbus_glib_bindings.pyx"],
+ Extension("_dbus_glib_bindings", ["_dbus_glib_bindings/module.c"],
include_dirs=dbus_glib_includes,
library_dirs=dbus_glib_libs,
libraries=["dbus-glib-1", "dbus-1", "glib-2.0"],
- define_macros=[
- ('DBUS_API_SUBJECT_TO_CHANGE', '1')
- ],
),
],
- cmdclass={'build_ext': build_ext, 'clean': full_clean, 'check': dbus_python_check}
+ cmdclass={'clean': full_clean, 'check': dbus_python_check}
)
# vim:ts=4:sw=4:tw=80:si:ai:showmatch:et
diff --git a/test/cross-test-client.py b/test/cross-test-client.py
index 560561b..ae88f53 100644
--- a/test/cross-test-client.py
+++ b/test/cross-test-client.py
@@ -1,10 +1,11 @@
import sys
from sets import Set
from time import sleep
+import logging
import gobject
-from dbus import SessionBus, Interface, Array, Byte, Double, Variant, Boolean, ByteArray
+from dbus import SessionBus, Interface, Array, Byte, Double, Variant, Boolean, ByteArray, Int32
from dbus.service import BusName
import dbus.glib
@@ -14,6 +15,9 @@ from crosstest import CROSS_TEST_PATH, CROSS_TEST_BUS_NAME,\
SignalTestsImpl
+logging.basicConfig()
+
+
class Client(SignalTestsImpl):
fail_id = 0
expected = Set()
@@ -22,7 +26,7 @@ class Client(SignalTestsImpl):
for x in self.expected:
self.fail_id += 1
print "<%s> fail %d" % (x, self.fail_id)
- print "report <%d>: reply to %s didn't arrive" % (self.fail_id, x)
+ print "report %d: reply to %s didn't arrive" % (self.fail_id, x)
sys.stderr.write("CLIENT: asking server to Exit\n")
Interface(self.obj, INTERFACE_TESTS).Exit(reply_handler=self.quit_reply_handler, error_handler=self.quit_error_handler)
# if the server doesn't reply we'll just exit anyway
@@ -44,7 +48,7 @@ class Client(SignalTestsImpl):
if (input1, input2) != (42, 23):
self.fail_id += 1
print "<%s.Trigger> fail %d" % (INTERFACE_SIGNAL_TESTS, self.fail_id)
- print ("report <%d>: expected (42,23), got %r"
+ print ("report %d: expected (42,23), got %r"
% (self.fail_id, (input1, input2)))
else:
print "<%s.Trigger> pass" % INTERFACE_SIGNAL_TESTS
@@ -58,13 +62,14 @@ class Client(SignalTestsImpl):
except Exception, e:
self.fail_id += 1
print "<%s.%s> fail %d" % (interface, member, self.fail_id)
- print ("report <%d>: %s.%s%r: expected %r, raised %r \"%s\""
+ print ("report %d: %s.%s%r: expected %r, raised %r \"%s\""
% (self.fail_id, interface, member, args, ret, e, e))
+ __import__('traceback').print_exc()
return
if real_ret != ret:
self.fail_id += 1
print "<%s.%s> fail %d" % (interface, member, self.fail_id)
- print ("report <%d>: %s.%s%r: expected %r, got %r"
+ print ("report %d: %s.%s%r: expected %r, got %r"
% (self.fail_id, interface, member, args, ret, real_ret))
return
print "<%s.%s> pass" % (interface, member)
@@ -77,12 +82,12 @@ class Client(SignalTestsImpl):
if sender_path != '/Where/Ever':
self.fail_id += 1
print "<%s.Trigger> fail %d" % (INTERFACE_TESTS, self.fail_id)
- print ("report <%d>: expected signal from /Where/Ever, got %r"
+ print ("report %d: expected signal from /Where/Ever, got %r"
% (self.fail_id, sender_path))
elif param != 42:
self.fail_id += 1
print "<%s.Trigger> fail %d" % (INTERFACE_TESTS, self.fail_id)
- print ("report <%d>: expected signal param 42, got %r"
+ print ("report %d: expected signal param 42, got %r"
% (self.fail_id, parameter))
else:
print "<%s.Trigger> pass" % INTERFACE_TESTS
@@ -123,20 +128,18 @@ class Client(SignalTestsImpl):
def run_synchronous_tests(self, obj):
# "Single tests"
self.assert_method_eq(INTERFACE_SINGLE_TESTS, 6, 'Sum', [1, 2, 3])
- # FIXME: works, but should it?
self.assert_method_eq(INTERFACE_SINGLE_TESTS, 6, 'Sum', ['\x01', '\x02', '\x03'])
self.assert_method_eq(INTERFACE_SINGLE_TESTS, 6, 'Sum', [Byte(1), Byte(2), Byte(3)])
# Main tests
- self.assert_method_eq(INTERFACE_TESTS, 'foo', 'Identity', 'foo')
- # FIXME: Arrays of Byte -> lists of str, but Byte -> int?!
- self.assert_method_eq(INTERFACE_TESTS, 42, 'Identity', Byte(42))
- self.assert_method_eq(INTERFACE_TESTS, 42, 'Identity', Variant(Byte(42)))
- self.assert_method_eq(INTERFACE_TESTS, 42, 'Identity', Variant(Variant(Variant(Variant(Variant(Variant(Variant(Byte(42)))))))))
- self.assert_method_eq(INTERFACE_TESTS, 42.5, 'Identity', Double(42.5))
- self.assert_method_eq(INTERFACE_TESTS, -42.5, 'Identity', -42.5)
- self.assert_method_eq(INTERFACE_TESTS, 0x42, 'IdentityByte', '\x42')
- self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityByte', Byte(42))
+ self.assert_method_eq(INTERFACE_TESTS, Variant(u'foo', 's'), 'Identity', 'foo')
+ self.assert_method_eq(INTERFACE_TESTS, Variant(Byte(42)), 'Identity', Byte(42))
+ self.assert_method_eq(INTERFACE_TESTS, Variant(Byte(42)), 'Identity', Variant(Byte(42)))
+ self.assert_method_eq(INTERFACE_TESTS, Variant(Variant(Variant(Variant(Variant(Variant(Variant(Byte(42)))))))), 'Identity', Variant(Variant(Variant(Variant(Variant(Variant(Variant(Byte(42)))))))))
+ self.assert_method_eq(INTERFACE_TESTS, Variant(42.5), 'Identity', Double(42.5))
+ self.assert_method_eq(INTERFACE_TESTS, Variant(-42.5), 'Identity', -42.5)
+ self.assert_method_eq(INTERFACE_TESTS, Byte(0x42), 'IdentityByte', '\x42')
+ self.assert_method_eq(INTERFACE_TESTS, Byte(42), 'IdentityByte', Byte(42))
self.assert_method_eq(INTERFACE_TESTS, True, 'IdentityBool', 42)
self.assert_method_eq(INTERFACE_TESTS, True, 'IdentityBool', Boolean(42))
self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityInt16', 42)
@@ -147,12 +150,11 @@ class Client(SignalTestsImpl):
self.assert_method_eq(INTERFACE_TESTS, 42, 'IdentityUInt64', 42)
self.assert_method_eq(INTERFACE_TESTS, 42.3, 'IdentityDouble', 42.3)
self.assert_method_eq(INTERFACE_TESTS, u'\xa9', 'IdentityString', u'\xa9')
- # FIXME: if this is the intended API, then Byte shouldn't subclass int
- self.assert_method_eq(INTERFACE_TESTS, ['\x01','\x02','\x03'], 'IdentityArray', ByteArray('\x01\x02\x03'))
- self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityArray', [1,2,3])
- self.assert_method_eq(INTERFACE_TESTS, ['a','b','c'], 'IdentityArray', ['a','b','c'])
- self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityByteArray', ByteArray('\x01\x02\x03'))
- self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityByteArray', ['\x01', '\x02', '\x03'])
+ self.assert_method_eq(INTERFACE_TESTS, [Variant(Byte('\x01')),Variant(Byte('\x02')),Variant(Byte('\x03'))], 'IdentityArray', ByteArray('\x01\x02\x03'))
+ self.assert_method_eq(INTERFACE_TESTS, [Variant(Int32(1)),Variant(Int32(2)),Variant(Int32(3))], 'IdentityArray', [1,2,3])
+ self.assert_method_eq(INTERFACE_TESTS, [Variant(u'a'),Variant(u'b'),Variant(u'c')], 'IdentityArray', ['a','b','c'])
+ self.assert_method_eq(INTERFACE_TESTS, ['\x01','\x02','\x03'], 'IdentityByteArray', ByteArray('\x01\x02\x03'))
+ self.assert_method_eq(INTERFACE_TESTS, ['\x01','\x02','\x03'], 'IdentityByteArray', ['\x01', '\x02', '\x03'])
self.assert_method_eq(INTERFACE_TESTS, [False,True,True], 'IdentityBoolArray', [0,1,2])
self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityInt16Array', [1,2,3])
self.assert_method_eq(INTERFACE_TESTS, [1,2,3], 'IdentityUInt16Array', [1,2,3])
@@ -165,10 +167,9 @@ class Client(SignalTestsImpl):
self.assert_method_eq(INTERFACE_TESTS, 6, 'Sum', [1,2,3])
self.assert_method_eq(INTERFACE_TESTS, {'fps': ['unreal', 'quake'], 'rts': ['warcraft']}, 'InvertMapping', {'unreal': 'fps', 'quake': 'fps', 'warcraft': 'rts'})
- # FIXME: for Pythonicness, this should really return a tuple
- self.assert_method_eq(INTERFACE_TESTS, ['a', 1, 2], 'DeStruct', ('a', 1, 2))
- self.assert_method_eq(INTERFACE_TESTS, ['x'], 'Primitize', [Variant(Variant(['x']))])
- self.assert_method_eq(INTERFACE_TESTS, ['x', 1, 2], 'Primitize', [Variant([Variant(['x']), Array([Byte(1), Byte(2)])])])
+ self.assert_method_eq(INTERFACE_TESTS, ('a', 1, 2), 'DeStruct', ('a', 1, 2))
+ self.assert_method_eq(INTERFACE_TESTS, [Variant('x')], 'Primitize', [Variant(Variant(['x']))])
+ self.assert_method_eq(INTERFACE_TESTS, [Variant('x'), Variant(Byte(1)), Variant(Byte(2))], 'Primitize', [Variant([Variant(['x']), Array([Byte(1), Byte(2)])])])
self.assert_method_eq(INTERFACE_TESTS, False, 'Invert', 42)
self.assert_method_eq(INTERFACE_TESTS, True, 'Invert', 0)
diff --git a/test/cross-test-server.py b/test/cross-test-server.py
index 9931c8f..88c43d3 100644
--- a/test/cross-test-server.py
+++ b/test/cross-test-server.py
@@ -1,5 +1,6 @@
import sys
from sets import Set
+import logging
import gobject
@@ -13,6 +14,9 @@ from crosstest import CROSS_TEST_PATH, CROSS_TEST_BUS_NAME, \
SignalTestsImpl
+logging.basicConfig()
+
+
class VerboseSet(Set):
def add(self, thing):
print '<%s> ok' % thing
@@ -63,7 +67,7 @@ class SingleTestsImpl(dbus.service.Object):
@dbus.service.method(INTERFACE_SINGLE_TESTS, 'ay', 'u')
def Sum(self, input):
tested_things.add(INTERFACE_SINGLE_TESTS + '.Sum')
- return sum(input)
+ return sum(map(ord, input))
class TestsImpl(dbus.service.Object):
@@ -219,7 +223,10 @@ class TestsImpl(dbus.service.Object):
for y in self.primitivize_helper(x):
yield y
for y in self.primitivize_helper(input[x]):
- yield input[x]
+ yield y
+ elif isinstance(input, dbus.Variant):
+ for x in self.primitivize_helper(input()):
+ yield x
else:
yield input
diff --git a/test/run-test.sh b/test/run-test.sh
index c8cd276..d592309 100755
--- a/test/run-test.sh
+++ b/test/run-test.sh
@@ -32,6 +32,7 @@ if test -z "$DBUS_TEST_PYTHON_IN_RUN_TEST"; then
export DBUS_TEST_PYTHON_IN_RUN_TEST
exec tools/run-with-tmp-session-bus.sh $SCRIPTNAME $MODE
fi
+echo "running test-standalone.py"
+test/test-standalone.py || die "test-standalone.py failed"
echo "running test-client.py"
test/test-client.py || die "test-client.py failed"
-
diff --git a/test/test-client.py b/test/test-client.py
index 7e91dc1..831dcce 100755
--- a/test/test-client.py
+++ b/test/test-client.py
@@ -3,6 +3,7 @@ import sys
import os
import unittest
import time
+import logging
builddir = os.environ["DBUS_TOP_BUILDDIR"]
pydir = builddir
@@ -16,6 +17,10 @@ import gobject
import dbus.glib
import dbus.service
+
+logging.basicConfig()
+
+
pkg = dbus.__file__
if not pkg.startswith(pydir):
raise Exception("DBus modules (%s) are not being picked up from the package"%pkg)
@@ -32,7 +37,7 @@ test_types_vals = [1, 12323231, 3.14159265, 99999999.99,
{1:"a", 2:"b"}, {"a":1, "b":2}, #{"a":(1,"B")},
{1:1.1, 2:2.2}, [[1,2,3],[2,3,4]], [["a","b"],["c","d"]],
True, False,
- dbus.Int16(-10), dbus.UInt16(10),
+ dbus.Int16(-10), dbus.UInt16(10), 'SENTINEL',
#([1,2,3],"c", 1.2, ["a","b","c"], {"a": (1,"v"), "b": (2,"d")})
]
@@ -62,7 +67,7 @@ class TestDBusBindings(unittest.TestCase):
for send_val in test_types_vals:
print "Testing %s"% str(send_val)
recv_val = self.iface.Echo(send_val)
- self.assertEquals(send_val, recv_val)
+ self.assertEquals(send_val, recv_val.object)
def testBenchmarkIntrospect(self):
print "\n********* Benchmark Introspect ************"
@@ -91,7 +96,7 @@ class TestDBusBindings(unittest.TestCase):
if self.do_exit:
main_loop.quit()
- self.test_controler.assertEquals(val, self.expected_result)
+ self.test_controler.assertEquals(val.object, self.expected_result)
except Exception, e:
print "%s:\n%s" % (e.__class__, e)
@@ -100,7 +105,8 @@ class TestDBusBindings(unittest.TestCase):
if self.do_exit:
main_loop.quit()
- self.test_controler.assert_(val, False)
+ self.test_controler.assert_(False, '%s: %s' % (error.__class__,
+ error))
last_type = test_types_vals[-1]
for send_val in test_types_vals:
@@ -123,7 +129,7 @@ class TestDBusBindings(unittest.TestCase):
values = ["", ("",""), ("","",""), [], {}, ["",""], ["","",""]]
methods = [
(self.iface.ReturnOneString, 'SignalOneString', set([0]), set([0])),
- (self.iface.ReturnTwoStrings, 'SignalTwoStrings', set([1, 5]), set([5])),
+ (self.iface.ReturnTwoStrings, 'SignalTwoStrings', set([1, 5]), set([1])),
(self.iface.ReturnStruct, 'SignalStruct', set([1, 5]), set([1])),
# all of our test values are sequences so will marshall correctly into an array :P
(self.iface.ReturnArray, 'SignalArray', set(range(len(values))), set([3, 5, 6])),
@@ -136,7 +142,7 @@ class TestDBusBindings(unittest.TestCase):
try:
ret = method(value)
except Exception, e:
- print "%s(%r) raised %s" % (method._method_name, values[value], e.__class__)
+ print "%s(%r) raised %s: %s" % (method._method_name, values[value], e.__class__, e)
# should fail if it tried to marshal the wrong type
self.assert_(value not in success_values, "%s should succeed when we ask it to return %r\n%s\n%s" % (method._method_name, values[value], e.__class__, e))
@@ -148,7 +154,7 @@ class TestDBusBindings(unittest.TestCase):
# check the value is right too :D
returns = map(lambda n: values[n], return_values)
- self.assert_(ret in returns, "%s should return one of %r" % (method._method_name, returns))
+ self.assert_(ret in returns, "%s should return one of %r but it returned %r instead" % (method._method_name, returns, ret))
print "\nTrying correct emission of", signal
for value in range(len(values)):
@@ -183,12 +189,11 @@ class TestDBusBindings(unittest.TestCase):
print "calling AsynchronousMethod with %s %s %s" % (async, fail, val)
ret = self.iface.AsynchronousMethod(async, fail, val)
except Exception, e:
- print "%s:\n%s" % (e.__class__, e)
- self.assert_(fail)
+ self.assert_(fail, '%s: %s' % (e.__class__, e))
+ print "Expected failure: %s: %s" % (e.__class__, e)
else:
- self.assert_(not fail)
- print val, ret
- self.assert_(val == ret)
+ self.assert_(not fail, 'Expected failure but succeeded?!')
+ self.assertEquals(val, ret.object)
def testBusInstanceCaching(self):
print "\n********* Testing dbus.Bus instance sharing *********"
@@ -262,7 +267,7 @@ class TestDBusBindings(unittest.TestCase):
del names
bus = dbus.Bus()
- ret = _dbus_bindings.bus_name_has_owner(bus._connection, 'org.freedesktop.DBus.Python.TestName')
+ ret = bus.name_has_owner('org.freedesktop.DBus.Python.TestName')
self.assert_(not ret, 'deleting reference failed to release BusName org.freedesktop.DBus.Python.TestName')
""" Remove this for now
@@ -298,7 +303,7 @@ class TestDBusPythonToGLibBindings(unittest.TestCase):
for send_val in test_types_vals:
print "Testing %s"% str(send_val)
recv_val = self.iface.EchoVariant(send_val)
- self.assertEquals(send_val, recv_val)
+ self.assertEquals(send_val, recv_val.object)
"""
if __name__ == '__main__':
gobject.threads_init()
diff --git a/test/test-service.py b/test/test-service.py
index 975ea79..bd985eb 100755
--- a/test/test-service.py
+++ b/test/test-service.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python
import sys
import os
+import logging
builddir = os.environ["DBUS_TOP_BUILDDIR"]
pydir = builddir
@@ -11,7 +12,6 @@ sys.path.insert(0, pydir + 'dbus')
import dbus
if not dbus.__file__.startswith(pydir):
- os.system("echo %s> /tmp/dbus.log"%pydir)
raise Exception("DBus modules are not being picked up from the package")
import dbus.service
@@ -19,6 +19,12 @@ import dbus.glib
import gobject
import random
+
+logging.basicConfig(filename=pydir + '/test-service.log', filemode='w')
+logging.getLogger().setLevel(1)
+logger = logging.getLogger('test-service')
+
+
class TestInterface(dbus.service.Interface):
@dbus.service.method("org.freedesktop.DBus.TestSuiteInterface", in_signature='', out_signature='b')
def CheckInheritance(self):
@@ -68,6 +74,7 @@ class TestObject(dbus.service.Object, TestInterface):
@dbus.service.method("org.freedesktop.DBus.TestSuiteInterface", in_signature='u', out_signature='(ss)')
def ReturnStruct(self, test):
+ logger.info('ReturnStruct(%r) -> %r', test, self.returnValue(test))
return self.returnValue(test)
@dbus.service.method("org.freedesktop.DBus.TestSuiteInterface", in_signature='u', out_signature='as')
@@ -80,15 +87,15 @@ class TestObject(dbus.service.Object, TestInterface):
@dbus.service.signal("org.freedesktop.DBus.TestSuiteInterface", signature='s')
def SignalOneString(self, test):
- pass
+ logger.info('SignalOneString(%r)', test)
@dbus.service.signal("org.freedesktop.DBus.TestSuiteInterface", signature='ss')
def SignalTwoStrings(self, test, test2):
- pass
+ logger.info('SignalTwoStrings(%r, %r)', test, test2)
@dbus.service.signal("org.freedesktop.DBus.TestSuiteInterface", signature='(ss)')
def SignalStruct(self, test):
- pass
+ logger.info('SignalStruct(%r)', test)
@dbus.service.signal("org.freedesktop.DBus.TestSuiteInterface", signature='as')
def SignalArray(self, test):
@@ -110,6 +117,7 @@ class TestObject(dbus.service.Object, TestInterface):
else:
val = tuple([val])
+ logger.info('Emitting %s with %r', signal, val)
sig(*val)
def CheckInheritance(self):
diff --git a/test/test-standalone.py b/test/test-standalone.py
new file mode 100755
index 0000000..454c6aa
--- /dev/null
+++ b/test/test-standalone.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python
+import sys
+import os
+import unittest
+import time
+
+builddir = os.environ["DBUS_TOP_BUILDDIR"]
+pydir = builddir
+
+sys.path.insert(0, pydir)
+sys.path.insert(0, pydir + 'dbus')
+
+import _dbus_bindings
+import dbus
+import dbus.types as types
+
+pkg = dbus.__file__
+if not pkg.startswith(pydir):
+ raise Exception("DBus modules (%s) are not being picked up from the package"%pkg)
+
+if not _dbus_bindings.__file__.startswith(pydir):
+ raise Exception("DBus modules (%s) are not being picked up from the package"%_dbus_bindings.__file__)
+
+class TestTypes(unittest.TestCase):
+
+ def test_append(self):
+ aeq = self.assertEquals
+ from _dbus_bindings import SignalMessage
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append([types.Byte(1)], signature='ay')
+ aeq(s.get_signature(), 'ay')
+ aeq(s.get_args_list(), [[types.Byte(1)]])
+
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append([], signature='ay')
+ aeq(s.get_args_list(), [[]])
+
+ def test_append_ByteArray(self):
+ aeq = self.assertEquals
+ from _dbus_bindings import SignalMessage
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(types.ByteArray('ab'), signature='ay')
+ aeq(s.get_args_list(), [[types.Byte('a'), types.Byte('b')]])
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(types.ByteArray('ab'), signature='av')
+ aeq(s.get_args_list(), [[types.Variant(types.Byte('a')),
+ types.Variant(types.Byte('b'))]])
+
+ def test_append_Variant(self):
+ a = self.assert_
+ aeq = self.assertEquals
+ from _dbus_bindings import SignalMessage
+ s = SignalMessage('/', 'foo.bar', 'baz')
+ s.append(types.Variant(1, signature='i'),
+ types.Variant('a', signature='s'),
+ types.Variant([(types.Variant('a', signature='y'), 'b'),
+ (types.Variant(123, signature='u'), 1)],
+ signature='a(vy)'))
+ aeq(s.get_signature(), 'vvv')
+ args = s.get_args_list()
+ aeq(args[0].__class__, types.Variant)
+ aeq(args[0].signature, 'i')
+ aeq(args[0].object.__class__, types.Int32)
+ aeq(args[0].object, 1)
+ aeq(args[1].__class__, types.Variant)
+ aeq(args[1].signature, 's')
+ a(isinstance(args[1].object, unicode))
+ aeq(args[2].__class__, types.Variant)
+ aeq(args[1].object, 'a')
+ aeq(args[2].signature, 'a(vy)')
+ avy = args[2].object
+ aeq(avy.__class__, types.Array)
+ aeq(len(avy), 2)
+ aeq(avy[0].__class__, tuple)
+ aeq(len(avy[0]), 2)
+ aeq(avy[0][0].__class__, types.Variant)
+ aeq(avy[0][0].signature, 'y')
+ aeq(avy[0][0].object.__class__, types.Byte)
+ aeq(avy[0][0].object, types.Byte('a'))
+ aeq(avy[0][1].__class__, types.Byte)
+ aeq(avy[0][1], types.Byte('b'))
+ aeq(avy[1].__class__, tuple)
+ aeq(len(avy[1]), 2)
+ aeq(avy[1][0].__class__, types.Variant)
+ aeq(avy[1][0].signature, 'u')
+ aeq(avy[1][0].object.__class__, types.UInt32)
+ aeq(avy[1][0].object, 123)
+ aeq(avy[1][1].__class__, types.Byte)
+ aeq(avy[1][1], types.Byte(1))
+
+ def test_Variant(self):
+ Variant = types.Variant
+ a = self.assert_
+ a(Variant(1, 'i') == Variant(1, 'i'))
+ a(not (Variant(1, 'i') == Variant(1, 'u')))
+ a(not (Variant(1, 'i') == Variant(2, 'i')))
+ a(not (Variant(1, 'i') == Variant(2, 'u')))
+ a(not (Variant(1, 'i') != Variant(1, 'i')))
+ a(Variant(1, 'i') != Variant(1, 'u'))
+ a(Variant(1, 'i') != Variant(2, 'i'))
+ a(Variant(1, 'i') != Variant(2, 'u'))
+
+ def test_Signature(self):
+ self.assertRaises(Exception, types.Signature, 'a')
+ self.assertEquals(types.Signature('ab'), 'ab')
+ self.assert_(isinstance(types.Signature('ab'), str))
+ self.assertEquals(tuple(types.Signature('ab(xt)a{sv}')),
+ ('ab', '(xt)', 'a{sv}'))
+ self.assert_(isinstance(tuple(types.Signature('ab'))[0],
+ types.Signature))
+
+ def test_guess_signature(self):
+ aeq = self.assertEquals
+ from _dbus_bindings import Message
+ aeq(Message.guess_signature(('a','b')), '(ss)')
+ aeq(Message.guess_signature('a','b'), 'ss')
+ aeq(Message.guess_signature(['a','b']), 'as')
+ aeq(Message.guess_signature(('a',)), '(s)')
+ aeq(Message.guess_signature('abc'), 's')
+ aeq(Message.guess_signature(types.Int32(123)), 'i')
+ aeq(Message.guess_signature(('a',)), '(s)')
+ aeq(Message.guess_signature(['a']), 'as')
+ aeq(Message.guess_signature({'a':'b'}), 'a{ss}')
+
+if __name__ == '__main__':
+ unittest.main()