##// END OF EJS Templates
Typo in comment fixed
Doug Blank -
Show More
@@ -1,229 +1,229 b''
1 1 ########################## LICENCE ###############################
2 2
3 3 # Copyright (c) 2005-2012, Michele Simionato
4 4 # All rights reserved.
5 5
6 6 # Redistribution and use in source and binary forms, with or without
7 7 # modification, are permitted provided that the following conditions are
8 8 # met:
9 9
10 10 # Redistributions of source code must retain the above copyright
11 11 # notice, this list of conditions and the following disclaimer.
12 12 # Redistributions in bytecode form must reproduce the above copyright
13 13 # notice, this list of conditions and the following disclaimer in
14 14 # the documentation and/or other materials provided with the
15 15 # distribution.
16 16
17 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 21 # HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23 23 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24 24 # OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 25 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26 26 # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27 27 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
28 28 # DAMAGE.
29 29
30 30 """
31 31 Decorator module, see http://pypi.python.org/pypi/decorator
32 32 for the documentation.
33 33
34 34 NOTE: this is an IPython-patched version to work on IronPython. See
35 35 FIXED comment below.
36 36 """
37 37 from __future__ import print_function
38 38
39 39 __version__ = '3.3.3'
40 40
41 41 __all__ = ["decorator", "FunctionMaker", "partial"]
42 42
43 43 import sys, re, inspect
44 44
45 45 try:
46 46 from functools import partial
47 47 except ImportError: # for Python version < 2.5
48 48 class partial(object):
49 49 "A simple replacement of functools.partial"
50 50 def __init__(self, func, *args, **kw):
51 51 self.func = func
52 52 self.args = args
53 53 self.keywords = kw
54 54 def __call__(self, *otherargs, **otherkw):
55 55 kw = self.keywords.copy()
56 56 kw.update(otherkw)
57 57 return self.func(*(self.args + otherargs), **kw)
58 58
59 59 if sys.version >= '3':
60 60 from inspect import getfullargspec
61 61 else:
62 62 class getfullargspec(object):
63 63 "A quick and dirty replacement for getfullargspec for Python 2.X"
64 64 def __init__(self, f):
65 65 self.args, self.varargs, self.varkw, self.defaults = \
66 66 inspect.getargspec(f)
67 67 self.kwonlyargs = []
68 68 self.kwonlydefaults = None
69 69 def __iter__(self):
70 70 yield self.args
71 71 yield self.varargs
72 72 yield self.varkw
73 73 yield self.defaults
74 74
75 75 DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(')
76 76
77 77 # basic functionality
78 78 class FunctionMaker(object):
79 79 """
80 80 An object with the ability to create functions with a given signature.
81 81 It has attributes name, doc, module, signature, defaults, dict and
82 82 methods update and make.
83 83 """
84 84 def __init__(self, func=None, name=None, signature=None,
85 85 defaults=None, doc=None, module=None, funcdict=None):
86 86 self.shortsignature = signature
87 87 if func:
88 88 # func can be a class or a callable, but not an instance method
89 89 self.name = func.__name__
90 90 if self.name == '<lambda>': # small hack for lambda functions
91 91 self.name = '_lambda_'
92 92 self.doc = func.__doc__
93 93 self.module = func.__module__
94 94 if inspect.isfunction(func):
95 95 argspec = getfullargspec(func)
96 96 self.annotations = getattr(func, '__annotations__', {})
97 97 for a in ('args', 'varargs', 'varkw', 'defaults', 'kwonlyargs',
98 98 'kwonlydefaults'):
99 99 setattr(self, a, getattr(argspec, a))
100 100 for i, arg in enumerate(self.args):
101 101 setattr(self, 'arg%d' % i, arg)
102 102 if sys.version < '3': # easy way
103 103 self.shortsignature = self.signature = \
104 104 inspect.formatargspec(
105 105 formatvalue=lambda val: "", *argspec)[1:-1]
106 106 else: # Python 3 way
107 107 self.signature = self.shortsignature = ', '.join(self.args)
108 108 if self.varargs:
109 109 self.signature += ', *' + self.varargs
110 110 self.shortsignature += ', *' + self.varargs
111 111 if self.kwonlyargs:
112 112 for a in self.kwonlyargs:
113 113 self.signature += ', %s=None' % a
114 114 self.shortsignature += ', %s=%s' % (a, a)
115 115 if self.varkw:
116 116 self.signature += ', **' + self.varkw
117 117 self.shortsignature += ', **' + self.varkw
118 118 self.dict = func.__dict__.copy()
119 119 # func=None happens when decorating a caller
120 120 if name:
121 121 self.name = name
122 122 if signature is not None:
123 123 self.signature = signature
124 124 if defaults:
125 125 self.defaults = defaults
126 126 if doc:
127 127 self.doc = doc
128 128 if module:
129 129 self.module = module
130 130 if funcdict:
131 131 self.dict = funcdict
132 132 # check existence required attributes
133 133 assert hasattr(self, 'name')
134 134 if not hasattr(self, 'signature'):
135 135 raise TypeError('You are decorating a non function: %s' % func)
136 136
137 137 def update(self, func, **kw):
138 138 "Update the signature of func with the data in self"
139 139 func.__name__ = self.name
140 140 func.__doc__ = getattr(self, 'doc', None)
141 141 func.__dict__ = getattr(self, 'dict', {})
142 142 func.__defaults__ = getattr(self, 'defaults', ())
143 143 func.__kwdefaults__ = getattr(self, 'kwonlydefaults', None)
144 144 func.__annotations__ = getattr(self, 'annotations', None)
145 # FIXED: The following is try/execpted in IPython to work
145 # FIXED: The following is try/excepted in IPython to work
146 146 # with IronPython.
147 147 try:
148 148 callermodule = sys._getframe(3).f_globals.get('__name__', '?')
149 149 except AttributeError: # IronPython _getframe only exists with FullFrames
150 150 callermodule = '?'
151 151 func.__module__ = getattr(self, 'module', callermodule)
152 152 func.__dict__.update(kw)
153 153
154 154 def make(self, src_templ, evaldict=None, addsource=False, **attrs):
155 155 "Make a new function from a given template and update the signature"
156 156 src = src_templ % vars(self) # expand name and signature
157 157 evaldict = evaldict or {}
158 158 mo = DEF.match(src)
159 159 if mo is None:
160 160 raise SyntaxError('not a valid function template\n%s' % src)
161 161 name = mo.group(1) # extract the function name
162 162 names = set([name] + [arg.strip(' *') for arg in
163 163 self.shortsignature.split(',')])
164 164 for n in names:
165 165 if n in ('_func_', '_call_'):
166 166 raise NameError('%s is overridden in\n%s' % (n, src))
167 167 if not src.endswith('\n'): # add a newline just for safety
168 168 src += '\n' # this is needed in old versions of Python
169 169 try:
170 170 code = compile(src, '<string>', 'single')
171 171 # print >> sys.stderr, 'Compiling %s' % src
172 172 exec(code, evaldict)
173 173 except:
174 174 print('Error in generated code:', file=sys.stderr)
175 175 print(src, file=sys.stderr)
176 176 raise
177 177 func = evaldict[name]
178 178 if addsource:
179 179 attrs['__source__'] = src
180 180 self.update(func, **attrs)
181 181 return func
182 182
183 183 @classmethod
184 184 def create(cls, obj, body, evaldict, defaults=None,
185 185 doc=None, module=None, addsource=True, **attrs):
186 186 """
187 187 Create a function from the strings name, signature and body.
188 188 evaldict is the evaluation dictionary. If addsource is true an attribute
189 189 __source__ is added to the result. The attributes attrs are added,
190 190 if any.
191 191 """
192 192 if isinstance(obj, str): # "name(signature)"
193 193 name, rest = obj.strip().split('(', 1)
194 194 signature = rest[:-1] #strip a right parens
195 195 func = None
196 196 else: # a function
197 197 name = None
198 198 signature = None
199 199 func = obj
200 200 self = cls(func, name, signature, defaults, doc, module)
201 201 ibody = '\n'.join(' ' + line for line in body.splitlines())
202 202 return self.make('def %(name)s(%(signature)s):\n' + ibody,
203 203 evaldict, addsource, **attrs)
204 204
205 205 def decorator(caller, func=None):
206 206 """
207 207 decorator(caller) converts a caller function into a decorator;
208 208 decorator(caller, func) decorates a function using a caller.
209 209 """
210 210 if func is not None: # returns a decorated function
211 211 evaldict = func.__globals__.copy()
212 212 evaldict['_call_'] = caller
213 213 evaldict['_func_'] = func
214 214 return FunctionMaker.create(
215 215 func, "return _call_(_func_, %(shortsignature)s)",
216 216 evaldict, undecorated=func, __wrapped__=func)
217 217 else: # returns a decorator
218 218 if isinstance(caller, partial):
219 219 return partial(decorator, caller)
220 220 # otherwise assume caller is a function
221 221 first = inspect.getargspec(caller)[0][0] # first arg
222 222 evaldict = caller.__globals__.copy()
223 223 evaldict['_call_'] = caller
224 224 evaldict['decorator'] = decorator
225 225 return FunctionMaker.create(
226 226 '%s(%s)' % (caller.__name__, first),
227 227 'return decorator(_call_, %s)' % first,
228 228 evaldict, undecorated=caller, __wrapped__=caller,
229 229 doc=caller.__doc__, module=caller.__module__)
General Comments 0
You need to be logged in to leave comments. Login now