Added first implementation of concepts ambiguity resolution + Jenkins file test
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, ParserResultConcept
|
||||
from core.concept import Concept, DoNotResolve, ConceptParts, Property, InfiniteRecursionResolved, CB, NotInit
|
||||
from core.sheerka.services.SheerkaEvaluateConcept import SheerkaEvaluateConcept
|
||||
from parsers.PythonParser import PythonNode
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
@@ -50,16 +51,16 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
:return:
|
||||
"""
|
||||
|
||||
sheerka, context, concept = self.init_concepts(Concept("foo", pre=expr))
|
||||
sheerka, context, concept = self.init_concepts(Concept("foo", post=expr))
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, concept)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert evaluated.metadata.body is None
|
||||
assert evaluated.metadata.pre == expr
|
||||
assert evaluated.metadata.post is None
|
||||
assert evaluated.metadata.post == expr
|
||||
assert evaluated.metadata.pre is None
|
||||
assert evaluated.metadata.where is None
|
||||
assert evaluated.get_value(ConceptParts.PRE) == expected
|
||||
assert evaluated.get_value(ConceptParts.POST) == expected
|
||||
assert evaluated.variables() == {}
|
||||
assert not evaluated.metadata.is_evaluated
|
||||
assert len(evaluated.values) == 0 if expr is None else 1
|
||||
@@ -353,27 +354,34 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
|
||||
assert evaluated.key == concept.init_key().key
|
||||
|
||||
@pytest.mark.parametrize("where_clause, expected", [
|
||||
("True", True),
|
||||
("False", False),
|
||||
("self < 10", False),
|
||||
("self < 11", True),
|
||||
("a < 20", False),
|
||||
("a > 19", True),
|
||||
("a + self > 20", True),
|
||||
@pytest.mark.parametrize("where_clause, expected, expected_body", [
|
||||
("True", True, BuiltinConcepts.NOT_INITIALIZED),
|
||||
("False", False, BuiltinConcepts.NOT_INITIALIZED),
|
||||
("self < 10", False, 10),
|
||||
("self < 11", True, 10),
|
||||
("a < 20", False, BuiltinConcepts.NOT_INITIALIZED),
|
||||
("a > 19", True, BuiltinConcepts.NOT_INITIALIZED),
|
||||
("a + self > 20", True, 10),
|
||||
])
|
||||
def test_i_can_evaluate_simple_where(self, where_clause, expected):
|
||||
def test_i_can_evaluate_simple_where(self, where_clause, expected, expected_body):
|
||||
# We check that the WHERE condition is correctly evaluated
|
||||
# We also check that the body is evaluated only when it's mandatory
|
||||
|
||||
sheerka, context, concept = self.init_concepts(
|
||||
Concept("foo", body="10", where=where_clause).def_var("a", "20"),
|
||||
)
|
||||
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True), concept)
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True), concept, eval_body=False)
|
||||
|
||||
if expected:
|
||||
assert evaluated.key == concept.key
|
||||
assert concept.body == expected_body
|
||||
else:
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.WHERE_CLAUSE_FAILED)
|
||||
assert evaluated.body == concept
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONDITION_FAILED)
|
||||
assert evaluated.body == where_clause
|
||||
assert evaluated.concept == concept
|
||||
assert evaluated.prop == ConceptParts.WHERE
|
||||
assert concept.body == expected_body
|
||||
|
||||
def test_i_can_evaluate_where_when_using_other_concept(self):
|
||||
sheerka, context, foo_true, foo_false = self.init_concepts(
|
||||
@@ -381,20 +389,20 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
Concept("foo_false", body="False"),
|
||||
)
|
||||
|
||||
concept = Concept("foo", where="foo_true").init_key()
|
||||
concept = Concept("foo", where="foo_true")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True), concept)
|
||||
assert evaluated.key == concept.key
|
||||
|
||||
concept = Concept("foo", where="foo_false")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, True), concept)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.WHERE_CLAUSE_FAILED)
|
||||
assert evaluated.body == concept
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONDITION_FAILED)
|
||||
assert evaluated.body == "foo_false"
|
||||
|
||||
concept = Concept("foo", where="foo_false").init_key()
|
||||
concept = Concept("foo", where="foo_false")
|
||||
evaluated = sheerka.evaluate_concept(self.get_context(sheerka, False, False), concept)
|
||||
assert evaluated.key == concept.key
|
||||
|
||||
def test_i_can_evaluate_disable_where_clause_evaluation(self):
|
||||
def test_i_can_enable_disable_where_clause_evaluation(self):
|
||||
sheerka, context, concept = self.init_concepts(
|
||||
Concept("foo", body="10", where="a > 10").def_var("a", None),
|
||||
)
|
||||
@@ -405,7 +413,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
evaluated = sheerka.evaluate_concept(context, concept)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
|
||||
def test_i_can_detect_infinite_recursion_with_numeric_constant(self):
|
||||
def test_i_can_detect_and_resolve_infinite_recursion_with_numeric_constant(self):
|
||||
sheerka, context, one_str, one_digit = self.init_concepts(
|
||||
Concept("one", body="1"),
|
||||
Concept("1", body="one"),
|
||||
@@ -423,7 +431,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
assert evaluated.key == one_str.key
|
||||
assert evaluated.body == InfiniteRecursionResolved(1)
|
||||
|
||||
def test_i_can_detect_infinite_recursion_with_boolean_constant(self):
|
||||
def test_i_can_detect_and_resolve_infinite_recursion_with_boolean_constant(self):
|
||||
sheerka, context, true_str, true_bool = self.init_concepts(
|
||||
Concept("true", body="True"),
|
||||
Concept("True", body="true"),
|
||||
@@ -523,3 +531,147 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
|
||||
evaluated = sheerka.evaluate_concept(context, forty_one_thousand)
|
||||
|
||||
assert evaluated.body == 41000
|
||||
|
||||
def test_i_can_evaluate_command(self):
|
||||
sheerka, context, command = self.init_concepts(Concept("command", body="a = 10"))
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, command)
|
||||
assert evaluated.key == command.key
|
||||
assert "a" not in sheerka.locals
|
||||
|
||||
sheerka.set_isa(context, command, sheerka.new(BuiltinConcepts.COMMAND))
|
||||
evaluated = sheerka.evaluate_concept(context, sheerka.new("command"))
|
||||
assert evaluated.key == command.key
|
||||
assert "a" in sheerka.locals
|
||||
|
||||
@pytest.mark.parametrize("metadata", [
|
||||
"where",
|
||||
"pre",
|
||||
"post",
|
||||
"ret"
|
||||
])
|
||||
def test_i_cannot_evaluate_python_statement_in_where_pre_post_ret(self, metadata, capsys):
|
||||
sheerka, context, foo = self.init_concepts("foo")
|
||||
setattr(foo.metadata, metadata, "a=10; print('10')")
|
||||
foo.metadata.need_validation = True
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, foo)
|
||||
captured = capsys.readouterr()
|
||||
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
error = evaluated.body
|
||||
assert sheerka.isinstance(error, BuiltinConcepts.PYTHON_SECURITY_ERROR)
|
||||
assert error.prop.value == metadata
|
||||
assert error.body == "a=10; print('10')"
|
||||
assert captured.out == ""
|
||||
|
||||
def test_python_builtin_function_are_forbidden_in_where_pre_post_ret(self, capsys):
|
||||
# I do the test only for PRE, as it will be the same for the other CounceptPart
|
||||
sheerka, context, foo, bar = self.init_concepts(
|
||||
Concept("foo", body="print('10')"), # print will be executed
|
||||
Concept("bar", pre="print('10')"), # print won't be executed
|
||||
)
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, foo, eval_body=True)
|
||||
captured = capsys.readouterr()
|
||||
assert evaluated.key == foo.key
|
||||
assert captured.out == "10\n"
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, bar)
|
||||
captured = capsys.readouterr()
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
|
||||
error = evaluated.body
|
||||
assert sheerka.isinstance(error, BuiltinConcepts.ERROR)
|
||||
assert captured.out == ""
|
||||
|
||||
def test_i_can_failed_a_concept_when_pre_clause_is_not_validated(self, capsys):
|
||||
sheerka, context, concept = self.init_concepts(
|
||||
Concept("foo", pre="in_context('foo')", body="print('10')"),
|
||||
)
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, concept, eval_body=True)
|
||||
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONDITION_FAILED)
|
||||
assert evaluated.body == "in_context('foo')"
|
||||
assert evaluated.concept == concept
|
||||
assert evaluated.prop == ConceptParts.PRE
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == ""
|
||||
|
||||
@pytest.mark.parametrize("concept, expected", [
|
||||
(Concept("foo"), []),
|
||||
(Concept("foo", pre="pre", post="post", ret="ret", where="where"), ["pre", "ret", "post"]),
|
||||
(Concept("foo", pre="a").def_var("a"), ["variables", "pre"]),
|
||||
(Concept("foo", pre="self"), ["body", "pre"]),
|
||||
(Concept("foo", pre="self + a").def_var("a"), ["variables", "body", "pre"]),
|
||||
(Concept("foo", pre="self + a", ret="ret").def_var("a"), ["variables", "body", "pre", "ret"]),
|
||||
(Concept("foo", body="body"), []) # only if eval_body_is_set
|
||||
])
|
||||
def test_i_can_compute_metadata_to_eval(self, concept, expected):
|
||||
sheerka, context, concept = self.init_concepts(concept)
|
||||
service = sheerka.services[SheerkaEvaluateConcept.NAME]
|
||||
|
||||
service.initialize_concept_asts(context, concept)
|
||||
assert service.compute_metadata_to_eval(context, concept) == expected
|
||||
|
||||
def test_i_can_compute_metadata_to_eval_for_where_and_body(self):
|
||||
sheerka = self.get_sheerka()
|
||||
service = sheerka.services[SheerkaEvaluateConcept.NAME]
|
||||
|
||||
context = self.get_context(sheerka, eval_where=True)
|
||||
concept = Concept("foo", where="where")
|
||||
service.initialize_concept_asts(context, concept)
|
||||
assert service.compute_metadata_to_eval(context, concept) == ["where"]
|
||||
|
||||
concept = Concept("foo", where="where a").def_var("a")
|
||||
service.initialize_concept_asts(context, concept)
|
||||
assert service.compute_metadata_to_eval(context, concept) == ["variables", "where"]
|
||||
|
||||
concept = Concept("foo", where="where self")
|
||||
service.initialize_concept_asts(context, concept)
|
||||
assert service.compute_metadata_to_eval(context, concept) == ["body", "where"]
|
||||
|
||||
context = self.get_context(sheerka, eval_body=True)
|
||||
concept = Concept("foo")
|
||||
assert service.compute_metadata_to_eval(context, concept) == ["variables", "body"]
|
||||
|
||||
context = self.get_context(sheerka, eval_body=True)
|
||||
concept = Concept("foo").def_var("a")
|
||||
assert service.compute_metadata_to_eval(context, concept) == ["variables", "body"]
|
||||
|
||||
context = self.get_context(sheerka, eval_body=True)
|
||||
concept = Concept("foo", body="body").def_var("a")
|
||||
assert service.compute_metadata_to_eval(context, concept) == ["variables", "body"]
|
||||
|
||||
@pytest.mark.parametrize("concept, expected", [
|
||||
(Concept("foo"), True),
|
||||
])
|
||||
def test_is_evaluated_is_correctly_set(self, concept, expected):
|
||||
sheerka, context, concept = self.init_concepts(concept)
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, concept, eval_body=True)
|
||||
|
||||
assert evaluated.key == concept.key
|
||||
assert concept.metadata.is_evaluated == expected
|
||||
|
||||
def test_i_only_compute_the_requested_metadata(self):
|
||||
sheerka, context, concept = self.init_concepts(
|
||||
Concept("foo", pre="'pre'", post="'post'", ret="'ret'", where="'where'", body="'body'").def_var("a", "'a'")
|
||||
)
|
||||
context.local_hints.add(BuiltinConcepts.EVAL_WHERE_REQUESTED) # to prove that we do not care
|
||||
context.local_hints.add(BuiltinConcepts.EVAL_BODY_REQUESTED) # to prove that we do not care
|
||||
|
||||
evaluated = sheerka.evaluate_concept(context, concept, metadata=['pre'])
|
||||
assert evaluated.values == {"a": Property("a", NotInit), ConceptParts.PRE: Property(ConceptParts.PRE, 'pre')}
|
||||
|
||||
# I cannot implement value cache for now
|
||||
# def test_values_when_no_variables_are_computed_only_once(self):
|
||||
# sheerka, context, foo = self.init_concepts(Concept("foo", body="10"))
|
||||
#
|
||||
# evaluated = sheerka.evaluate_concept(context, sheerka.new("foo"), eval_body=True)
|
||||
# assert evaluated.body == 10
|
||||
# assert len(evaluated.compiled) > 0
|
||||
#
|
||||
# evaluated_2 = sheerka.evaluate_concept(context, sheerka.new("foo"), eval_body=True)
|
||||
# assert evaluated_2.body == 10
|
||||
# assert len(evaluated_2.compiled) == 0
|
||||
|
||||
@@ -239,8 +239,6 @@ class TestSheerkaSetsManager(TestUsingMemoryBasedSheerka):
|
||||
Concept("foo"),
|
||||
Concept("bar"),
|
||||
Concept("baz"),
|
||||
create_new=True
|
||||
|
||||
)
|
||||
|
||||
sheerka.set_isa(context, foo, bar)
|
||||
@@ -250,6 +248,18 @@ class TestSheerkaSetsManager(TestUsingMemoryBasedSheerka):
|
||||
assert sheerka.isa(bar, baz)
|
||||
assert sheerka.isa(foo, baz)
|
||||
|
||||
def test_i_cannot_manage_isa_transitivity_when_using_body(self):
|
||||
sheerka, context, one, another_one, number = self.init_concepts(
|
||||
"one",
|
||||
Concept("another one", body="one"),
|
||||
"number"
|
||||
)
|
||||
|
||||
sheerka.set_isa(context, one, number)
|
||||
|
||||
assert sheerka.isa(one, number) # sanity
|
||||
assert not sheerka.isa(another_one, number) # Correct this misbehaviour when BuiltinConcepts.IS is implemented
|
||||
|
||||
def test_concept_expression_recurse_id_is_updated(self):
|
||||
sheerka, context, one, number, twenties = self.init_concepts(
|
||||
"one",
|
||||
|
||||
@@ -3,6 +3,7 @@ import ast
|
||||
import core.builtin_helpers
|
||||
import pytest
|
||||
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
|
||||
from core.concept import Concept
|
||||
|
||||
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
|
||||
|
||||
@@ -142,3 +143,73 @@ class TestBuiltinHelpers(TestUsingMemoryBasedSheerka):
|
||||
assert len(actual) == len(expected)
|
||||
for i in range(len(actual)):
|
||||
assert self.dump_ast(actual[i]) == self.dump_ast(expected[i])
|
||||
|
||||
@pytest.mark.parametrize("concepts, expected", [
|
||||
([], []),
|
||||
([Concept("foo", pre="True"), Concept("bar")], ["foo"]),
|
||||
([Concept("foo").def_var("a"), Concept("bar")], ["bar"]),
|
||||
])
|
||||
def test_i_can_resolve_ambiguity_when_empty(self, concepts, expected):
|
||||
context = self.get_context()
|
||||
res = core.builtin_helpers.resolve_ambiguity(context, concepts)
|
||||
assert [c.name for c in res] == expected
|
||||
|
||||
# @pytest.mark.parametrize("return_values", [
|
||||
# None,
|
||||
# []
|
||||
# ])
|
||||
# def test_i_can_resolve_simple_ambiguity_when_no_return_values(self, return_values):
|
||||
# context = self.get_context()
|
||||
#
|
||||
# assert core.builtin_helpers.remove_ambiguity(context, return_values) == return_values
|
||||
|
||||
# def test_resolve_ambiguity_concepts_with_no_variable_take_precedence(self):
|
||||
# context = self.get_context()
|
||||
# return_values = [
|
||||
# self.pretval(Concept("hello a").def_var("a", "world"), "hello word"),
|
||||
# self.pretval(Concept("hello world"), "hello word"),
|
||||
# # self.pretval(Concept("hello world", pre="False"), "hello word"),
|
||||
# self.retval(Concept("not a parser result")),
|
||||
# self.retval(Concept("status is false"), status=False),
|
||||
# self.pretval(Concept("false parser result"), status=False),
|
||||
# ]
|
||||
#
|
||||
# ret = core.builtin_helpers.remove_ambiguity(context, return_values)
|
||||
# assert ret.status
|
||||
# assert ret.parents == return_values
|
||||
#
|
||||
# filtered = ret.body
|
||||
# assert context.sheerka.isinstance(ret.body, BuiltinConcepts.FILTERED)
|
||||
# assert filtered.body == [
|
||||
# return_values[2],
|
||||
# return_values[3],
|
||||
# return_values[4],
|
||||
# return_values[1],
|
||||
# ]
|
||||
# assert filtered.iterable == return_values
|
||||
# assert filtered.predicate == "remove_ambiguity(context, iterable)"
|
||||
#
|
||||
# def test_resolve_ambiguity_failed_pre_condition_are_discarded(self):
|
||||
# context = self.get_context()
|
||||
# return_values = [
|
||||
# self.pretval(Concept("hello world"), "hello word"),
|
||||
# self.pretval(Concept("hello world", pre="False"), "hello word"),
|
||||
# ]
|
||||
#
|
||||
# ret = core.builtin_helpers.remove_ambiguity(context, return_values)
|
||||
# filtered = ret.body
|
||||
# assert context.sheerka.isinstance(ret.body, BuiltinConcepts.FILTERED)
|
||||
# assert filtered.body == [
|
||||
# return_values[0],
|
||||
# ]
|
||||
#
|
||||
# def test_resolve_ambiguity_original_return_value_is_returned_when_nothing_to_filter(self):
|
||||
# context = self.get_context()
|
||||
# return_values = [
|
||||
# self.pretval(Concept("hello a").def_var("a", "world"), "hello word"),
|
||||
# self.retval(Concept("not a parser result")),
|
||||
# self.retval(Concept("status is false"), status=False),
|
||||
# self.pretval(Concept("false parser result"), status=False),
|
||||
# ]
|
||||
#
|
||||
# assert core.builtin_helpers.remove_ambiguity(context, return_values) == return_values
|
||||
|
||||
@@ -165,10 +165,13 @@ class EvaluatorAllReduceFooBar(EvaluatorAllWithPriority):
|
||||
return ret
|
||||
|
||||
|
||||
class EvaluatorAllSuppressFooEntry(EvaluatorAllWithPriority):
|
||||
class EvaluatorAllReplaceFooEntry(EvaluatorAllWithPriority):
|
||||
"""
|
||||
This evaluator replaces the concept 'foo' by another one
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("suppress", 100)
|
||||
super().__init__("replace", 100)
|
||||
|
||||
def matches(self, context, return_values):
|
||||
super().matches(context, return_values)
|
||||
@@ -187,6 +190,30 @@ class EvaluatorAllSuppressFooEntry(EvaluatorAllWithPriority):
|
||||
return None
|
||||
|
||||
|
||||
class EvaluatorAllSuppressEntries(EvaluatorAllWithPriority):
|
||||
"""
|
||||
This evaluator totally removes 'foo' and 'bar' the entries from the workflow
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("suppress", 100)
|
||||
|
||||
def matches(self, context, return_values):
|
||||
super().matches(context, return_values)
|
||||
to_remove = [r for r in return_values if r.body.name in ("foo", "bar")]
|
||||
return len(to_remove) > 0
|
||||
|
||||
def eval(self, context, return_values):
|
||||
super().eval(context, return_values)
|
||||
|
||||
to_remove = [r for r in return_values if r.body.name in ("foo", "bar")]
|
||||
return context.sheerka.ret(
|
||||
self.name,
|
||||
True,
|
||||
[],
|
||||
parents=to_remove)
|
||||
|
||||
|
||||
class EvaluatorOneDoNotModifyExecutionFlow(EvaluatorOneWithPriority):
|
||||
"""
|
||||
To test that when eval() returns the initial return_value, the execution flow is not modified
|
||||
@@ -485,3 +512,22 @@ class TestSheerkaExecuteEvaluators(TestUsingMemoryBasedSheerka):
|
||||
|
||||
# check that 'foo' is no longer in res, but 'bar' is added
|
||||
assert res == [self.tretval(sheerka, Concept("bar"))]
|
||||
|
||||
def test_i_can_remove_return_values_from_the_execution_workflow(self):
|
||||
sheerka = self.get_sheerka()
|
||||
sheerka.evaluators = [EvaluatorAllSuppressEntries]
|
||||
|
||||
entries = [self.tretval(sheerka, Concept("foo")),
|
||||
self.tretval(sheerka, Concept("bar")),
|
||||
self.tretval(sheerka, Concept("baz"))]
|
||||
Out.debug_out = []
|
||||
res = sheerka.execute(self.get_context(sheerka), entries, [BuiltinConcepts.EVALUATION])
|
||||
|
||||
assert Out.debug_out == [
|
||||
"__EVALUATION [0] suppress - matches - target=['foo', 'bar', 'baz']",
|
||||
"__EVALUATION [0] suppress - eval - target=['foo', 'bar', 'baz']",
|
||||
"__EVALUATION [1] suppress - matches - target=['baz']",
|
||||
]
|
||||
|
||||
# check that 'foo' is no longer in res, but 'bar' is added
|
||||
assert res == [self.tretval(sheerka, Concept("baz"))]
|
||||
|
||||
Reference in New Issue
Block a user