Fixed #20: I can parse simple concepts

This commit is contained in:
2023-07-09 18:08:47 +02:00
parent ba397b0b72
commit 57f9ce2bbb
44 changed files with 2462 additions and 149 deletions
+55 -9
View File
@@ -5,11 +5,10 @@ from common.global_symbols import NotInit
from conftest import NewOntology
from core.BuiltinConcepts import BuiltinConcepts
from core.concept import ConceptDefaultProps
from core.error import ErrorContext
from core.python_fragment import PythonFragment
from helpers import define_new_concept, get_concept, get_concepts, get_metadata
from services.SheerkaConceptEvaluator import ConceptEvaluator
from services.SheerkaPython import EvaluationRef
from services.SheerkaConceptEvaluator import ConceptEvaluator, InfiniteRecursion, TooManyErrors
from services.SheerkaPython import ObjectRef
class TestConceptManager(BaseTest):
@@ -77,8 +76,8 @@ class TestConceptManager(BaseTest):
compiled = service._build_attributes(context, metadata)
pf = getattr(compiled, ConceptDefaultProps.BODY)
assert isinstance(pf, PythonFragment)
assert pf.namespace == {"a": EvaluationRef("self", "a"),
"b": EvaluationRef("self", "b")}
assert pf.namespace == {"a": ObjectRef("self", "a"),
"b": ObjectRef("self", "b")}
def test_i_can_manage_parsing_errors(self, context, service):
metadata = get_metadata(
@@ -98,7 +97,7 @@ class TestConceptManager(BaseTest):
assert pf.source_code == "NotInit"
error = getattr(compiled, ConceptDefaultProps.BODY)
assert isinstance(error, ErrorContext)
assert isinstance(error, TooManyErrors)
def test_i_can_eval_concept_attributes(self, context, service):
with NewOntology(context, "test_i_can_eval_concept_attributes"):
@@ -225,6 +224,9 @@ class TestConceptManager(BaseTest):
assert context.sheerka.objvalue(qux) == 1
def test_concept_variables_precede_global_concepts(self, context, service):
# In this test, there is a variable named "foo"
# Its value is the concept 'bar'
# So when the body is evaluated, we expected Concept(bar), not Concept(foo)
with NewOntology(context, "test_concept_variables_precede_global_concepts"):
foo, bar, baz = get_concepts(context,
get_concept("foo"),
@@ -237,6 +239,20 @@ class TestConceptManager(BaseTest):
assert context.sheerka.isinstance(res, baz)
assert context.sheerka.isinstance(res.body, bar)
def test_concept_variables_precede_global_concept_during_computation(self, context, service):
# In this test, there is a variable named "foo" and a concept also named "foo"
# When evaluated, foo + 1 must use the variable 'foo', not the Concept("foo")
with NewOntology(context, "test_concept_variables_precede_global_concepts"):
foo, bar = get_concepts(context,
get_concept("foo", body="2"),
get_concept("bar", body="foo + 1", variables=(("foo", "1"),)),
use_sheerka=True)
res = service.evaluate_concept(context, bar)
assert context.sheerka.isinstance(res, bar)
assert context.sheerka.objvalue(res) == 2
def test_i_can_evaluate_concept_when_variables_reference_others_concepts_with_body(self, context, service):
with NewOntology(context, "test_i_can_evaluate_concept_when_variables_reference_others_concepts_with_body"):
foo, bar, baz = get_concepts(context,
@@ -463,6 +479,7 @@ class TestConceptManager(BaseTest):
res = service.evaluate_concept(context, foo)
assert context.sheerka.isinstance(res, BuiltinConcepts.EVALUATION_ERROR)
assert context.sheerka.isinstance(res.concept, foo)
assert isinstance(res.reason, InfiniteRecursion)
assert res.reason.ids == [foo.id, bar.id, baz.id]
def test_i_can_detect_sub_infinite_loop(self, context, service):
@@ -476,6 +493,7 @@ class TestConceptManager(BaseTest):
res = service.evaluate_concept(context, foo)
assert context.sheerka.isinstance(res, BuiltinConcepts.EVALUATION_ERROR)
assert context.sheerka.isinstance(res.concept, bar)
assert isinstance(res.reason, InfiniteRecursion)
assert res.reason.ids == [bar.id, baz.id]
def test_i_can_detect_auto_infinite_loop(self, context, service):
@@ -487,10 +505,11 @@ class TestConceptManager(BaseTest):
res = service.evaluate_concept(context, foo)
assert context.sheerka.isinstance(res, BuiltinConcepts.EVALUATION_ERROR)
assert context.sheerka.isinstance(res.concept, foo)
assert isinstance(res.reason, InfiniteRecursion)
assert res.reason.ids == [foo.id]
def test_i_can_select_the_valid_result_when_multiple_choice_invalid_concept(self, context, service):
with NewOntology(context, "test_i_can_select_the_valid_result_when_multiple_choice"):
with NewOntology(context, "test_i_can_select_the_valid_result_when_multiple_choice_invalid_concept"):
foo, two_ok, two_nok = get_concepts(context,
get_concept("foo", body="two"),
get_concept("two", body="1 +"), # has to come before the other 'two'
@@ -502,7 +521,7 @@ class TestConceptManager(BaseTest):
assert context.sheerka.objvalue(foo) == 2
def test_i_can_select_the_valid_result_when_multiple_choice_evaluation_error(self, context, service):
with NewOntology(context, "test_i_can_select_the_valid_result_when_multiple_choice"):
with NewOntology(context, "test_i_can_select_the_valid_result_when_multiple_choice_evaluation_error"):
foo, two_ok, two_nok = get_concepts(context,
get_concept("foo", body="two"),
get_concept("two", body="1 / 0"), # has to come before the other 'two'
@@ -529,8 +548,35 @@ class TestConceptManager(BaseTest):
with NewOntology(context, "test_i_do_not_use_ret_in_case_of_error"):
foo, baz = get_concepts(context,
get_concept("foo"),
get_concept("baz", body="foo", ret="bar"),
get_concept("baz", body="foo", ret="bar"), # Concept("bar") is not defined
use_sheerka=True)
res = service.evaluate_concept(context, baz)
assert context.sheerka.isinstance(res, BuiltinConcepts.EVALUATION_ERROR)
@pytest.mark.skip("Cannot remove concept")
def test_i_do_not_use_ret_in_case_of_error_when_concept_was_removed(self, context, service):
# Make sure that ret is not returned in case of UNKNOWN_CONCEPT error message
foo, bar, baz = get_concepts(context,
get_concept("foo"),
get_concept("bar"),
get_concept("baz", body="foo", ret="bar"), # Concept("bar") is not defined
use_sheerka=True)
service.evaluate_concept(context, baz) # creates the compiled for Concept("baz")
context.sheerka.remove_concept(bar) # Concept("bar") no longer exists, but compiled for "baz" remains the same
res = service.evaluate_concept(context, baz)
assert context.sheerka.isinstance(res, BuiltinConcepts.EVALUATION_ERROR)
assert "#ret#" in res.reason
assert res.reason["#ret#"].value == context.sheerka.newn(BuiltinConcepts.UNKNOWN_CONCEPT, requested="bar")
def test_i_cannot_evaluate_when_error(self, context, service):
with NewOntology(context, "test_i_cannot_evaluate_when_error"):
foo, = get_concepts(context,
get_concept("foo", body="I am a concept"), # "one" does not exist
use_sheerka=True)
res = service.evaluate_concept(context, foo)
assert context.sheerka.isinstance(res, BuiltinConcepts.INVALID_CONCEPT)
+101 -5
View File
@@ -6,8 +6,8 @@ from conftest import NewOntology
from core.BuiltinConcepts import BuiltinConcepts
from core.concept import ConceptMetadata
from core.error import ErrorContext
from helpers import get_concepts, get_metadata
from services.SheerkaConceptManager import ConceptAlreadyDefined, ConceptManager
from helpers import get_concept, get_concepts, get_metadata
from services.SheerkaConceptManager import ConceptAlreadyDefined, ConceptManager, ConceptRef
class TestConceptManager(BaseTest):
@@ -86,7 +86,7 @@ class TestConceptManager(BaseTest):
assert metadata.name == "name"
assert metadata.key == "name"
assert metadata.body == "body"
assert metadata.digest == "c75faa4efbc9ef9dbc5174c52786d5b066e2ece41486b81c27336e292917fecb"
assert metadata.digest == "f32363f42e698b1642c8f76f969d76d56f53f0e0732cb651e3360e3ede7b2b11"
assert metadata.all_attrs == ('#where#', '#pre#', '#post#', '#body#', '#ret#')
# is sorted in db
@@ -96,6 +96,60 @@ class TestConceptManager(BaseTest):
assert om.get(ConceptManager.CONCEPTS_BY_KEY_ENTRY, metadata.key) == metadata
assert om.get(ConceptManager.CONCEPTS_BY_HASH_ENTRY, metadata.digest) == metadata
# check first token
assert om.get(ConceptManager.CONCEPT_BY_FIRST_TOKEN_IN_KEY, "name") == ["1001"]
def test_i_can_define_a_new_concept_with_variables(self, context, service):
with NewOntology(context, "test_i_can_define_a_new_concept_with_variables"):
res = service.define_new_concept(context,
name="a multiplied by b",
variables=[("a", NotInit), ("b", NotInit)])
metadata = res.value.metadata
assert isinstance(metadata, ConceptMetadata)
assert metadata.id == "1001"
assert metadata.name == "a multiplied by b"
assert metadata.key == "__var__0 multiplied by __var__1"
assert metadata.digest == "17d2360d82fc4264e2bcb75e4aa30ee3de87531acee72f5d939e23bff246b2dd"
assert metadata.all_attrs == ('#where#', '#pre#', '#post#', '#body#', '#ret#', "a", "b")
# is sorted in db
om = context.sheerka.om
assert om.get(ConceptManager.CONCEPTS_BY_ID_ENTRY, metadata.id) == metadata
assert om.get(ConceptManager.CONCEPTS_BY_NAME_ENTRY, metadata.name) == metadata
assert om.get(ConceptManager.CONCEPTS_BY_KEY_ENTRY, metadata.key) == metadata
assert om.get(ConceptManager.CONCEPTS_BY_HASH_ENTRY, metadata.digest) == metadata
# check first token
assert om.get(ConceptManager.CONCEPT_BY_FIRST_TOKEN_IN_KEY, "multiplied") == ["1001"]
def test_i_can_define_a_new_concept_using_definition(self, context, service):
with NewOntology(context, "test_i_can_define_a_new_concept_using_definition"):
res = service.define_new_concept(context,
name="multiplication",
definition="a multiplied by b",
variables=[("a", NotInit), ("b", NotInit)])
metadata = res.value.metadata
assert isinstance(metadata, ConceptMetadata)
assert metadata.id == "1001"
assert metadata.name == "multiplication"
assert metadata.definition == "a multiplied by b"
assert metadata.key == "__var__0 multiplied by __var__1"
assert metadata.digest == "b29007ea67bddc48329a2ae0124a320e26c86fb6b106aad6581bc75dfdf5ebeb"
assert metadata.all_attrs == ('#where#', '#pre#', '#post#', '#body#', '#ret#', "a", "b")
# is sorted in db
om = context.sheerka.om
assert om.get(ConceptManager.CONCEPTS_BY_ID_ENTRY, metadata.id) == metadata
assert om.get(ConceptManager.CONCEPTS_BY_NAME_ENTRY, metadata.name) == metadata
assert om.get(ConceptManager.CONCEPTS_BY_KEY_ENTRY, metadata.key) == metadata
assert om.get(ConceptManager.CONCEPTS_BY_HASH_ENTRY, metadata.digest) == metadata
# check first token
assert om.get(ConceptManager.CONCEPT_BY_FIRST_TOKEN_IN_KEY, "multiplied") == ["1001"]
assert om.get(ConceptManager.CONCEPT_BY_FIRST_TOKEN_IN_NAME, "multiplication") == ["1001"]
def test_i_cannot_create_the_same_concept_twice(self, context, service):
with NewOntology(context, "test_i_cannot_create_the_same_concept_twice"):
res = service.define_new_concept(context, "name", body="body")
@@ -176,11 +230,11 @@ class TestConceptManager(BaseTest):
def test_i_cannot_instantiate_a_concept_which_does_not_exist(self, context, service):
foo = service.newn("foo", var1="value1", var2="value2")
assert foo.key == BuiltinConcepts.UNKNOWN_CONCEPT
assert foo.requested_name == "foo"
assert foo.requested == "foo"
foo = service.newi("1001", var1="value1", var2="value2")
assert foo.key == BuiltinConcepts.UNKNOWN_CONCEPT
assert foo.requested_id == "1001"
assert foo.requested == "#1001"
def test_i_can_instantiate_by_name_when_multiple_results(self, context, service):
with NewOntology(context, "test_i_can_instantiate_by_name_when_multiple_results"):
@@ -255,6 +309,48 @@ class TestConceptManager(BaseTest):
assert context.sheerka.isinstance(res[0], foo)
assert context.sheerka.isinstance(res[1], bar)
def test_i_can_new_using_concept_reference(self, context, service):
with NewOntology(context, "test_i_can_new_using_concept_reference"):
foo, bar, baz = get_concepts(context, "foo", "bar", "baz", use_sheerka=True)
foo.get_runtime_info().info["resolution_method"] = "id"
bar.get_runtime_info().info["resolution_method"] = "key"
foo_concept_ref = ConceptRef(foo)
res = service.new(foo_concept_ref)
assert context.sheerka.isinstance(res, foo)
bar_concept_ref = ConceptRef(bar)
res = service.new(bar_concept_ref)
assert context.sheerka.isinstance(res, bar)
baz_concept_ref = ConceptRef(baz)
res = service.new(baz_concept_ref)
assert context.sheerka.isinstance(res, baz)
def test_i_can_new_using_concept_reference_when_multiple_results(self, context, service):
with NewOntology(context, "test_i_can_new_using_concept_reference"):
foo1, foo2 = get_concepts(context,
get_concept("foo", body="1"),
get_concept("foo", body="2"),
use_sheerka=True)
foo = get_concept("foo") # blueprint, no need to be known by Sheerka
foo.get_runtime_info().info["resolution_method"] = "name"
foo_concept_ref = ConceptRef(foo)
res = service.new(foo_concept_ref)
assert res == [foo1, foo2]
def test_i_cannot_new_using_concept_reference_when_unknown(self, context, service):
foo = get_concept("foo") # not known by Sheerka
foo.get_runtime_info().info["resolution_method"] = "name"
foo_concept_ref = ConceptRef(foo)
res = service.new(foo_concept_ref)
assert context.sheerka.isinstance(res, BuiltinConcepts.UNKNOWN_CONCEPT)
assert res.requested == "foo"
def test_unknown_concept_is_return_if_the_identifier_is_not_found(self, service):
assert service.new("unknown").name == BuiltinConcepts.UNKNOWN_CONCEPT
@@ -0,0 +1,66 @@
import pytest
from base import BaseTest
from services.SheerkaDummyEventManager import SheerkaDummyEventManager
def example_of_function(context):
print(f"example_of_class_method. event={context.event.get_digest()}")
def example_of_function_with_data(context, data):
print(f"example_of_class_method. event={context.event.get_digest()}, {data=}")
class TestSheerkaEventManager(BaseTest):
@pytest.fixture()
def service(self, sheerka):
service = sheerka.services[SheerkaDummyEventManager.NAME]
yield service
service.test_only_reset_service()
def example_of_class_method(self, context):
print(f"example_of_class_method. event={context.event.get_digest()}")
@staticmethod
def example_of_static_method(context):
print(f"example_of_static_method. event={context.event.get_digest()}")
def example_of_class_method_with_data(self, context, data):
print(f"example_of_class_method. event={context.event.get_digest()}, {data=}")
@staticmethod
def example_of_static_method_with_data(context, data):
print(f"example_of_static_method. event={context.event.get_digest()}, {data=}")
def test_i_can_subscribe_and_publish(self, context, service, capsys):
topic = "my topic"
service.subscribe(topic, self.example_of_class_method)
service.subscribe(topic, self.example_of_static_method)
service.subscribe(topic, example_of_function)
service.publish(context, topic)
captured = capsys.readouterr()
assert captured.out == """example_of_class_method. event=xxx
example_of_static_method. event=xxx
example_of_class_method. event=xxx
"""
def test_i_can_subscribe_and_publish_with_data(self, context, service, capsys):
topic = "my topic"
service.subscribe(topic, self.example_of_class_method_with_data)
service.subscribe(topic, self.example_of_static_method_with_data)
service.subscribe(topic, example_of_function_with_data)
service.publish(context, topic, "42")
captured = capsys.readouterr()
assert captured.out == """example_of_class_method. event=xxx, data='42'
example_of_static_method. event=xxx, data='42'
example_of_class_method. event=xxx, data='42'
"""
+46 -15
View File
@@ -1,3 +1,5 @@
import ast
import pytest
from base import BaseTest, DummyObj
@@ -7,11 +9,13 @@ from core.BuiltinConcepts import BuiltinConcepts
from core.ExecutionContext import ContextActions
from core.concept import ConceptDefaultProps
from core.error import MethodAccessError
from core.python_fragment import PythonFragment
from evaluators.PythonParser import PythonParser
from helpers import _rv, define_new_concept, get_concepts, get_evaluated_concept, get_metadata
from helpers import _rv, define_new_concept, get_concepts, get_evaluated_concept, get_evaluated_concepts, get_metadata
from parsers.ParserInput import ParserInput
from parsers.tokenizer import Token, TokenKind
from services.SheerkaPython import EvalMethod, EvaluationContext, EvaluationRef, Expando, MultipleResults, SheerkaPython
from services.SheerkaConceptManager import ConceptRef
from services.SheerkaPython import EvalMethod, EvaluationContext, Expando, MultipleResults, ObjectRef, SheerkaPython
def get_python_fragment(sheerka, context, command):
@@ -70,7 +74,7 @@ class TestSheerkaPython(BaseTest):
def test_i_can_eval_using_eval_ref(self, sheerka, context, service):
python_fragment = get_python_fragment(sheerka, context, "a")
python_fragment.namespace = {"a": EvaluationRef("self", "a")}
python_fragment.namespace = {"a": ObjectRef("self", "a")}
ret = service.evaluate_python(context, EvaluationContext(), python_fragment,
{"self": DummyObj("my dummy value")})
@@ -95,6 +99,38 @@ class TestSheerkaPython(BaseTest):
ret = service.evaluate_python(context, EvaluationContext(), python_fragment)
assert ret == 3
def test_i_can_eval_when_multiple_concepts(self, sheerka, context, service):
with NewOntology(context, "test_i_can_eval_when_multiple_concepts"):
get_concepts(context,
get_metadata("one", body="'one'"),
get_metadata("one", body="1"),
use_sheerka=True)
python_fragment = get_python_fragment(sheerka, context, "one + 1")
ret = service.evaluate_python(context, EvaluationContext(), python_fragment)
assert ret == 2
def test_i_can_eval_when_multiple_result_in_local_namespace(self, sheerka, context, service):
# In the test, the PythonFragment contains a MultipleResult in its namespace
# (normally, the MultipleResult is created inside the evaluate_python)
# We need to make sure that multiple results are created in the same way
with NewOntology(context, "test_i_can_eval_when_multiple_result_in_local_namespace"):
one1, one2 = get_concepts(context,
get_metadata("one", body="'one'"),
get_metadata("one", body="1"),
use_sheerka=True)
concept_ref = "__concept_id__"
ast_tree = ast.parse(concept_ref, "<user input>", 'eval')
ref = MultipleResults(ConceptRef(one1), ConceptRef(one2))
python_fragment = PythonFragment(concept_ref, ast_tree=ast_tree, namespace={concept_ref: ref})
ret = service.evaluate_python(context, EvaluationContext(eval_method=EvalMethod.All), python_fragment)
evaluated_one1, evaluated_one2 = get_evaluated_concepts(context, one1, one2, use_sheerka=True)
assert ret == MultipleResults(evaluated_one1, "one", evaluated_one2, 1)
def test_i_can_remember_previous_results(self, sheerka, context, service):
python_fragment = get_python_fragment(sheerka, context, "a=10")
ret = service.evaluate_python(context, EvaluationContext(), python_fragment)
@@ -151,18 +187,6 @@ class TestSheerkaPython(BaseTest):
get_evaluated_concept(foo_3, body='bar'),
"bar")
def test_i_can_eval_when_multiple_concepts(self, sheerka, context, service):
with NewOntology(context, "test_i_can_eval_when_multiple_concepts"):
get_concepts(context,
get_metadata("one", body="'one'"),
get_metadata("one", body="1"),
use_sheerka=True)
python_fragment = get_python_fragment(sheerka, context, "one + 1")
ret = service.evaluate_python(context, EvaluationContext(), python_fragment)
assert ret == 2
def test_i_can_eval_until_a_successful_result_is_found(self, sheerka, context, service):
with NewOntology(context, "test_i_can_eval_when_multiple_concepts"):
get_concepts(context,
@@ -338,3 +362,10 @@ class TestSheerkaPython(BaseTest):
foo, bar = get_concepts(context, "foo", "bar")
assert MultipleResults(foo, "one", bar, 1).concepts_only() == MultipleResults(foo, bar)
assert MultipleResults("one", 1).concepts_only() == MultipleResults()
def test_i_can_add_multiple_results_of_multiple_results(self, context):
foo, bar, baz, qux = get_concepts(context, "foo", "bar", "baz", "qux")
m1 = MultipleResults(foo, bar)
m2 = MultipleResults(bar, baz, m1)
assert m2.items == [bar, baz, foo, bar]