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
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 10 months, 2 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 6 months ago
Comments
Please login first before commenting.