##// END OF EJS Templates
Updated Michele Simionato's decorator.py module to 3.1.2....
Brian Granger -
Show More
@@ -0,0 +1,254 b''
1 ########################## LICENCE ###############################
2 ##
3 ## Copyright (c) 2005, Michele Simionato
4 ## All rights reserved.
5 ##
6 ## Redistributions of source code must retain the above copyright
7 ## notice, this list of conditions and the following disclaimer.
8 ## Redistributions in bytecode form must reproduce the above copyright
9 ## notice, this list of conditions and the following disclaimer in
10 ## the documentation and/or other materials provided with the
11 ## distribution.
12
13 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15 ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16 ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17 ## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18 ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
20 ## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
22 ## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23 ## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
24 ## DAMAGE.
25
26 """
27 Decorator module, see http://pypi.python.org/pypi/decorator
28 for the documentation.
29 """
30
31 __all__ = ["decorator", "FunctionMaker", "partial",
32 "deprecated", "getinfo", "new_wrapper"]
33
34 import os, sys, re, inspect, string, warnings
35 try:
36 from functools import partial
37 except ImportError: # for Python version < 2.5
38 class partial(object):
39 "A simple replacement of functools.partial"
40 def __init__(self, func, *args, **kw):
41 self.func = func
42 self.args = args
43 self.keywords = kw
44 def __call__(self, *otherargs, **otherkw):
45 kw = self.keywords.copy()
46 kw.update(otherkw)
47 return self.func(*(self.args + otherargs), **kw)
48
49 DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(')
50
51 # basic functionality
52 class FunctionMaker(object):
53 """
54 An object with the ability to create functions with a given signature.
55 It has attributes name, doc, module, signature, defaults, dict and
56 methods update and make.
57 """
58 def __init__(self, func=None, name=None, signature=None,
59 defaults=None, doc=None, module=None, funcdict=None):
60 if func:
61 # func can be a class or a callable, but not an instance method
62 self.name = func.__name__
63 if self.name == '<lambda>': # small hack for lambda functions
64 self.name = '_lambda_'
65 self.doc = func.__doc__
66 self.module = func.__module__
67 if inspect.isfunction(func):
68 argspec = inspect.getargspec(func)
69 self.args, self.varargs, self.keywords, self.defaults = argspec
70 for i, arg in enumerate(self.args):
71 setattr(self, 'arg%d' % i, arg)
72 self.signature = inspect.formatargspec(
73 formatvalue=lambda val: "", *argspec)[1:-1]
74 self.dict = func.__dict__.copy()
75 if name:
76 self.name = name
77 if signature is not None:
78 self.signature = signature
79 if defaults:
80 self.defaults = defaults
81 if doc:
82 self.doc = doc
83 if module:
84 self.module = module
85 if funcdict:
86 self.dict = funcdict
87 # check existence required attributes
88 assert hasattr(self, 'name')
89 if not hasattr(self, 'signature'):
90 raise TypeError('You are decorating a non function: %s' % func)
91
92 def update(self, func, **kw):
93 "Update the signature of func with the data in self"
94 func.__name__ = self.name
95 func.__doc__ = getattr(self, 'doc', None)
96 func.__dict__ = getattr(self, 'dict', {})
97 func.func_defaults = getattr(self, 'defaults', ())
98 callermodule = sys._getframe(3).f_globals.get('__name__', '?')
99 func.__module__ = getattr(self, 'module', callermodule)
100 func.__dict__.update(kw)
101
102 def make(self, src_templ, evaldict=None, addsource=False, **attrs):
103 "Make a new function from a given template and update the signature"
104 src = src_templ % vars(self) # expand name and signature
105 evaldict = evaldict or {}
106 mo = DEF.match(src)
107 if mo is None:
108 raise SyntaxError('not a valid function template\n%s' % src)
109 name = mo.group(1) # extract the function name
110 reserved_names = set([name] + [
111 arg.strip(' *') for arg in self.signature.split(',')])
112 for n, v in evaldict.iteritems():
113 if n in reserved_names:
114 raise NameError('%s is overridden in\n%s' % (n, src))
115 if not src.endswith('\n'): # add a newline just for safety
116 src += '\n'
117 try:
118 code = compile(src, '<string>', 'single')
119 exec code in evaldict
120 except:
121 print >> sys.stderr, 'Error in generated code:'
122 print >> sys.stderr, src
123 raise
124 func = evaldict[name]
125 if addsource:
126 attrs['__source__'] = src
127 self.update(func, **attrs)
128 return func
129
130 @classmethod
131 def create(cls, obj, body, evaldict, defaults=None,
132 doc=None, module=None, addsource=True,**attrs):
133 """
134 Create a function from the strings name, signature and body.
135 evaldict is the evaluation dictionary. If addsource is true an attribute
136 __source__ is added to the result. The attributes attrs are added,
137 if any.
138 """
139 if isinstance(obj, str): # "name(signature)"
140 name, rest = obj.strip().split('(', 1)
141 signature = rest[:-1] #strip a right parens
142 func = None
143 else: # a function
144 name = None
145 signature = None
146 func = obj
147 fun = cls(func, name, signature, defaults, doc, module)
148 ibody = '\n'.join(' ' + line for line in body.splitlines())
149 return fun.make('def %(name)s(%(signature)s):\n' + ibody,
150 evaldict, addsource, **attrs)
151
152 def decorator(caller, func=None):
153 """
154 decorator(caller) converts a caller function into a decorator;
155 decorator(caller, func) decorates a function using a caller.
156 """
157 if func is not None: # returns a decorated function
158 return FunctionMaker.create(
159 func, "return _call_(_func_, %(signature)s)",
160 dict(_call_=caller, _func_=func), undecorated=func)
161 else: # returns a decorator
162 if isinstance(caller, partial):
163 return partial(decorator, caller)
164 # otherwise assume caller is a function
165 f = inspect.getargspec(caller)[0][0] # first arg
166 return FunctionMaker.create(
167 '%s(%s)' % (caller.__name__, f),
168 'return decorator(_call_, %s)' % f,
169 dict(_call_=caller, decorator=decorator), undecorated=caller,
170 doc=caller.__doc__, module=caller.__module__)
171
172 ###################### deprecated functionality #########################
173
174 @decorator
175 def deprecated(func, *args, **kw):
176 "A decorator for deprecated functions"
177 warnings.warn(
178 ('Calling the deprecated function %r\n'
179 'Downgrade to decorator 2.3 if you want to use this functionality')
180 % func.__name__, DeprecationWarning, stacklevel=3)
181 return func(*args, **kw)
182
183 @deprecated
184 def getinfo(func):
185 """
186 Returns an info dictionary containing:
187 - name (the name of the function : str)
188 - argnames (the names of the arguments : list)
189 - defaults (the values of the default arguments : tuple)
190 - signature (the signature : str)
191 - doc (the docstring : str)
192 - module (the module name : str)
193 - dict (the function __dict__ : str)
194
195 >>> def f(self, x=1, y=2, *args, **kw): pass
196
197 >>> info = getinfo(f)
198
199 >>> info["name"]
200 'f'
201 >>> info["argnames"]
202 ['self', 'x', 'y', 'args', 'kw']
203
204 >>> info["defaults"]
205 (1, 2)
206
207 >>> info["signature"]
208 'self, x, y, *args, **kw'
209 """
210 assert inspect.ismethod(func) or inspect.isfunction(func)
211 regargs, varargs, varkwargs, defaults = inspect.getargspec(func)
212 argnames = list(regargs)
213 if varargs:
214 argnames.append(varargs)
215 if varkwargs:
216 argnames.append(varkwargs)
217 signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults,
218 formatvalue=lambda value: "")[1:-1]
219 return dict(name=func.__name__, argnames=argnames, signature=signature,
220 defaults = func.func_defaults, doc=func.__doc__,
221 module=func.__module__, dict=func.__dict__,
222 globals=func.func_globals, closure=func.func_closure)
223
224 @deprecated
225 def update_wrapper(wrapper, model, infodict=None):
226 "A replacement for functools.update_wrapper"
227 infodict = infodict or getinfo(model)
228 wrapper.__name__ = infodict['name']
229 wrapper.__doc__ = infodict['doc']
230 wrapper.__module__ = infodict['module']
231 wrapper.__dict__.update(infodict['dict'])
232 wrapper.func_defaults = infodict['defaults']
233 wrapper.undecorated = model
234 return wrapper
235
236 @deprecated
237 def new_wrapper(wrapper, model):
238 """
239 An improvement over functools.update_wrapper. The wrapper is a generic
240 callable object. It works by generating a copy of the wrapper with the
241 right signature and by updating the copy, not the original.
242 Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module',
243 'dict', 'defaults'.
244 """
245 if isinstance(model, dict):
246 infodict = model
247 else: # assume model is a function
248 infodict = getinfo(model)
249 assert not '_wrapper_' in infodict["argnames"], (
250 '"_wrapper_" is a reserved argument name!')
251 src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict
252 funcopy = eval(src, dict(_wrapper_=wrapper))
253 return update_wrapper(funcopy, model, infodict)
254
@@ -22,7 +22,7 b' import sys'
22 # Third-party imports
22 # Third-party imports
23
23
24 # This is Michele Simionato's decorator module, also kept verbatim.
24 # This is Michele Simionato's decorator module, also kept verbatim.
25 from decorator_msim import decorator, update_wrapper
25 from IPython.external.decorator import decorator, update_wrapper
26
26
27 # Grab the numpy-specific decorators which we keep in a file that we
27 # Grab the numpy-specific decorators which we keep in a file that we
28 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
28 # occasionally update from upstream: decorators_numpy.py is an IDENTICAL copy
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now