##// 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
73 formatvalue=lambda val: "", *argspec)[1:-1]
99 self.shortsignature = self.signature = \
100 inspect.formatargspec(
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:'
@@ -129,7 +173,7 b' class FunctionMaker(object):'
129
173
130 @classmethod
174 @classmethod
131 def create(cls, obj, body, evaldict, defaults=None,
175 def create(cls, obj, body, evaldict, defaults=None,
132 doc=None, module=None, addsource=True,**attrs):
176 doc=None, module=None, addsource=True, **attrs):
133 """
177 """
134 Create a function from the strings name, signature and body.
178 Create a function from the strings name, signature and body.
135 evaldict is the evaluation dictionary. If addsource is true an attribute
179 evaldict is the evaluation dictionary. If addsource is true an attribute
@@ -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