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