decorator factory for dbus-python methods

Previous Entry Add to Memories Tell a Friend Next Entry
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?
Posted 8/11/09 17:10 — 1 comment

Comments

From:(Anonymous)
Date:2009-11-09 00:31 (UTC)
(Link)
Is there any reason to prefer this over something like telepathy.method("Telepathy.Client.Observer.ObserveChannels") and lose the __getattr__() hack? That kind of "anything goes" __getattr__() implementation can easily hide bugs in your code (e.g. if you misspelled "self._namespace" anywhere in the decorator).

Also, I wouldn't use the name "decorator" in the name of the decorator. We can tell that it is a decorator from the context it is being used, so it'd be better to use the name to describe what it does.

Other than that, it sounds like a great idea. I wonder if it'd be worth integrating into dbus-python as an argument type registry that the existing @dbus.service.method decorator could use if the argument types were not specified?