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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
# decorators.py
import inspect, itertools
def getinfo(func):
"""Return an info dictionary containing:
- name (the name of the function : str)
- argnames (the names of the arguments : list)
- defarg (the values of the default arguments : list)
- fullsign (the full signature : str)
- shortsign (the short signature : str)
- arg0 ... argn (shortcuts for the names of the arguments)
>> def f(self, x=1, y=2, *args, **kw): pass
>> info = getinfo(f)
>> info["name"]
'f'
>> info["argnames"]
['self', 'x', 'y', 'args', 'kw']
>> info["defarg"]
(1, 2)
>> info["shortsign"]
'self, x, y, *args, **kw'
>> info["fullsign"]
'self, x=defarg[0], y=defarg[1], *args, **kw'
>> info["arg0"], info["arg1"], info["arg2"], info["arg3"], info["arg4"]
('self', 'x', 'y', 'args', 'kw')
"""
assert inspect.ismethod(func) or inspect.isfunction(func)
regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
argnames = list(regargs)
if varargs: argnames.append(varargs)
if varkwargs: argnames.append(varkwargs)
counter = itertools.count()
fullsign = inspect.formatargspec(
regargs, varargs, varkwargs, defaults,
formatvalue=lambda value: "=defarg[%i]" % counter.next())[1:-1]
shortsign = inspect.formatargspec(
regargs, varargs, varkwargs, defaults,
formatvalue=lambda value: "")[1:-1]
dic = dict(("arg%s" % n, name) for n, name in enumerate(argnames))
dic.update(name=func.__name__, argnames=argnames, shortsign=shortsign,
fullsign = fullsign, defarg = func.func_defaults or ())
return dic
def _contains_reserved_names(dic): # helper
return "_call_" in dic or "_func_" in dic
def _decorate(func, caller):
"""Takes a function and a caller and returns the function
decorated with that caller. The decorated function is obtained
by evaluating a lambda function with the correct signature.
"""
infodict = getinfo(func)
assert not _contains_reserved_names(infodict["argnames"]), \
"You cannot use _call_ or _func_ as argument names!"
execdict=dict(_func_=func, _call_=caller, defarg=func.func_defaults or ())
if func.__name__ == "<lambda>":
lambda_src = "lambda %(fullsign)s: _call_(_func_, %(shortsign)s)" \
% infodict
dec_func = eval(lambda_src, execdict)
else:
func_src = """def %(name)s(%(fullsign)s):
return _call_(_func_, %(shortsign)s)""" % infodict
exec func_src in execdict
dec_func = execdict[func.__name__]
dec_func.__doc__ = func.__doc__
dec_func.__dict__ = func.__dict__
return dec_func
class decorator(object):
"""General purpose decorator factory: takes a caller function as
input and returns a decorator. A caller function is any function like this::
def caller(func, *args, **kw):
# do something
return func(*args, **kw)
Here is an example of usage:
>> @decorator
.. def chatty(f, *args, **kw):
.. print "Calling %r" % f.__name__
.. return f(*args, **kw)
>> @chatty
.. def f(): pass
..
>> f()
Calling 'f'
"""
def __init__(self, caller):
self.caller = caller
def __call__(self, func):
return _decorate(func, self.caller)
|