Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions features/error_propagation.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
Feature: error_propagation
Tests to ensure errors propagate how they are supposed to

Scenario: equal

When CEL expression '{}.a == 1' is evaluated
Then eval_error is "no such member"

Scenario: not_equal

When CEL expression '{}.a != 1' is evaluated
Then eval_error is "no such member"

Scenario: greater_than

When CEL expression '{}.a > 1' is evaluated
Then eval_error is "no such member"

Scenario: greater_than_or_equal

When CEL expression '{}.a >= 1' is evaluated
Then eval_error is "no such member"

Scenario: less_than

When CEL expression '{}.a > 1' is evaluated
Then eval_error is "no such member"

Scenario: less_than_or_equal

When CEL expression '{}.a >= 1' is evaluated
Then eval_error is "no such member"

Scenario: add

When CEL expression '{}.a + 1' is evaluated
Then eval_error is "no such member"

Scenario: subtract

When CEL expression '{}.a - 1' is evaluated
Then eval_error is "no such member"

Scenario: multiply

When CEL expression '{}.a * 1' is evaluated
Then eval_error is "no such member"

Scenario: divide

When CEL expression '{}.a / 1' is evaluated
Then eval_error is "no such member"

Scenario: modulo

When CEL expression '{}.a % 1' is evaluated
Then eval_error is "no such member"

Scenario: in

When CEL expression '{}.a in [1]' is evaluated
Then eval_error is "no such member"

Scenario: function

When CEL expression 'size({}.a)' is evaluated
Then eval_error is "no such member"

Scenario: method

When CEL expression '{}.a.size()' is evaluated
Then eval_error is "no such member"

Scenario: not

When CEL expression '!{}.a' is evaluated
Then eval_error is "no such member"

Scenario: and_error

When CEL expression '{}.a && true' is evaluated
Then eval_error is "no such member"

Scenario: and_ignore

When CEL expression '{}.a && false' is evaluated
Then eval_error is None

Scenario: or_error

When CEL expression '{}.a || false' is evaluated
Then eval_error is "no such member"

Scenario: or_ignore

When CEL expression '{}.a || true' is evaluated
Then eval_error is None

Scenario: all_error

When CEL expression '[{"a": 1}, {}].all(v, v.a == 1)' is evaluated
Then eval_error is "no such member"

Scenario: all_ignore

When CEL expression '[{"a": 1}, {}].all(v, v.a == 2)' is evaluated
Then eval_error is None

Scenario: exists_error

When CEL expression '[{"a": 1}, {}].exists(v, v.a == 2)' is evaluated
Then eval_error is "no such member"

Scenario: exists_ignore

When CEL expression '[{"a": 1}, {}].exists(v, v.a == 1)' is evaluated
Then eval_error is None

Scenario: exists_one_error

When CEL expression '[{"a": 1}, {}].exists_one(v, v.a == 1)' is evaluated
Then eval_error is "no such member"
2 changes: 2 additions & 0 deletions src/celpy/celtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ def logical_not(x: Value) -> Value:
This could almost be `logical_or = evaluation.boolean(operator.not_)`,
but the definition would expose Python's notion of "truthiness", which isn't appropriate for CEL.
"""
if isinstance(x, Exception):
return x
if isinstance(x, BoolType):
result_value = BoolType(not x)
else:
Expand Down
17 changes: 16 additions & 1 deletion src/celpy/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,11 @@ def boolean(
def bool_function(
a: celpy.celtypes.Value, b: celpy.celtypes.Value
) -> celpy.celtypes.BoolType:
if isinstance(a, CELEvalError):
return a
if isinstance(b, CELEvalError):
return b

result_value = function(a, b)
if result_value == NotImplemented:
return cast(celpy.celtypes.BoolType, result_value)
Expand Down Expand Up @@ -323,7 +328,9 @@ def operator_in(item: Result, container: Result) -> Result:

- True. There was a item found. Exceptions may or may not have been found.
- False. No item found AND no exceptions.
- CELEvalError. No item found AND at least one exception.
- CELEvalError. Either:
- No item found AND at least one exception or
- The input item or container itself was already an error

To an extent this is a little like the ``exists()`` macro.
We can think of ``container.contains(item)`` as ``container.exists(r, r == item)``.
Expand All @@ -333,6 +340,11 @@ def operator_in(item: Result, container: Result) -> Result:

``reduce(logical_or, (item == c for c in container), BoolType(False))``
"""
if isinstance(item, CELEvalError):
return item
if isinstance(container, CELEvalError):
return container

result_value: Result = celpy.celtypes.BoolType(False)
for c in cast(Iterable[Result], container):
try:
Expand Down Expand Up @@ -1547,6 +1559,9 @@ def function_eval(

try:
list_exprlist = cast(List[Result], exprlist or [])
for expr in list_exprlist:
if isinstance(expr, CELEvalError):
return expr
return function(*list_exprlist)
except ValueError as ex:
value = CELEvalError(
Expand Down
Loading