Files
Sheerka-Old/tests/evaluators/test_PythonEvaluator.py
T
kodjo 87cab44fb8 Fixed #125: SheerkaErrorManager
Fixed #135: Change services service priorities
Fixed #136: ErrorManager: Implement recognize_error
Fixed #137: BNFNodeParser : Error when parsing regex with sub parsers
Fixed #138: get_last_errors(): real errors sources are lost
Fixed #139: OneError return value removes the origin of the error
Fixed #140: Concept variables are not correctly handled when parsing sub expression
Fixed #143: Implement has_unknown_concepts()
2021-10-28 14:04:41 +02:00

409 lines
17 KiB
Python

import ast
import pytest
from core.ast_helpers import NamesWithAttributesVisitor
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.builtin_helpers import CreateObjectIdentifiers
from core.concept import Concept
from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
from core.sheerka.services.SheerkaExecute import ParserInput
from core.tokenizer import Tokenizer
from evaluators.PythonEvaluator import PythonEvaluator, PythonEvalError
from parsers.BaseNodeParser import SourceCodeNode, SourceCodeWithConceptNode
from parsers.FunctionParser import FunctionParser
from parsers.PythonParser import PythonNode, PythonParser
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.parsers.parsers_utils import CB, compare_with_test_object
def get_obj_name(obj):
return obj.name
def return_return_value(status):
return ReturnValueConcept("who", status, f"the value is {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__" + CreateObjectIdentifiers.sanitize(concept.name)
if concept.id:
identifier += "__" + concept.id
identifier += "__C__"
source_code = source_code.replace(concept_name, identifier)
concepts[identifier] = concept
if source_code:
python_node = PythonNode(source_code, ast.parse(source_code, f"<source>", 'eval'))
else:
python_node = PythonNode("", None)
if concepts is None:
tokens = list(Tokenizer(source_code, yield_eof=False))
return SourceCodeNode(0, len(tokens), tokens, python_node=python_node)
else:
python_node.objects = concepts
scwcn = SourceCodeWithConceptNode(None, None)
scwcn.python_node = python_node
return scwcn
def get_ret_val_from_source_code(context, source_code, concepts):
parsed = get_source_code_node(source_code, concepts)
return context.sheerka.ret("parsers.??", True, ParserResultConcept(value=parsed))
class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("ret_val, expected", [
(ReturnValueConcept("some_name", True, ParserResultConcept(value=PythonNode("", None))), True),
(ReturnValueConcept("some_name", True, ParserResultConcept(value=get_source_code_node(""))), True),
(ReturnValueConcept("some_name", True, ParserResultConcept(value=get_source_code_node("", {}))), True),
(ReturnValueConcept("some_name", True, ParserResultConcept(value="other thing")), False),
(ReturnValueConcept("some_name", False, "not relevant"), False),
(ReturnValueConcept("some_name", True, Concept()), False)
])
def test_i_can_match(self, ret_val, expected):
context = self.get_context()
assert PythonEvaluator().matches(context, ret_val) == expected
@pytest.mark.parametrize("text, expected", [
("1 + 1", 2),
("test()", "I have access to Sheerka !"),
("sheerka.test()", "I have access to Sheerka !"),
("a=10\na", 10),
("Concept('foo')", Concept('foo')),
("BuiltinConcepts.NOP", BuiltinConcepts.NOP),
("in_context(BuiltinConcepts.EVAL_QUESTION_REQUESTED)", False),
])
def test_i_can_eval(self, text, expected):
context = self.get_context()
parsed = PythonParser().parse(context, ParserInput(text))
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value == expected
def test_i_can_eval_isinstance(self):
sheerka, context, foo = self.init_concepts("foo")
parsed = PythonParser().parse(context, ParserInput("isinstance('foo', str)"))
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value
parsed = PythonParser().parse(context, ParserInput("isinstance(foo, 'foo')"))
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value
def test_i_can_eval_context_obj_properties(self):
sheerka, context, foo = self.init_concepts(Concept("foo").def_var("a", "hello world!").auto_init())
context = self.get_context()
parsed = PythonParser().parse(context, ParserInput("a"))
context.obj = foo
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value == "hello world!"
@pytest.mark.parametrize("source_code_node, expected", [
(get_source_code_node("1 + 1"), 2),
(get_source_code_node("one + one", {"one": Concept("one", body="1")}), 2)
])
def test_i_can_eval_source_code_node(self, source_code_node, expected):
context = self.get_context()
return_value = context.sheerka.ret("parsers.??", True, ParserResultConcept(value=source_code_node))
evaluated = PythonEvaluator().eval(context, return_value)
assert evaluated.status
assert evaluated.value == expected
def test_i_can_eval_a_method_that_requires_the_context(self):
context = self.get_context()
parsed = PythonParser().parse(context, ParserInput("test_using_context('value for param')"))
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value == "I have access to Sheerka ! param='value for param', event='xxx'."
def test_i_can_eval_using_context_when_self_is_not_sheerka(self):
sheerka, context = self.init_test().unpack()
parsed = PythonParser().parse(context, ParserInput("create_new_concept(Concept('foo'))"))
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert sheerka.services[SheerkaConceptManager.NAME].has_key("foo")
def test_i_can_eval_ast_expression_that_references_concepts(self):
"""
I can test modules with variables
:return:
"""
context = self.get_context()
context.sheerka.test_only_add_in_cache(Concept("foo", body="1"))
parsed = PythonParser().parse(context, ParserInput("foo + 2"))
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value == 3
def test_i_can_eval_ast_module_that_references_concepts(self):
"""
I can test modules with variables
:return:
"""
context = self.get_context()
context.sheerka.test_only_add_in_cache(Concept("foo"))
parsed = PythonParser().parse(context, ParserInput("def a(b):\n return b\na(c:foo:)"))
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
assert evaluated.value == Concept("foo").init_key()
def test_i_can_eval_ast_module_that_references_concepts_with_body(self):
"""
I can test modules with variables
:return:
"""
sheerka, context, foo = self.init_concepts(Concept("foo", body="2"))
parsed = PythonParser().parse(context, ParserInput("def a(b):\n return b\na(foo)"))
evaluated = PythonEvaluator().eval(context, parsed)
assert evaluated.status
compare_with_test_object(evaluated.value, CB("foo", 2))
def test_i_can_eval_concept_token(self):
context = self.get_context()
context.sheerka.test_only_add_in_cache(Concept("foo", body="2"))
context.add_to_short_term_memory("get_obj_name", get_obj_name)
parsed = PythonParser().parse(context, ParserInput("get_obj_name(c:foo:)"))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
assert evaluated.value == "foo"
def test_i_can_eval_when_expect_success(self):
context = self.get_context()
context.sheerka.test_only_add_in_cache(Concept("foo", body="2"))
parsed = PythonParser().parse(context, ParserInput("foo==2"))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
assert not evaluated.value # the first test is between Concept(foo) and int(2)
context.protected_hints.add(BuiltinConcepts.EVAL_UNTIL_SUCCESS_REQUESTED)
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
assert evaluated.value # we test until we compare foo.body and 2
parsed = PythonParser().parse(context, ParserInput("foo==3"))
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
assert not evaluated.value # neither foo or foo.body ==3
def test_i_can_call_function_with_complex_concepts(self):
sheerka, context, plus, mult = self.init_concepts(
self.from_def_concept("plus", "a plus b", ["a", "b"]),
self.from_def_concept("mult", "a mult b", ["a", "b"]),
)
parsed = PythonParser().parse(context,
ParserInput("set_is_greater_than(BuiltinConcepts.PRECEDENCE, mult, plus)"))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
assert sheerka.get_weights(BuiltinConcepts.PRECEDENCE) == {'c:__var__0 plus __var__1|1001:': 1,
'c:__var__0 mult __var__1|1002:': 2}
def test_i_can_define_variables(self):
sheerka, context = self.init_test().unpack()
parsed = PythonParser().parse(context, ParserInput("a=10"))
python_evaluator = PythonEvaluator()
python_evaluator.eval(context, parsed)
parsed = PythonParser().parse(context, ParserInput("a"))
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
assert evaluated.body == 10
def test_i_can_get_all_possibles_globals(self):
sheerka, context, foo = self.init_concepts(Concept("foo", body="foo").auto_init())
python_evaluator = PythonEvaluator()
my_globals = {
"a": "a string",
"b": self.test_i_can_get_all_possibles_globals,
"foo": foo
}
all_globals = python_evaluator.get_all_possible_globals(context, my_globals)
assert len(all_globals) == 2
compare_with_test_object(all_globals[0]["foo"], CB(foo, "foo"))
assert all_globals[1]["foo"] == 'foo' # body is evaluated
def test_i_can_detect_one_error(self):
sheerka, context, foo = self.init_concepts("foo")
parsed = PythonParser().parse(context, ParserInput("foo + 1"))
evaluated = PythonEvaluator().eval(context, parsed)
assert not evaluated.status
assert context.sheerka.isinstance(evaluated.value, BuiltinConcepts.ERROR)
error = evaluated.body.body
assert isinstance(error, PythonEvalError)
assert isinstance(error.error, TypeError)
assert error.error.args[0] == "unsupported operand type(s) for +: 'Concept' and 'int'"
assert error.concepts == {'foo': foo}
def test_i_can_detect_multiple_errors(self):
sheerka, context, foo = self.init_concepts(Concept("foo", body="'string'"))
parsed = PythonParser().parse(context, ParserInput("foo + 1"))
evaluated = PythonEvaluator().eval(context, parsed)
assert not evaluated.status
assert context.sheerka.isinstance(evaluated.value, BuiltinConcepts.ERROR)
error0 = evaluated.body.body[0]
assert isinstance(error0, PythonEvalError)
assert isinstance(error0.error, TypeError)
assert error0.error.args[0] == "unsupported operand type(s) for +: 'Concept' and 'int'"
compare_with_test_object(error0.concepts, {'foo': CB(foo, 'string')})
error1 = evaluated.body.body[1]
assert isinstance(error1, PythonEvalError)
assert isinstance(error1.error, TypeError)
assert error1.error.args[0] == 'can only concatenate str (not "int") to str'
assert error1.concepts == {'foo': 'string'}
def test_i_can_eval_concept_with_ret(self):
sheerka, context, one, the = self.init_concepts("one", Concept("the a", ret="a").def_var("a"))
ret_val = get_ret_val_from_source_code(context, "test_using_context(the one)", {
"the one": self.get_concept_instance(sheerka, the, a="one")
})
evaluated = PythonEvaluator().eval(context, ret_val)
assert evaluated.status
assert evaluated.value == "I have access to Sheerka ! param=(1001)one, event='xxx'."
def test_i_can_eval_rules_from_python_parser(self):
sheerka, context = self.init_test().unpack()
parsed_ret_val = PythonParser().parse(context, ParserInput("r:|1:.id"))
assert parsed_ret_val.status
evaluated = PythonEvaluator().eval(context, parsed_ret_val)
assert evaluated.status
assert evaluated.value == "1"
def test_i_can_eval_rules_from_function_parser(self):
context = self.get_context()
context.add_to_short_term_memory("get_obj_name", get_obj_name)
parsed = FunctionParser().parse(context, ParserInput("get_obj_name(r:|1:)"))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
assert evaluated.value == "Print return values"
def test_i_can_update_sheerka_memory_using_assignation(self):
sheerka, context = self.init_test().unpack()
parsed = PythonParser().parse(context, ParserInput("a = 10"))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
from_memory = sheerka.get_from_memory(context, "a")
assert from_memory.obj == 10
@pytest.mark.parametrize("method, expected_status", [
("return_return_value(True)", True),
("return_return_value(False)", False),
])
def test_i_can_eval_a_function_that_returns_a_return_value(self, method, expected_status):
context = self.get_context()
context.add_to_short_term_memory("return_return_value", return_return_value)
parsed = FunctionParser().parse(context, ParserInput(method))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
ret_val = return_return_value(expected_status)
assert evaluated.status == expected_status
assert evaluated.value == ret_val.body
assert ret_val in evaluated.parents
@pytest.mark.parametrize("text, expected", [
("foo.bar.baz", [["foo", "bar", "baz"]]),
("foo.bar.baz; one.two.three", [["foo", "bar", "baz"]]),
("foo.bar.baz; one.two.three; foo.bar", [["foo", "bar", "baz"], ["foo", "bar"]]),
("one.two.three", []),
("foo", [["foo"]]),
("foo.bar[1].baz", [["foo", "bar", "baz"]]),
("foo[1].bar.baz", [["foo", "bar", "baz"]]),
])
def test_names_with_attributes_visitor(self, text, expected):
ast_ = ast.parse(text, "<src>", mode="exec")
visitor = NamesWithAttributesVisitor()
sequence = visitor.get_sequences(ast_, "foo")
assert sequence == expected
@pytest.mark.parametrize("parser, value", [
(PythonParser(), "3"),
(FunctionParser(), "3"),
(PythonParser(), 3),
(FunctionParser(), 3),
])
def test_i_can_eval_unresolved_rules(self, parser, value):
context = self.get_context()
context.add_to_short_term_memory("get_obj_name", get_obj_name)
context.add_to_short_term_memory("x", value)
parsed = parser.parse(context, ParserInput("get_obj_name(r:|x:)"))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
assert evaluated.status
assert evaluated.value == context.sheerka.get_rule_by_id(str(value)).name
def test_i_cannot_call_methods_with_side_effect_when_is_question_is_set(self):
sheerka, context, foo, bar = self.init_concepts("foo", "bar")
context.add_to_protected_hints(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
parsed = PythonParser().parse(context, ParserInput("set_isa(foo, bar)"))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
assert sheerka.has_error(context, evaluated, __type="MethodAccessError")
def test_something(self):
def func(**kwargs):
for k, v in kwargs.items():
print(f"{k=}, {v=}")
to_compile = "func(x=y)"
ast_ = ast.parse(to_compile, mode="eval")
compiled = compile(ast_, "<test>", mode="eval")
eval(compiled, {"x": "hello", "y": "world", "func": func})