Refactored to allow ConceptEvaluator

This commit is contained in:
2019-11-14 22:04:38 +01:00
parent 576ce77740
commit 9e10e77737
30 changed files with 2406 additions and 1007 deletions
+400 -59
View File
@@ -5,11 +5,13 @@ import os
from os import path
import shutil
from core.concept import Concept, ConceptParts, ReturnValueConcept
from core import utils
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
from core.concept import Concept, ConceptParts
from core.sheerka import Sheerka, ExecutionContext
from parsers.DefaultParser import DefConceptNode, DefaultParser
from parsers.DefaultParser import DefaultParser
from parsers.PythonParser import PythonParser
from sdp.sheerkaDataProvider import SheerkaDataProvider
from sdp.sheerkaDataProvider import SheerkaDataProvider, SheerkaDataProviderDuplicateKeyError
tests_root = path.abspath("../build/tests")
root_folder = "init_folder"
@@ -36,68 +38,55 @@ def test_root_folder_is_created_after_initialization():
assert os.path.exists(root_folder), "init folder should be created"
def test_lists_of_concepts_is_initialized():
def test_i_can_list_builtin_concepts():
sheerka = get_sheerka()
assert len(sheerka.concepts_cache) > 1
builtins = list(sheerka.get_builtins_classes_as_dict())
assert str(BuiltinConcepts.ERROR) in builtins
assert str(BuiltinConcepts.RETURN_VALUE) in builtins
def get_concept():
text = """
def concept a+b
where isinstance(a, int) and isinstance(b, int)
pre isinstance(a, int) and isinstance(b, int)
post isinstance(res, int)
as:
def func(x,y):
return x+y
func(a,b)
"""
parser = DefaultParser(PythonParser)
return parser.parse(None, text)
def test_builtin_concepts_are_initialized():
sheerka = get_sheerka()
assert len(sheerka.concepts_cache) == len(BuiltinConcepts)
for concept_name in BuiltinConcepts:
assert str(concept_name) in sheerka.concepts_cache
assert sheerka.sdp.get_safe(sheerka.CONCEPTS_ENTRY, str(concept_name)) is not None
for key, concept_class in sheerka.get_builtins_classes_as_dict().items():
assert isinstance(sheerka.concepts_cache[key], concept_class)
def test_builtin_concepts_can_be_updated():
sheerka = get_sheerka()
loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA)
loaded_sheerka.desc = "I have a description"
sheerka.sdp.modify("Test", sheerka.CONCEPTS_ENTRY, loaded_sheerka.key, loaded_sheerka)
sheerka = get_sheerka()
loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA)
assert loaded_sheerka.desc == "I have a description"
def test_i_can_add_a_concept():
sheerka = get_sheerka()
concept = get_concept()
res = sheerka.add_concept(ExecutionContext(sheerka, "xxx"), concept)
concept_found = res.value
concept = get_default_concept()
res = sheerka.create_new_concept(get_context(sheerka), concept)
assert res.status
assert concept_found == Concept(
name="a + b",
where="isinstance(a, int) and isinstance(b, int)",
pre="isinstance(a, int) and isinstance(b, int)",
post="isinstance(res, int)",
body="def func(x,y):\n return x+y\nfunc(a,b)")
assert isinstance(concept_found.codes[ConceptParts.WHERE], ast.Expression)
assert isinstance(concept_found.codes[ConceptParts.PRE], ast.Expression)
assert isinstance(concept_found.codes[ConceptParts.POST], ast.Expression)
assert isinstance(concept_found.codes[ConceptParts.BODY], ast.Module)
assert sheerka.isinstance(res.value, BuiltinConcepts.NEW_CONCEPT)
all_props = list(concept_found.props.keys())
assert all_props == ["a", "b"]
concept_found = res.value.body
for prop in Concept.props_to_serialize:
assert getattr(concept_found, prop) == getattr(concept, prop)
assert concept_found.key == "__var__0 + __var__1"
assert concept_found.id == "1001"
assert path.exists(sheerka.sdp.get_obj_path(SheerkaDataProvider.ObjectsFolder,
"4f249487410db35d8bcbcf4521acb3dd8354978804cd99bbc4de17a323b2f237"))
@pytest.mark.parametrize("text, expected", [
("1 + 1", 2),
("sheerka.test()", 'I have access to Sheerka !')
])
def test_i_can_eval_simple_python_expressions(text, expected):
sheerka = Sheerka(debug=True)
sheerka.initialize(root_folder)
res = sheerka.eval(text)
assert len(res) == 1
assert res[0].status
assert res[0].value.body == expected
assert sheerka.isinstance(res[0].value, ReturnValueConcept())
assert concept.key in sheerka.concepts_cache
assert path.exists(sheerka.sdp.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_found.get_digest()))
def test_i_cannot_add_the_same_concept_twice():
@@ -105,30 +94,382 @@ def test_i_cannot_add_the_same_concept_twice():
Checks that duplicated concepts are managed by sheerka, not by sheerka.sdp
:return:
"""
pass
sheerka = get_sheerka()
concept = get_default_concept()
sheerka.create_new_concept(get_context(sheerka), concept)
res = sheerka.create_new_concept(get_context(sheerka), concept)
assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.ERROR)
assert res.value.body.args[0] == "Duplicate object."
def test_i_can_get_a_concept():
def test_i_can_get_a_builtin_concept_by_their_enum_or_the_string():
"""
Checks that a concept can be found its name
even when there are variables in the name (ex 'hello + a' or 'a + b' )
:return:
"""
pass
sheerka = get_sheerka()
for key in sheerka.get_builtins_classes_as_dict():
assert sheerka.get(key) is not None
assert sheerka.get(str(key)) is not None
def test_i_can_get_new_concept():
sheerka = get_sheerka()
concept = get_default_concept()
sheerka.create_new_concept(get_context(sheerka), concept)
from_cache = sheerka.get(concept.key)
assert from_cache is not None
assert from_cache.key == concept.key
assert from_cache == concept
def test_i_first_look_in_local_cache():
sheerka = get_sheerka()
concept = get_default_concept()
sheerka.create_new_concept(get_context(sheerka), concept)
sheerka.concepts_cache[concept.key].pre = "I have modified the concept in cache"
from_cache = sheerka.get(concept.key)
assert from_cache is not None
assert from_cache.key == concept.key
assert from_cache.pre == "I have modified the concept in cache"
def test_i_can_get_a_known_concept_when_not_in_cache():
"""
When not in cache, uses sdp
:return:
"""
sheerka = get_sheerka()
concept = get_default_concept()
sheerka.create_new_concept(get_context(sheerka), concept)
sheerka.concepts_cache = {} # reset the cache
loaded = sheerka.get(concept.key)
assert loaded is not None
assert loaded == concept
def test_unknown_concept_is_return_when_the_concept_is_not_found():
sheerka = get_sheerka()
loaded = sheerka.get("fake_key")
assert loaded is not None
assert sheerka.isinstance(loaded, BuiltinConcepts.UNKNOWN_CONCEPT)
assert loaded.body == "fake_key"
def test_i_can_instantiate_a_builtin_concept_when_it_has_its_own_class():
sheerka = get_sheerka()
ret = sheerka.new(BuiltinConcepts.RETURN_VALUE, who="who", status="status", value="value", message="message")
assert isinstance(ret, ReturnValueConcept)
assert ret.key == str(BuiltinConcepts.RETURN_VALUE)
assert ret.who == "who"
assert ret.status == "status"
assert ret.value == "value"
assert ret.message == "message"
def test_i_can_instantiate_a_builtin_concept_when_no_specific_class():
sheerka = get_sheerka()
ret = sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body="fake_concept")
assert isinstance(ret, Concept)
assert ret.key == str(BuiltinConcepts.UNKNOWN_CONCEPT)
assert ret.body == "fake_concept"
def test_i_can_instantiate_a_concept():
sheerka = get_sheerka()
concept = get_default_concept()
sheerka.create_new_concept(get_context(sheerka), concept)
new = sheerka.new(concept.key, a=10, b="value")
assert sheerka.isinstance(new, concept)
for prop in Concept.props_to_serialize:
assert getattr(new, prop) == getattr(concept, prop)
assert new.props["a"].value == 10
assert new.props["b"].value == "value"
def test_instances_are_different_when_asking_for_new():
sheerka = get_sheerka()
concept = get_default_concept()
sheerka.create_new_concept(get_context(sheerka), concept)
new1 = sheerka.new(concept.key, a=10, b="value")
new2 = sheerka.new(concept.key, a=10, b="value")
assert new1 == new2
assert id(new1) != id(new2)
def test_i_get_the_same_instance_when_is_unique_is_true():
sheerka = get_sheerka()
concept = get_unique_concept()
sheerka.create_new_concept(get_context(sheerka), concept)
new1 = sheerka.new(concept.key, a=10, b="value")
new2 = sheerka.new(concept.key, a=10, b="value")
assert new1 == new2
assert id(new1) == id(new2)
def test_i_cannot_instantiate_an_unknown_concept():
sheerka = get_sheerka()
new = sheerka.new("fake_concept")
assert sheerka.isinstance(new, BuiltinConcepts.UNKNOWN_CONCEPT)
assert new.body == "fake_concept"
def test_i_cannot_instantiate_when_properties_are_not_recognized():
sheerka = get_sheerka()
concept = get_default_concept()
sheerka.create_new_concept(get_context(sheerka), concept)
new = sheerka.new(concept.key, a=10, c="value")
assert sheerka.isinstance(new, BuiltinConcepts.UNKNOWN_PROPERTY)
assert new.property_name == "c"
assert sheerka.isinstance(new.concept, concept)
def test_i_can_use_expect_one_when_empty():
sheerka = get_sheerka()
res = sheerka.expect_one(get_context(sheerka), [])
assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.IS_EMPTY)
def test_i_can_use_expect_one_when_too_many_success():
sheerka = get_sheerka()
items = [
ReturnValueConcept("who", True, None),
ReturnValueConcept("who", True, None),
]
res = sheerka.expect_one(get_context(sheerka), items)
assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_SUCCESS)
assert res.value.obj == items
def test_i_can_use_expect_when_only_errors_1():
sheerka = get_sheerka()
items = [
ReturnValueConcept("who", False, None),
]
res = sheerka.expect_one(get_context(sheerka), items)
assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS)
assert res.value.obj == items
def test_i_can_use_expect_when_only_errors_2():
sheerka = get_sheerka()
items = [
ReturnValueConcept("who", False, None),
ReturnValueConcept("who", False, None),
]
res = sheerka.expect_one(get_context(sheerka), items)
assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS)
assert res.value.obj == items
def test_i_can_use_expect_one_when_one_success_1():
sheerka = get_sheerka()
items = [
ReturnValueConcept("who", True, None),
]
res = sheerka.expect_one(get_context(sheerka), items)
assert res.status
assert res == items[0]
def test_i_can_use_expect_one_when_one_success_2():
sheerka = get_sheerka()
items = [
ReturnValueConcept("who", False, None),
ReturnValueConcept("who", True, None),
ReturnValueConcept("who", False, None),
]
res = sheerka.expect_one(get_context(sheerka), items)
assert res.status
assert res == items[1]
def test_i_can_use_expect_one_when_not_a_list_true():
sheerka = get_sheerka()
res = sheerka.expect_one(get_context(sheerka), ReturnValueConcept("who", True, None))
assert res.status
assert res == ReturnValueConcept("who", True, None)
def test_i_can_use_expect_one_when_not_a_list_false():
sheerka = get_sheerka()
res = sheerka.expect_one(get_context(sheerka), ReturnValueConcept("who", False, None))
assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS)
assert res.value.obj == [ReturnValueConcept("who", False, None)]
@pytest.mark.parametrize("text, expected", [
("1 + 1", 2),
("sheerka.test()", 'I have access to Sheerka !')
])
def test_i_can_eval_simple_python_expressions(text, expected):
sheerka = get_sheerka()
res = sheerka.eval(text)
assert len(res) == 1
assert res[0].status
assert res[0].value == expected
def test_i_can_eval_simple_concept():
sheerka = get_sheerka()
concept = Concept(name="one", body="1").init_key()
sheerka.add_in_cache(concept)
text = "one"
res = sheerka.eval(text)
assert len(res) == 1
assert res[0].status
assert res[0].value == 1
def test_i_can_eval_def_concept_request():
text = """
def concept a + b
where isinstance(a, int) and isinstance(b, int)
pre isinstance(a, int) and isinstance(b, int)
post isinstance(res, int)
as:
def func(x,y):
return x+y
func(a,b)
"""
Test the new() functionnality
make sure that some Concept are singleton (ex Sheerka, True, False)
otherwise, make sure that new() returns a **new** instance
expected = get_default_concept()
expected.id = "1001"
expected.desc = None
expected.init_key()
sheerka = get_sheerka()
res = sheerka.eval(text)
assert len(res) == 1
assert res[0].status
assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT)
concept_saved = res[0].value.body
for prop in Concept.props_to_serialize:
assert getattr(concept_saved, prop) == getattr(expected, prop)
assert concept_saved.key in sheerka.concepts_cache
assert path.exists(sheerka.sdp.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_digest()))
def test_i_can_eval_def_concept_part_when_one_part_is_a_ref_of_another_concept():
"""
In this test, we test that the properties of 'concept a xx b' (which are 'a' and 'b')
are correctly detected, because of the concept 'a plus b' in its body
:return:
"""
pass
sheerka = get_sheerka()
# concept 'a plus b' is known
concept_a_plus_b = Concept(name="a plus b").set_prop("a").set_prop("b").init_key()
sheerka.add_in_cache(concept_a_plus_b)
res = sheerka.eval("def concept a xx b as a plus b")
expected = Concept(name="a xx b", body="a plus b").set_prop("a").set_prop("b").init_key()
expected.id = "1001"
assert len(res) == 1
assert res[0].status
assert sheerka.isinstance(res[0].value, BuiltinConcepts.NEW_CONCEPT)
concept_saved = res[0].value.body
for prop in Concept.props_to_serialize:
assert getattr(concept_saved, prop) == getattr(expected, prop)
assert concept_saved.key in sheerka.concepts_cache
assert path.exists(sheerka.sdp.get_obj_path(SheerkaDataProvider.ObjectsFolder, concept_saved.get_digest()))
def test_i_cannot_eval_the_same_def_concept_twice():
text = """
def concept a + b
where isinstance(a, int) and isinstance(b, int)
pre isinstance(a, int) and isinstance(b, int)
post isinstance(res, int)
as:
def func(x,y):
return x+y
func(a,b)
"""
sheerka = get_sheerka()
sheerka.eval(text)
res = sheerka.eval(text)
assert len(res) == 1
assert not res[0].status
assert sheerka.isinstance(res[0].value, BuiltinConcepts.CONCEPT_ALREADY_DEFINED)
def get_sheerka():
sheerka = Sheerka()
sheerka.initialize(root_folder)
return sheerka
return sheerka
def get_context(sheerka):
return ExecutionContext("test", "xxx", sheerka)
def get_default_concept():
concept = Concept(
name="a + b",
where="isinstance(a, int) and isinstance(b, int)",
pre="isinstance(a, int) and isinstance(b, int)",
post="isinstance(res, int)",
body="def func(x,y):\n return x+y\nfunc(a,b)",
desc="specific description")
concept.set_prop("a", "value1")
concept.set_prop("b", "value2")
return concept
def get_unique_concept():
return Concept(name="unique", is_unique=True)