##// END OF EJS Templates
Update decorator module in externals to version 3.3.3 (upstream).
Fernando Perez -
Show More
@@ -1,254 +1,220 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
38 class partial(object):
44 class partial(object):
39 "A simple replacement of functools.partial"
45 "A simple replacement of functools.partial"
40 def __init__(self, func, *args, **kw):
46 def __init__(self, func, *args, **kw):
41 self.func = func
47 self.func = func
42 self.args = args
48 self.args = args
43 self.keywords = kw
49 self.keywords = kw
44 def __call__(self, *otherargs, **otherkw):
50 def __call__(self, *otherargs, **otherkw):
45 kw = self.keywords.copy()
51 kw = self.keywords.copy()
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
52 class FunctionMaker(object):
74 class FunctionMaker(object):
53 """
75 """
54 An object with the ability to create functions with a given signature.
76 An object with the ability to create functions with a given signature.
55 It has attributes name, doc, module, signature, defaults, dict and
77 It has attributes name, doc, module, signature, defaults, dict and
56 methods update and make.
78 methods update and make.
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__
63 if self.name == '<lambda>': # small hack for lambda functions
86 if self.name == '<lambda>': # small hack for lambda functions
64 self.name = '_lambda_'
87 self.name = '_lambda_'
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:
78 self.signature = signature
119 self.signature = signature
79 if defaults:
120 if defaults:
80 self.defaults = defaults
121 self.defaults = defaults
81 if doc:
122 if doc:
82 self.doc = doc
123 self.doc = doc
83 if module:
124 if module:
84 self.module = module
125 self.module = module
85 if funcdict:
126 if funcdict:
86 self.dict = funcdict
127 self.dict = funcdict
87 # check existence required attributes
128 # check existence required attributes
88 assert hasattr(self, 'name')
129 assert hasattr(self, 'name')
89 if not hasattr(self, 'signature'):
130 if not hasattr(self, 'signature'):
90 raise TypeError('You are decorating a non function: %s' % func)
131 raise TypeError('You are decorating a non function: %s' % func)
91
132
92 def update(self, func, **kw):
133 def update(self, func, **kw):
93 "Update the signature of func with the data in self"
134 "Update the signature of func with the data in self"
94 func.__name__ = self.name
135 func.__name__ = self.name
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)
101
144
102 def make(self, src_templ, evaldict=None, addsource=False, **attrs):
145 def make(self, src_templ, evaldict=None, addsource=False, **attrs):
103 "Make a new function from a given template and update the signature"
146 "Make a new function from a given template and update the signature"
104 src = src_templ % vars(self) # expand name and signature
147 src = src_templ % vars(self) # expand name and signature
105 evaldict = evaldict or {}
148 evaldict = evaldict or {}
106 mo = DEF.match(src)
149 mo = DEF.match(src)
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:'
122 print >> sys.stderr, src
166 print >> sys.stderr, src
123 raise
167 raise
124 func = evaldict[name]
168 func = evaldict[name]
125 if addsource:
169 if addsource:
126 attrs['__source__'] = src
170 attrs['__source__'] = src
127 self.update(func, **attrs)
171 self.update(func, **attrs)
128 return func
172 return func
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
136 __source__ is added to the result. The attributes attrs are added,
180 __source__ is added to the result. The attributes attrs are added,
137 if any.
181 if any.
138 """
182 """
139 if isinstance(obj, str): # "name(signature)"
183 if isinstance(obj, str): # "name(signature)"
140 name, rest = obj.strip().split('(', 1)
184 name, rest = obj.strip().split('(', 1)
141 signature = rest[:-1] #strip a right parens
185 signature = rest[:-1] #strip a right parens
142 func = None
186 func = None
143 else: # a function
187 else: # a function
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):
153 """
197 """
154 decorator(caller) converts a caller function into a decorator;
198 decorator(caller) converts a caller function into a decorator;
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