Jul 26, 2009

Technical decorating that makes sense

I have been using Python for several small-ish project in past year or two. In that time I have never found reason to really use decorators. You might have seen them in Java source codes in form of
@deprecated
void getVal()
{
//code
}
Python has same syntax, but because it's scripting language certain things are different (read: more flexible :-) ).

Now to the main thing. Where did I use it? As I was deciding what ORM library/tool to use in my GSoC project, I came to conclusion that I could probably use Django DB backend to work with database. This way I would avoid doing the same thing (ORM) twice. Once for backend and once again for web interface later on. As always, things are not as straightforward as they seem in the beginning. Django is web framework and it's tied with its database backend. In other words, the backend was not created with standalone usage in mind. Some things get a bit hairy because of that. These things are mostly connection management and exception handling.

Stackoverflow to the rescue (once again). There was already a question regarding use of only db part of django framework. Normally function like this in django:


would have to become this:

def add_package(self, name):
reset_queries()
try:
p = Package.objects.filter(name=name)
if len(p) > 0:
return p[0].id
p = Package(name=name)
p.save()
return p.id
except:
_rollback_on_exception()
finally:
close_connection()
Imagine that this exception handling, rollbacks and connection closing would have to be in every function. A bit ugly isn't it? We cannot really use inheritance to our advantage, but we could use metaclass(es). I like look of decorators a bit better, so that's what I used. So final code looks like this:

def dbquery(f):
def newfunc(*args, **kwargs):
reset_queries()
try:
return f(*args, **kwargs)
except Exception, e:
_rollback_on_exception()
raise e
return newfunc

@dbquery
def add_package(self, name):
p = Package.objects.filter(name=name)
if len(p) > 0:
return p[0].id
p = Package(name=name)
p.save()
return p.id
For other functions we could just add simple @dbquery in the beginning and voila, problem solved. Maybe there are even cleaner and/or better ways to do the same thing but at least I finally found non-trivial use for decorators.

Share/Save/Bookmark