import ast import pytest from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts from core.concept import Concept, CB, NotInit 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.PythonParser import PythonNode, PythonParser from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka def get_concept_name(concept): return concept.name def get_source_code_node(source_code, concepts=None): if source_code: python_node = PythonNode(source_code, ast.parse(source_code, f"", '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.concepts = concepts scwcn = SourceCodeWithConceptNode(None, None) scwcn.python_node = python_node return scwcn 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), ]) 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 @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_using_context(self): context = self.get_context() parsed = PythonParser().parse(context, ParserInput("test_using_context('value for param1', 10)")) evaluated = PythonEvaluator().eval(context, parsed) assert evaluated.status assert evaluated.value.startswith("I have access to Sheerka ! param1='value for param1', param2=10, event=") def test_i_can_eval_using_context_when_self_is_not_sheerka(self): sheerka, context = self.init_concepts() parsed = PythonParser().parse(context, ParserInput("create_new_concept(Concept('foo'))")) evaluated = PythonEvaluator().eval(context, parsed) assert evaluated.status assert sheerka.has_key("foo") @pytest.mark.parametrize("concept", [ Concept("foo"), Concept("foo", body="2"), Concept("foo").def_var("prop", "'a'"), Concept("foo", body="bar") ]) def test_simple_concepts_are_not_for_me(self, concept): context = self.get_context() context.sheerka.add_in_cache(Concept("foo")) parsed = PythonParser().parse(context, ParserInput("foo")) evaluated = PythonEvaluator().eval(context, parsed) assert not evaluated.status assert context.sheerka.isinstance(evaluated.value, BuiltinConcepts.NOT_FOR_ME) def test_i_can_eval_ast_expression_that_references_concepts(self): """ I can test modules with variables :return: """ context = self.get_context() context.sheerka.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.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 assert evaluated.value == CB("foo", 2) def test_i_can_eval_concept_token(self): context = self.get_context() context.sheerka.add_in_cache(Concept("foo", body="2")) parsed = PythonParser().parse(context, ParserInput("get_concept_name(c:foo:)")) python_evaluator = PythonEvaluator() python_evaluator.globals["get_concept_name"] = get_concept_name 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.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_concepts_weights(BuiltinConcepts.PRECEDENCE) == {'1001': 1, '1002': 2} def test_i_can_define_variables(self): sheerka, context = self.init_concepts() 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 assert 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.TOO_MANY_ERRORS) 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'" assert 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_do_not_include_not_initialized_variables_when_evaluating(self): sheerka, context, foo = self.init_concepts( Concept("foo a", pre="a == 'True'").def_var("a", "'True'").def_var("b")) foo.set_value("b", "'Initialized!'") context.obj = foo assert foo.get_value("a") == NotInit assert foo.get_value("b") == "'Initialized!'" my_globals = {} PythonEvaluator().update_globals_with_context(my_globals, context) assert my_globals == {"self": foo, "b": "'Initialized!'"} def test_i_can_use_sheerka_locals(self): sheerka, context = self.init_concepts() def func(i): return i + 1 sheerka.locals["func"] = func parsed = PythonParser().parse(context, ParserInput("func(10)")) python_evaluator = PythonEvaluator() evaluated = python_evaluator.eval(context, parsed) assert evaluated.status assert evaluated.value == 11