Mercurial > genshi > mirror
comparison genshi/template/tests/eval.py @ 820:9755836bb396 experimental-inline
Sync (old) experimental inline branch with trunk@1027.
author | cmlenz |
---|---|
date | Wed, 11 Mar 2009 17:51:06 +0000 |
parents | 3eb30e4ece8c |
children | fe25855324dd |
comparison
equal
deleted
inserted
replaced
500:3eb30e4ece8c | 820:9755836bb396 |
---|---|
1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
2 # | 2 # |
3 # Copyright (C) 2006-2007 Edgewall Software | 3 # Copyright (C) 2006-2008 Edgewall Software |
4 # All rights reserved. | 4 # All rights reserved. |
5 # | 5 # |
6 # This software is licensed as described in the file COPYING, which | 6 # This software is licensed as described in the file COPYING, which |
7 # you should have received as part of this distribution. The terms | 7 # you should have received as part of this distribution. The terms |
8 # are also available at http://genshi.edgewall.org/wiki/License. | 8 # are also available at http://genshi.edgewall.org/wiki/License. |
10 # This software consists of voluntary contributions made by many | 10 # This software consists of voluntary contributions made by many |
11 # individuals. For the exact contribution history, see the revision | 11 # individuals. For the exact contribution history, see the revision |
12 # history and logs, available at http://genshi.edgewall.org/log/. | 12 # history and logs, available at http://genshi.edgewall.org/log/. |
13 | 13 |
14 import doctest | 14 import doctest |
15 import pickle | |
16 from StringIO import StringIO | |
15 import sys | 17 import sys |
16 import unittest | 18 import unittest |
17 | 19 |
18 from genshi.core import Markup | 20 from genshi.core import Markup |
21 from genshi.template.base import Context | |
19 from genshi.template.eval import Expression, Suite, Undefined, UndefinedError, \ | 22 from genshi.template.eval import Expression, Suite, Undefined, UndefinedError, \ |
20 UNDEFINED | 23 UNDEFINED |
21 | 24 |
22 | 25 |
23 class ExpressionTestCase(unittest.TestCase): | 26 class ExpressionTestCase(unittest.TestCase): |
29 | 32 |
30 def test_hash(self): | 33 def test_hash(self): |
31 expr = Expression('x,y') | 34 expr = Expression('x,y') |
32 self.assertEqual(hash(expr), hash(Expression('x,y'))) | 35 self.assertEqual(hash(expr), hash(Expression('x,y'))) |
33 self.assertNotEqual(hash(expr), hash(Expression('y, x'))) | 36 self.assertNotEqual(hash(expr), hash(Expression('y, x'))) |
37 | |
38 def test_pickle(self): | |
39 expr = Expression('1 < 2') | |
40 buf = StringIO() | |
41 pickle.dump(expr, buf, 2) | |
42 buf.seek(0) | |
43 unpickled = pickle.load(buf) | |
44 assert unpickled.evaluate({}) is True | |
34 | 45 |
35 def test_name_lookup(self): | 46 def test_name_lookup(self): |
36 self.assertEqual('bar', Expression('foo').evaluate({'foo': 'bar'})) | 47 self.assertEqual('bar', Expression('foo').evaluate({'foo': 'bar'})) |
37 self.assertEqual(id, Expression('id').evaluate({})) | 48 self.assertEqual(id, Expression('id').evaluate({})) |
38 self.assertEqual('bar', Expression('id').evaluate({'id': 'bar'})) | 49 self.assertEqual('bar', Expression('id').evaluate({'id': 'bar'})) |
182 self.assertEqual(True, Expression("x == y").evaluate({'x': 1, 'y': 1})) | 193 self.assertEqual(True, Expression("x == y").evaluate({'x': 1, 'y': 1})) |
183 | 194 |
184 def test_compare_ne(self): | 195 def test_compare_ne(self): |
185 self.assertEqual(False, Expression("1 != 1").evaluate({})) | 196 self.assertEqual(False, Expression("1 != 1").evaluate({})) |
186 self.assertEqual(False, Expression("x != y").evaluate({'x': 1, 'y': 1})) | 197 self.assertEqual(False, Expression("x != y").evaluate({'x': 1, 'y': 1})) |
187 self.assertEqual(False, Expression("1 <> 1").evaluate({})) | 198 if sys.version < '3': |
188 self.assertEqual(False, Expression("x <> y").evaluate({'x': 1, 'y': 1})) | 199 self.assertEqual(False, Expression("1 <> 1").evaluate({})) |
200 self.assertEqual(False, Expression("x <> y").evaluate({'x': 1, 'y': 1})) | |
189 | 201 |
190 def test_compare_lt(self): | 202 def test_compare_lt(self): |
191 self.assertEqual(True, Expression("1 < 2").evaluate({})) | 203 self.assertEqual(True, Expression("1 < 2").evaluate({})) |
192 self.assertEqual(True, Expression("x < y").evaluate({'x': 1, 'y': 2})) | 204 self.assertEqual(True, Expression("x < y").evaluate({'x': 1, 'y': 2})) |
193 | 205 |
228 return x | 240 return x |
229 expr = Expression("foo(**bar)") | 241 expr = Expression("foo(**bar)") |
230 self.assertEqual(42, expr.evaluate({'foo': foo, 'bar': {"x": 42}})) | 242 self.assertEqual(42, expr.evaluate({'foo': foo, 'bar': {"x": 42}})) |
231 | 243 |
232 def test_lambda(self): | 244 def test_lambda(self): |
233 # Define a custom `sorted` function cause the builtin isn't available | 245 data = {'items': range(5)} |
234 # on Python 2.3 | 246 expr = Expression("filter(lambda x: x > 2, items)") |
235 def sorted(items, compfunc): | 247 self.assertEqual([3, 4], expr.evaluate(data)) |
236 items.sort(compfunc) | |
237 return items | |
238 data = {'items': [{'name': 'b', 'value': 0}, {'name': 'a', 'value': 1}], | |
239 'sorted': sorted} | |
240 expr = Expression("sorted(items, lambda a, b: cmp(a.name, b.name))") | |
241 self.assertEqual([{'name': 'a', 'value': 1}, {'name': 'b', 'value': 0}], | |
242 expr.evaluate(data)) | |
243 | 248 |
244 def test_list_comprehension(self): | 249 def test_list_comprehension(self): |
245 expr = Expression("[n for n in numbers if n < 2]") | 250 expr = Expression("[n for n in numbers if n < 2]") |
246 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)})) | 251 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)})) |
247 | 252 |
261 def test_list_comprehension_with_getitem(self): | 266 def test_list_comprehension_with_getitem(self): |
262 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] | 267 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
263 expr = Expression("[i['name'] for i in items if i['value'] > 1]") | 268 expr = Expression("[i['name'] for i in items if i['value'] > 1]") |
264 self.assertEqual(['b'], expr.evaluate({'items': items})) | 269 self.assertEqual(['b'], expr.evaluate({'items': items})) |
265 | 270 |
266 if sys.version_info >= (2, 4): | 271 def test_generator_expression(self): |
267 # Generator expressions only supported in Python 2.4 and up | 272 expr = Expression("list(n for n in numbers if n < 2)") |
268 | 273 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)})) |
269 def test_generator_expression(self): | 274 |
270 expr = Expression("list(n for n in numbers if n < 2)") | 275 expr = Expression("list((i, n + 1) for i, n in enumerate(numbers))") |
271 self.assertEqual([0, 1], expr.evaluate({'numbers': range(5)})) | 276 self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)], |
272 | 277 expr.evaluate({'numbers': range(5)})) |
273 expr = Expression("list((i, n + 1) for i, n in enumerate(numbers))") | 278 |
274 self.assertEqual([(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)], | 279 expr = Expression("list(offset + n for n in numbers)") |
275 expr.evaluate({'numbers': range(5)})) | 280 self.assertEqual([2, 3, 4, 5, 6], |
276 | 281 expr.evaluate({'numbers': range(5), 'offset': 2})) |
277 expr = Expression("list(offset + n for n in numbers)") | 282 |
278 self.assertEqual([2, 3, 4, 5, 6], | 283 def test_generator_expression_with_getattr(self): |
279 expr.evaluate({'numbers': range(5), 'offset': 2})) | 284 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
280 | 285 expr = Expression("list(i.name for i in items if i.value > 1)") |
281 def test_generator_expression_with_getattr(self): | 286 self.assertEqual(['b'], expr.evaluate({'items': items})) |
282 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] | 287 |
283 expr = Expression("list(i.name for i in items if i.value > 1)") | 288 def test_generator_expression_with_getitem(self): |
284 self.assertEqual(['b'], expr.evaluate({'items': items})) | 289 items = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}] |
285 | 290 expr = Expression("list(i['name'] for i in items if i['value'] > 1)") |
286 def test_generator_expression_with_getitem(self): | 291 self.assertEqual(['b'], expr.evaluate({'items': items})) |
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})) | |
290 | 292 |
291 if sys.version_info >= (2, 5): | 293 if sys.version_info >= (2, 5): |
292 def test_conditional_expression(self): | 294 def test_conditional_expression(self): |
293 expr = Expression("'T' if foo else 'F'") | 295 expr = Expression("'T' if foo else 'F'") |
294 self.assertEqual('T', expr.evaluate({'foo': True})) | 296 self.assertEqual('T', expr.evaluate({'foo': True})) |
319 def test_slice_negative_end(self): | 321 def test_slice_negative_end(self): |
320 expr = Expression("numbers[:-1]") | 322 expr = Expression("numbers[:-1]") |
321 self.assertEqual([0, 1, 2, 3], expr.evaluate({'numbers': range(5)})) | 323 self.assertEqual([0, 1, 2, 3], expr.evaluate({'numbers': range(5)})) |
322 | 324 |
323 def test_access_undefined(self): | 325 def test_access_undefined(self): |
324 expr = Expression("nothing", filename='index.html', lineno=50) | 326 expr = Expression("nothing", filename='index.html', lineno=50, |
327 lookup='lenient') | |
325 retval = expr.evaluate({}) | 328 retval = expr.evaluate({}) |
326 assert isinstance(retval, Undefined) | 329 assert isinstance(retval, Undefined) |
327 self.assertEqual('nothing', retval._name) | 330 self.assertEqual('nothing', retval._name) |
328 assert retval._owner is UNDEFINED | 331 assert retval._owner is UNDEFINED |
329 | 332 |
330 def test_getattr_undefined(self): | 333 def test_getattr_undefined(self): |
331 class Something(object): | 334 class Something(object): |
332 def __repr__(self): | 335 def __repr__(self): |
333 return '<Something>' | 336 return '<Something>' |
334 something = Something() | 337 something = Something() |
335 expr = Expression('something.nil', filename='index.html', lineno=50) | 338 expr = Expression('something.nil', filename='index.html', lineno=50, |
339 lookup='lenient') | |
336 retval = expr.evaluate({'something': something}) | 340 retval = expr.evaluate({'something': something}) |
337 assert isinstance(retval, Undefined) | 341 assert isinstance(retval, Undefined) |
338 self.assertEqual('nil', retval._name) | 342 self.assertEqual('nil', retval._name) |
339 assert retval._owner is something | 343 assert retval._owner is something |
344 | |
345 def test_getattr_exception(self): | |
346 class Something(object): | |
347 def prop_a(self): | |
348 raise NotImplementedError | |
349 prop_a = property(prop_a) | |
350 def prop_b(self): | |
351 raise AttributeError | |
352 prop_b = property(prop_b) | |
353 self.assertRaises(NotImplementedError, | |
354 Expression('s.prop_a').evaluate, {'s': Something()}) | |
355 self.assertRaises(AttributeError, | |
356 Expression('s.prop_b').evaluate, {'s': Something()}) | |
340 | 357 |
341 def test_getitem_undefined_string(self): | 358 def test_getitem_undefined_string(self): |
342 class Something(object): | 359 class Something(object): |
343 def __repr__(self): | 360 def __repr__(self): |
344 return '<Something>' | 361 return '<Something>' |
345 something = Something() | 362 something = Something() |
346 expr = Expression('something["nil"]', filename='index.html', lineno=50) | 363 expr = Expression('something["nil"]', filename='index.html', lineno=50, |
364 lookup='lenient') | |
347 retval = expr.evaluate({'something': something}) | 365 retval = expr.evaluate({'something': something}) |
348 assert isinstance(retval, Undefined) | 366 assert isinstance(retval, Undefined) |
349 self.assertEqual('nil', retval._name) | 367 self.assertEqual('nil', retval._name) |
350 assert retval._owner is something | 368 assert retval._owner is something |
369 | |
370 def test_getitem_exception(self): | |
371 class Something(object): | |
372 def __getitem__(self, key): | |
373 raise NotImplementedError | |
374 self.assertRaises(NotImplementedError, | |
375 Expression('s["foo"]').evaluate, {'s': Something()}) | |
351 | 376 |
352 def test_error_access_undefined(self): | 377 def test_error_access_undefined(self): |
353 expr = Expression("nothing", filename='index.html', lineno=50, | 378 expr = Expression("nothing", filename='index.html', lineno=50, |
354 lookup='strict') | 379 lookup='strict') |
355 try: | 380 try: |
418 self.assertEqual(50, frame.tb_lineno) | 443 self.assertEqual(50, frame.tb_lineno) |
419 | 444 |
420 | 445 |
421 class SuiteTestCase(unittest.TestCase): | 446 class SuiteTestCase(unittest.TestCase): |
422 | 447 |
448 def test_pickle(self): | |
449 suite = Suite('foo = 42') | |
450 buf = StringIO() | |
451 pickle.dump(suite, buf, 2) | |
452 buf.seek(0) | |
453 unpickled = pickle.load(buf) | |
454 data = {} | |
455 unpickled.execute(data) | |
456 self.assertEqual(42, data['foo']) | |
457 | |
458 def test_internal_shadowing(self): | |
459 # The context itself is stored in the global execution scope of a suite | |
460 # It used to get stored under the name 'data', which meant the | |
461 # following test would fail, as the user defined 'data' variable | |
462 # shadowed the Genshi one. We now use the name '__data__' to avoid | |
463 # conflicts | |
464 suite = Suite("""data = [] | |
465 bar = foo | |
466 """) | |
467 data = {'foo': 42} | |
468 suite.execute(data) | |
469 self.assertEqual(42, data['bar']) | |
470 | |
423 def test_assign(self): | 471 def test_assign(self): |
424 suite = Suite("foo = 42") | 472 suite = Suite("foo = 42") |
425 data = {} | 473 data = {} |
426 suite.execute(data) | 474 suite.execute(data) |
427 self.assertEqual(42, data['foo']) | 475 self.assertEqual(42, data['foo']) |
432 suite.execute(data) | 480 suite.execute(data) |
433 assert 'donothing' in data | 481 assert 'donothing' in data |
434 self.assertEqual(None, data['donothing']()) | 482 self.assertEqual(None, data['donothing']()) |
435 | 483 |
436 def test_def_with_multiple_statements(self): | 484 def test_def_with_multiple_statements(self): |
437 suite = Suite("""def donothing(): | 485 suite = Suite(""" |
486 def donothing(): | |
438 if True: | 487 if True: |
439 return foo | 488 return foo |
440 """) | 489 """) |
441 data = {'foo': 'bar'} | 490 data = {'foo': 'bar'} |
442 suite.execute(data) | 491 suite.execute(data) |
443 assert 'donothing' in data | 492 assert 'donothing' in data |
444 self.assertEqual('bar', data['donothing']()) | 493 self.assertEqual('bar', data['donothing']()) |
494 | |
495 def test_def_using_nonlocal(self): | |
496 suite = Suite(""" | |
497 values = [] | |
498 def add(value): | |
499 if value not in values: | |
500 values.append(value) | |
501 add('foo') | |
502 add('bar') | |
503 """) | |
504 data = {} | |
505 suite.execute(data) | |
506 self.assertEqual(['foo', 'bar'], data['values']) | |
507 | |
508 def test_def_some_defaults(self): | |
509 suite = Suite(""" | |
510 def difference(v1, v2=10): | |
511 return v1 - v2 | |
512 x = difference(20, 19) | |
513 y = difference(20) | |
514 """) | |
515 data = {} | |
516 suite.execute(data) | |
517 self.assertEqual(1, data['x']) | |
518 self.assertEqual(10, data['y']) | |
519 | |
520 def test_def_all_defaults(self): | |
521 suite = Suite(""" | |
522 def difference(v1=100, v2=10): | |
523 return v1 - v2 | |
524 x = difference(20, 19) | |
525 y = difference(20) | |
526 z = difference() | |
527 """) | |
528 data = {} | |
529 suite.execute(data) | |
530 self.assertEqual(1, data['x']) | |
531 self.assertEqual(10, data['y']) | |
532 self.assertEqual(90, data['z']) | |
533 | |
534 def test_def_vararg(self): | |
535 suite = Suite(""" | |
536 def mysum(*others): | |
537 rv = 0 | |
538 for n in others: | |
539 rv = rv + n | |
540 return rv | |
541 x = mysum(1, 2, 3) | |
542 """) | |
543 data = {} | |
544 suite.execute(data) | |
545 self.assertEqual(6, data['x']) | |
546 | |
547 def test_def_kwargs(self): | |
548 suite = Suite(""" | |
549 def smash(**kw): | |
550 return [''.join(i) for i in kw.items()] | |
551 x = smash(foo='abc', bar='def') | |
552 """) | |
553 data = {} | |
554 suite.execute(data) | |
555 self.assertEqual(['fooabc', 'bardef'], data['x']) | |
556 | |
557 def test_def_nested(self): | |
558 suite = Suite(""" | |
559 def doit(): | |
560 values = [] | |
561 def add(value): | |
562 if value not in values: | |
563 values.append(value) | |
564 add('foo') | |
565 add('bar') | |
566 return values | |
567 x = doit() | |
568 """) | |
569 data = {} | |
570 suite.execute(data) | |
571 self.assertEqual(['foo', 'bar'], data['x']) | |
445 | 572 |
446 def test_delete(self): | 573 def test_delete(self): |
447 suite = Suite("""foo = 42 | 574 suite = Suite("""foo = 42 |
448 del foo | 575 del foo |
449 """) | 576 """) |
455 suite = Suite("class plain(object): pass") | 582 suite = Suite("class plain(object): pass") |
456 data = {} | 583 data = {} |
457 suite.execute(data) | 584 suite.execute(data) |
458 assert 'plain' in data | 585 assert 'plain' in data |
459 | 586 |
587 def test_class_in_def(self): | |
588 suite = Suite(""" | |
589 def create(): | |
590 class Foobar(object): | |
591 def __str__(self): | |
592 return 'foobar' | |
593 return Foobar() | |
594 x = create() | |
595 """) | |
596 data = {} | |
597 suite.execute(data) | |
598 self.assertEqual('foobar', str(data['x'])) | |
599 | |
600 def test_class_with_methods(self): | |
601 suite = Suite("""class plain(object): | |
602 def donothing(): | |
603 pass | |
604 """) | |
605 data = {} | |
606 suite.execute(data) | |
607 assert 'plain' in data | |
608 | |
460 def test_import(self): | 609 def test_import(self): |
461 suite = Suite("from itertools import ifilter") | 610 suite = Suite("from itertools import ifilter") |
462 data = {} | 611 data = {} |
463 suite.execute(data) | 612 suite.execute(data) |
464 assert 'ifilter' in data | 613 assert 'ifilter' in data |
614 | |
615 def test_import_star(self): | |
616 suite = Suite("from itertools import *") | |
617 data = Context() | |
618 suite.execute(data) | |
619 assert 'ifilter' in data | |
620 | |
621 def test_import_in_def(self): | |
622 suite = Suite("""def fun(): | |
623 from itertools import ifilter | |
624 return ifilter(None, xrange(3)) | |
625 """) | |
626 data = Context() | |
627 suite.execute(data) | |
628 assert 'ifilter' not in data | |
629 self.assertEqual([1, 2], list(data['fun']())) | |
465 | 630 |
466 def test_for(self): | 631 def test_for(self): |
467 suite = Suite("""x = [] | 632 suite = Suite("""x = [] |
468 for i in range(3): | 633 for i in range(3): |
469 x.append(i**2) | 634 x.append(i**2) |
470 """) | 635 """) |
471 data = {} | 636 data = {} |
472 suite.execute(data) | 637 suite.execute(data) |
473 self.assertEqual([0, 1, 4], data['x']) | 638 self.assertEqual([0, 1, 4], data['x']) |
639 | |
640 def test_for_in_def(self): | |
641 suite = Suite("""def loop(): | |
642 for i in range(10): | |
643 if i == 5: | |
644 break | |
645 return i | |
646 """) | |
647 data = {} | |
648 suite.execute(data) | |
649 assert 'loop' in data | |
650 self.assertEqual(5, data['loop']()) | |
474 | 651 |
475 def test_if(self): | 652 def test_if(self): |
476 suite = Suite("""if foo == 42: | 653 suite = Suite("""if foo == 42: |
477 x = True | 654 x = True |
478 """) | 655 """) |
523 self.assertEqual(43, d["k"]) | 700 self.assertEqual(43, d["k"]) |
524 | 701 |
525 def test_local_augmented_assign(self): | 702 def test_local_augmented_assign(self): |
526 Suite("x = 1; x += 42; assert x == 43").execute({}) | 703 Suite("x = 1; x += 42; assert x == 43").execute({}) |
527 | 704 |
705 def test_augmented_assign_in_def(self): | |
706 d = {} | |
707 Suite("""def foo(): | |
708 i = 1 | |
709 i += 1 | |
710 return i | |
711 x = foo()""").execute(d) | |
712 self.assertEqual(2, d['x']) | |
713 | |
714 def test_augmented_assign_in_loop_in_def(self): | |
715 d = {} | |
716 Suite("""def foo(): | |
717 i = 0 | |
718 for n in range(5): | |
719 i += n | |
720 return i | |
721 x = foo()""").execute(d) | |
722 self.assertEqual(10, d['x']) | |
723 | |
528 def test_assign_in_list(self): | 724 def test_assign_in_list(self): |
529 suite = Suite("[d['k']] = 'foo',; assert d['k'] == 'foo'") | 725 suite = Suite("[d['k']] = 'foo',; assert d['k'] == 'foo'") |
530 d = {"k": "bar"} | 726 d = {"k": "bar"} |
531 suite.execute({"d": d}) | 727 suite.execute({"d": d}) |
532 self.assertEqual("foo", d["k"]) | 728 self.assertEqual("foo", d["k"]) |
567 self.failIf(hasattr(obj, 'attr')) | 763 self.failIf(hasattr(obj, 'attr')) |
568 | 764 |
569 def test_delitem(self): | 765 def test_delitem(self): |
570 d = {'k': 'foo'} | 766 d = {'k': 'foo'} |
571 Suite("del d['k']").execute({'d': d}) | 767 Suite("del d['k']").execute({'d': d}) |
572 self.failIf('k' in d, `d`) | 768 self.failIf('k' in d, repr(d)) |
573 | 769 |
574 | 770 |
575 def suite(): | 771 def suite(): |
576 suite = unittest.TestSuite() | 772 suite = unittest.TestSuite() |
577 suite.addTest(doctest.DocTestSuite(Expression.__module__)) | 773 suite.addTest(doctest.DocTestSuite(Expression.__module__)) |