Fixed #18 : Parsing and evaluating Python

This commit is contained in:
2023-05-14 12:12:29 +02:00
parent e41094f908
commit 09a0246420
46 changed files with 2084 additions and 165 deletions
+82 -58
View File
@@ -4,28 +4,27 @@ import pytest
from base import BaseTest
from core.BuiltinConcepts import BuiltinConcepts
from core.ExecutionContext import ExecutionContext, ExecutionContextActions
from core.ExecutionContext import ExecutionContext, ContextActions
from core.ReturnValue import ReturnValue
from core.services.SheerkaEngine import SheerkaEngine
from evaluators.CreateParserInput import CreateParserInput
from evaluators.base_evaluator import AllReturnValuesEvaluator, BaseEvaluator, EvaluatorEvalResult, \
EvaluatorMatchResult, \
OneReturnValueEvaluator
EvaluatorMatchResult, OneReturnValueEvaluator
from helpers import _rvc
from services.SheerkaEngine import SheerkaEngine
ALL_STEPS = [
ExecutionContextActions.BEFORE_PARSING,
ExecutionContextActions.PARSING,
ExecutionContextActions.AFTER_PARSING,
ExecutionContextActions.BEFORE_EVALUATION,
ExecutionContextActions.EVALUATION,
ExecutionContextActions.AFTER_EVALUATION
ContextActions.BEFORE_PARSING,
ContextActions.PARSING,
ContextActions.AFTER_PARSING,
ContextActions.BEFORE_EVALUATION,
ContextActions.EVALUATION,
ContextActions.AFTER_EVALUATION
]
class OneReturnValueEvaluatorForTesting(OneReturnValueEvaluator):
def __init__(self, name,
step: ExecutionContextActions,
step: ContextActions,
priority: int,
enabled=True,
match: bool | Callable = True,
@@ -56,12 +55,12 @@ class OneReturnValueEvaluatorForTesting(OneReturnValueEvaluator):
if ret_val != return_value:
ret_val.parents = [return_value]
return EvaluatorEvalResult(self.eval_result, self.eval_eaten or [return_value])
return EvaluatorEvalResult(self.eval_result, [return_value] if self.eval_eaten is None else self.eval_eaten)
class AllReturnValuesEvaluatorForTesting(AllReturnValuesEvaluator):
def __init__(self, name,
step: ExecutionContextActions,
step: ContextActions,
priority: int,
enabled=True,
match: bool | Callable = True,
@@ -91,31 +90,31 @@ class AllReturnValuesEvaluatorForTesting(AllReturnValuesEvaluator):
for ret_val in self.eval_result:
ret_val.parents = return_values
return EvaluatorEvalResult(self.eval_result, self.eval_eaten or return_values)
return EvaluatorEvalResult(self.eval_result, return_values if self.eval_eaten is None else self.eval_eaten)
class TestSheerkaEngine(BaseTest):
@pytest.fixture()
def service(self, sheerka):
return SheerkaEngine(sheerka)
return SheerkaEngine(sheerka) # I want a new instance to keep Sheerka clean (when a change execution_plan)
def test_i_can_compute_execution_plan(self, service):
assert service.compute_execution_plan([]) == {}
e1 = BaseEvaluator("eval1", ExecutionContextActions.BEFORE_EVALUATION, 5)
e2 = BaseEvaluator("eval2", ExecutionContextActions.BEFORE_EVALUATION, 5)
e3 = BaseEvaluator("eval3", ExecutionContextActions.BEFORE_EVALUATION, 10)
e4 = BaseEvaluator("eval4", ExecutionContextActions.EVALUATION, 10)
e5 = BaseEvaluator("eval5", ExecutionContextActions.AFTER_EVALUATION, 10, enabled=False)
e1 = BaseEvaluator("eval1", ContextActions.BEFORE_EVALUATION, 5)
e2 = BaseEvaluator("eval2", ContextActions.BEFORE_EVALUATION, 5)
e3 = BaseEvaluator("eval3", ContextActions.BEFORE_EVALUATION, 10)
e4 = BaseEvaluator("eval4", ContextActions.EVALUATION, 10)
e5 = BaseEvaluator("eval5", ContextActions.AFTER_EVALUATION, 10, enabled=False)
res = service.compute_execution_plan([e1, e2, e3, e4, e5])
assert res == {ExecutionContextActions.BEFORE_EVALUATION: {5: [e1, e2], 10: [e3]},
ExecutionContextActions.EVALUATION: {10: [e4]}}
assert res == {ContextActions.BEFORE_EVALUATION: {5: [e1, e2], 10: [e3]},
ContextActions.EVALUATION: {10: [e4]}}
def test_i_can_call_execute(self, sheerka, context, service):
service.execution_plan = {ExecutionContextActions.BEFORE_EVALUATION: {50: [CreateParserInput()]}}
service.execution_plan = {ContextActions.BEFORE_EVALUATION: {50: [CreateParserInput()]}}
start = [ReturnValue("TestSheerkaEngine", True, sheerka.newn(BuiltinConcepts.USER_INPUT, command="1 + 1"))]
ret = service.execute(context, start, [ExecutionContextActions.BEFORE_EVALUATION])
ret = service.execute(context, start, [ContextActions.BEFORE_EVALUATION])
assert len(ret) == 1
ret = ret[0]
assert isinstance(ret, ReturnValue)
@@ -127,7 +126,7 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = {}
start = [_rvc("foo")]
ret = service.execute(context, start, [ExecutionContextActions.EVALUATION])
ret = service.execute(context, start, [ContextActions.EVALUATION])
assert ret == start
@@ -135,12 +134,12 @@ class TestSheerkaEngine(BaseTest):
# properly init the service
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval1", ExecutionContextActions.AFTER_PARSING, 21, match=False),
_("eval2", ExecutionContextActions.BEFORE_EVALUATION, 5, match=False),
_("eval3", ExecutionContextActions.AFTER_EVALUATION, 12, match=False),
_("eval4", ExecutionContextActions.EVALUATION, 99, match=False),
_("eval5", ExecutionContextActions.BEFORE_PARSING, 5, match=False),
_("eval6", ExecutionContextActions.PARSING, 25, match=False),
_("eval1", ContextActions.AFTER_PARSING, 21, match=False),
_("eval2", ContextActions.BEFORE_EVALUATION, 5, match=False),
_("eval3", ContextActions.AFTER_EVALUATION, 12, match=False),
_("eval4", ContextActions.EVALUATION, 99, match=False),
_("eval5", ContextActions.BEFORE_PARSING, 5, match=False),
_("eval6", ContextActions.PARSING, 25, match=False),
]
service.execution_plan = service.compute_execution_plan(evaluators)
@@ -155,15 +154,15 @@ class TestSheerkaEngine(BaseTest):
# properly init the service
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval1", ExecutionContextActions.EVALUATION, 20, match=False),
_("eval2", ExecutionContextActions.EVALUATION, 5, match=False),
_("eval3", ExecutionContextActions.EVALUATION, 20, match=False),
_("eval4", ExecutionContextActions.EVALUATION, 99, match=False),
_("eval1", ContextActions.EVALUATION, 20, match=False),
_("eval2", ContextActions.EVALUATION, 5, match=False),
_("eval3", ContextActions.EVALUATION, 20, match=False),
_("eval4", ContextActions.EVALUATION, 99, match=False),
]
service.execution_plan = service.compute_execution_plan(evaluators)
start = [_rvc("foo")]
service.execute(context, start, [ExecutionContextActions.EVALUATION])
service.execute(context, start, [ContextActions.EVALUATION])
# to check what happened, look at the execution context children
evaluators_executed = [ec.action_context["evaluator"] for ec in context.get_children() if
@@ -176,7 +175,7 @@ class TestSheerkaEngine(BaseTest):
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[rv_bar])
@@ -184,8 +183,8 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [rv_foo]
service.execute(context, start, [ExecutionContextActions.EVALUATION])
children = [ec for ec in context.get_children() if ec.action == ExecutionContextActions.EVALUATING_ITERATION]
service.execute(context, start, [ContextActions.EVALUATION])
children = [ec for ec in context.get_children() if ec.action == ContextActions.EVALUATING_ITERATION]
assert len(children) == 2
def test_eval_is_not_called_if_match_fails_for_one_return(self, context, service):
@@ -193,7 +192,7 @@ class TestSheerkaEngine(BaseTest):
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[_rvc("bar")])
@@ -201,7 +200,7 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [_rvc("baz")]
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == start
# check what happen in details
@@ -214,7 +213,7 @@ class TestSheerkaEngine(BaseTest):
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[_rvc("bar")])
@@ -222,7 +221,7 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [_rvc("foo")]
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == [_rvc("bar")]
assert res[0].parents == start
@@ -238,7 +237,7 @@ class TestSheerkaEngine(BaseTest):
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[rv_qux])
@@ -246,7 +245,7 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [rv_bar, rv_foo, rv_baz]
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == [rv_bar, rv_qux, rv_baz] # We must keep the order ! rv_qux replaces rv_foo
assert res[0].parents is None
assert res[1].parents == [rv_foo]
@@ -267,12 +266,12 @@ class TestSheerkaEngine(BaseTest):
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[rv_bar]),
_("eval2",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[rv_baz])
@@ -280,7 +279,7 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [rv_qux, rv_foo, rv_qux]
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == [rv_qux, rv_bar, rv_baz, rv_qux] # they both eat it !
assert res[1].parents == [rv_foo]
assert res[2].parents == [rv_foo]
@@ -293,12 +292,12 @@ class TestSheerkaEngine(BaseTest):
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[rv_bar]),
_("eval2",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
30,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[rv_baz])
@@ -306,7 +305,7 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [rv_foo]
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == [rv_baz]
assert res[0].parents == start
@@ -315,7 +314,7 @@ class TestSheerkaEngine(BaseTest):
_ = AllReturnValuesEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r[0].value, "foo"),
eval_result=[_rvc("bar")])
@@ -323,11 +322,11 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [_rvc("baz")]
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == start
start = [_rvc("foo")]
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == [_rvc("bar")]
assert res[0].parents == start
@@ -338,7 +337,7 @@ class TestSheerkaEngine(BaseTest):
_ = AllReturnValuesEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda lst: context.sheerka.isinstance(lst[0].value, "foo"),
eval_result=[rv_bar])
@@ -346,7 +345,7 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [rv_baz, rv_foo] # foo is not the first in the list
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == start
# check what happen in details
@@ -360,7 +359,7 @@ class TestSheerkaEngine(BaseTest):
_ = AllReturnValuesEvaluatorForTesting
evaluators = [
_("eval1",
ExecutionContextActions.EVALUATION,
ContextActions.EVALUATION,
20,
match=lambda lst: context.sheerka.isinstance(lst[0].value, "foo"),
eval_result=[rv_bar])
@@ -368,7 +367,7 @@ class TestSheerkaEngine(BaseTest):
service.execution_plan = service.compute_execution_plan(evaluators)
start = [rv_foo, rv_baz]
res = service.execute(context, start, [ExecutionContextActions.EVALUATION])
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == [rv_bar]
assert res[0].parents == start
@@ -377,3 +376,28 @@ class TestSheerkaEngine(BaseTest):
exec_context = next(filter(lambda ec: "evaluator" in ec.action_context, context.get_children()))
evaluation_trace = exec_context.values["evaluation"]
assert evaluation_trace == {"match": True, "new": res, "eaten": start}
def test_ret_val_not_removed_does_not_cause_infinite_recursion(self, context, service):
rv_foo, rv_bar = _rvc("foo"), _rvc("bar") # rv => ReturnValue
# properly init the service
# both evaluator want to eat 'foo'
_ = OneReturnValueEvaluatorForTesting
evaluators = [
_("eval",
ContextActions.EVALUATION,
20,
match=lambda r: context.sheerka.isinstance(r.value, "foo"),
eval_result=[rv_bar], eval_eaten=[]),
]
service.execution_plan = service.compute_execution_plan(evaluators)
# in the test, 'foo' produces 'bar', but is not removed
# during the second iteration, 'foo' still exists, so it will produce 'bar' again
# and so on...
# This test validate that the infinite loop is broken
start = [rv_foo]
res = service.execute(context, start, [ContextActions.EVALUATION])
assert res == [rv_bar]
assert res[0].parents == [rv_foo]