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()
Copyright (C) 2012-2017 Edgewall Software