From 825e1a25ef1fd2febf1cc83955938d34e7e22e2e Mon Sep 17 00:00:00 2001 From: "S.Lott" Date: Wed, 21 May 2025 09:53:12 -0400 Subject: [PATCH] Add a test to show issue #42 seems to be resolved --- .gitignore | 2 ++ demo/README.rst | 2 +- src/celpy/evaluation.py | 31 ++++++++++++++++++++++++------- tests/test_evaluation.py | 18 +++++++++++++++++- tox.ini | 3 ++- type_check/lineprecision.txt | 16 ++++++++-------- 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 120531f..5462dcc 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,5 @@ type_check/lineprecision.txt # PyCharm Stuff .idea/* demo/issue_41.py +demo/issue_80.py +setup.sh diff --git a/demo/README.rst b/demo/README.rst index b3dd96a..f332eed 100644 --- a/demo/README.rst +++ b/demo/README.rst @@ -50,4 +50,4 @@ and examine the notebooks. We'll look at this from a Cloud Custodian (C7N) persp - `demo/complex_policy.ipynb`_ shows a complex policy and how we can build -- and debug -- the policy filter expression. -These should help youo build an understanding of +These should help you build an understanding of how to develop complex filters. diff --git a/src/celpy/evaluation.py b/src/celpy/evaluation.py index afe461a..b7ab2b2 100644 --- a/src/celpy/evaluation.py +++ b/src/celpy/evaluation.py @@ -1417,7 +1417,8 @@ def expr(self, tree: lark.Tree) -> Result: expr : conditionalor ["?" conditionalor ":" expr] The default implementation short-circuits - and can ignore an CELEvalError in a sub-expression. + and can ignore a CELEvalError in the two alternative sub-expressions. + The conditional sub-expression CELEvalError is propogated out as the result. See https://github.com/google/cel-spec/blob/master/doc/langdef.md#logical-operators @@ -2030,7 +2031,15 @@ def member_dot_arg(self, tree: lark.Tree) -> Result: Tuple[lark.Tree, lark.Token], tree.children[:2] ) - if method_name_token.value in {"map", "filter", "all", "exists", "exists_one", "reduce", "min"}: + if method_name_token.value in { + "map", + "filter", + "all", + "exists", + "exists_one", + "reduce", + "min", + }: member_list = cast(celpy.celtypes.ListType, self.visit(member_tree)) if isinstance(member_list, CELEvalError): @@ -2038,7 +2047,9 @@ def member_dot_arg(self, tree: lark.Tree) -> Result: if method_name_token.value == "map": sub_expr = self.build_macro_eval(tree) - mapping = cast(Iterable[celpy.celtypes.Value], map(sub_expr, member_list)) + mapping = cast( + Iterable[celpy.celtypes.Value], map(sub_expr, member_list) + ) result = celpy.celtypes.ListType(mapping) return result @@ -2052,9 +2063,12 @@ def member_dot_arg(self, tree: lark.Tree) -> Result: and_oper = cast( CELBoolFunction, eval_error("no such overload", TypeError)( - celpy.celtypes.logical_and) + celpy.celtypes.logical_and + ), + ) + reduction = reduce( + and_oper, map(sub_expr, member_list), celpy.celtypes.BoolType(True) ) - reduction = reduce(and_oper, map(sub_expr, member_list), celpy.celtypes.BoolType(True)) return reduction elif method_name_token.value == "exists": @@ -2062,9 +2076,12 @@ def member_dot_arg(self, tree: lark.Tree) -> Result: or_oper = cast( CELBoolFunction, eval_error("no such overload", TypeError)( - celpy.celtypes.logical_or) + celpy.celtypes.logical_or + ), + ) + reduction = reduce( + or_oper, map(sub_expr, member_list), celpy.celtypes.BoolType(False) ) - reduction = reduce(or_oper, map(sub_expr, member_list), celpy.celtypes.BoolType(False)) return reduction elif method_name_token.value == "exists_one": diff --git a/tests/test_evaluation.py b/tests/test_evaluation.py index c5d8bab..2a36cda 100644 --- a/tests/test_evaluation.py +++ b/tests/test_evaluation.py @@ -548,8 +548,24 @@ def bad_condition(a, b, c): activation, functions={"_?_:_": bad_condition} ) - with raises(celpy.evaluation.CELEvalError): + with raises(celpy.evaluation.CELEvalError) as exc_info: evaluator.evaluate() + assert exc_info.value.args == ("found no matching overload for _?_:_ applied to '(, , )'", TypeError, ()) + +def test_eval_expr_3_bad_cond_value(mock_left_expr_tree): + def bad_condition(a, b, c): + raise celpy.evaluation.CELEvalError("Baseline Error") + activation = Mock() + evaluator = Evaluator( + mock_left_expr_tree, + activation, + functions={"_?_:_": bad_condition} + ) + with raises(celpy.evaluation.CELEvalError) as exc_info: + evaluator.evaluate() + print(repr(exc_info.value.args)) + assert exc_info.value.args == ('Baseline Error',) + @fixture def mock_right_expr_tree(): diff --git a/tox.ini b/tox.ini index f77b024..31dfa28 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ [tox] skipsdist = true -envlist = py38,py39,py310,py311,py312,lint +envlist = py39,py310,py311,py312,py313,lint minversion = 4.15.0 [testenv] @@ -36,6 +36,7 @@ commands = poetry run mypy --show-error-codes src [testenv:lint] +usedevelop = true depends = py312 basepython = python3.12 commands = diff --git a/type_check/lineprecision.txt b/type_check/lineprecision.txt index 5e06b3b..a95ae72 100644 --- a/type_check/lineprecision.txt +++ b/type_check/lineprecision.txt @@ -1,11 +1,11 @@ Name Lines Precise Imprecise Any Empty Unanalyzed ------------------------------------------------------------------- -celpy 293 76 0 4 213 0 -celpy.__main__ 465 172 7 42 244 0 -celpy.adapter 137 35 3 9 85 5 -celpy.c7nlib 1584 340 15 154 1075 0 -celpy.celparser 401 207 2 22 170 0 -celpy.celtypes 1503 438 14 221 791 39 -celpy.evaluation 2472 835 33 177 1411 16 +celpy 306 76 0 4 226 0 +celpy.__main__ 508 204 8 42 254 0 +celpy.adapter 141 34 3 9 89 6 +celpy.c7nlib 1663 351 14 149 1149 0 +celpy.celparser 388 199 3 22 164 0 +celpy.celtypes 1504 417 14 224 812 37 +celpy.evaluation 2595 872 38 177 1490 18 xlate 0 0 0 0 0 0 -xlate.c7n_to_cel 1730 377 102 145 1101 5 +xlate.c7n_to_cel 1755 385 103 146 1115 6