3 http://www.phyast.pitt.edu/~micheles/python/documentation.html
4 for the documentation and below for the licence.
7 ## The basic trick is to generate the source code for the decorated function
8 ## with the right signature and to evaluate it.
9 ## Uncomment the statement 'print >> sys.stderr, func_src' in _decorator
10 ## to understand what is going on.
12 __all__ = ["decorator", "new_wrapper", "getinfo"]
19 from sets import Set as set
23 Returns an info dictionary containing:
24 - name (the name of the function : str)
25 - argnames (the names of the arguments : list)
26 - defaults (the values of the default arguments : tuple)
27 - signature (the signature : str)
28 - doc (the docstring : str)
29 - module (the module name : str)
30 - dict (the function __dict__ : str)
32 >>> def f(self, x=1, y=2, *args, **kw): pass
39 ['self', 'x', 'y', 'args', 'kw']
45 'self, x, y, *args, **kw'
47 assert inspect.ismethod(func) or inspect.isfunction(func)
48 regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
49 argnames = list(regargs)
51 argnames.append(varargs)
53 argnames.append(varkwargs)
54 signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults,
55 formatvalue=lambda value: "")[1:-1]
56 return dict(name=func.__name__, argnames=argnames, signature=signature,
57 defaults = func.func_defaults, doc=func.__doc__,
58 module=func.__module__, dict=func.__dict__,
59 globals=func.func_globals, closure=func.func_closure)
61 # akin to functools.update_wrapper
62 def update_wrapper(wrapper, model, infodict=None):
63 infodict = infodict or getinfo(model)
65 wrapper.__name__ = infodict['name']
66 except: # Python version < 2.4
68 wrapper.__doc__ = infodict['doc']
69 wrapper.__module__ = infodict['module']
70 wrapper.__dict__.update(infodict['dict'])
71 wrapper.func_defaults = infodict['defaults']
72 wrapper.undecorated = model
75 def new_wrapper(wrapper, model):
77 An improvement over functools.update_wrapper. The wrapper is a generic
78 callable object. It works by generating a copy of the wrapper with the
79 right signature and by updating the copy, not the original.
80 Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module',
83 if isinstance(model, dict):
85 else: # assume model is a function
86 infodict = getinfo(model)
87 assert not '_wrapper_' in infodict["argnames"], (
88 '"_wrapper_" is a reserved argument name!')
89 src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict
90 funcopy = eval(src, dict(_wrapper_=wrapper))
91 return update_wrapper(funcopy, model, infodict)
93 # helper used in decorator_factory
94 def __call__(self, func):
95 infodict = getinfo(func)
96 for name in ('_func_', '_self_'):
97 assert not name in infodict["argnames"], (
98 '%s is a reserved argument name!' % name)
99 src = "lambda %(signature)s: _self_.call(_func_, %(signature)s)"
100 new = eval(src % infodict, dict(_func_=func, _self_=self))
101 return update_wrapper(new, func, infodict)
103 def decorator_factory(cls):
105 Take a class with a ``.caller`` method and return a callable decorator
106 object. It works by adding a suitable __call__ method to the class;
107 it raises a TypeError if the class already has a nontrivial __call__
110 attrs = set(dir(cls))
111 if '__call__' in attrs:
112 raise TypeError('You cannot decorate a class with a nontrivial '
114 if 'call' not in attrs:
115 raise TypeError('You cannot decorate a class without a '
117 cls.__call__ = __call__
120 def decorator(caller):
122 General purpose decorator factory: takes a caller function as
123 input and returns a decorator with the same attributes.
124 A caller function is any function like this::
126 def caller(func, *args, **kw):
128 return func(*args, **kw)
130 Here is an example of usage:
133 ... def chatty(f, *args, **kw):
134 ... print "Calling %r" % f.__name__
135 ... return f(*args, **kw)
146 decorator can also take in input a class with a .caller method; in this
147 case it converts the class into a factory of callable decorator objects.
148 See the documentation for an example.
150 if inspect.isclass(caller):
151 return decorator_factory(caller)
152 def _decorator(func): # the real meat is here
153 infodict = getinfo(func)
154 argnames = infodict['argnames']
155 assert not ('_call_' in argnames or '_func_' in argnames), (
156 'You cannot use _call_ or _func_ as argument names!')
157 src = "lambda %(signature)s: _call_(_func_, %(signature)s)" % infodict
158 # import sys; print >> sys.stderr, src # for debugging purposes
159 dec_func = eval(src, dict(_func_=func, _call_=caller))
160 return update_wrapper(dec_func, func, infodict)
161 return update_wrapper(_decorator, caller)
163 if __name__ == "__main__":
164 import doctest; doctest.testmod()
166 ########################## LEGALESE ###############################
168 ## Redistributions of source code must retain the above copyright
169 ## notice, this list of conditions and the following disclaimer.
170 ## Redistributions in bytecode form must reproduce the above copyright
171 ## notice, this list of conditions and the following disclaimer in
172 ## the documentation and/or other materials provided with the
175 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
176 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
177 ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
178 ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
179 ## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
180 ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
181 ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
182 ## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
183 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
184 ## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
185 ## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH