# HG changeset patch # User cmlenz # Date 1151522027 0 # Node ID 2224a52256ca15c460ba635a417bdf94194a17d5 # Parent 2ab5fa60575d4cd84135a5ae862a99eb05ef87ee A couple more operators supported in expressions. diff --git a/markup/eval.py b/markup/eval.py --- a/markup/eval.py +++ b/markup/eval.py @@ -13,6 +13,8 @@ """Support for "safe" evaluation of Python expressions.""" +from __future__ import division + import __builtin__ try: import _ast # Python 2.5 @@ -155,9 +157,18 @@ return reduce(lambda x, y: x or y, [self._visit(n, data) for n in node.nodes]) + def _visit_bitand(self, node, data): + return reduce(operator.and_, + [self._visit(n, data) for n in node.nodes]) + + def _visit_bitor(self, node, data): + return reduce(operator.or_, + [self._visit(n, data) for n in node.nodes]) + _OP_MAP = {'==': operator.eq, '!=': operator.ne, '<': operator.lt, '<=': operator.le, '>': operator.gt, '>=': operator.ge, + 'is': operator.is_, 'is not': operator.is_not, 'in': lambda x, y: operator.contains(y, x), 'not in': lambda x, y: not operator.contains(y, x)} def _visit_compare(self, node, data): @@ -189,15 +200,18 @@ def _visit_sub(self, node, data): return self._visit(node.left, data) - self._visit(node.right, data) - def _visit_not(self, node, data): - return not self._visit(node.expr, data) - def _visit_unaryadd(self, node, data): return +self._visit(node.expr, data) def _visit_unarysub(self, node, data): return -self._visit(node.expr, data) + def _visit_not(self, node, data): + return not self._visit(node.expr, data) + + def _visit_invert(self, node, data): + return ~self._visit(node.expr, data) + # Identifiers & Literals def _visit_name(self, node, data): @@ -286,10 +300,12 @@ # Operators _OP_MAP = {_ast.Add: operator.add, _ast.And: lambda l, r: l and r, - _ast.Div: operator.div, _ast.Eq: operator.eq, + _ast.BitAnd: operator.and_, _ast.BitOr: operator.or_, + _ast.Div: operator.truediv, _ast.Eq: operator.eq, _ast.FloorDiv: operator.floordiv, _ast.Gt: operator.gt, - _ast.GtE: operator.ge, + _ast.GtE: operator.ge, _ast.Invert: operator.inv, _ast.In: lambda l, r: operator.contains(r, l), + _ast.Is: operator.is_, _ast.IsNot: operator.is_not, _ast.Lt: operator.lt, _ast.LtE: operator.le, _ast.Mod: operator.mod, _ast.Mult: operator.mul, _ast.Not: operator.not_, _ast.NotEq: operator.ne, diff --git a/markup/tests/eval.py b/markup/tests/eval.py --- a/markup/tests/eval.py +++ b/markup/tests/eval.py @@ -64,6 +64,10 @@ self.assertEqual(False, Expression("not True").evaluate({})) self.assertEqual(False, Expression("not x").evaluate({'x': True})) + def test_unaryop_inv(self): + self.assertEqual(-2, Expression("~1").evaluate({})) + self.assertEqual(-2, Expression("~x").evaluate({'x': 1})) + def test_binop_add(self): self.assertEqual(3, Expression("2 + 1").evaluate({})) self.assertEqual(3, Expression("x + y").evaluate({'x': 2, 'y': 1})) @@ -96,6 +100,14 @@ self.assertEqual(1, Expression("3 % 2").evaluate({})) self.assertEqual(1, Expression("x % y").evaluate({'x': 3, 'y': 2})) + def test_binop_and(self): + self.assertEqual(0, Expression("1 & 0").evaluate({})) + self.assertEqual(0, Expression("x & y").evaluate({'x': 1, 'y': 0})) + + def test_binop_or(self): + self.assertEqual(1, Expression("1 | 0").evaluate({})) + self.assertEqual(1, Expression("x | y").evaluate({'x': 1, 'y': 0})) + 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, @@ -106,6 +118,20 @@ self.assertEqual(True, Expression("x not in y").evaluate({'x': 4, 'y': (1, 2, 3)})) + def test_binop_is(self): + self.assertEqual(True, Expression("1 is 1").evaluate({})) + self.assertEqual(True, Expression("x is y").evaluate({'x': 1, 'y': 1})) + self.assertEqual(False, Expression("1 is 2").evaluate({})) + self.assertEqual(False, Expression("x is y").evaluate({'x': 1, 'y': 2})) + + def test_binop_is_not(self): + self.assertEqual(True, Expression("1 is not 2").evaluate({})) + self.assertEqual(True, Expression("x is not y").evaluate({'x': 1, + 'y': 2})) + self.assertEqual(False, Expression("1 is not 1").evaluate({})) + self.assertEqual(False, Expression("x is not y").evaluate({'x': 1, + 'y': 1})) + def test_boolop_and(self): self.assertEqual(False, Expression("True and False").evaluate({})) self.assertEqual(False, Expression("x and y").evaluate({'x': True, @@ -123,6 +149,8 @@ def test_compare_ne(self): self.assertEqual(False, Expression("1 != 1").evaluate({})) self.assertEqual(False, Expression("x != y").evaluate({'x': 1, 'y': 1})) + 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({}))