1
# -*- coding: utf-8 -*-
3
# Copyright (C) 2006-2007 Edgewall Software
6
# This software is licensed as described in the file COPYING, which
7
# you should have received as part of this distribution. The terms
8
# are also available at http://genshi.edgewall.org/wiki/License.
10
# This software consists of voluntary contributions made by many
11
# individuals. For the exact contribution history, see the revision
12
# history and logs, available at http://genshi.edgewall.org/log/.
18
from genshi.core import Markup
19
from genshi.template.eval import Expression, Suite, Undefined, UndefinedError, \
23
class ExpressionTestCase(unittest.TestCase):
26
expr = Expression('x,y')
27
self.assertEqual(expr, Expression('x,y'))
28
self.assertNotEqual(expr, Expression('y, x'))
31
expr = Expression('x,y')
32
self.assertEqual(hash(expr), hash(Expression('x,y')))
33
self.assertNotEqual(hash(expr), hash(Expression('y, x')))
35
def test_name_lookup(self):
36
self.assertEqual('bar', Expression('foo').evaluate({'foo': 'bar'}))
37
self.assertEqual(id, Expression('id').evaluate({}))
38
self.assertEqual('bar', Expression('id').evaluate({'id': 'bar'}))
39
self.assertEqual(None, Expression('id').evaluate({'id': None}))
41
def test_builtins(self):
42
expr = Expression('Markup')
43
self.assertEqual(expr.evaluate({}), Markup)
45
def test_str_literal(self):
46
self.assertEqual('foo', Expression('"foo"').evaluate({}))
47
self.assertEqual('foo', Expression('"""foo"""').evaluate({}))
48
self.assertEqual('foo', Expression("'foo'").evaluate({}))
49
self.assertEqual('foo', Expression("'''foo'''").evaluate({}))
50
self.assertEqual('foo', Expression("u'foo'").evaluate({}))
51
self.assertEqual('foo', Expression("r'foo'").evaluate({}))
53
def test_str_literal_non_ascii(self):
54
expr = Expression(u"u'\xfe'")
55
self.assertEqual(u'þ', expr.evaluate({}))
56
expr = Expression("u'\xfe'")
57
self.assertEqual(u'þ', expr.evaluate({}))
58
expr = Expression("'\xc3\xbe'")
59
self.assertEqual(u'þ', expr.evaluate({}))
61
def test_num_literal(self):
62
self.assertEqual(42, Expression("42").evaluate({}))
63
self.assertEqual(42L, Expression("42L").evaluate({}))
64
self.assertEqual(.42, Expression(".42").evaluate({}))
65
self.assertEqual(07, Expression("07").evaluate({}))
66
self.assertEqual(0xF2, Expression("0xF2").evaluate({}))
67
self.assertEqual(0XF2, Expression("0XF2").evaluate({}))
69
def test_dict_literal(self):
70
self.assertEqual({}, Expression("{}").evaluate({}))
71
self.assertEqual({'key': True},
72
Expression("{'key': value}").evaluate({'value': True}))
74
def test_list_literal(self):
75
self.assertEqual([], Expression("[]").evaluate({}))
76
self.assertEqual([1, 2, 3], Expression("[1, 2, 3]").evaluate({}))
77
self.assertEqual([True],
78
Expression("[value]").evaluate({'value': True}))
80
def test_tuple_literal(self):
81
self.assertEqual((), Expression("()").evaluate({}))
82
self.assertEqual((1, 2, 3), Expression("(1, 2, 3)").evaluate({}))
83
self.assertEqual((True,),
84
Expression("(value,)").evaluate({'value': True}))
86
def test_unaryop_pos(self):
87
self.assertEqual(1, Expression("+1").evaluate({}))
88
self.assertEqual(1, Expression("+x").evaluate({'x': 1}))
90
def test_unaryop_neg(self):
91
self.assertEqual(-1, Expression("-1").evaluate({}))
92
self.assertEqual(-1, Expression("-x").evaluate({'x': 1}))
94
def test_unaryop_not(self):
95
self.assertEqual(False, Expression("not True").evaluate({}))
96
self.assertEqual(False, Expression("not x").evaluate({'x': True}))
98
def test_unaryop_inv(self):
99
self.assertEqual(-2, Expression("~1").evaluate({}))
100
self.assertEqual(-2, Expression("~x").evaluate({'x': 1}))
102
def test_binop_add(self):
103
self.assertEqual(3, Expression("2 + 1").evaluate({}))
104
self.assertEqual(3, Expression("x + y").evaluate({'x': 2, 'y': 1}))
106
def test_binop_sub(self):
107
self.assertEqual(1, Expression("2 - 1").evaluate({}))
108
self.assertEqual(1, Expression("x - y").evaluate({'x': 1, 'y': 1}))
110
def test_binop_sub(self):
111
self.assertEqual(1, Expression("2 - 1").evaluate({}))
112
self.assertEqual(1, Expression("x - y").evaluate({'x': 2, 'y': 1}))
114
def test_binop_mul(self):
115
self.assertEqual(4, Expression("2 * 2").evaluate({}))
116
self.assertEqual(4, Expression("x * y").evaluate({'x': 2, 'y': 2}))
118
def test_binop_pow(self):
119
self.assertEqual(4, Expression("2 ** 2").evaluate({}))
120
self.assertEqual(4, Expression("x ** y").evaluate({'x': 2, 'y': 2}))
122
def test_binop_div(self):
123
self.assertEqual(2, Expression("4 / 2").evaluate({}))
124
self.assertEqual(2, Expression("x / y").evaluate({'x': 4, 'y': 2}))
126
def test_binop_floordiv(self):
127
self.assertEqual(1, Expression("3 // 2").evaluate({}))
128
self.assertEqual(1, Expression("x // y").evaluate({'x': 3, 'y': 2}))
130
def test_binop_mod(self):
131
self.assertEqual(1, Expression("3 % 2").evaluate({}))
132
self.assertEqual(1, Expression("x % y").evaluate({'x': 3, 'y': 2}))
134
def test_binop_and(self):
135
self.assertEqual(0, Expression("1 & 0").evaluate({}))
136
self.assertEqual(0, Expression("x & y").evaluate({'x': 1, 'y': 0}))
138
def test_binop_or(self):
139
self.assertEqual(1, Expression("1 | 0").evaluate({}))
140
self.assertEqual(1, Expression("x | y").evaluate({'x': 1, 'y': 0}))
142
def test_binop_xor(self):
143
self.assertEqual(1, Expression("1 ^ 0").evaluate({}))
144
self.assertEqual(1, Expression("x ^ y").evaluate({'x': 1, 'y': 0}))
146
def test_binop_contains(self):
147
self.assertEqual(True, Expression("1 in (1, 2, 3)").evaluate({}))
148
self.assertEqual(True, Expression("x in y").evaluate({'x': 1,
151
def test_binop_not_contains(self):
152
self.assertEqual(True, Expression("4 not in (1, 2, 3)").evaluate({}))
153
self.assertEqual(True, Expression("x not in y").evaluate({'x': 4,
156
def test_binop_is(self):
157
self.assertEqual(True, Expression("1 is 1").evaluate({}))
158
self.assertEqual(True, Expression("x is y").evaluate({'x': 1, 'y': 1}))
159
self.assertEqual(False, Expression("1 is 2").evaluate({}))
160
self.assertEqual(False, Expression("x is y").evaluate({'x': 1, 'y': 2}))
162
def test_binop_is_not(self):
163
self.assertEqual(True, Expression("1 is not 2").evaluate({}))
164
self.assertEqual(True, Expression("x is not y").evaluate({'x': 1,
166
self.assertEqual(False, Expression("1 is not 1").evaluate({}))
167
self.assertEqual(False, Expression("x is not y").evaluate({'x': 1,
170
def test_boolop_and(self):
171
self.assertEqual(False, Expression("True and False").evaluate({}))
172
self.assertEqual(False, Expression("x and y").evaluate({'x': True,
175
def test_boolop_or(self):
176
self.assertEqual(True, Expression("True or False").evaluate({}))
177
self.assertEqual(True, Expression("x or y").evaluate({'x': True,
180
def test_compare_eq(self):
181
self.assertEqual(True, Expression("1 == 1").evaluate({}))
182
self.assertEqual(True, Expression("x == y").evaluate({'x': 1, 'y': 1}))
184
def test_compare_ne(self):
185
self.assertEqual(False, Expression("1 != 1").evaluate({}))
186
self.assertEqual(False, Expression("x != y").evaluate({'x': 1, 'y': 1}))
187
self.assertEqual(False, Expression("1 <> 1").evaluate({}))
188
self.assertEqual(False, Expression("x <> y").evaluate({'x': 1, 'y': 1}))
190
def test_compare_lt(self):
191
self.assertEqual(True, Expression("1 < 2").evaluate({}))
192
self.assertEqual(True, Expression("x < y").evaluate({'x': 1, 'y': 2}))
194
def test_compare_le(self):
195
self.assertEqual(True, Expression("1 <= 1").evaluate({}))
196
self.assertEqual(True, Expression("x <= y").evaluate({'x': 1, 'y': 1}))
198
def test_compare_gt(self):
199
self.assertEqual(True, Expression("2 > 1").evaluate({}))
200
self.assertEqual(True, Expression("x > y").evaluate({'x': 2, 'y': 1}))
202
def test_compare_ge(self):
203
self.assertEqual(True, Expression("1 >= 1").evaluate({}))
204
self.assertEqual(True, Expression("x >= y").evaluate({'x': 1, 'y': 1}))
206
def test_compare_multi(self):
207
self.assertEqual(True, Expression("1 != 3 == 3").evaluate({}))
208
self.assertEqual(True, Expression("x != y == y").evaluate({'x': 1,
211
def test_call_function(self):
212
self.assertEqual(42, Expression("foo()").evaluate({'foo': lambda: 42}))
213
data = {'foo': 'bar'}
214
self.assertEqual('BAR', Expression("foo.upper()").evaluate(data))
215
data = {'foo': {'bar': range(42)}}
216
self.assertEqual(42, Expression("len(foo.bar)").evaluate(data))
218
def test_call_keywords(self):
219
self.assertEqual(42, Expression("foo(x=bar)").evaluate({'foo': lambda x: x,
222
def test_call_star_args(self):
223
self.assertEqual(42, Expression("foo(*bar)").evaluate({'foo': lambda x: x,
226
def test_call_dstar_args(self):
229
expr = Expression("foo(**bar)")
230
self.assertEqual(42, expr.evaluate({'foo': foo, 'bar': {"x": 42}}))
232
def test_lambda(self):
233
# Define a custom `sorted` function cause the builtin isn't available
235
def sorted(items, compfunc):
238
data = {'items': [{'name': 'b', 'value': 0}, {'name': 'a', 'value': 1}],
240
expr = Expression("sorted(items, lambda a, b: cmp(a.name, b.name))")
241
self.assertEqual([{'name': 'a', 'value': 1}, {'name': 'b', 'value': 0}],
244
def test_list_comprehension(self):
245
expr = Expression("[n for n in numbers if n < 2]")
246
self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)}))
248
expr = Expression("[(i, n + 1) for i, n in enumerate(numbers)]")
249
self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)],
250
expr.evaluate({'numbers': range(5)}))
252
expr = Expression("[offset + n for n in numbers]")
253
self.assertEqual([2, 3, 4, 5, 6],
254
expr.evaluate({'numbers': range(5), 'offset': 2}))
256
def test_list_comprehension_with_getattr(self):
257
items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}]
258
expr = Expression("[i.name for i in items if i.value > 1]")
259
self.assertEqual(['b'], expr.evaluate({'items': items}))
261
def test_list_comprehension_with_getitem(self):
262
items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}]
263
expr = Expression("[i['name'] for i in items if i['value'] > 1]")
264
self.assertEqual(['b'], expr.evaluate({'items': items}))
266
if sys.version_info >= (2, 4):
267
# Generator expressions only supported in Python 2.4 and up
269
def test_generator_expression(self):
270
expr = Expression("list(n for n in numbers if n < 2)")
271
self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)}))
273
expr = Expression("list((i, n + 1) for i, n in enumerate(numbers))")
274
self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)],
275
expr.evaluate({'numbers': range(5)}))
277
expr = Expression("list(offset + n for n in numbers)")
278
self.assertEqual([2, 3, 4, 5, 6],
279
expr.evaluate({'numbers': range(5), 'offset': 2}))
281
def test_generator_expression_with_getattr(self):
282
items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}]
283
expr = Expression("list(i.name for i in items if i.value > 1)")
284
self.assertEqual(['b'], expr.evaluate({'items': items}))
286
def test_generator_expression_with_getitem(self):
287
items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}]
288
expr = Expression("list(i['name'] for i in items if i['value'] > 1)")
289
self.assertEqual(['b'], expr.evaluate({'items': items}))
291
if sys.version_info >= (2, 5):
292
def test_conditional_expression(self):
293
expr = Expression("'T' if foo else 'F'")
294
self.assertEqual('T', expr.evaluate({'foo': True}))
295
self.assertEqual('F', expr.evaluate({'foo': False}))
297
def test_slice(self):
298
expr = Expression("numbers[0:2]")
299
self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)}))
301
def test_slice_with_vars(self):
302
expr = Expression("numbers[start:end]")
303
self.assertEqual([0, 1], expr.evaluate({'numbers': range(5), 'start': 0,
306
def test_slice_copy(self):
307
expr = Expression("numbers[:]")
308
self.assertEqual([0, 1, 2, 3, 4], expr.evaluate({'numbers': range(5)}))
310
def test_slice_stride(self):
311
expr = Expression("numbers[::stride]")
312
self.assertEqual([0, 2, 4], expr.evaluate({'numbers': range(5),
315
def test_slice_negative_start(self):
316
expr = Expression("numbers[-1:]")
317
self.assertEqual([4], expr.evaluate({'numbers': range(5)}))
319
def test_slice_negative_end(self):
320
expr = Expression("numbers[:-1]")
321
self.assertEqual([0, 1, 2, 3], expr.evaluate({'numbers': range(5)}))
323
def test_access_undefined(self):
324
expr = Expression("nothing", filename='index.html', lineno=50)
325
retval = expr.evaluate({})
326
assert isinstance(retval, Undefined)
327
self.assertEqual('nothing', retval._name)
328
assert retval._owner is UNDEFINED
330
def test_getattr_undefined(self):
331
class Something(object):
334
something = Something()
335
expr = Expression('something.nil', filename='index.html', lineno=50)
336
retval = expr.evaluate({'something': something})
337
assert isinstance(retval, Undefined)
338
self.assertEqual('nil', retval._name)
339
assert retval._owner is something
341
def test_getitem_undefined_string(self):
342
class Something(object):
345
something = Something()
346
expr = Expression('something["nil"]', filename='index.html', lineno=50)
347
retval = expr.evaluate({'something': something})
348
assert isinstance(retval, Undefined)
349
self.assertEqual('nil', retval._name)
350
assert retval._owner is something
352
def test_error_access_undefined(self):
353
expr = Expression("nothing", filename='index.html', lineno=50,
357
self.fail('Expected UndefinedError')
358
except UndefinedError, e:
359
exc_type, exc_value, exc_traceback = sys.exc_info()
360
frame = exc_traceback.tb_next
363
frame = frame.tb_next
365
self.assertEqual('"nothing" not defined', str(e))
366
self.assertEqual("<Expression 'nothing'>",
367
frames[-3].tb_frame.f_code.co_name)
368
self.assertEqual('index.html',
369
frames[-3].tb_frame.f_code.co_filename)
370
self.assertEqual(50, frames[-3].tb_lineno)
372
def test_error_getattr_undefined(self):
373
class Something(object):
376
expr = Expression('something.nil', filename='index.html', lineno=50,
379
expr.evaluate({'something': Something()})
380
self.fail('Expected UndefinedError')
381
except UndefinedError, e:
382
exc_type, exc_value, exc_traceback = sys.exc_info()
383
frame = exc_traceback.tb_next
386
frame = frame.tb_next
388
self.assertEqual('<Something> has no member named "nil"', str(e))
389
self.assertEqual("<Expression 'something.nil'>",
390
frames[-3].tb_frame.f_code.co_name)
391
self.assertEqual('index.html',
392
frames[-3].tb_frame.f_code.co_filename)
393
self.assertEqual(50, frames[-3].tb_lineno)
395
def test_error_getitem_undefined_string(self):
396
class Something(object):
399
expr = Expression('something["nil"]', filename='index.html', lineno=50,
402
expr.evaluate({'something': Something()})
403
self.fail('Expected UndefinedError')
404
except UndefinedError, e:
405
exc_type, exc_value, exc_traceback = sys.exc_info()
406
frame = exc_traceback.tb_next
409
frame = frame.tb_next
411
self.assertEqual('<Something> has no member named "nil"', str(e))
412
self.assertEqual('''<Expression 'something["nil"]'>''',
413
frames[-3].tb_frame.f_code.co_name)
414
self.assertEqual('index.html',
415
frames[-3].tb_frame.f_code.co_filename)
416
self.assertEqual(50, frames[-3].tb_lineno)
419
class SuiteTestCase(unittest.TestCase):
421
def test_assign(self):
422
suite = Suite("foo = 42")
425
self.assertEqual(42, data['foo'])
428
suite = Suite("def donothing(): pass")
431
assert 'donothing' in data
432
self.assertEqual(None, data['donothing']())
434
def test_delete(self):
435
suite = Suite("""foo = 42
440
assert 'foo' not in data
442
def test_class(self):
443
suite = Suite("class plain(object): pass")
446
assert 'plain' in data
448
def test_import(self):
449
suite = Suite("from itertools import ifilter")
452
assert 'ifilter' in data
455
suite = Suite("""x = []
461
self.assertEqual([0, 1, 4], data['x'])
464
suite = Suite("""if foo == 42:
469
self.assertEqual(True, data['x'])
471
def test_raise(self):
472
suite = Suite("""raise NotImplementedError""")
473
self.assertRaises(NotImplementedError, suite.execute, {})
475
def test_try_except(self):
476
suite = Suite("""try:
484
self.assertEqual(None, data['somemod'])
486
def test_finally(self):
487
suite = Suite("""try:
494
self.assertEqual(None, data['x'])
496
def test_while_break(self):
497
suite = Suite("""x = 0
505
self.assertEqual(4, data['x'])
509
suite = unittest.TestSuite()
510
suite.addTest(doctest.DocTestSuite(Expression.__module__))
511
suite.addTest(unittest.makeSuite(ExpressionTestCase, 'test'))
512
suite.addTest(unittest.makeSuite(SuiteTestCase, 'test'))
515
if __name__ == '__main__':
516
unittest.main(defaultTest='suite')