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 ]
So those “decorators” are basically more primitive version of C# metadata?
Comment by Tomasz Torcz — May 13, 2005 @ 9:12 am
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
This explains the relationship of C#’s attributes to Python’s decorators from http://www.aminus.org/rbre/python/pydec.html:
Java annotations are somewhat similar but a bit more different than C# attributes:
Comment by J5 — May 13, 2005 @ 10:54 am
So it looks like C# ones are primitive rather
Comment by Tomasz Torcz — May 13, 2005 @ 7:08 pm
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
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
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
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