Over the last couple of years I’ve found myself using python decorators to annotate handlers for web requests more and more, both when using Django and with micro-frameworks like mnml and newf.

Where the same functionality is required for all handlers, or the required functionality can be determined from standard request or response headers, using WSGI or Django middleware is fine, but where the required functionality is varies based on the handler its much cleaner to use a parameterised decorator than poluting the environment or response objects just to control the middleware. Functionality can be added to a framework as a suite of decorators and plugged together in an aspect oriented way like lego to easily build up sophisticated behaviours.

Unlike other mechanisms for implementing macros, templating or aspect orientation that introduce a new language, python decorators are pure syntactic sugar that under the hood are simply rewritten as python expressions:

@requires_oauth_scope("email")
def notify_friends(request):
    pass

Is simply shorthand for:

def notify_friends(request):
    pass

notify_friends = requires_oauth_scope("email")(notify_friends)

This simplicity is powerful as it allows decorators to also be used as normal functions, for example to build up higher level decorators that bundle common decorator configurations, but it also means that decorators potentially interact badly with another powerful Python feature: introspection.

In the above example the undecorated notify_friends function has the name “notify_friends”, but the decorated function has the name “requires_oauth_scope”. When decorators are used extensively, this can seriously impact the usefulness of introspection for debugging or generating documentation.

Decorating your decorators with the functools @wraps decorator, which copies the name of the wrapped function over to the wrapping function solves this introspection problem, but introduces another: the decorators now become invisible to introspection. In the example above the name of the decorated function would now be “notify_friends” as in the undecorated case, but we wouldn’t know that the function had been decorated or not.

A potential solution to this new problem is to store the details about the decoration in another attribute that can be inspected at runtime. In addition to copying over the name attribute, functools.wraps also copies over the target dict by default, allowing it to be used to store information about the decoration and be correctly copied over when decorators are chained:

from functools import wraps

def requires_oauth_scope(scope):

    def decorator(target):

        target.__dict__["my_project_requires_oauth_scope"] = scope

        @wraps(target)
        def wrapper(*args, **kwargs):
            # return target(*args, **kwargs) or FORBIDDEN if token does not have required scope

        return wrapper
    return decorator

By constructing decorators in this way we get the benefits of python decorators and more declarative C# style attributes that are visible to introspection.


The Why and How of Automated Testing with Python and Django

Thu 04 November 2010 by Jim Purbrick

Jamie has just uploaded the movie of my talk “The Why and How of Automated Testing with Python and Django” which I gave at BrightonPy a week ago (and this time it really is a movie, clocking in at a feature length 1 hr and 35 minutes). The audio on ...

read more

Spawning Django Blogs

Mon 18 October 2010 by Jim Purbrick

Since leaving Linden Lab I have been talking to a number of people about doing freelance consulting and development work while I get my start-up off the ground and last week got round to setting up a UK limited company so that people will actually be able to pay me ...

read more

A Collaborative User Generated Ambient Augmented Virtual Reality Scientific Visualisation The Size Of Denmark

Tue 01 July 2008 by Jim Purbrick

2 years ago at Euro FOO 2006 who commented that climate change would be much easier to deal with if we could see carbon dioxide. The second was with Claus Dahl who observed that Second Life is a great platform to prototype large scale augmented reality applications as every object ...

read more

Hello World

Tue 01 July 2008 by Jim Purbrick

Well, not exactly. Having blogged previously on Terra Nova, the original Creation Engine and currently on the Official Second Life Blog, I’m not exactly stumbling blinking in to the blinding light of the blogosphere. Recently a number of things have come up that I’ve wanted to write more ...

read more
Fork me on GitHub