I was out last night with friends at the usual Wednesday night haunt, the Overdraugt. Brian Clark asked me a question that got me thinking. “What are those funny looking statements in your DBus python code, you know the ones with the @ symbol?”, he asked. I had assumed that programmers could figure that out, it seemed so natural to me. But, I remembered that Brian was a designer not a developer.

It is easy to forget because Brian does do development but unlike developers he could care less about minutiae of a perticular language. He just uses them to get things done which can often times be more effective than reveling over an elegent language construct. As developers we often forget or target audience should be people like Brian. It is one of the reasons languages like Visual Basic or formats like HTML became popular. It targeted the masses.

Targetting the masses doesn’t have to mean everyone and their mother is going to use it but one should always have the mentality that they are developing for people outside of their core audience. That is how one expand usage and by shear luck of the laws of logic, expand the usefulness of what one is developing.

Good API is one way to get there in the programming sense, but VB will never be accused of having good API. Another way to get there is good documentation. I have noted in past blogs that Python has excellent documentation contributing to its success (it also has an excellent API IMHO). Formal documentation is all well and good but one can often get lost if they don’t know what they are looking for. So to further make the DBus Python bindings slightly more useful and teach others about a fairly new Python feature I present my understanding of decorators:

J5’s Understanding of Decorators from a DBus Point of View

Decorators are Python constructs used to “decorate” functions and methods. At the highest layers they can be thought of as markers that provide extra information about the function being decorated. Decorators begin with the @ symbol followed by the decorators name. In DBus we have two such decorators. These are:

  • dbus.method
  • dbus.signal

These particular decorators can only be placed infront of python methods and not function because that is the constraints I placed on them.

Placing one of these decorators infront of a python method marks that python method as being exported as a dbus method or signal. Take this code fragment for example:

import dbus

class foo(dbus.Object):
    @dbus.method('org.FooInterface')
    def hello(msg):
        return 'hello' + msg


The hello method of class foo is now marked as also being a dbus method with interface “org.FooInterface”. Decorators not only tell the program what to do but they also provide uncluttered visual clues as to how a user can use the decorated method. So, if someone looks at the above example they can instantly tell that the hello method is exported over the bus. There are other ways we could have told the program what methods we wanted to export but none as readable as a decorator.

What is a Decorator Really (those who just want to use them can stop here)?

At the lowest levels a decorator is simply a function that takes a function or method as input and returns another function as output. Some other little bits go on in the background such as replacing the decorated function with the outputted function. What goes on inside the black box of the decorator is up to the developer. Decorators can simply return the function that was passed to it without modifying it. They can add metadata to the original function object or they could return a completely different function object.

The key to decorators is that they are executed when the function or method is parsed in the Python interpreter. This allows the decorator to modify the function before it is used. Take the dbus.Method decorator code as an example:

def method(dbus_interface):
    _util._validate_interface_or_name(dbus_interface)

    def decorator(func):
        func._dbus_is_method = True
        func._dbus_interface = dbus_interface
        func._dbus_args = inspect.getargspec(func)[0]
        func._dbus_args.pop(0)
        return func

    return decorator

When the foo class is parsed the method decorator gets called and the interface is sent into the function. We validate the interface and then return the inner function which we have conviniently named “decorator”. Once the method class for “hello” has been created it is passed into the returned decorator function. The decorator function then sets a couple of attributes on the function object itself and returns the original function. Should another function be returned it would take the place of the original. Normaly if you did that you would call the original function somewhere in the returned function but it really is just a normal python function so you can do whatever you wish. The attributes we set is later used by a Metaclass to create a list of methods that need to be registered and exported over the bus and to construct the introspection data. That is all there is to it.

Other uses for decorators is validating argument types, adding generic debugging to a problem function, adding logging or forcing security checks. Since at its core decorators are just functions that replace functions one can dream up many applications for their use.

[read this post in: ar de es fr it ja ko pt ru zh-CN ]