Files
Sheerka/tests/services/test_SheerkaPython.py
T

372 lines
19 KiB
Python

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, "<user input>", '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]