Mercurial > genshi > mirror
changeset 31:2ab5fa60575d trunk
* More test cases for expression evaluation.
* Fixes for the new AST-based Python 2.5 evaluation.
author | cmlenz |
---|---|
date | Wed, 28 Jun 2006 18:39:05 +0000 |
parents | bcdbb7e5e4e5 |
children | 2224a52256ca |
files | markup/eval.py markup/template.py markup/tests/eval.py |
diffstat | 3 files changed, 108 insertions(+), 46 deletions(-) [+] |
line wrap: on
line diff
--- a/markup/eval.py +++ b/markup/eval.py @@ -58,44 +58,7 @@ >>> Expression('mine[key]').evaluate(data) 'Bar' - Most of the standard Python operators are also available to template - expressions. Bitwise operators (including inversion and shifting) are not - supported. - - >>> Expression('1 + 1').evaluate(data) - 2 - >>> Expression('3 - 1').evaluate(data) - 2 - >>> Expression('1 * 2').evaluate(data) - 2 - >>> Expression('4 / 2').evaluate(data) - 2 - >>> Expression('4 // 3').evaluate(data) - 1 - >>> Expression('4 % 3').evaluate(data) - 1 - >>> Expression('2 ** 3').evaluate(data) - 8 - >>> Expression('not True').evaluate(data) - False - >>> Expression('True and False').evaluate(data) - False - >>> Expression('True or False').evaluate(data) - True - >>> Expression('1 == 3').evaluate(data) - False - >>> Expression('1 != 3 == 3').evaluate(data) - True - >>> Expression('1 > 0').evaluate(data) - True - >>> Expression('True and "Foo"').evaluate(data) - 'Foo' - >>> data = dict(items=[1, 2, 3]) - >>> Expression('2 in items').evaluate(data) - True - >>> Expression('not 2 in items').evaluate(data) - False - + All of the standard Python operators are available to template expressions. Built-in functions such as `len()` are also available in template expressions: @@ -325,9 +288,12 @@ _OP_MAP = {_ast.Add: operator.add, _ast.And: lambda l, r: l and r, _ast.Div: operator.div, _ast.Eq: operator.eq, _ast.FloorDiv: operator.floordiv, _ast.Gt: operator.gt, + _ast.GtE: operator.ge, _ast.In: lambda l, r: operator.contains(r, l), + _ast.Lt: operator.lt, _ast.LtE: operator.le, _ast.Mod: operator.mod, _ast.Mult: operator.mul, _ast.Not: operator.not_, _ast.NotEq: operator.ne, + _ast.NotIn: lambda l, r: not operator.contains(r, l), _ast.Or: lambda l, r: l or r, _ast.Pow: operator.pow, _ast.Sub: operator.sub, _ast.UAdd: operator.pos, _ast.USub: operator.neg}
--- a/markup/template.py +++ b/markup/template.py @@ -165,9 +165,8 @@ Directives can be "anonymous" or "registered". Registered directives can be applied by the template author using an XML attribute with the corresponding name in the template. Such directives should be subclasses of - this base class that can be instantiated with two parameters: `template` - is the `Template` instance, and `value` is the value of the directive - attribute. + this base class that can be instantiated with the value of the directive + attribute as parameter. Anonymous directives are simply functions conforming to the protocol described above, and can only be applied programmatically (for example by @@ -335,7 +334,8 @@ class ForDirective(Directive): - """Implementation of the `py:for` template directive. + """Implementation of the `py:for` template directive for repeating an + element based on an iterable in the context data. >>> ctxt = Context(items=[1, 2, 3]) >>> tmpl = Template('''<ul xmlns:py="http://purl.org/kid/ns#"> @@ -374,7 +374,8 @@ class IfDirective(Directive): - """Implementation of the `py:if` template directive. + """Implementation of the `py:if` template directive for conditionally + excluding elements from being output. >>> ctxt = Context(foo=True, bar='Hello') >>> tmpl = Template('''<div xmlns:py="http://purl.org/kid/ns#"> @@ -471,6 +472,9 @@ class ReplaceDirective(Directive): """Implementation of the `py:replace` template directive. + This directive replaces the element with the result of evaluating the + value of the `py:replace` attribute: + >>> ctxt = Context(bar='Bye') >>> tmpl = Template('''<div xmlns:py="http://purl.org/kid/ns#"> ... <span py:replace="bar">Hello</span> @@ -647,8 +651,8 @@ value = list(self._interpolate(value, *pos)) new_attrib.append((name, value)) if directives: - directives.sort(lambda a, b: cmp(self._dir_order.index(a.__class__), - self._dir_order.index(b.__class__))) + directives.sort(lambda a, b: cmp(self._dir_order.index(b.__class__), + self._dir_order.index(a.__class__))) dirmap[(depth, tag)] = (directives, len(stream)) stream.append((kind, (tag, Attributes(new_attrib)), pos)) @@ -780,7 +784,6 @@ # nested events to which those directives should be # applied directives, substream = data - directives.reverse() for directive in directives: substream = directive(iter(substream), ctxt) substream = self._match(self._eval(substream, ctxt), ctxt)
--- a/markup/tests/eval.py +++ b/markup/tests/eval.py @@ -52,6 +52,99 @@ self.assertEqual((True,), Expression("(value,)").evaluate({'value': True})) + def test_unaryop_pos(self): + self.assertEqual(1, Expression("+1").evaluate({})) + self.assertEqual(1, Expression("+x").evaluate({'x': 1})) + + def test_unaryop_neg(self): + self.assertEqual(-1, Expression("-1").evaluate({})) + self.assertEqual(-1, Expression("-x").evaluate({'x': 1})) + + def test_unaryop_not(self): + self.assertEqual(False, Expression("not True").evaluate({})) + self.assertEqual(False, Expression("not x").evaluate({'x': True})) + + def test_binop_add(self): + self.assertEqual(3, Expression("2 + 1").evaluate({})) + self.assertEqual(3, Expression("x + y").evaluate({'x': 2, 'y': 1})) + + def test_binop_sub(self): + self.assertEqual(1, Expression("2 - 1").evaluate({})) + self.assertEqual(1, Expression("x - y").evaluate({'x': 1, 'y': 1})) + + def test_binop_sub(self): + self.assertEqual(1, Expression("2 - 1").evaluate({})) + self.assertEqual(1, Expression("x - y").evaluate({'x': 2, 'y': 1})) + + def test_binop_mul(self): + self.assertEqual(4, Expression("2 * 2").evaluate({})) + self.assertEqual(4, Expression("x * y").evaluate({'x': 2, 'y': 2})) + + def test_binop_pow(self): + self.assertEqual(4, Expression("2 ** 2").evaluate({})) + self.assertEqual(4, Expression("x ** y").evaluate({'x': 2, 'y': 2})) + + def test_binop_div(self): + self.assertEqual(2, Expression("4 / 2").evaluate({})) + self.assertEqual(2, Expression("x / y").evaluate({'x': 4, 'y': 2})) + + def test_binop_floordiv(self): + self.assertEqual(1, Expression("3 // 2").evaluate({})) + self.assertEqual(1, Expression("x // y").evaluate({'x': 3, 'y': 2})) + + def test_binop_mod(self): + self.assertEqual(1, Expression("3 % 2").evaluate({})) + self.assertEqual(1, Expression("x % y").evaluate({'x': 3, 'y': 2})) + + def test_binop_contains(self): + self.assertEqual(True, Expression("1 in (1, 2, 3)").evaluate({})) + self.assertEqual(True, Expression("x in y").evaluate({'x': 1, + 'y': (1, 2, 3)})) + + def test_binop_not_contains(self): + self.assertEqual(True, Expression("4 not in (1, 2, 3)").evaluate({})) + self.assertEqual(True, Expression("x not in y").evaluate({'x': 4, + 'y': (1, 2, 3)})) + + def test_boolop_and(self): + self.assertEqual(False, Expression("True and False").evaluate({})) + self.assertEqual(False, Expression("x and y").evaluate({'x': True, + 'y': False})) + + def test_boolop_or(self): + self.assertEqual(True, Expression("True or False").evaluate({})) + self.assertEqual(True, Expression("x or y").evaluate({'x': True, + 'y': False})) + + def test_compare_eq(self): + self.assertEqual(True, Expression("1 == 1").evaluate({})) + self.assertEqual(True, Expression("x == y").evaluate({'x': 1, 'y': 1})) + + def test_compare_ne(self): + self.assertEqual(False, Expression("1 != 1").evaluate({})) + self.assertEqual(False, Expression("x != y").evaluate({'x': 1, 'y': 1})) + + def test_compare_lt(self): + self.assertEqual(True, Expression("1 < 2").evaluate({})) + self.assertEqual(True, Expression("x < y").evaluate({'x': 1, 'y': 2})) + + def test_compare_le(self): + self.assertEqual(True, Expression("1 <= 1").evaluate({})) + self.assertEqual(True, Expression("x <= y").evaluate({'x': 1, 'y': 1})) + + def test_compare_gt(self): + self.assertEqual(True, Expression("2 > 1").evaluate({})) + self.assertEqual(True, Expression("x > y").evaluate({'x': 2, 'y': 1})) + + def test_compare_ge(self): + self.assertEqual(True, Expression("1 >= 1").evaluate({})) + self.assertEqual(True, Expression("x >= y").evaluate({'x': 1, 'y': 1})) + + def test_compare_multi(self): + self.assertEqual(True, Expression("1 != 3 == 3").evaluate({})) + self.assertEqual(True, Expression("x != y == y").evaluate({'x': 3, + 'y': 3})) + def suite(): suite = unittest.TestSuite()