1 """
2 Decorator module by Michele Simionato <[email protected]>
3 Copyright Michele Simionato, distributed under the terms of the BSD License (see below).
4 http://www.phyast.pitt.edu/~micheles/python/documentation.html
5
6 Included in NLTK for its support of a nice memoization decorator.
7 """
8 __docformat__ = 'restructuredtext en'
9
10
11
12
13
14
15 __all__ = ["decorator", "new_wrapper", "getinfo"]
16
17 import inspect, sys
18
19 try:
20 set
21 except NameError:
22 from sets import Set as set
23
25 """
26 Returns an info dictionary containing:
27 - name (the name of the function : str)
28 - argnames (the names of the arguments : list)
29 - defaults (the values of the default arguments : tuple)
30 - signature (the signature : str)
31 - doc (the docstring : str)
32 - module (the module name : str)
33 - dict (the function __dict__ : str)
34
35 >>> def f(self, x=1, y=2, *args, **kw): pass
36
37 >>> info = getinfo(f)
38
39 >>> info["name"]
40 'f'
41 >>> info["argnames"]
42 ['self', 'x', 'y', 'args', 'kw']
43
44 >>> info["defaults"]
45 (1, 2)
46
47 >>> info["signature"]
48 'self, x, y, *args, **kw'
49 """
50 assert inspect.ismethod(func) or inspect.isfunction(func)
51 regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
52 argnames = list(regargs)
53 if varargs:
54 argnames.append(varargs)
55 if varkwargs:
56 argnames.append(varkwargs)
57 signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults,
58 formatvalue=lambda value: "")[1:-1]
59 return dict(name=func.__name__, argnames=argnames, signature=signature,
60 defaults = func.func_defaults, doc=func.__doc__,
61 module=func.__module__, dict=func.__dict__,
62 globals=func.func_globals, closure=func.func_closure)
63
64
66 infodict = infodict or getinfo(model)
67 try:
68 wrapper.__name__ = infodict['name']
69 except:
70 pass
71 wrapper.__doc__ = infodict['doc']
72 wrapper.__module__ = infodict['module']
73 wrapper.__dict__.update(infodict['dict'])
74 wrapper.func_defaults = infodict['defaults']
75 wrapper.undecorated = model
76 return wrapper
77
79 """
80 An improvement over functools.update_wrapper. The wrapper is a generic
81 callable object. It works by generating a copy of the wrapper with the
82 right signature and by updating the copy, not the original.
83 Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module',
84 'dict', 'defaults'.
85 """
86 if isinstance(model, dict):
87 infodict = model
88 else:
89 infodict = getinfo(model)
90 assert not '_wrapper_' in infodict["argnames"], (
91 '"_wrapper_" is a reserved argument name!')
92 src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict
93 funcopy = eval(src, dict(_wrapper_=wrapper))
94 return update_wrapper(funcopy, model, infodict)
95
96
98 return new_wrapper(lambda *a, **k : self.call(func, *a, **k), func)
99
101 """
102 Take a class with a ``.caller`` method and return a callable decorator
103 object. It works by adding a suitable __call__ method to the class;
104 it raises a TypeError if the class already has a nontrivial __call__
105 method.
106 """
107 attrs = set(dir(cls))
108 if '__call__' in attrs:
109 raise TypeError('You cannot decorate a class with a nontrivial '
110 '__call__ method')
111 if 'call' not in attrs:
112 raise TypeError('You cannot decorate a class without a '
113 '.call method')
114 cls.__call__ = __call__
115 return cls
116
118 """
119 General purpose decorator factory: takes a caller function as
120 input and returns a decorator with the same attributes.
121 A caller function is any function like this::
122
123 def caller(func, *args, **kw):
124 # do something
125 return func(*args, **kw)
126
127 Here is an example of usage:
128
129 >>> @decorator
130 ... def chatty(f, *args, **kw):
131 ... print "Calling %r" % f.__name__
132 ... return f(*args, **kw)
133
134 >>> chatty.__name__
135 'chatty'
136
137 >>> @chatty
138 ... def f(): pass
139 ...
140 >>> f()
141 Calling 'f'
142
143 decorator can also take in input a class with a .caller method; in this
144 case it converts the class into a factory of callable decorator objects.
145 See the documentation for an example.
146 """
147 if inspect.isclass(caller):
148 return decorator_factory(caller)
149 def _decorator(func):
150 infodict = getinfo(func)
151 argnames = infodict['argnames']
152 assert not ('_call_' in argnames or '_func_' in argnames), (
153 'You cannot use _call_ or _func_ as argument names!')
154 src = "lambda %(signature)s: _call_(_func_, %(signature)s)" % infodict
155
156 dec_func = eval(src, dict(_func_=func, _call_=caller))
157 return update_wrapper(dec_func, func, infodict)
158 return update_wrapper(_decorator, caller)
159
161 "Similar to .setdefault in dictionaries."
162 try:
163 return getattr(obj, name)
164 except AttributeError:
165 default = default_thunk()
166 setattr(obj, name, default)
167 return default
168
169 @decorator
171 dic = getattr_(func, "memoize_dic", dict)
172
173 if args in dic:
174 return dic[args]
175 else:
176 result = func(*args)
177 dic[args] = result
178 return result
179
180 if __name__ == "__main__":
181 import doctest; doctest.testmod()
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204