Fixed #68: Implement SheerkaQL

Fixed #70: SheerkaFilterManager : Pipe functions
Fixed #71: SheerkaFilterManager : filter_objects
Fixed #75: SheerkaMemory: Enhance memory() to use the filtering capabilities
Fixed #76: SheerkaEvaluateConcept: Concepts that modify the state of the system must not be evaluated during question
This commit is contained in:
2021-04-26 19:13:47 +02:00
parent bef5f3208c
commit 1059ce25c5
57 changed files with 5759 additions and 1302 deletions
+47 -3
View File
@@ -12,9 +12,12 @@ class TestExecutionContext(TestUsingMemoryBasedSheerka):
def test_id_is_incremented_by_event_digest(self):
sheerka = self.get_sheerka()
a = ExecutionContext("foo", Event("event_1"), sheerka, BuiltinConcepts.NOP, None)
b = ExecutionContext("foo", Event("event_1"), sheerka, BuiltinConcepts.NOP, None)
c = ExecutionContext("foo", Event("event_2"), sheerka, BuiltinConcepts.NOP, None)
event1 = Event("event_1")
event2 = Event("event_2")
a = ExecutionContext("foo", event1, sheerka, BuiltinConcepts.NOP, None)
b = ExecutionContext("foo", event1, sheerka, BuiltinConcepts.NOP, None)
c = ExecutionContext("foo", event2, sheerka, BuiltinConcepts.NOP, None)
d = b.push(BuiltinConcepts.NOP, None)
e = c.push(BuiltinConcepts.NOP, None)
@@ -177,6 +180,47 @@ class TestExecutionContext(TestUsingMemoryBasedSheerka):
assert sub2.has_parent(root.id)
assert not sub1.has_parent(sub2.id)
assert not sub2.has_parent(sub1.id)
def test_i_can_reset_global_hints(self):
sheerka = self.get_sheerka()
context = ExecutionContext("foo", Event("event_1"), sheerka, BuiltinConcepts.NOP, None)
context.add_to_global_hints(BuiltinConcepts.TESTING)
context.add_to_global_hints(BuiltinConcepts.DEBUG)
sub_context1 = context.push(BuiltinConcepts.NOP, None)
assert BuiltinConcepts.TESTING in sub_context1.global_hints
assert BuiltinConcepts.DEBUG in sub_context1.global_hints
sub_context2 = context.push(BuiltinConcepts.NOP, None, reset_hints={BuiltinConcepts.TESTING})
assert BuiltinConcepts.TESTING not in sub_context2.global_hints
assert BuiltinConcepts.DEBUG in sub_context2.global_hints
sub_context3 = context.push(BuiltinConcepts.NOP,
None,
reset_hints={BuiltinConcepts.DEBUG, BuiltinConcepts.TESTING})
assert sub_context3.global_hints == set()
def test_i_can_reset_protected_hints(self):
sheerka = self.get_sheerka()
context = ExecutionContext("foo", Event("event_1"), sheerka, BuiltinConcepts.NOP, None)
context.add_to_protected_hints(BuiltinConcepts.TESTING)
context.add_to_protected_hints(BuiltinConcepts.DEBUG)
sub_context1 = context.push(BuiltinConcepts.NOP, None)
assert BuiltinConcepts.TESTING in sub_context1.protected_hints
assert BuiltinConcepts.DEBUG in sub_context1.protected_hints
sub_context2 = context.push(BuiltinConcepts.NOP, None, reset_hints={BuiltinConcepts.TESTING})
assert BuiltinConcepts.TESTING not in sub_context2.protected_hints
assert BuiltinConcepts.DEBUG in sub_context2.protected_hints
sub_context3 = context.push(BuiltinConcepts.NOP,
None,
reset_hints={BuiltinConcepts.DEBUG, BuiltinConcepts.TESTING})
assert sub_context3.protected_hints == set()
# def test_variables_are_passed_to_children_but_not_to_parents(self):
# sheerka = self.get_sheerka()
#
+2 -23
View File
@@ -2,7 +2,7 @@ import pytest
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept, ParserResultConcept
from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved, \
concept_part_value, DEFINITION_TYPE_DEF
DEFINITION_TYPE_DEF
from core.global_symbols import NotInit, NotFound
from core.sheerka.services.SheerkaEvaluateConcept import SheerkaEvaluateConcept
from core.sheerka.services.SheerkaMemory import SheerkaMemory
@@ -420,7 +420,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
concept = Concept("foo", body="a")
evaluated = sheerka.evaluate_concept(context, concept)
assert evaluated.key == concept.key
compare_with_test_object(evaluated.body, CB("a", "concept_a")) # this test was already done
compare_with_test_object(evaluated.body, CB("a", "concept_a")) # this test was already done
# so check this one.
concept = Concept("foo", body="a").def_var("a", "'property_a'")
@@ -784,27 +784,6 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
assert evaluated.key == command.key
assert sheerka.get_from_memory(context, "a").obj == 10
@pytest.mark.parametrize("metadata", [
ConceptParts.WHERE,
ConceptParts.PRE,
ConceptParts.POST,
ConceptParts.RET
])
def test_i_cannot_evaluate_python_statement_in_where_pre_post_ret(self, metadata, capsys):
sheerka, context, foo = self.init_concepts("foo")
setattr(foo.get_metadata(), concept_part_value(metadata), "a=10; print('10')")
foo.get_metadata().need_validation = True
evaluated = sheerka.evaluate_concept(context, foo, eval_body=True)
captured = capsys.readouterr()
assert sheerka.isinstance(evaluated, BuiltinConcepts.CONCEPT_EVAL_ERROR)
error = evaluated.body
assert sheerka.isinstance(error, BuiltinConcepts.PYTHON_SECURITY_ERROR)
assert error.prop == metadata
assert error.body == "a=10; print('10')"
assert captured.out == ""
def test_python_builtin_function_are_forbidden_in_where_pre_post_ret(self, capsys):
# I do the test only for PRE, as it will be the same for the other ConceptPart
sheerka, context, foo, bar = self.init_concepts(
+11 -10
View File
@@ -9,8 +9,9 @@ from core.sheerka.Sheerka import RECOGNIZED_BY_ID, RECOGNIZED_BY_NAME
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules, LOW_PRIORITY_RULES, DISABLED_RULES
from core.sheerka.services.SheerkaExecute import ParserInput
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, CompiledCondition
from evaluators.PythonEvaluator import PythonEvaluator, Expando
from evaluators.PythonEvaluator import PythonEvaluator
from parsers.PythonParser import PythonParser
from sheerkapython.python_wrapper import Expando
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -84,7 +85,7 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
# create fake compiled predicates
parser = PythonParser()
my_rule.compiled_conditions = [
CompiledCondition(PythonEvaluator.NAME, parser.parse(context, ParserInput(exp)), set(), set(), None)
CompiledCondition(PythonEvaluator.NAME, parser.parse(context, ParserInput(exp)), set(), set(), None, set())
for exp in predicates]
my_rule.metadata.is_compiled = True
my_rule.metadata.is_enabled = True
@@ -114,9 +115,9 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
assert res == {True: [r1, r3], False: [r2]}
@pytest.mark.parametrize("predicate", [
"greetings",
"c:|1001:",
"hello 'kodjo'"
"recognize(__ret.body, greetings)",
"recognize(__ret.body, c:|1001:)",
"recognize(__ret.body, hello 'kodjo')"
])
def test_i_can_evaluate_rules_when_concepts_are_not_questions(self, predicate):
"""
@@ -146,7 +147,7 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("there"),
create_new=True).with_format_rules(
Rule(predicate="hello there", action="")).unpack()
Rule(predicate="recognize(__ret.body, hello there)", action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
there_instance = sheerka.new_from_template(there, there.key)
@@ -169,7 +170,7 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("my friend"),
create_new=True).with_format_rules(
Rule(predicate="hello my friend", action="")).unpack()
Rule(predicate="recognize(__ret.body, hello my friend)", action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
my_friend_instance = sheerka.new_from_template(my_friend, my_friend.key)
@@ -183,7 +184,7 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
sheerka, context, greetings, rule = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True).with_format_rules(
Rule(predicate="hello sheerka", action="")).unpack()
Rule(predicate="recognize(__ret.body, hello sheerka)", action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
ret = sheerka.ret("evaluator", True, sheerka.new(greetings, a=Expando("sheerka", {})))
@@ -196,7 +197,7 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("greetings", definition="hi a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True).with_format_rules(
Rule(predicate="greetings", action="")).unpack()
Rule(predicate="recognize(__ret.body, greetings)", action="")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
ret1 = sheerka.ret("evaluator", True, sheerka.new(g1, a="kodjo"))
@@ -210,7 +211,7 @@ class TestSheerkaEvaluateRules(TestUsingMemoryBasedSheerka):
def test_i_can_evaluate_concept_rule_with_the_same_name_when_the_second_concept_is_declared_after(self):
sheerka, context, g1, rule, g2 = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
create_new=True).with_format_rules(Rule(predicate="greetings", action="")).with_concepts(
create_new=True).with_format_rules(Rule(predicate="recognize(__ret.body, greetings)", action="")).with_concepts(
Concept("greetings", definition="hi a", definition_type=DEFINITION_TYPE_DEF).def_var("a")).unpack()
service = sheerka.services[SheerkaEvaluateRules.NAME]
+41 -1
View File
@@ -87,7 +87,9 @@ class TestSheerkaMemory(TestUsingMemoryBasedSheerka):
foo = Concept("foo")
sheerka.add_to_memory(context, "a", foo)
assert sheerka.om.copy(SheerkaMemory.OBJECTS_ENTRY) == {"a": MemoryObject(context.event.get_digest(), foo)}
assert sheerka.om.copy(SheerkaMemory.OBJECTS_ENTRY) == {"a": MemoryObject(context.event.get_digest(),
context.event.date.timestamp(),
foo)}
assert id(sheerka.get_from_memory(context, "a").obj) == id(foo)
def test_i_can_use_memory_with_a_string(self):
@@ -106,6 +108,44 @@ class TestSheerkaMemory(TestUsingMemoryBasedSheerka):
assert sheerka.memory(context, Concept("foo")) == foo
def test_i_can_use_memory_with_a_query(self):
sheerka, context, foo, bar = self.init_concepts("foo", "bar")
sheerka.add_to_memory(context, "x", foo)
sheerka.add_to_memory(context, "y", bar)
assert sheerka.memory(context, "self.name == 'foo'") == foo
def test_i_retrieve_the_last_entry_when_requesting_memory_with_a_query(self):
sheerka, context, foo, bar, foo2 = self.init_concepts("foo", "bar", Concept("foo", body="2"))
sheerka.add_to_memory(context, "x", foo)
sheerka.add_to_memory(context, "y", bar)
context2 = self.get_context(sheerka) # timestamp is newer
sheerka.add_to_memory(context2, "z", foo2)
assert sheerka.memory(context, "self.name == 'foo'") == foo2
def test_i_can_look_in_the_previous_objects_when_using_query_to_request_the_memory(self):
sheerka, context, foo, bar, baz = self.init_concepts("foo", "bar", "baz")
sheerka.add_to_memory(context, "x", foo)
sheerka.add_to_memory(context, "y", bar)
sheerka.add_to_memory(context, "z", baz)
# another layer
sheerka.add_to_memory(context, "x", bar)
sheerka.add_to_memory(context, "y", baz)
# another layer
sheerka.add_to_memory(context, "x", baz)
# so under x there is [foo] -> [bar] -> [baz]
# so under y there is [bar] -> [baz]
# so under z there is [baz]
assert sheerka.memory(context, "self.name == 'foo'") == foo
def test_concept_not_found_is_return_when_not_found(self):
sheerka, context = self.init_test().unpack()
+205
View File
@@ -0,0 +1,205 @@
from dataclasses import dataclass
import pytest
from core.builtin_concepts_ids import BuiltinConcepts
from core.concept import Concept
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@dataclass
class A:
prop1: object
prop2: object
def __eq__(self, other):
if not isinstance(other, A):
return False
return self.prop1 == other.prop1 and self.prop2 == other.prop2
def __hash__(self):
hash_res = []
for p in [self.prop1, self.prop2]:
hash_res.append(0 if isinstance(p, (list, dict, set)) else p)
return hash(tuple(hash_res))
@dataclass
class B(A):
def as_bag(self):
return {
"fake_prop1": self.prop1,
"fake_prop2": self.prop2
}
def __hash__(self):
return hash((self.prop1, self.prop2))
class TestSheerkaQueryManager(TestUsingMemoryBasedSheerka):
def test_i_can_filter_objects_using_kwargs(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", 10), A("a21", 3.14), A({1, "v"}, 0xab), A([0, 1], {"key": "value"})]
assert sheerka.filter_objects(context, lst, prop1="a21") == [lst[1]]
assert sheerka.filter_objects(context, lst, prop2=10) == [lst[0]]
assert sheerka.filter_objects(context, lst, prop2=3.14) == [lst[1]]
assert sheerka.filter_objects(context, lst, prop2=0xab) == [lst[2]]
assert sheerka.filter_objects(context, lst, prop1=[0, 1]) == [lst[3]]
assert sheerka.filter_objects(context, lst, prop2={"key": "value"}) == [lst[3]]
# assert sheerka.filter_objects(context, lst, prop1={1, "v"}) == [lst[2]] set are not supported
def test_i_can_filter_by_object_type(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", "a12"), Concept("foo", body="a").auto_init(), Concept("foo", body="b").auto_init()]
assert sheerka.filter_objects(context, lst, __type="foo") == [lst[1], lst[2]]
def test_i_can_filter_on_atomic_def(self):
sheerka, context, isa, plus, isa2 = self.init_concepts(
Concept('x is a y').def_var("x").def_var("y"),
Concept('a plus b').def_var("a").def_var("b"),
Concept('u is a v').def_var("u").def_var("v"),
)
lst = [isa, plus, isa2]
assert sheerka.filter_objects(context, lst, atomic_def="is a") == [lst[0], lst[2]]
def test_i_can_filter_on_as_bag_property(self):
sheerka, context = self.init_test().unpack()
lst = [B("a11", "a12"), B("a21", "a22"), B("a31", "a32")]
assert sheerka.filter_objects(context, lst, fake_prop1="a21") == [lst[1]]
def test_i_can_filter_container(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", 10), A("a21", 3.14), A({1, "v"}, 0xab), A([0, 1], {"key": "value"})]
container = sheerka.new(BuiltinConcepts.EXPLANATION, body=lst, digest="xxx", command="text")
res = sheerka.filter_objects(context, container, prop1="a21")
assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION)
assert res.digest == "xxx"
assert res.command == "text"
assert res.body == [lst[1]]
def test_i_can_filter_when_property_does_not_exist(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", "a12"), B("a21", "a22"), B("a31", "a32")]
assert sheerka.filter_objects(context, lst, prop1="a11") == [lst[0]]
def test_i_can_filter_objets_using_predicate(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", 10),
A("a21", 3.14),
A({1, "v"}, 0xab),
A([0, 1], {"key": "value"}),
Concept("foo", body="a").auto_init(),
B("a21", "a22"),
Concept("foo", body="b").auto_init(),
B("a31", "a32")]
assert sheerka.filter_objects(context, lst, "self.prop1 == 'a21'") == [lst[1]]
assert sheerka.filter_objects(context, lst, "self.prop2 >= 1") == [lst[0], lst[1], lst[2]]
assert sheerka.filter_objects(context, lst, "get_type(self) == 'foo' ") == [lst[4], lst[6]]
assert sheerka.filter_objects(context, lst, "self.fake_prop1 == 'a21' ") == [lst[5]]
assert sheerka.filter_objects(context, lst, "hasattr(self, 'fake_prop1')") == [lst[5], lst[7]]
def test_i_can_filter_object_using_predicate_and_sheerka_objects(self):
sheerka, context, foo, bar = self.init_concepts("foo", "bar")
lst = [foo, bar, A("a21", 3.14)]
assert sheerka.filter_objects(context, lst, "self == bar") == [lst[1]]
def test_i_can_filter_objects_using_concept(self):
sheerka, context, foo, bar, isa = self.init_concepts(
"foo",
"bar",
Concept("x is a concept", body="isinstance(x, Concept)", pre="is_question()").def_var("x"),
create_new=True)
lst = [foo, A("a21", 3.14), bar, B("a21", 3.14)]
assert sheerka.filter_objects(context, lst, "self is a concept") == [foo, bar]
def test_i_can_filter_objects_when_no_kwargs_and_no_predicate(self):
sheerka, context, foo, bar = self.init_concepts("foo", "bar")
lst = [foo, bar, A("a21", 3.14)]
assert sheerka.filter_objects(context, lst) == lst
def test_i_must_select_object_property_using_string(self):
sheerka, context = self.init_test().unpack()
with pytest.raises(SyntaxError):
sheerka.select_objects(context, [], 00)
def test_i_can_select_objects_with_args(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", 10), A("a21", 3.14), A({1, "v"}, 0xab), A([0, 1], {"key": "value"})]
assert sheerka.select_objects(context, lst, "prop1", "prop2") == (
('a11', 10),
('a21', 3.14),
({1, 'v'}, 171),
([0, 1], {'key': 'value'}))
def test_i_can_select_objects_when_container(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", 10), A("a21", 3.14), A({1, "v"}, 0xab), A([0, 1], {"key": "value"})]
container = sheerka.new(BuiltinConcepts.EXPLANATION, body=lst, digest="xxx", command="text")
res = sheerka.select_objects(context, container, "prop1", "prop2")
assert sheerka.isinstance(res, BuiltinConcepts.EXPLANATION)
assert res.digest == "xxx"
assert res.command == "text"
assert res.body == (('a11', 10),
('a21', 3.14),
({1, 'v'}, 171),
([0, 1], {'key': 'value'}))
def test_i_can_select_objects_with_complicated_request(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", 10), A("a21", 3.14), A({1, "v"}, 0xab)]
assert sheerka.select_objects(context, lst, "self.prop2 + 5") == (15, 8.14, 0xab + 5)
assert sheerka.select_objects(context, lst, "isinstance(self.prop1, str)") == (True, True, False)
def test_error_when_collecting_returns_are_managed(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", 10), A("a21", {"key": "value"})]
res = sheerka.select_objects(context, lst, "self.prop2 + 5")
assert len(res) == 2
assert res[0] == 15
assert isinstance(res[1], TypeError)
def test_i_can_select_objects_using_kwargs(self):
sheerka, context = self.init_test().unpack()
lst = [A("a11", 10), A("a21", 3.14), A({1, "v"}, 0xab), A([0, 1], {"key": "value"})]
assert sheerka.select_objects(context, lst, p1="prop1", p2="prop2") == (
{"p1": "a11", "p2": 10},
{"p1": "a21", "p2": 3.14},
{"p1": {1, "v"}, "p2": 0xab},
{"p1": [0, 1], "p2": {"key": "value"}},
)
def test_i_can_collect_attributes(self):
sheerka = self.get_sheerka()
lst = [A("", ""),
B("", ""),
Concept("foo").def_var("a").auto_init(),
Concept("bar").def_var("y").def_var("x").auto_init()]
res = sheerka.collect_attributes(lst)
assert sheerka.isinstance(res, BuiltinConcepts.TO_DICT)
assert res.body == {
"A": ["prop1", "prop2"],
"B": ["fake_prop1", "fake_prop2"],
"foo": ["a", 'body', 'id', 'key', 'name'],
"bar": ['body', 'id', 'key', 'name', "x", "y"] # attributes are sorted
}
+12 -741
View File
@@ -1,5 +1,3 @@
import ast
import pytest
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
@@ -7,26 +5,20 @@ from core.concept import Concept, DEFINITION_TYPE_DEF
from core.global_symbols import RULE_COMPARISON_CONTEXT, NotFound, EVENT_RULE_DELETED
from core.rule import Rule, ACTION_TYPE_PRINT, ACTION_TYPE_EXEC
from core.sheerka.Sheerka import RECOGNIZED_BY_ID, RECOGNIZED_BY_NAME
from core.sheerka.services.SheerkaEvaluateRules import SheerkaEvaluateRules
from core.sheerka.services.SheerkaExecute import ParserInput
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, FormatRuleActionParser, \
FormatAstRawText, FormatAstVariable, FormatAstSequence, FormatAstFunction, \
FormatRuleSyntaxError, FormatAstList, UnexpectedEof, FormatAstColor, FormatAstDict, \
FormatAstMulti, \
PythonCodeEmitter, FormatAstNode, ReteConditionExprVisitor, PythonConditionExprVisitor, \
CompiledCondition
PythonCodeEmitter, FormatAstNode, ReteConditionExprVisitor
from core.tokenizer import Token, TokenKind
from evaluators.PythonEvaluator import Expando
from parsers.BaseParser import ErrorSink
from parsers.ExpressionParser import ExpressionParser
from parsers.PythonParser import PythonNode
from sheerkarete.common import V
from sheerkarete.conditions import Condition, AndConditions, FilterCondition
from sheerkarete.conditions import FilterCondition
from sheerkarete.network import ReteNetwork
from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.parsers.parsers_utils import get_rete_conditions, NEGCOND, \
NCCOND
from tests.parsers.parsers_utils import get_rete_conditions, NEGCOND, NCCOND
seq = FormatAstSequence
raw = FormatAstRawText
@@ -188,7 +180,7 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
rule = service.init_rule(context, rule)
assert len(rule.error_sink["when"]) > 0
assert sheerka.is_error(rule.error_sink["when"][0])
assert sheerka.has_error(context, rule.error_sink["when"][0])
assert "print" not in rule.error_sink
assert "then" not in rule.error_sink
assert rule.metadata.is_compiled
@@ -210,7 +202,7 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
other_action_type = ACTION_TYPE_PRINT if action_type == ACTION_TYPE_EXEC else ACTION_TYPE_EXEC
assert sheerka.is_error(rule.error_sink[action_type])
assert sheerka.has_error(context, rule.error_sink[action_type])
assert other_action_type not in rule.error_sink
assert "when" not in rule.error_sink
assert rule.metadata.is_compiled
@@ -282,30 +274,6 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
assert parser.error_sink == expected_error
@pytest.mark.parametrize("text, compiled_text", [
("a == 5", "a == 5"),
("foo > 5", "foo > 5"),
("func() == 5", "func() == 5"),
("not a == 5", "not (a == 5)"),
("not foo > 5", "not (foo > 5)"),
("not func() == 5", "not (func() == 5)"),
])
def test_i_can_compile_predicate_when_pure_python(self, text, compiled_text):
sheerka, context, *concepts = self.init_concepts("foo")
service = sheerka.services[SheerkaRuleManager.NAME]
ast_ = ast.parse(compiled_text, "<source>", 'eval')
expected_python_node = PythonNode(compiled_text, ast_)
compilation_result = service.compile_when(context, "test", text)
res = compilation_result.python_conditions
assert len(res) == 1
assert isinstance(res[0], CompiledCondition)
assert res[0].evaluator_type == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(res[0].return_value, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(res[0].return_value) == expected_python_node
assert res[0].concept is None
def test_i_can_get_rule_priorities(self):
sheerka, context, rule_true, rule_false = self.init_test().with_format_rules(("True", "True"),
("False", "False")).unpack()
@@ -499,20 +467,6 @@ isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
res = sheerka.get_exec_rules()
assert res == [r2, r3, r1]
@pytest.mark.skip
def test_i_can_compile_rete_using_name(self):
sheerka, context, *concepts = self.init_test().unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
text = "__ret"
compilation_result = service.compile_when(context, "test", text)
res = compilation_result.rete_disjunctions
assert len(res) == 1
assert isinstance(res[0], AndConditions)
assert res[0].conditions == [Condition(V("__x_00__"), "__name__", "__ret")]
def test_i_can_properly_copy_a_rule(self):
sheerka, context = self.init_test().unpack()
service = sheerka.services[SheerkaRuleManager.NAME]
@@ -525,98 +479,6 @@ isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
for k, v in vars(rule).items():
assert getattr(clone, k) == getattr(rule, k)
@pytest.mark.parametrize("expression, expected_as_str, expected_variables", [
(
"__ret",
["#__x_00__|__name__|'__ret'"],
{"__ret"}
),
(
"__ret.status == True",
["#__x_00__|__name__|'__ret'", "#__x_00__|status|True"],
{"__ret"}
),
(
"__ret.status",
["#__x_00__|__name__|'__ret.status'"],
{"__ret.status"}
),
(
"body",
["#__x_00__|__name__|'body'"],
{"body"}
),
(
"__ret and __ret.status",
["#__x_00__|__name__|'__ret'", "#__x_01__|__name__|'__ret.status'"],
{"__ret", "__ret.status"}
),
])
def test_i_can_get_rete_conditions(self, expression, expected_as_str, expected_variables):
sheerka, context = self.init_test().unpack()
parser = ExpressionParser()
expected = get_rete_conditions(*expected_as_str)
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = ReteConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert conditions == [expected]
# check against a Rete network
network = ReteNetwork()
rule = Rule("test", expression, None)
rule.metadata.id = 9999
rule.metadata.is_compiled = True
rule.metadata.is_enabled = True
rule.rete_disjunctions = conditions
network.add_rule(rule)
return_value = ReturnValueConcept("Test", True, None)
if "__ret" in expected_variables:
network.add_obj("__ret", return_value)
if "__ret.status" in expected_variables:
network.add_obj("__ret.status", return_value.status)
if "body" in expected_variables:
network.add_obj("body", return_value.body)
matches = list(network.matches)
assert len(matches) == 1
def test_i_can_get_rete_conditions_when_no_attribute(self):
sheerka, context = self.init_test().unpack()
expression = "a == 10"
expected_as_str = ["#__x_00__|__name__|'a'", "#__x_00__|__self__|10"]
parser = ExpressionParser()
expected = get_rete_conditions(*expected_as_str)
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = ReteConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert conditions == [expected]
# check against a Rete network
network = ReteNetwork()
rule = Rule("test", expression, None)
rule.metadata.id = 9999
rule.metadata.is_compiled = True
rule.metadata.is_enabled = True
rule.rete_disjunctions = conditions
network.add_rule(rule)
network.add_obj("a", 10)
matches = list(network.matches)
assert len(matches) == 1
@pytest.mark.skip("No ready yet for SheerkaFilterCondition")
def test_i_can_get_rete_conditions_when_function(self):
sheerka, context, greetings = self.init_test().with_concepts(
@@ -652,235 +514,6 @@ isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
# matches = list(network.matches)
# assert len(matches) == 1
@pytest.mark.parametrize("test_name, expression, variable_name, expected_as_str", [
(
"recognize by name",
"recognize(__ret.body, greetings)",
None,
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|name|'greetings'"]
),
(
"recognize by id",
"recognize(__ret.body, c:|1001:)",
None,
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|id|'1001'"]
),
(
"recognize by name using c_str",
"recognize(__ret.body, c:greetings:)",
None,
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|name|'greetings'"]
),
(
"recognize by name and add other conditions (str)",
"recognize(__ret.body, greetings) and __ret.body.a == 'my friend'",
"my friend",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|name|'greetings'",
"#__x_01__|a|'my friend'"]
),
(
"recognize by name and add other conditions (sheerka)",
"recognize(__ret.body, greetings) and __ret.body.a == sheerka",
"sheerka",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|name|'greetings'",
"#__x_01__|a|'__sheerka__'"]
),
(
"recognize by name and add other conditions (concept)",
"recognize(__ret.body, greetings) and __ret.body.a == foo",
"foo",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|name|'greetings'",
"#__x_01__|a|#__x_02__",
"#__x_02__|__is_concept__|True",
"#__x_02__|key|'foo'"]
),
(
"recognize by instance",
"recognize(__ret.body, hello sheerka)",
"sheerka",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|key|'hello __var__0'",
"#__x_01__|a|'__sheerka__'"]
),
(
"recognize by instance",
"recognize(__ret.body, hello 'my friend')",
"my friend",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|key|'hello __var__0'",
"#__x_01__|a|'my friend'"]
),
(
"recognize by instance",
"recognize(__ret.body, hello foo)",
"foo",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|key|'hello __var__0'",
"#__x_01__|a|#__x_02__",
"#__x_02__|__is_concept__|True",
"#__x_02__|key|'foo'",
]
),
(
"recognize by instance when long concept",
"recognize(__ret.body, hello my best friend)",
"my best friend",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|key|'hello __var__0'",
"#__x_01__|a|#__x_02__",
"#__x_02__|__is_concept__|True",
"#__x_02__|key|'my best friend'",
]
),
])
def test_i_can_get_rete_using_recognize_function(self, test_name, expression, variable_name, expected_as_str):
sheerka, context, greetings, foo, my_best_friend = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("foo"),
Concept("my best friend"),
create_new=True
).unpack()
parser = ExpressionParser()
expected = get_rete_conditions(*expected_as_str)
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = ReteConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert conditions == [expected]
# check against a Rete network
network = ReteNetwork()
rule = Rule("test", expression, None)
rule.metadata.id = 9999
rule.metadata.is_compiled = True
rule.metadata.is_enabled = True
rule.rete_disjunctions = conditions
network.add_rule(rule)
variable_map = {
"foo": foo,
"my best friend": my_best_friend,
"sheerka": sheerka
}
variable = variable_map.get(variable_name, variable_name)
to_recognize = sheerka.new_from_template(greetings, greetings.key, a=variable)
network.add_obj("__ret", ReturnValueConcept("Test", True, to_recognize))
matches = list(network.matches)
assert len(matches) == 1
@pytest.mark.parametrize("expression, variable_name, expected_as_str", [
(
"greetings",
None,
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|name|'greetings'"]
),
(
"c:|1001:",
None,
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|id|'1001'"]
),
(
"hello foo",
"foo",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|key|'hello __var__0'",
"#__x_01__|a|#__x_02__",
"#__x_02__|__is_concept__|True",
"#__x_02__|key|'foo'",
]
),
(
"hello my best friend",
"my best friend",
["#__x_00__|__name__|'__ret'",
"#__x_00__|body|#__x_01__",
"#__x_01__|__is_concept__|True",
"#__x_01__|key|'hello __var__0'",
"#__x_01__|a|#__x_02__",
"#__x_02__|__is_concept__|True",
"#__x_02__|key|'my best friend'",
]
),
])
def test_i_can_get_rete_when_a_concept_is_recognized(self, expression, variable_name, expected_as_str):
sheerka, context, greetings, foo, my_best_friend = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("foo"),
Concept("my best friend"),
create_new=True
).unpack()
parser = ExpressionParser()
expected = get_rete_conditions(*expected_as_str)
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = ReteConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert conditions == [expected]
# check against a Rete network
network = ReteNetwork()
rule = Rule("test", expression, None)
rule.metadata.id = 9999
rule.metadata.is_compiled = True
rule.metadata.is_enabled = True
rule.rete_disjunctions = conditions
network.add_rule(rule)
variable_map = {
"foo": foo,
"my best friend": my_best_friend,
"sheerka": sheerka
}
variable = variable_map.get(variable_name, variable_name)
to_recognize = sheerka.new_from_template(greetings, greetings.key, a=variable)
network.add_obj("__ret", ReturnValueConcept("Test", True, to_recognize))
matches = list(network.matches)
assert len(matches) == 1
@pytest.mark.parametrize("expression, bag_key, expected", [
("not __ret", "__other", [NEGCOND("#__x_00__|__name__|'__ret'")]),
("not not __ret", "__ret", ["#__x_00__|__name__|'__ret'"]),
@@ -894,6 +527,10 @@ isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
"#__x_01__|key|'hello __var__0'",
"#__x_01__|a|'__sheerka__'"])]),
("__ret and not __error", "__ret", ["#__x_00__|__name__|'__ret'", NEGCOND("#__x_01__|__name__|'__error'")]),
("not recognize(self, hello sheerka)", "__ret", ["#__x_00__|__name__|'self'",
NCCOND(["#__x_00__|__is_concept__|True",
"#__x_00__|key|'hello __var__0'",
"#__x_00__|a|'__sheerka__'"])]),
])
def test_i_can_get_rete_using_not(self, expression, bag_key, expected):
sheerka, context, greetings, foo = self.init_test().with_concepts(
@@ -914,7 +551,7 @@ isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
assert conditions == [expected_conditions]
@pytest.mark.skip("I am not sure yet of what I want to get")
@pytest.mark.parametrize("expression, expected_as_str", [
@pytest.mark.parametrize("expression, expected_as_list_of_str", [
(
"eval(__ret.body, 'foo' starts with 'f')",
["#__x_00__|__name__|'__ret'",
@@ -926,7 +563,7 @@ isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
"$eval(__x_01__, a, b, c)"]
),
])
def test_i_can_get_rete_conditions_using_eval_function(self, expression, expected_as_str):
def test_i_can_get_rete_conditions_using_eval_function(self, expression, expected_as_list_of_str):
sheerka, context, start_with = self.init_test().with_concepts(
Concept("x starts with y",
pre="is_question",
@@ -935,7 +572,7 @@ isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
).unpack()
parser = ExpressionParser()
expected = get_rete_conditions(*expected_as_str)
expected = get_rete_conditions(*expected_as_list_of_str)
error_sink = ErrorSink()
parser_input = ParserInput(expression)
@@ -961,372 +598,6 @@ isinstance(var, Concept) and var.key == 'hello __var__0'""" + \
matches = list(network.matches)
assert len(matches) == 1
@pytest.mark.parametrize("expression, expected_compiled, expected_variables", [
("__ret", None, {"__ret"}),
("__ret.status == True", "__ret.status == True", {"__ret"}),
("__ret.status", None, {"__ret.status"}),
("body", None, {"body"}),
("__ret and __ret.status", None, {"__ret", "__ret.status"})
])
def test_i_can_get_compiled_conditions(self, expression, expected_compiled, expected_variables):
sheerka, context = self.init_test().unpack()
parser = ExpressionParser()
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = PythonConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert len(conditions) == 1
assert isinstance(conditions[0], CompiledCondition)
if expected_compiled:
ast_ = ast.parse(expected_compiled, "<source>", 'eval')
expected_python_node = PythonNode(expected_compiled, ast_)
assert conditions[0].evaluator_type == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(conditions[0].return_value, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(conditions[0].return_value) == expected_python_node
else:
assert conditions[0].evaluator_type is None
assert conditions[0].return_value is None
assert conditions[0].concept is None
assert conditions[0].variables == expected_variables
# check against SheerkaEvaluateRules
evaluate_rules_service = sheerka.services[SheerkaEvaluateRules.NAME]
return_value = ReturnValueConcept("Test", True, None)
bag = {}
if "__ret" in expected_variables:
bag["__ret"] = return_value
if "__ret.status" in expected_variables:
bag["__ret.status"] = return_value.status
if "body" in expected_variables:
bag["body"] = return_value.body
with context.push(BuiltinConcepts.RULES_EVALUATION, bag, desc="Evaluating rules...") as sub_context:
sub_context.sheerka.add_many_to_short_term_memory(sub_context, bag)
rule = Rule(name="test_i_can_get_compiled_conditions", predicate=expression)
rule.compiled_conditions = conditions
res = evaluate_rules_service.evaluate_rule(sub_context, rule, bag)
assert res.status
assert self.sheerka.is_success(self.sheerka.objvalue(res))
def test_i_can_get_compiled_conditions_when_no_attribute(self):
sheerka, context = self.init_test().unpack()
expression = "a == 10"
expected_compiled = "a == 10"
parser = ExpressionParser()
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = PythonConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert len(conditions) == 1
assert isinstance(conditions[0], CompiledCondition)
ast_ = ast.parse(expected_compiled, "<source>", 'eval')
expected_python_node = PythonNode(expected_compiled, ast_)
assert conditions[0].evaluator_type == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(conditions[0].return_value, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(conditions[0].return_value) == expected_python_node
assert conditions[0].concept is None
assert conditions[0].variables == {"a"}
# check against SheerkaEvaluateRules
evaluate_rules_service = sheerka.services[SheerkaEvaluateRules.NAME]
bag = {"a": 10}
with context.push(BuiltinConcepts.RULES_EVALUATION, bag, desc="Evaluating rules...") as sub_context:
sub_context.sheerka.add_many_to_short_term_memory(sub_context, bag)
rule = Rule(name="test_i_can_get_compiled_conditions", predicate=expression)
rule.compiled_conditions = conditions
res = evaluate_rules_service.evaluate_rule(sub_context, rule, bag)
assert res.status
assert self.sheerka.is_success(self.sheerka.objvalue(res))
def test_i_can_get_compiled_conditions_when_function(self):
sheerka, context, greetings = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
).unpack()
expression = "isinstance(a, greetings)"
expected_compiled = "isinstance(a, greetings)"
parser = ExpressionParser()
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = PythonConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert len(conditions) == 1
assert isinstance(conditions[0], CompiledCondition)
ast_ = ast.parse(expected_compiled, "<source>", 'eval')
expected_python_node = PythonNode(expected_compiled, ast_)
assert conditions[0].evaluator_type == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(conditions[0].return_value, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(conditions[0].return_value) == expected_python_node
assert conditions[0].concept is None
assert conditions[0].variables == {"a"}
# check against SheerkaEvaluateRules
evaluate_rules_service = sheerka.services[SheerkaEvaluateRules.NAME]
bag = {"a": sheerka.new(greetings, a="my friend")}
with context.push(BuiltinConcepts.RULES_EVALUATION, bag, desc="Evaluating rules...") as sub_context:
sub_context.sheerka.add_many_to_short_term_memory(sub_context, bag)
rule = Rule(name="test_i_can_get_compiled_conditions", predicate=expression)
rule.compiled_conditions = conditions
res = evaluate_rules_service.evaluate_rule(sub_context, rule, bag)
assert res.status
assert self.sheerka.is_success(self.sheerka.objvalue(res))
@pytest.mark.parametrize("expression, variable_name, expected_compiled", [
(
"recognize(__ret.body, greetings)",
None,
"__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings'"
),
(
"recognize(__ret.body, c:|1001:)",
None,
"__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.id == '1001'"
),
(
"recognize(__ret.body, c:greetings:)",
None,
"__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings'"
),
(
"recognize(__ret.body, greetings) and __ret.body.a == 'my friend'",
"my friend",
"__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and __x_00__.a == 'my friend'"
),
(
"recognize(__ret.body, greetings) and __ret.body.a == sheerka",
"sheerka",
"""__x_00__ = __ret.body
isinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and is_sheerka(__x_00__.a)"""
),
(
"recognize(__ret.body, greetings) and __ret.body.a == foo",
"foo",
"""__x_00__ = __ret.body
__x_01__ = __x_00__.a
isinstance(__x_00__, Concept) and __x_00__.name == 'greetings' and isinstance(__x_01__, Concept) and __x_01__.key == 'foo'"""
),
(
"recognize(__ret.body, hello sheerka)",
"sheerka",
"""__x_00__ = __ret.body
isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and is_sheerka(__x_00__.a)"""
),
(
"recognize(__ret.body, hello 'my friend')",
"my friend",
"""__x_00__ = __ret.body
isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and __x_00__.a == 'my friend'"""
),
(
"recognize(__ret.body, hello foo)",
"foo",
"""__x_00__ = __ret.body
__x_01__ = __x_00__.a
isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and isinstance(__x_01__, Concept) and __x_01__.key == 'foo'"""
),
(
"recognize(__ret.body, hello my best friend)",
"my best friend",
"""__x_00__ = __ret.body
__x_01__ = __x_00__.a
isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and isinstance(__x_01__, Concept) and __x_01__.key == 'my best friend'"""
),
])
def test_i_can_get_compiled_using_recognize_function(self, expression, variable_name, expected_compiled):
sheerka, context, greetings, foo, my_best_friend = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("foo"),
Concept("my best friend"),
create_new=True
).unpack()
parser = ExpressionParser()
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = PythonConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert len(conditions) == 1
assert isinstance(conditions[0], CompiledCondition)
if expected_compiled:
ast_ = ast.parse(expected_compiled, "<source>", 'exec')
expected_python_node = PythonNode(expected_compiled, ast_, expected_compiled)
assert conditions[0].evaluator_type == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(conditions[0].return_value, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(conditions[0].return_value) == expected_python_node
else:
assert conditions[0].evaluator_type is None
assert conditions[0].return_value is None
assert conditions[0].concept is None
assert conditions[0].variables == {"__ret"}
# check against SheerkaEvaluateRules
evaluate_rules_service = sheerka.services[SheerkaEvaluateRules.NAME]
variable_map = {
"foo": foo,
"my best friend": my_best_friend,
"sheerka": Expando("sheerka", {})
}
variable = variable_map.get(variable_name, variable_name)
to_recognize = sheerka.new_from_template(greetings, greetings.key, a=variable)
bag = {"__ret": ReturnValueConcept("Test", True, to_recognize)}
with context.push(BuiltinConcepts.RULES_EVALUATION, bag, desc="Evaluating rules...") as sub_context:
sub_context.sheerka.add_many_to_short_term_memory(sub_context, bag)
rule = Rule(name="test_i_can_get_compiled_using_recognize_function", predicate=expression)
rule.compiled_conditions = conditions
res = evaluate_rules_service.evaluate_rule(sub_context, rule, bag)
assert res.status
assert self.sheerka.is_success(self.sheerka.objvalue(res))
@pytest.mark.parametrize("expression, variable_name, expected_compiled", [
(
"greetings",
None,
"__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.name == 'greetings'"
),
(
"c:|1001:",
None,
"__x_00__ = __ret.body\nisinstance(__x_00__, Concept) and __x_00__.id == '1001'"
),
(
"hello foo",
"foo",
"""__x_00__ = __ret.body
__x_01__ = __x_00__.a
isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and isinstance(__x_01__, Concept) and __x_01__.key == 'foo'"""
),
(
"hello my best friend",
"my best friend",
"""__x_00__ = __ret.body
__x_01__ = __x_00__.a
isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and isinstance(__x_01__, Concept) and __x_01__.key == 'my best friend'"""
),
])
def test_i_can_get_compiled_when_a_concept_is_recognized(self, expression, variable_name, expected_compiled):
sheerka, context, greetings, foo, my_best_friend = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("foo"),
Concept("my best friend"),
create_new=True
).unpack()
parser = ExpressionParser()
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = PythonConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert len(conditions) == 1
assert isinstance(conditions[0], CompiledCondition)
if expected_compiled:
ast_ = ast.parse(expected_compiled, "<source>", 'exec')
expected_python_node = PythonNode(expected_compiled, ast_, expected_compiled)
assert conditions[0].evaluator_type == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(conditions[0].return_value, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(conditions[0].return_value) == expected_python_node
else:
assert conditions[0].evaluator_type is None
assert conditions[0].return_value is None
assert conditions[0].concept is None
assert conditions[0].variables == {"__ret"}
# check against SheerkaEvaluateRules
evaluate_rules_service = sheerka.services[SheerkaEvaluateRules.NAME]
variable_map = {
"foo": foo,
"my best friend": my_best_friend,
"sheerka": Expando("sheerka", {})
}
variable = variable_map.get(variable_name, variable_name)
to_recognize = sheerka.new_from_template(greetings, greetings.key, a=variable)
bag = {"__ret": ReturnValueConcept("Test", True, to_recognize)}
with context.push(BuiltinConcepts.RULES_EVALUATION, bag, desc="Evaluating rules...") as sub_context:
sub_context.sheerka.add_many_to_short_term_memory(sub_context, bag)
rule = Rule(name="test_i_can_get_compiled_using_recognize_function", predicate=expression)
rule.compiled_conditions = conditions
res = evaluate_rules_service.evaluate_rule(sub_context, rule, bag)
assert res.status
assert self.sheerka.is_success(self.sheerka.objvalue(res))
@pytest.mark.parametrize("expression, expected_compiled, variables, not_variables", [
("not __ret", None, set(), {"__ret"}),
("not not __ret", None, {"__ret"}, set()),
("not __ret.status == True", "not (__ret.status == True)", {"__ret"}, set()),
("not __ret.status", None, set(), {"__ret.status"},),
("__ret and not __ret.status", None, {"__ret"}, {"__ret.status"}),
("not recognize(__ret.body, hello sheerka)", """__x_00__ = __ret.body
not (isinstance(__x_00__, Concept) and __x_00__.key == 'hello __var__0' and is_sheerka(__x_00__.a))""", {"__ret"},
set()),
("__ret and not __error", None, {"__ret"}, {"__error"}),
])
def test_i_can_get_compiled_condition_using_not(self, expression, expected_compiled, variables, not_variables):
sheerka, context, greetings, foo = self.init_test().with_concepts(
Concept("greetings", definition="hello a", definition_type=DEFINITION_TYPE_DEF).def_var("a"),
Concept("foo"),
).unpack()
parser = ExpressionParser()
error_sink = ErrorSink()
parser_input = ParserInput(expression)
parser.reset_parser_input(parser_input, error_sink)
parsed = parser.parse_input(context, parser_input, error_sink)
visitor = PythonConditionExprVisitor(context)
conditions = visitor.get_conditions(parsed)
assert len(conditions) == 1
assert isinstance(conditions[0], CompiledCondition)
if expected_compiled:
if "\n" in expected_compiled:
ast_ = ast.parse(expected_compiled, "<source>", 'exec')
else:
ast_ = ast.parse(expected_compiled, "<source>", 'eval')
expected_python_node = PythonNode(expected_compiled, ast_)
assert conditions[0].evaluator_type == PYTHON_EVALUATOR_NAME
assert sheerka.isinstance(conditions[0].return_value, BuiltinConcepts.RETURN_VALUE)
assert sheerka.objvalue(conditions[0].return_value) == expected_python_node
else:
assert conditions[0].evaluator_type is None
assert conditions[0].return_value is None
assert conditions[0].variables == variables
assert conditions[0].not_variables == not_variables
assert conditions[0].concept is None
# check against SheerkaEvaluateRules
evaluate_rules_service = sheerka.services[SheerkaEvaluateRules.NAME]
ret_val_key = "__ret" if "__ret" in conditions[0].variables else "__other"
bag = {ret_val_key: ReturnValueConcept("Test", False, None)}
with context.push(BuiltinConcepts.RULES_EVALUATION, bag, desc="Evaluating rules...") as sub_context:
sub_context.sheerka.add_many_to_short_term_memory(sub_context, bag)
rule = Rule(name="test_i_can_get_compiled_conditions", predicate=expression)
rule.compiled_conditions = conditions
res = evaluate_rules_service.evaluate_rule(sub_context, rule, bag)
assert res.status
assert self.sheerka.is_success(self.sheerka.objvalue(res))
class TestSheerkaRuleManagerUsingFileBasedSheerka(TestUsingFileBasedSheerka):
def test_rules_are_initialized_at_startup(self):
File diff suppressed because it is too large Load Diff
+11
View File
@@ -182,6 +182,17 @@ class TestBuiltinHelpers(TestUsingMemoryBasedSheerka):
concept = Concept("foo", pre=pre)
assert core.builtin_helpers.is_a_question(context, concept) == expected
def test_context_hints_are_reset_when_call_evaluate_from_source(self):
sheerka, context, one = self.init_concepts(Concept("one", body="1"))
context.add_to_global_hints(BuiltinConcepts.EVAL_BODY_REQUESTED)
context.add_to_protected_hints(BuiltinConcepts.EVAL_BODY_REQUESTED)
context.add_to_private_hints(BuiltinConcepts.EVAL_BODY_REQUESTED)
res = core.builtin_helpers.evaluate_from_source(context, "one", eval_body=False)
evaluated = [r for r in res if r.status][0].body
assert evaluated.body is NotInit
# @pytest.mark.parametrize("return_values", [
# None,
# []
+12 -12
View File
@@ -439,27 +439,27 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
def test_i_can_get_error_for_simple_objects(self, obj, expected):
sheerka, context = self.init_test().unpack()
assert sheerka.get_errors(obj) == expected
assert sheerka.get_errors(context, obj) == expected
def test_i_can_get_error_when_builtin_concept_in_error(self):
sheerka, context = self.init_test().unpack()
obj = sheerka.new(BuiltinConcepts.ONTOLOGY_ALREADY_DEFINED)
assert sheerka.get_errors(obj) == [obj]
assert sheerka.get_errors(context, obj) == [obj]
def test_i_can_get_error_when_return_value(self):
sheerka, context = self.init_test().unpack()
error = sheerka.err("an error")
ret_val = ReturnValueConcept("Test", False, sheerka.err("an error"))
assert sheerka.get_errors(ret_val) == [error]
assert sheerka.get_errors(context, ret_val) == [error]
def test_i_can_get_inner_error(self):
sheerka, context = self.init_test().unpack()
error = sheerka.err("an error")
ret_val = ReturnValueConcept("Test", False, sheerka.err("an error"))
assert sheerka.get_errors(ret_val) == [error]
assert sheerka.get_errors(context, ret_val) == [error]
def test_i_can_get_error_when_embedded_errors(self):
sheerka, context = self.init_test().unpack()
@@ -470,7 +470,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
error = sheerka.err([concept_eval_error, unknown_concept, not_an_error])
ret_val = ReturnValueConcept("Test", False, error)
errors_found = sheerka.get_errors(ret_val)
errors_found = sheerka.get_errors(context, ret_val)
assert errors_found == [error, concept_eval_error, unknown_concept]
@@ -488,7 +488,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
multiple_error = sheerka.new(BuiltinConcepts.MULTIPLE_ERRORS, body=[python_error, value_not_found])
ret_val_2 = ReturnValueConcept("Test", False, multiple_error)
errors_found = sheerka.get_errors([ret_val_1, ret_val_2])
errors_found = sheerka.get_errors(context, [ret_val_1, ret_val_2])
assert errors_found == [error, concept_eval_error, unknown_concept,
multiple_error, python_error, value_not_found]
@@ -502,7 +502,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
error = sheerka.err([concept_eval_error, unknown_concept, python_error])
ret_val = ReturnValueConcept("Test", False, error)
errors_found = sheerka.get_errors(ret_val, __type=BuiltinConcepts.CONCEPT_EVAL_ERROR)
errors_found = sheerka.get_errors(context, ret_val, __type=BuiltinConcepts.CONCEPT_EVAL_ERROR)
assert errors_found == [concept_eval_error]
def test_i_can_filter_error_by_class_name(self):
@@ -514,7 +514,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
error = sheerka.err([concept_eval_error, unknown_concept, python_error])
ret_val = ReturnValueConcept("Test", False, error)
errors_found = sheerka.get_errors(ret_val, __type="PythonErrorNode")
errors_found = sheerka.get_errors(context, ret_val, __type="PythonErrorNode")
assert errors_found == [python_error]
def test_i_can_filter_error_by_concept_attribute(self):
@@ -526,7 +526,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
error = sheerka.err([concept_eval_error, unknown_concept, python_error])
ret_val = ReturnValueConcept("Test", False, error)
errors_found = sheerka.get_errors(ret_val, concept_ref="a_concept_ref")
errors_found = sheerka.get_errors(context, ret_val, concept_ref="a_concept_ref")
assert errors_found == [unknown_concept]
def test_i_can_filter_error_by_class_attribute(self):
@@ -538,7 +538,7 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
error = sheerka.err([concept_eval_error, unknown_concept, python_error])
ret_val = ReturnValueConcept("Test", False, error)
errors_found = sheerka.get_errors(ret_val, source="error source")
errors_found = sheerka.get_errors(context, ret_val, source="error source")
assert errors_found == [python_error]
def test_i_can_filter_error_on_multiple_criteria(self):
@@ -550,14 +550,14 @@ class TestSheerkaUsingMemoryBasedSheerka(TestUsingMemoryBasedSheerka):
error = sheerka.err([concept_eval_error, unknown_concept, value_not_found])
ret_val = ReturnValueConcept("Test", False, error)
errors_found = sheerka.get_errors(ret_val, __type="ValueNotFound", item="an_item", value="a value")
errors_found = sheerka.get_errors(context, ret_val, __type="ValueNotFound", item="an_item", value="a value")
assert errors_found == [value_not_found]
def test_i_cannot_get_error_when_return_value_s_status_is_true(self):
sheerka, context = self.init_test().unpack()
ret_val = ReturnValueConcept("Test", True, sheerka.err("an error"))
assert sheerka.get_errors(ret_val) == []
assert sheerka.get_errors(context, ret_val) == []
class TestSheerkaUsingFileBasedSheerka(TestUsingFileBasedSheerka):
+25
View File
@@ -469,3 +469,28 @@ def test_tokens_are_matching_when_eof_differs():
tokens2 = Tokenizer(expression2, yield_eof=False)
assert core.utils.tokens_are_matching(tokens1, tokens2)
def test_sheerka_hasattr_get_attr():
class A:
def __init__(self, property_value):
self.property_value = property_value
def as_bag(self):
return {"prop": self.property_value}
# test object with bag
a = A("foo")
assert core.utils.sheerka_hasattr(a, "prop")
assert core.utils.sheerka_getattr(a, "prop") == "foo"
assert not core.utils.sheerka_hasattr(a, "property_value")
with pytest.raises(AttributeError):
core.utils.sheerka_getattr(a, "property_value")
# test for concept
concept = Concept("foo").def_var("a", "value").auto_init()
assert core.utils.sheerka_hasattr(concept, "a")
assert core.utils.sheerka_getattr(concept, "a") == "value"
assert not core.utils.sheerka_hasattr(concept, "b")
with pytest.raises(AttributeError):
core.utils.sheerka_getattr(concept, "b")
+12 -1
View File
@@ -2,13 +2,14 @@ import ast
import pytest
from core.ast_helpers import NamesWithAttributesVisitor
from core.builtin_concepts import ReturnValueConcept, ParserResultConcept, BuiltinConcepts
from core.builtin_helpers import CreateObjectIdentifiers
from core.concept import Concept
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 evaluators.PythonEvaluator import PythonEvaluator, PythonEvalError
from parsers.BaseNodeParser import SourceCodeNode, SourceCodeWithConceptNode
from parsers.FunctionParser import FunctionParser
from parsers.PythonParser import PythonNode, PythonParser
@@ -386,6 +387,16 @@ class TestPythonEvaluator(TestUsingMemoryBasedSheerka):
assert evaluated.status
assert evaluated.value == context.sheerka.get_rule_by_id(str(value)).name
def test_i_cannot_call_methods_with_side_effect_when_is_question_is_set(self):
sheerka, context, foo, bar = self.init_concepts("foo", "bar")
context.add_to_protected_hints(BuiltinConcepts.EVAL_QUESTION_REQUESTED)
parsed = PythonParser().parse(context, ParserInput("set_isa(foo, bar)"))
python_evaluator = PythonEvaluator()
evaluated = python_evaluator.eval(context, parsed)
assert sheerka.has_error(context, evaluated, __type="MethodAccessError")
def test_something(self):
def func(**kwargs):
for k, v in kwargs.items():
+57 -3
View File
@@ -7,6 +7,7 @@ from core.sheerka.services.SheerkaConceptManager import SheerkaConceptManager
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
from evaluators.OneSuccessEvaluator import OneSuccessEvaluator
from evaluators.PythonEvaluator import PythonEvalError
from sheerkapython.python_wrapper import MethodAccessError
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
from tests.parsers.parsers_utils import CMV, CC, compare_with_test_object, CB
@@ -642,7 +643,7 @@ as:
assert res[0].body == 21
def test_i_can_use_where_in_bnf(self):
sheerka = self.get_sheerka()
sheerka, context = self.init_test().unpack()
init = [
"def concept one as 1",
@@ -683,12 +684,12 @@ as:
assert len(res) == 1
assert not res[0].status
assert sheerka.isinstance(res[0].body, BuiltinConcepts.MULTIPLE_ERRORS)
assert str(BuiltinConcepts.CONDITION_FAILED) in [error.key for error in sheerka.get_errors(res[0].body.body)]
assert sheerka.has_error(context, res, __type=BuiltinConcepts.CONDITION_FAILED)
res = sheerka.evaluate_user_input("eval twenty three")
assert len(res) == 1
assert not res[0].status
assert str(BuiltinConcepts.CONDITION_FAILED) in [error.key for error in sheerka.get_errors(res[0].body.body)]
assert sheerka.has_error(context, res, __type=BuiltinConcepts.CONDITION_FAILED)
def test_i_can_manage_some_type_of_infinite_recursion(self):
sheerka = self.get_sheerka()
@@ -1298,6 +1299,7 @@ as:
assert sheerka.objvalue(res[0].body.get_value("qty")) == 2
def test_i_can_implement_the_concept_and(self):
# Normally, redefining and leads to a circular ref between the concept and the python
init = [
"def concept x and y as x and y",
"set_is_lesser(__PRECEDENCE, c:x and y:, 'Sya')",
@@ -1308,3 +1310,55 @@ as:
assert len(res) == 1
assert res[0].status
def test_i_can_use_result_from_memory_filtering(self):
init = [
"def concept female",
"def concept girl",
"set_isa(girl, female)",
"def concept she ret memory('isa(self, female)')",
"girl"
]
sheerka = self.init_scenario(init)
context = self.get_context(sheerka)
res = sheerka.evaluate_user_input("set_attr(she, 'my_attr', 'my value')")
assert len(res) == 1
assert res[0].status
girl_from_memory = sheerka.get_last_from_memory(context, "girl")
assert girl_from_memory.obj.get_value("my_attr") == "my value"
def test_i_can_use_result_from_memory_filtering_within_other_concept(self):
init = [
"def concept female",
"def concept girl",
"set_isa(girl, female)",
"def concept she ret memory('isa(self, female)')",
"def concept x attribute y equals z as set_attr(x, y, z)",
"girl"
]
sheerka = self.init_scenario(init)
context = self.get_context(sheerka)
res = sheerka.evaluate_user_input("eval she attribute 'my_attr' equals 'my value'")
assert len(res) == 1
assert res[0].status
girl_from_memory = sheerka.get_last_from_memory(context, "girl")
assert girl_from_memory.obj.get_value("my_attr") == "my value"
def test_i_cannot_use_method_that_alter_the_global_state_within_question(self):
init = [
"def concept foo as question(set_debug(True))",
]
sheerka = self.init_scenario(init)
context = self.get_context(sheerka)
res = sheerka.evaluate_user_input("question(set_debug(True))")
assert sheerka.has_error(context, res, __type="MethodAccessError")
res = sheerka.evaluate_user_input("eval foo")
assert sheerka.has_error(context, res, __type="MethodAccessError")
@@ -0,0 +1,85 @@
from core.builtin_concepts_ids import BuiltinConcepts
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestSheerkaNonRegPipeFunctions(TestUsingMemoryBasedSheerka):
def test_i_can_filter_a_list_using_pipe(self):
init = [
"def concept one as 1",
"def concept two as 2",
"def concept three as 3",
"add_to_memory('x', [one, two, three])"
]
sheerka = self.init_scenario(init)
res = sheerka.evaluate_user_input("x | where(body=2)")
assert len(res) == 1
assert res[0].status
assert isinstance(res[0].body, list)
assert len(res[0].body) == 1
assert sheerka.isinstance(res[0].body[0], "two")
res = sheerka.evaluate_user_input("x | where(__self=two)")
assert len(res) == 1
assert res[0].status
assert isinstance(res[0].body, list)
assert len(res[0].body) == 1
assert sheerka.isinstance(res[0].body[0], "two")
def test_i_can_filter_using_sheerka_methods(self):
init = [
"def concept one as 1",
"def concept number",
"set_isa(one, number)",
"add_to_memory('x', [one])"
]
sheerka = self.init_scenario(init)
res = sheerka.evaluate_user_input("x | where('isa(self, number)')")
assert len(res) == 1
assert res[0].status
assert isinstance(res[0].body, list)
assert len(res[0].body) == 1
assert sheerka.isinstance(res[0].body[0], "one")
def test_i_can_select_properties(self):
init = [
"def concept one as 1",
"def concept two as 2",
"def concept three as 3",
"add_to_memory('x', [one, two, three])"
]
sheerka = self.init_scenario(init)
res = sheerka.evaluate_user_input("x | select('id', 'name')")
assert len(res) == 1
assert res[0].status
assert res[0].body == (("1001", "one"), ("1002", "two"), ("1003", "three"))
res = sheerka.evaluate_user_input("x | select(p1='id', p2='name')")
assert len(res) == 1
assert res[0].status
assert res[0].body == ({"p1": "1001", "p2": "one"},
{"p1": "1002", "p2": "two"},
{"p1": "1003", "p2": "three"})
def test_i_can_collect_properties(self):
init = [
"def concept one as 1",
"def concept isa from x is a y def_var x def_var y",
"def concept plus from a plus b as a + b",
"add_to_memory('x', [one, isa, plus])"
]
sheerka = self.init_scenario(init)
res = sheerka.evaluate_user_input("x | props()")
assert len(res) == 1
assert res[0].status
assert sheerka.isinstance(res[0].body, BuiltinConcepts.TO_DICT)
assert res[0].body.body == {'isa': ['body', 'id', 'key', 'name', 'x', 'y'],
'one': ['body', 'id', 'key', 'name'],
'plus': ['a', 'b', 'body', 'id', 'key', 'name']}
+1 -1
View File
@@ -5,7 +5,7 @@ from evaluators.PythonEvaluator import PythonEvalError
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestSheerkaNonRegDisplay(TestUsingMemoryBasedSheerka):
class TestSheerkaNonRegRules(TestUsingMemoryBasedSheerka):
@pytest.mark.skip
def test_i_can_apply_simple_rule(self):
+10 -5
View File
@@ -16,6 +16,7 @@ from parsers.BaseNodeParser import UnrecognizedTokensNode, SourceCodeNode, RuleN
from parsers.FunctionParser import FunctionNode
from parsers.PythonParser import PythonNode
from parsers.SyaNodeParser import SyaConceptParserHelper
from sheerkapython.python_wrapper import sheerka_globals
from sheerkarete.common import V
from sheerkarete.conditions import Condition, AndConditions, NegatedCondition, NegatedConjunctiveConditions
@@ -934,11 +935,18 @@ class FN:
@dataclass()
class NEGCOND:
"""
Represents a NegatedCondition
"""
condition: str
@dataclass()
class NCCOND:
"""
Represents a NegatedConjunctiveConditions
"""
conditions: List[str]
@@ -1319,11 +1327,8 @@ def get_rete_conditions(*conditions):
def get_value(obj):
if obj.startswith("#"):
return V(obj[1:])
if obj.startswith("'"):
return obj[1:-1]
if obj in ("True", "False"):
return obj == "True"
return int(obj)
return eval(obj, sheerka_globals)
res = []
for cond in conditions:
+2 -2
View File
@@ -671,7 +671,7 @@ from give me the date !
assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.ERROR)
assert sheerka.has_error(res, __type="SyntaxErrorNode", message="Empty 'auto_eval' declaration.")
assert sheerka.has_error(context, res, __type="SyntaxErrorNode", message="Empty 'auto_eval' declaration.")
def test_i_cannot_parse_when_wrong_auto_eval_value(self):
sheerka, context, parser, *concepts = self.init_parser()
@@ -709,4 +709,4 @@ from give me the date !
assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.ERROR)
assert sheerka.has_error(res, __type="SyntaxErrorNode", message="Empty 'def_var' declaration.")
assert sheerka.has_error(context, res, __type="SyntaxErrorNode", message="Empty 'def_var' declaration.")
View File
+169
View File
@@ -0,0 +1,169 @@
import pytest
from core.builtin_concepts_ids import BuiltinConcepts
from core.concept import Concept
from core.global_symbols import SyaAssociativity
from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.services.SheerkaAdmin import SheerkaAdmin
from sheerkapython.python_wrapper import Expando, create_namespace, inject_context, get_sheerka_method, Pipe, \
MethodAccessError
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestPythonWrapper(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("name, expected", [
("Concept", Concept),
("BuiltinConcepts", BuiltinConcepts),
("Expando", Expando),
("ExecutionContext", ExecutionContext),
("SyaAssociativity", SyaAssociativity),
])
def test_i_can_create_namespace_from_internal_references(self, name, expected):
context = self.get_context()
assert create_namespace(context, "TestPythonWrapper", [name], None, {}, False) == {name: expected}
def test_i_can_create_namespace_with_context_method(self):
context = self.get_context()
res = create_namespace(context, "TestPythonWrapper", ["in_context", "isinstance"], None, {}, False)
assert res["in_context"] == context.in_context
assert res["isinstance"] == context.sheerka.services[SheerkaAdmin.NAME].extended_isinstance
def test_i_can_create_namespace_when_sheerka_expando_object(self):
sheerka, context = self.init_test().unpack()
res = create_namespace(context, "TestPythonWrapper", ["sheerka"], set(), {}, False)
assert res["sheerka"] == Expando("sheerka", {})
res = create_namespace(context, "TestPythonWrapper", ["sheerka"], {"test", "set_debug"}, {}, False)
assert isinstance(res["sheerka"], Expando)
assert res["sheerka"].get_name() == "sheerka"
assert set(vars(res["sheerka"])) == {"_Expando__name", "test", "set_debug"}
def test_i_can_create_namespace_when_short_term_memory(self):
context = self.get_context()
context.add_to_short_term_memory("my_key", "my_value")
assert create_namespace(context, "TestPythonWrapper", ["my_key"], None, {}, False) == {"my_key": "my_value"}
def test_i_can_create_namespace_when_value_from_memory(self):
sheerka, context = self.init_test().unpack()
sheerka.add_to_memory(context, "my_key", "my_value")
assert create_namespace(context, "TestPythonWrapper", ["my_key"], None, {}, False) == {"my_key": "my_value"}
def test_i_can_create_namespace_when_sheerka_methods(self):
sheerka, context = self.init_test().unpack()
res = create_namespace(context, "TestPythonWrapper", ["test", "set_debug"], None, {}, False)
assert res["test"] == sheerka.test
assert isinstance(res["set_debug"], type(inject_context))
assert res["set_debug"].__name__ == "set_debug"
def test_i_can_create_namespace_when_value_from_context_obj(self):
sheerka, context = self.init_test().unpack()
obj = Concept("foo").def_var("a", "value1").auto_init()
context.obj = obj
res = create_namespace(context, "TestPythonWrapper", ["self", "a"], None, {}, False)
assert res == {"self": obj, "a": "value1"}
def test_i_can_create_namespace_when_value_from_local_objects(self):
sheerka, context = self.init_test().unpack()
obj = Concept("foo")
objects = {"self": obj, "a": Concept("bar")}
res = create_namespace(context, "TestPythonWrapper", ["self", "a"], None, objects, False)
assert res == {"self": obj, "a": objects["a"]}
def test_i_can_create_namespace_when_name_refers_to_a_concept(self):
sheerka, context, foo = self.init_concepts("foo")
assert create_namespace(context, "TestPythonWrapper", ["foo"], None, {}, False) == {"foo": foo}
def test_internal_references_and_context_method_take_over_short_term_memory(self):
context = self.get_context()
context.add_to_short_term_memory("Concept", "short_term_value")
context.add_to_short_term_memory("isinstance", "short_term_value")
context.add_to_short_term_memory("in_context", "short_term_value")
res = create_namespace(context, "TestPythonWrapper", ["Concept", "isinstance", "in_context"], None, {}, False)
assert res == {
"Concept": Concept,
"isinstance": context.sheerka.services[SheerkaAdmin.NAME].extended_isinstance,
"in_context": context.in_context,
}
def test_short_term_memory_takes_precedence_over_long_term_memory(self):
sheerka, context = self.init_test().unpack()
context.add_to_short_term_memory("my_key", "short_term")
sheerka.add_to_memory(context, "my_key", "long_term")
assert create_namespace(context, "TestPythonWrapper", ["my_key"], None, {}, False) == {"my_key": "short_term"}
def test_long_term_memory_takes_precedence_over_sheerka_methods(self):
# I am not really sure why
sheerka, context = self.init_test().unpack()
sheerka.add_to_memory(context, "test", "from memory")
assert create_namespace(context, "TestPythonWrapper", ["test"], None, {}, False) == {"test": "from memory"}
def test_sheerka_method_takes_precedence_over_context_obj(self):
sheerka, context = self.init_test().unpack()
obj = Concept("foo").def_var("test", "value1").auto_init()
context.obj = obj
assert create_namespace(context, "TestPythonWrapper", ["test", ], None, {}, False) == {"test": sheerka.test}
def test_context_obj_takes_precedence_over_local_objects(self):
sheerka, context = self.init_test().unpack()
obj = Concept("foo").def_var("a", "value1").auto_init()
context.obj = obj
objects = {"self": Concept("bar"), "a": Concept("bar")}
res = create_namespace(context, "TestPythonWrapper", ["self", "a"], None, objects, False)
assert res == {"self": obj, "a": "value1"}
def test_local_objects_take_precedence_over_object_instantiation(self):
sheerka, context, foo = self.init_concepts("from instantiation")
objects = {"foo": Concept("from local")}
res = create_namespace(context, "TestPythonWrapper", ["foo"], None, objects, False)
assert res == {"foo": objects["foo"]}
def test_i_can_get_sheerka_method(self):
context = self.get_context()
# sheerka direct method
assert get_sheerka_method(context, "TestPythonWrapper", "test", True) == context.sheerka.test
# sheerka indirect method
assert get_sheerka_method(context, "TestPythonWrapper", "get_value", True) == context.sheerka.get_value
# method that need context are wrapped
res = get_sheerka_method(context, "TestPythonWrapper", "test_using_context", True)
assert res != context.sheerka.test_using_context
assert type(res) == type(inject_context)
assert res.__name__ == "test_using_context"
# return None when the method is not found
assert get_sheerka_method(context, "TestPythonWrapper", "xxx", True) is None
def test_i_cannot_get_method_that_modifies_the_state_when_expression_only(self):
sheerka, context = self.init_test().unpack()
assert get_sheerka_method(context, "TestPythonWrapper", "set_debug", expression_only=False) is not None
with pytest.raises(MethodAccessError) as ex:
get_sheerka_method(context, "TestPythonWrapper", "set_debug", expression_only=True)
assert ex.value.method_name == "set_debug"
def test_i_can_get_method_when_pipe_function(self):
context = self.get_context()
res = get_sheerka_method(context, "TestPythonWrapper", "where", True)
assert isinstance(res, Pipe)
View File
@@ -0,0 +1,570 @@
from dataclasses import dataclass
import pytest
from sheerkaql.SheerkaQueryLangage import SheerkaQueryLanguage
from sheerkaql.symbols import flwr_sequence, attribute_value
def oset(x):
return x
class A(object):
def __init__(self, q):
self.q = q
def __repr__(self):
return f"A({vars(self)})"
def __eq__(self, other):
if not isinstance(other, A):
return False
return self.q == other.q
def __hash__(self):
return hash(str(self.q))
@dataclass
class BagClass:
property1: object
property2: object
def as_bag(self):
return {
"prop1": self.property1,
"prop2": self.property2,
}
execute = SheerkaQueryLanguage().execute
class TestSheerkaQueryLanguage:
def test_i_can_get_the_root_of_a_query(self):
hello = 'hello world!'
q = SheerkaQueryLanguage().compile('hello')
assert q(locals()) == oset([hello])
def test_i_can_traverse_object(self):
a = A(A(A("hello world!")))
q = SheerkaQueryLanguage().compile("a.q.q.q")
assert q(locals()) == oset(["hello world!"])
def test_i_can_traverse_list(self):
lst = [A("one"), A("two"), A("three")]
a = A(lst)
assert execute("a.q.q", {"a": a}) == oset(["one", "two", "three"])
def test_i_can_traverse_list_of_list(self):
sub_lst_number = [A("1"), A("2"), A("2")]
sub_lst_letter = [A("a"), A("b"), A("c")]
lst = [A("one"), A(sub_lst_number), A(sub_lst_letter)]
a = A(lst)
res = execute("a.q.q", {"a": a})
assert res == oset(["one", *sub_lst_number, *sub_lst_letter])
def test_i_can_traverse_object_when_where_condition_is_a_boolean(self):
a = A(A(A("hello world!")))
b_true = A(A(True))
b_false = A(A(False))
namespace = {"a": a, "hasattr": hasattr, "func": lambda x: x, "b_true": b_true, "b_false": b_false}
assert execute("a.q[1 == 1].q.q", namespace) == oset(["hello world!"])
assert execute("a.q[hasattr(self, 'q')].q.q", namespace) == oset(["hello world!"])
assert execute("a.q[1 == 2].q.q", namespace) == oset([])
assert execute("a.q[hasattr(self, 'x')].q.q", namespace) == oset([])
assert execute("a.q[True].q.q", namespace, allow_builtins=True) == oset(["hello world!"])
assert execute("a.q[False].q.q", namespace, allow_builtins=True) == oset([])
assert execute("a.q[func(True)].q.q", namespace) == oset(["hello world!"])
assert execute("a.q[func(False)].q.q", namespace) == oset([])
assert execute("a.q[b_true.q.q].q.q", namespace) == oset(["hello world!"])
assert execute("a.q[b_false.q.q].q.q", namespace) == oset([])
def test_i_can_request_by_list_index(self):
lst = [A("one"), A("two"), A("three")]
a = A(lst)
assert execute("a.q[1]", {"a": a}) == oset([lst[1]])
with pytest.raises(IndexError):
execute("a.q[99]", {"a": a})
with pytest.raises(TypeError):
execute("a.q['key']", {"a": a})
def test_i_can_request_by_dictionary_key(self):
lst = {"key1": "value1", "key2": "value2"}
a = A(lst)
assert execute("a.q['key1']", {"a": a}) == oset([lst["key1"]])
with pytest.raises(KeyError):
execute("a.q['key3']", {"a": a})
def test_i_can_filter(self):
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert execute('l[self < 5]', locals()) == [0, 1, 2, 3, 4]
def test_i_cannot_traverse_object_when_where_condition_is_not_a_boolean(self):
a = A("hello world!")
namespace = {"a": a, "func": lambda x: x, "dictionary": {"key": "value"}, "list": ["value"]}
with pytest.raises(TypeError):
execute("a.q[1].q.q", namespace)
with pytest.raises(TypeError):
execute("a.q[func(3)].q.q", namespace)
with pytest.raises(TypeError):
execute("a.q[dictionary['key']].q.q", namespace)
with pytest.raises(TypeError):
execute("a.q[list[0]].q.q", namespace)
def test_i_can_traverse_object_when_as_bag_is_defined(self):
a = BagClass(BagClass("sub_value1", BagClass("sub_sub_value1", "sub_sub_value1")), "value2")
assert execute("a.prop1.prop2.prop1", {"a": a}) == oset(["sub_sub_value1"])
def test_i_can_traverse_objects_when_where_condition_uses_as_bag(self):
hash_map = {"sub_sub_value1": "hello world!"}
b = BagClass(BagClass("sub_value1", BagClass("sub_sub_value1", "sub_sub_value1")), "value2")
a = A(hash_map)
assert execute("a.q[b.prop1.prop2.prop1]", {"a": a, "b": b}) == oset(["hello world!"])
def test_i_can_compute_set_operations(self):
a = [1, 2, 3, 3]
b = [2, 4, 3, 4]
assert execute("a | b", locals()) == [1, 2, 3, 4]
assert execute("a - b", locals()) == [1]
assert execute("b - a", locals()) == [4]
assert execute("a & b", locals()) == [2, 3]
def test_can_execute_set_expression(self):
a = [1, 2, 3, 3]
b = [2, 4, 3, 4]
c = [1, 2]
assert execute("return 1 if 1 in a else 0", locals()) == (1,)
assert execute("return 1 if 1 not in a else 0", locals()) == (0,)
assert execute("return 1 if 5 in a else 0", locals()) == (0,)
assert execute("return 1 if 5 not in a else 0", locals()) == (1,)
assert execute("return 1 if <c> subset <a> else 0", locals()) == (1,)
assert execute("return 1 if <c> superset <a> else 0", locals()) == (0,)
assert execute("return 1 if <a> subset <c> else 0", locals()) == (0,)
assert execute("return 1 if <a> superset <c> else 0", locals()) == (1,)
l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert execute('l - l[self < 5]', locals()) == (5, 6, 7, 8, 9)
def test_i_can_traverse_a_complex_path(self):
answer = 'o.x.y'
o = A('top')
o.x = [A('asdf'), A('123')]
o.x[0].y = A(answer)
d = {'hasattr': hasattr, 'o': o}
assert execute('o/x[hasattr(self,"y")]/y/q', d) == oset([answer])
assert execute('o/x', d) == oset(o.x)
def test_i_can_execute_comparison_in_where_clauses(self):
a = A(5)
assert execute('a[self.q == 5]', locals()) == oset([a])
assert execute('a[self.q != 5]', locals()) == oset([])
assert execute('a[self.q >= 5]', locals()) == oset([a])
assert execute('a[self.q <= 5]', locals()) == oset([a])
assert execute('a[self.q > 5]', locals()) == oset([])
assert execute('a[self.q < 5]', locals()) == oset([])
assert execute('a[self.q == 7]', locals()) == oset([])
assert execute('a[self.q != 7]', locals()) == oset([a])
assert execute('a[self.q >= 7]', locals()) == oset([])
assert execute('a[self.q <= 7]', locals()) == oset([a])
assert execute('a[self.q > 7]', locals()) == oset([])
assert execute('a[self.q < 7]', locals()) == oset([a])
assert execute('a[self.q == 3]', locals()) == oset([])
assert execute('a[self.q != 3]', locals()) == oset([a])
assert execute('a[self.q >= 3]', locals()) == oset([a])
assert execute('a[self.q <= 3]', locals()) == oset([])
assert execute('a[self.q > 3]', locals()) == oset([a])
assert execute('a[self.q < 3]', locals()) == oset([])
def test_i_can_execute_boolean_expression_in_where_clauses(self):
a = 'hello'
true = True
false = False
assert execute('a[true]', locals()) == oset([a])
assert execute('a[false]', locals()) == oset([])
assert execute('a[not true]', locals()) == oset([])
assert execute('a[not false]', locals()) == oset([a])
assert execute('a[true and true]', locals()) == oset([a])
assert execute('a[false and true]', locals()) == oset([])
assert execute('a[not true and true]', locals()) == oset([])
assert execute('a[not false and true]', locals()) == oset([a])
assert execute('a[true or false]', locals()) == oset([a])
assert execute('a[true or true]', locals()) == oset([a])
assert execute('a[false or true]', locals()) == oset([a])
assert execute('a[false or false]', locals()) == oset([])
assert execute('a[not true or true]', locals()) == oset([a])
assert execute('a[not false or false]', locals()) == oset([a])
assert execute('a[true and true and true and true]', locals()) == oset([a])
assert execute('a[true and true and true and false]', locals()) == oset([])
assert execute('a[true and (false or true)]', locals()) == oset([a])
assert execute('a[true and (false and true)]', locals()) == oset([])
assert execute('a[true and (true and true)]', locals()) == oset([a])
assert execute('a[true and (true and (not true or false))]', locals()) == oset([])
def test_i_can_execute_comparison_in_where_clauses_using_builtin_functions(self):
a = 'hello'
true = True
false = False
d = locals()
assert execute('a[1 == 1]', d) == oset([a])
assert execute('a[-1 == -1]', d) == oset([a])
assert execute('a[2.2 == 2.2]', d) == oset([a])
assert execute('a[2.2 == float("2.2")]', d, True) == oset([a])
assert execute('a[2 == int(2.2)]', d, True) == oset([a])
assert execute('a["hello" == a]', d, True) == oset([a])
assert execute('a["HELLO" == a.upper()]', d, True) == oset([a])
def test_i_can_use_function_in_where_clauses(self):
a = 'hello'
def f():
return 'hello'
def g(x, y, z):
return x + y + z
def h(f, x):
return f(x)
def i(x):
return x ** 2
def j(f):
return f
true = True
false = False
d = locals()
assert execute('a[f() == "hello"]', d) == oset([a])
assert execute('a[g(1,2,3) == 6]', d) == oset([a])
assert execute('a[h(i,3) == 9]', d) == oset([a])
assert execute('a[i(j(j)(j)(j)(h)(i,3)) == 81]', d) == oset([a])
with pytest.raises(TypeError):
execute('a[f()]', d)
def test_i_can_use_lists_in_where_clauses(self):
a = 'hello'
l = [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, 8]]]
d = locals()
assert execute('a[l[0] == 1]', d) == oset([a])
assert execute('a[l[1] == 2]', d) == oset([a])
assert execute('a[l[7][0] == 1]', d) == oset([a])
assert execute('a[l[7][1] == 2]', d) == oset([a])
assert execute('a[l[7][7][0] == 1]', d) == oset([a])
assert execute('a[l[7][7][1] == 2]', d) == oset([a])
assert execute('a[l[7][7][7] == 8]', d) == oset([a])
def test_i_can_use_dicts_in_where_clauses(self):
a = 'hello'
l = {"one": 1, "two": 2, "next": {"one": 1, "two": 2, "next": {"one": 1, "two": 2}}}
d = locals()
assert execute('a[l["one"] == 1]', d) == oset([a])
assert execute('a[l["two"] == 2]', d) == oset([a])
assert execute('a[l["next"]["one"] == 1]', d) == oset([a])
assert execute('a[l["next"]["two"] == 2]', d) == oset([a])
assert execute('a[l["next"]["next"]["one"] == 1]', d) == oset([a])
assert execute('a[l["next"]["next"]["two"] == 2]', d) == oset([a])
def test_i_can_use_callable_in_where_clauses(self):
a = 'hello'
def f():
return 'hello'
def g(x, y, z):
return x + y + z
def h(f, x):
return f(x)
def i(x):
return x ** 2
def j(f):
return f
m = {"one": 1, "two": 2, "next": [1, 2, 3, 4, 5, 6, 7, j]}
d = locals()
assert execute('a[m["next"][7](j)(m["next"][7])(m["next"])[7](i)(m["two"]) == 4]', d) == oset([a])
def test_i_can_execute_flwr_expression(self):
def f():
return 1, 2, 3
d = locals()
assert execute('for x in f() return x', d) == (1, 2, 3)
assert execute('for x in f() let y = f() return x, y', d) == ((1, (1, 2, 3)), (2, (1, 2, 3)), (3, (1, 2, 3)))
def test_i_can_execute_flwr_with_order_by(self):
def f():
return [1, 3, 2]
d = locals()
with pytest.raises(SyntaxError):
execute('for x in f() order by "asdf" asc return x', d)
with pytest.raises(SyntaxError):
execute('for x in f() order by 0 asc return "asdf":x', d)
assert execute('for x in f() order by 0 asc return x', d) == (1, 2, 3)
assert execute('for x in f() order by 0 desc return x', d) == (3, 2, 1)
def test_i_can_execute_flwr_with_user_defined_functions(self):
a = 'hello'
l = [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, 8]]]
d = locals()
assert execute('''
for i in l
let f = function() { 125 }
return f()
''', d) == (125, 125, 125, 125, 125, 125, 125, 125)
assert execute('''
for i in l
let f = function(q) {
for _ in <a>
where isinstance(q, list)
return {
for j in q
return f(j)
}
}
return f(i)
''', d, True) == ((), (), (), (), (), (), (),
(((), (), (), (), (), (), (),
(((), (), (), (), (), (), (), ()),)),))
def test_i_can_execute_flwr_with_if_expression(self):
a = 'hello'
l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
q = True
d = locals()
assert execute('''
for x in <a> return if (q) then 1 else 0
''', d) == (1,)
assert execute('''
for x in <l> return if (x % 2 == 0) then 1 else 0
''', d) == (0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
assert execute('''
for x in <l> return if x % 2 == 0 then 1 else 0
''', d) == (0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
assert execute('''
for x in <l> return 1 if x % 2 == 0 else 0
''', d) == (0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
assert execute('''
for x in <l> return 1 if (x % 2 == 0) else 0
''', d) == (0, 1, 0, 1, 0, 1, 0, 1, 0, 1)
assert execute('''
for x in <a> return if (True or X) then 1 else 0
''', d, True) == (1,)
def test_if_short_circuit(self):
a = 'hello'
l = [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, 8]]]
d = locals()
assert execute('''
for x in <a> return if (True or X) then 1 else 0
''', d, True) == (1,)
assert execute('''
for x in <a> return if (False and false.x) then 1 else 0
''', d, True) == (0,)
def test_i_can_flatten_result(self):
a = 'hello'
l = [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, 8]]]
d = locals()
assert execute('''
for x in l return flatten x
''', d, True) == (1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8)
def test_flattened_return(self):
a = 'hello'
l = [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, 8]]]
d = locals()
assert execute('''
for i in l
let f = function(l) {
if (isinstance(l, list))
then {for j in l return f(j)}
else l
}
return flatten f(i)''', d, True) == (1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8)
assert execute('''
for i in l
let f = function(l) {
if (isinstance(l, list))
then {for j in l return f(j)}
else {a:l}
}
return flatten f(i)
''', d, True) == tuple({a: i} for i in (1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8))
def test_i_can_return_none(self):
a = 'hello'
l = [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, 8]]]
d = locals()
assert execute('for x in l return None', d, True) == (None, None, None, None, None, None, None, None)
def test_i_can_return_a_class(self):
l = [1, 2, 3]
d = {"A": A, "l": l}
assert execute('for x in l return A(x)', d, True) == (A(1), A(2), A(3))
def test_i_can_execute_flwr_when_there_is_no_for_statement(self):
a = 'hello'
l = [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, [1, 2, 3, 4, 5, 6, 7, 8]]]
d = locals()
flwr = flwr_sequence([attribute_value('hello', scalar=True)])
assert flwr(d) == ('hello',)
assert execute("return l", d) == (l,)
assert execute('''
let f = function(l) {
if (isinstance(l, list))
then {for j in l return f(j)}
else l
}
return flatten f(l)
''', d, True) == (1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8)
def test_can_collect(self):
l = [1, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 3, 4]
d = locals()
assert execute('''
for n in l
collect n as n with function(prev, next) {
if prev == None then 1 else prev + 1
}
''', d, True) == {1: 1, 2: 1, 3: 3, 4: 3, 5: 2, 6: 2, 7: 2}
assert execute('''
for n in [1,2,3,4,5,6,7,3,4,5,6,7,3,4]
collect n as n with function(prev, next) {
if prev == None then 1 else prev + 1
}
''', d, True) == {1: 1, 2: 1, 3: 3, 4: 3, 5: 2, 6: 2, 7: 2}
def test_i_can_perform_calculations(self):
d = locals()
assert execute('''
for n in [
4.0*3.0/2.0,
4.0/3.0*2.0,
(3.0+9.0)*4.0/8.0,
((9.0-3.0)+(5.0-3.0))/2.0 + 2.0,
5.0 * 4.0 / 2.0 - 10.0 + 5.0 - 2.0 + 3.0,
5.0 / 4.0 * 2.0 + 10.0 - 5.0 * 2.0 / 3.0
]
return n
''', d, True) == (
4.0 * 3.0 / 2.0,
4.0 / 3.0 * 2.0,
(3.0 + 9.0) * 4.0 / 8.0,
((9.0 - 3.0) + (5.0 - 3.0)) / 2.0 + 2.0,
5.0 * 4.0 / 2.0 - 10.0 + 5.0 - 2.0 + 3.0,
5.0 / 4.0 * 2.0 + 10.0 - 5.0 * 2.0 / 3.0
)
def test_i_can_execute_flwr_with_in_and_not_in(self):
l = [[1, 2, 3], [4, 5, 6], [7, 4, 3], [5, 6, 7], [3, 4]]
d = locals()
assert execute('''
for n in l
where 4 in n
return n
''', d, True) == ([4, 5, 6], [7, 4, 3], [3, 4])
assert execute('''
for n in l
where 4 not in n
return n
''', d, True) == ([1, 2, 3], [5, 6, 7])
def test_multi_collect(self):
l = [1, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 3, 4]
d = locals()
assert execute('''
for n in l
let counter = function(prev, next) {
if prev == None then 1 else prev + 1
}
where 1 in l and 12 not in l
collect n as n with counter
collect n as (int(n)/int(2)) with counter
''', d, True), (
{1: 1, 2: 1, 3: 3, 4: 3, 5: 2, 6: 2, 7: 2},
{0: 1, 1: 4, 2: 5, 3: 4})
def test_i_can_execute_flwr_on_objects_attributes(self):
lst = [A(1), A(2), A(3)]
d = locals()
assert execute('for x in <lst.q> return x', d) == (1, 2, 3)
assert execute('for x in lst return x.q', d) == (1, 2, 3) # another way
def test_exception_during_return_are_caught(self):
lst = [A(1), A([2])]
d = locals()
res = execute('for x in lst return x.q + [2]', d)
assert len(res) == 2
assert isinstance(res[0], TypeError)
assert res[1] == [2,2]
def test_i_cannot_execute_flwr_on_attributes_that_does_not_exist(self):
lst = [A(1), A(2), A(3)]
d = locals()
with pytest.raises(AttributeError):
execute('for x in <lst.x> return x', d)
with pytest.raises(AttributeError):
execute('for x in lst.q return x', d) # AttributeError: 'list' object has no attribute 'q'
+122
View File
@@ -0,0 +1,122 @@
from contextlib import contextmanager
from ply import lex
from sheerkaql import lexer
def compare(a, b):
return (a.type == b.type and a.value == b.value and
a.lexpos == b.lexpos and a.lineno == b.lineno)
def token(token_type, value, pos, line):
t = lex.LexToken()
t.type = token_type
t.value = value
t.lexpos = pos
t.lineno = line
return t
@contextmanager
def comparable_tokens():
eq = lex.LexToken.__eq__
ne = lex.LexToken.__ne__
setattr(lex.LexToken, "__eq__", compare)
setattr(lex.LexToken, "__ne__", lambda a, b: not compare(a, b))
yield
setattr(lex.LexToken, "__eq__", eq)
setattr(lex.LexToken, "__ne__", ne)
class TestSheerkaQueryLanguageLexer:
def test_context_manager(self):
t1 = token("NAME", 'a', 0, 1)
t2 = token("NAME", 'a', 0, 1)
assert t1 != t2
with comparable_tokens():
assert t1 == t2
def test_NAME(self):
clex = lexer.Lexer()
clex.input('a 9a')
tokens = [token("NAME", 'a', 0, 1),
token("NUMBER", 9, 2, 1),
token("NAME", 'a', 3, 1)]
with comparable_tokens():
for t in tokens:
assert next(clex) == t
def test_STRING(self):
clex = lexer.Lexer()
clex.input("'asdf' \"asdf\" '\n'")
tokens = [token("STRING", 'asdf', 0, 1),
token("STRING", 'asdf', 7, 1),
token("STRING", '\n', 14, 1), ]
with comparable_tokens():
for t in tokens:
assert next(clex) == t
def test_HEX(self):
clex = lexer.Lexer()
clex.input("0xab 0xab")
tokens = [token("NUMBER", 0xab, 0, 1),
token("NUMBER", 171, 5, 1)]
with comparable_tokens():
for t in tokens:
assert next(clex) == t
def test_FLOAT(self):
clex = lexer.Lexer()
clex.input("1.2 .2 2.3e4 .2 2.3e4")
tokens = [token("NUMBER", 1.2, 0, 1), token("NUMBER", .2, 4, 1),
token("NUMBER", 2.3e4, 7, 1), token("NUMBER", .2, 13, 1),
token("NUMBER", 2.3e4, 16, 1)]
with comparable_tokens():
for t in tokens:
assert next(clex) == t
def test_OCT(self):
clex = lexer.Lexer()
clex.input("073 073 073")
tokens = [token("NUMBER", 0o73, 0, 1),
token("NUMBER", 59, 4, 1),
token("NUMBER", 59, 8, 1)]
with comparable_tokens():
for t in tokens:
assert next(clex) == t
def test_INTEGER(self):
clex = lexer.Lexer()
clex.input("73 730 7")
tokens = [token("NUMBER", 73, 0, 1),
token("NUMBER", 730, 3, 1),
token("NUMBER", 7, 7, 1)]
with comparable_tokens():
for t in tokens:
assert next(clex) == t
def test_KEYWORDS(self):
for value, token_type in list(lexer.reserved.items()):
clex = lexer.Lexer()
clex.input(value)
tokens = [token(token_type, value, 0, 1)]
with comparable_tokens():
for t in tokens:
assert next(clex) == t
def test_chrs(self):
for token_type, value in [(attr[2:], getattr(lexer.Lexer, attr))
for attr in dir(lexer.Lexer)
if attr[:2] == 't_' and
isinstance(getattr(lexer.Lexer, attr), str) and
attr[2:] != 'ignore']:
if value[0] == '\\':
value = value[1:]
clex = lexer.Lexer()
clex.input(value)
tokens = [token(token_type, value, 0, 1)]
with comparable_tokens():
for t in tokens:
assert next(clex) == t
+297
View File
@@ -0,0 +1,297 @@
import pytest
from sheerkaql.SheerkaQueryLangage import SheerkaQueryLanguage
from sheerkaql.lexer import Lexer
from sheerkaql.parser import Parser
class TestSheerkaQueryLanguageParser:
def test_hello(self):
SheerkaQueryLanguage().compile('hello')
with pytest.raises(SyntaxError) as ex:
SheerkaQueryLanguage().compile('hello there')
def test_i_can_parse_with_slash(self):
SheerkaQueryLanguage().compile('hello/part1')
SheerkaQueryLanguage().compile('hello/part1/part2/part3')
SheerkaQueryLanguage().compile('hello/part1 / part2 / part3')
with pytest.raises(SyntaxError):
SheerkaQueryLanguage().compile('hello/part1/part3 part4/part5')
def test_i_can_parse_with_dot(self):
SheerkaQueryLanguage().compile('hello.part1')
SheerkaQueryLanguage().compile('hello.part1.part2.part3')
SheerkaQueryLanguage().compile('hello.part1 . part2 . part3')
with pytest.raises(SyntaxError):
SheerkaQueryLanguage().compile('hello.part1.part3 part4.part5')
def test_i_can_parse_simple_where_conditions(self):
SheerkaQueryLanguage().compile('hello[wheRe]/hello[asdf]')
SheerkaQueryLanguage().compile('hello/hello[asdf]/asdf/wef')
SheerkaQueryLanguage().compile('hello/hello/wewe[asdf]/wef/waef/awef/weaf')
SheerkaQueryLanguage().compile('hello.hello[ asdf ] . wewe. wef[asdf] .waef .awef[asdf].weaf')
SheerkaQueryLanguage().compile('hello["foo"]')
SheerkaQueryLanguage().compile('hello[123]')
SheerkaQueryLanguage().compile('hello[123.234]')
with pytest.raises(SyntaxError):
SheerkaQueryLanguage().compile('hello/aef[asdf] hello[adsf]')
def test_i_can_parse_where_conditions_with_comparisons(self):
SheerkaQueryLanguage().compile('hello[1 == 1]')
SheerkaQueryLanguage().compile('hello[1 != 1]')
SheerkaQueryLanguage().compile('hello[1 < 1]')
SheerkaQueryLanguage().compile('hello[1 <= 1]')
SheerkaQueryLanguage().compile('hello[1 > 1]')
SheerkaQueryLanguage().compile('hello[1 >= 1]')
def test_i_can_parse_where_conditions_with_logical_operations(self):
SheerkaQueryLanguage().compile('hello[a and b]')
SheerkaQueryLanguage().compile('hello[a or b]')
SheerkaQueryLanguage().compile('hello[not a or b]')
SheerkaQueryLanguage().compile('hello[a or not b]')
SheerkaQueryLanguage().compile('hello[not a and b]')
SheerkaQueryLanguage().compile('hello[a and not b]')
SheerkaQueryLanguage().compile('hello[not a or not b]')
SheerkaQueryLanguage().compile('hello[not a and not b]')
def test_i_can_parse_where_conditions_with_logical_operations_and_parenthesis(self):
SheerkaQueryLanguage().compile('hello[((a and b) and not (a or b) or not (a and b)) and not (not a or b)]')
def test_i_can_parse_where_conditions_with_arithmetic_operations(self):
SheerkaQueryLanguage().compile('hello[a + b]')
SheerkaQueryLanguage().compile('hello[a - b]')
SheerkaQueryLanguage().compile('hello[a * b]')
SheerkaQueryLanguage().compile('hello[a / b]')
SheerkaQueryLanguage().compile('hello[(a + b) / (a - b)]')
SheerkaQueryLanguage().compile('hello[-a]')
SheerkaQueryLanguage().compile('hello[a + b * c / e]')
def test_i_can_parse_nested_where_conditions(self):
SheerkaQueryLanguage().compile('hello[a]')
SheerkaQueryLanguage().compile("hello[a[0]['hello']]")
SheerkaQueryLanguage().compile("hello[a[0][\"hello\"]]")
def test_i_can_parse_where_conditions_with_function_call(self):
SheerkaQueryLanguage().compile("hello[a[0]['hello']()]")
SheerkaQueryLanguage().compile("hello[a[0]['hello'](0)]")
SheerkaQueryLanguage().compile("hello[a[0]['hello']('asdf')]")
SheerkaQueryLanguage().compile("hello[a[0]['hello'](asdf)]")
SheerkaQueryLanguage().compile("hello[a[0]['hello'](0, 'asdf', asdf)()()(1,2)]")
SheerkaQueryLanguage().compile('hello[f(1)]')
SheerkaQueryLanguage().compile('hello[f(1,2,3)]')
SheerkaQueryLanguage().compile('hello[f(a,b,c)]')
SheerkaQueryLanguage().compile('hello[f(a(),b(),c())]')
SheerkaQueryLanguage().compile('hello[f(a[a],b[b],c[c])]')
def test_i_can_parse_where_conditions_with_attributes_parsing(self):
SheerkaQueryLanguage().compile('hello[foo.bar.baz]')
SheerkaQueryLanguage().compile('hello[foo[12].bar().baz]')
def test_i_can_parse_where_conditions_with_flwr(self):
SheerkaQueryLanguage().compile('hello[f(1,<asdf>,{for x in <asdf> return x})]')
def test_i_can_parse_where_conditions_with_quantified_expressions(self):
SheerkaQueryLanguage().compile('hello[every x in <asdf> satisfies (x)]')
with pytest.raises(SyntaxError):
SheerkaQueryLanguage().compile('hello[every x in <asdf> statisfies (x)]')
with pytest.raises(SyntaxError):
SheerkaQueryLanguage().compile('hello[every x in <asdf> statisfies x]')
SheerkaQueryLanguage().compile('hello[some x in <asdf> satisfies (x)]')
SheerkaQueryLanguage().compile('hello[some x in <self/asdf> satisfies (x)]')
SheerkaQueryLanguage().compile('hello[some x in {for x in <asdf> return x} satisfies (x)]')
SheerkaQueryLanguage().compile('hello[some x in {for x in <asdf> return x} satisfies (x == y)]')
SheerkaQueryLanguage().compile('hello[some x in {for x in <asdf> return x} satisfies (x and not y(1,2))]')
def test_i_can_parse_where_conditions_with_set_comparisons(self):
SheerkaQueryLanguage().compile('hello[a in <qs>]')
SheerkaQueryLanguage().compile('hello[a not in <qs>]')
SheerkaQueryLanguage().compile('hello[not a in <qs>]')
SheerkaQueryLanguage().compile('hello[<a> subset <qs>]')
SheerkaQueryLanguage().compile('hello[<a> superset <qq>]')
SheerkaQueryLanguage().compile('hello[<a> proper subset <aq>]')
SheerkaQueryLanguage().compile('hello[<a> proper superset <aq>]')
SheerkaQueryLanguage().compile('hello[<a> is <qs>]')
SheerkaQueryLanguage().compile('hello[<a> is not <qs>]')
def test_i_can_parse_operations_on_sets(self):
SheerkaQueryLanguage().compile('asdf - asdf')
SheerkaQueryLanguage().compile('asdf & asdf')
SheerkaQueryLanguage().compile('asdf | asdf')
SheerkaQueryLanguage().compile('(asdf | asdf) & asdf - (asdf & asdf) - (asdf & (afsd | asdf))')
SheerkaQueryLanguage().compile('asdf/asdf - asdf/asd[erw]')
def test_i_can_parse_flwr_expression(self):
SheerkaQueryLanguage().compile('for x in y return x')
SheerkaQueryLanguage().compile('for x in <asfd> return x')
SheerkaQueryLanguage().compile('for x in <asfd>, y in <adsf> return x')
SheerkaQueryLanguage().compile('for x in {for x in <asdf> return x} return x')
SheerkaQueryLanguage().compile('for x in <asfd> where x == y return x')
SheerkaQueryLanguage().compile('for x in <asfd> let y = <x/asdf> return x')
SheerkaQueryLanguage().compile('for x in <asfd> let y = {for x in <asdf> return x} return x')
SheerkaQueryLanguage().compile('for x in <asfd> let y = <x/asdf>, x = <adf> return x')
SheerkaQueryLanguage().compile('for x in <asfd> let y = <x/asdf> let x = <adf> return x')
SheerkaQueryLanguage().compile('for x in <asfd>, z in <asdf> let y = <x/asdf> let x = <adf> return x')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>
let y = <x/asdf>
let x = <adf>
where every x in <y> satisfies (q)
return x''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>
let y = <x/asdf>
let x = <adf>
where every x in <y> satisfies (q)
return x,y,z''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>
let y = <x/asdf>
let x = <adf>
where every x in <y> satisfies (q)
return x,y.sdf.asd,z''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>
let y = <x/asdf>
let x = <adf>
where every x in <y> satisfies (q)
return x,y.sdf.asd,z()()()[asdf][asfd](1,2,3)''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>
let y = <x/asdf>
let x = <adf>
where every x in <y> satisfies (q)
return 'asdf':asdf''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>
let y = <x/asdf>
let x = <adf>
where every x in <y> satisfies (q)
return 'asdf':asdf, "hello":"hello World!"''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>
let y = <x/asdf>
let x = <adf>
where every x in <y> satisfies (q == z and (<y> is not <z>))
return 'asdf':asdf, "one":1, "2.0":2.0''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>, y in <asdf/asdf>
let y = <x/asdf>
let x = <adf>
where every x in <y> satisfies (q == z and (<y> is not <z>))
return 'asdf':asdf, "one":1, "2.0":2.0''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>, y in <asdf/asdf>
let y = <x/asdf>, y1 = <Afd>, y2 = <asdf>, y3 = <asdf>
let x = <adf>
where every x in <y> satisfies (q == z and (<y> is not <z>))
return 'asdf':asdf, "one":1, "2.0":2.0''')
def test_i_can_parse_flwr_with_attribute_value(self):
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>, y in sdaf.asdf(asdf, asdf)[1]
let y = <x/asdf>, y1 = <Afd>, y2 = <asdf>, y3 = <asdf>
let x = <adf>
where every x in <y> satisfies (q == z and (<y> is not <z>))
return 'asdf':asdf, "one":1, "2.0":2.0''')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>, y in sdaf.asdf(asdf, asdf)[1]
let y = <x/asdf>, y1 = <Afd>, y2 = <asdf>, y3 = <asdf>
let x = <adf>
let q = sadf.asdf().asfd[1](1,2,3)
where every x in <y> satisfies (q == z and (<y> is not <z>))
return 'asdf':asdf, "one":1, "2.0":2.0''')
def test_flwr_orderby(self):
SheerkaQueryLanguage().compile('for x in <asdf> order by "adsf" desc return "adsf":x')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>, y in sdaf.asdf(asdf, asdf)[1]
let y = <x/asdf>, y1 = <Afd>, y2 = <asdf>, y3 = <asdf>
let x = <adf>
where every x in <y> satisfies (q == z and (<y> is not <z>))
order by "asdf" desc
return 'asdf':asdf, "one":1, "2.0":2.0''')
SheerkaQueryLanguage().compile('for x in <asdf> order by 0 asc return x')
SheerkaQueryLanguage().compile('''for x in <asfd>, z in <asdf>, y in sdaf.asdf(asdf, asdf)[1]
let y = <x/asdf>, y1 = <Afd>, y2 = <asdf>, y3 = <asdf>
let x = <adf>
where every x in <y> satisfies (q == z and (<y> is not <z>))
order by 1 asc
return asdf, 1, 2.0''')
def test_flwr_function_noargs(self):
SheerkaQueryLanguage().compile('''
for x in <asdf>
let f = function() { for y in <asdf> return y }
return f
''')
def test_flwr_function_args(self):
SheerkaQueryLanguage().compile('''
for x in <asdf>
let f = function(q) { for y in q return y }
return f
''')
def test_if(self):
SheerkaQueryLanguage().compile('''
for x in <asdf> return if (0) then 1 else 0
''')
def test_reduce(self):
SheerkaQueryLanguage().compile('''
for x in <asdf>
collect x.tree as x.attr with function(prev, next) {
if prev == None then next else prev.combine(next)
}
''')
def test_in_list1(self):
SheerkaQueryLanguage().compile("hello['foo' in ['foo','bar']]")
def test_in_list2(self):
SheerkaQueryLanguage().compile("hello['baz' in ['foo','bar']]")
def test_not_in_list1(self):
SheerkaQueryLanguage().compile("hello['foo' not in ['foo','bar']]")
def test_not_in_list2(self):
SheerkaQueryLanguage().compile("hello['baz' not in ['foo','bar']]")
def test_in_list3(self):
result = SheerkaQueryLanguage().execute("res[test_elt in ['foo','bar']]",
{'res': True, 'test_elt': 'foo'})
assert bool(result)
def test_in_list4(self):
result = SheerkaQueryLanguage().execute("res[test_elt in ['foo','bar']]",
{'res': True, 'test_elt': 'baz'})
assert not bool(result)
def test_not_in_list4(self):
result = SheerkaQueryLanguage().execute("res[test_elt not in ['foo','bar']]",
{'res': True, 'test_elt': 'baz'})
assert bool(result)
def test_not_in_list5(self):
result = SheerkaQueryLanguage().execute("res[test_elt not in ['foo','bar']]",
{'res': True, 'test_elt': 'foo'})
assert not bool(result)
@pytest.mark.parametrize("text, expected", [
("hello", {"hello"}),
("hello.foo.bar", {"hello"}),
("hello/foo/bar", {"hello"}),
("hello[foo.bar.baz]", {"hello", "foo"}),
("hello[foo()]", {"hello", "foo"}),
("hello[foo(bar)]", {"hello", "foo", "bar"}),
("hello[foo(1, bar.baz)]", {"hello", "foo", "bar"}),
("hello[foo[bar]]", {"hello", "foo", "bar"}),
("hello[foo > bar]", {"hello", "foo", "bar"}),
("hello[foo + bar]", {"hello", "foo", "bar"}),
("hello[[a,b,c]]", {"hello", "a", "b", "c"}),
("hello[{a:b}]", {"hello", "a", "b"}),
])
def test_i_can_get_names(self, text, expected):
parser = Parser()
parser.parse(bytes(text, 'utf-8').decode('unicode_escape'), lexer=Lexer())
assert parser.names == expected
@pytest.mark.parametrize("text", [
"sheerka.method",
"sheerka/method",
"hello[sheerka.method]",
"hello[sheerka.method.xx]",
])
def test_i_can_get_sheerka_methods(self, text):
parser = Parser()
parser.parse(bytes(text, 'utf-8').decode('unicode_escape'), lexer=Lexer())
assert parser.sheerka_names == {"method"}