Lately I’ve been working on a project that has me using DBus a lot. After trying to figure out how to work DBus with C, and seeing how easy it is to do in Python, we figured we’d try to use embedded Python to do this. Fortunately, it’s very simple to use - especially thanks to this guide.
It later turned out to be much easier to do in C, as described in Faster Languages.
Now, we couldn’t have the Python code throwing exceptions outwards, so we had each function return, along with its actual return value (if any), a numeric code identifying the error. Unfortunately, this made the code get really big, really fast - especially once DBus exceptions are thrown into the mix. But once I learned how to use decorators, I accomplished something like this diff:
+@wrap_exceptions((False,)) def checkSomething(): - global error_string - - error_string = "" - - try: - return (try_doing_something_over_dbus(), RET_OK) - except dbus.DBusException, e: - error_string = str(e) - if _nm_dbus_exception(e, "ServiceUnknown"): - return (False, RET_SERVICE_NOT_RUNNING) - return (False, RET_ERROR) - except Exception, e: - error_string = str(e) - return (False, RET_ERROR) - + return (try_doing_something_over_dbus(), RET_OK)
Now, the duplicate DBus/non-DBus exception handling, global
etc. - that happened in a lot of functions. Unfortunately, they didn’t all
return their values in the same way. Some just returned a
RET_VALUE, but most
had other values before it in the tuple (not the ideal design, come to think of
it…). Here’s the decorator I wrote:
class wrap_exceptions: def __init__(self, prepend_tuple=None): self.prepend_tuple = prepend_tuple def tuplize(self, retval): # Change retval into a default tuple form, if necessary if not self.prepend_tuple: return retval return tuple(list(self.prepend_tuple) + [retval]) def __call__(self, f): def exception_wrapped(*args, **kargs): global error_string error_string = "" try: return f(*args, **kargs) except dbus.DBusException, e: # Check known DBus Exceptions first if _nm_dbus_exception(e, "ServiceUnknown"): return self.tuplize(RET_SERVICE_NOT_RUNNING) # Unknown exceptions (DBus) error_string = str(e) # Includes get_dbus_name return self.tuplize(RET_ERROR) except Exception, e: # Unknown exceptions (non-DBus) error_string = repr(e) return self.tuplize(RET_ERROR) return exception_wrapped