Login

decorator for decorators with optional args

Author:
homunq
Posted:
November 30, 2011
Language:
Python
Version:
1.3
Score:
0 (after 0 ratings)

Have you ever wanted a decorator that you could apply either straight-out:

@mydec def myfun(...): ...

or with special arguments:

@mydec(special=foo) def myfun(...): ...

? Well, decorate it with this metadecorator, and voila.

(I had this idea independently, but it's been done before as decorator_withargs: http://osdir.com/ml/python.ideas/2008-01/msg00048.html. My version is actually useful because it deals with signatures and calling directly.)

As http://www.siafoo.net/article/68 points out, the standard decorator module has too much magic: the "@decorator" decorator expects a wrapping function, not a working decorator. This module fixes that.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from decorator import decorator
from functools import partial
from inspect import getargspec


def better_update_wrapper(wrapper,template):
    """Like functools.update_wrapper, but changes signature as well.
    Like decorator.decorator, but doesn't try any funny magic
    """
    def decorator_module_sucks(f,*a,**kw):
        return wrapper(*a,**kw)
    return decorator(decorator_module_sucks,template)

def dec_w_args(dec):
    """Turns a working decorator function with default keyword args into something that you can either
    apply directly as in `@mydec` or change the default args as in `@mydec(myarg="special")`. Also fixes
    function signatures.
    
    >>> @dec_w_args
    ... def tracing(fun=None, before="enter", after="leave"):
    ...     def traced(*args, **kw):
    ...         print before, fun.__name__, args, kw
    ...         fun(*args, **kw)
    ...         print after
    ...     return traced #decorator(traced, fun)
        
    >>> @tracing
    ... def t(x):
    ...     print x * x
        
    >>> t(2)
    enter t (2,) {}
    4
    leave
    
    >>> @tracing(before = "abandon hope ye who enter")
    ... def tdoom(x):
    ...     print x * x
    
    >>> tdoom(3)
    abandon hope ye who enter tdoom (3,) {}
    9
    leave
    """
    spec = getargspec(dec)
    assert len(spec.args) == len(spec.defaults) and spec.defaults[0] == None, "dec_w_args decorates decorators that look like like my_decorator(fun=None,...)"
    def optargs_decorator(f=None, *args, **kw):
        def curried_dec(inner_f):
            return better_update_wrapper(partial(dec, **kw)(inner_f),inner_f)
        if f:
            return curried_dec(f)
        return curried_dec #partial(decorator,curried_dec)
    return better_update_wrapper(optargs_decorator,dec)

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 6 months ago

Comments

Please login first before commenting.