Mercurial > genshi > mirror
annotate markup/tests/eval.py @ 227:96a7e5011c69 trunk
Use list comprehension instead of `map()` in the AST transformer.
author | cmlenz |
---|---|
date | Fri, 08 Sep 2006 09:17:42 +0000 |
parents | 16d7b5db7ef4 |
children |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
66
59eb24184e9c
Switch copyright to Edgewall and URLs to markup.edgewall.org.
cmlenz
parents:
32
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:
32
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:
32
diff
changeset
|
12 # history and logs, available at hhttp://markup.edgewall.org/log/. |
1 | 13 |
14 import doctest | |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
15 import sys |
1 | 16 import unittest |
17 | |
195 | 18 from markup.eval import Expression, Undefined |
1 | 19 |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
20 |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
21 class ExpressionTestCase(unittest.TestCase): |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
22 |
192
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
23 def test_name_lookup(self): |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
24 self.assertEqual('bar', Expression('foo').evaluate({'foo': 'bar'})) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
25 self.assertEqual(id, Expression('id').evaluate({}, nocall=True)) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
26 self.assertEqual('bar', Expression('id').evaluate({'id': 'bar'})) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
27 self.assertEqual(None, Expression('id').evaluate({'id': None}, |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
28 nocall=True)) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
29 |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
30 def test_str_literal(self): |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
31 self.assertEqual('foo', Expression('"foo"').evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
32 self.assertEqual('foo', Expression('"""foo"""').evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
33 self.assertEqual('foo', Expression("'foo'").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
34 self.assertEqual('foo', Expression("'''foo'''").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
35 self.assertEqual('foo', Expression("u'foo'").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
36 self.assertEqual('foo', Expression("r'foo'").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
37 |
167
1999291f7a30
Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents:
134
diff
changeset
|
38 def test_str_literal_non_ascii(self): |
1999291f7a30
Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents:
134
diff
changeset
|
39 expr = Expression(u"u'\xfe'") |
1999291f7a30
Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents:
134
diff
changeset
|
40 self.assertEqual(u'þ', expr.evaluate({})) |
1999291f7a30
Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents:
134
diff
changeset
|
41 expr = Expression("u'\xfe'") |
1999291f7a30
Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents:
134
diff
changeset
|
42 self.assertEqual(u'þ', expr.evaluate({})) |
180
061491fb4ea8
String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents:
167
diff
changeset
|
43 expr = Expression("'\xc3\xbe'") |
061491fb4ea8
String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents:
167
diff
changeset
|
44 self.assertEqual(u'þ', expr.evaluate({})) |
167
1999291f7a30
Handle non-ASCII characters in expressions. Closes #29. Thanks to Arnar Birgisson for reporting the problem and comping up with a patch!
cmlenz
parents:
134
diff
changeset
|
45 |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
46 def test_num_literal(self): |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
47 self.assertEqual(42, Expression("42").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
48 self.assertEqual(42L, Expression("42L").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
49 self.assertEqual(.42, Expression(".42").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
50 self.assertEqual(07, Expression("07").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
51 self.assertEqual(0xF2, Expression("0xF2").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
52 self.assertEqual(0XF2, Expression("0XF2").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
53 |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
54 def test_dict_literal(self): |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
55 self.assertEqual({}, Expression("{}").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
56 self.assertEqual({'key': True}, |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
57 Expression("{'key': value}").evaluate({'value': True})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
58 |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
59 def test_list_literal(self): |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
60 self.assertEqual([], Expression("[]").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
61 self.assertEqual([1, 2, 3], Expression("[1, 2, 3]").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
62 self.assertEqual([True], |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
63 Expression("[value]").evaluate({'value': True})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
64 |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
65 def test_tuple_literal(self): |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
66 self.assertEqual((), Expression("()").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
67 self.assertEqual((1, 2, 3), Expression("(1, 2, 3)").evaluate({})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
68 self.assertEqual((True,), |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
69 Expression("(value,)").evaluate({'value': True})) |
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
70 |
31 | 71 def test_unaryop_pos(self): |
72 self.assertEqual(1, Expression("+1").evaluate({})) | |
73 self.assertEqual(1, Expression("+x").evaluate({'x': 1})) | |
74 | |
75 def test_unaryop_neg(self): | |
76 self.assertEqual(-1, Expression("-1").evaluate({})) | |
77 self.assertEqual(-1, Expression("-x").evaluate({'x': 1})) | |
78 | |
79 def test_unaryop_not(self): | |
80 self.assertEqual(False, Expression("not True").evaluate({})) | |
81 self.assertEqual(False, Expression("not x").evaluate({'x': True})) | |
82 | |
32 | 83 def test_unaryop_inv(self): |
84 self.assertEqual(-2, Expression("~1").evaluate({})) | |
85 self.assertEqual(-2, Expression("~x").evaluate({'x': 1})) | |
86 | |
31 | 87 def test_binop_add(self): |
88 self.assertEqual(3, Expression("2 + 1").evaluate({})) | |
89 self.assertEqual(3, Expression("x + y").evaluate({'x': 2, 'y': 1})) | |
90 | |
91 def test_binop_sub(self): | |
92 self.assertEqual(1, Expression("2 - 1").evaluate({})) | |
93 self.assertEqual(1, Expression("x - y").evaluate({'x': 1, 'y': 1})) | |
94 | |
95 def test_binop_sub(self): | |
96 self.assertEqual(1, Expression("2 - 1").evaluate({})) | |
97 self.assertEqual(1, Expression("x - y").evaluate({'x': 2, 'y': 1})) | |
98 | |
99 def test_binop_mul(self): | |
100 self.assertEqual(4, Expression("2 * 2").evaluate({})) | |
101 self.assertEqual(4, Expression("x * y").evaluate({'x': 2, 'y': 2})) | |
102 | |
103 def test_binop_pow(self): | |
104 self.assertEqual(4, Expression("2 ** 2").evaluate({})) | |
105 self.assertEqual(4, Expression("x ** y").evaluate({'x': 2, 'y': 2})) | |
106 | |
107 def test_binop_div(self): | |
108 self.assertEqual(2, Expression("4 / 2").evaluate({})) | |
109 self.assertEqual(2, Expression("x / y").evaluate({'x': 4, 'y': 2})) | |
110 | |
111 def test_binop_floordiv(self): | |
112 self.assertEqual(1, Expression("3 // 2").evaluate({})) | |
113 self.assertEqual(1, Expression("x // y").evaluate({'x': 3, 'y': 2})) | |
114 | |
115 def test_binop_mod(self): | |
116 self.assertEqual(1, Expression("3 % 2").evaluate({})) | |
117 self.assertEqual(1, Expression("x % y").evaluate({'x': 3, 'y': 2})) | |
118 | |
32 | 119 def test_binop_and(self): |
120 self.assertEqual(0, Expression("1 & 0").evaluate({})) | |
121 self.assertEqual(0, Expression("x & y").evaluate({'x': 1, 'y': 0})) | |
122 | |
123 def test_binop_or(self): | |
124 self.assertEqual(1, Expression("1 | 0").evaluate({})) | |
125 self.assertEqual(1, Expression("x | y").evaluate({'x': 1, 'y': 0})) | |
126 | |
31 | 127 def test_binop_contains(self): |
128 self.assertEqual(True, Expression("1 in (1, 2, 3)").evaluate({})) | |
129 self.assertEqual(True, Expression("x in y").evaluate({'x': 1, | |
130 'y': (1, 2, 3)})) | |
131 | |
132 def test_binop_not_contains(self): | |
133 self.assertEqual(True, Expression("4 not in (1, 2, 3)").evaluate({})) | |
134 self.assertEqual(True, Expression("x not in y").evaluate({'x': 4, | |
135 'y': (1, 2, 3)})) | |
136 | |
32 | 137 def test_binop_is(self): |
138 self.assertEqual(True, Expression("1 is 1").evaluate({})) | |
139 self.assertEqual(True, Expression("x is y").evaluate({'x': 1, 'y': 1})) | |
140 self.assertEqual(False, Expression("1 is 2").evaluate({})) | |
141 self.assertEqual(False, Expression("x is y").evaluate({'x': 1, 'y': 2})) | |
142 | |
143 def test_binop_is_not(self): | |
144 self.assertEqual(True, Expression("1 is not 2").evaluate({})) | |
145 self.assertEqual(True, Expression("x is not y").evaluate({'x': 1, | |
146 'y': 2})) | |
147 self.assertEqual(False, Expression("1 is not 1").evaluate({})) | |
148 self.assertEqual(False, Expression("x is not y").evaluate({'x': 1, | |
149 'y': 1})) | |
150 | |
31 | 151 def test_boolop_and(self): |
152 self.assertEqual(False, Expression("True and False").evaluate({})) | |
153 self.assertEqual(False, Expression("x and y").evaluate({'x': True, | |
154 'y': False})) | |
155 | |
156 def test_boolop_or(self): | |
157 self.assertEqual(True, Expression("True or False").evaluate({})) | |
158 self.assertEqual(True, Expression("x or y").evaluate({'x': True, | |
159 'y': False})) | |
160 | |
161 def test_compare_eq(self): | |
162 self.assertEqual(True, Expression("1 == 1").evaluate({})) | |
163 self.assertEqual(True, Expression("x == y").evaluate({'x': 1, 'y': 1})) | |
164 | |
165 def test_compare_ne(self): | |
166 self.assertEqual(False, Expression("1 != 1").evaluate({})) | |
167 self.assertEqual(False, Expression("x != y").evaluate({'x': 1, 'y': 1})) | |
32 | 168 self.assertEqual(False, Expression("1 <> 1").evaluate({})) |
169 self.assertEqual(False, Expression("x <> y").evaluate({'x': 1, 'y': 1})) | |
31 | 170 |
171 def test_compare_lt(self): | |
172 self.assertEqual(True, Expression("1 < 2").evaluate({})) | |
173 self.assertEqual(True, Expression("x < y").evaluate({'x': 1, 'y': 2})) | |
174 | |
175 def test_compare_le(self): | |
176 self.assertEqual(True, Expression("1 <= 1").evaluate({})) | |
177 self.assertEqual(True, Expression("x <= y").evaluate({'x': 1, 'y': 1})) | |
178 | |
179 def test_compare_gt(self): | |
180 self.assertEqual(True, Expression("2 > 1").evaluate({})) | |
181 self.assertEqual(True, Expression("x > y").evaluate({'x': 2, 'y': 1})) | |
182 | |
183 def test_compare_ge(self): | |
184 self.assertEqual(True, Expression("1 >= 1").evaluate({})) | |
185 self.assertEqual(True, Expression("x >= y").evaluate({'x': 1, 'y': 1})) | |
186 | |
187 def test_compare_multi(self): | |
188 self.assertEqual(True, Expression("1 != 3 == 3").evaluate({})) | |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
66
diff
changeset
|
189 self.assertEqual(True, Expression("x != y == y").evaluate({'x': 1, |
31 | 190 'y': 3})) |
191 | |
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:
81
diff
changeset
|
192 def test_call_function(self): |
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:
81
diff
changeset
|
193 self.assertEqual(42, Expression("foo()").evaluate({'foo': lambda: 42})) |
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:
81
diff
changeset
|
194 data = {'foo': 'bar'} |
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:
81
diff
changeset
|
195 self.assertEqual('BAR', Expression("foo.upper()").evaluate(data)) |
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:
81
diff
changeset
|
196 data = {'foo': {'bar': range(42)}} |
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:
81
diff
changeset
|
197 self.assertEqual(42, Expression("len(foo.bar)").evaluate(data)) |
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:
81
diff
changeset
|
198 |
102 | 199 def test_call_keywords(self): |
200 self.assertEqual(42, Expression("foo(x=bar)").evaluate({'foo': lambda x: x, | |
201 'bar': 42})) | |
202 | |
203
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
203 def test_call_star_args(self): |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
204 self.assertEqual(42, Expression("foo(*bar)").evaluate({'foo': lambda x: x, |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
205 'bar': [42]})) |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
206 |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
207 def test_call_dstar_args(self): |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
208 def foo(x): |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
209 return x |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
210 self.assertEqual(42, Expression("foo(**bar)").evaluate({'foo': foo, |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
211 'bar': {"x": 42}})) |
48fab34e5e4d
Fix for handling function calls with star/dstar arguments in expressions. Closes #42. Many thanks to David Fraser for reporting the problem and providing a patch!
cmlenz
parents:
198
diff
changeset
|
212 |
90
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
213 def test_call_function_without_params(self): |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
214 self.assertEqual(42, Expression("foo").evaluate({'foo': lambda: 42})) |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
215 data = {'foo': 'bar'} |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
216 self.assertEqual('BAR', Expression("foo.upper").evaluate(data)) |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
217 data = {'foo': {'bar': range(42)}} |
c835e81c50af
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
218 |
118
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
219 def test_lambda(self): |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
220 # Define a custom `sorted` function cause the builtin isn't available |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
221 # on Python 2.3 |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
222 def sorted(items, compfunc): |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
223 items.sort(compfunc) |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
224 return items |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
225 data = {'items': [{'name': 'b', 'value': 0}, {'name': 'a', 'value': 1}], |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
226 'sorted': sorted} |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
227 expr = Expression("sorted(items, lambda a, b: cmp(a.name, b.name))") |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
228 self.assertEqual([{'name': 'a', 'value': 1}, {'name': 'b', 'value': 0}], |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
229 expr.evaluate(data)) |
c392d38694d9
Add basic support for using `lambda`s in expressions. Closes #21. (Not sure about default arguments, need a test case).
cmlenz
parents:
102
diff
changeset
|
230 |
88
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
231 def test_list_comprehension(self): |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
232 expr = Expression("[n for n in numbers if n < 2]") |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
233 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)})) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
234 |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
235 expr = Expression("[(i, n + 1) for i, n in enumerate(numbers)]") |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
236 self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)], |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
237 expr.evaluate({'numbers': range(5)})) |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
238 |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
239 expr = Expression("[offset + n for n in numbers]") |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
240 self.assertEqual([2, 3, 4, 5, 6], |
628ba9ed39ef
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
241 expr.evaluate({'numbers': range(5), 'offset': 2})) |
81
d60486018004
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
66
diff
changeset
|
242 |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
243 def test_list_comprehension_with_getattr(self): |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
244 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
245 expr = Expression("[i.name for i in items if i.value > 1]") |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
246 self.assertEqual(['b'], expr.evaluate({'items': items})) |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
247 |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
248 def test_list_comprehension_with_getitem(self): |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
249 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
250 expr = Expression("[i['name'] for i in items if i['value'] > 1]") |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
251 self.assertEqual(['b'], expr.evaluate({'items': items})) |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
252 |
198
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
253 if sys.version_info >= (2, 4): |
227
96a7e5011c69
Use list comprehension instead of `map()` in the AST transformer.
cmlenz
parents:
225
diff
changeset
|
254 # Generator expressions only supported in Python 2.4 and up |
96a7e5011c69
Use list comprehension instead of `map()` in the AST transformer.
cmlenz
parents:
225
diff
changeset
|
255 |
198
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
256 def test_generator_expression(self): |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
257 expr = Expression("list(n for n in numbers if n < 2)") |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
258 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)})) |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
259 |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
260 expr = Expression("list((i, n + 1) for i, n in enumerate(numbers))") |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
261 self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)], |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
262 expr.evaluate({'numbers': range(5)})) |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
263 |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
264 expr = Expression("list(offset + n for n in numbers)") |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
265 self.assertEqual([2, 3, 4, 5, 6], |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
266 expr.evaluate({'numbers': range(5), 'offset': 2})) |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
267 |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
268 def test_generator_expression_with_getattr(self): |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
269 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
270 expr = Expression("list(i.name for i in items if i.value > 1)") |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
271 self.assertEqual(['b'], expr.evaluate({'items': items})) |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
272 |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
273 def test_generator_expression_with_getitem(self): |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
274 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
275 expr = Expression("list(i['name'] for i in items if i['value'] > 1)") |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
276 self.assertEqual(['b'], expr.evaluate({'items': items})) |
ca8a9defe6fe
Implemented support for generator expressions (fixes #16)
mgood
parents:
195
diff
changeset
|
277 |
225 | 278 def test_slice(self): |
279 expr = Expression("numbers[0:2]") | |
280 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)})) | |
281 | |
282 def test_slice_with_vars(self): | |
283 expr = Expression("numbers[start:end]") | |
284 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5), 'start': 0, 'end': 2})) | |
285 | |
286 def test_slice_copy(self): | |
287 expr = Expression("numbers[:]") | |
288 self.assertEqual([0, 1, 2, 3, 4], expr.evaluate({'numbers': range(5)})) | |
289 | |
290 def test_slice_stride(self): | |
291 expr = Expression("numbers[::stride]") | |
292 self.assertEqual([0, 2, 4], expr.evaluate({'numbers': range(5), 'stride': 2})) | |
293 | |
294 def test_slice_negative_start(self): | |
295 expr = Expression("numbers[-1:]") | |
296 self.assertEqual([4], expr.evaluate({'numbers': range(5)})) | |
297 | |
298 def test_slice_negative_end(self): | |
299 expr = Expression("numbers[:-1]") | |
300 self.assertEqual([0, 1, 2, 3], expr.evaluate({'numbers': range(5)})) | |
301 | |
194
c0535b218466
Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents:
192
diff
changeset
|
302 def test_error_access_undefined(self): |
c0535b218466
Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents:
192
diff
changeset
|
303 expr = Expression("nothing", filename='index.html', lineno=50) |
c0535b218466
Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents:
192
diff
changeset
|
304 self.assertEqual(Undefined, type(expr.evaluate({}))) |
c0535b218466
Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents:
192
diff
changeset
|
305 |
192
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
306 def test_error_call_undefined(self): |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
307 expr = Expression("nothing()", filename='index.html', lineno=50) |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
308 try: |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
309 expr.evaluate({}) |
192
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
310 self.fail('Expected NameError') |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
311 except NameError, e: |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
312 exc_type, exc_value, exc_traceback = sys.exc_info() |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
313 frame = exc_traceback.tb_next |
192
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
314 frames = [] |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
315 while frame.tb_next: |
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
316 frame = frame.tb_next |
192
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
317 frames.append(frame) |
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:
131
diff
changeset
|
318 self.assertEqual('<Expression "nothing()">', |
192
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
319 frames[-3].tb_frame.f_code.co_name) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
320 self.assertEqual('index.html', |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
321 frames[-3].tb_frame.f_code.co_filename) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
322 self.assertEqual(50, frames[-3].tb_lineno) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
323 |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
324 def test_error_getattr_undefined(self): |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
325 expr = Expression("nothing.nil", filename='index.html', lineno=50) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
326 try: |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
327 expr.evaluate({}) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
328 self.fail('Expected NameError') |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
329 except NameError, e: |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
330 exc_type, exc_value, exc_traceback = sys.exc_info() |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
331 frame = exc_traceback.tb_next |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
332 frames = [] |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
333 while frame.tb_next: |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
334 frame = frame.tb_next |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
335 frames.append(frame) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
336 self.assertEqual('<Expression "nothing.nil">', |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
337 frames[-3].tb_frame.f_code.co_name) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
338 self.assertEqual('index.html', |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
339 frames[-3].tb_frame.f_code.co_filename) |
b64e36bc1100
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
340 self.assertEqual(50, frames[-3].tb_lineno) |
131
2ad83f1d337c
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
341 |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
342 |
1 | 343 def suite(): |
344 suite = unittest.TestSuite() | |
30
bcdbb7e5e4e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
345 suite.addTest(unittest.makeSuite(ExpressionTestCase, 'test')) |
27 | 346 suite.addTest(doctest.DocTestSuite(Expression.__module__)) |
1 | 347 return suite |
348 | |
349 if __name__ == '__main__': | |
350 unittest.main(defaultTest='suite') |