Implemented a first and basic version of a Rete rule engine

This commit is contained in:
2021-02-09 16:06:32 +01:00
parent 821dbed189
commit a2a8d5c5e5
110 changed files with 7301 additions and 1654 deletions
+11 -5
View File
@@ -3,7 +3,7 @@ from dataclasses import dataclass, field
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.concept import Concept, DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF, freeze_concept_attrs
from core.rule import Rule, ACTION_TYPE_PRINT
from core.rule import Rule, ACTION_TYPE_PRINT, ACTION_TYPE_EXEC
from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.Sheerka import Sheerka
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager
@@ -59,16 +59,22 @@ class InitTestHelper:
return self
def with_rules(self, *rules, **kwargs):
def with_format_rules(self, *rules, **kwargs):
return self.with_rules(ACTION_TYPE_PRINT, *rules, **kwargs)
def with_exec_rules(self, *rules, **kwargs):
return self.with_rules(ACTION_TYPE_EXEC, *rules, **kwargs)
def with_rules(self, action_type, *rules, **kwargs):
create_new = kwargs.get("create_new", True)
compile_rule = kwargs.get("compile_rule", True)
for rule_template in rules:
if isinstance(rule_template, tuple):
if len(rule_template) == 3:
rule = Rule(ACTION_TYPE_PRINT, rule_template[0], rule_template[1], rule_template[2])
rule = Rule(action_type, rule_template[0], rule_template[1], rule_template[2])
else:
rule = Rule(ACTION_TYPE_PRINT, None, rule_template[0], rule_template[1])
rule = Rule(action_type, None, rule_template[0], rule_template[1])
else:
rule = rule_template
@@ -150,7 +156,7 @@ class BaseTest:
return self.init_test(**init_test_args).with_concepts(*concepts, **with_concepts_args).unpack()
def init_format_rules(self, *rules, **kwargs):
return self.init_test(**kwargs).with_rules(*rules, **kwargs).unpack()
return self.init_test(**kwargs).with_format_rules(*rules, **kwargs).unpack()
@staticmethod
def get_concept_instance(sheerka, concept, **kwargs):
+12 -2
View File
@@ -1,4 +1,5 @@
from conftest import SHEERKA_TEST_FOLDER
from core.global_symbols import EVENT_ONTOLOGY_CREATED
from core.sheerka.Sheerka import Sheerka
from core.sheerka.SheerkaOntologyManager import SheerkaOntologyManager
@@ -10,19 +11,28 @@ class TestUsingFileBasedSheerka(BaseTest):
context = None
root_ontology_name = SheerkaOntologyManager.ROOT_ONTOLOGY_NAME
ontologies_created = set()
def teardown_method(self, method):
# to do after the test
if TestUsingFileBasedSheerka.sheerka:
while TestUsingFileBasedSheerka.sheerka.om.current_ontology().name != self.root_ontology_name:
ontology = TestUsingFileBasedSheerka.sheerka.pop_ontology().body.body
ontology = TestUsingFileBasedSheerka.sheerka.pop_ontology(self.context).body.body
ontology.cache_manager.sdp.test_only_destroy_refs()
# remove other created ontologies
for ontology in TestUsingFileBasedSheerka.ontologies_created:
TestUsingFileBasedSheerka.sheerka.om.current_sdp().test_only_destroy_refs(ontology)
TestUsingFileBasedSheerka.ontologies_created.clear()
@staticmethod
def new_sheerka_instance(cache_only):
sheerka = Sheerka(cache_only=cache_only)
sheerka.initialize(SHEERKA_TEST_FOLDER,
save_execution_context=False,
enable_process_return_values=False)
enable_process_return_values=False,
enable_process_rules=False)
sheerka.subscribe(EVENT_ONTOLOGY_CREATED, lambda c, o: TestUsingFileBasedSheerka.ontologies_created.add(o))
return sheerka
def get_sheerka(self, **kwargs) -> Sheerka:
+5 -2
View File
@@ -13,12 +13,15 @@ class TestUsingMemoryBasedSheerka(BaseTest):
# to do after the test
if TestUsingMemoryBasedSheerka.sheerka:
while TestUsingMemoryBasedSheerka.sheerka.om.current_ontology().name != self.root_ontology_name:
TestUsingMemoryBasedSheerka.sheerka.pop_ontology()
TestUsingMemoryBasedSheerka.sheerka.pop_ontology(self.context)
@staticmethod
def new_sheerka_instance(cache_only):
sheerka = Sheerka(cache_only=cache_only)
sheerka.initialize("mem://", save_execution_context=False, enable_process_return_values=False)
sheerka.initialize("mem://",
save_execution_context=False,
enable_process_return_values=False,
enable_process_rules=False)
return sheerka
def get_sheerka(self, **kwargs) -> Sheerka:
+11 -11
View File
@@ -65,9 +65,9 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
assert in_db == [ComparisonObj(context.event.get_digest(), "prop_name", two.str_id, one.str_id, ">", "#")]
def test_i_can_add_is_greater_than_for_rules(self):
sheerka, context, r1, r2 = self.init_test(cache_only=False).with_rules(("True", "true"),
("False", "false"),
compile_rule=False).unpack()
sheerka, context, r1, r2 = self.init_test(cache_only=False).with_format_rules(("True", "true"),
("False", "false"),
compile_rule=False).unpack()
service = sheerka.services[SheerkaComparisonManager.NAME]
@@ -106,10 +106,10 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
assert in_db == [ComparisonObj(context.event.get_digest(), "prop_name", one.str_id, two.str_id, "<", "#")]
def test_i_can_add_is_less_than_for_rules(self):
sheerka, context, r1, r2 = self.init_test(cache_only=False).with_rules(("True", "true"),
("False", "false"),
compile_rule=False,
create_new=True).unpack()
sheerka, context, r1, r2 = self.init_test(cache_only=False).with_format_rules(("True", "true"),
("False", "false"),
compile_rule=False,
create_new=True).unpack()
service = sheerka.services[SheerkaComparisonManager.NAME]
@@ -213,7 +213,7 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
assert sheerka.om.get(SheerkaComparisonManager.RESOLVED_COMPARISON_ENTRY, "prop_name|#") == expected_weights2
# I can retrieve the previous values
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert sheerka.om.get(SheerkaComparisonManager.COMPARISON_ENTRY, "prop_name|#") == expected_in_cache
assert sheerka.om.get(SheerkaComparisonManager.RESOLVED_COMPARISON_ENTRY, "prop_name|#") == expected_weights
@@ -544,9 +544,9 @@ class TestSheerkaGreaterThanManager(TestUsingMemoryBasedSheerka):
assert event_received
def test_an_event_is_fired_when_modifying_rule_precedence(self):
sheerka, context, r1, r2 = self.init_test(cache_only=False).with_rules(("True", "true"),
("False", "false"),
compile_rule=False).unpack()
sheerka, context, r1, r2 = self.init_test(cache_only=False).with_format_rules(("True", "true"),
("False", "false"),
compile_rule=False).unpack()
foo = Concept("foo")
event_received = False
sheerka.om.clear(SheerkaComparisonManager.COMPARISON_ENTRY)
+2 -2
View File
@@ -563,7 +563,7 @@ class TestSheerkaConceptManager(TestUsingMemoryBasedSheerka):
assert updated.get_metadata().variables == [("a", None), ("c", "value")]
assert updated.get_metadata().props == {BuiltinConcepts.ISA: {"bar"}}
sheerka.pop_ontology()
sheerka.pop_ontology(context)
def test_i_cannot_modify_without_any_modification(self):
sheerka, context, foo = self.init_concepts("foo")
@@ -726,7 +726,7 @@ class TestSheerkaConceptManager(TestUsingMemoryBasedSheerka):
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.CONCEPT_ALREADY_DEFINED)
sheerka.pop_ontology()
sheerka.pop_ontology(context)
# But I can if I remove the layer
res = sheerka.create_new_concept(context, Concept("bar"))
assert res.status
+10 -232
View File
@@ -1,12 +1,11 @@
import pytest
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept
from core.global_symbols import NotInit, NotFound
from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.services.SheerkaDebugManager import SheerkaDebugManager, DebugItem, ConceptDebugObj
from parsers.PythonParser import PythonNode
from sdp.sheerkaDataProvider import Event
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -21,220 +20,12 @@ class DummyObj:
class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
def test_i_can_activate_debug(self):
sheerka, context = self.init_concepts()
return_value_id = 0
sheerka.set_debug(context, True)
assert sheerka.debug_activated()
sheerka.set_debug(context, False)
assert not sheerka.debug_activated()
def test_when_debug_mode_is_activated_context_are_in_debug_mode(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
sheerka.set_debug(root_context, True)
context = root_context.push(BuiltinConcepts.NOP, None) # sub_context.id = 1
sub_context = context.push(BuiltinConcepts.NOP, None) # sub_sub_context.parent = 1
sub_context2 = context.push(BuiltinConcepts.NOP, None) # sub_sub_context2.parent = 1
sub_sub_context = sub_context.push(BuiltinConcepts.NOP, None) # is a child
assert context.id == 1
assert context.debug_enabled
assert sub_context.debug_enabled
assert sub_context2.debug_enabled
assert sub_sub_context.debug_enabled
assert not root_context.debug_enabled
def test_i_can_activate_debug_for_new_context(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, True)
sheerka.activate_debug_for(root_context, 1)
context = ExecutionContext("test", Event(), sheerka, BuiltinConcepts.TESTING, None) # context.id = 1
assert context.debug_enabled
def test_i_can_activate_debug_for_new_context_2(self):
"""
This time children is also requested
:return:
"""
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, True)
sheerka.activate_debug_for(root_context, 1, children=True)
context = ExecutionContext("test", Event(), sheerka, BuiltinConcepts.TESTING, None) # context.id = 1
assert context.debug_enabled
def test_i_can_activate_debug_for_a_context_using_push(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
service = sheerka.services[SheerkaDebugManager.NAME]
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, True)
sheerka.activate_debug_for(root_context, 1)
context = root_context.push(BuiltinConcepts.NOP, None) # sub_context.id = 1
sub_context = context.push(BuiltinConcepts.NOP, None) # sub_sub_context.parent = 1
assert context.id == 1
assert 1 in service.context_cache
assert context.debug_enabled
assert not sub_context.debug_enabled
assert not root_context.debug_enabled
def test_global_debug_must_be_activated_to_activate_context_debug(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
service = sheerka.services[SheerkaDebugManager.NAME]
sheerka.set_debug(root_context, False)
sheerka.activate_debug_for(root_context, 1)
context = root_context.push(BuiltinConcepts.NOP, None) # sub_context.id = 1
assert context.id == 1
assert 1 in service.context_cache
assert not context.debug_enabled
def test_i_can_activate_debug_for_sub_children(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
service = sheerka.services[SheerkaDebugManager.NAME]
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, True)
sheerka.activate_debug_for(root_context, 1, children=True)
context = root_context.push(BuiltinConcepts.NOP, None) # sub_context.id = 1
sub_context = context.push(BuiltinConcepts.NOP, None) # sub_sub_context.parent = 1
sub_context2 = context.push(BuiltinConcepts.NOP, None) # sub_sub_context2.parent = 1
sub_sub_context = sub_context.push(BuiltinConcepts.NOP, None) # is a child
assert context.id == 1
assert 1 in service.context_cache
assert "1+" in service.context_cache
assert context.debug_enabled
assert sub_context.debug_enabled
assert sub_context2.debug_enabled
assert sub_sub_context.debug_enabled
assert not root_context.debug_enabled
def test_i_can_deactivate_debug_for_a_context(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
service = sheerka.services[SheerkaDebugManager.NAME]
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, True)
sheerka.activate_debug_for(root_context, 1)
sheerka.deactivate_debug_for(root_context, 1)
context = root_context.push(BuiltinConcepts.NOP, None)
assert context.id == 1
assert 1 not in service.context_cache
assert not context.debug_enabled
def test_i_can_deactivate_context_but_not_children(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
service = sheerka.services[SheerkaDebugManager.NAME]
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, True)
sheerka.activate_debug_for(root_context, 1, True)
sheerka.deactivate_debug_for(root_context, 1)
context = root_context.push(BuiltinConcepts.NOP, None) # sub_context.id = 1
sub_context = context.push(BuiltinConcepts.NOP, None) # sub_sub_context.parent = 1
sub_context2 = context.push(BuiltinConcepts.NOP, None) # sub_sub_context2.parent = 1
sub_sub_context = sub_context.push(BuiltinConcepts.NOP, None) # is a child
assert context.id == 1
assert 1 not in service.context_cache
assert "1+" in service.context_cache
assert not context.debug_enabled
assert sub_context.debug_enabled
assert sub_context2.debug_enabled
assert sub_sub_context.debug_enabled
def test_i_can_deactivate_context_and_children(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
service = sheerka.services[SheerkaDebugManager.NAME]
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, True)
sheerka.activate_debug_for(root_context, 1)
sheerka.deactivate_debug_for(root_context, 1, children=True)
context = root_context.push(BuiltinConcepts.NOP, None) # sub_context.id = 1
sub_context = context.push(BuiltinConcepts.NOP, None) # sub_sub_context.parent = 1
sub_context2 = context.push(BuiltinConcepts.NOP, None) # sub_sub_context2.parent = 1
sub_sub_context = sub_context.push(BuiltinConcepts.NOP, None) # is a child
assert 1 not in service.context_cache
assert "1+" not in service.context_cache
assert not context.debug_enabled
assert not sub_context.debug_enabled
assert not sub_context2.debug_enabled
assert not sub_sub_context.debug_enabled
def test_i_can_activate_debug_for_a_variable(self):
sheerka, context = self.init_concepts()
service = sheerka.services[SheerkaDebugManager.NAME]
sheerka.set_debug(context)
sheerka.activate_debug_for(context, "Out")
assert "Out" in service.variable_cache
assert sheerka.debug_activated_for("Out")
sheerka.deactivate_debug_for(context, "Out")
assert "Out" not in service.variable_cache
assert not sheerka.debug_activated_for("Out")
def test_i_can_activate_debug_for_sub_children_using_the_simplified_form(self):
sheerka = self.get_sheerka()
ExecutionContext.ids.clear()
root_context = self.get_context(sheerka)
service = sheerka.services[SheerkaDebugManager.NAME]
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, True)
sheerka.activate_debug_for(root_context, "1+")
context = root_context.push(BuiltinConcepts.NOP, None) # sub_context.id = 1
sub_context = context.push(BuiltinConcepts.NOP, None) # sub_sub_context.parent = 1
sub_context2 = context.push(BuiltinConcepts.NOP, None) # sub_sub_context2.parent = 1
sub_sub_context = sub_context.push(BuiltinConcepts.NOP, None) # is a child
assert context.id == 1
assert 1 in service.context_cache
assert "1+" in service.context_cache
assert context.debug_enabled
assert sub_context.debug_enabled
assert sub_context2.debug_enabled
assert sub_sub_context.debug_enabled
assert not root_context.debug_enabled
@classmethod
def setup(cls):
sheerka = cls().get_sheerka()
cls.return_value_id = sheerka.get_by_key("__RETURN_VALUE").id
@pytest.mark.parametrize("item_type", [
"vars", "rules", "concepts"
@@ -631,9 +422,6 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
root_context = self.get_context(sheerka)
sheerka.set_debug(root_context, True)
sheerka.set_explicit(root_context, False)
sheerka.activate_debug_for(root_context, 1, children=True)
sheerka.activate_debug_for(root_context, "SomeVar")
sheerka.debug_var(root_context, "service_name.*.var")
sheerka.debug_rule(root_context, 1)
sheerka.debug_concept(root_context, 1001)
@@ -642,9 +430,6 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
another_service.initialize_deferred(root_context, True)
assert another_service.activated
assert not another_service.explicit
assert another_service.context_cache == {1, "1+"}
assert another_service.variable_cache == {"SomeVar"}
assert another_service.debug_vars_settings == [
DebugItem('var', 'service_name', None, None, False, None, False, True)]
assert another_service.debug_rules_settings == [
@@ -688,7 +473,7 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
python_node = PythonNode("one + 1").init_ast()
res = sheerka.inspect(context, python_node)
assert set(res.body.keys()) == {"#type#", 'ast_', 'ast_str', 'compiled', 'objects', 'source'}
assert set(res.body.keys()) == {"#type#", 'ast_', 'ast_str', 'compiled', 'objects', 'source', 'original_source'}
def test_i_can_inspect_object_specified_attributes(self):
sheerka, context = self.init_concepts()
@@ -872,7 +657,7 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
assert res.body == {
'body': concept_debug_obj,
'#type#': 'ReturnValueConcept',
'id': '43',
'id': f'{self.return_value_id}',
'key': '__RETURN_VALUE',
'name': '__RETURN_VALUE',
'parents': [concept_debug_obj],
@@ -920,21 +705,18 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
sheerka.push_ontology(context, "new ontology")
service.set_debug(context)
service.set_explicit(context)
service.debug_var(context, "var_service.var_method.var_name", "1+", 1)
service.debug_rule(context, "rule_service.rule_method.rule_name", "2+", 2)
service.debug_concept(context, "concept_service.concept_method.concept_name", "3+", 3)
# sanity check
assert service.activated
assert service.explicit
assert service.debug_vars_settings != []
assert service.debug_rules_settings != []
assert service.debug_concepts_settings != []
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert not service.activated
assert not service.explicit
assert service.context_cache == set()
assert service.variable_cache == set()
assert service.debug_vars_settings == []
@@ -946,7 +728,6 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
service = sheerka.services[SheerkaDebugManager.NAME]
service.set_debug(context)
service.set_explicit(context)
service.debug_var(context, "v_service.v_method.v_name", "1+", 1)
service.debug_rule(context, "r_service.r_method.r_name", "2+", 2)
service.debug_concept(context, "c_serv.c_method.c_name", "3+", 3)
@@ -955,7 +736,6 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
# modify the state
service.set_debug(context, False)
service.set_explicit(context, False)
service.debug_var(context, "var_service2.var_method2.var_name2", "11+", 11)
service.debug_rule(context, "rule_service2.rule_method2.rule_name2", "22+", 22)
service.debug_concept(context, "concept_service2.concept_method2.concept_name2", "33+", 33)
@@ -967,10 +747,8 @@ class TestSheerkaDebugManager(TestUsingMemoryBasedSheerka):
assert len(service.debug_rules_settings) == 2
assert len(service.debug_concepts_settings) == 2
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert service.activated
assert service.explicit
assert service.debug_vars_settings == [DebugItem("v_name", "v_service", "v_method", 1, True, 1, False, True)]
assert service.debug_rules_settings == [DebugItem("r_name", "r_service", "r_method", 2, True, 2, False, True)]
assert service.debug_concepts_settings == [DebugItem("c_name", "c_serv", "c_method", 3, True, 3, False, True)]
+179 -6
View File
@@ -1,15 +1,22 @@
import operator
from core.concept import Concept
from core.rule import Rule
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules, LOW_PRIORITY_RULES, DISABLED_RULES
import pytest
from core.builtin_concepts_ids import BuiltinConcepts
from core.concept import Concept, DEFINITION_TYPE_DEF
from core.rule import Rule, ACTION_TYPE_EXEC
from core.sheerka.Sheerka import RECOGNIZED_BY_ID, RECOGNIZED_BY_NAME
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules, LOW_PRIORITY_RULES, DISABLED_RULES
from core.sheerka.services.SheerkaExecute import ParserInput
from core.sheerka.services.SheerkaRuleManager import RuleCompiledPredicate, SheerkaRuleManager
from evaluators.PythonEvaluator import PythonEvaluator, Expando
from parsers.PythonParser import PythonParser
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
def test_i_can_evaluate_python_rules(self):
sheerka, context, r1, r2, r3, r4, r5, r6, r7, r8, r9 = self.init_test().with_rules(
sheerka, context, r1, r2, r3, r4, r5, r6, r7, r8, r9 = self.init_test().with_format_rules(
Rule(predicate="a == 1", action="", priority=1), # r1
Rule(predicate="a == 2", action="", priority=1), # r2
Rule(predicate="a == 3", action="", priority=0), # r3
@@ -32,9 +39,10 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
DISABLED_RULES: [r7]
}
def test_i_can_evaluate_concept_rules(self):
def test_i_can_evaluate_question_concept_rules(self):
sheerka, context, concept, r1, r2, r3, r4, r5, r6, r7, r8, r9 = self.init_test().with_concepts(
Concept("x equals y", body="x == y").def_var("x").def_var("y"), create_new=True).with_rules(
Concept("x equals y", body="x == y", pre="is_question()").def_var("x").def_var("y"),
create_new=True).with_format_rules(
Rule(predicate="a equals 1", action="", priority=1), # r1
Rule(predicate="a equals 2", action="", priority=1), # r2
Rule(predicate="a equals 3", action="", priority=0), # r3
@@ -58,6 +66,159 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
DISABLED_RULES: [r7]
}
@pytest.mark.parametrize("predicates", [
("True", "False"),
("False", "True"),
])
def test_i_can_eval_when_multiple_python_predicates(self, predicates):
"""
In this test, the rule has multiple predicates to evaluate
The test make sure that is one predicate is successful, the rule must be evaluated
"""
sheerka, context, my_rule = self.init_test().with_format_rules(Rule("my rule"),
compile_rule=False,
create_new=False).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
# create fake compiled predicates
parser = PythonParser()
my_rule.compiled_predicates = [
RuleCompiledPredicate("my rule", None, PythonEvaluator.NAME, parser.parse(context, ParserInput(exp)), None)
for exp in predicates]
my_rule.metadata.is_compiled = True
my_rule.metadata.is_enabled = True
res = service.evaluate_rules(context, [my_rule], {}, set())
assert res == {
True: [my_rule],
}
def test_i_can_evaluate_rules_when_concepts_are_questions(self):
sheerka, context, isa, cat, crocodile, pet, r1, r2, r3 = self.init_test().with_concepts(
Concept("x is a y", body="isa(x,y)", pre="is_question()").def_var("x").def_var("y"),
"cat",
"crocodile",
"pet",
create_new=True).with_format_rules(
Rule(predicate="cat is a pet", action=""),
Rule(predicate="crocodile is a pet", action=""),
Rule(predicate="not crocodile is a pet", action=""),
).unpack()
sheerka.set_isa(context, cat, pet)
service = sheerka.services[SheerkaEvaluateRules.NAME]
res = service.evaluate_rules(context, [r1, r2, r3], {}, set())
assert res == {True: [r1, r3], False: [r2]}
@pytest.mark.parametrize("predicate", [
"greetings",
"c:|1001:",
"hello 'kodjo'"
])
def test_i_can_evaluate_rules_when_concepts_are_not_questions(self, predicate):
"""
In this test, we evaluate rules that involves concepts that are not questions
"""
sheerka, context, greetings, rule = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True).with_format_rules(
Rule(predicate=predicate, action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a="kodjo"))
res = service.evaluate_rules(context, [rule], {"__ret": ret}, set())
assert res == {True: [rule]}
@pytest.mark.parametrize("recognized_by", [
RECOGNIZED_BY_ID,
RECOGNIZED_BY_NAME,
None
])
def test_i_can_evaluate_concept_rules_when_variable_is_a_one_word_concept(self, recognized_by):
"""
In this test, we evaluate rules that involves concepts that are not questions
"""
sheerka, context, greetings, there, rule = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("there"),
create_new=True).with_format_rules(
Rule(predicate="hello there", action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
there_instance = sheerka.new_from_template(there, there.key)
if recognized_by:
there_instance.set_hint(BuiltinConcepts.RECOGNIZED_BY, recognized_by)
ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a=there_instance))
res = service.evaluate_rules(context, [rule], {"__ret": ret}, set())
assert res == {True: [rule]}
@pytest.mark.parametrize("recognized_by", [
RECOGNIZED_BY_ID,
RECOGNIZED_BY_NAME,
None
])
def test_i_can_evaluate_concept_rules_when_variable_is_a_two_words_concept(self, recognized_by):
"""
In this test, we evaluate rules that involves concepts that are not questions
"""
sheerka, context, greetings, my_friend, rule = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("my friend"),
create_new=True).with_format_rules(
Rule(predicate="hello my friend", action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
my_friend_instance = sheerka.new_from_template(my_friend, my_friend.key)
if recognized_by:
my_friend_instance.set_hint(BuiltinConcepts.RECOGNIZED_BY, recognized_by)
ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a=my_friend_instance))
res = service.evaluate_rules(context, [rule], {"__ret": ret}, set())
assert res == {True: [rule]}
def test_i_can_evaluate_concept_rules_when_variable_is_an_expando(self):
sheerka, context, greetings, rule = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True).with_format_rules(
Rule(predicate="hello sheerka", action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a=Expando("sheerka", {})))
res = service.evaluate_rules(context, [rule], {"__ret": ret}, set())
assert res == {True: [rule]}
def test_i_can_evaluate_concept_rules_when_same_name(self):
sheerka, context, g1, g2, rule = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("greetings", definition="hi a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True).with_format_rules(
Rule(predicate="greetings", action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
ret1 = sheerka.ret("evaluator", True, sheerka.new(g1, a="kodjo"))
res = service.evaluate_rules(context, [rule], {"__ret": ret1}, set())
assert res == {True: [rule]}
ret2 = sheerka.ret("evaluator", True, sheerka.new(g2, a="kodjo"))
res = service.evaluate_rules(context, [rule], {"__ret": ret2}, set())
assert res == {True: [rule]}
def test_i_can_evaluate_concept_rule_with_the_same_name_when_the_second_concept_is_declared_after(self):
sheerka, context, g1, rule, g2 = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True).with_format_rules(Rule(predicate="greetings", action="")).with_concepts(
Concept("greetings", definition="hi a", definition_type=DEFINITION_TYPE_DEF).def_var("a")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
ret1 = sheerka.ret("evaluator", True, sheerka.new(g1, a="kodjo"))
res = service.evaluate_rules(context, [rule], {"__ret": ret1}, set())
assert res == {True: [rule]}
ret2 = sheerka.ret("evaluator", True, sheerka.new(g2, a="kodjo"))
res = service.evaluate_rules(context, [rule], {"__ret": ret2}, set())
assert res == {True: [rule]}
def test_i_can_disable_rules_at_runtime(self):
sheerka, context, r1, r2, = self.init_format_rules(
Rule(predicate="a == 1", action="", priority=2), # r1
@@ -72,3 +233,15 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
True: [r2],
DISABLED_RULES: [r1]
}
def test_rete_network_is_updated_on_new_rule_creation(self):
sheerka, context = self.init_test().unpack()
evaluate_rule_service = sheerka.services[SheerkaEvaluateRules.NAME]
rule_manager_service = sheerka.services[SheerkaRuleManager.NAME]
rule = Rule(ACTION_TYPE_EXEC, "test", "ret.status == True", "test()")
rule_manager_service.init_rule(context, rule)
sheerka.create_new_rule(context, rule)
assert rule in evaluate_rule_service.network.rules
assert rule.rete_net == evaluate_rule_service.network
+1 -1
View File
@@ -80,6 +80,6 @@ example_of_class_method. event=xxx, data='42'
assert "my first topic" in service.subscribers
assert "my second topic" in service.subscribers
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert "my first topic" in service.subscribers
assert "my second topic" not in service.subscribers
@@ -122,7 +122,7 @@ class TestSheerkaFunctionsParametersHistory(TestUsingMemoryBasedSheerka):
3: [("'string value'", 1)]
})}
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert sheerka.om.copy(service.FUNCTIONS_PARAMETERS_ENTRY) == {"function": FunctionParametersObj(
context.event.get_digest(),
"function",
+1 -1
View File
@@ -322,7 +322,7 @@ class TestSheerkaIsAManager(TestUsingMemoryBasedSheerka):
assert sheerka.isa(foo, group2)
# I can revert back
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert sheerka.isaset(context, group1)
assert sheerka.isinset(foo, group1)
assert sheerka.isa(foo, group1)
+679 -83
View File
@@ -3,15 +3,21 @@ import ast
import pytest
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, CMV
from core.global_symbols import RULE_COMPARISON_CONTEXT
from core.concept import Concept, CMV, DEFINITION_TYPE_DEF, CC, DoNotResolve
from core.global_symbols import RULE_COMPARISON_CONTEXT, NotFound, EVENT_RULE_DELETED
from core.rule import Rule, ACTION_TYPE_PRINT, ACTION_TYPE_EXEC
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, FormatRuleParser, \
from core.sheerka.Sheerka import RECOGNIZED_BY_ID, RECOGNIZED_BY_NAME
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, FormatRuleActionParser, \
FormatAstRawText, FormatAstVariable, FormatAstSequence, FormatAstFunction, \
FormatRuleSyntaxError, FormatAstList, UnexpectedEof, FormatAstColor, RulePredicate, FormatAstDict, FormatAstMulti
FormatRuleSyntaxError, FormatAstList, UnexpectedEof, FormatAstColor, RuleCompiledPredicate, FormatAstDict, \
FormatAstMulti, \
PythonCodeEmitter, NoConditionFound, FormatAstNode
from core.sheerka.services.sheerka_service import FailedToCompileError
from core.tokenizer import Token, TokenKind
from parsers.BaseNodeParser import SourceCodeWithConceptNode, SourceCodeNode
from parsers.PythonParser import PythonNode
from sheerkarete.common import V
from sheerkarete.conditions import Condition, AndConditions
from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -32,7 +38,7 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
(ACTION_TYPE_EXEC, SheerkaRuleManager.EXEC_RULE_ENTRY),
])
def test_i_can_create_a_new_rule(self, action_type, cache_entry):
sheerka, context = self.init_concepts(cache_only=False)
sheerka, context = self.init_test(cache_only=False).unpack()
previous_rules_number = sheerka.om.get_all(sheerka.OBJECTS_IDS_ENTRY)[SheerkaRuleManager.RULE_IDS]
rule = Rule(action_type, "name", "True", "Hello world")
@@ -96,6 +102,114 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
sheerka.om.current_cache_manager().caches[
SheerkaRuleManager.FORMAT_RULE_ENTRY].cache) == 2 + previous_rules_number
@pytest.mark.parametrize("action_type, cache_entry", [
(ACTION_TYPE_PRINT, SheerkaRuleManager.FORMAT_RULE_ENTRY),
(ACTION_TYPE_EXEC, SheerkaRuleManager.EXEC_RULE_ENTRY),
])
def test_i_can_delete_a_rule(self, action_type, cache_entry):
sheerka, context, rule = self.init_test(cache_only=False).with_rules(
action_type,
Rule(action_type, "rule_name", "id.attr == 'value'", 'True')).unpack()
rule_to_delete = Rule(rule.compiled_action, rule_id=rule.id)
event_sink = []
self.sheerka.subscribe(EVENT_RULE_DELETED, lambda c, r: event_sink.append(r))
ret = sheerka.remove_rule(context, rule_to_delete)
assert ret.status
assert sheerka.om.get(cache_entry, rule.id) is NotFound
assert sheerka.om.get(SheerkaRuleManager.RULES_BY_NAME_ENTRY, rule.id) is NotFound
sheerka.om.commit(context)
assert sheerka.om.current_sdp().get(cache_entry, rule.id)
assert sheerka.om.current_sdp().get(SheerkaRuleManager.RULES_BY_NAME_ENTRY, rule.id)
assert event_sink == [rule]
def test_i_can_init_rule_with_a_exec_rule(self):
sheerka, context = self.init_test(cache_only=False).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
rule = Rule(ACTION_TYPE_EXEC, "name", "__input == 'hello world'", "'Hello back at you !'")
rule = service.init_rule(context, rule)
assert rule.metadata.is_compiled
assert rule.metadata.is_enabled
assert len(rule.compiled_predicates) == 1
assert len(rule.rete_disjunctions) == 1
assert sheerka.isinstance(rule.compiled_action, BuiltinConcepts.RETURN_VALUE)
assert rule.compiled_action.status
assert rule.error_sink is None
def test_i_can_init_rule_with_a_format_rule(self):
sheerka, context = self.init_test(cache_only=False).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
rule = Rule(ACTION_TYPE_PRINT, "name", "__input == 'hello world'", "Hello back at you !")
rule = service.init_rule(context, rule)
assert rule.metadata.is_compiled
assert rule.metadata.is_enabled
assert len(rule.compiled_predicates) == 1
assert len(rule.rete_disjunctions) == 1
assert isinstance(rule.compiled_action, FormatAstNode)
assert rule.error_sink is None
def test_i_do_not_init_rule_an_already_compiled_rule(self):
sheerka, context = self.init_test(cache_only=False).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
rule = Rule(ACTION_TYPE_EXEC, "name", "cannot build", "cannot compile either")
rule.metadata.is_compiled = True
rule = service.init_rule(context, rule)
assert rule.metadata.is_compiled
assert rule.error_sink is None # no error detected
@pytest.mark.parametrize("action_type, action", [
(ACTION_TYPE_EXEC, "'Hello back at you !'"),
(ACTION_TYPE_PRINT, "Hello back at you !"),
])
def test_init_rule_returns_errors_when_cannot_build_predicate(self, action_type, action):
sheerka, context = self.init_test(cache_only=False).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
rule = Rule(ACTION_TYPE_EXEC, "name", "cannot build", "'Hello back at you !'")
rule.metadata.is_enabled = True # it should be disabled
rule = service.init_rule(context, rule)
assert len(rule.error_sink["when"]) > 0
assert sheerka.is_error(rule.error_sink["when"][0])
assert "print" not in rule.error_sink
assert "then" not in rule.error_sink
assert rule.metadata.is_compiled
assert not rule.metadata.is_enabled
assert rule.compiled_predicates is None
assert rule.rete_disjunctions is None
@pytest.mark.parametrize("action_type, action", [
(ACTION_TYPE_EXEC, "cannot build action"),
(ACTION_TYPE_PRINT, "list("),
])
def test_init_rule_returns_error_when_cannot_build_action(self, action_type, action):
sheerka, context = self.init_test(cache_only=False).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
rule = Rule(action_type, "name", "__input == 'hello world'", action)
rule.metadata.is_enabled = True # it should be disabled
rule = service.init_rule(context, rule)
other_action_type = ACTION_TYPE_PRINT if action_type == ACTION_TYPE_EXEC else ACTION_TYPE_EXEC
assert sheerka.is_error(rule.error_sink[action_type])
assert other_action_type not in rule.error_sink
assert "when" not in rule.error_sink
assert rule.metadata.is_compiled
assert not rule.metadata.is_enabled
assert rule.compiled_action is None
@pytest.mark.parametrize("text, expected", [
("", FormatAstRawText("")),
(" ", FormatAstRawText(" ")),
@@ -134,7 +248,7 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
("multi(var_name)", FormatAstMulti("var_name")),
])
def test_i_can_parse_format_rule(self, text, expected):
assert FormatRuleParser(text).parse() == expected
assert FormatRuleActionParser(text).parse() == expected
@pytest.mark.parametrize("text, expected_error", [
("{", UnexpectedEof("while parsing variable", Token(TokenKind.LBRACE, "{", 0, 1, 1))),
@@ -156,15 +270,52 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
("dict()", FormatRuleSyntaxError("variable name not found", None)),
])
def test_i_cannot_parse_invalid_format(self, text, expected_error):
parser = FormatRuleParser(text)
parser = FormatRuleActionParser(text)
parser.parse()
assert parser.error_sink == expected_error
@pytest.mark.parametrize("text", [
"__action == 'some_action' and True",
"__action == 'some_action' and not a question",
"__action == 'some_action' and is a question",
])
def test_i_can_compile_predicate_when_action_is_provided(self, text):
sheerka, context, *concepts = self.init_test().with_concepts(
"a question",
Concept("is a question", pre='is_question()'),
create_new=True).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].action == 'some_action'
def test_i_can_compile___action_is_not_part_of_the_predicates(self):
sheerka, context, *concepts = self.init_concepts("foo")
service = sheerka.services[SheerkaRuleManager.NAME]
ast_ = ast.parse("a == 5", "<source>", 'eval')
expected_python_node = PythonNode('a == 5', ast_)
compiled_result = service.compile_when(context, "test", "__action == 'some action' and a == 5")
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate) == expected_python_node
assert res[0].concept is None
@pytest.mark.parametrize("text", [
"a == 5",
"foo == 5",
"foo > 5",
"func() == 5",
"not a == 5",
"not foo > 5",
"not func() == 5",
])
def test_i_can_compile_predicate_when_pure_python(self, text):
sheerka, context, *concepts = self.init_concepts("foo")
@@ -172,10 +323,11 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
ast_ = ast.parse(text, "<source>", 'eval')
expected_python_node = PythonNode(text, ast_)
res = service.compile_when(context, "test", text)
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RulePredicate)
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate) == expected_python_node
@@ -184,6 +336,8 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("text, expected_type", [
("isinstance(a, int)", SourceCodeWithConceptNode),
("func()", SourceCodeNode),
("not isinstance(a, int)", PythonNode),
("not func()", PythonNode),
])
def test_i_can_compile_predicates_that_resolve_to_python(self, text, expected_type):
sheerka, context, *concepts = self.init_concepts()
@@ -191,30 +345,15 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
ast_ = ast.parse(text, "<source>", 'eval')
expected_python_node = PythonNode(text, ast_)
res = service.compile_when(context, "test", text)
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RulePredicate)
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert isinstance(sheerka.objvalue(res[0].predicate), expected_type)
assert sheerka.objvalue(res[0].predicate).python_node == expected_python_node
assert res[0].concept is None
def test_i_can_compile_predicate_when_python_and_concept(self):
sheerka, context, *concepts = self.init_test().with_concepts(Concept("foo bar"), create_new=True).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
text = "foo bar == 5"
ast_ = ast.parse("__C__foo0bar__1001__C__ == 5", "<source>", 'eval')
resolved_expected = PythonNode(text, ast_)
res = service.compile_when(context, "test", text)
assert len(res) == 1
assert isinstance(res[0], RulePredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate) == resolved_expected
assert sheerka.objvalue(res[0].predicate).get_python_node() == expected_python_node
assert res[0].concept is None
@pytest.mark.parametrize("text, expected_variables", [
@@ -234,15 +373,106 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
expected = concepts[0]
expected.get_metadata().variables = [('x', expected_variables[0]), ('y', expected_variables[1])]
res = service.compile_when(context, "test", text)
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RulePredicate)
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == CONCEPT_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate) == expected
assert res[0].concept == expected
@pytest.mark.parametrize("text, text_to_compile, expected_variables", [
("not cat is an animal", "not __C__00var0000is0an000var001__1001__C__", ["cat", "animal"]),
("not a is an animal", "not __C__00var0000is0an000var001__1001__C__", ["a", "animal"]),
("not cat is an b", "not __C__00var0000is0an000var001__1001__C__", ["cat", "b"]),
])
def test_i_can_compile_negative_predicate_when_exact_concept(self, text, text_to_compile, expected_variables):
sheerka, context, *concepts = self.init_test().with_concepts(
Concept("x is an y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("cat"),
Concept("animal"),
create_new=True
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
expected_concept = concepts[0]
expected_concept.get_metadata().variables = [('x', expected_variables[0]), ('y', expected_variables[1])]
ast_ = ast.parse(text_to_compile, "<source>", 'eval')
expected_python_node = PythonNode(text_to_compile, ast_)
expected_python_node.original_source = text
expected_python_node.objects = {"__C__00var0000is0an000var001__1001__C__": expected_concept}
res = service.compile_when(context, "test", text)
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert isinstance(sheerka.objvalue(res[0].predicate), PythonNode)
python_node = sheerka.objvalue(res[0].predicate).get_python_node()
assert python_node == expected_python_node
assert len(python_node.objects) == 1
assert python_node.objects["__C__00var0000is0an000var001__1001__C__"] == expected_concept
assert res[0].concept is None
@pytest.mark.skip("Not managed yet")
@pytest.mark.parametrize("text, text_to_compile, expected_variables", [
("not cat is an animal", "__C__00var0000is0an000var001__1001__C__", ["not cat", "animal"])
])
def test_i_can_compile_negative_predicate_when_exact_concept(self, text, text_to_compile, expected_variables):
sheerka, context, *concepts = self.init_test().with_concepts(
Concept("x is an y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("cat"),
Concept("animal"),
create_new=True
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
expected_concept = concepts[0]
expected_concept.get_metadata().variables = [('x', expected_variables[0]), ('y', expected_variables[1])]
ast_ = ast.parse(text_to_compile, "<source>", 'eval')
expected_python_node = PythonNode(text_to_compile, ast_)
expected_python_node.original_source = text
expected_python_node.objects = {"__C__00var0000is0an000var001__1001__C__": expected_concept}
res = service.compile_when(context, "test", text)
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert isinstance(sheerka.objvalue(res[0].predicate), PythonNode)
python_node = sheerka.objvalue(res[0].predicate).get_python_node()
assert python_node == expected_python_node
assert len(python_node.objects) == 1
assert python_node.objects["__C__00var0000is0an000var001__1001__C__"] == expected_concept
assert res[0].concept is None
@pytest.mark.parametrize("text, text_to_compile", [
("foo bar == 5", "__C__foo0bar__1001__C__ == 5"),
("not foo bar == 5", "not __C__foo0bar__1001__C__ == 5"),
])
def test_i_can_compile_predicate_when_python_and_concept(self, text, text_to_compile):
sheerka, context, *concepts = self.init_test().with_concepts(Concept("foo bar"), create_new=True).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
ast_ = ast.parse(text_to_compile, "<source>", 'eval')
resolved_expected = PythonNode(text_to_compile, ast_, text)
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
python_node = sheerka.objvalue(res[0].predicate).get_python_node()
assert python_node == resolved_expected
assert python_node.objects == {'__C__foo0bar__1001__C__': concepts[0]}
assert res[0].concept is None
@pytest.mark.parametrize("text, expected_variables", [
("a cat is an animal", ["a cat", "animal"]),
("a cat is an b", ["a cat", "b"]),
@@ -257,10 +487,11 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
service = sheerka.services[SheerkaRuleManager.NAME]
expected = CMV(concepts[0], x=expected_variables[0], y=expected_variables[1])
res = service.compile_when(context, "test", text)
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RulePredicate)
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == CONCEPT_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate)[0].concept == expected
@@ -275,15 +506,101 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
service = sheerka.services[SheerkaRuleManager.NAME]
expected = concepts[1]
res = service.compile_when(context, "test", "cat is an animal")
compiled_result = service.compile_when(context, "test", "cat is an animal")
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RulePredicate)
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == CONCEPT_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate)[0].concept == expected
assert res[0].concept == expected
def test_i_can_compile_predicate_when_mix_of_concepts_and_python(self):
sheerka, context, animal, cat, dog, pet, is_a, is_an = self.init_test().with_concepts(
Concept("animal"),
Concept("a cat"),
Concept("dog"),
Concept("pet"),
Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("x is an y", pre="is_question()", definition="('cat'|'bird')=x 'is an' animal").def_var("x"),
create_new=True
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
text = "a cat is a pet and bird is an animal and x > 5 and dog is a pet"
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
to_compile = '__C__00var0000is0a000var001__1005__C__ and __C__00var0000is0an0y__1006__C__ and x > 5 and __C__00var0000is0a000var001__1005_1__C__'
ast_ = ast.parse(to_compile, "<source>", 'eval')
expected_python_node = PythonNode(to_compile, ast_, text)
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
python_node = res[0].predicate.body.body
assert python_node == expected_python_node
assert python_node.objects == {
"__C__00var0000is0a000var001__1005__C__": CC(is_a, x=cat, y=pet),
"__C__00var0000is0an0y__1006__C__": CC(is_an, exclude_body=True, x=DoNotResolve("bird"), animal=animal),
"__C__00var0000is0a000var001__1005_1__C__": CMV(is_a, x="dog", y="pet"),
}
@pytest.mark.parametrize("text", [
"a and not b",
"not b and a",
"__ret and not __ret.status",
])
def test_i_can_compile_negative_conjunctions_when_pure_python(self, text):
sheerka, context, *concepts = self.init_concepts("foo")
service = sheerka.services[SheerkaRuleManager.NAME]
ast_ = ast.parse(text, "<source>", 'eval')
expected_python_node = PythonNode(text, ast_)
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate) == expected_python_node
assert res[0].concept is None
def test_i_can_compile_negative_conjunction_of_mix_of_concepts_and_python(self):
sheerka, context, animal, cat, dog, pet, is_a, is_an = self.init_test().with_concepts(
Concept("animal"),
Concept("a cat"),
Concept("dog"),
Concept("pet"),
Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("x is an y", pre="is_question()", definition="('cat'|'bird')=x 'is an' animal").def_var("x"),
create_new=True
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
text = "not a cat is a pet and not bird is an animal and not x > 5 and not dog is a pet"
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
to_compile = 'not __C__00var0000is0a000var001__1005__C__'
to_compile += ' and not __C__00var0000is0an0y__1006__C__'
to_compile += ' and not x > 5'
to_compile += ' and not __C__00var0000is0a000var001__1005_1__C__'
ast_ = ast.parse(to_compile, "<source>", 'eval')
expected_python_node = PythonNode(to_compile, ast_, text)
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
python_node = res[0].predicate.body.body
assert python_node == expected_python_node
assert python_node.objects == {
"__C__00var0000is0a000var001__1005__C__": CC(is_a, x=cat, y=pet),
"__C__00var0000is0an0y__1006__C__": CC(is_an, exclude_body=True, x=DoNotResolve("bird"), animal=animal),
"__C__00var0000is0a000var001__1005_1__C__": CMV(is_a, x="dog", y="pet"),
}
def test_i_can_compile_predicate_when_multiple_choices(self):
sheerka, context, *concepts = self.init_test().with_concepts(
Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
@@ -292,24 +609,185 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
res = service.compile_when(context, "test", "a is a b")
compiled_result = service.compile_when(context, "test", "a is a b")
res = compiled_result.compiled_predicates
assert len(res) == 2
assert isinstance(res[0], RulePredicate)
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == CONCEPT_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate)[0].concept == CMV(concepts[0], x="a", y="b")
assert res[0].concept == CMV(concepts[0], x="a", y="b")
assert isinstance(res[1], RulePredicate)
assert isinstance(res[1], RuleCompiledPredicate)
assert res[1].evaluator == CONCEPT_EVALUATOR_NAME
assert sheerka.isinstance(res[1].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[1].predicate)[0].concept == CMV(concepts[1], x="a", y="b")
assert res[1].concept == CMV(concepts[1], x="a", y="b")
def test_i_can_compile_predicate_when_mix_and_multiple_choices(self):
sheerka, context, *concepts = self.init_test().with_concepts(
Concept("animal"),
Concept("a cat"),
Concept("dog"),
Concept("pet"),
Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("x is a y", pre="is_question()", body="isa(x, y)").def_var("x").def_var("y"),
Concept("x is an y", pre="is_question()", definition="('cat'|'bird')=x 'is an' animal").def_var("x"),
create_new=True
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
text = "__action == 'value' and a cat is a pet and bird is an animal and x > 5 and dog is a pet"
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 4
trimmed_source = "a cat is a pet and bird is an animal and x > 5 and dog is a pet"
current_res = res[0]
assert isinstance(current_res, RuleCompiledPredicate)
assert current_res.evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(current_res.predicate, BuiltinConcepts.RETURN_VALUE)
python_source = "__C__00var0000is0a000var001__1005__C__ and __C__00var0000is0an0y__1007__C__ and x > 5 and __C__00var0000is0a000var001__1005_1__C__"
ast_ = ast.parse(python_source, "<source>", 'eval')
resolved_expected = PythonNode(python_source, ast_, trimmed_source)
assert sheerka.objvalue(current_res.predicate) == resolved_expected
assert current_res.concept is None
current_res = res[1]
assert isinstance(current_res, RuleCompiledPredicate)
assert current_res.evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(current_res.predicate, BuiltinConcepts.RETURN_VALUE)
python_source = "__C__00var0000is0a000var001__1005__C__ and __C__00var0000is0an0y__1007__C__ and x > 5 and __C__00var0000is0a000var001__1006__C__"
ast_ = ast.parse(python_source, "<source>", 'eval')
resolved_expected = PythonNode(python_source, ast_, trimmed_source)
assert sheerka.objvalue(current_res.predicate) == resolved_expected
assert current_res.concept is None
current_res = res[2]
assert isinstance(current_res, RuleCompiledPredicate)
assert current_res.evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(current_res.predicate, BuiltinConcepts.RETURN_VALUE)
python_source = "__C__00var0000is0a000var001__1006__C__ and __C__00var0000is0an0y__1007__C__ and x > 5 and __C__00var0000is0a000var001__1005__C__"
ast_ = ast.parse(python_source, "<source>", 'eval')
resolved_expected = PythonNode(python_source, ast_, trimmed_source)
assert sheerka.objvalue(current_res.predicate) == resolved_expected
assert current_res.concept is None
current_res = res[3]
assert isinstance(current_res, RuleCompiledPredicate)
assert current_res.evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(current_res.predicate, BuiltinConcepts.RETURN_VALUE)
python_source = "__C__00var0000is0a000var001__1006__C__ and __C__00var0000is0an0y__1007__C__ and x > 5 and __C__00var0000is0a000var001__1006_1__C__"
ast_ = ast.parse(python_source, "<source>", 'eval')
resolved_expected = PythonNode(python_source, ast_, trimmed_source)
assert sheerka.objvalue(current_res.predicate) == resolved_expected
assert current_res.concept is None
@pytest.mark.parametrize("text, mode, compiled_text", [
("greetings", "eval", f"__ret.status and isinstance(__ret.body, Concept) and __ret.body.name == 'greetings'"),
("c:|1001:", "eval", f"__ret.status and isinstance(__ret.body, Concept) and __ret.body.id == '1001'"),
("hello 'there'", "eval",
f"__ret.status and isinstance(__ret.body, Concept) and __ret.body.key == 'hello __var__0' and __ret.body.get_value('a') == 'there'"),
("hello there", "exec",
f"__x_00__ = __ret.body.get_value('a')\n__ret.status and isinstance(__ret.body, Concept) and __ret.body.key == 'hello __var__0' and isinstance(__x_00__, Concept) and __x_00__.key == 'there'"),
("hello my friend", "exec",
f"__x_00__ = __ret.body.get_value('a')\n__ret.status and isinstance(__ret.body, Concept) and __ret.body.key == 'hello __var__0' and isinstance(__x_00__, Concept) and __x_00__.key == 'my friend'"),
])
def test_i_can_compile_predicate_when_concept_is_not_a_question(self, text, mode, compiled_text):
sheerka, context, greetings, there, my_friend = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("there"),
Concept("my friend"),
create_new=True
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
ast_ = ast.parse(compiled_text, "<source>", mode)
expected_python_node = PythonNode(compiled_text, ast_)
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate) == expected_python_node
assert res[0].concept is None
def test_i_can_compile_predicate_when_concept_is_not_a_question_and_involves_sheerka(self):
sheerka, context, greetings = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
compiled_text = "__x_00__ = __ret.body.get_value('a')\n"
compiled_text += "__ret.status"
compiled_text += " and isinstance(__ret.body, Concept) and __ret.body.key == 'hello __var__0'"
compiled_text += " and isinstance(__x_00__, Expando) and __x_00__.get_name() == 'sheerka'"
ast_ = ast.parse(compiled_text, "<source>", "exec")
expected_python_node = PythonNode(compiled_text, ast_)
compiled_result = service.compile_when(context, "test", "hello sheerka")
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate) == expected_python_node
assert res[0].concept is None
@pytest.mark.skip("Not managed yet")
def test_i_can_compile_when_concept_starts_with_not(self):
sheerka, context, *concepts = self.init_test().with_concepts(
Concept("not a cheesecake", pre="is_question()"),
create_new=True).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
text = "not a cheesecake"
expected = concepts[0]
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.compiled_predicates
assert len(res) == 1
assert isinstance(res[0], RuleCompiledPredicate)
assert res[0].evaluator == CONCEPT_EVALUATOR_NAME
assert sheerka.isinstance(res[0].predicate, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].predicate) == expected
assert res[0].concept == expected
def test_i_cannot_compile_when_concept_is_not_a_question_and_has_unknown_variable(self):
sheerka, context, greetings = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True
).unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
with pytest.raises(FailedToCompileError) as ex:
service.compile_when(context, "test", "hello there")
assert sheerka.isinstance(ex.value.cause[0], BuiltinConcepts.CONCEPT_EVAL_ERROR)
@pytest.mark.parametrize("text, expected_error", [
("__action == 'some_action'", NoConditionFound())
])
def test_i_cannot_compile_when_error(self, text, expected_error):
sheerka, context = self.init_test().unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
with pytest.raises(FailedToCompileError) as ex:
service.compile_when(context, "test", text)
assert ex.value.cause == [expected_error]
def test_i_can_get_rule_priorities(self):
sheerka, context, rule_true, rule_false = self.init_test().with_rules(("True", "True"),
("False", "False")).unpack()
sheerka, context, rule_true, rule_false = self.init_test().with_format_rules(("True", "True"),
("False", "False")).unpack()
sheerka.set_is_greater_than(context, BuiltinConcepts.PRECEDENCE,
rule_true,
@@ -322,7 +800,7 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
assert rules_from_cache[rule_false.id].priority == 1
def test_i_can_get_and_retrieve_rules_when_multiple_ontology_layers(self):
sheerka, context, rule_true = self.init_test().with_rules(("true", "True", "True")).unpack()
sheerka, context, rule_true = self.init_test().with_format_rules(("true", "True", "True")).unpack()
sheerka.push_ontology(context, "new ontology")
rule_false = sheerka.create_new_rule(context, Rule(ACTION_TYPE_EXEC, "false", "False", "False")).body.body
@@ -331,12 +809,12 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
assert sheerka.get_rule_by_id(rule_true.id) == rule_true
assert sheerka.get_rule_by_id(rule_false.id) == rule_false
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert sheerka.get_rule_by_id(rule_true.id) == rule_true
assert not sheerka.is_known(sheerka.get_rule_by_id(rule_false.id))
def test_i_can_resolve_rule(self):
sheerka, context, rule = self.init_test().with_rules(("my rule", "True", "True")).unpack()
sheerka, context, rule = self.init_test().with_format_rules(("my rule", "True", "True")).unpack()
context.add_to_short_term_memory("x", rule.id)
# direct access by id
@@ -370,57 +848,175 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
unresolved.metadata.id_is_unresolved = True
assert sheerka.resolve_rule(context, unresolved) == rule
# @pytest.mark.skip
# @pytest.mark.parametrize("text, expected", [
# ("cat is an animal", set()),
# ("a is an animal", {"a"}),
# ("a is an b", {"a", "b"}),
# ("cat is an b", {"b"}),
# ("a cat is an b", {"b"}),
#
# ("cat is a animal", set()),
# ("a is a animal", {"a"}),
# ("a is a b", {"a", "b"}),
# ("cat is a b", {"b"}),
# ("a cat is an b", {"b"}),
#
# ("a == 5", {"a"}),
# ("isinstance(a, int)", {"a"}),
# ("a cat == b", {"b"})
# ])
# def test_i_can_get_rules_variables(self, text, expected):
# sheerka, context, *concepts = self.init_concepts(
# Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
# Concept("x is a y", pre="is_question()", body="isa(x, y)").def_var("x").def_var("y"),
# Concept("x is an y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
# Concept("cat"),
# Concept("animal"),
# Concept("a cat"),
# create_new=True
# )
# service = sheerka.services[SheerkaRuleManager.NAME]
#
# compiled = service.compile_when(context, "test", "a is a b")
#
# assert service.get_unknown_variables(compiled) == expected
@pytest.mark.parametrize("obj, expected", [
("text value", "var == 'text value'"),
("text 'value'", '''var == "text 'value'"'''),
('text "value"', """var == 'text "value"'"""),
(10, "var == 10"),
(10.01, "var == 10.01"),
])
def test_i_can_test_python_code_emitter_for_basic_types(self, obj, expected):
sheerka, context = self.init_test().unpack()
assert PythonCodeEmitter(context).recognize(obj, "var").get_text() == expected
assert PythonCodeEmitter(context, "status").recognize(obj, "var").get_text() == "status and " + expected
@pytest.mark.parametrize("recognized_by, expected", [
(RECOGNIZED_BY_ID, "isinstance(var, Concept) and var.id == '1001'"),
(RECOGNIZED_BY_NAME, "isinstance(var, Concept) and var.name == 'greetings'"),
(None, "isinstance(var, Concept) and var.key == 'hello'"),
])
def test_i_can_test_python_code_emitter_for_concepts(self, recognized_by, expected):
sheerka, context, foo = self.init_concepts(
Concept("greetings", definition="hello", definition_type=DEFINITION_TYPE_DEF))
instance = sheerka.new_from_template(foo, foo.key)
if recognized_by:
instance.set_hint(BuiltinConcepts.RECOGNIZED_BY, recognized_by)
assert PythonCodeEmitter(context).recognize(instance, "var").get_text() == expected
assert PythonCodeEmitter(context, "status").recognize(instance, "var").get_text() == "status and " + expected
def test_i_can_test_python_code_emitter_for_concepts_with_variable(self):
sheerka, context, greetings, little, foo, bar, and_concept = self.init_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("little x").def_var("x"),
"foo",
"bar",
Concept("a and b").def_var("a").def_var("b")
)
# variable is a string
greetings_instance = sheerka.new_from_template(greetings, greetings.key, a='sheerka')
expected = "isinstance(var, Concept) and var.key == 'hello __var__0' and var.get_value('a') == 'sheerka'"
text = PythonCodeEmitter(context).recognize(greetings_instance, "var").get_text()
assert text == expected
# variable is a concept recognized by id
foo_instance = sheerka.new_from_template(foo, foo.key)
foo_instance.set_hint(BuiltinConcepts.RECOGNIZED_BY, RECOGNIZED_BY_ID)
greetings_instance = sheerka.new_from_template(greetings, greetings.key, a=foo_instance)
expected = """__x_00__ = var.get_value('a')
isinstance(var, Concept) and var.key == 'hello __var__0' and isinstance(__x_00__, Concept) and __x_00__.id == '1003'"""
text = PythonCodeEmitter(context).recognize(greetings_instance, "var").get_text()
assert text == expected
# variable is a concept recognized by name
foo_instance = sheerka.new_from_template(foo, foo.key)
foo_instance.set_hint(BuiltinConcepts.RECOGNIZED_BY, RECOGNIZED_BY_NAME)
greetings_instance = sheerka.new_from_template(greetings, greetings.key, a=foo_instance)
expected = """__x_00__ = var.get_value('a')
isinstance(var, Concept) and var.key == 'hello __var__0' and isinstance(__x_00__, Concept) and __x_00__.name == 'foo'"""
text = PythonCodeEmitter(context).recognize(greetings_instance, "var").get_text()
assert text == expected
# variable is a concept recognized by value
foo_instance = sheerka.new_from_template(foo, foo.key)
greetings_instance = sheerka.new_from_template(greetings, greetings.key, a=foo_instance)
expected = """__x_00__ = var.get_value('a')
isinstance(var, Concept) and var.key == 'hello __var__0' and isinstance(__x_00__, Concept) and __x_00__.key == 'foo'"""
text = PythonCodeEmitter(context).recognize(greetings_instance, "var").get_text()
assert text == expected
# variable is a concept witch has itself some variable
foo_instance = sheerka.new_from_template(foo, foo.key)
little_instance = sheerka.new_from_template(little, little.key, x=foo_instance)
greetings_instance = sheerka.new_from_template(greetings, greetings.key, a=little_instance)
expected = """__x_00__ = var.get_value('a')
__x_01__ = __x_00__.get_value('x')
isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
" and isinstance(__x_00__, Concept) and __x_00__.key == 'little __var__0'" + \
" and isinstance(__x_01__, Concept) and __x_01__.key == 'foo'"""
text = PythonCodeEmitter(context).recognize(greetings_instance, "var").get_text()
assert text == expected
# concept with multiple variables (which are themselves concepts)
foo_instance = sheerka.new_from_template(foo, foo.key)
bar_instance = sheerka.new_from_template(bar, bar.key)
little_instance = sheerka.new_from_template(little, little.key, x=foo_instance)
and_instance = sheerka.new_from_template(and_concept, and_concept.key, a=bar_instance, b=little_instance)
greetings_instance = sheerka.new_from_template(greetings, greetings.key, a=and_instance)
expected = """__x_00__ = var.get_value('a')
__x_01__ = __x_00__.get_value('a')
__x_02__ = __x_00__.get_value('b')
__x_03__ = __x_02__.get_value('x')
isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
" and isinstance(__x_00__, Concept) and __x_00__.key == '__var__0 and __var__1'" + \
" and isinstance(__x_01__, Concept) and __x_01__.key == 'bar'" + \
" and isinstance(__x_02__, Concept) and __x_02__.key == 'little __var__0'" + \
" and isinstance(__x_03__, Concept) and __x_03__.key == 'foo'"
text = PythonCodeEmitter(context).recognize(greetings_instance, "var").get_text()
assert text == expected
def test_i_can_get_format_rules(self):
sheerka, context = self.init_test().unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
sheerka.om.current_cache_manager().clear(service.FORMAT_RULE_ENTRY)
r1 = Rule(ACTION_TYPE_PRINT, "name 1", "True", "Hello world 1", priority=1)
r2 = Rule(ACTION_TYPE_PRINT, "name 2", "False", "Hello world 2", priority=3)
r3 = Rule(ACTION_TYPE_PRINT, "name 2", "None", "Hello world 3", priority=2)
sheerka.create_new_rule(context, r1)
sheerka.create_new_rule(context, r2)
sheerka.create_new_rule(context, r3)
res = sheerka.get_format_rules()
assert res == [r2, r3, r1]
def test_i_can_get_exec_rules(self):
sheerka, context = self.init_test().unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
sheerka.om.current_cache_manager().clear(service.EXEC_RULE_ENTRY)
r1 = Rule(ACTION_TYPE_EXEC, "name 1", "True", "'Hello world 1'", priority=1)
r2 = Rule(ACTION_TYPE_EXEC, "name 2", "False", "'Hello world 2'", priority=3)
r3 = Rule(ACTION_TYPE_EXEC, "name 2", "None", "'Hello world 3'", priority=2)
sheerka.create_new_rule(context, r1)
sheerka.create_new_rule(context, r2)
sheerka.create_new_rule(context, r3)
res = sheerka.get_exec_rules()
assert res == [r2, r3, r1]
def test_i_can_compile_rete_using_name(self):
sheerka, context, *concepts = self.init_test().unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
text = "__ret"
compiled_result = service.compile_when(context, "test", text)
res = compiled_result.rete_disjunctions
assert len(res) == 1
assert isinstance(res[0], AndConditions)
assert res[0].conditions == [Condition(V("__x_00__"), "__name__", "__ret")]
class TestSheerkaRuleManagerUsingFileBasedSheerka(TestUsingFileBasedSheerka):
def test_rules_are_initialized_at_startup(self):
sheerka, context, *rules = self.init_format_rules(
sheerka, context, *rules = self.init_test().with_rules(
None,
Rule("print", "name1", "True", "Hello world"),
Rule("print", "name2", "value() is __EXPLANATION", "list(value())")
)
Rule("print", "name2", "value() is __EXPLANATION", "list(value())"),
Rule("exec", "name3", "True", "'Hello world'"),
Rule("exec", "name4", "value() is __EXPLANATION", "list(value())"),
).unpack()
sheerka.set_is_greater_than(context, BuiltinConcepts.PRECEDENCE,
rules[0],
rules[1],
RULE_COMPARISON_CONTEXT)
sheerka.set_is_less_than(context, BuiltinConcepts.PRECEDENCE,
rules[2],
rules[3],
RULE_COMPARISON_CONTEXT)
sheerka.om.commit(context)
expected_rules_by_id = sheerka.om.get_all(SheerkaRuleManager.FORMAT_RULE_ENTRY)
expected_rules_by_id.update(sheerka.om.get_all(SheerkaRuleManager.EXEC_RULE_ENTRY))
sheerka = self.new_sheerka_instance(False) # new instance
rules_by_id = sheerka.om.get_all(SheerkaRuleManager.FORMAT_RULE_ENTRY)
rules_by_id.update(sheerka.om.get_all(SheerkaRuleManager.EXEC_RULE_ENTRY))
assert len(rules_by_id) == len(expected_rules_by_id)
@@ -432,10 +1028,10 @@ class TestSheerkaRuleManagerUsingFileBasedSheerka(TestUsingFileBasedSheerka):
assert rule.metadata.predicate == expected.metadata.predicate
assert rule.metadata.action == expected.metadata.action
assert rule.metadata.id == expected.metadata.id
assert rule.metadata.is_compiled == expected.metadata.is_compiled
assert rule.metadata.is_enabled == expected.metadata.is_enabled
assert rule.metadata.is_compiled
assert rule.metadata.is_enabled
assert rule.compiled_action == expected.compiled_action
assert rule.compiled_predicate == expected.compiled_predicate
assert rule.compiled_predicates == expected.compiled_predicates
assert rule.priority is not None
assert rule.priority == expected.priority
@@ -468,6 +1064,6 @@ class TestSheerkaRuleManagerUsingFileBasedSheerka(TestUsingFileBasedSheerka):
assert rule.metadata.is_compiled == expected.metadata.is_compiled
assert rule.metadata.is_enabled == expected.metadata.is_enabled
assert rule.compiled_action == expected.compiled_action
assert rule.compiled_predicate == expected.compiled_predicate
assert rule.compiled_predicates == expected.compiled_predicates
assert rule.priority is not None
assert rule.priority == expected.priority
+2 -2
View File
@@ -110,11 +110,11 @@ class TestSheerkaVariable(TestUsingMemoryBasedSheerka):
assert sheerka.load_internal_var("TestSheerkaVariable", "lambda")(10) == 12
# I can revert back
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert sheerka.load_var("TestSheerkaVariable", "my_variable") == 1
assert sheerka.load_internal_var("TestSheerkaVariable", "lambda")(10) == 11
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert sheerka.load_var("TestSheerkaVariable", "my_variable") == 1
assert sheerka.load_internal_var("TestSheerkaVariable", "lambda")(10) == 11
+28 -3
View File
@@ -1,10 +1,9 @@
import ast
import pytest
import core.builtin_helpers
import pytest
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
from core.concept import Concept
from core.global_symbols import NotInit
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -157,6 +156,32 @@ class TestBuiltinHelpers(TestUsingMemoryBasedSheerka):
res = core.builtin_helpers.resolve_ambiguity(context, concepts)
assert [c.name for c in res] == expected
@pytest.mark.parametrize("pre, expected", [
("x and y", False),
("is_question()", True),
(" is_question ( ) ", True),
("context.in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", True),
(" context . in_context ( BuiltinConcepts . EVAL_QUESTION_REQUESTED ) ", True),
(None, False),
("", False),
(NotInit, False),
("is _ question()", False),
("is_ question()", False),
("is _question()", False),
("context.in _context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", False),
("not is_question()", False),
("not context.in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", False),
("is_question() and True", True),
("is_question() and False", True), # don't care about the second argument if it is not related to question
("is_question() and xxx", True), # don't care about the second argument if it is not related to question
("is_question() and not is_question()", False), # error ?
("is_question() and not context.in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", False), # error ?
])
def test_is_a_question(self, pre, expected):
sheerka, context = self.init_test().unpack()
concept = Concept("foo", pre=pre)
assert core.builtin_helpers.is_a_question(context, concept) == expected
# @pytest.mark.parametrize("return_values", [
# None,
# []
+5 -5
View File
@@ -340,7 +340,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
sheerka.modify_concept(context, foo, to_add={"variables": {"c": None}}, to_remove={"variables": ["b"]})
assert get_concept_attrs(foo) == ["a", "c"]
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert get_concept_attrs(foo) == ["a", "b"]
def test_i_can_manage_concepts_ids_on_multiple_ontology_layers(self):
@@ -356,7 +356,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
res = sheerka.create_new_concept(context, Concept("baz"))
assert res.body.body.id == "1003"
sheerka.pop_ontology()
sheerka.pop_ontology(context)
res = sheerka.create_new_concept(context, Concept("baz"))
assert res.body.body.id == "1002"
@@ -380,7 +380,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
# record the ontology
ontology = sheerka.get_ontology(context)
sheerka.pop_ontology()
sheerka.pop_ontology(context)
# Create another ontology with some other values
sheerka.push_ontology(context, "another ontology")
@@ -403,7 +403,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
assert get_concept_attrs(foo) == ["a", "b"]
# sanity check
sheerka.pop_ontology()
sheerka.pop_ontology(context)
assert sheerka.get_by_name("foo") == foo2
assert sheerka.get_by_name("bar") == bar
assert sheerka.locals == {"key2": "value2"}
@@ -562,7 +562,7 @@ class TestSheerkaUsingFileBasedSheerka(TestUsingFileBasedSheerka):
sheerka, context = self.init_test().unpack()
sheerka.push_ontology(context, "to remove")
sheerka.pop_ontology()
sheerka.pop_ontology(context)
sheerka.push_ontology(context, "new ontology")
sheerka.push_ontology(context, "another ontology")
+2 -2
View File
@@ -21,7 +21,7 @@ class TestSheerkaResultManager(TestUsingMemoryBasedSheerka):
@classmethod
def teardown_class(cls):
cls.sheerka.pop_ontology()
cls.sheerka.pop_ontology(TestSheerkaResultManager.context)
cls.root_ontology_name = SheerkaOntologyManager.ROOT_ONTOLOGY_NAME
def init_service(self):
@@ -410,7 +410,7 @@ class TestSheerkaResultManagerFileBased(TestUsingFileBasedSheerka):
@classmethod
def teardown_class(cls):
cls.sheerka.pop_ontology()
cls.sheerka.pop_ontology(TestSheerkaResultManagerFileBased.context)
cls.root_ontology_name = SheerkaOntologyManager.ROOT_ONTOLOGY_NAME
def test_i_can_retrieve_the_last_error_after_startup(self):
+228 -32
View File
@@ -8,7 +8,9 @@ from cache.IncCache import IncCache
from cache.ListCache import ListCache
from cache.ListIfNeededCache import ListIfNeededCache
from core.concept import Concept
from core.global_symbols import NotFound, Removed
from core.global_symbols import NotFound, Removed, EVENT_CONCEPT_ID_DELETED, \
EVENT_RULE_ID_DELETED
from core.rule import Rule, ACTION_TYPE_EXEC
from core.sheerka.SheerkaOntologyManager import SheerkaOntologyManager, OntologyManagerFrozen, OntologyManagerNotFrozen, \
OntologyManagerCannotPopLatest, OntologyAlreadyExists
from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka
@@ -168,22 +170,22 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.current_cache_manager().has("cache_name", "key")
def test_i_cannot_pop_ontology_when_not_frozen(self):
sheerka = self.get_sheerka()
sheerka, context = self.init_test().unpack()
manager = SheerkaOntologyManager(sheerka, sheerka.root_folder, sheerka.cache_only)
with pytest.raises(OntologyManagerNotFrozen):
manager.pop_ontology()
manager.pop_ontology(context)
def test_i_cannot_pop_the_latest_cache_manager(self):
sheerka = self.get_sheerka()
sheerka, context = self.init_test().unpack()
manager = SheerkaOntologyManager(sheerka, sheerka.root_folder, sheerka.cache_only)
manager.freeze()
with pytest.raises(OntologyManagerCannotPopLatest):
manager.pop_ontology()
manager.pop_ontology(context)
def test_i_can_pop_ontology(self):
sheerka = self.get_sheerka()
sheerka, context = self.init_test().unpack()
manager = SheerkaOntologyManager(sheerka, sheerka.root_folder, sheerka.cache_only)
manager.freeze()
@@ -191,13 +193,13 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
manager.push_ontology("ontology2")
manager.push_ontology("ontology3")
manager.pop_ontology()
manager.pop_ontology(context)
assert len(manager.ontologies) == 3
manager.pop_ontology()
manager.pop_ontology()
manager.pop_ontology(context)
manager.pop_ontology(context)
with pytest.raises(OntologyManagerCannotPopLatest):
manager.pop_ontology()
manager.pop_ontology(context)
def test_i_can_add_ontology(self):
sheerka, context = self.init_test().unpack()
@@ -217,7 +219,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.get("cache_name", "key3") is NotFound
new_ontology = manager.get_ontology()
manager.pop_ontology()
manager.pop_ontology(context)
# add another ontology, with its own values
manager.push_ontology("another ontology")
@@ -250,7 +252,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.get_ontology("name4")
def test_i_can_access_values_after_push_and_pop_cache_only_true(self):
sheerka = self.get_sheerka(cache_only=True)
sheerka, context = self.init_test(cache_only=True).unpack()
manager = SheerkaOntologyManager(sheerka, sheerka.root_folder, sheerka.cache_only)
manager.register_cache("cache_name", Cache())
@@ -263,7 +265,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
manager.put("cache_name", "key", "value2")
assert manager.get("cache_name", "key") == "value2"
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value1"
def test_i_can_access_values_after_push_and_pop_cache_only_false(self):
@@ -296,7 +298,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.ontologies[1].cache_manager.sdp.state.data == {'cache_name': {'key': 'value1'}}
# remove a layer
manager.pop_ontology()
manager.pop_ontology(context)
assert not manager.current_cache_manager().has("cache_name", "key") # value is no longer in cache
assert manager.get("cache_name", "key") == "value1"
@@ -333,13 +335,13 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.get("cache_name", "key") == "value4"
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value3"
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value2"
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value1"
def test_i_have_access_to_sub_layers_values_cache_only_false(self):
@@ -368,6 +370,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
def test_i_can_get_value_from_all_layers(self):
sheerka = self.get_sheerka(cache_only=False)
context = self.get_context(sheerka)
manager = SheerkaOntologyManager(sheerka, sheerka.root_folder, sheerka.cache_only)
manager.register_cache("cache_name", Cache().auto_configure("cache_name"))
@@ -381,14 +384,15 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.get("cache_name", "key") == "value"
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value"
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value"
def test_i_can_only_get_top_layer_values_when_dictionary_cache(self):
sheerka = self.get_sheerka()
context = self.get_context(sheerka)
manager = SheerkaOntologyManager(sheerka, sheerka.root_folder, sheerka.cache_only)
manager.register_cache("cache_name", DictionaryCache().auto_configure("cache_name"))
@@ -407,7 +411,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.get_all("cache_name") == {"key": "value", "key1": "value1"}
# I can get back my values after pop
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.copy("cache_name") == {"key": "value"}
def test_dictionary_caches_values_are_copied_when_a_new_ontology_is_pushed(self):
@@ -728,7 +732,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.ontologies[1].cache_manager.sdp.state.data == {'cache_name': {"key": "value"}}
# The entry still exists in lower ontology
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value"
def test_i_can_remove_when_value_is_in_both_low_and_current_level(self):
@@ -764,7 +768,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.ontologies[1].cache_manager.sdp.state.data == {'cache_name': {"key": "value"}}
# The entry still exists in lower ontology
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value"
def test_i_can_remove_when_value_is_not_low_level(self):
@@ -830,8 +834,8 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.ontologies[2].cache_manager.sdp.state.data == {'cache_name': {"key": ["value", "value2"]}}
# The entry still exists in lower ontology
manager.pop_ontology()
manager.pop_ontology()
manager.pop_ontology(context)
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == ["value", "value2"]
def test_i_can_add_concept_default_layer(self):
@@ -941,7 +945,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.ontologies[1].cache_manager.sdp.get('by_id', foo.id) == foo
# so I can get the old values when I pop ontology
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("by_key", foo.key) == foo
assert manager.get("by_id", foo.id) == foo
@@ -990,7 +994,7 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.ontologies[1].cache_manager.sdp.get('by_id', foo.id) == foo
# so I can get the old values when I pop ontology
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("by_key", foo.key) == foo
assert manager.get("by_id", foo.id) == foo
@@ -1119,12 +1123,12 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.ontologies[2].cache_manager.sdp.get("by_key") == {foo.key: foo}
# So I can pop
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("by_id", foo.id) == foo
assert manager.get("by_key", foo.key) == foo
# and pop again
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("by_id", foo.id) == foo
assert manager.get("by_key", foo.key) == foo
@@ -1167,6 +1171,145 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
"key5": "value5"
}
def test_i_can_keep_track_of_created_concepts_by_ontologies(self):
sheerka, context, foo = self.init_concepts("foo", create_new=True)
def from_cache(entry):
return sheerka.om.self_cache_manager.copy(entry)
def from_db(entry):
return sheerka.om.self_cache_manager.sdp.get(entry)
# check that the new concept is tracked
assert from_cache(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {'#unit_test#': {'1001'}}
assert from_cache(SheerkaOntologyManager.ONTOLOGY_BY_CONCEPT_ENTRY) == {'1001': '#unit_test#'}
# add a new ontology and make sure the new concepts are tracked
sheerka.push_ontology(context, "new ontology")
sheerka.create_new_concept(context, Concept("bar"))
assert from_cache(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {'#unit_test#': {'1001'},
'new ontology': {'1002'}}
assert from_cache(SheerkaOntologyManager.ONTOLOGY_BY_CONCEPT_ENTRY) == {'1001': '#unit_test#',
'1002': 'new ontology'}
# commit the info and check the DB
sheerka.om.commit(context)
assert from_db(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {'#unit_test#': {'1001'},
'new ontology': {'1002'}, }
assert from_db(SheerkaOntologyManager.ONTOLOGY_BY_CONCEPT_ENTRY) == {'1001': '#unit_test#',
'1002': 'new ontology', }
# remove a concept a check
sheerka.remove_concept(context, sheerka.get_by_name("foo"))
assert from_cache(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {'new ontology': {'1002'}, }
assert from_cache(SheerkaOntologyManager.ONTOLOGY_BY_CONCEPT_ENTRY) == {'1002': 'new ontology', }
sheerka.remove_concept(context, sheerka.get_by_name("bar"))
assert from_cache(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {}
assert from_cache(SheerkaOntologyManager.ONTOLOGY_BY_CONCEPT_ENTRY) == {}
# commit again and check
sheerka.om.commit(context)
assert from_db(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {}
assert from_db(SheerkaOntologyManager.ONTOLOGY_BY_CONCEPT_ENTRY) == {}
def test_i_can_keep_track_of_created_rules_by_ontologies(self):
sheerka, context, rule1 = self.init_format_rules(("rule1", "id.attr == 'value'", "True"))
def rules_by_ontology_from_cache():
res = sheerka.om.self_cache_manager.copy(SheerkaOntologyManager.RULES_BY_ONTOLOGY_ENTRY)
del res[SheerkaOntologyManager.ROOT_ONTOLOGY_NAME] # discard builtin rules
return res
def ontologies_from_cache():
res = sheerka.om.self_cache_manager.copy(SheerkaOntologyManager.ONTOLOGY_BY_RULE_ENTRY)
return {k: v for k, v in res.items() if v != SheerkaOntologyManager.ROOT_ONTOLOGY_NAME}
def rules_by_ontology_from_db():
res = sheerka.om.self_cache_manager.sdp.get(SheerkaOntologyManager.RULES_BY_ONTOLOGY_ENTRY)
del res[SheerkaOntologyManager.ROOT_ONTOLOGY_NAME] # discard builtin rules
return res
def ontologies_from_db():
res = sheerka.om.self_cache_manager.sdp.get(SheerkaOntologyManager.ONTOLOGY_BY_RULE_ENTRY)
return {k: v for k, v in res.items() if v != SheerkaOntologyManager.ROOT_ONTOLOGY_NAME}
assert rules_by_ontology_from_cache() == {"#unit_test#": {rule1.id}}
assert ontologies_from_cache() == {rule1.id: "#unit_test#"}
# add a new rule from a new ontology and check
sheerka.push_ontology(context, "new ontology")
rule2 = Rule(ACTION_TYPE_EXEC, "rule2", "id2.attr2 == 'value'", "True")
sheerka.create_new_rule(context, rule2)
assert rules_by_ontology_from_cache() == {"#unit_test#": {rule1.id}, "new ontology": {rule2.id}}
assert ontologies_from_cache() == {rule1.id: "#unit_test#", rule2.id: "new ontology"}
# commit and check the result
sheerka.om.commit(context)
assert rules_by_ontology_from_db() == {"#unit_test#": {rule1.id}, "new ontology": {rule2.id}}
assert ontologies_from_db() == {rule1.id: "#unit_test#", rule2.id: "new ontology"}
sheerka.remove_rule(context, rule1)
assert rules_by_ontology_from_cache() == {"new ontology": {rule2.id}}
assert ontologies_from_cache() == {rule2.id: "new ontology"}
# remove the last rule
sheerka.remove_rule(context, rule2)
assert rules_by_ontology_from_cache() == {}
assert ontologies_from_cache() == {}
# commit and check the db
sheerka.om.commit(context)
assert rules_by_ontology_from_db() == {}
assert ontologies_from_db() == {}
def test_i_can_keep_track_of_created_concept_on_ontology_pop(self):
sheerka, context, foo = self.init_concepts("foo", create_new=True)
events_raised = set()
sheerka.subscribe(EVENT_CONCEPT_ID_DELETED, lambda ctx, c: events_raised.add(c))
def from_cache(entry):
return sheerka.om.self_cache_manager.copy(entry)
sheerka.push_ontology(context, "new ontology")
sheerka.create_new_concept(context, Concept("bar"))
sheerka.create_new_concept(context, Concept("baz"))
sheerka.pop_ontology(context)
assert from_cache(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {'#unit_test#': {'1001'}}
assert from_cache(SheerkaOntologyManager.ONTOLOGY_BY_CONCEPT_ENTRY) == {'1001': '#unit_test#'}
# check that the 'concept is deleted' events are raised
assert events_raised == {'1002', '1003'}
def test_i_can_keep_track_of_created_rules_on_ontology_pop(self):
sheerka, context, rule1 = self.init_format_rules(("rule1", "id.attr == 'value'", "True"))
events_raised = set()
sheerka.subscribe(EVENT_RULE_ID_DELETED, lambda ctx, r: events_raised.add(r))
def rules_by_ontology_from_cache():
res = sheerka.om.self_cache_manager.copy(SheerkaOntologyManager.RULES_BY_ONTOLOGY_ENTRY)
del res[SheerkaOntologyManager.ROOT_ONTOLOGY_NAME] # discard builtin rules
return res
def ontologies_from_cache():
res = sheerka.om.self_cache_manager.copy(SheerkaOntologyManager.ONTOLOGY_BY_RULE_ENTRY)
return {k: v for k, v in res.items() if v != SheerkaOntologyManager.ROOT_ONTOLOGY_NAME}
sheerka.push_ontology(context, "new ontology")
sheerka.create_new_rule(context, Rule(ACTION_TYPE_EXEC, "rule2", "id2.attr2 == 'value'", "True"))
sheerka.create_new_rule(context, Rule(ACTION_TYPE_EXEC, "rule3", "id3.attr3 == 'value'", "True"))
sheerka.pop_ontology(context)
assert rules_by_ontology_from_cache() == {'#unit_test#': {'10'}}
assert ontologies_from_cache() == {'10': '#unit_test#'}
# check that the 'rule is deleted' events are raised
assert events_raised == {'11', '12'}
# def test_i_can_list_by_key_when_dictionaries(self):
# sheerka = self.get_sheerka(cache_only=False)
# context = self.get_context(sheerka)
@@ -1268,7 +1411,6 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
# assert manager.list_by_key("cache_name", "key2") == ["e", "f", "g"]
# assert manager.list_by_key("cache_name", "key3") == ["a", "b", "c", "e", "f", "g"]
def test_i_can_get_call_when_a_cache_is_cleared(self):
sheerka = self.get_sheerka(cache_only=False)
context = self.get_context(sheerka)
@@ -1401,11 +1543,11 @@ class TestSheerkaOntology(TestUsingMemoryBasedSheerka):
assert manager.ontologies[2].cache_manager.get_cache("cache_name").copy() == {'key1': 'value1',
'key2': 'value2'}
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key1") == "new value1"
assert manager.get("cache_name", "key2") is NotFound
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key1") == "value1"
assert manager.get("cache_name", "key2") == "value2"
@@ -1453,9 +1595,63 @@ class TestSheerkaOntologyWithFileBasedSheerka(TestUsingFileBasedSheerka):
assert manager.get("cache_name", "key") == "value2"
manager.pop_ontology()
manager.pop_ontology(context)
assert manager.get("cache_name", "key") == "value1"
# put back the previous ontology
manager.push_ontology("new ontology")
assert manager.get("cache_name", "key") == "value2"
def test_i_can_remember_concept_and_rules_by_ontology(self):
sheerka, context, foo, r1 = self.init_test().with_concepts(
"foo",
create_new=True
).with_format_rules(
("rule1", "__ret", "True"),
).unpack()
sheerka.om.commit(context)
sheerka = self.new_sheerka_instance(False)
context = self.get_context(sheerka)
sheerka.create_new_concept(context, Concept("bar"))
r2 = sheerka.create_new_rule(context, Rule(ACTION_TYPE_EXEC, "rule2", "__ret.status", "True")).body.body
sheerka.om.commit(context)
sheerka.push_ontology(context, "new ontology")
sheerka.create_new_concept(context, Concept("baz"))
sheerka.create_new_rule(context, Rule(ACTION_TYPE_EXEC, "rule3", "id3.attr3 == 'value'", "True"))
sheerka.om.commit(context)
sheerka = self.new_sheerka_instance(False)
context = self.get_context(sheerka)
sheerka.push_ontology(context, "another ontology")
sheerka.create_new_concept(context, Concept("qux"))
r4 = sheerka.create_new_rule(context, Rule(ACTION_TYPE_EXEC, "rule4", "id4.attr4", "True")).body.body
sheerka.remove_concept(context, foo)
sheerka.remove_rule(context, r2)
sheerka.om.commit(context)
assert sheerka.om.self_cache_manager.copy(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {
'#unit_test#': {'1002'},
'another ontology': {'1004'},
}
assert sheerka.om.self_cache_manager.copy(SheerkaOntologyManager.RULES_BY_ONTOLOGY_ENTRY) == {
'#unit_test#': {r1.id},
'another ontology': {r4.id},
}
# in db
assert sheerka.om.self_cache_manager.sdp.get(SheerkaOntologyManager.CONCEPTS_BY_ONTOLOGY_ENTRY) == {
'#unit_test#': {'1002'},
'another ontology': {'1004'},
'new ontology': {'1003'}}
rules_from_db = sheerka.om.self_cache_manager.sdp.get(SheerkaOntologyManager.RULES_BY_ONTOLOGY_ENTRY)
del rules_from_db["__default__"]
assert rules_from_db == {
'#unit_test#': {'10'},
'another ontology': {'13'},
'new ontology': {'12'}}
+7 -7
View File
@@ -368,19 +368,19 @@ second : 'value d'
sheerka.print(lst)
captured = capsys.readouterr()
assert captured.out == """(1001)foo a b
a : 'value a'
b : 'value b'
id : '1001'
name: 'foo a b'
key : 'foo __var__0 __var__1'
a : 'value a'
b : 'value b'
body: **NotInit**
self: (1001)foo a b
(1001)foo a b
a : 'value c'
b : 'value d'
id : '1001'
name: 'foo a b'
key : 'foo __var__0 __var__1'
a : 'value c'
b : 'value d'
body: **NotInit**
self: (1001)foo a b
"""
@@ -404,6 +404,9 @@ self: (1001)foo a b
sheerka.print(lst)
captured = capsys.readouterr()
assert captured.out == """(1001)foo a b
id : '1001'
name: 'foo a b'
key : 'foo __var__0 __var__1'
a : 'value a'
b : {'a' : 'value a'
'beta' : {'b1': 10
@@ -425,9 +428,6 @@ b : {'a' : 'value a'
'empty': ()}
'h' : {'set' : {'set-a'}
'empty': {}}}
id : '1001'
name: 'foo a b'
key : 'foo __var__0 __var__1'
body: **NotInit**
self: (1001)foo a b
"""
+31 -2
View File
@@ -1,7 +1,8 @@
from dataclasses import dataclass
import core.utils
import pytest
import core.utils
from core.builtin_concepts import BuiltinConcepts
from core.builtin_helpers import evaluate_expression
from core.concept import Concept
@@ -111,7 +112,7 @@ def test_i_can_get_sub_classes():
([['a'], ['b']], ['c', 'd', 'e'], [['a', 'c'], ['b', 'c'], ['a', 'd'], ['b', 'd'], ['a', 'e'], ['b', 'e']]),
])
def test_i_can_product(a, b, expected):
res = core.utils.product(a, b)
res = core.utils.sheerka_product(a, b)
assert res == expected
@@ -425,3 +426,31 @@ def test_i_can_deep_copy_a_custom_type():
assert core.utils.sheerka_deepcopy(NotInit) is NotInit
assert core.utils.sheerka_deepcopy(NotFound) is NotFound
assert core.utils.sheerka_deepcopy(Removed) is Removed
@pytest.mark.parametrize("expression1, expression2, expected", [
("foo bar baz", "foo bar baz", True),
("foo()", " foo ( ) ", True),
("is_instance()", "is _ instance()", False),
("foo bar baz", "foo bar", False)
])
def test_tokens_are_matching(expression1, expression2, expected):
assert core.utils.tokens_are_matching(Tokenizer(expression1), Tokenizer(expression2)) == expected
def test_tokens_are_matching_when_no_eof():
expression1 = "foo bar baz"
expression2 = "foo bar"
tokens1 = Tokenizer(expression1, yield_eof=False)
tokens2 = Tokenizer(expression2, yield_eof=False)
assert not core.utils.tokens_are_matching(tokens1, tokens2)
def test_tokens_are_matching_when_eof_differs():
expression1 = "foo bar baz"
expression2 = "foo bar baz"
tokens1 = Tokenizer(expression1, yield_eof=True)
tokens2 = Tokenizer(expression2, yield_eof=False)
assert core.utils.tokens_are_matching(tokens1, tokens2)
+60
View File
@@ -0,0 +1,60 @@
import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.rule import Rule, RuleMetadata
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Tokenizer
from evaluators.DefRuleEvaluator import DefRuleEvaluator
from parsers.DefRuleParser import DefFormatRuleNode, DefRuleParser, DefExecRuleNode
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestDefRuleEvaluator(TestUsingMemoryBasedSheerka):
@staticmethod
def get_ret_val_from_rule(rule_def):
tokens = {k: list(Tokenizer(k.value + " " + v, yield_eof=False)) for k, v in rule_def.items()}
for v in tokens.values():
del v[1]
node = DefFormatRuleNode(tokens)
return ReturnValueConcept("parsers.FormatRule", True, ParserResultConcept(value=node))
@pytest.mark.parametrize("ret_val, expected", [
(ReturnValueConcept("name", True, ParserResultConcept(value=DefFormatRuleNode({}))), True),
(ReturnValueConcept("name", True, ParserResultConcept(value=DefExecRuleNode({}))), True),
(ReturnValueConcept("name", True, ParserResultConcept(value="other object")), False),
(ReturnValueConcept("name", False, ParserResultConcept(value=DefFormatRuleNode({}))), False),
(ReturnValueConcept("name", False, DefFormatRuleNode({})), False),
])
def test_i_can_match(self, ret_val, expected):
context = self.get_context()
assert DefRuleEvaluator().matches(context, ret_val) == expected
@pytest.mark.parametrize("text, expected_action_type, expected_name", [
("when True print 'hello world'", "print", None),
("when True then 'hello world'", "exec", None),
("def rule rule name as when True print 'hello world'", "print", "rule name"),
("def rule rule name as when True then 'hello world'", "exec", "rule name"),
])
def test_i_can_eval(self, text, expected_action_type, expected_name):
sheerka, context = self.init_concepts()
ret_val = DefRuleParser().parse(context, ParserInput(text))
res = DefRuleEvaluator().eval(context, ret_val)
assert res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.NEW_RULE)
assert isinstance(res.body.body, Rule)
assert res.body.body.metadata == RuleMetadata(expected_action_type,
expected_name,
"True",
"'hello world'",
id=res.body.body.metadata.id, # no need to compare the id
is_compiled=True,
is_enabled=True)
rule = res.body.body
assert rule.compiled_predicates is not None
assert rule.compiled_action is not None
assert rule.rete_disjunctions is not None
@@ -1,51 +0,0 @@
import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.rule import Rule, RuleMetadata
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Tokenizer
from evaluators.FormatRuleEvaluator import FormatRuleEvaluator
from parsers.DefFormatRuleParser import FormatRuleNode, DefFormatRuleParser
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestFormatRuleEvaluator(TestUsingMemoryBasedSheerka):
@staticmethod
def get_ret_val_from_rule(rule_def):
tokens = {k: list(Tokenizer(k.value + " " + v, yield_eof=False)) for k, v in rule_def.items()}
for v in tokens.values():
del v[1]
node = FormatRuleNode(tokens)
return ReturnValueConcept("parsers.FormatRule", True, ParserResultConcept(value=node))
@pytest.mark.parametrize("ret_val, expected", [
(ReturnValueConcept("name", True, ParserResultConcept(value=FormatRuleNode({}))), True),
(ReturnValueConcept("name", True, ParserResultConcept(value="other object")), False),
(ReturnValueConcept("name", False, ParserResultConcept(value=FormatRuleNode({}))), False),
(ReturnValueConcept("name", False, FormatRuleNode({})), False),
])
def test_i_can_match(self, ret_val, expected):
context = self.get_context()
assert FormatRuleEvaluator().matches(context, ret_val) == expected
def test_i_can_eval(self):
sheerka, context = self.init_concepts()
text = "when isinstance(value, __EXPLANATION) print list(value)"
ret_val = DefFormatRuleParser().parse(context, ParserInput(text))
res = FormatRuleEvaluator().eval(context, ret_val)
assert res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.NEW_RULE)
assert isinstance(res.body.body, Rule)
assert res.body.body.metadata == RuleMetadata("print",
None,
"isinstance(value, __EXPLANATION)",
"list(value)",
id=res.body.body.metadata.id, # no need to compare the id
is_compiled=True,
is_enabled=True)
assert res.body.body.compiled_predicate is not None
assert res.body.body.compiled_action is not None
+1 -1
View File
@@ -87,6 +87,6 @@ class TestLexerNodeEvaluator(TestUsingMemoryBasedSheerka):
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert wrapper.source == "foo + 1"
assert return_value == PythonNode('foo + 1', ast.parse("__C__foo__C__ + 1", mode="eval"))
assert return_value == PythonNode('__C__foo__C__ + 1', ast.parse("__C__foo__C__ + 1", mode="eval"), "foo + 1")
assert return_value.objects == {"__C__foo__C__": foo}
assert result.parents == [ret_val]
+2 -1
View File
@@ -3,6 +3,7 @@ import ast
import pytest
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.builtin_helpers import CreateObjectIdentifiers
from core.concept import Concept, CB
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
from core.sheerka.services.SheerkaExecute import ParserInput
@@ -26,7 +27,7 @@ def return_return_value(status):
def get_source_code_node(source_code, concepts=None):
if concepts:
for concept_name, concept in sorted(concepts.items(), key=lambda kv: len(kv[0]), reverse=True):
identifier = "__C__" + PythonWithConceptsParser.sanitize(concept.name)
identifier = "__C__" + CreateObjectIdentifiers.sanitize(concept.name)
if concept.id:
identifier += "__" + concept.id
identifier += "__C__"
+1 -1
View File
@@ -1230,7 +1230,7 @@ as:
assert res[0].body == 21
def test_i_can_define_rules_priorities(self):
sheerka, context, r1, r2 = self.init_test().with_rules(("True", "True"), ("False", "False")).unpack()
sheerka, context, r1, r2 = self.init_test().with_format_rules(("True", "True"), ("False", "False")).unpack()
sheerka.evaluate_user_input("def concept rule x > rule y where isinstance(x, int) and isinstance(y, int) as set_is_greater_than(__PRECEDENCE, r:|x:, r:|y:, 'Rule')")
res = sheerka.evaluate_user_input(f"eval rule {r1.id} > rule {r2.id}")
@@ -51,6 +51,23 @@ post : None
ret : None
vars : []
props : {}
"""
def test_i_can_describe_a_rule(self, capsys):
sheerka = self.get_sheerka()
sheerka.enable_process_return_values = True
sheerka.evaluate_user_input("desc(r:|1:)")
captured = capsys.readouterr()
assert captured.out == """id : 1
name : Print return values
type : print
predicate: __rets
action : list(__rets)
priority : 4
compiled : True
enabled : True
"""
def test_i_can_display_multiple_success(self, capsys):
@@ -0,0 +1,25 @@
from core.builtin_concepts_ids import BuiltinConcepts
from evaluators.PythonEvaluator import PythonEvalError
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestSheerkaNonRegDisplay(TestUsingMemoryBasedSheerka):
def test_i_can_apply_simple_rule(self):
init = [
"def concept one as 1",
"when __ret.status then test_error()",
]
sheerka = self.init_scenario(init)
sheerka.enable_process_rules = True
res = sheerka.evaluate_user_input("one")
assert len(res) == 1
ret = res[0]
assert not ret.status
assert sheerka.isinstance(ret.body, BuiltinConcepts.ERROR)
assert isinstance(ret.body.body, PythonEvalError)
assert isinstance(ret.body.body.error, Exception)
assert ret.body.body.error.args == ("I can raise an error",)
+1 -1
View File
@@ -38,7 +38,7 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
@classmethod
def teardown_class(cls):
cls.sheerka.pop_ontology()
cls.sheerka.pop_ontology(TestSheerkaOut.context)
cls.root_ontology_name = SheerkaOntologyManager.ROOT_ONTOLOGY_NAME
def init_service_with_rules(self, *rules, **kwargs):
+273 -5
View File
@@ -1,8 +1,193 @@
from core.concept import CC, Concept, ConceptParts, DoNotResolve, CIO
import ast
from dataclasses import dataclass
from core.builtin_helpers import CreateObjectIdentifiers
from core.concept import CC, Concept, ConceptParts, DoNotResolve, CIO, CMV
from core.tokenizer import Tokenizer, TokenKind, Token
from parsers.BaseNodeParser import scnode, utnode, cnode, SCWC, CNC, short_cnode, SourceCodeWithConceptNode, CN, UTN, \
SCN, RN
from core.utils import get_text_from_tokens, tokens_index
from parsers.BaseNodeParser import scnode, utnode, cnode, SCWC, CNC, short_cnode, CN, UTN, \
SCN, RN, UnrecognizedTokensNode, SourceCodeNode
from parsers.PythonParser import PythonNode
from parsers.SyaNodeParser import SyaConceptParserHelper
from parsers.expressions import NameExprNode, AndNode, OrNode, NotNode, VariableNode, ComparisonNode, ComparisonType
from sheerkarete.common import V
from sheerkarete.conditions import Condition, AndConditions
@dataclass
class Obj:
prop_a: object
prop_b: object = None
prop_c: object = None
parent: object = None
class AND:
""" Test class for AndNode"""
def __init__(self, *parts, source=None):
self.parts = parts
self.source = source
class OR:
""" Test class for OrNode"""
def __init__(self, *parts, source=None):
self.parts = parts
self.source = source
@dataclass
class NOT:
""" Test class for NotNode"""
expr: object
source: str = None
@dataclass
class EXPR:
"""Test class for NameNode. E stands for Expression"""
source: str
@dataclass
class VAR:
"""Test class for VarNode"""
full_name: str
source: str = None
@dataclass
class EQ:
left: object
right: object
source = None
@dataclass
class NEQ:
left: object
right: object
source = None
@dataclass
class GT:
left: object
right: object
source = None
@dataclass
class GTE:
left: object
right: object
source = None
@dataclass
class LT:
left: object
right: object
source = None
@dataclass
class LTE:
left: object
right: object
source = None
@dataclass
class IN:
left: object
right: object
source = None
@dataclass
class NIN: # for NOT INT
left: object
right: object
source = None
comparison_type_mapping = {
"EQ": ComparisonType.EQUALS,
"NEQ": ComparisonType.NOT_EQUAlS,
"LT": ComparisonType.LESS_THAN,
"LTE": ComparisonType.LESS_THAN_OR_EQUALS,
"GT": ComparisonType.GREATER_THAN,
"GTE": ComparisonType.GREATER_THAN_OR_EQUALS,
"IN": ComparisonType.IN,
"NIN": ComparisonType.NOT_IN,
}
def get_expr_node_from_test_node(full_text, test_node):
"""
Returns EXPR, OR, NOT, AND object to ease the comparison with the real ExprNode
"""
full_text_as_tokens = list(Tokenizer(full_text, yield_eof=False))
def get_pos(nodes):
start, end = None, None
for n in nodes:
if start is None or start > n.start:
start = n.start
if end is None or end < n.end:
end = n.end
return start, end
def get_pos_from_source(source):
source_as_node = list(Tokenizer(source, yield_eof=False))
start = tokens_index(full_text_as_tokens, source_as_node)
end = start + len(source_as_node) - 1
return start, end
def get_expr_node(node):
if isinstance(node, EXPR):
value_as_tokens = list(Tokenizer(node.source, yield_eof=False))
start = tokens_index(full_text_as_tokens, value_as_tokens, 0)
end = start + len(value_as_tokens) - 1
return NameExprNode(start, end, full_text_as_tokens[start: end + 1])
if isinstance(node, AND):
parts = [get_expr_node(part) for part in node.parts]
start, end = get_pos_from_source(node.source) if node.source else get_pos(parts)
return AndNode(start, end, full_text_as_tokens[start: end + 1], *parts)
if isinstance(node, OR):
parts = [get_expr_node(part) for part in node.parts]
start, end = get_pos_from_source(node.source) if node.source else get_pos(parts)
return OrNode(start, end, full_text_as_tokens[start: end + 1], *parts)
if isinstance(node, NOT):
part = get_expr_node(node.expr)
start, end = get_pos_from_source(node.source) if node.source else (part.start - 2, part.end)
return NotNode(start, end, full_text_as_tokens[start: end + 1], part)
if isinstance(node, VAR):
value_as_tokens = list(Tokenizer(node.source or node.full_name, yield_eof=False))
start = tokens_index(full_text_as_tokens, value_as_tokens, 0)
end = start + len(value_as_tokens) - 1
parts = node.full_name.split(".")
if len(parts) == 1:
return VariableNode(start, end, full_text_as_tokens[start: end + 1], parts[0])
else:
return VariableNode(start, end, full_text_as_tokens[start: end + 1], parts[0], *parts[1:])
if isinstance(node, (EQ, NEQ, GT, GTE, LT, LTE, IN, NIN)):
node_type = comparison_type_mapping[type(node).__name__]
left_node, right_node = get_expr_node(node.left), get_expr_node(node.right)
start, end = get_pos_from_source(node.source) if node.source else get_pos([left_node, right_node])
return ComparisonNode(start, end, full_text_as_tokens[start: end + 1],
node_type, left_node, right_node)
return get_expr_node(test_node)
def _index(tokens, expr, index):
@@ -101,7 +286,7 @@ def get_node(
sub_expr.fix_pos(sub_expr.first)
sub_expr.fix_pos(sub_expr.last)
return sub_expr
#return SourceCodeWithConceptNode(first, last, content).pseudo_fix_source()
# return SourceCodeWithConceptNode(first, last, content).pseudo_fix_source()
if isinstance(sub_expr, SCN):
node = get_node(concepts_map, expression_as_tokens, sub_expr.source, sya=sya)
@@ -128,7 +313,8 @@ def get_node(
sub_expr.fix_pos((concept_node.start, concept_node.end if hasattr(concept_node, "end") else concept_node.start))
if hasattr(sub_expr, "compiled"):
for k, v in sub_expr.compiled.items():
node = get_node(concepts_map, expression_as_tokens, v, sya=sya, exclude_body=exclude_body) # need to get start and end positions
node = get_node(concepts_map, expression_as_tokens, v, sya=sya,
exclude_body=exclude_body) # need to get start and end positions
if isinstance(v, str) and v in concepts_map:
new_value_concept = concepts_map[v]
new_value = CC(Concept().update_from(new_value_concept), exclude_body=exclude_body)
@@ -214,3 +400,85 @@ def compute_expected_array(concepts_map, expression, expected, sya=False, init_e
sya=sya,
init_empty_body=init_empty_body,
exclude_body=exclude_body) for sub_expr in expected]
def get_unrecognized_node(start, text):
tokens = list(Tokenizer(text, yield_eof=False))
return UnrecognizedTokensNode(start, start + len(tokens) - 1, tokens)
def get_source_code_node(start, text, concepts_map, id_manager=None):
id_manager = id_manager or CreateObjectIdentifiers()
id_mapping = {}
concept_mapping_by_id = {}
# get the concepts, mapped by their new id
for concept_name, concept in concepts_map.items():
concept_identifier = id_manager.get_identifier(concept, "__C__")
id_mapping[concept_name] = concept_identifier
concept_mapping_by_id[concept_identifier] = concept
# transform the source code to use the new id
tokens = list(Tokenizer(text, yield_eof=False))
text_to_compile_tokens = []
for t in tokens:
if t.type == TokenKind.IDENTIFIER and t.value in id_mapping:
text_to_compile_tokens.append(Token(TokenKind.IDENTIFIER, id_mapping[t.value], -1, -1, -1))
else:
text_to_compile_tokens.append(t)
text_to_compile = get_text_from_tokens(text_to_compile_tokens)
# create the python node
ast_ = ast.parse(text_to_compile, "<source>", 'eval')
python_node = PythonNode(text_to_compile, ast_, text)
python_node.objects = concept_mapping_by_id
return SourceCodeNode(start, start + len(tokens) - 1, tokens, text, python_node)
def resolve_test_concept(concept_map, hint):
if isinstance(hint, str):
return concept_map[hint]
if isinstance(hint, CC):
concept = concept_map[hint.concept_key]
compiled = {k: resolve_test_concept(concept_map, v) for k, v in hint.compiled.items()}
return CC(concept, source=hint.source, exclude_body=hint.exclude_body, **compiled)
if isinstance(hint, CMV):
concept = concept_map[hint.concept_key]
return CMV(concept, **hint.variables)
# CV
#
# CMV
#
# CIO
raise NotImplementedError()
def get_rete_conditions(*conditions_as_string):
"""
Transform a list of string into a list of Condition (Rete conditions)
:param conditions_as_string: conditions in the form 'identifier|attribute|value'
when one argument starts with "#" it means that it's a variables
ex : "#__x_00__|__name__|'__ret'" -> Condition(V('#__x_00__'), '__name__', '__ret')
Caution, the value part is evaluated
"identifier|__name__|'True'" -> Condition(identifier, '__name__', 'True') # the string 'True'
"identifier|__name__|True" -> Condition(identifier, '__name__', True) # the bool True
"""
res = []
for as_string in conditions_as_string:
identifier, attribute, value = as_string.split("|")
if identifier.startswith("#"):
identifier = V(identifier[1:])
if value.startswith("'"):
value = value[1:-1]
elif value in ("True", "False"):
value = (value == "True")
else:
value = int(value)
res.append(Condition(identifier, attribute, value))
return AndConditions(res)
@@ -1,9 +1,9 @@
import pytest
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Keywords, Tokenizer, TokenKind
from parsers.BaseCustomGrammarParser import BaseCustomGrammarParser, SyntaxErrorNode, KeywordNotFound
from parsers.BaseParser import UnexpectedEofParsingError, UnexpectedTokenParsingError
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -43,6 +43,7 @@ class TestBaseCustomGrammarParser(TestUsingMemoryBasedSheerka):
("when uuu vvv print xxx yyy", False, {Keywords.WHEN: "when uuu vvv ", Keywords.PRINT: "print xxx yyy"}),
("print xxx yyy when uuu vvv", False, {Keywords.WHEN: "when uuu vvv", Keywords.PRINT: "print xxx yyy "}),
(" when xxx", False, {Keywords.WHEN: "when xxx"}),
("when xxx yyy", True, {Keywords.WHEN: "when xxx yyy"}),
("when uuu vvv print xxx yyy", True, {Keywords.WHEN: "when uuu vvv", Keywords.PRINT: "print xxx yyy"}),
("print xxx yyy when uuu vvv", True, {Keywords.WHEN: "when uuu vvv", Keywords.PRINT: "print xxx yyy"}),
@@ -84,7 +85,7 @@ func(a)
assert parser.get_parts(["when", "print"], Keywords.PRINT) is None
assert parser.error_sink == [UnexpectedTokenParsingError(f"'print' keyword not found.",
"when",
"when",
[Keywords.PRINT])]
def test_i_can_detect_when_a_keyword_appears_several_times(self):
@@ -214,7 +215,8 @@ print xxx"""
sheerka, context, parser = self.init_parser("")
assert parser.get_body(list(Tokenizer(text, yield_eof=False))) is None
assert parser.error_sink == [UnexpectedTokenParsingError("Indentation not found.", "zzz", [TokenKind.WHITESPACE])]
assert parser.error_sink == [
UnexpectedTokenParsingError("Indentation not found.", "zzz", [TokenKind.WHITESPACE])]
def test_i_can_detect_invalid_indentation_when_get_body(self):
sheerka, context, parser = self.init_parser("")
+1 -1
View File
@@ -136,7 +136,7 @@ class TestBnfNodeParser(TestUsingMemoryBasedSheerka):
sheerka.set_isa(context, sheerka.new("thousands"), sheerka.new("number"))
cls.shared_ontology = sheerka.get_ontology(context)
sheerka.pop_ontology()
sheerka.pop_ontology(context)
@staticmethod
def update_bnf(context, concept):
+98
View File
@@ -0,0 +1,98 @@
import pytest
from core.builtin_concepts_ids import BuiltinConcepts
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import TokenKind, Tokenizer
from parsers.ComparisonParser import ComparisonParser
from parsers.ExpressionParser import ParenthesisMismatchError
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.parsers.parsers_utils import get_expr_node_from_test_node, VAR, EXPR, EQ, NEQ, GT, GTE, LT, LTE, IN, NIN
class TestComparisonParser(TestUsingMemoryBasedSheerka):
def init_parser(self):
sheerka, context = self.init_concepts()
parser = ComparisonParser()
return sheerka, context, parser
def test_i_can_detect_empty_expression(self):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(""))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.IS_EMPTY)
@pytest.mark.parametrize("expression, expected", [
("var_name", VAR("var_name")),
("var_name.attr", VAR("var_name.attr")),
("var_name .attr", VAR("var_name.attr", source="var_name .attr")),
("var_name. attr", VAR("var_name.attr", source="var_name. attr")),
("var_name . attr", VAR("var_name.attr", source="var_name . attr")),
("var_name.attr.get_value(x)", VAR("var_name.attr.get_value(x)")),
("var_name.attr == 10", EQ(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr != 10", NEQ(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr > 10", GT(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr >= 10", GTE(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr < 10", LT(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr <= 10", LTE(VAR("var_name.attr"), EXPR("10"))),
("var_name.attr in (a, b)", IN(VAR("var_name.attr"), EXPR("(a, b)"))),
("var_name.attr not in (a, b)", NIN(VAR("var_name.attr"), EXPR("(a, b)"))),
("var1.attr1 == var2.attr2", EQ(VAR("var1.attr1"), VAR("var2.attr2"))),
("not a var identifier", EXPR("not a var identifier")),
("func()", EXPR("func()")),
("func(a, not an identifier, x >5)", EXPR("func(a, not an identifier, x >5)")),
])
def test_i_can_parse_simple_expressions(self, expression, expected):
sheerka, context, parser = self.init_parser()
expected = get_expr_node_from_test_node(expression, expected)
res = parser.parse(context, ParserInput(expression))
parser_result = res.body
parsed_expr = parser_result.body
assert res.status
assert res.who == parser.name
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
assert parsed_expr == expected
@pytest.mark.parametrize("expression, expected_error, parenthesis_type, index", [
("(", BuiltinConcepts.NOT_FOR_ME, TokenKind.LPAR, 0),
(")", BuiltinConcepts.NOT_FOR_ME, TokenKind.RPAR, 0),
("something (", BuiltinConcepts.NOT_FOR_ME, TokenKind.LPAR, 10),
("something )", BuiltinConcepts.NOT_FOR_ME, TokenKind.RPAR, 10),
("something == (", BuiltinConcepts.ERROR, TokenKind.LPAR, 13),
("something == )", BuiltinConcepts.ERROR, TokenKind.RPAR, 13),
("something (==", BuiltinConcepts.NOT_FOR_ME, TokenKind.LPAR, 10),
("something )==", BuiltinConcepts.NOT_FOR_ME, TokenKind.RPAR, 10),
])
def test_i_can_detect_unbalanced_parenthesis(self, expression, expected_error, parenthesis_type, index):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(expression))
assert not res.status
if expected_error == BuiltinConcepts.NOT_FOR_ME:
assert sheerka.isinstance(res.body, BuiltinConcepts.NOT_FOR_ME)
assert isinstance(res.body.reason[0], ParenthesisMismatchError)
assert res.body.reason[0].token.type == parenthesis_type
assert res.body.reason[0].token.index == index
else:
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], ParenthesisMismatchError)
assert res.body.body[0].token.type == parenthesis_type
assert res.body.body[0].token.index == index
def test_i_can_parse_tokens_rather_than_parser_input(self):
sheerka, context, parser = self.init_parser()
expression = "var1.attr1 == var2.attr2"
expected = EQ(VAR("var1.attr1"), VAR("var2.attr2"))
expected = get_expr_node_from_test_node(expression, expected)
res = parser.parse(context, list(Tokenizer(expression)))
parser_result = res.body
parsed_expr = parser_result.body
assert res.status
assert res.who == parser.name
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
assert parsed_expr == expected
+7 -6
View File
@@ -4,10 +4,11 @@ from dataclasses import dataclass
import pytest
from core.builtin_concepts import ParserResultConcept, BuiltinConcepts, ReturnValueConcept
from core.concept import DEFINITION_TYPE_BNF, DEFINITION_TYPE_DEF, Concept, CV
from core.global_symbols import NotInit
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Keywords, Tokenizer, LexerError
from parsers.BaseNodeParser import SCWC
from parsers.BaseParser import NotInitializedNode, UnexpectedEofParsingError
from parsers.BaseParser import UnexpectedEofParsingError
from parsers.BnfNodeParser import OrderedChoice, ConceptExpression, StrMatch, Sequence
from parsers.BnfDefinitionParser import BnfDefinitionParser
from parsers.DefConceptParser import DefConceptParser, NameNode, SyntaxErrorNode
@@ -222,11 +223,11 @@ class TestDefConceptParser(TestUsingMemoryBasedSheerka):
assert res.status
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
assert isinstance(node, DefConceptNode)
assert node.body == NotInitializedNode()
assert node.where == NotInitializedNode()
assert node.pre == NotInitializedNode()
assert node.post == NotInitializedNode()
assert node.ret == NotInitializedNode()
assert node.body is NotInit
assert node.where is NotInit
assert node.pre is NotInit
assert node.post is NotInit
assert node.ret is NotInit
@pytest.mark.parametrize("part", [
"as",
-143
View File
@@ -1,143 +0,0 @@
import pytest
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept
from core.sheerka.services.SheerkaExecute import ParserInput
from core.sheerka.services.SheerkaRuleManager import FormatAstRawText, RulePredicate, FormatAstVariable
from core.tokenizer import Keywords, Tokenizer
from parsers.BaseCustomGrammarParser import KeywordNotFound
from parsers.DefFormatRuleParser import DefFormatRuleParser, FormatRuleNode
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
cmap = {
"is a": Concept("x is a y").def_var("x").def_var("y"),
"is a question": Concept("x is a y", pre="is_question()").def_var("x").def_var("y"),
"a is good": Concept("a is good").def_var("a"),
"b is good": Concept("b is good").def_var("b"),
}
class TestDefFormatRuleParser(TestUsingMemoryBasedSheerka):
shared_ontology = None
@classmethod
def setup_class(cls):
init_test_helper = cls().init_test(cache_only=False, ontology="#TestDefFormatRuleParser#")
sheerka, context, *updated = init_test_helper.with_concepts(*cmap.values(), create_new=True).unpack()
for i, concept_name in enumerate(cmap):
cmap[concept_name] = updated[i]
cls.shared_ontology = sheerka.get_ontology(context)
sheerka.pop_ontology()
def init_parser(self, my_concepts_map=None, **kwargs):
if my_concepts_map is None:
sheerka, context = self.init_test().unpack()
sheerka.add_ontology(context, self.shared_ontology)
else:
sheerka, context, *updated = self.init_test().with_concepts(*my_concepts_map.values(), **kwargs).unpack()
for i, pair in enumerate(my_concepts_map):
my_concepts_map[pair] = updated[i]
parser = DefFormatRuleParser()
return sheerka, context, parser
def test_i_can_detect_empty_expression(self):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(""))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.IS_EMPTY)
def test_input_must_be_a_parser_input(self):
sheerka, context, parser = self.init_parser()
parser.parse(context, "not a parser input") is None
def test_i_can_parse_a_simple_rule(self):
sheerka, context, parser = self.init_parser()
text = "when isinstance(last_value(), Concept) print hello world!"
res = parser.parse(context, ParserInput(text))
parser_result = res.body
format_rule = res.body.body
rules = format_rule.rule
format_ast = format_rule.format_ast
assert res.status
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
assert isinstance(format_rule, FormatRuleNode)
assert self.dump_tokens(format_rule.tokens[Keywords.WHEN][1:]) == self.dump_tokens(
Tokenizer("isinstance(last_value(), Concept)", False))
assert self.dump_tokens(format_rule.tokens[Keywords.PRINT][1:]) == self.dump_tokens(
Tokenizer("hello world!", False))
assert len(rules) == 1
assert isinstance(rules[0], RulePredicate)
assert format_ast == FormatAstRawText("hello world!")
def test_when_is_parsed_in_the_context_of_a_question(self):
sheerka, context, parser = self.init_parser()
text = "when foo is a bar print hello world"
res = parser.parse(context, ParserInput(text))
format_rule = res.body.body
rules = format_rule.rule
assert res.status
assert len(rules) == 1
assert isinstance(rules[0], RulePredicate)
assert rules[0].predicate.body.body.get_metadata().pre == "is_question()"
def test_when_can_support_multiple_possibilities(self):
sheerka, context, parser = self.init_parser()
text = "when foo is good print hello world"
res = parser.parse(context, ParserInput(text))
format_rule = res.body.body
rules = format_rule.rule
assert res.status
assert len(rules) == 2
assert rules[0].predicate.body.body.get_metadata().name == "a is good"
assert rules[1].predicate.body.body.get_metadata().name == "b is good"
@pytest.mark.parametrize("text, error", [
("hello world", [KeywordNotFound(None, keywords=['when', 'print'])]),
("when True", [KeywordNotFound([], keywords=['print'])]),
("print True", [KeywordNotFound([], keywords=['when'])]),
])
def test_cannot_parse_when_not_for_me(self, text, error):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(text))
not_for_me = res.body
assert not res.status
assert sheerka.isinstance(not_for_me, BuiltinConcepts.NOT_FOR_ME)
assert not_for_me.reason == error
@pytest.mark.parametrize("text, expected_error", [
("when a b print hello world!", BuiltinConcepts.TOO_MANY_ERRORS),
])
def test_i_cannot_parse_invalid_predicates(self, text, expected_error):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(text))
assert not res.status
assert sheerka.isinstance(res.body, expected_error)
@pytest.mark.parametrize("expr, expected", [
("hello world", FormatAstRawText("hello world")),
("{id}", FormatAstVariable("id")),
])
def test_i_can_parse_valid_print_expression(self, expr, expected):
sheerka, context, parser = self.init_parser()
text = "when True print " + expr
res = parser.parse(context, ParserInput(text))
format_rule = res.body.body
format_ast = format_rule.format_ast
assert res.status
assert format_ast == expected
+213
View File
@@ -0,0 +1,213 @@
import pytest
from core.builtin_concepts_ids import BuiltinConcepts
from core.concept import Concept
from core.sheerka.services.SheerkaExecute import ParserInput
from core.sheerka.services.SheerkaRuleManager import RuleCompiledPredicate, FormatAstNode
from core.tokenizer import Tokenizer, Keywords
from core.utils import tokens_are_matching
from parsers.BaseCustomGrammarParser import KeywordNotFound, NameNode, SyntaxErrorNode
from parsers.BaseParser import UnexpectedEofParsingError
from parsers.DefRuleParser import DefRuleParser, DefExecRuleNode, DefFormatRuleNode
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
cmap = {
"foo": Concept("foo"),
"bar": Concept("bar"),
"is a": Concept("x is a y").def_var("x").def_var("y"),
"is a question": Concept("x is a y", pre="is_question()").def_var("x").def_var("y"),
"a is good": Concept("a is good", pre="is_question()").def_var("a"),
"b is good": Concept("b is good", pre="is_question()").def_var("b"),
"greetings": Concept("hello a").def_var("a")
}
class TestDefRuleParser(TestUsingMemoryBasedSheerka):
shared_ontology = None
@classmethod
def setup_class(cls):
init_test_ref = cls().init_test(cache_only=False, ontology="#TestDefRuleParser#")
sheerka, context, *updated = init_test_ref.with_concepts(*cmap.values(), create_new=True).unpack()
for i, concept_name in enumerate(cmap):
cmap[concept_name] = updated[i]
cls.shared_ontology = sheerka.get_ontology(context)
sheerka.pop_ontology(context)
def init_parser(self, my_concepts_map=None, **kwargs):
if my_concepts_map is None:
sheerka, context = self.init_test().unpack()
sheerka.add_ontology(context, self.shared_ontology)
else:
sheerka, context, *updated = self.init_test().with_concepts(*my_concepts_map.values(), **kwargs).unpack()
for i, pair in enumerate(my_concepts_map):
my_concepts_map[pair] = updated[i]
parser = DefRuleParser()
return sheerka, context, parser
def test_i_cannot_parse_when_parser_input_is_initialized_from_token(self):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput("", list(Tokenizer("init from tokens"))))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.NOT_FOR_ME)
def test_i_can_detect_empty_expression(self):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(""))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.IS_EMPTY)
def test_input_must_be_a_parser_input(self):
sheerka, context, parser = self.init_parser()
assert parser.parse(context, "not a parser input") is None
def test_i_can_parse_simple_exec_rule_definition(self):
sheerka, context, parser = self.init_parser()
text = "when True then answer('that is true')"
res = parser.parse(context, ParserInput(text))
parser_result = res.body
parsed = res.body.body
assert res.status
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
assert isinstance(parsed, DefExecRuleNode)
assert len(parsed.tokens) == 2
assert tokens_are_matching(parsed.tokens[Keywords.WHEN], Tokenizer("when True"))
assert tokens_are_matching(parsed.tokens[Keywords.THEN], Tokenizer("then answer('that is true')"))
assert isinstance(parsed.when, list)
assert len(parsed.when) == 1
assert isinstance(parsed.when[0], RuleCompiledPredicate)
assert sheerka.isinstance(parsed.then, BuiltinConcepts.RETURN_VALUE)
def test_i_can_parse_simple_format_rule_definition(self):
sheerka, context, parser = self.init_parser()
text = "when True print hello world!"
res = parser.parse(context, ParserInput(text))
parser_result = res.body
parsed = res.body.body
assert res.status
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
assert isinstance(parsed, DefFormatRuleNode)
assert len(parsed.tokens) == 2
assert tokens_are_matching(parsed.tokens[Keywords.WHEN], Tokenizer("when True"))
assert tokens_are_matching(parsed.tokens[Keywords.PRINT], Tokenizer("print hello world!"))
assert isinstance(parsed.when, list)
assert len(parsed.when) == 1
assert isinstance(parsed.when[0], RuleCompiledPredicate)
assert isinstance(parsed.print, FormatAstNode)
def test_i_can_parse_exec_rule_with_name(self):
sheerka, context, parser = self.init_parser()
text = "def rule my rule as when True then answer('that is true')"
res = parser.parse(context, ParserInput(text))
parser_result = res.body
parsed = res.body.body
assert res.status
assert sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
assert isinstance(parsed, DefExecRuleNode)
assert parsed.name == NameNode(list(Tokenizer("my rule")))
assert len(parsed.tokens) == 2
assert tokens_are_matching(parsed.tokens[Keywords.WHEN], Tokenizer("when True"))
assert tokens_are_matching(parsed.tokens[Keywords.THEN], Tokenizer("then answer('that is true')"))
assert isinstance(parsed.when, list)
assert len(parsed.when) == 1
assert isinstance(parsed.when[0], RuleCompiledPredicate)
assert sheerka.isinstance(parsed.then, BuiltinConcepts.RETURN_VALUE)
def test_when_is_parsed_in_the_context_of_a_question(self):
sheerka, context, parser = self.init_parser()
text = "when foo is a bar print hello world"
res = parser.parse(context, ParserInput(text))
format_rule = res.body.body
rules = format_rule.when
assert res.status
assert len(rules) == 1
assert isinstance(rules[0], RuleCompiledPredicate)
assert rules[0].predicate.body.body.get_metadata().pre == "is_question()"
def test_when_can_support_multiple_possibilities_when_question_only(self):
sheerka, context, parser = self.init_parser()
text = "when foo is good print hello world"
res = parser.parse(context, ParserInput(text))
format_rule = res.body.body
rules = format_rule.when
assert res.status
assert len(rules) == 2
assert rules[0].predicate.body.body.get_metadata().name == "a is good"
assert rules[1].predicate.body.body.get_metadata().name == "b is good"
@pytest.mark.parametrize("text, error", [
("def", [KeywordNotFound(None, keywords=['rule'])]),
("def concept", [KeywordNotFound(None, keywords=['rule'])]),
("def other", [KeywordNotFound(None, keywords=['rule'])]),
("def rule name", [KeywordNotFound(None, keywords=['as'])]),
("def rule complicated long name", [KeywordNotFound(None, keywords=['as'])]),
("hello world", [KeywordNotFound(None, keywords=['when'])]),
("when True", [KeywordNotFound([], keywords=['then', 'print'])]),
("print True", [KeywordNotFound([], keywords=['when'])]),
("when True print 'hello world' then answer('yes')",
[SyntaxErrorNode([], message="Cannot have both 'print' and 'then' keywords")])
])
def test_i_can_detect_not_for_me(self, text, error):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(text))
not_for_me = res.body
assert not res.status
assert sheerka.isinstance(not_for_me, BuiltinConcepts.NOT_FOR_ME)
assert not_for_me.reason == error
@pytest.mark.parametrize("text, expected_error", [
("when x x print 'hello world'", BuiltinConcepts.TOO_MANY_ERRORS),
])
def test_i_can_detect_errors(self, text, expected_error):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(text))
assert not res.status
assert sheerka.isinstance(res.body, expected_error)
@pytest.mark.parametrize("text, error_message", [
("def rule rule_name as", "While parsing 'when'."),
("def rule rule_name as ", "While parsing 'when'."),
])
def test_i_cannot_parse_when_unexpected_eof(self, text, error_message):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput(text))
not_for_me = res.body
assert not res.status
assert sheerka.isinstance(not_for_me, BuiltinConcepts.NOT_FOR_ME)
assert isinstance(not_for_me.reason[0], UnexpectedEofParsingError)
assert not_for_me.reason[0].message == error_message
def test_rule_name_is_mandatory_when_using_def_rule(self):
sheerka, context, parser = self.init_parser()
res = parser.parse(context, ParserInput("def rule as when True print 'true'"))
error = res.body
assert not res.status
assert sheerka.isinstance(error, BuiltinConcepts.ERROR)
assert isinstance(error.error[0], SyntaxErrorNode)
assert error.error[0].message == "Name is mandatory"
+392 -112
View File
@@ -1,27 +1,21 @@
from dataclasses import dataclass
import ast
import pytest
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
from core.concept import Concept
from core.concept import Concept, CMV, DoNotResolve, CC
from core.rule import Rule
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Tokenizer, TokenKind
from core.tokenizer import TokenKind
from parsers.BaseNodeParser import CNC
from parsers.BaseParser import UnexpectedEofParsingError, UnexpectedTokenParsingError
from parsers.ExpressionParser import PropertyEqualsNode, PropertyEqualsSequenceNode, PropertyContainsNode, AndNode, \
OrNode, NotNode, LambdaNode, IsaNode, NameExprNode, ExpressionParser, LeftPartNotFoundError, TrueifyVisitor
from parsers.ExpressionParser import ExpressionParser, LeftPartNotFoundError, ParenthesisMismatchError
from parsers.PythonParser import PythonNode
from parsers.expressions import TrueifyVisitor, IsAQuestionVisitor, AndNode
from sheerkarete.network import ReteNetwork
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@dataclass
class Obj:
prop_a: object
prop_b: object = None
prop_c: object = None
parent: object = None
def n(value):
return NameExprNode(Tokenizer(value, yield_eof=False))
from tests.parsers.parsers_utils import compute_expected_array, resolve_test_concept, EXPR, OR, AND, NOT, \
get_expr_node_from_test_node, get_rete_conditions
class TestExpressionParser(TestUsingMemoryBasedSheerka):
@@ -32,19 +26,34 @@ class TestExpressionParser(TestUsingMemoryBasedSheerka):
return sheerka, context, parser
@pytest.mark.parametrize("expression, expected", [
("one complicated expression", n("one complicated expression")),
("function_call(a,b,c)", n("function_call(a,b,c)")),
("one expression or another expression", OrNode(n("one expression"), n("another expression"))),
("one expression and another expression", AndNode(n("one expression"), n("another expression"))),
("one or two or three", OrNode(n("one"), n("two"), n("three"))),
("one and two and three", AndNode(n("one"), n("two"), n("three"))),
("one or two and three", OrNode(n("one"), AndNode(n("two"), n("three")))),
("one and two or three", OrNode(AndNode(n("one"), n("two")), n("three"))),
("one and (two or three)", AndNode(n("one"), OrNode(n("two"), n("three")))),
("one complicated expression", EXPR("one complicated expression")),
("function_call(a,b,c)", EXPR("function_call(a,b,c)")),
("one expression or another expression", OR(EXPR("one expression"), EXPR("another expression"))),
("one expression and another expression", AND(EXPR("one expression"), EXPR("another expression"))),
("not one", NOT(EXPR("one"))),
("one and not two", AND(EXPR("one"), NOT(EXPR("two")))),
("not one and two", AND(NOT(EXPR("one")), EXPR("two"))),
("one or not two", OR(EXPR("one"), NOT(EXPR("two")))),
("not one or two", OR(NOT(EXPR("one")), EXPR("two"))),
("one or two or three", OR(EXPR("one"), EXPR("two"), EXPR("three"))),
("one and two and three", AND(EXPR("one"), EXPR("two"), EXPR("three"))),
("one or two and three", OR(EXPR("one"), AND(EXPR("two"), EXPR("three")))),
("one and two or three", OR(AND(EXPR("one"), EXPR("two")), EXPR("three"))),
("one and (two or three)", AND(EXPR("one"), OR(EXPR("two"), EXPR("three")), source="one and (two or three)")),
("not not one", NOT(NOT(EXPR("one")))),
("not (one and two)", NOT(AND(EXPR("one"), EXPR("two")), source="not (one and two)")),
("one 'and' two or three", OR(EXPR("one 'and' two"), EXPR("three"))),
("not ((a or b) and (c or d))", NOT(AND(OR(EXPR("a"), EXPR("b")), OR(EXPR("c"), EXPR("d")),
source="(a or b) and (c or d)"),
source="not ((a or b) and (c or d))")),
("not ((a and b) or (c and d))", NOT(OR(AND(EXPR("a"), EXPR("b")), AND(EXPR("c"), EXPR("d")),
source="(a and b) or (c and d)"),
source="not ((a and b) or (c and d))")),
])
def test_i_can_parse_expression(self, expression, expected):
sheerka, context, parser = self.init_parser()
expected = get_expr_node_from_test_node(expression, expected)
res = parser.parse(context, ParserInput(expression))
wrapper = res.body
expressions = res.body.body
@@ -69,6 +78,15 @@ class TestExpressionParser(TestUsingMemoryBasedSheerka):
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert res.body.body == expected_errors
def test_i_can_detect_unexpected_not_error(self):
sheerka, context, parser = self.init_parser()
expression = "a cat is not a human"
res = parser.parse(context, ParserInput(expression))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], UnexpectedTokenParsingError)
def test_i_can_detect_unbalanced_parenthesis(self):
sheerka, context, parser = self.init_parser()
@@ -86,6 +104,27 @@ class TestExpressionParser(TestUsingMemoryBasedSheerka):
assert res.body.reason[0].token.type == TokenKind.RPAR
assert res.body.reason[0].expected_tokens == []
res = parser.parse(context, ParserInput("one and two("))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], ParenthesisMismatchError)
assert res.body.body[0].token.type == TokenKind.LPAR
assert res.body.body[0].token.index == 11
res = parser.parse(context, ParserInput("one ("))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.NOT_FOR_ME)
assert isinstance(res.body.reason[0], ParenthesisMismatchError)
assert res.body.reason[0].token.type == TokenKind.LPAR
assert res.body.reason[0].token.index == 4
res = parser.parse(context, ParserInput("one (and"))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], ParenthesisMismatchError)
assert res.body.body[0].token.type == TokenKind.LPAR
assert res.body.body[0].token.index == 4
res = parser.parse(context, ParserInput("one and two)"))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
@@ -93,7 +132,14 @@ class TestExpressionParser(TestUsingMemoryBasedSheerka):
assert res.body.body[0].token.type == TokenKind.RPAR
assert res.body.body[0].expected_tokens == []
res = parser.parse(context, ParserInput("one and two)"))
res = parser.parse(context, ParserInput("one )"))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], UnexpectedTokenParsingError)
assert res.body.body[0].token.type == TokenKind.RPAR
assert res.body.body[0].expected_tokens == []
res = parser.parse(context, ParserInput("one ) and"))
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.ERROR)
assert isinstance(res.body.body[0], UnexpectedTokenParsingError)
@@ -107,90 +153,6 @@ class TestExpressionParser(TestUsingMemoryBasedSheerka):
assert not res.status
assert sheerka.isinstance(res.body, BuiltinConcepts.IS_EMPTY)
def test_i_can_test_property_equals(self):
node = PropertyEqualsNode("prop_a", "good value")
assert node.eval(Obj(prop_a="good value"))
assert not node.eval(Obj(prop_a="other value"))
def test_i_can_test_property_equals_for_int(self):
node = PropertyEqualsNode("prop_a", "1")
assert node.eval(Obj(prop_a=1))
assert node.eval(Obj(prop_a="1"))
def test_i_can_test_property_equals_sequence(self):
node = PropertyEqualsSequenceNode(["prop_b", "prop_a"], ["good parent", "good child"])
assert node.eval(Obj(prop_a="good child", parent=Obj(prop_a="Don't care", prop_b="good parent")))
assert not node.eval(Obj(prop_a="good child", parent=Obj(prop_a="Don't care", prop_b="wrong parent")))
assert not node.eval(Obj(prop_a="good child"))
assert not node.eval(Obj(prop_a="wrong child", parent=Obj(prop_a="Don't care", prop_b="good parent")))
def test_i_can_test_property_contains(self):
node = PropertyContainsNode("prop_a", "substring")
assert node.eval(Obj(prop_a="it contains substring in it"))
assert not node.eval(Obj(prop_a="it does not"))
def test_i_can_test_property_contains_for_int(self):
node = PropertyContainsNode("prop_a", "44")
assert node.eval(Obj(prop_a=123445))
assert not node.eval(Obj(prop_a=12435))
def test_i_can_test_and(self):
left = PropertyEqualsNode("prop_a", "good a")
right = PropertyEqualsNode("prop_b", "good b")
other = PropertyEqualsNode("prop_c", "good c")
and_node = AndNode(left, right, other)
assert and_node.eval(Obj("good a", "good b", "good c"))
assert not and_node.eval(Obj("wrong a", "good b", "good c"))
assert not and_node.eval(Obj("good a", "wrong b", "good c"))
assert not and_node.eval(Obj("good a", "good b", "wrong c"))
def test_i_can_test_or(self):
left = PropertyEqualsNode("prop_a", "good a")
right = PropertyEqualsNode("prop_b", "good b")
other = PropertyEqualsNode("prop_c", "good c")
or_node = OrNode(left, right, other)
assert or_node.eval(Obj("wrong a", "good b", "good c"))
assert or_node.eval(Obj("good a", "wrong b", "good c"))
assert or_node.eval(Obj("good a", "good b", "wrong c"))
assert not or_node.eval(Obj("wrong a", "wrong b", "wrong c"))
def test_i_can_test_not(self):
node = PropertyEqualsNode("prop_a", "good value")
not_node = NotNode(node)
assert not not_node.eval(Obj(prop_a="good value"))
assert not_node.eval(Obj(prop_a="wrong value"))
def test_i_can_test_lambda_node(self):
node = LambdaNode(lambda o: o.prop_a + o.prop_b == "ab")
assert node.eval(Obj(prop_a="a", prop_b="b"))
assert not node.eval(Obj(prop_a="wrong value", prop_b="wrong value"))
assert not node.eval(Obj(prop_a="wrong value")) # exception is caught
def test_i_can_test_isa_node(self):
class_node = IsaNode(Obj)
assert class_node.eval(Obj(prop_a="value"))
assert not class_node.eval(TestExpressionParser())
concept_node = IsaNode(BuiltinConcepts.RETURN_VALUE)
assert concept_node.eval(ReturnValueConcept())
assert concept_node.eval(Concept(name="foo", key=BuiltinConcepts.RETURN_VALUE))
assert not concept_node.eval(Obj)
assert not concept_node.eval(Concept())
concept_node2 = IsaNode("foo")
assert concept_node2.eval(Concept("foo").init_key())
assert not concept_node2.eval(Obj)
assert not concept_node2.eval(Concept())
@pytest.mark.parametrize("expression, to_trueify, to_skip, expected", [
("a", ["b"], ["a"], "a"),
("b", ["b"], ["a"], "True"),
@@ -208,3 +170,321 @@ class TestExpressionParser(TestUsingMemoryBasedSheerka):
translated_node = TrueifyVisitor(to_trueify, to_skip).visit(expr_node)
assert str(translated_node) == expected
@pytest.mark.parametrize("expression, expected", [
("foo", None),
("", None),
("is_question()", True),
(" is_question() ", True),
("is_question ( ) ", True),
("is _question()", None),
("is_ question()", None),
("is _ question()", None),
("context.in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", True),
("not is_question()", False),
("not context.in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", False),
("not foo", None),
("not is_question() and not is_question()", False),
("not is_question() and is_question()", False),
("not is_question() and foo", False),
("is_question() and not is_question()", False),
("is_question() and is_question()", True),
("is_question() and foo", True),
("foo and not is_question()", False),
("foo and is_question()", True),
("foo and bar", None),
("not is_question() or not is_question()", False),
("not is_question() or is_question()", True),
("not is_question() or foo", False),
("is_question() or not is_question()", True),
("is_question() or is_question()", True),
("is_question() or foo", True),
("foo or not is_question()", False),
("foo or is_question()", True),
("foo or bar", None),
])
def test_is_a_question(self, expression, expected):
sheerka, context, parser = self.init_parser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
assert IsAQuestionVisitor().visit(expr_node) == expected
@pytest.mark.parametrize("expression, expected", [
("foo", "foo"),
("one two", "one two"),
("foo is a bar", CMV("is a", x='foo', y='bar')),
("one two is a bar", [CNC("is a", source="one two is a bar", x="one two", y="bar")]),
("foo is an foo bar",
[CNC("is an", source="foo is an foo bar", x=DoNotResolve(value='foo'), exclude_body=True)]),
])
def test_i_can_get_compiled_expr_from_simple_concepts_expressions(self, expression, expected):
concepts_map = {
"foo": Concept("foo"),
"bar": Concept("bar"),
"one two": Concept("one two"),
"is a": Concept("x is a y").def_var("x").def_var("y"),
"is an": Concept("x is an y", definition="('foo'|'bar')=x 'is an' 'foo bar'").def_var("x"),
}
sheerka, context, *concepts = self.init_test().with_concepts(*concepts_map.values(), create_new=True).unpack()
parser = ExpressionParser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
return_values, _ = parser.compile_conjunctions(context, [expr_node], "test")
assert len(return_values) == 1
ret = return_values[0]
if isinstance(expected, list):
expected_nodes = compute_expected_array(concepts_map, expression, expected)
assert ret.body.body == expected_nodes
else:
expected_concept = resolve_test_concept(concepts_map, expected)
assert ret.body.body == expected_concept
@pytest.mark.parametrize("expression", [
"a == 5",
"foo > 5",
"func() == 5",
"not a == 5",
"not foo > 5",
"not func() == 5",
"isinstance(a, int)",
"func()",
"not isinstance(a, int)",
"not func()"
])
def test_i_can_get_compiled_expr_from_simple_python_expressions(self, expression):
sheerka, context, = self.init_test().unpack()
parser = ExpressionParser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
return_values, _ = parser.compile_conjunctions(context, [expr_node], "test")
assert len(return_values) == 1
ret = return_values[0]
assert ret.status
python_node = ret.body.body.get_python_node()
_ast = ast.parse(expression, mode="eval")
expected_python_node = PythonNode(expression, _ast)
assert python_node == expected_python_node
@pytest.mark.parametrize("expression", [
"a and not b",
"not b and a",
"__ret and not __ret.status",
])
def test_i_can_compile_negative_conjunctions_when_pure_python(self, expression):
sheerka, context, *concepts = self.init_concepts("foo")
parser = ExpressionParser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
return_values, _ = parser.compile_conjunctions(context, expr_node.parts, "test")
ast_ = ast.parse(expression, "<source>", 'eval')
expected_python_node = PythonNode(expression, ast_)
assert len(return_values) == 1
ret = return_values[0]
assert sheerka.objvalue(ret) == expected_python_node
@pytest.mark.parametrize("expression, text_to_compile", [
("foo bar == 5", "__C__foo0bar__1001__C__ == 5"),
("not foo bar == 5", "not __C__foo0bar__1001__C__ == 5"),
])
def test_i_can_get_compiled_expr_from_python_and_concept(self, expression, text_to_compile):
sheerka, context, *concepts = self.init_test().with_concepts(Concept("foo bar"), create_new=True).unpack()
parser = ExpressionParser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
return_values, _ = parser.compile_conjunctions(context, [expr_node], "test")
assert len(return_values) == 1
ret = return_values[0]
assert ret.status
python_node = ret.body.body.get_python_node()
_ast = ast.parse(text_to_compile, mode="eval")
expected_python_node = PythonNode(text_to_compile, _ast, expression)
assert python_node == expected_python_node
def test_i_can_get_compiled_expr_from__mix_of_concepts_and_python(self):
sheerka, context, animal, cat, dog, pet, is_a, is_an = self.init_test().with_concepts(
Concept("animal"),
Concept("a cat"),
Concept("dog"),
Concept("pet"),
Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("x is an y", pre="is_question()", definition="('cat'|'bird')=x 'is an' animal").def_var("x"),
create_new=True
).unpack()
parser = ExpressionParser()
expression = "not a cat is a pet and not bird is an animal and not x > 5 and not dog is a pet"
expr_node = parser.parse(context, ParserInput(expression)).body.body
return_values, _ = parser.compile_conjunctions(context, expr_node.parts, "test")
to_compile = 'not __C__00var0000is0a000var001__1005__C__'
to_compile += ' and not __C__00var0000is0an0y__1006__C__'
to_compile += ' and not x > 5'
to_compile += ' and not __C__00var0000is0a000var001__1005_1__C__'
ast_ = ast.parse(to_compile, "<source>", 'eval')
expected_python_node = PythonNode(to_compile, ast_, expression)
assert len(return_values) == 1
ret = return_values[0]
python_node = ret.body.body
assert python_node == expected_python_node
assert python_node.objects == {
"__C__00var0000is0a000var001__1005__C__": CC(is_a, x=cat, y=pet),
"__C__00var0000is0an0y__1006__C__": CC(is_an, exclude_body=True, x=DoNotResolve("bird"), animal=animal),
"__C__00var0000is0a000var001__1005_1__C__": CMV(is_a, x="dog", y="pet"),
}
def test_i_can_get_compiled_expr_from_mix(self):
sheerka, context, animal, cat, dog, pet, is_a, is_an = self.init_test().with_concepts(
Concept("animal"),
Concept("a cat"),
Concept("dog"),
Concept("pet"),
Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("x is an y", pre="is_question()", definition="('cat'|'bird')=x 'is an' animal").def_var("x"),
create_new=True
).unpack()
expression = "a cat is a pet and bird is an animal and x > 5 and dog is a pet"
parser = ExpressionParser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
return_values, _ = parser.compile_conjunctions(context, expr_node.parts, "test")
assert len(return_values) == 1
ret = return_values[0]
to_compile = '__C__00var0000is0a000var001__1005__C__ and __C__00var0000is0an0y__1006__C__ and x > 5 and __C__00var0000is0a000var001__1005_1__C__'
ast_ = ast.parse(to_compile, "<source>", 'eval')
expected_python_node = PythonNode(to_compile, ast_, expression)
python_node = ret.body.body
assert python_node == expected_python_node
assert python_node.objects == {
"__C__00var0000is0a000var001__1005__C__": CC(is_a, x=cat, y=pet),
"__C__00var0000is0an0y__1006__C__": CC(is_an, exclude_body=True, x=DoNotResolve("bird"), animal=animal),
"__C__00var0000is0a000var001__1005_1__C__": CMV(is_a, x="dog", y="pet"),
}
def test_i_can_get_compiled_expr_when_multiple_choices(self):
sheerka, context, *concepts = self.init_test().with_concepts(
Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("x is a y", pre="is_question()", body="isa(x, y)").def_var("x").def_var("y"),
create_new=True
).unpack()
parser = ExpressionParser()
expression = "a is a b"
expr_node = parser.parse(context, ParserInput(expression)).body.body
return_values, _ = parser.compile_conjunctions(context, [expr_node], "test")
assert len(return_values) == 2
ret = return_values[0]
assert sheerka.objvalue(ret)[0].concept == CMV(concepts[0], x="a", y="b")
ret = return_values[1]
assert sheerka.objvalue(ret)[0].concept == CMV(concepts[1], x="a", y="b")
def test_i_can_get_compiled_expr_from_mix_when_multiple_choices(self):
sheerka, context, *concepts = self.init_test().with_concepts(
Concept("animal"),
Concept("a cat"),
Concept("dog"),
Concept("pet"),
Concept("x is a y", pre="is_question()", body="isinstance(x, y)").def_var("x").def_var("y"),
Concept("x is a y", pre="is_question()", body="isa(x, y)").def_var("x").def_var("y"),
Concept("x is an y", pre="is_question()", definition="('cat'|'bird')=x 'is an' animal").def_var("x"),
create_new=True
).unpack()
expression = "a cat is a pet and bird is an animal and x > 5 and dog is a pet"
parser = ExpressionParser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
return_values, _ = parser.compile_conjunctions(context, expr_node.parts, "test")
assert len(return_values) == 4
trimmed_source = "a cat is a pet and bird is an animal and x > 5 and dog is a pet"
current_ret = return_values[0]
python_source = "__C__00var0000is0a000var001__1005__C__ and __C__00var0000is0an0y__1007__C__ and x > 5 and __C__00var0000is0a000var001__1005_1__C__"
ast_ = ast.parse(python_source, "<source>", 'eval')
resolved_expected = PythonNode(python_source, ast_, trimmed_source)
assert sheerka.objvalue(current_ret) == resolved_expected
current_ret = return_values[1]
assert sheerka.isinstance(current_ret, BuiltinConcepts.RETURN_VALUE)
python_source = "__C__00var0000is0a000var001__1005__C__ and __C__00var0000is0an0y__1007__C__ and x > 5 and __C__00var0000is0a000var001__1006__C__"
ast_ = ast.parse(python_source, "<source>", 'eval')
resolved_expected = PythonNode(python_source, ast_, trimmed_source)
assert sheerka.objvalue(current_ret) == resolved_expected
current_ret = return_values[2]
assert sheerka.isinstance(current_ret, BuiltinConcepts.RETURN_VALUE)
python_source = "__C__00var0000is0a000var001__1006__C__ and __C__00var0000is0an0y__1007__C__ and x > 5 and __C__00var0000is0a000var001__1005__C__"
ast_ = ast.parse(python_source, "<source>", 'eval')
resolved_expected = PythonNode(python_source, ast_, trimmed_source)
assert sheerka.objvalue(current_ret) == resolved_expected
current_ret = return_values[3]
python_source = "__C__00var0000is0a000var001__1006__C__ and __C__00var0000is0an0y__1007__C__ and x > 5 and __C__00var0000is0a000var001__1006_1__C__"
ast_ = ast.parse(python_source, "<source>", 'eval')
resolved_expected = PythonNode(python_source, ast_, trimmed_source)
assert sheerka.objvalue(current_ret) == resolved_expected
@pytest.mark.parametrize("expression, expected_conditions, test_obj", [
(
"__ret",
["#__x_00__|__name__|'__ret'"],
ReturnValueConcept("Test", True, None)
),
(
"__ret.status == True",
["#__x_00__|__name__|'__ret'", "#__x_00__|status|True"],
ReturnValueConcept("Test", True, None)
),
(
"__ret.status",
["#__x_00__|__name__|'__ret'", "#__x_00__|status|True"],
ReturnValueConcept("Test", True, None)
),
(
"__ret and __ret.status",
["#__x_00__|__name__|'__ret'", "#__x_00__|status|True"],
ReturnValueConcept("Test", True, None)
),
])
def test_i_can_get_rete_condition_from_python(self, expression, expected_conditions, test_obj):
sheerka, context, = self.init_test().unpack()
expected_full_condition = get_rete_conditions(*expected_conditions)
parser = ExpressionParser()
expr_node = parser.parse(context, ParserInput(expression)).body.body
nodes = expr_node.parts if isinstance(expr_node, AndNode) else [expr_node]
_, rete_disjunctions = parser.compile_conjunctions(context, nodes, "test")
assert len(rete_disjunctions) == 1
assert rete_disjunctions == [expected_full_condition]
# check against a Rete network
network = ReteNetwork()
rule = Rule("test", expression, None)
rule.metadata.id = 9999
rule.metadata.is_compiled = True
rule.metadata.is_enabled = True
rule.rete_disjunctions = rete_disjunctions
network.add_rule(rule)
network.add_obj("__ret", test_obj)
matches = list(network.matches)
assert len(matches) > 0
+1 -1
View File
@@ -28,7 +28,7 @@ class TestFunctionParser(TestUsingMemoryBasedSheerka):
cmap[concept_name] = updated[i]
cls.shared_ontology = sheerka.get_ontology(context)
sheerka.pop_ontology()
sheerka.pop_ontology(context)
def init_parser(self, my_concepts_map=None, **kwargs):
if my_concepts_map is None:
+34 -7
View File
@@ -1,18 +1,18 @@
import ast
import pytest
import core.utils
from core.builtin_concepts import ParserResultConcept, BuiltinConcepts, ReturnValueConcept
from core.concept import Concept
from core.rule import Rule
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Token, TokenKind, Tokenizer
from parsers.BaseNodeParser import ConceptNode, UnrecognizedTokensNode, RuleNode
from parsers.BaseNodeParser import ConceptNode, UnrecognizedTokensNode, RuleNode, SourceCodeNode
from parsers.PythonParser import PythonNode
from parsers.PythonWithConceptsParser import PythonWithConceptsParser
from parsers.UnrecognizedNodeParser import UnrecognizedNodeParser
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.parsers.parsers_utils import get_source_code_node
unrecognized_nodes_parser = UnrecognizedNodeParser()
@@ -73,7 +73,8 @@ class TestPythonWithConceptsParser(TestUsingMemoryBasedSheerka):
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert wrapper.source == "foo + 1"
assert isinstance(return_value, PythonNode)
assert return_value.source == "foo + 1"
assert return_value.source == "__C__foo__C__ + 1"
assert return_value.original_source == "foo + 1"
assert return_value.get_dump(return_value.ast_) == to_str_ast("__C__foo__C__ + 1")
assert return_value.objects["__C__foo__C__"] == foo
@@ -93,7 +94,8 @@ class TestPythonWithConceptsParser(TestUsingMemoryBasedSheerka):
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert wrapper.source == "foo + 1"
assert isinstance(return_value, PythonNode)
assert return_value.source == "foo + 1"
assert return_value.source == "__C__foo__1001__C__ + 1"
assert return_value.original_source == "foo + 1"
assert return_value.get_dump(return_value.ast_) == to_str_ast("__C__foo__1001__C__ + 1")
assert return_value.objects["__C__foo__1001__C__"] == foo
@@ -124,7 +126,8 @@ class TestPythonWithConceptsParser(TestUsingMemoryBasedSheerka):
assert context.sheerka.isinstance(parser_result, BuiltinConcepts.PARSER_RESULT)
assert parser_result.source == "func(foo, bar)"
assert isinstance(return_value, PythonNode)
assert return_value.source == "func(foo, bar)"
assert return_value.source == "func(__C__foo__1001__C__, __C__bar__1002__C__)"
assert return_value.original_source == "func(foo, bar)"
assert return_value.get_dump(return_value.ast_) == to_str_ast("func(__C__foo__1001__C__, __C__bar__1002__C__)")
assert return_value.objects["__C__foo__1001__C__"] == foo
assert return_value.objects["__C__bar__1002__C__"] == bar
@@ -144,7 +147,8 @@ class TestPythonWithConceptsParser(TestUsingMemoryBasedSheerka):
assert context.sheerka.isinstance(wrapper, BuiltinConcepts.PARSER_RESULT)
assert wrapper.source == "r:|rule_id: + 1"
assert isinstance(return_value, PythonNode)
assert return_value.source == "r:|rule_id: + 1"
assert return_value.source == "__R____rule_id__R__ + 1"
assert return_value.original_source == "r:|rule_id: + 1"
assert return_value.get_dump(return_value.ast_) == to_str_ast("__R____rule_id__R__ + 1")
assert return_value.objects["__R____rule_id__R__"] == rule
@@ -193,3 +197,26 @@ class TestPythonWithConceptsParser(TestUsingMemoryBasedSheerka):
assert not result.status
assert context.sheerka.isinstance(result.value, BuiltinConcepts.NOT_FOR_ME)
def test_i_can_parse_nodes_when_source_code_node(self):
context = self.get_context()
foo = Concept("foo")
bar = Concept("bar")
nodes = [
UnrecognizedTokensNode(0, 1, list(Tokenizer("not ", yield_eof=False))),
get_source_code_node(2, "foo == 1", {"foo": foo}),
UnrecognizedTokensNode(7, 9, list(Tokenizer(" and ", yield_eof=False))),
get_source_code_node(10, "bar < 1", {"bar": bar}),
]
expected_ast = ast.parse('not __C__foo__C__ == 1 and __C__bar__C__ < 1', "<source>", 'eval')
parser = PythonWithConceptsParser()
result = parser.parse_nodes(context, nodes)
result_python_node = result.value.value
assert isinstance(result_python_node, PythonNode)
assert result_python_node.source == 'not __C__foo__C__ == 1 and __C__bar__C__ < 1'
assert result_python_node.ast_str == PythonNode.get_dump(expected_ast)
assert result_python_node.original_source == "not foo == 1 and bar < 1"
assert result_python_node.objects == {"__C__foo__C__": foo, "__C__bar__C__": bar}
+3 -3
View File
@@ -14,17 +14,17 @@ class TestRuleParser(TestUsingMemoryBasedSheerka):
@classmethod
def setup_class(cls):
init_test_helper = cls().init_test(cache_only=False, ontology="#TestRuleParser#")
sheerka, context, *updated = init_test_helper.with_rules(*my_rules).unpack()
sheerka, context, *updated = init_test_helper.with_format_rules(*my_rules).unpack()
cls.shared_ontology = sheerka.get_ontology(context)
sheerka.pop_ontology()
sheerka.pop_ontology(context)
def init_parser(self, rules=None, **kwargs):
if rules is None:
sheerka, context = self.init_test().unpack()
sheerka.add_ontology(context, self.shared_ontology)
else:
sheerka, context, *updated = self.init_test().with_rules(*rules, **kwargs).unpack()
sheerka, context, *updated = self.init_test().with_format_rules(*rules, **kwargs).unpack()
parser = RuleParser()
return sheerka, context, parser
+1 -1
View File
@@ -66,7 +66,7 @@ class TestSyaNodeParser(TestUsingMemoryBasedSheerka):
CONCEPT_COMPARISON_CONTEXT)
cls.shared_ontology = sheerka.get_ontology(context)
sheerka.pop_ontology()
sheerka.pop_ontology(context)
def init_parser(self,
my_concepts_map=None,
+1 -1
View File
@@ -85,7 +85,7 @@ class TestUnrecognizedNodeParser(TestUsingMemoryBasedSheerka):
concepts_map["plus"], 'Sya')
cls.shared_ontology = sheerka.get_ontology(context)
sheerka.pop_ontology()
sheerka.pop_ontology(context)
def init_parser(self, my_concepts_map=None, **kwargs):
if my_concepts_map is None:
+10 -2
View File
@@ -31,6 +31,14 @@ class Obj:
class TestSheerkaPickler(TestUsingMemoryBasedSheerka):
user_input_id = 0
return_value_id = 0
@classmethod
def setup(cls):
sheerka = cls().get_sheerka()
cls.user_input_id = sheerka.get_by_key("__USER_INPUT").id
cls.return_value_id = sheerka.get_by_key("__RETURN_VALUE").id
@pytest.mark.parametrize("obj, expected", [
(1, 1),
@@ -194,13 +202,13 @@ class TestSheerkaPickler(TestUsingMemoryBasedSheerka):
sheerka, context = self.init_test().unpack()
sheerka.push_ontology(context, "new ontology")
ontology = sheerka.pop_ontology().body.body
ontology = sheerka.pop_ontology(context).body.body
obj = sheerka.ret(sheerka.name, True, ontology)
flatten = SheerkaPickler(sheerka).flatten(obj)
assert flatten == {
'_sheerka/obj': 'core.builtin_concepts.ReturnValueConcept',
'concept/id': ('__RETURN_VALUE', '43'),
'concept/id': ('__RETURN_VALUE', self.return_value_id),
'status': True,
'value': 'new ontology',
'who': '__SHEERKA'}
+24
View File
@@ -0,0 +1,24 @@
from core.rule import Rule, ACTION_TYPE_TEST
class RuleForTestingRete(Rule):
def __init__(self, *disjunctions, name=None):
"""
:param disjunctions: list of list of Condition. List of OR. inner list is a list of AND
:para name: optional name for the rule
"""
try:
predicate = " or ".join([
" and ".join([str(c) for c in disjunction.conditions]) for disjunction in
disjunctions])
except AttributeError:
predicate = "N/A"
super().__init__(ACTION_TYPE_TEST, name, predicate)
self.disjunctions = disjunctions
self.metadata.id = 9999
self.metadata.is_compiled = True
self.metadata.is_enabled = True
def get_rete_disjunctions(self):
return self.disjunctions
View File
+31
View File
@@ -0,0 +1,31 @@
from sheerkarete.common import ReteToken, WME, V
from sheerkarete.conditions import Condition, NegatedConjunctiveConditions
def test_token():
tdummy = ReteToken(None, None)
t0 = ReteToken(tdummy, WME('B1', 'on', 'B2'))
assert tdummy.parent is None
assert t0.parent == tdummy
assert tdummy.children == [t0]
assert t0.children == []
def test_condition_vars():
c0 = Condition(V('x'), 'is', V('y'))
assert c0.vars == [('identifier', V("x")), ('value', V("y"))]
def test_condition_contain():
c0 = Condition(V('a'), V('b'), V('c'))
assert c0.contain(V('a'))
assert not c0.contain(V('d'))
def test_ncc():
c0 = Condition(V('a'), V('b'), V('c'))
c1 = NegatedConjunctiveConditions(Condition(V('x'), 'color', 'red'))
c2 = NegatedConjunctiveConditions(c0, c1)
assert c2.number_of_conditions == 2
+341
View File
@@ -0,0 +1,341 @@
from sheerkarete.common import WME, V
from sheerkarete.conditions import NotEqualsCondition, AndConditions, Condition, NegatedCondition, \
NegatedConjunctiveConditions, FilterCondition, BindCondition
from sheerkarete.network import ReteNetwork
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.sheerkarete.RuleForTestingRete import RuleForTestingRete
class TestReteConditions(TestUsingMemoryBasedSheerka):
def test_i_can_manage_condition(self):
network = ReteNetwork()
condition = Condition("id", "attr", "value")
rule = RuleForTestingRete(AndConditions([condition]))
network.add_rule(rule)
assert len(list(network.matches)) == 0
wme = WME("id", "attr", "value")
network.add_wme(wme)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule]
assert matches[0].token.wmes == [wme]
# remove wme
network.remove_wme(wme)
assert len(list(network.matches)) == 0
def test_i_can_manage_not_equals_condition(self):
network = ReteNetwork()
# I can add the condition
cond1 = NotEqualsCondition("id", "value", 5)
rule1 = RuleForTestingRete(AndConditions([cond1]), name="first id != 5")
network.add_rule(rule1)
assert list(network.alpha_hash.keys()) == [("id", "value", "*")]
assert len(network.alpha_hash[("id", "value", "*")]) == 1
assert network.alpha_hash[("id", "value", "*")][0].condition == cond1
# Another rule with the same condition
rule2 = RuleForTestingRete(AndConditions([cond1]), name="second id != 5")
network.add_rule(rule2)
assert list(network.alpha_hash.keys()) == [("id", "value", "*")]
assert len(network.alpha_hash[("id", "value", "*")]) == 1
assert network.alpha_hash[("id", "value", "*")][0].condition == cond1
cond2 = NotEqualsCondition("id", "value", 6)
rule3 = RuleForTestingRete(AndConditions([cond2]), name="id != 6")
network.add_rule(rule3)
assert list(network.alpha_hash.keys()) == [("id", "value", "*")]
assert len(network.alpha_hash[("id", "value", "*")]) == 2
assert network.alpha_hash[("id", "value", "*")][0].condition == cond1
assert network.alpha_hash[("id", "value", "*")][1].condition == cond2
# I can match
wme1 = WME("id", "value", "1")
network.add_wme(wme1)
matches = list(network.matches)
assert len(matches) == 2
assert matches[0].pnode.rules == [rule1, rule2]
assert matches[1].pnode.rules == [rule3]
# I can remove
network.remove_wme(wme1)
assert len(list(network.matches)) == 0
# I can add a non match
wme2 = WME("id", "value", 5)
network.add_wme(wme2)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule3]
def test_i_can_manage_negative_condition(self):
network = ReteNetwork()
condition = NegatedCondition("id", "attr", "value")
rule = RuleForTestingRete(AndConditions([condition]))
network.add_rule(rule)
assert len(network.beta_root.children) == 1
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule]
assert matches[0].token.wmes == [None]
wme = WME("id", "attr", "value")
network.add_wme(wme)
matches = list(network.matches)
assert len(matches) == 0
# remove wme
network.remove_wme(wme)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule]
assert matches[0].token.wmes == [None]
def test_i_can_manage_ncc(self):
network = ReteNetwork()
c1 = Condition(V("x"), "is a", "light")
c2 = Condition(V("x"), "is", "red")
c3 = Condition(V("x"), "is", "blue")
rule = RuleForTestingRete(AndConditions([c1, NegatedConjunctiveConditions(c2, c3)]))
network.add_rule(rule)
wme1 = WME("x1", "is a", "light")
wme2 = WME("x1", "is", "blue")
network.add_wme(wme1)
network.add_wme(wme2)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [wme1, None]
wme3 = WME("x1", "is", "red")
network.add_wme(wme3)
assert len(list(network.matches)) == 0
wme4 = WME("x2", "is a", "light")
network.add_wme(wme4)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [wme4, None]
# remove a wme
network.remove_wme(wme2)
matches = list(network.matches)
assert len(matches) == 2
assert matches[0].token.wmes == [wme4, None]
assert matches[1].token.wmes == [wme1, None]
def test_i_can_manage_ncc_when_already_inserted_elements(self):
network = ReteNetwork()
wme1 = WME("x1", "is a", "light")
wme2 = WME("x1", "is", "blue")
network.add_wme(wme1)
network.add_wme(wme2)
c1 = Condition(V("x"), "is a", "light")
c2 = Condition(V("x"), "is", "red")
c3 = Condition(V("x"), "is", "blue")
rule = RuleForTestingRete(AndConditions([c1, NegatedConjunctiveConditions(c2, c3)]))
network.add_rule(rule)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [wme1, None]
wme3 = WME("x1", "is", "red")
network.add_wme(wme3)
assert len(list(network.matches)) == 0
# remove back
network.remove_wme(wme2)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [wme1, None]
def test_i_can_manage_ncc_when_already_inserted_elements2(self):
network = ReteNetwork()
wme1 = WME("x1", "is a", "light")
wme2 = WME("x1", "is", "blue")
wme3 = WME("x1", "is", "red")
network.add_wme(wme1)
network.add_wme(wme2)
network.add_wme(wme3)
c1 = Condition(V("x"), "is a", "light")
c2 = Condition(V("x"), "is", "red")
c3 = Condition(V("x"), "is", "blue")
rule = RuleForTestingRete(AndConditions([c1, NegatedConjunctiveConditions(c2, c3)]))
network.add_rule(rule)
assert len(list(network.matches)) == 0
# remove a wme
network.remove_wme(wme3)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [wme1, None]
def test_i_can_manage_filter_condition(self):
network = ReteNetwork()
c1 = Condition("value", "equals", V("x"))
c2 = Condition("value", "equals", V("y"))
c3 = FilterCondition(lambda x, y: x == y + 1)
rule = RuleForTestingRete(AndConditions([c1, c2, c3]))
network.add_rule(rule)
assert len(list(network.matches)) == 0
wme1 = WME("value", "equals", 5)
wme2 = WME("value", "equals", 3)
network.add_wme(wme1)
network.add_wme(wme2)
assert len(list(network.matches)) == 0
wme3 = WME("value", "equals", 4)
network.add_wme(wme3)
matches = list(network.matches)
assert len(matches) == 2
assert matches[0].token.wmes == [wme1, wme3]
assert matches[1].token.wmes == [wme3, wme2]
# remove a WME
network.remove_wme(wme1)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [wme3, wme2]
def test_i_can_manage_filter_when_it_is_the_first_condition(self):
network = ReteNetwork()
c1 = FilterCondition(lambda: True)
rule = RuleForTestingRete(AndConditions([c1]))
network.add_rule(rule)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [None]
assert matches[0].token.binding == {}
# remove wme
network.remove_rule(rule)
assert len(list(network.matches)) == 0
def test_i_can_filter_already_inserted_elements(self):
network = ReteNetwork()
wme1 = WME("value", "equals", 5)
wme2 = WME("value", "equals", 3)
wme3 = WME("value", "equals", 4)
network.add_wme(wme1)
network.add_wme(wme2)
network.add_wme(wme3)
c1 = Condition("value", "equals", V("x"))
c2 = Condition("value", "equals", V("y"))
c3 = FilterCondition(lambda x, y: x == y + 1)
rule = RuleForTestingRete(AndConditions([c1, c2, c3]))
network.add_rule(rule)
matches = list(network.matches)
assert len(matches) == 2
assert matches[0].token.wmes == [wme3, wme2]
assert matches[1].token.wmes == [wme1, wme3]
def test_i_can_manage_binding_conditions(self):
network = ReteNetwork()
c1 = Condition("value", "equals", V("x"))
c2 = BindCondition(lambda x: x + 5, V("z"))
rule = RuleForTestingRete(AndConditions([c1, c2]))
network.add_rule(rule)
assert len(list(network.matches)) == 0
wme1 = WME("value", "equals", 1)
network.add_wme(wme1)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [wme1]
assert matches[0].token.binding == {V("z"): 6, V("x"): 1}
# remove wme
network.remove_wme(wme1)
assert len(list(network.matches)) == 0
def test_i_can_manage_bind_when_it_is_the_first_condition(self):
network = ReteNetwork()
c1 = BindCondition(lambda: 5, V("x"))
rule = RuleForTestingRete(AndConditions([c1]))
network.add_rule(rule)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].token.wmes == [None]
assert matches[0].token.binding == {V("x"): 5}
# remove wme
network.remove_rule(rule)
assert len(list(network.matches)) == 0
def test_i_can_match_on_the_correct_variable_name(self):
network = ReteNetwork()
condition = Condition(V("x"), "attr", V("x"))
rule = RuleForTestingRete(AndConditions([condition]))
network.add_rule(rule)
wme = WME('a', 'attr', 'c')
network.add_wme(wme)
assert len(list(network.matches)) == 0
wme = WME('a', 'attr', 'a')
network.add_wme(wme)
assert len(list(network.matches)) == 1
def test_condition_test(self):
c0 = Condition(V('x'), 'color', 'red')
w0 = WME('B1', 'color', 'red')
w1 = WME('B1', 'color', 'blue')
assert c0.test(w0)
assert not c0.test(w1)
c1 = Condition(V('x'), 'is', V('x'))
w2 = WME('B1', 'is', 'B3')
assert not c1.test(w2)
def test_black_white(self):
# KSI 2021-02-07. I simply copy this test
net = ReteNetwork()
c1 = Condition(V('item'), 'cat', V('cid'))
c2 = Condition(V('item'), 'shop', V('sid'))
white = NegatedConjunctiveConditions(
NegatedCondition(V('item'), 'cat', '100'),
NegatedCondition(V('item'), 'cat', '101'),
NegatedCondition(V('item'), 'cat', '102'),
)
n1 = NegatedCondition(V('item'), 'shop', '1')
n2 = NegatedCondition(V('item'), 'shop', '2')
n3 = NegatedCondition(V('item'), 'shop', '3')
rule = RuleForTestingRete(AndConditions([c1, c2, white, n1, n2, n3]))
net.add_rule(rule)
wmes = [
WME('item:1', 'cat', '101'),
WME('item:1', 'shop', '4'),
WME('item:2', 'cat', '100'),
WME('item:2', 'shop', '1'),
]
for wme in wmes:
net.add_wme(wme)
p0 = rule.rete_p_nodes[0]
assert len(list(p0.activations)) == 1
assert list(p0.activations)[0].binding[V('item')] == 'item:1'
+685
View File
@@ -0,0 +1,685 @@
import pytest
from core.builtin_concepts import ReturnValueConcept
from core.concept import Concept, DEFINITION_TYPE_DEF
from core.rule import Rule, ACTION_TYPE_EXEC
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager
from sheerkarete.beta import BetaMemory
from sheerkarete.common import V, WME, ReteToken
from sheerkarete.conditions import Condition, NegatedCondition, AndConditions
from sheerkarete.join_node import JoinNode
from sheerkarete.negative_node import NegativeNode
from sheerkarete.network import ReteNetwork, FACT_ID
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.sheerkarete.RuleForTestingRete import RuleForTestingRete
class TestReteNetwork(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("condition, expected_key", [
(Condition("id", "attr", "value"), ("id", "attr", "value")),
(Condition(V("x"), "attr", "value"), ("*", "attr", "value")),
(Condition("id", V("x"), "value"), ("id", "*", "value")),
(Condition("id", "attr", V("x")), ("id", "attr", "*")),
(Condition(V("x"), V("x"), V("x")), ("*", "*", "*")),
])
def test_i_can_create_alpha_memory(self, condition, expected_key):
network = ReteNetwork()
amem = network.build_or_share_alpha_memory(condition)
assert amem.key == expected_key
assert amem.key in network.alpha_hash
def test_same_amem_is_reused(self):
network = ReteNetwork()
condition = Condition("id", "attr", "value")
amem1 = network.build_or_share_alpha_memory(condition)
amem2 = network.build_or_share_alpha_memory(condition)
assert amem1 == amem2
assert len(network.alpha_hash) == 1
def test_i_can_initialize_a_simple_network(self):
network = ReteNetwork()
condition = Condition("id", "attr", "value")
rule = RuleForTestingRete(AndConditions([condition]))
network.add_rule(rule)
assert condition.get_key() in network.alpha_hash
assert len(network.pnodes) == 1
join_node = network.pnodes[0].parent
assert isinstance(join_node, JoinNode)
assert isinstance(join_node.parent, BetaMemory)
assert join_node.amem == network.alpha_hash[condition.get_key()][0]
def test_i_can_add_multiple_rules_with_the_same_conditions(self):
network = ReteNetwork()
condition = Condition("id", "attr", "value")
rule1 = RuleForTestingRete(AndConditions([condition]))
rule2 = RuleForTestingRete(AndConditions([condition]))
network.add_rule(rule1)
network.add_rule(rule2)
assert len(network.pnodes) == 1
assert network.pnodes[0].rules == [rule1, rule2]
def test_i_can_update_conditions_attributes_by_id_when_constraint_on__name__(self):
network = ReteNetwork()
conditions = [Condition(V("x"), "__name__", "fact_name"),
Condition(V("x"), "attr1", "value1"),
Condition(V("x"), "attr2", "value1")]
rule = RuleForTestingRete(AndConditions(conditions))
network.add_rule(rule)
assert network.attributes_by_id == {"fact_name": ["__name__", "attr1", "attr2"]}
def test_adding_obj_when_no_rule_has_no_effect(self):
network = ReteNetwork()
ret = ReturnValueConcept("test", True, "value")
network.add_obj("__ret", ret)
assert network.working_memory == set()
def test_adding_obj_when_rule_with_no_attribute_constraint(self):
"""
When a rule has no attribute constraint, we are forced to provide all the known properties
"""
network = ReteNetwork()
ret = ReturnValueConcept("test", True, "value")
network.add_rule(RuleForTestingRete(
AndConditions([Condition("__ret", "*", True)]))) # test only, this rule cannot be matched
network.add_obj("__ret", ret)
assert network.working_memory == {
WME("f-00000", "status", True),
WME("f-00000", "value", "value"),
WME("f-00000", "body", "value"),
WME("f-00000", "parents", None),
WME("f-00000", "id", None),
WME("f-00000", "name", "__RETURN_VALUE"),
WME("f-00000", "key", "__RETURN_VALUE"),
WME("f-00000", "self", ret),
WME("f-00000", "who", "test"),
}
def test_adding_obj_when_rule_with_attributes_constraints(self):
"""
When a rule with attribute constraint, we only add the requested attributes
"""
network = ReteNetwork()
ret = ReturnValueConcept("test", True, "value")
network.add_rule(RuleForTestingRete(
AndConditions([Condition("__ret", "status", True), Condition("__ret", "body", "value")])))
network.add_obj("__ret", ret)
assert network.working_memory == {
WME("f-00000", "status", True),
WME("f-00000", "body", "value"),
}
def test_adding_obj_when_requested_attribute_is_not_found(self):
"""
When a rule with attribute constraint, we only add the requested attributes
"""
network = ReteNetwork()
ret = ReturnValueConcept("test", True, "value")
network.add_rule(RuleForTestingRete(AndConditions([Condition("__ret", "not found", True),
Condition("__ret", "body", "value")])))
network.add_obj("__ret", ret)
assert network.working_memory == {
WME("f-00000", "body", "value"),
}
def test_adding_object_when_id_is_not_defined_but_attribute_is_known(self):
network = ReteNetwork()
rule = RuleForTestingRete(AndConditions([
Condition(V("1"), "status", True),
]))
network.add_rule(rule)
ret1 = ReturnValueConcept("test", True, "first one")
ret2 = ReturnValueConcept("test", False, "to discard")
network.add_obj("__ret", ret1)
network.add_obj("__ret", ret2)
assert network.working_memory == {
WME("f-00000", "status", True),
WME("f-00001", "status", False),
}
def test_adding_obj_full_test_obj_is_a_concept(self):
"""
When the value is a concept, we create sub objects, to ease the requests
"""
sheerka, context, boy, the, greetings = self.init_concepts(
"boy",
Concept("the x").def_var("x"),
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a")
)
network = ReteNetwork()
the_boy = sheerka.new(the, x=boy)
hello_the_boy = sheerka.new(greetings, a=the_boy)
# original condition may look like
# __ret.status
# and __ret.body == $body
# and isinstance($body, Concept)
# and $body.name == "greetings"
# and $body.get_value("a") == $the
# and isinstance($the, Concept)
# and $the.id == 1002
# and $the.get_value("x") = $boy
# and instance($boy, Concept)
# and $boy.name = "boy"
ret = ReturnValueConcept("test", True, hello_the_boy)
rule = RuleForTestingRete(AndConditions([Condition(V("ret"), "__name__", "__ret"),
Condition(V("ret"), "status", True),
Condition(V("ret"), "body", V("body")),
Condition(V("body"), "name", "greetings"),
Condition(V("body"), "a", V("the")),
Condition(V("the"), "id", '1002'),
Condition(V("the"), "x", V("boy")),
Condition(V("boy"), "name", "boy"),
]))
network.add_rule(rule)
network.add_obj("__ret", ret)
assert network.working_memory == {
WME("f-00000", "__name__", "__ret"),
WME("f-00000", "status", True),
WME("f-00000", "body", "f-00000.body"),
WME("f-00000.body", "id", "1003"),
WME("f-00000.body", "name", "greetings"),
WME("f-00000.body", "key", "hello __var__0"),
WME("f-00000.body", "a", "f-00000.body.a"),
WME("f-00000.body", "self", ret.body),
WME("f-00000.body.a", "id", "1002"),
WME("f-00000.body.a", "name", "the x"),
WME("f-00000.body.a", "key", "the __var__0"),
WME("f-00000.body.a", "x", "f-00000.body.a.x"),
WME("f-00000.body.a", "self", the_boy),
WME("f-00000.body.a.x", "id", "1001"),
WME("f-00000.body.a.x", "name", "boy"),
WME("f-00000.body.a.x", "key", "boy"),
WME("f-00000.body.a.x", "self", boy),
}
# sanity check that the WME produced match the condition
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule]
assert network.facts == {'f-00000': ret}
def test_i_can_distinguish_objects_with_different_value(self):
network = ReteNetwork()
rule = RuleForTestingRete(AndConditions([
Condition(V("1"), "status", True),
Condition(V("1"), "value", "first one"),
Condition(V("2"), "status", True),
Condition(V("2"), "value", "second one")
]))
network.add_rule(rule)
ret1 = ReturnValueConcept("test", True, "first one")
ret2 = ReturnValueConcept("test", False, "to discard")
ret3 = ReturnValueConcept("test", True, "second one")
network.add_obj("__ret", ret1)
network.add_obj("__ret", ret2)
network.add_obj("__ret", ret3)
assert network.working_memory == {
WME("f-00000", "status", True),
WME("f-00000", "value", "first one"),
WME("f-00001", "status", False),
WME("f-00001", "value", "to discard"),
WME("f-00002", "status", True),
WME("f-00002", "value", "second one"),
}
# Check that the rule is fired
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule]
@pytest.mark.parametrize("conditions, expected", [
([Condition("id", "some_weird_attribute", "value")], {'id': ['some_weird_attribute']}),
([Condition("id", "attr1", V('x')), Condition("id", "attr2", "*")], {'id': ['attr1', 'attr2']}),
([Condition(V('x'), "attr", "value")], {}),
([Condition("id", V('x'), "value")], {'id': ["*"]}),
])
def test_attributes_by_identifier_are_updated_upon_condition_creation(self, conditions, expected):
network = ReteNetwork()
rule = RuleForTestingRete(AndConditions(conditions))
network.add_rule(rule)
assert network.attributes_by_id == expected
def test_i_cannot_add_the_same_object_twice(self):
network = ReteNetwork()
rule = RuleForTestingRete(AndConditions([Condition("__obj", "attr", "value")]))
network.add_rule(rule)
foo = Concept("foo")
network.add_obj("__obj", foo)
with pytest.raises(ValueError):
network.add_obj("__obj", foo)
def test_i_can_remove_wme(self):
network = ReteNetwork()
cond1 = Condition("id", "attr", "value")
wme = WME("id", "attr", "value")
rule = RuleForTestingRete(AndConditions([cond1]))
network.add_rule(rule)
network.add_wme(wme)
assert len(list(network.matches)) == 1
network.remove_wme(wme)
assert len(list(network.matches)) == 0
def test_i_can_remove_wme_when_multiple_p_nodes(self):
network = ReteNetwork()
cond1 = Condition("id", "attr", V("x"))
cond2 = Condition("id", "attr", V("y"))
rule1 = RuleForTestingRete(AndConditions([cond1]))
rule2 = RuleForTestingRete(AndConditions([cond2]))
network.add_rule(rule1)
network.add_rule(rule2)
assert len(network.pnodes) == 2
wme = WME("id", "attr", "value")
network.add_wme(wme)
assert len(list(network.matches)) == 2
network.remove_wme(wme)
assert len(list(network.matches)) == 0
def test_i_can_remove_and_add_again_a_wme(self):
network = ReteNetwork()
cond1 = Condition("id", "attr", "value")
wme = WME("id", "attr", "value")
rule = RuleForTestingRete(AndConditions([cond1]))
network.add_rule(rule)
network.add_wme(wme)
network.remove_wme(wme)
assert len(list(network.matches)) == 0
network.add_wme(wme)
assert len(list(network.matches)) == 1
def test_i_can_remove_a_fact(self):
sheerka, context, boy, the, greetings = self.init_concepts(
"boy",
Concept("the x").def_var("x"),
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a")
)
network = ReteNetwork()
the_boy = sheerka.new(the, x=boy)
hello_the_boy = sheerka.new(greetings, a=the_boy)
# to allow all attributes for '__ret'
rule = RuleForTestingRete(AndConditions([Condition(V("ret"), "__name__", "__ret"),
Condition(V("ret"), V("x"), V("y"))]))
network.add_rule(rule)
ret = ReturnValueConcept("test", True, hello_the_boy)
network.add_obj("__ret", ret)
# sanity check
assert len(network.working_memory) > 0
assert len(list(network.matches)) > 0
# remove the object
network.remove_obj(ret)
assert len(network.working_memory) == 0
assert len(list(network.matches)) == 0
def test_i_cannot_remove_an_object_with_no_fact_id(self):
network = ReteNetwork()
with pytest.raises(ValueError):
network.remove_obj(object())
def test_i_cannot_remove_an_object_that_is_not_in_the_system(self):
network = ReteNetwork()
obj = ReturnValueConcept("who", True, "value")
setattr(obj, FACT_ID, "xxx")
with pytest.raises(ValueError):
network.remove_obj(obj)
def test_i_can_find_stack_of_two_blocks_to_the_left_of_the_red_block(self):
network = ReteNetwork()
c1 = Condition(V("x"), "on", V("y"))
c2 = Condition(V("y"), "left-of", V("z"))
c3 = Condition(V("z"), "color", "red")
rule = RuleForTestingRete(AndConditions([c1, c2, c3]))
network.add_rule(rule)
wme1 = WME("B1", "on", "B2")
wme2 = WME("B1", "on", "B3")
wme3 = WME("B1", "color", "red")
wme4 = WME("B2", "on", "table")
wme5 = WME("B2", "left-of", "B3")
wme6 = WME("B2", "color", "blue")
wme7 = WME("B3", "left-of", "B4")
wme8 = WME("B3", "on", "table")
wme9 = WME("B3", "color", "red")
network.add_wme(wme1)
network.add_wme(wme2)
network.add_wme(wme3)
network.add_wme(wme4)
network.add_wme(wme5)
network.add_wme(wme6)
network.add_wme(wme7)
network.add_wme(wme8)
network.add_wme(wme9)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule]
assert matches[0].token.wmes == [wme1, wme5, wme9]
am1 = network.build_or_share_alpha_memory(c1)
am2 = network.build_or_share_alpha_memory(c2)
am3 = network.build_or_share_alpha_memory(c3)
assert am1.items == [wme1, wme2, wme4, wme8]
assert am2.items == [wme5, wme7]
assert am3.items == [wme3, wme9]
dummy_join = am1.successors[0]
join_on_value_y = am2.successors[0]
join_on_value_z = am3.successors[0]
match_c0 = dummy_join.children[0]
match_c0c1 = join_on_value_y.children[0]
match_c0c1c2 = join_on_value_z.children[0]
assert [t.wmes for t in match_c0.items] == [[wme1], [wme2], [wme4], [wme8]]
assert [t.wmes for t in match_c0c1.items] == [[wme1, wme5], [wme2, wme7]]
assert [t.wmes for t in match_c0c1c2.items] == [[wme1, wme5, wme9]]
t0 = ReteToken(ReteToken(None, None), wme1)
t1 = ReteToken(t0, wme5)
t2 = ReteToken(t1, wme9)
assert match_c0c1c2.items[0] == t2
def test_i_can_find_stack_of_two_blocks_to_the_left_of_one_that_is_not_red(self):
network = ReteNetwork()
c1 = Condition(V("x"), "on", V("y"))
c2 = Condition(V("y"), "left-of", V("z"))
c3 = NegatedCondition(V("z"), "color", "red")
rule = RuleForTestingRete(AndConditions([c1, c2, c3]))
network.add_rule(rule)
wme1 = WME("B1", "on", "B2")
wme2 = WME("B1", "on", "B3")
wme3 = WME("B1", "color", "red")
wme4 = WME("B2", "on", "table")
wme5 = WME("B2", "left-of", "B3")
wme6 = WME("B2", "color", "blue")
wme7 = WME("B3", "left-of", "B4")
wme8 = WME("B3", "on", "table")
wme9 = WME("B3", "color", "red")
network.add_wme(wme1)
network.add_wme(wme2)
network.add_wme(wme3)
network.add_wme(wme4)
network.add_wme(wme5)
network.add_wme(wme6)
network.add_wme(wme7)
network.add_wme(wme8)
network.add_wme(wme9)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule]
assert matches[0].token.wmes == [wme2, wme7, None]
am1 = network.build_or_share_alpha_memory(c1)
am2 = network.build_or_share_alpha_memory(c2)
am3 = network.build_or_share_alpha_memory(c3)
assert am1.items == [wme1, wme2, wme4, wme8]
assert am2.items == [wme5, wme7]
assert am3.items == [wme3, wme9] # same than positif condition
dummy_join = am1.successors[0]
join_on_value_y = am2.successors[0]
join_on_value_z = am3.successors[0]
match_c0 = dummy_join.children[0]
match_c0c1 = join_on_value_y.children[0]
match_c0c1c2 = join_on_value_z.children[0]
assert [t.wmes for t in match_c0.items] == [[wme1], [wme2], [wme4], [wme8]]
assert [t.wmes for t in match_c0c1.items] == [[wme1, wme5], [wme2, wme7]]
assert type(join_on_value_z) == NegativeNode
assert [t.wmes for t in match_c0c1c2.items] == [[wme2, wme7, None]]
def test_i_can_remove_a_rule(self):
network = ReteNetwork()
rule1 = RuleForTestingRete(AndConditions([Condition("id", "attr", "value")]))
network.add_rule(rule1)
network.remove_rule(rule1)
assert len(network.pnodes) == 0
assert len(network.alpha_hash) == 0
assert len(network.rules) == 0
# assert len(network.attributes_by_id) == 0
# assert len(network.default_attributes) == 0
def test_i_can_remove_a_rule_when_it_shares_p_node(self):
network = ReteNetwork()
rule1 = RuleForTestingRete(AndConditions([Condition("id", "attr", "value")]), name="rule1")
rule2 = RuleForTestingRete(AndConditions([Condition("id", "attr", "value")]), name="rule2")
network.add_rule(rule1)
network.add_rule(rule2)
network.remove_rule(rule1)
assert len(network.pnodes) == 1
assert network.pnodes[0].rules == [rule2]
assert network.rules == {rule2}
# sanity
wme = WME("id", "attr", "value")
network.add_wme(wme)
matches = list(network.matches)
assert len(matches) == 1
assert matches[0].pnode.rules == [rule2]
assert matches[0].token.wmes == [wme]
@pytest.mark.skip("Need a better attribute management")
def test_i_can_manage_attributes_by_key_on_rule_removal(self):
network = ReteNetwork()
rule1 = RuleForTestingRete(AndConditions([Condition("id", "attr", "value")]))
rule2 = RuleForTestingRete(AndConditions([Condition("id", "attr", V("x"))]))
rule3 = RuleForTestingRete(AndConditions([Condition("id", "attr2", V("x"))]))
network.add_rule(rule1)
network.add_rule(rule2)
network.add_rule(rule3)
# sanity
assert network.attributes_by_id == {}
network.add_rule(rule1)
assert network.attributes_by_id == {}
network.add_rule(rule3)
assert network.attributes_by_id == {}
network.add_rule(rule2)
assert network.attributes_by_id == {}
@pytest.mark.skip("Need a better attribute management")
def test_i_can_manage_default_attributes_on_rule_removal(self):
network = ReteNetwork()
rule1 = RuleForTestingRete(AndConditions([Condition("id", V("x"), "value")]))
rule2 = RuleForTestingRete(AndConditions([Condition("id", V("y"), V("z"))]))
rule3 = RuleForTestingRete(AndConditions([Condition("id2", V("x"), V("y"))]))
network.add_rule(rule1)
network.add_rule(rule2)
network.add_rule(rule3)
# sanity
assert network.default_attributes == set()
network.add_rule(rule1)
assert network.default_attributes == set()
network.add_rule(rule3)
assert network.default_attributes == set()
network.add_rule(rule2)
assert network.default_attributes == set()
def test_i_can_add_an_empty_rule(self):
network = ReteNetwork()
rule = RuleForTestingRete(AndConditions([]))
network.add_rule(rule)
assert len(network.beta_root.children) == 1
network.remove_rule(rule)
assert len(network.beta_root.children) == 0
def test_i_can_match_when_duplicate_conditions(self):
network = ReteNetwork()
c1 = Condition(V('x'), 'self', V('y'))
c2 = Condition(V('x'), 'color', 'red')
c3 = Condition(V('x'), 'color', 'red')
rule = RuleForTestingRete(AndConditions([c1, c2, c3]))
network.add_rule(rule)
wme1 = WME('B1', 'self', 'B1')
wme2 = WME('B1', 'color', 'red')
network.add_wme(wme1)
network.add_wme(wme2)
assert len(list(network.matches)) == 1
def test_i_can_match_when_duplicate_conditions2(self):
network = ReteNetwork()
c1 = Condition(V('x'), 'self', V('y'))
c2 = Condition(V('x'), 'color', 'red')
c3 = Condition(V('y'), 'color', 'red')
rule = RuleForTestingRete(AndConditions([c1, c2, c3]))
network.add_rule(rule)
wme1 = WME('B1', 'self', 'B1')
wme2 = WME('B1', 'color', 'red')
network.add_wme(wme1)
network.add_wme(wme2)
assert len(list(network.matches)) == 1
def test_multiple_rules(self):
network = ReteNetwork()
c1 = Condition(V('x'), 'on', V('y'))
c2 = Condition(V('y'), 'left-of', V('z'))
c3 = Condition(V('z'), 'color', 'red')
c4 = Condition(V('z'), 'on', 'table')
c5 = Condition(V('z'), 'left-of', 'B4')
rule1 = RuleForTestingRete(AndConditions([c1, c2, c3]))
rule2 = RuleForTestingRete(AndConditions([c1, c2, c4, c5]))
network.add_rule(rule1)
network.add_rule(rule2)
wmes = [
WME('B1', 'on', 'B2'),
WME('B1', 'on', 'B3'),
WME('B1', 'color', 'red'),
WME('B2', 'on', 'table'),
WME('B2', 'left-of', 'B3'),
WME('B2', 'color', 'blue'),
WME('B3', 'left-of', 'B4'),
WME('B3', 'on', 'table'),
WME('B3', 'color', 'red'),
]
for wme in wmes:
network.add_wme(wme)
# add rule on the fly
rule3 = RuleForTestingRete(AndConditions([c1, c2, c4, c3]))
network.add_rule(rule3)
p_node1 = rule1.rete_p_nodes[0]
p_node2 = rule2.rete_p_nodes[0]
p_node3 = rule3.rete_p_nodes[0]
assert len(list(p_node1.activations)) == 1
assert len(list(p_node2.activations)) == 1
assert len(list(p_node3.activations)) == 1
assert list(p_node1.activations)[0].wmes == [wmes[0], wmes[4], wmes[8]]
assert list(p_node2.activations)[0].wmes == [wmes[0], wmes[4], wmes[7], wmes[6]]
assert list(p_node3.activations)[0].wmes == [wmes[0], wmes[4], wmes[7], wmes[8]]
network.remove_rule(rule3)
assert len(list(p_node3.activations)) == 0
def test_rule_is_added_to_rete_network_when_it_is_created(self):
sheerka, context, rule = self.init_test().with_exec_rules(("rule_name", "id.attr == 'value'", 'True')).unpack()
evaluation_service = sheerka.services[SheerkaEvaluateRules.NAME]
rete_network = evaluation_service.network
assert len(rete_network.pnodes) == 1
assert rete_network.pnodes[0].rules[0] == rule
def test_rule_is_removed_to_rete_network_when_it_is_deleted(self):
sheerka, context, rule = self.init_test().with_exec_rules(("id.attr == 'value'", 'True')).unpack()
evaluation_service = sheerka.services[SheerkaEvaluateRules.NAME]
rete_network = evaluation_service.network
sheerka.remove_rule(context, rule)
assert len(rete_network.pnodes) == 0
def test_rules_are_removed_upon_ontology_removal(self):
sheerka, context = self.init_test().unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
evaluation_service = sheerka.services[SheerkaEvaluateRules.NAME]
rete_network = evaluation_service.network
sheerka.push_ontology(context, "Testing Rule removal 1")
rule = Rule(ACTION_TYPE_EXEC, "name", "id.attr = 'value'", "True")
rule = service.init_rule(context, rule)
service.create_new_rule(context, rule)
assert len(rete_network.pnodes) == 1
assert rete_network.pnodes[0].rules[0] == rule
sheerka.push_ontology(context, "Testing Rule removal 2")
rule2 = Rule(ACTION_TYPE_EXEC, "name2", "id2.attr2 = 'value2'", "True")
rule2 = service.init_rule(context, rule2)
service.create_new_rule(context, rule2)
assert len(rete_network.pnodes) == 2
assert rete_network.pnodes[0].rules[0] == rule
assert rete_network.pnodes[1].rules[0] == rule2
sheerka.pop_ontology(context)
assert len(rete_network.pnodes) == 1
assert rete_network.pnodes[0].rules[0] == rule
sheerka.pop_ontology(context)
assert len(rete_network.pnodes) == 0