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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| +@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 error_string
,
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| 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
|