=======================
  Intro to decorators
=======================

A decorator is a closure that extends another function by wrapping it
and returning a new function.

For example let's make some simple decorators that print some
debugging info to a buffer for us to inspect.

First, we'll extend StringIO so we don't have to keep calling strip()
in this doctest whenever we inspect our buffer.  We'll use a decorator
to extend StringIO to call strip for us.  Like a monkeypatch, the
decorator replaces the method for all instances of the class. Unlike a
monkey patch, the decorator only extends the original function; note
our closure does not duplicate or reimplement any of the 'getvalue'
method's logic ::

    >>> def addstrip(getvalue):
    ...     def newgetvalue(*args, **kw):
    ...         value = getvalue(*args, **kw)
    ...         return value.strip()
    ...     return newgetvalue

Let's import StringIO and decorate!  First we'll try the normal
behavior ::
    
    >>> from StringIO import StringIO
    >>> output = StringIO()
    >>> print >> output, 'bhaaaaa'
    >>> print output.getvalue()
    bhaaaaa
    <BLANKLINE>

Now let's decorate and compare the same example ::

    >>> StringIO.old_getvalue = StringIO.getvalue
    >>> StringIO.getvalue = addstrip(StringIO.old_getvalue)
    >>> output = StringIO()
    >>> print >> output, 'bhaaaaa'
    >>> print output.getvalue()
    bhaaaaa

Ahh...no annoying blankline!

Now, let's use decorators to extend code within our control.  We'll
define a buffer accesible to the namespace of the closures; the new
'decorate' functions can print to this, and we can look at it and see
what is going on ::
    
    >>> output = StringIO()

Now, we will define a closure.  It will print the name of the function
passed to it, and wrap that function with behavior to print the
function's name, arguments, and return value to our buffer ::

    >>> def print_info(f):
    ...     print >> output, 'sprucing up %s' %f.__name__
    ...     def newfunc(*args, **kwargs):
    ...         value = f(*args, **kwargs);
    ...         info = (f.__name__, args, value)
    ...         print >> output, 'calling %s args:%s value:%s' %info
    ...         return value
    ...     return newfunc

We'll define a simple adding function to decorate ::

    >>> def adder(x, y): return x + y

Before python 2.4, decorators could be simply by calling the closure
on the target function like so ::

    >>> adder = print_info(adder)
    >>> print output.getvalue()
    sprucing up adder
    
    >>> adder(1, 2)
    3

    >>> print output.getvalue()
    sprucing up adder
    calling adder args:(1, 2) value:3

In python 2.4, we get some syntactic sugar to the most common
usecases for decoration more convenient and readable.  Lets
decorate 'adder' again ::

    >>> output = StringIO()
    >>> @print_info
    ... def adder(x, y): return x + y
    >>> adder(1, 2)
    3

    >>> print output.getvalue()
    sprucing up adder
    calling adder args:(1, 2) value:3

Using python2.4, we can 'stack' decorators. Stacking alludes to the
appearance in the code.  As you read down a page of python the last
decorator to wrap a function is on the first line of a function
declaration.  For example, let's create a rather generic function that
we can use to observe what happens.  We'll define a counting variable
to let us follow the application of the decorators to our function ::

    >>> output = StringIO()
    >>> global count
    >>> count = 0
    >>> def decorate(f):
    ...     fname = f.__name__
    ...     global count
    ...     count +=1
    ...     print >> output, 'step %s decorate: %s' %(count, fname)
    ...     def new(*args, **kwargs):
    ...         value = f(*args, **kwargs)
    ...         print >> output, 'new(%s), value = %s' %(fname, value)
    ...         return value
    ...     new.__name__ = 'new%s(%s)' %(count, fname)
    ...     return new

We'll copy these functions to some new names ::

    >>> first = decorate
    >>> second = decorate
    >>> third = decorate

Now, we'll stack them ::

    >>> @third
    ... @second
    ... @first
    ... def adder(x, y): return x + y
    >>> adder24 = adder
    >>> val24 = output.getvalue()
    >>> print val24
    step 1 decorate: adder
    step 2 decorate: new1(adder)
    step 3 decorate: new2(new1(adder))

Let's do the equivalent for 2.3 ::

    >>> count = 0
    >>> output = StringIO()
    >>> def adder(x, y): return x + y
    >>> adder23 = third(second(first(adder)))
    >>> val23 = output.getvalue()
    >>> print val23
    step 1 decorate: adder
    step 2 decorate: new1(adder)
    step 3 decorate: new2(new1(adder))
    
    >>> val23 == val24
    True

And finally, what happens when we call our new function ::

    >>> output = StringIO()
    >>> adder24(1, 2)
    3
    >>> adder23(3, 4)
    7
    >>> print output.getvalue()
    new(adder), value = 3
    new(new1(adder)), value = 3
    new(new2(new1(adder))), value = 3
    new(adder), value = 7
    new(new1(adder)), value = 7
    new(new2(new1(adder))), value = 7

Let's cleanup in case someone runs this test with others ;)

    >>> StringIO.getvalue = StringIO.old_getvalue
