Decorators

TurboGears comes with a decorator library decorator making it easy to write interoperable decorators.

Motivation

There are three fundamental problems when using ordinary decorators:

  1. Difficult introspection (identity of the underlying function changes, generic decorators hide underlying function's signature,...)
  2. Syntactic sugar is available only in Python 2.4 and newer
  3. Underlying function's attributes (docstring, name, ...) are not preserved.

Using decorator library

decorator()

The staple of the decorator library is function decorator():

Decorators created by decorator() preserve the underlying function's signature and its attributes. They also introduce an alternative syntax for use with Python 2.3:

In order to use it, the decorator needs to conform to the form:

You can use your choice of function names, however TurboGears coding standard uses entangle for the closure binding the function being decorated and repeating the decorator name in inner function (also referred to as "caller") to ensure meaningful tracebacks.

An optional argument signature, similar in format to what Python's sys.exc_info() returns, allows one to define a custom signature for the resulting (decorated) function. This can be useful in various adaptation schemes, for example see turbogears.util.bind_args().

weak_signature_decorator()

For decorators needing to pass a state around, weak_signature_decorator() can be useful, especially in combination with turbogears.util.adapt_call() to clean up afterwards. weak_signature_decorator() adds *identifier and **identifier (named _decorator__varargs and _decorator__kwargs respectively) to the signature (original names are preserved if available)

Most TurboGears decorators are created using weak_signature_decorator(). Generic decorators to be used with expose() are encouraged to follow this lead.

simple_decorator() and simple_weak_signature_decorator()

Sometimes decorators with closures are not needed. To reduce boilerplate simple_decorator() and simple_weak_signature_decorator() are available that apply directly to caller:

Mind though, alternative syntax is not available for such decorators.

Another use-case is adaptation where decorators are not used as such, nevertheless preservation qualities afforded by this library are very much sought.

Utility functions

To facilitate use of decorators some utility functions are available. To get the best results decorators created with this library are recommended but not necessary.

compose()

Composes decorators into one.

Let's say we have a controller where a number of methods need to be exposed, check for same permissions and their argument id validated. In keeping with DRY, one may want to use compose to combine all three corresponding decorators into one:

func_composition()

Returns decorator composition as a list of functions (decorators) for a given function. Every function is at the very least composed of itself.

func_original()

Returns original (fully undecorated) function.

func_id()

Python's id() for decorated functions.

func_eq()

Checks if two functions are identical (using func_id()).