Closed amrabed closed 11 months ago
You define a function in Python like any other function and then you pass it into the thing that's being evaluated. That is to say that functions are passed in the same way as any other value.
>>> def add(a, b):
... return a+b
...
>>> thing = {'add': add}
>>>
exiting the edit console...
rule > add(1, 2)
result:
Decimal('3')
rule >
Additional functions can be either added them to the evaluated object or by extending the builtin symbols. It is only possible to call a function from within the rule text. Functions can not be defined as other data types can be.
Which statement there is confusing? If you have suggestions on how to clarify the docs, let me know.
Thanks for your reply, but that does not seem to work for the object case. Here is an example:
from rule_engine import Context, Rule, resolve_attribute
CONDITION = (
"is_valid_thing(things, 'thing1') ? things['thing1'] : is_valid_thing(things, 'thing2') ? things['thing2'] : null"
)
def is_valid_thing(things: dict, name: str) -> bool:
return name in things and things[name]["isValid"]
class Container:
def __init__(self, things: dict):
self.things = things
self.is_valid_thing = is_valid_thing
if __name__ == '__main__':
thing1 = {"name": "thing1", "isValid": True}
thing2 = {"name": "thing2", "isValid": False}
container = Container({"thing1": thing1, "thing2": thing2})
thing = Rule(CONDITION, context=Context(resolver=resolve_attribute)).evaluate(container)
print(thing)
I am getting this error:
rule_engine.errors.EvaluationError: data type mismatch (not a callable value)
complaining about this line in my code:
thing = Rule(CONDITION, context=Context(resolver=resolve_attribute)).evaluate(container)
Trace:
thing = Rule(CONDITION, context=Context(resolver=resolve_attribute)).evaluate(container)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../python3.11/site-packages/rule_engine/engine.py", line 546, in evaluate
return self.statement.evaluate(thing)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../python3.11/site-packages/rule_engine/ast.py", line 1111, in evaluate
return self.expression.evaluate(thing)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../python3.11/site-packages/rule_engine/ast.py", line 1149, in evaluate
case = (self.case_true if self.condition.evaluate(thing) else self.case_false)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File ".../python3.11/site-packages/rule_engine/ast.py", line 1050, in evaluate
raise errors.EvaluationError('data type mismatch (not a callable value)')
rule_engine.errors.EvaluationError: data type mismatch (not a callable value)
Not sure what I am missing or doing wrong here. I have also tried to define it as a member function and a static member function, but none seems to work
Replacing my condition with this one (not using a function) seems to be working fine though:
CONDITION = (
"'thing1' in things and things['thing1']['isValid'] ? things['thing1'] : "
"'thing2' in things and things['thing2']['isValid'] ? things['thing2'] : null"
)
Thanks for the test case. I'll look into it this weekend. It's possible there's a bug because what you're doing looks correct.
This isn't a problem with the function definition being on an object instead of a container. If the condition is simply is_valid_thing(things, 'thing1')
, it works as intended. There appears to be a parsing error possibly related to the order of operations with either the function call or the embedded ternary operator. Things also work as intended if you add quotes to false expression of the first ternary operator like this: is_valid_thing(things, 'thing1') ? things['thing1'] : (is_valid_thing(things, 'thing2') ? things['thing2'] : null)
.
Yeah this was an issue with the precedence of the parenthesis in the parser. They had not been defined with a rule and thus weren't being parsed correctly leading to the issue you identified.
This issue was fixed in version 4.0.1 which is now available on PyPi. Thank you for bringing it to my attention.
Thank you so much for looking into this and for the quick fix. Will test the update today
It's not clear from the documentation how/where to define a new custom function when the rule is to be applied to an object. Would you please provide an example of such a case or point me to where I can find one?
Also, this part of the documentation about FUNCTION is a bit confusing: