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/ex |
|
|
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