Mercurial > genshi > genshi-test
annotate markup/eval.py @ 134:df44110ca91d
* 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 | b9a0031d4bbb |
children | d91e1e822969 |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
66
822089ae65ce
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
822089ae65ce
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
822089ae65ce
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
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
203f459e7e26
* 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
cc034182061e
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
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
67 __slots__ = ['source', 'code'] |
1 | 68 |
81
cc034182061e
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
df44110ca91d
* 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
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
81 """Evaluate the expression against the given data dictionary. |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
82 |
cc034182061e
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
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
86 @return: the result of the evaluation |
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
87 """ |
118
226613431921
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, |
226613431921
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, |
226613431921
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, |
226613431921
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
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
93 retval = retval() |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
94 return retval |
30
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
95 |
87
c6f07b7cd3ea
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
df44110ca91d
* 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): |
df44110ca91d
* 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
c6f07b7cd3ea
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
203f459e7e26
* 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
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
105 elif not filename: |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
106 filename = '<string>' |
132
0288dedce3d2
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
107 tree.filename = filename |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
108 if lineno <= 0: |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
109 lineno = 1 |
87
c6f07b7cd3ea
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
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
112 gen.optimized = True |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
113 code = gen.getCode() |
87
c6f07b7cd3ea
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
203f459e7e26
* 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 |
203f459e7e26
* 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 |
203f459e7e26
* 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, |
203f459e7e26
* 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
df44110ca91d
* 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
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
120 lineno, code.co_lnotab, (), ()) |
116 | 121 |
132
0288dedce3d2
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
122 def _lookup_name(data, name, locals_=None): |
0288dedce3d2
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
123 val = None |
0288dedce3d2
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
124 if locals_: |
0288dedce3d2
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
125 val = locals_.get(name) |
0288dedce3d2
Name lookup in expressions: try locals first, then the context.
cmlenz
parents:
131
diff
changeset
|
126 if val is None: |
0288dedce3d2
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
226613431921
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
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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): |
c6f07b7cd3ea
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. |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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. |
c6f07b7cd3ea
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 """ |
c6f07b7cd3ea
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 = {} |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
160 def visit(self, node, *args, **kwargs): |
87
c6f07b7cd3ea
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__) |
c6f07b7cd3ea
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: |
c6f07b7cd3ea
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__) |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
165 return v(node, *args, **kwargs) |
87
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
167 def visitExpression(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
168 node.node = self.visit(node.node, *args, **kwargs) |
87
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
173 def visitCallFunc(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
174 node.node = self.visit(node.node, *args, **kwargs) |
9ecae580dd93
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
c6f07b7cd3ea
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
9ecae580dd93
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), |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
178 node.star_args) |
87
c6f07b7cd3ea
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
9ecae580dd93
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), |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
181 node.dstar_args) |
87
c6f07b7cd3ea
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
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
183 |
118
226613431921
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): |
226613431921
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) |
226613431921
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 |
226613431921
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 |
226613431921
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
189 def visitGetattr(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
190 node.expr = self.visit(node.expr, *args, **kwargs) |
87
c6f07b7cd3ea
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
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
192 |
88
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
193 def visitSubscript(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
194 node.expr = self.visit(node.expr, *args, **kwargs) |
9ecae580dd93
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
c6f07b7cd3ea
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
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
197 |
87
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
200 def _visitBoolOp(self, node, *args, **kwargs): |
9ecae580dd93
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
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
205 def _visitBinOp(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
206 node.left = self.visit(node.left, *args, **kwargs) |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
207 node.right = self.visit(node.right, *args, **kwargs) |
87
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
213 def visitCompare(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
214 node.expr = self.visit(node.expr, *args, **kwargs) |
9ecae580dd93
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
c6f07b7cd3ea
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) |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
219 def _visitUnaryOp(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
220 node.expr = self.visit(node.expr, *args, **kwargs) |
87
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
bc73d3ab823f
Bugfix in `builder` module: attribute values need to be converted to strings when generating streams.
cmlenz
parents:
90
diff
changeset
|
223 visitBackquote = _visitUnaryOp |
87
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
225 # Identifiers, Literals and Comprehensions |
87
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
227 def _visitDefault(self, node, *args, **kwargs): |
87
c6f07b7cd3ea
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
9ecae580dd93
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
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
236 def visitDict(self, node, *args, **kwargs): |
9ecae580dd93
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), |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
238 self.visit(v, *args, **kwargs)), |
87
c6f07b7cd3ea
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) |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
242 def visitTuple(self, node, *args, **kwargs): |
9ecae580dd93
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
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
246 def visitList(self, node, *args, **kwargs): |
9ecae580dd93
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) |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
248 return node |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
249 |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
250 def visitListComp(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
251 node.expr = self.visit(node.expr, *args, **kwargs) |
9ecae580dd93
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) |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
253 return node |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
254 |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
255 def visitListCompFor(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
256 node.assign = self.visit(node.assign, *args, **kwargs) |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
257 node.list = self.visit(node.list, *args, **kwargs) |
9ecae580dd93
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) |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
259 return node |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
260 |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
261 def visitListCompIf(self, node, *args, **kwargs): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
262 node.test = self.visit(node.test, *args, **kwargs) |
87
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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 |
c6f07b7cd3ea
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
c6f07b7cd3ea
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 """ |
c6f07b7cd3ea
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
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
271 def visitGetattr(self, node, locals_=False): |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
272 return ast.CallFunc(ast.Name('_lookup_attr'), [ |
203f459e7e26
* 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_), |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
274 ast.Const(node.attrname) |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
275 ]) |
30
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
276 |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
277 def visitLambda(self, node, locals_=False): |
203f459e7e26
* 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
226613431921
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 |
226613431921
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 |
226613431921
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
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
282 def visitListComp(self, node, locals_=False): |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
283 node.expr = self.visit(node.expr, locals_=True) |
203f459e7e26
* 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
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
285 return node |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
286 |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
287 def visitName(self, node, locals_=False): |
88
9ecae580dd93
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
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
289 if locals_: |
88
9ecae580dd93
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
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
69
diff
changeset
|
292 |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
293 def visitSubscript(self, node, locals_=False): |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
294 return ast.CallFunc(ast.Name('_lookup_item'), [ |
203f459e7e26
* 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_), |
203f459e7e26
* 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)) |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
120
diff
changeset
|
297 ]) |