This is a crazy idea I had; that I want to share with people.
When you're implementing an object in dbus-python, you decorate your published method calls like this:
class ExampleObserver(dbus.service.Object): ... @dbus.service.method(dbus_interface=telepathy.interfaces.CLIENT_OBSERVER, in_signature='ooa(oa{sv})oaoa{sv}', out_signature='') def ObserveChannels(self, account, connection, channels, dispatch_operation, requests_satisfied, observer_info): ...
The input and output signatures are incredibly easy to get wrong. The thing is, most D-Bus APIs (e.g. Telepathy) have a specification that contains these arguments. Some APIs (e.g. Telepathy-Python) provide generated code including interface names and constants. So why can't we do something more like?
class ExampleObserver(dbus.service.Object): ... @telepathy.decorators.Telepathy.Client.Observer.ObserveChannels def ObserveChannels(self, account, connection, channels, dispatch_operation, requests_satisfied, observer_info): ...
With a decorator factory that looks up the parameters and then wraps the dbus.service.method factory.
Well, I just wrote a proof-of-concept. It looks something like this:
class Decorators(object): methods = { 'org.freedesktop.DBus.Properties.GetAll': [ 's', 'a{sv}' ], 'org.freedesktop.DBus.Properties.Get': [ 'ss', 'v' ], 'org.freedesktop.Telepathy.Client.Observer.ObserveChannels': [ 'ooa(oa{sv})oaoa{sv}', '' ], } def __init__(self, namespace): self._namespace = namespace def __getattr__(self, key): return Decorators('%s.%s' % (self._namespace, key)) def __call__(self, func): iface = self._namespace.rsplit('.', 1)[0] in_sig, out_sig = self.methods[self._namespace] return dbus.service.method(dbus_interface=iface, in_signature=in_sig, out_signature=out_sig)(func) def __str__(self): return self._namespace decorators = Decorators('org.freedesktop')
Obviously in the real version, it would have a generated map of functions, or map of interfaces each with a map of functions, and a way to handle signals, but neat huh?