Implemented a first and basic version of a Rete rule engine
This commit is contained in:
+11
-5
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
# []
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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'}}
|
||||
|
||||
@@ -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
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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]
|
||||
|
||||
@@ -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__"
|
||||
|
||||
@@ -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",)
|
||||
@@ -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):
|
||||
|
||||
@@ -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("")
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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'
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user