J5’s Blog

May 12, 2005

Decorate on a Dime

Filed under: Linux — J5 @ 7:15 pm

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 ]

8 Comments

  1. So those “decorators” are basically more primitive version of C# metadata?

    Comment by Tomasz Torcz — May 13, 2005 @ 9:12 am

  2. I don’t know if more primitive is the word but I just read up on C# metadata and you are correct in saying they are similar. Hopefully the DBus C# bindings will use them in the same way we use them in Python. I’ll have to talk to Joe about that.

    Comment by J5 — May 13, 2005 @ 9:27 am

  3. This explains the relationship of C#’s attributes to Python’s decorators from http://www.aminus.org/rbre/python/pydec.html:

    Similarly, the previous []-before-def syntax was inspired by Microsoft’s C# language. But C# also limits the expressivity of what it calls “attributes”. They are similar to Python decorators in the sense that both involve what appear to be expressions as opposed to statements; however, .Net attributes really are non-statements; they do not modify their referents so much as attach themselves to them. Microsoft often calls attributes “declarative” as opposed to “imperative” 19. Further, they are required to be commutative 20, a requirement which would cripple many uses of Python decorators 21.

    Java annotations are somewhat similar but a bit more different than C# attributes:

    The current @-decorator syntax is inspired by Java’s “annotations”. In fact, the authors of the Java annotation feature state that the @-symbol is a mnemonic for annotation type. However, Python decorators differ significantly from Java’s use of that syntax. Most importantly, “annotations” are limited to attributes and other non-transformative operations in Java. From the JDK 1.5 specification:

    “This facility allows developers to define custom annotation types and to annotate fields, methods, classes, and other program elements with annotations corresponding to these types. These annotations do not directly affect the semantics of a program.” 18 (emphasis added)

    Java annotations provide semantic transparency; the referent is not modified. One may use “interceptor” functions like @trace, but the annotated function is not wrapped. In Java, function transformations continue to be provided by keywords such as static.

    Comment by J5 — May 13, 2005 @ 10:54 am

  4. So it looks like C# ones are primitive rather :)

    Comment by Tomasz Torcz — May 13, 2005 @ 7:08 pm

  5. I’d love to do some cool dbus-in-python coding, but I’ve found no documentation. Have you found any documentation or are you using the source to find your way?

    Comment by Jimmy — May 15, 2005 @ 6:38 am

  6. I plan on writting up documentation while I’m at GUADEC at the end of this month. Other than that it is really easy to use the python bindings. Take a look at the exmple code in CVS.

    Comment by J5 — May 15, 2005 @ 10:02 am

  7. Hi,

    I need to interface with some code using DBus, and I’d ideally like to use Python for this task. Sadly I don’t see a documented API and the examples you speak of in CVS don’t quite tell me how to do what I am trying to do.

    Basically the sample python client code provided by the third party component uses:

    remote_bus=dbus.SystemBus()
    remote_bus.get_service(’org.freedesktop.DBus’)

    But the interpreter complains about get_service not being a method on SystemBus.

    Using python 2.4.1 and dbus 0.33 on FC4…

    Any ideas/pointers?

    Thanks,
    Manik

    Comment by Manik — August 10, 2005 @ 6:12 am

  8. hey Manik. The API changed quite a bit. Where did you get the examples from? YOu can check out the tutorial on the dbus wiki – http://www.freedesktop.org/Software/dbus

    bus = dbus.SystemBus()
    remote_object = bus.get_object(”org.freedesktop.DBus”, “/org/freedesktop/DBus”)
    iface = dbus.Interface(remote_object, “org.freedesktop.DBus”)

    iface.method()

    Comment by J5 — August 11, 2005 @ 9:01 am

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress