class Rule: ''' In-memory representation of logic-engine rule, relying on parsing utilities in BooleanExpression. ''' def __init__(self, rule_name, rule_cfg): self.name = rule_name self.expr = BooleanExpression(rule_cfg["expression"]) self.actions = rule_cfg.get("actions") self.false_actions = rule_cfg.get("false_actions") self.new_facts = rule_cfg.get("facts") def evaluate(self, evaluated_facts): """ Evaluates a compiled expression given the supplied facts. :type evaluated_facts: dict :arg evaluated_facts: Initial fact values (e.g. True or False) for each fact name. :rtype: bool """ return self.expr.evaluate(evaluated_facts) def __str__(self): # pragma: no cover return f"name: {self.name}\n" f"expression: '{self.expr}'\n" f"actions: {self.actions}\n" f"false_actions: {self.false_actions}\n" f"facts: {self.new_facts}"
def __init__(self, cfg): super().__init__(cfg) self.logger = logging.getLogger() self.facts = { name: BooleanExpression(expr) for name, expr in cfg["facts"].items() } self.rule_engine = RuleEngine(cfg["facts"].keys(), cfg["rules"])
def __init__(self, cfg): super().__init__(cfg) self.logger = structlog.getLogger(CHANNELLOGGERNAME) self.logger = self.logger.bind(class_module=__name__.split(".")[-1], channel=self.channel_name) self.facts = { name: BooleanExpression(expr) for name, expr in cfg["facts"].items() } self.rule_engine = RuleEngine(cfg["facts"].keys(), cfg["rules"])
def test_compound_fact(): fact = BooleanExpression("z < 100 and a == 4") assert set(fact.required_names) == {"z", "a"} assert fact.evaluate({"z": 50, "a": 4}) is True assert fact.evaluate({"z": 100, "a": 4}) is False assert fact.evaluate({"z": 200, "a": 4}) is False assert fact.evaluate({"z": 200, "a": 5}) is False assert fact.evaluate({"z": 100, "a": 5}) is False assert fact.evaluate({"z": 50, "a": 5}) is False
def __init__(self, rule_name, rule_cfg): self.name = rule_name self.expr = BooleanExpression(rule_cfg["expression"]) self.actions = rule_cfg.get("actions") self.false_actions = rule_cfg.get("false_actions") self.new_facts = rule_cfg.get("facts")
def test_fact_using_numpy_function(): fact = BooleanExpression("np.sum(vals) > 40") assert set(fact.required_names) == {"vals", "np"} with pytest.raises(Exception, match="name 'np' is not defined"): fact.evaluate(make_db(3))
def test_simple_fact(): fact = BooleanExpression("z < 100") assert fact.required_names == ["z"] assert fact.evaluate({"z": 50}) is True assert fact.evaluate({"z": 100}) is False assert fact.evaluate({"z": 200}) is False
def test_fact_using_numpy_array(): fact = BooleanExpression("vals.sum() > 40") assert fact.required_names == ["vals"] assert fact.evaluate(make_db(3)) is False assert fact.evaluate(make_db(10)) is True
def test_fact_with_nested_names(): fact = BooleanExpression("z > 100 and b.c == 10") assert set(fact.required_names) == {"z", "b"}
def test_fact_with_fail_on_error(): fact = BooleanExpression("fail_on_error(a[0])") assert set(fact.required_names) == {"a"}
def test_syntax_error(caplog): with pytest.raises(SyntaxError): BooleanExpression("z <") assert "The following expression string could not be parsed" in caplog.text