Mercurial > genshi > genshi-test
annotate markup/tests/eval.py @ 195:a180441acbff
Fix missing import in unit test added in [244].
author | cmlenz |
---|---|
date | Thu, 24 Aug 2006 19:06:23 +0000 |
parents | eb5c8200f49b |
children | d7c0a7d65783 |
rev | line source |
---|---|
1 | 1 # -*- coding: utf-8 -*- |
2 # | |
66
822089ae65ce
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
822089ae65ce
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
822089ae65ce
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
203f459e7e26
* 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
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
20 |
2ee9f28e16e5
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): |
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
22 |
192
cda3bdfc19ed
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): |
cda3bdfc19ed
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'})) |
cda3bdfc19ed
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)) |
cda3bdfc19ed
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'})) |
cda3bdfc19ed
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}, |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
28 nocall=True)) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
29 |
30
2ee9f28e16e5
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): |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
37 |
167
7888f4104cc0
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): |
7888f4104cc0
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'") |
7888f4104cc0
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({})) |
7888f4104cc0
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'") |
7888f4104cc0
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
194025141c89
String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents:
167
diff
changeset
|
43 expr = Expression("'\xc3\xbe'") |
194025141c89
String literals in XPath expressions are assumed to be UTF-8 encoded.
cmlenz
parents:
167
diff
changeset
|
44 self.assertEqual(u'þ', expr.evaluate({})) |
167
7888f4104cc0
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
2ee9f28e16e5
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): |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
53 |
2ee9f28e16e5
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): |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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}, |
2ee9f28e16e5
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})) |
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
58 |
2ee9f28e16e5
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): |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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], |
2ee9f28e16e5
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})) |
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
64 |
2ee9f28e16e5
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): |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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({})) |
2ee9f28e16e5
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,), |
2ee9f28e16e5
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})) |
2ee9f28e16e5
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
cc034182061e
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
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:
81
diff
changeset
|
192 def test_call_function(self): |
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:
81
diff
changeset
|
193 self.assertEqual(42, Expression("foo()").evaluate({'foo': lambda: 42})) |
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:
81
diff
changeset
|
194 data = {'foo': 'bar'} |
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:
81
diff
changeset
|
195 self.assertEqual('BAR', Expression("foo.upper()").evaluate(data)) |
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:
81
diff
changeset
|
196 data = {'foo': {'bar': range(42)}} |
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:
81
diff
changeset
|
197 self.assertEqual(42, Expression("len(foo.bar)").evaluate(data)) |
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:
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 | |
90
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
203 def test_call_function_without_params(self): |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
204 self.assertEqual(42, Expression("foo").evaluate({'foo': lambda: 42})) |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
205 data = {'foo': 'bar'} |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
206 self.assertEqual('BAR', Expression("foo.upper").evaluate(data)) |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
207 data = {'foo': {'bar': range(42)}} |
242610137d1f
When an expression evaluates to a callable, it is called implicitly.
cmlenz
parents:
88
diff
changeset
|
208 |
118
226613431921
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
|
209 def test_lambda(self): |
226613431921
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
|
210 # Define a custom `sorted` function cause the builtin isn't available |
226613431921
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
|
211 # on Python 2.3 |
226613431921
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
|
212 def sorted(items, compfunc): |
226613431921
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
|
213 items.sort(compfunc) |
226613431921
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
|
214 return items |
226613431921
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
|
215 data = {'items': [{'name': 'b', 'value': 0}, {'name': 'a', 'value': 1}], |
226613431921
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
|
216 'sorted': sorted} |
226613431921
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
|
217 expr = Expression("sorted(items, lambda a, b: cmp(a.name, b.name))") |
226613431921
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
|
218 self.assertEqual([{'name': 'a', 'value': 1}, {'name': 'b', 'value': 0}], |
226613431921
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 expr.evaluate(data)) |
226613431921
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 |
88
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
221 def test_list_comprehension(self): |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
222 expr = Expression("[n for n in numbers if n < 2]") |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
223 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)})) |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
224 |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
225 expr = Expression("[(i, n + 1) for i, n in enumerate(numbers)]") |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
226 self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)], |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
227 expr.evaluate({'numbers': range(5)})) |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
228 |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
229 expr = Expression("[offset + n for n in numbers]") |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
230 self.assertEqual([2, 3, 4, 5, 6], |
9ecae580dd93
Add support for list comprehension in expressions (see #12).
cmlenz
parents:
87
diff
changeset
|
231 expr.evaluate({'numbers': range(5), 'offset': 2})) |
81
cc034182061e
Template expressions are now compiled to Python bytecode.
cmlenz
parents:
66
diff
changeset
|
232 |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
233 def test_list_comprehension_with_getattr(self): |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
234 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
235 expr = Expression("[i.name for i in items if i.value > 1]") |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
236 self.assertEqual(['b'], expr.evaluate({'items': items})) |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
237 |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
238 def test_list_comprehension_with_getitem(self): |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
239 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
240 expr = Expression("[i['name'] for i in items if i['value'] > 1]") |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
241 self.assertEqual(['b'], expr.evaluate({'items': items})) |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
242 |
194
eb5c8200f49b
Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents:
192
diff
changeset
|
243 def test_error_access_undefined(self): |
eb5c8200f49b
Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents:
192
diff
changeset
|
244 expr = Expression("nothing", filename='index.html', lineno=50) |
eb5c8200f49b
Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents:
192
diff
changeset
|
245 self.assertEqual(Undefined, type(expr.evaluate({}))) |
eb5c8200f49b
Fix regression introduced in [242]: any reference to an undefined name would result in a `NameError`.
cmlenz
parents:
192
diff
changeset
|
246 |
192
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
247 def test_error_call_undefined(self): |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
248 expr = Expression("nothing()", filename='index.html', lineno=50) |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
249 try: |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
250 expr.evaluate({}) |
192
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
251 self.fail('Expected NameError') |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
252 except NameError, e: |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
253 exc_type, exc_value, exc_traceback = sys.exc_info() |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
254 frame = exc_traceback.tb_next |
192
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
255 frames = [] |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
256 while frame.tb_next: |
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
257 frame = frame.tb_next |
192
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
258 frames.append(frame) |
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:
131
diff
changeset
|
259 self.assertEqual('<Expression "nothing()">', |
192
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
260 frames[-3].tb_frame.f_code.co_name) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
261 self.assertEqual('index.html', |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
262 frames[-3].tb_frame.f_code.co_filename) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
263 self.assertEqual(50, frames[-3].tb_lineno) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
264 |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
265 def test_error_getattr_undefined(self): |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
266 expr = Expression("nothing.nil", filename='index.html', lineno=50) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
267 try: |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
268 expr.evaluate({}) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
269 self.fail('Expected NameError') |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
270 except NameError, e: |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
271 exc_type, exc_value, exc_traceback = sys.exc_info() |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
272 frame = exc_traceback.tb_next |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
273 frames = [] |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
274 while frame.tb_next: |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
275 frame = frame.tb_next |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
276 frames.append(frame) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
277 self.assertEqual('<Expression "nothing.nil">', |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
278 frames[-3].tb_frame.f_code.co_name) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
279 self.assertEqual('index.html', |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
280 frames[-3].tb_frame.f_code.co_filename) |
cda3bdfc19ed
Expression evaluation now differentiates between undefined variables and variables that are defined but set to `None`.
cmlenz
parents:
180
diff
changeset
|
281 self.assertEqual(50, frames[-3].tb_lineno) |
131
203f459e7e26
* Support for line numbers in exceptions in expression evaluation (#22).
cmlenz
parents:
118
diff
changeset
|
282 |
30
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
283 |
1 | 284 def suite(): |
285 suite = unittest.TestSuite() | |
30
2ee9f28e16e5
Experimental support for using the new native AST in Python 2.5 instead of the `compiler` package.
cmlenz
parents:
27
diff
changeset
|
286 suite.addTest(unittest.makeSuite(ExpressionTestCase, 'test')) |
27 | 287 suite.addTest(doctest.DocTestSuite(Expression.__module__)) |
1 | 288 return suite |
289 | |
290 if __name__ == '__main__': | |
291 unittest.main(defaultTest='suite') |