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 evaluators.PythonParser import PythonParser from helpers import _rv, define_new_concept, get_concepts, get_evaluated_concept, get_metadata from parsers.ParserInput import ParserInput from parsers.tokenizer import Token, TokenKind from services.SheerkaPython import EvalMethod, EvaluationContext, EvaluationRef, Expando, MultipleResults, 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": EvaluationRef("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_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_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_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()