##// END OF EJS Templates
Update decorator module in externals to version 3.3.3 (upstream).
Fernando Perez -
Show More
@@ -1,37 +1,43 b''
1 ########################## LICENCE ###############################
1 ########################## LICENCE ###############################
2 ##
2
3 ## Copyright (c) 2005, Michele Simionato
3 # Copyright (c) 2005-2012, Michele Simionato
4 ## All rights reserved.
4 # All rights reserved.
5 ##
5
6 ## Redistributions of source code must retain the above copyright
6 # Redistribution and use in source and binary forms, with or without
7 ## notice, this list of conditions and the following disclaimer.
7 # modification, are permitted provided that the following conditions are
8 ## Redistributions in bytecode form must reproduce the above copyright
8 # met:
9 ## notice, this list of conditions and the following disclaimer in
9
10 ## the documentation and/or other materials provided with the
10 # Redistributions of source code must retain the above copyright
11 ## distribution.
11 # notice, this list of conditions and the following disclaimer.
12
12 # Redistributions in bytecode form must reproduce the above copyright
13 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
13 # notice, this list of conditions and the following disclaimer in
14 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
14 # the documentation and/or other materials provided with the
15 ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
15 # distribution.
16 ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
16
17 ## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 ## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
21 # HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 ## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 ## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
23 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24 ## DAMAGE.
24 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
28 # DAMAGE.
25
29
26 """
30 """
27 Decorator module, see http://pypi.python.org/pypi/decorator
31 Decorator module, see http://pypi.python.org/pypi/decorator
28 for the documentation.
32 for the documentation.
29 """
33 """
30
34
31 __all__ = ["decorator", "FunctionMaker", "partial",
35 __version__ = '3.3.3'
32 "deprecated", "getinfo", "new_wrapper"]
36
37 __all__ = ["decorator", "FunctionMaker", "partial"]
38
39 import sys, re, inspect
33
40
34 import os, sys, re, inspect, string, warnings
35 try:
41 try:
36 from functools import partial
42 from functools import partial
37 except ImportError: # for Python version < 2.5
43 except ImportError: # for Python version < 2.5
@@ -46,6 +52,22 b' except ImportError: # for Python version < 2.5'
46 kw.update(otherkw)
52 kw.update(otherkw)
47 return self.func(*(self.args + otherargs), **kw)
53 return self.func(*(self.args + otherargs), **kw)
48
54
55 if sys.version >= '3':
56 from inspect import getfullargspec
57 else:
58 class getfullargspec(object):
59 "A quick and dirty replacement for getfullargspec for Python 2.X"
60 def __init__(self, f):
61 self.args, self.varargs, self.varkw, self.defaults = \
62 inspect.getargspec(f)
63 self.kwonlyargs = []
64 self.kwonlydefaults = None
65 def __iter__(self):
66 yield self.args
67 yield self.varargs
68 yield self.varkw
69 yield self.defaults
70
49 DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(')
71 DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(')
50
72
51 # basic functionality
73 # basic functionality
@@ -57,6 +79,7 b' class FunctionMaker(object):'
57 """
79 """
58 def __init__(self, func=None, name=None, signature=None,
80 def __init__(self, func=None, name=None, signature=None,
59 defaults=None, doc=None, module=None, funcdict=None):
81 defaults=None, doc=None, module=None, funcdict=None):
82 self.shortsignature = signature
60 if func:
83 if func:
61 # func can be a class or a callable, but not an instance method
84 # func can be a class or a callable, but not an instance method
62 self.name = func.__name__
85 self.name = func.__name__
@@ -65,13 +88,31 b' class FunctionMaker(object):'
65 self.doc = func.__doc__
88 self.doc = func.__doc__
66 self.module = func.__module__
89 self.module = func.__module__
67 if inspect.isfunction(func):
90 if inspect.isfunction(func):
68 argspec = inspect.getargspec(func)
91 argspec = getfullargspec(func)
69 self.args, self.varargs, self.keywords, self.defaults = argspec
92 self.annotations = getattr(func, '__annotations__', {})
93 for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs',
94 'kwonlydefaults'):
95 setattr(self, a, getattr(argspec, a))
70 for i, arg in enumerate(self.args):
96 for i, arg in enumerate(self.args):
71 setattr(self, 'arg%d' % i, arg)
97 setattr(self, 'arg%d' % i, arg)
72 self.signature = inspect.formatargspec(
98 if sys.version < '3': # easy way
99 self.shortsignature = self.signature = \
100 inspect.formatargspec(
73 formatvalue=lambda val: "", *argspec)[1:-1]
101 formatvalue=lambda val: "", *argspec)[1:-1]
102 else: # Python 3 way
103 self.signature = self.shortsignature = ', '.join(self.args)
104 if self.varargs:
105 self.signature += ', *' + self.varargs
106 self.shortsignature += ', *' + self.varargs
107 if self.kwonlyargs:
108 for a in self.kwonlyargs:
109 self.signature += ', %s=None' % a
110 self.shortsignature += ', %s=%s' % (a, a)
111 if self.varkw:
112 self.signature += ', **' + self.varkw
113 self.shortsignature += ', **' + self.varkw
74 self.dict = func.__dict__.copy()
114 self.dict = func.__dict__.copy()
115 # func=None happens when decorating a caller
75 if name:
116 if name:
76 self.name = name
117 self.name = name
77 if signature is not None:
118 if signature is not None:
@@ -95,6 +136,8 b' class FunctionMaker(object):'
95 func.__doc__ = getattr(self, 'doc', None)
136 func.__doc__ = getattr(self, 'doc', None)
96 func.__dict__ = getattr(self, 'dict', {})
137 func.__dict__ = getattr(self, 'dict', {})
97 func.func_defaults = getattr(self, 'defaults', ())
138 func.func_defaults = getattr(self, 'defaults', ())
139 func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None)
140 func.__annotations__ = getattr(self, 'annotations', None)
98 callermodule = sys._getframe(3).f_globals.get('__name__', '?')
141 callermodule = sys._getframe(3).f_globals.get('__name__', '?')
99 func.__module__ = getattr(self, 'module', callermodule)
142 func.__module__ = getattr(self, 'module', callermodule)
100 func.__dict__.update(kw)
143 func.__dict__.update(kw)
@@ -107,15 +150,16 b' class FunctionMaker(object):'
107 if mo is None:
150 if mo is None:
108 raise SyntaxError('not a valid function template\n%s' % src)
151 raise SyntaxError('not a valid function template\n%s' % src)
109 name = mo.group(1) # extract the function name
152 name = mo.group(1) # extract the function name
110 reserved_names = set([name] + [
153 names = set([name] + [arg.strip(' *') for arg in
111 arg.strip(' *') for arg in self.signature.split(',')])
154 self.shortsignature.split(',')])
112 for n, v in evaldict.iteritems():
155 for n in names:
113 if n in reserved_names:
156 if n in ('_func_', '_call_'):
114 raise NameError('%s is overridden in\n%s' % (n, src))
157 raise NameError('%s is overridden in\n%s' % (n, src))
115 if not src.endswith('\n'): # add a newline just for safety
158 if not src.endswith('\n'): # add a newline just for safety
116 src += '\n'
159 src += '\n' # this is needed in old versions of Python
117 try:
160 try:
118 code = compile(src, '<string>', 'single')
161 code = compile(src, '<string>', 'single')
162 # print >> sys.stderr, 'Compiling %s' % src
119 exec code in evaldict
163 exec code in evaldict
120 except:
164 except:
121 print >> sys.stderr, 'Error in generated code:'
165 print >> sys.stderr, 'Error in generated code:'
@@ -144,9 +188,9 b' class FunctionMaker(object):'
144 name = None
188 name = None
145 signature = None
189 signature = None
146 func = obj
190 func = obj
147 fun = cls(func, name, signature, defaults, doc, module)
191 self = cls(func, name, signature, defaults, doc, module)
148 ibody = '\n'.join(' ' + line for line in body.splitlines())
192 ibody = '\n'.join(' ' + line for line in body.splitlines())
149 return fun.make('def %(name)s(%(signature)s):\n' + ibody,
193 return self.make('def %(name)s(%(signature)s):\n' + ibody,
150 evaldict, addsource, **attrs)
194 evaldict, addsource, **attrs)
151
195
152 def decorator(caller, func=None):
196 def decorator(caller, func=None):
@@ -155,100 +199,22 b' def decorator(caller, func=None):'
155 decorator(caller, func) decorates a function using a caller.
199 decorator(caller, func) decorates a function using a caller.
156 """
200 """
157 if func is not None: # returns a decorated function
201 if func is not None: # returns a decorated function
202 evaldict = func.func_globals.copy()
203 evaldict['_call_'] = caller
204 evaldict['_func_'] = func
158 return FunctionMaker.create(
205 return FunctionMaker.create(
159 func, "return _call_(_func_, %(signature)s)",
206 func, "return _call_(_func_, %(shortsignature)s)",
160 dict(_call_=caller, _func_=func), undecorated=func)
207 evaldict, undecorated=func, __wrapped__=func)
161 else: # returns a decorator
208 else: # returns a decorator
162 if isinstance(caller, partial):
209 if isinstance(caller, partial):
163 return partial(decorator, caller)
210 return partial(decorator, caller)
164 # otherwise assume caller is a function
211 # otherwise assume caller is a function
165 f = inspect.getargspec(caller)[0][0] # first arg
212 first = inspect.getargspec(caller)[0][0] # first arg
213 evaldict = caller.func_globals.copy()
214 evaldict['_call_'] = caller
215 evaldict['decorator'] = decorator
166 return FunctionMaker.create(
216 return FunctionMaker.create(
167 '%s(%s)' % (caller.__name__, f),
217 '%s(%s)' % (caller.__name__, first),
168 'return decorator(_call_, %s)' % f,
218 'return decorator(_call_, %s)' % first,
169 dict(_call_=caller, decorator=decorator), undecorated=caller,
219 evaldict, undecorated=caller, __wrapped__=caller,
170 doc=caller.__doc__, module=caller.__module__)
220 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
General Comments 0
You need to be logged in to leave comments. Login now