diff options
author | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2008-07-17 13:00:15 +0100 |
---|---|---|
committer | Simon McVittie <simon.mcvittie@collabora.co.uk> | 2008-07-17 13:00:15 +0100 |
commit | d1ded84e774c4aaad9bf02842e1898580dd599ea (patch) | |
tree | 8f29632421a1dd19899e1084f882605acc13591a | |
parent | b962965f8c30d785ade69dd6a60924b42d6a1c8d (diff) | |
download | dbus-python-d1ded84e774c4aaad9bf02842e1898580dd599ea.tar.gz |
Omit the remote traceback from certain D-Bus errors
Specifically, DBusException and its subclasses no longer have the remote
traceback by default (although subclasses can turn it back on again
by setting include_traceback = True, and the various "programmer error"
subclasses of DBusException do have this set).
Hopefully this will stop people thinking it's a dbus-python or
telepathy-python bug when a D-Bus API like Telepathy deliberately raises an
error (and so dbus-python or telepathy-python is visible in the traceback).
-rw-r--r-- | dbus/exceptions.py | 33 | ||||
-rw-r--r-- | dbus/service.py | 5 | ||||
-rwxr-xr-x | test/test-client.py | 37 | ||||
-rwxr-xr-x | test/test-service.py | 22 |
4 files changed, 96 insertions, 1 deletions
diff --git a/dbus/exceptions.py b/dbus/exceptions.py index 6a0fbaf..8d84a29 100644 --- a/dbus/exceptions.py +++ b/dbus/exceptions.py @@ -28,6 +28,17 @@ __all__ = ('DBusException', 'MissingErrorHandlerException', 'NameExistsException') class DBusException(Exception): + + include_traceback = False + """If True, tracebacks will be included in the exception message sent to + D-Bus clients. + + Exceptions that are not DBusException subclasses always behave + as though this is True. Set this to True on DBusException subclasses + that represent a programming error, and leave it False on subclasses that + represent an expected failure condition (e.g. a network server not + responding).""" + def __init__(self, *args, **kwargs): name = kwargs.pop('name', None) if name is not None or getattr(self, '_dbus_error_name', None) is None: @@ -44,30 +55,52 @@ class DBusException(Exception): else: return s + def get_dbus_message(self): + s = Exception.__str__(self) + return s.decode('utf-8', 'replace') + def get_dbus_name(self): return self._dbus_error_name class MissingErrorHandlerException(DBusException): + + include_traceback = True + def __init__(self): DBusException.__init__(self, "error_handler not defined: if you define a reply_handler you must also define an error_handler") class MissingReplyHandlerException(DBusException): + + include_traceback = True + def __init__(self): DBusException.__init__(self, "reply_handler not defined: if you define an error_handler you must also define a reply_handler") class ValidationException(DBusException): + + include_traceback = True + def __init__(self, msg=''): DBusException.__init__(self, "Error validating string: %s"%msg) class IntrospectionParserException(DBusException): + + include_traceback = True + def __init__(self, msg=''): DBusException.__init__(self, "Error parsing introspect data: %s"%msg) class UnknownMethodException(DBusException): + + include_traceback = True _dbus_error_name = 'org.freedesktop.DBus.Error.UnknownMethod' + def __init__(self, method): DBusException.__init__(self, "Unknown method: %s"%method) class NameExistsException(DBusException): + + include_traceback = True + def __init__(self, name): DBusException.__init__(self, "Bus name already exists: %s"%name) diff --git a/dbus/service.py b/dbus/service.py index 55bb04e..b92d840 100644 --- a/dbus/service.py +++ b/dbus/service.py @@ -277,7 +277,10 @@ def _method_reply_error(connection, message, exception): name = 'org.freedesktop.DBus.Python.%s.%s' % (exception.__module__, exception.__class__.__name__) et, ev, etb = sys.exc_info() - if ev is exception: + if isinstance(exception, DBusException) and not exception.include_traceback: + # We don't actually want the traceback anyway + contents = exception.get_dbus_message() + elif ev is exception: # The exception was actually thrown, so we can get a traceback contents = ''.join(traceback.format_exception(et, ev, etb)) else: diff --git a/test/test-client.py b/test/test-client.py index 5b977da..753d892 100755 --- a/test/test-client.py +++ b/test/test-client.py @@ -508,6 +508,43 @@ class TestDBusBindings(unittest.TestCase): self.assertRaises(dbus.DBusException, lambda: self.iface.AsyncWait500ms(timeout=0.25)) + def testExceptions(self): + #self.assertRaises(dbus.DBusException, + # lambda: self.iface.RaiseValueError) + #self.assertRaises(dbus.DBusException, + # lambda: self.iface.RaiseDBusExceptionNoTraceback) + #self.assertRaises(dbus.DBusException, + # lambda: self.iface.RaiseDBusExceptionWithTraceback) + + try: + self.iface.RaiseValueError() + except Exception, e: + self.assert_(isinstance(e, dbus.DBusException), e.__class__) + self.assert_('.ValueError: Traceback ' in str(e), + 'Wanted a traceback but got:\n"""%s"""' % str(e)) + else: + raise AssertionError('Wanted an exception') + + try: + self.iface.RaiseDBusExceptionNoTraceback() + except Exception, e: + self.assert_(isinstance(e, dbus.DBusException), e.__class__) + self.assertEquals(str(e), + 'com.example.Networking.ServerError: ' + 'Server not responding') + else: + raise AssertionError('Wanted an exception') + + try: + self.iface.RaiseDBusExceptionWithTraceback() + except Exception, e: + self.assert_(isinstance(e, dbus.DBusException), e.__class__) + self.assert_(str(e).startswith('com.example.Misc.RealityFailure: ' + 'Traceback '), + 'Wanted a traceback but got:\n%s' % str(e)) + else: + raise AssertionError('Wanted an exception') + """ Remove this for now class TestDBusPythonToGLibBindings(unittest.TestCase): def setUp(self): diff --git a/test/test-service.py b/test/test-service.py index 74829d4..51865eb 100755 --- a/test/test-service.py +++ b/test/test-service.py @@ -305,6 +305,28 @@ class TestObject(dbus.service.Object, TestInterface): return False gobject.timeout_add(500, return_from_async_wait) + @dbus.service.method(IFACE, in_signature='', out_signature='') + def RaiseValueError(self): + raise ValueError('Wrong!') + + @dbus.service.method(IFACE, in_signature='', out_signature='') + def RaiseDBusExceptionNoTraceback(self): + class ServerError(dbus.DBusException): + """Exception representing a normal "environmental" error""" + include_traceback = False + _dbus_error_name = 'com.example.Networking.ServerError' + + raise ServerError('Server not responding') + + @dbus.service.method(IFACE, in_signature='', out_signature='') + def RaiseDBusExceptionWithTraceback(self): + class RealityFailure(dbus.DBusException): + """Exception representing a programming error""" + include_traceback = True + _dbus_error_name = 'com.example.Misc.RealityFailure' + + raise RealityFailure('Botched invariant') + @dbus.service.method(IFACE, in_signature='', out_signature='', async_callbacks=('return_cb', 'raise_cb')) def AsyncRaise(self, return_cb, raise_cb): |