Mercurial > genshi > mirror
annotate markup/eval.py @ 134:d681d2c3cd8d trunk
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
* Evaluation errors in expressions now include the original expression code in the traceback.
author | cmlenz |
---|---|
date | Sun, 06 Aug 2006 18:07:21 +0000 |
parents | 79f445396cd7 |
children | 8ad716b4180d |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
66
59eb24184e9c
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
42
diff
changeset
|
3 # Copyright (C) 2006 Edgewall Software |
1 | 4 # All rights reserved. |
5 # | |
6 # This software is licensed as described in the file COPYING, which | |
7 # you should have received as part of this distribution. The terms | |
66
59eb24184e9c
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
42
diff
changeset
|
8 # are also available at http://markup.edgewall.org/wiki/License. |
1 | 9 # |
10 # This software consists of voluntary contributions made by many | |
11 # individuals. For the exact contribution history, see the revision | |
66
59eb24184e9c
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
42
diff
changeset
|
12 # history and logs, available at http://markup.edgewall.org/log/. |
27 | 13 |
14 """Support for "safe" evaluation of Python expressions.""" | |
1 | 15 |
16 import __builtin__ | |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
17 from compiler import ast, parse |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
18 from compiler.pycodegen import ExpressionCodeGenerator |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
19 import new |
1 | 20 |
21 from markup.core import Stream | |
22 | |
23 __all__ = ['Expression'] | |
24 | |
25 | |
26 class Expression(object): | |
27 """Evaluates Python expressions used in templates. | |
28 | |
29 >>> data = dict(test='Foo', items=[1, 2, 3], dict={'some': 'thing'}) | |
30 >>> Expression('test').evaluate(data) | |
31 'Foo' | |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
32 |
1 | 33 >>> Expression('items[0]').evaluate(data) |
34 1 | |
35 >>> Expression('items[-1]').evaluate(data) | |
36 3 | |
37 >>> Expression('dict["some"]').evaluate(data) | |
38 'thing' | |
39 | |
40 Similar to e.g. Javascript, expressions in templates can use the dot | |
41 notation for attribute access to access items in mappings: | |
42 | |
43 >>> Expression('dict.some').evaluate(data) | |
44 'thing' | |
86 | 45 |
1 | 46 This also works the other way around: item access can be used to access |
47 any object attribute (meaning there's no use for `getattr()` in templates): | |
48 | |
49 >>> class MyClass(object): | |
50 ... myattr = 'Bar' | |
51 >>> data = dict(mine=MyClass(), key='myattr') | |
52 >>> Expression('mine.myattr').evaluate(data) | |
53 'Bar' | |
54 >>> Expression('mine["myattr"]').evaluate(data) | |
55 'Bar' | |
56 >>> Expression('mine[key]').evaluate(data) | |
57 'Bar' | |
58 | |
31 | 59 All of the standard Python operators are available to template expressions. |
1 | 60 Built-in functions such as `len()` are also available in template |
61 expressions: | |
62 | |
63 >>> data = dict(items=[1, 2, 3]) | |
64 >>> Expression('len(items)').evaluate(data) | |
65 3 | |
66 """ | |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
67 __slots__ = ['source', 'code'] |
1 | 68 |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
69 def __init__(self, source, filename=None, lineno=-1): |
27 | 70 """Create the expression. |
71 | |
72 @param source: the expression as string | |
73 """ | |
1 | 74 self.source = source |
134
d681d2c3cd8d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
75 self.code = _compile(self, filename, lineno) |
1 | 76 |
77 def __repr__(self): | |
78 return '<Expression "%s">' % self.source | |
79 | |
120 | 80 def evaluate(self, data, nocall=False): |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
81 """Evaluate the expression against the given data dictionary. |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
82 |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
83 @param data: a mapping containing the data to evaluate against |
120 | 84 @param nocall: if true, the result of the evaluation is not called if |
85 if it is a callable | |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
86 @return: the result of the evaluation |
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
87 """ |
118
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
88 retval = eval(self.code, {'data': data, |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
89 '_lookup_name': _lookup_name, |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
90 '_lookup_attr': _lookup_attr, |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
91 '_lookup_item': _lookup_item}) |
120 | 92 if not nocall and callable(retval): |
90
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
93 retval = retval() |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
94 return retval |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
95 |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
96 |
134
d681d2c3cd8d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
97 def _compile(expr, filename=None, lineno=-1): |
d681d2c3cd8d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
98 tree = parse(expr.source, 'eval') |
116 | 99 xform = ExpressionASTTransformer() |
100 tree = xform.visit(tree) | |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
101 |
116 | 102 if isinstance(filename, unicode): |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
103 # unicode file names not allowed for code objects |
116 | 104 filename = filename.encode('utf-8', 'replace') |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
105 elif not filename: |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
106 filename = '<string>' |
132
dc42cb3c02dc
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
107 tree.filename = filename |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
108 if lineno <= 0: |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
109 lineno = 1 |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
110 |
116 | 111 gen = ExpressionCodeGenerator(tree) |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
112 gen.optimized = True |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
113 code = gen.getCode() |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
114 |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
115 # We'd like to just set co_firstlineno, but it's readonly. So we need to |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
116 # clone the code object while adjusting the line number |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
117 return new.code(0, code.co_nlocals, code.co_stacksize, |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
118 code.co_flags | 0x0040, code.co_code, code.co_consts, |
134
d681d2c3cd8d
* Improve the accuracy of line numbers for text nodes, so that reported errors about syntax or evaluation errors in expressions point to the right line (not quite perfect yet, though).
cmlenz
parents:
133
diff
changeset
|
119 code.co_names, code.co_varnames, filename, repr(expr), |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
120 lineno, code.co_lnotab, (), ()) |
116 | 121 |
132
dc42cb3c02dc
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
122 def _lookup_name(data, name, locals_=None): |
dc42cb3c02dc
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
123 val = None |
dc42cb3c02dc
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
124 if locals_: |
dc42cb3c02dc
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
125 val = locals_.get(name) |
dc42cb3c02dc
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
126 if val is None: |
dc42cb3c02dc
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
127 val = data.get(name) |
116 | 128 if val is None: |
129 val = getattr(__builtin__, name, None) | |
130 return val | |
131 | |
118
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
132 def _lookup_attr(data, obj, key): |
116 | 133 if hasattr(obj, key): |
134 return getattr(obj, key) | |
135 try: | |
136 return obj[key] | |
137 except (KeyError, TypeError): | |
138 return None | |
139 | |
140 def _lookup_item(data, obj, key): | |
141 if len(key) == 1: | |
142 key = key[0] | |
143 try: | |
144 return obj[key] | |
145 except (KeyError, IndexError, TypeError), e: | |
146 if isinstance(key, basestring): | |
147 try: | |
148 return getattr(obj, key) | |
149 except (AttributeError, TypeError), e: | |
150 pass | |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
151 |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
152 class ASTTransformer(object): |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
153 """General purpose base class for AST transformations. |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
154 |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
155 Every visitor method can be overridden to return an AST node that has been |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
156 altered or replaced in some way. |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
157 """ |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
158 _visitors = {} |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
159 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
160 def visit(self, node, *args, **kwargs): |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
161 v = self._visitors.get(node.__class__) |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
162 if not v: |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
163 v = getattr(self, 'visit%s' % node.__class__.__name__) |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
164 self._visitors[node.__class__] = v |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
165 return v(node, *args, **kwargs) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
166 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
167 def visitExpression(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
168 node.node = self.visit(node.node, *args, **kwargs) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
169 return node |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
170 |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
171 # Functions & Accessors |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
172 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
173 def visitCallFunc(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
174 node.node = self.visit(node.node, *args, **kwargs) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
175 node.args = map(lambda x: self.visit(x, *args, **kwargs), node.args) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
176 if node.star_args: |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
177 node.star_args = map(lambda x: self.visit(x, *args, **kwargs), |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
178 node.star_args) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
179 if node.dstar_args: |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
180 node.dstart_args = map(lambda x: self.visit(x, *args, **kwargs), |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
181 node.dstar_args) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
182 return node |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
183 |
118
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
184 def visitLambda(self, node, *args, **kwargs): |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
185 node.code = self.visit(node.code, *args, **kwargs) |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
186 node.filename = '<string>' # workaround for bug in pycodegen |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
187 return node |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
188 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
189 def visitGetattr(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
190 node.expr = self.visit(node.expr, *args, **kwargs) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
191 return node |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
192 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
193 def visitSubscript(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
194 node.expr = self.visit(node.expr, *args, **kwargs) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
195 node.subs = map(lambda x: self.visit(x, *args, **kwargs), node.subs) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
196 return node |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
197 |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
198 # Operators |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
199 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
200 def _visitBoolOp(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
201 node.nodes = map(lambda x: self.visit(x, *args, **kwargs), node.nodes) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
202 return node |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
203 visitAnd = visitOr = visitBitand = visitBitor = _visitBoolOp |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
204 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
205 def _visitBinOp(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
206 node.left = self.visit(node.left, *args, **kwargs) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
207 node.right = self.visit(node.right, *args, **kwargs) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
208 return node |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
209 visitAdd = visitSub = _visitBinOp |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
210 visitDiv = visitFloorDiv = visitMod = visitMul = visitPower = _visitBinOp |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
211 visitLeftShift = visitRightShift = _visitBinOp |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
212 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
213 def visitCompare(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
214 node.expr = self.visit(node.expr, *args, **kwargs) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
215 node.ops = map(lambda (op, n): (op, self.visit(n, *args, **kwargs)), |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
216 node.ops) |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
217 return node |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
218 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
219 def _visitUnaryOp(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
220 node.expr = self.visit(node.expr, *args, **kwargs) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
221 return node |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
222 visitUnaryAdd = visitUnarySub = visitNot = visitInvert = _visitUnaryOp |
98
44af12832c5a
Bugfix in `builder` module: attribute values need to be converted to strings when generating streams.
cmlenz
parents:
90
diff
changeset
|
223 visitBackquote = _visitUnaryOp |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
224 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
225 # Identifiers, Literals and Comprehensions |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
226 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
227 def _visitDefault(self, node, *args, **kwargs): |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
228 return node |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
229 visitAssName = visitAssTuple = _visitDefault |
102 | 230 visitConst = visitName = _visitDefault |
231 | |
232 def visitKeyword(self, node, *args, **kwargs): | |
233 node.expr = self.visit(node.expr, *args, **kwargs) | |
234 return node | |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
235 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
236 def visitDict(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
237 node.items = map(lambda (k, v): (self.visit(k, *args, **kwargs), |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
238 self.visit(v, *args, **kwargs)), |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
239 node.items) |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
240 return node |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
241 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
242 def visitTuple(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
243 node.nodes = map(lambda n: self.visit(n, *args, **kwargs), node.nodes) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
244 return node |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
245 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
246 def visitList(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
247 node.nodes = map(lambda n: self.visit(n, *args, **kwargs), node.nodes) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
248 return node |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
249 |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
250 def visitListComp(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
251 node.expr = self.visit(node.expr, *args, **kwargs) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
252 node.quals = map(lambda x: self.visit(x, *args, **kwargs), node.quals) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
253 return node |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
254 |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
255 def visitListCompFor(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
256 node.assign = self.visit(node.assign, *args, **kwargs) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
257 node.list = self.visit(node.list, *args, **kwargs) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
258 node.ifs = map(lambda x: self.visit(x, *args, **kwargs), node.ifs) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
259 return node |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
260 |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
261 def visitListCompIf(self, node, *args, **kwargs): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
262 node.test = self.visit(node.test, *args, **kwargs) |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
263 return node |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
264 |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
265 |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
266 class ExpressionASTTransformer(ASTTransformer): |
112 | 267 """Concrete AST transformer that implements the AST transformations needed |
268 for template expressions. | |
87
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
269 """ |
1b874f032bde
Fix some problems in expression evaluation by transforming the AST and compiling that to bytecode, instead of generating bytecode directly. Invalidates #13.
cmlenz
parents:
86
diff
changeset
|
270 |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
271 def visitGetattr(self, node, locals_=False): |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
272 return ast.CallFunc(ast.Name('_lookup_attr'), [ |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
273 ast.Name('data'), self.visit(node.expr, locals_=locals_), |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
274 ast.Const(node.attrname) |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
275 ]) |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
276 |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
277 def visitLambda(self, node, locals_=False): |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
278 node.code = self.visit(node.code, locals_=True) |
118
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
279 node.filename = '<string>' # workaround for bug in pycodegen |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
280 return node |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
116
diff
changeset
|
281 |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
282 def visitListComp(self, node, locals_=False): |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
283 node.expr = self.visit(node.expr, locals_=True) |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
284 node.quals = map(lambda x: self.visit(x, locals_=True), node.quals) |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
285 return node |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
286 |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
287 def visitName(self, node, locals_=False): |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
288 func_args = [ast.Name('data'), ast.Const(node.name)] |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
289 if locals_: |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
290 func_args.append(ast.CallFunc(ast.Name('locals'), [])) |
116 | 291 return ast.CallFunc(ast.Name('_lookup_name'), func_args) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
292 |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
293 def visitSubscript(self, node, locals_=False): |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
294 return ast.CallFunc(ast.Name('_lookup_item'), [ |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
295 ast.Name('data'), self.visit(node.expr, locals_=locals_), |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
296 ast.Tuple(map(lambda x: self.visit(x, locals_=locals_), node.subs)) |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
297 ]) |