From 2e9df555d9c7ca79d4b55714e3e5a9e20c473e68 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Sun, 28 Jun 2020 06:06:13 +0200 Subject: [PATCH] Raise a reasonable exception when an operator is undefined --- simpleeval.py | 24 +++++++++++++++++++++--- test_simpleeval.py | 19 +++++++++++++++++-- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/simpleeval.py b/simpleeval.py index bcdcf53..d020eb6 100644 --- a/simpleeval.py +++ b/simpleeval.py @@ -175,6 +175,17 @@ def __init__(self, attr, expression): self.expression = expression +class OperatorNotDefined(InvalidExpression): + """operator does not exist""" + + def __init__(self, attr, expression): + self.message = \ + "Operator '{0}' does not exist in expression '{1}'".format( + attr, expression) + self.attr = attr + self.expression = expression + + class FeatureNotAvailable(InvalidExpression): """ What you're trying to do is not allowed. """ @@ -392,11 +403,18 @@ def _eval_constant(node): return node.value def _eval_unaryop(self, node): - return self.operators[type(node.op)](self._eval(node.operand)) + try: + op = self.operators[type(node.op)] + except KeyError: + raise OperatorNotDefined(node.op, self.expr) + return op(self._eval(node.operand)) def _eval_binop(self, node): - return self.operators[type(node.op)](self._eval(node.left), - self._eval(node.right)) + try: + op = self.operators[type(node.op)] + except KeyError: + raise OperatorNotDefined(node.op, self.expr) + return op(self._eval(node.left), self._eval(node.right)) def _eval_boolop(self, node): if isinstance(node.op, ast.And): diff --git a/test_simpleeval.py b/test_simpleeval.py index 8629a1d..2114aab 100644 --- a/test_simpleeval.py +++ b/test_simpleeval.py @@ -15,7 +15,7 @@ import warnings from simpleeval import ( SimpleEval, EvalWithCompoundTypes, FeatureNotAvailable, FunctionNotDefined, NameNotDefined, - InvalidExpression, AttributeDoesNotExist, simple_eval + OperatorNotDefined, InvalidExpression, AttributeDoesNotExist, simple_eval ) @@ -1080,13 +1080,28 @@ def test_functions_are_disallowed_in_expressions(self): simpleeval.DEFAULT_FUNCTIONS = DF.copy() -class TestNoFunctions(DRYTest): +class TestNoEntries(DRYTest): def test_no_functions(self): self.s.eval('int(42)') with self.assertRaises(FunctionNotDefined): s = SimpleEval(functions={}) s.eval('int(42)') +# does not work, AST interprets built-ins directly +# +# def test_no_names(self): +# import pdb;pdb.set_trace() +# self.s.eval('True') +# with self.assertRaises(NameNotDefined): +# s = SimpleEval(names={}) +# s.eval('True') + + def test_no_operators(self): + self.s.eval('1+2') + with self.assertRaises(OperatorNotDefined): + s = SimpleEval(operators={}) + s.eval('1+2') + if __name__ == '__main__': # pragma: no cover