Meta-class fun with PEP 318 [was Re: PEP 318 - posting draft]

Sean Ross sross at connectmail.carleton.ca
Fri Mar 26 20:45:13 EST 2004


[snip]
> --------------------------------------------------------------------------
----
> PEP: 318
> Title: Function/Method Decorator Syntax
[snip]
> Abstract
> ========
>
> 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.
>
>
> Motivation
> ==========
>
> 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
> replace::
>
>     def foo(cls):
>         pass
>     foo = synchronized(lock)(foo)
>     foo = classmethod(foo)
>
> with an alternative that places the decoration in the function's
> declaration::
>
>     def foo(cls) using [synchronized(lock), classmethod]:
>         pass
>


Just for fun, I thought I'd try a little meta-class hackery to bring
method decoration and method annotation (adding attributes to
a method) near/before the method declaration. This is *not*
meant as an alternative to PEP318 (for one thing, it only applies
to methods), I just wanted to see if/how it could be done.




import sys

def delayed_decorate(method, *decorators):
    "Decorate method during class instantation"
    for decorator in decorators:
        method = decorator(method)
    return method

# new syntax for this operation is being discussed on python dev ...
#   e.g.,
#       @author = "your name here"
def delayed_annotate(method, **attributes):
    "Annotate (add attributes to) method during class instantation"
    method.__dict__.update(attributes)

def annotate(funcname, **attributes):
    "Mark method for delayed annotation"
    clsdict = sys._getframe(1).f_locals
    clsdict.setdefault("annotated", {})[funcname] = attributes

def decorate(funcname, *decorators):
    "Mark method for delayed decoration"
    clsdict = sys._getframe(1).f_locals
    clsdict.setdefault("decorated", {})[funcname] = decorators


class MetaDecorate(type):
    "Enables delayed decoration and annotation of methods"
    def __new__(klass, name, bases, _dict):
        if "annotated" in _dict:
            for method, attrs in _dict["annotated"].iteritems():
                delayed_annotate(_dict[method], **attrs)
            del _dict["annotated"]
        if "decorated" in _dict:
            for method, decorators in _dict["decorated"].iteritems():
                _dict[method] = delayed_decorate(_dict[method], *decorators)
            del _dict["decorated"]
        return type.__new__(klass, name, bases, _dict)

class Decorate(object):
    __metaclass__ = MetaDecorate

class C(Decorate):
    "Example class"

    a = "A"

    # You can annotate and decorate a method before it has been defined.

    annotate("f", author="Sean Ross", version="0.9.1", deprecated=True)
    decorate("f", classmethod)
    def f(klass):
        return klass.a


    decorate("g", staticmethod)
    def g():
        return "G"

print "C.f.author =", C.f.author
print "C.f.version =", C.f.version
print "C.f() =>", C.f()
print "C.g() =>", C.g()



""" OUTPUT:
C.f.author = Sean Ross
C.f.version = 0.9.1
C.f() => A
C.g() => G
"""


Neat.





More information about the Python-list mailing list