import ast import pytest from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts from core.builtin_helpers import CreateObjectIdentifiers from core.concept import Concept, CB 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, NamesWithAttributesVisitor from parsers.BaseNodeParser import SourceCodeNode, SourceCodeWithConceptNode from parsers.FunctionParser import FunctionParser from parsers.PythonParser import PythonNode, PythonParser from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka 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"", '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 assert 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 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.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'" 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_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, "", 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_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_, "", mode="eval") eval(compiled, {"x": "hello", "y": "world", "func": func})