Files
Sheerka/tests/services/test_SheerkaPython.py
T

341 lines
17 KiB
Python

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()