PEP 318 - posting draft

Skip Montanaro skip at
Tue Mar 23 18:02:06 CET 2004

Here's the current state of PEP 318.  I have to take a break from this to
get some real work done and may not get back to it in a major way for
awhile.  I received a significant rewrite of Kevin Smith's most recent
version from Jim Jewett and incorporated much of what he wrote, modifying a
lot of it along the way, but have still not digested everything he sent me.

I've tried to reflect the concensus which seems to be emerging on the
python-dev list, though I suspect I've done a poor job of that.  The
discussions there and on comp.lang.python have ranged far and wide and thus
resist summary in a finite amount of time.  I recommend interested
comp.lang.python readers spend some time in the python-dev archives for
February and March if they find major fault with the current state of the

If you post corrections or comments to either list I should see them.

Skip Montanaro

PEP: 318
Title: Function/Method Decorator Syntax
Version: $Revision: 1.5 $
Last-Modified: $Date: 2004/03/23 16:41:17 $
Author: Kevin D. Smith <Kevin.Smith at>, 
        Jim Jewett <jimjjewett at>,
        Skip Montanaro <skip at>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 05-Jun-2003
Python-Version: 2.4
Post-History: 09-Jun-2003, 10-Jun-2003, 27-Feb-2004, 23-Mar-2004

The current method for declaring class and static methods is awkward 
and can lead to code that is difficult to understand.  Ideally, these 
transformations should be made at the same point in the code where the 
declaration itself is made.  This PEP introduces new syntax for 
transformations of a declaration.


The current method of applying a transformation to a function or
method places the actual translation after the function body.  For
large functions this separates a key component of the function's
behavior from the definition of the rest of the function's external
interface.  For example::

    def foo(self):
        perform method operation
    foo = classmethod(foo)

This becomes less readable with longer methods.  It also seems less
than pythonic to name the function three times for what is
conceptually a single declaration.  A solution to this problem is to
move the transformation of the method closer to the method's own
declaration.  While the new syntax is not yet final, the intent is to

    def foo(cls):
    foo = synchronized(lock)(foo)
    foo = classmethod(foo)

with an alternative that places the decoration in the function's

    def foo(cls) using [synchronized(lock), classmethod]:


There is general agreement that syntactic support is desirable to the
current state of affairs.  Guido mentioned `syntactic support for
decorators`_ in his DevDay keynote presentation at the `10th Python
Conference`_, though `he later said`_ it was only one of several
extensions he proposed there "semi-jokingly".  `Michael Hudson raised
the topic`_ on ``python-dev`` shortly after the conference,
attributing the bracketed syntax to an earlier proposal on
``comp.lang.python`` by `Gareth

.. _syntactic support for decorators:
.. _10th python conference:
.. _michael hudson raised the topic:
.. _he later said:
.. _gareth mccaughan:

Design Goals

The new syntax should

*  work for arbitrary wrappers, including user-defined callables and
   the existing builtins ``classmethod()`` and ``staticmethod``

*  work with multiple wrappers per definition

*  make it obvious what is happening; at the very least it should be 
   obvious that new users can safely ignore it when writing their own 

*  not make future extensions more difficult

*  be easy to type;  programs that use it are expected to use it very 

*  not make it more difficult to scan through code quickly.  It should 
   still be easy to search for all definitions, a particular 
   definition, or the arguments that a function accepts

*  not needlessly complicate secondary support tools such as
   language-sensitive editors and other "`toy parser tools out

.. _toy parser tools out there:

Proposed Syntax

The currently proposed syntax is::

    def func(arg1, arg2, ...) [dec1, dec2, ...]:

The decorators are near the declaration of the function's API but are
clearly secondary.  The square brackets make it possible to fairly
easily break long lists of decorators across multiple lines.

Alternate Proposals

A few other syntaxes have been proposed::

    def func(arg1, arg2, ...) as dec1, dec2, ...:

The absence of brackets makes it cumbersome to break long lists of
decorators across multiple lines.  The keyword "as" doesn't have the
same meaning as its use in the ``import`` statement.


    def [dec1, dec2, ...] func(arg1, arg2, ...):

This form has the disadvantage that the decorators become visually
higher priority than the function name and argument list.


    def func [dec1, dec2, ...] (arg1, arg2, ...):

Quixote's Page Template Language uses this form, but only supports a
single decorator chosen from a restricted set.  For short lists it
works okay, but for long list it separates the argument list from the
function name.


    def foo(arg1, arg2, ...):

The function definition is not nested within the using: block making
it impossible to tell which objects following the block will be
decorated.  Nesting the function definition within the using: block
suggests block structure that doesn't exist.  The name ``foo`` would
actually exist at the same scope as the using: block.  Finally, it
would require the introduction of a new keyword.

Current Implementation

Michael Hudson has posted a `patch`_ at Starship, which implements the
proposed syntax and left-first application of decorators::

    def func(arg1, arg2, ...) [dec1, dec2]:

is equivalent to::

    def func(arg1, arg2, ...):
    func = dec2(dec1(func))

though without the intermediate creation of a variable named ``func``.

.. _patch:


Much of the discussion on ``comp.lang.python`` and the ``python-dev``
mailing list focuses on the use of the ``staticmethod()`` and
``classmethod()`` builtins.  This capability is much more powerful
than that.  This section presents some examples of use.

1. Define a function to be executed at exit.  Note that the function
   isn't actually "wrapped" in the usual sense.


    def onexit(f):
        import atexit
        return f

    def func() [onexit]:

2. Define a class with a singleton instance.  Note that once the class
   disappears enterprising programmers would have to be more creative
   to create more instances.  (From Shane Hathaway on ``python-dev``.)


    def singleton(cls):
        return cls()

    class MyClass [singleton]:

3. Decorate a function with release information.  (Based on an example
   posted by Anders Munch on ``python-dev``.)


    def release(**kwds):
        def decorate(f):
            for k in kwds:
                setattr(f, k, kwds[k])
            return f
        return decorate

    def classmethod(f) [release(versionadded="2.2",
                                author="Guido van Rossum")]:

4. Enforce function argument and return types.


    def accepts(*types):
        def check_accepts(f):
            def new_f(*args, **kwds):
                for (a, t) in zip(args, types):
                    assert isinstance(a, t), \
                           "arg %r does not match %s" % (a,t)
                return f(*args, **kwds)
            assert len(types) == f.func_code.co_argcount
            return new_f
        return check_accepts

    def returns(rtype):
        def check_returns(f):
            def new_f(*args, **kwds):
                result = f(*args, **kwds)
                assert isinstance(result, rtype), \
                       "return value %r does not match %s" % (result,rtype)
                return result
            return new_f
        return check_returns

    def func(arg1, arg2) [accepts(int, (int,float)),
        return arg1 * arg2

Of course, all these examples are possible today, though without the
syntactic support.

Possible Extensions

The proposed syntax is general enough that it could be used on class
definitions as well::

    class foo(object) [dec1, dec2, ...]:
        class definition here

Use would likely be much less than function decorators.  The current
patch only implements function decorators.


This document has been placed in the public domain.

Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70

More information about the Python-list mailing list