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?