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