import ast import pytest from base import BaseTest, DummyObj from common.global_symbols import NoFirstToken, NotFound, NotInit, Removed from conftest import NewOntology from core.BuiltinConcepts import BuiltinConcepts from core.ExecutionContext import ContextActions from core.concept import ConceptDefaultProps from core.error import MethodAccessError from core.python_fragment import PythonFragment from evaluators.PythonParser import PythonParser from helpers import _rv, define_new_concept, get_concepts, get_evaluated_concept, get_evaluated_concepts, get_metadata from parsers.ParserInput import ParserInput from parsers.tokenizer import Token, TokenKind from services.SheerkaConceptManager import ConceptRef from services.SheerkaPython import EvalMethod, EvaluationContext, Expando, MultipleResults, ObjectRef, SheerkaPython def get_python_fragment(sheerka, context, command): pi = ParserInput(command) pi.init() parser_start = _rv(sheerka.newn(BuiltinConcepts.PARSER_INPUT, pi=pi)) ret = PythonParser().eval(context, None, parser_start) return ret.new[0].value.pf class TestSheerkaPython(BaseTest): @pytest.fixture() def service(self, sheerka) -> SheerkaPython: return sheerka.services[SheerkaPython.NAME] @pytest.mark.parametrize("text, expected", [ ("1 + 1", 2), ("echo('I have access to Sheerka !')", "I have access to Sheerka !"), ("sheerka.echo('I have access to Sheerka !')", "I have access to Sheerka !"), ("a=10\na", 10), ("NotInit", NotInit), ("NotFound", NotFound), ("Removed", Removed), ("NoFirstToken", NoFirstToken), ]) def test_i_can_evaluate_simple_expression(self, sheerka, context, service, text, expected): with NewOntology(context, "test_i_can_evaluate_simple_expression"): python_fragment = get_python_fragment(sheerka, context, text) ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret == expected def test_i_can_eval_isinstance_with_a_type(self, sheerka, context, service): python_fragment = get_python_fragment(sheerka, context, "isinstance('some string', str)") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret is True def test_i_can_eval_isinstance_with_a_concept(self, sheerka, context, service): with NewOntology(context, "test_i_can_eval_isinstance_for_concept"): get_concepts(context, "foo", use_sheerka=True) python_fragment = get_python_fragment(sheerka, context, "isinstance(foo, 'foo')") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret is True # 'foo' is also a Concept python_fragment = get_python_fragment(sheerka, context, "isinstance(foo, Concept)") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret is True def test_i_can_use_value_from_global_namespace(self, sheerka, context, service): python_fragment = get_python_fragment(sheerka, context, "self.a") ret = service.evaluate_python(context, EvaluationContext(), python_fragment, {"self": DummyObj("my dummy value")}) assert ret == "my dummy value" def test_i_can_eval_using_eval_ref(self, sheerka, context, service): python_fragment = get_python_fragment(sheerka, context, "a") python_fragment.namespace = {"a": ObjectRef("self", "a")} ret = service.evaluate_python(context, EvaluationContext(), python_fragment, {"self": DummyObj("my dummy value")}) assert ret == "my dummy value" def test_i_can_eval_concept_properties(self, sheerka, context, service): with NewOntology(context, "test_i_can_eval_concept_properties"): foo_meta = get_metadata("foo", variables=[("a", "'hello world'")]) define_new_concept(context, foo_meta) python_fragment = get_python_fragment(sheerka, context, "foo.a") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret == "hello world" def test_i_can_eval_python_mixed_with_concept(self, sheerka, context, service): with NewOntology(context, "test_i_can_eval_python_mixed_with_concept"): foo_meta = get_metadata("foo", variables=[("a", "1")]) bar_meta = get_metadata("bar", body="2") get_concepts(context, foo_meta, bar_meta, use_sheerka=True) python_fragment = get_python_fragment(sheerka, context, "bar + foo.a") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret == 3 def test_i_can_eval_when_multiple_concepts(self, sheerka, context, service): with NewOntology(context, "test_i_can_eval_when_multiple_concepts"): get_concepts(context, get_metadata("one", body="'one'"), get_metadata("one", body="1"), use_sheerka=True) python_fragment = get_python_fragment(sheerka, context, "one + 1") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret == 2 def test_i_can_eval_when_multiple_result_in_local_namespace(self, sheerka, context, service): # In the test, the PythonFragment contains a MultipleResult in its namespace # (normally, the MultipleResult is created inside the evaluate_python) # We need to make sure that multiple results are created in the same way with NewOntology(context, "test_i_can_eval_when_multiple_result_in_local_namespace"): one1, one2 = get_concepts(context, get_metadata("one", body="'one'"), get_metadata("one", body="1"), use_sheerka=True) concept_ref = "__concept_id__" ast_tree = ast.parse(concept_ref, "", 'eval') ref = MultipleResults(ConceptRef(one1), ConceptRef(one2)) python_fragment = PythonFragment(concept_ref, ast_tree=ast_tree, namespace={concept_ref: ref}) ret = service.evaluate_python(context, EvaluationContext(eval_method=EvalMethod.All), python_fragment) evaluated_one1, evaluated_one2 = get_evaluated_concepts(context, one1, one2, use_sheerka=True) assert ret == MultipleResults(evaluated_one1, "one", evaluated_one2, 1) def test_i_can_remember_previous_results(self, sheerka, context, service): python_fragment = get_python_fragment(sheerka, context, "a=10") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret is None python_fragment = get_python_fragment(sheerka, context, "a") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret == 10 def test_i_can_import_module(self, sheerka, context, service): python_fragment = get_python_fragment(sheerka, context, "import math") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret is None python_fragment = get_python_fragment(sheerka, context, "math.sqrt(4)") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret == 2 def test_i_can_import_function_from_module(self, sheerka, context, service): python_fragment = get_python_fragment(sheerka, context, "from math import sqrt") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret is None python_fragment = get_python_fragment(sheerka, context, "sqrt(4)") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret == 2 def test_i_can_eval_when_context_is_needed(self, sheerka, context, service): with NewOntology(context, "test_i_can_eval_when_context_is_needed"): python_fragment = get_python_fragment(sheerka, context, "define_new_concept('foo')") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert sheerka.isinstance(ret.value, BuiltinConcepts.NEW_CONCEPT) # for info, there are two level of value # one for PythonEvaluator return value # one for the ConceptManager return value def test_i_can_return_multiple_results(self, sheerka, context, service): with NewOntology(context, "test_i_can_return_multiple_values"): foo_1, foo_2, foo_3 = get_concepts(context, get_metadata("foo", body="'foo'"), get_metadata("foo", body="True"), get_metadata("foo", body="'bar'"), use_sheerka=True) python_fragment = get_python_fragment(sheerka, context, "foo") evaluation_context = EvaluationContext(eval_method=EvalMethod.All) ret = service.evaluate_python(context, evaluation_context, python_fragment) assert ret == MultipleResults(get_evaluated_concept(foo_1, body='foo'), "foo", get_evaluated_concept(foo_2, body=True), True, get_evaluated_concept(foo_3, body='bar'), "bar") def test_i_can_eval_until_a_successful_result_is_found(self, sheerka, context, service): with NewOntology(context, "test_i_can_eval_when_multiple_concepts"): get_concepts(context, get_metadata("one", body="'one'"), get_metadata("one", body="1"), get_metadata("one", body="2"), use_sheerka=True) python_fragment = get_python_fragment(sheerka, context, "one + 1") service.evaluate_python(context, EvaluationContext(), python_fragment) # check the number of evaluated namespaces # there are 3 concepts, so there must be 6 results # But we stop after the second 'one' concept ec = next(filter(lambda _ec: _ec.action == ContextActions.EVALUATING_PYTHON, context.get_children())) assert len(ec.values["all_results"]) == 4 def test_i_can_eval_all_namespaces(self, sheerka, context, service): with NewOntology(context, "test_i_can_return_multiple_values"): foo_1, foo_2, foo_3 = get_concepts(context, get_metadata("foo", body="'foo'"), get_metadata("foo", body="True"), get_metadata("foo", body="'bar'"), use_sheerka=True) python_fragment = get_python_fragment(sheerka, context, "foo") evaluation_context = EvaluationContext(eval_method=EvalMethod.All) service.evaluate_python(context, evaluation_context, python_fragment) # check the number of evaluated namespaces ec = next(filter(lambda _ec: _ec.action == ContextActions.EVALUATING_PYTHON, context.get_children())) assert len(ec.values["all_results"]) == 6 def test_i_can_eval_until_true(self, sheerka, context, service): with NewOntology(context, "test_i_can_return_multiple_values"): get_concepts(context, get_metadata("foo", body="False"), get_metadata("foo", body="True"), get_metadata("foo", body="'bar'"), use_sheerka=True) python_fragment = get_python_fragment(sheerka, context, "foo") evaluation_context = EvaluationContext(eval_method=EvalMethod.UntilTrue) res = service.evaluate_python(context, evaluation_context, python_fragment) assert res is True # check the number of evaluated namespaces # We stop after the second 'one' concept, so only 4 results ec = next(filter(lambda _ec: _ec.action == ContextActions.EVALUATING_PYTHON, context.get_children())) assert len(ec.values["all_results"]) == 4 def test_eval_until_success_return_false_if_true_is_not_found(self, sheerka, context, service): with NewOntology(context, "test_i_can_return_multiple_values"): get_concepts(context, get_metadata("foo", body="False"), get_metadata("foo", body="'bar'"), use_sheerka=True) python_fragment = get_python_fragment(sheerka, context, "foo") evaluation_context = EvaluationContext(eval_method=EvalMethod.UntilTrue) ret = service.evaluate_python(context, evaluation_context, python_fragment) assert ret is False def test_i_can_return_empty_list(self, sheerka, context, service): python_fragment = get_python_fragment(sheerka, context, "[]") ret = service.evaluate_python(context, EvaluationContext(), python_fragment) assert ret == [] def test_can_create_namespaces(self, context, service): with NewOntology(context, "test_i_can_eval_when_context_is_needed"): namespace = service.create_namespace(context, ['in_context'], None, {}, {}, False) assert namespace == {"in_context": context.in_context} namespace = service.create_namespace(context, ['isinstance'], None, {}, {}, False) assert namespace == {"isinstance": context.sheerka.extended_isinstance} namespace = service.create_namespace(context, ['print'], None, {}, {}, False) assert namespace == {"print": print} namespace = service.create_namespace(context, ['print'], None, {}, {}, True) assert namespace == {} # print method has side effect, so it's excluded # ################### # sheerka expando # ################### namespace = service.create_namespace(context, ['sheerka'], set(), {}, {}, False) assert isinstance(namespace["sheerka"], Expando) assert len(vars(namespace["sheerka"])) == 1 # 'expando_name' only namespace = service.create_namespace(context, ['sheerka'], {"new", "echo"}, {}, {}, False) assert isinstance(namespace["sheerka"], Expando) assert len(vars(namespace["sheerka"])) == 3 # 'expando_name' + new() + echo() with pytest.raises(MethodAccessError): # new method is not allowed if expression_only is True service.create_namespace(context, ['sheerka'], {"new", "echo"}, {}, {}, True) # ################### # sheerka methods # ################### namespace = service.create_namespace(context, ['new'], None, {}, {}, False) assert namespace == {"new": context.sheerka.new} # Sheerka methods are not set with pytest.raises(MethodAccessError): # new method is not allowed if expression_only is True service.create_namespace(context, ['new'], None, {}, {}, True) # ################### context.sheerka.add_to_short_term_memory("key", "short term memory value") namespace = service.create_namespace(context, ['key'], None, {}, {}, False) assert namespace == {"key": "short term memory value"} foo = define_new_concept(context, get_metadata("foo", body="1")) foo_token = Token(TokenKind.CONCEPT, ("foo", None), 0, 1, 1) namespace = service.create_namespace(context, ['foo'], None, {"foo": foo_token}, {}, False) assert context.sheerka.isinstance(namespace["foo"], foo) assert namespace["foo"].body == 1 # local namespace are evaluated namespace = service.create_namespace(context, ['foo'], None, {}, {"foo": foo}, False) assert context.sheerka.isinstance(namespace["foo"], foo) assert namespace["foo"].body is NotInit # global namespace are used as is namespace = service.create_namespace(context, ['foo'], None, {}, {}, False) assert context.sheerka.isinstance(namespace["foo"], foo) assert namespace["foo"].body == 1 # concept instantiation are evaluated def test_i_can_manage_concept_synonyms(self, context, service): foo, bar, baz = get_concepts(context, "foo", "bar", "baz", use_sheerka=False) # foo, bar and baz are supposed to be concept synonyms namespace = {"a": "value a", "foo": MultipleResults(foo, bar, baz), "b": "value b", "bar": MultipleResults(baz, bar)} res = service.manage_multiple_choices(namespace) assert len(res) == 6 assert res[0] == {"a": "value a", "b": "value b", "foo": foo, "bar": baz} assert res[1] == {"a": "value a", "b": "value b", "foo": foo, "bar": bar} assert res[2] == {"a": "value a", "b": "value b", "foo": bar, "bar": baz} assert res[3] == {"a": "value a", "b": "value b", "foo": bar, "bar": bar} assert res[4] == {"a": "value a", "b": "value b", "foo": baz, "bar": baz} assert res[5] == {"a": "value a", "b": "value b", "foo": baz, "bar": bar} def test_i_can_manage_namespaces_when_concepts_have_values(self, context, service): foo, bar, baz = get_concepts(context, "foo", "bar", "baz", use_sheerka=False) foo.set_value(ConceptDefaultProps.BODY, "foo value") foo.get_runtime_info().is_evaluated = True bar.set_value(ConceptDefaultProps.BODY, "bar value") bar.get_runtime_info().is_evaluated = True namespaces = [ {"a": "value a", "foo": foo, "baz": baz}, {"a": "value a", "foo": bar, "baz": baz}, ] res = service.manage_concepts_with_body(context, namespaces) assert len(res) == 4 assert res == [ {"a": "value a", "baz": baz, "foo": foo}, {"a": "value a", "baz": baz, "foo": "foo value"}, {"a": "value a", "baz": baz, "foo": bar}, {"a": "value a", "baz": baz, "foo": "bar value"}, ] def test_multiple_results_concept_only_return_concepts(self, context): foo, bar = get_concepts(context, "foo", "bar") assert MultipleResults(foo, "one", bar, 1).concepts_only() == MultipleResults(foo, bar) assert MultipleResults("one", 1).concepts_only() == MultipleResults() def test_i_can_add_multiple_results_of_multiple_results(self, context): foo, bar, baz, qux = get_concepts(context, "foo", "bar", "baz", "qux") m1 = MultipleResults(foo, bar) m2 = MultipleResults(bar, baz, m1) assert m2.items == [bar, baz, foo, bar]