From 86c2ff58d406947cfc8001452dd9c768f0020213 Mon Sep 17 00:00:00 2001 From: Kodjo Sossouvi Date: Mon, 17 Feb 2020 21:07:06 +0100 Subject: [PATCH] Added set of set handling (thru concept ISA) --- src/core/ast/nodes.py | 2 +- src/core/ast/visitors.py | 4 +- src/core/builtin_concepts.py | 33 +-- src/core/builtin_helpers.py | 3 +- src/core/concept.py | 38 ++-- .../Services/SheerkaCreateNewConcept.py | 2 + src/core/sheerka/Services/SheerkaDump.py | 8 +- .../Services/SheerkaEvaluateConcept.py | 8 +- .../sheerka/Services/SheerkaHistoryManager.py | 2 +- .../sheerka/Services/SheerkaModifyConcept.py | 15 ++ .../sheerka/Services/SheerkaSetsManager.py | 167 ++++++++++++-- src/core/sheerka/Sheerka.py | 43 +++- src/evaluators/AddConceptEvaluator.py | 2 +- src/evaluators/EvalEvaluator.py | 10 +- src/evaluators/PythonEvaluator.py | 18 +- src/parsers/BnfParser.py | 6 +- src/parsers/ConceptLexerParser.py | 4 +- src/parsers/DefaultParser.py | 2 +- src/sdp/sheerkaDataProvider.py | 25 +- src/sdp/sheerkaSerializer.py | 17 +- src/sheerkapickle/sheerka_handlers.py | 47 ++-- tests/BaseTest.py | 27 ++- tests/TestUsingFileBasedSheerka.py | 4 +- tests/TestUsingMemoryBasedSheerka.py | 3 +- tests/core/test_SheerkaEvaluateConcept.py | 1 + tests/core/test_SheerkaSetsManager.py | 213 ++++++++++++++---- tests/core/test_ast.py | 26 +-- tests/core/test_builtin_helpers.py | 1 + tests/core/test_sheerka.py | 6 +- tests/evaluators/test_EvalEvaluator.py | 18 ++ tests/non_reg/test_sheerka_non_reg.py | 10 +- tests/sheerkapickle/test_SheerkaPickler.py | 9 - tests/sheerkapickle/test_sheerka_handlers.py | 93 +++++--- 33 files changed, 635 insertions(+), 232 deletions(-) create mode 100644 src/core/sheerka/Services/SheerkaModifyConcept.py diff --git a/src/core/ast/nodes.py b/src/core/ast/nodes.py index 7b9b9dc..49d5420 100644 --- a/src/core/ast/nodes.py +++ b/src/core/ast/nodes.py @@ -139,7 +139,7 @@ def concept_to_python(concept_node): value = node.get_prop(field) if isinstance(value, list) or isinstance(value, Concept) and value.key == str(BuiltinConcepts.LIST): lst = [] - for i in value: + for i in value.body: lst.append(_transform(i)) setattr(ast_object, field, lst) elif isinstance(value, NodeConcept): diff --git a/src/core/ast/visitors.py b/src/core/ast/visitors.py index 93572ca..d274261 100644 --- a/src/core/ast/visitors.py +++ b/src/core/ast/visitors.py @@ -22,7 +22,7 @@ class ConceptNodeVisitor: """Called if no explicit visitor function exists for a node.""" for field, value in iter_props(node): if isinstance(value, ListConcept): - for item in value: + for item in value.body: if isinstance(item, NodeConcept): self.visit(item) elif isinstance(value, NodeConcept): @@ -97,8 +97,6 @@ class ExtractPredicateVisitor(ConceptNodeVisitor): self.variable_name = variable_name - - def get_parents(node): if node.parent is None: return [] diff --git a/src/core/builtin_concepts.py b/src/core/builtin_concepts.py index 9470844..9415559 100644 --- a/src/core/builtin_concepts.py +++ b/src/core/builtin_concepts.py @@ -52,6 +52,7 @@ class BuiltinConcepts(Enum): NOT_A_SET = "not a set" # the concept has no entry in sets WHERE_CLAUSE_FAILED = "where clause failed" # failed to validate where clause during evaluation CHICKEN_AND_EGG = "chicken and egg" # infinite recursion when declaring concept + ISA = "is a" # builtin concept to express that a concept is an instance of another one NODE = "node" GENERIC_NODE = "generic node" @@ -340,8 +341,8 @@ class EnumerationConcept(Concept): self.set_metadata_value(ConceptParts.BODY, iteration) self.metadata.is_evaluated = True - def __iter__(self): - return iter(self.body) + # def __iter__(self): + # return iter(self.body) class ListConcept(Concept): @@ -353,20 +354,20 @@ class ListConcept(Concept): def append(self, obj): self.body.append(obj) - def __len__(self): - return len(self.body) - - def __getitem__(self, key): - return self.body[key] - - def __setitem__(self, key, value): - self.body[key] = value - - def __iter__(self): - return iter(self.body) - - def __contains__(self, item): - return item in self.body + # def __len__(self): + # return len(self.body) + # + # def __getitem__(self, key): + # return self.body[key] + # + # def __setitem__(self, key, value): + # self.body[key] = value + # + # def __iter__(self): + # return iter(self.body) + # + # def __contains__(self, item): + # return item in self.body class ConceptAlreadyInSet(Concept): diff --git a/src/core/builtin_helpers.py b/src/core/builtin_helpers.py index a7492cf..a6ee448 100644 --- a/src/core/builtin_helpers.py +++ b/src/core/builtin_helpers.py @@ -7,7 +7,6 @@ from core.ast.visitors import UnreferencedNamesVisitor from core.builtin_concepts import BuiltinConcepts - def is_same_success(sheerka, return_values): """ Returns True if all returns values are successful and have the same value @@ -206,7 +205,7 @@ def _extract_predicates(sheerka, node, variables_to_include, variables_to_exclud elif node.node_type == "BoolOp": all_op = True temp_res = [] - for op in node.get_prop("values"): + for op in node.get_prop("values").body: res = _extract_predicates(sheerka, op, variables_to_include, variables_to_exclude) if len(res) == 0: all_op = False diff --git a/src/core/concept.py b/src/core/concept.py index 68e780f..68e6668 100644 --- a/src/core/concept.py +++ b/src/core/concept.py @@ -47,6 +47,7 @@ class ConceptMetadata: id: str # unique identifier for a concept. The id will never be modified (but the key can) props: list # list properties, with their default values is_evaluated: bool = False # True is the concept is evaluated by sheerka.eval_concept() + full_serialization: bool = False # If True, the full object will be serialized, rather than just the diff simplec = namedtuple("concept", "name body") # for simple concept (tests purposes only) @@ -163,10 +164,10 @@ class Concept: name = self.name if 'metadata' in vars(self) else 'Concept' raise AttributeError(f"'{name}' concept has no attribute '{item}'") - def def_prop(self, prop_name: str, default_value=None): + def def_prop(self, prop_name, default_value=None): """ Adds a property to the metadata - :param prop_name: + :param prop_name: name or concept :param default_value: :return: """ @@ -242,25 +243,6 @@ class Concept: def body(self): return self.values[ConceptParts.BODY] if ConceptParts.BODY in self.values else None - # def add_codes(self, codes): - # """ - # Gets the ASTs for 'where', 'pre', 'post' and 'body' - # There ASTs are know when the concept is freshly parsed. - # So the values are kept in cache. - # - # For concepts loaded from sdp, these ASTs must be created again - # TODO : Seems to be a service method. Can be put somewhere else - # :param codes: - # :return: - # """ - # if codes is None: - # return - # - # for key in codes: - # self.compiled[key] = codes[key] - # - # return self - def get_digest(self): """ Returns the digest of the event @@ -321,12 +303,22 @@ class Concept: return self - def set_prop(self, prop_name: str, prop_value): - """Directly sets a value to a property""" + def set_prop(self, prop_name, prop_value): + """ + Set the value of a property (not the metadata) + :param prop_name: Name the property or another concept + :param prop_value: + :return: + """ self.props[prop_name] = Property(prop_name, prop_value) return self def get_prop(self, prop_name: str): + """ + Gets the value of a property + :param prop_name: name or concept + :return: + """ return self.props[prop_name].value def set_metadata_value(self, metadata: ConceptParts, value): diff --git a/src/core/sheerka/Services/SheerkaCreateNewConcept.py b/src/core/sheerka/Services/SheerkaCreateNewConcept.py index ba0b69b..ea5102b 100644 --- a/src/core/sheerka/Services/SheerkaCreateNewConcept.py +++ b/src/core/sheerka/Services/SheerkaCreateNewConcept.py @@ -59,6 +59,7 @@ class SheerkaCreateNewConcept: return self.sheerka.ret(self.logger_name, False, ErrorConcept(init_ret_value.value)) # save the new concept in sdp + concept.metadata.full_serialization = True try: # TODO : needs to make these calls atomic (or at least one single call) # save the new concept @@ -90,6 +91,7 @@ class SheerkaCreateNewConcept: error.args[0]) # Updates the caches + concept.metadata.full_serialization = False self.sheerka.cache_by_key[concept.key] = self.sheerka.sdp.get_safe(self.sheerka.CONCEPTS_ENTRY, concept.key) self.sheerka.cache_by_id[concept.id] = concept if init_ret_value is not None and init_ret_value.status: diff --git a/src/core/sheerka/Services/SheerkaDump.py b/src/core/sheerka/Services/SheerkaDump.py index 7e8b876..d404265 100644 --- a/src/core/sheerka/Services/SheerkaDump.py +++ b/src/core/sheerka/Services/SheerkaDump.py @@ -31,6 +31,8 @@ class SheerkaDump: def dump_desc(self, *concept_names, eval=False): first = True + event = Event(f"Dumping description", "") + context = ExecutionContext("dump_desc", event, self.sheerka) for concept_name in concept_names: if isinstance(concept_name, Concept): concepts = concept_name @@ -45,8 +47,6 @@ class SheerkaDump: for c in concepts: if eval: - event = Event(f"Evaluating {c}", "") - context = ExecutionContext("dump_desc", event, self.sheerka) evaluated = self.sheerka.evaluate_concept(context, c) value = evaluated.body if evaluated.key == c.key else evaluated @@ -60,8 +60,8 @@ class SheerkaDump: self.sheerka.log.info(f"value : {value}") self.sheerka.log.info(f"digest : {c.get_digest()}") - if self.sheerka.isaset(c): - items = self.sheerka.get_set_elements(c) + if self.sheerka.isaset(context, c): + items = self.sheerka.get_set_elements(context, c) self.sheerka.log.info(f"elements : {items}") first = False diff --git a/src/core/sheerka/Services/SheerkaEvaluateConcept.py b/src/core/sheerka/Services/SheerkaEvaluateConcept.py index d0d2579..5b0cde7 100644 --- a/src/core/sheerka/Services/SheerkaEvaluateConcept.py +++ b/src/core/sheerka/Services/SheerkaEvaluateConcept.py @@ -235,6 +235,12 @@ class SheerkaEvaluateConcept: concept.set_prop(prop_name, resolved) else: part_key = ConceptParts(metadata_to_eval) + + # do not evaluate where when the body is a set + # Indeed, the way that the where clause is expressed is not a valid python or concept code + if part_key == ConceptParts.WHERE and self.sheerka.isaset(context, concept.body): + continue + if part_key in concept.compiled and concept.compiled[part_key] is not None: metadata_ast = concept.compiled[part_key] resolved = self.resolve(context, metadata_ast, part_key, concept, logger) @@ -244,7 +250,7 @@ class SheerkaEvaluateConcept: concept.values[part_key] = self.get_infinite_recursion_resolution(resolved) or resolved # validate where clause - if concept.metadata.where is not None: + if ConceptParts.WHERE in concept.values: where_value = concept.values[ConceptParts.WHERE] if not (where_value is None or self.sheerka.value(where_value) is True): return self.sheerka.new(BuiltinConcepts.WHERE_CLAUSE_FAILED, body=concept) diff --git a/src/core/sheerka/Services/SheerkaHistoryManager.py b/src/core/sheerka/Services/SheerkaHistoryManager.py index 5c7f268..c733e3b 100644 --- a/src/core/sheerka/Services/SheerkaHistoryManager.py +++ b/src/core/sheerka/Services/SheerkaHistoryManager.py @@ -68,7 +68,7 @@ class SheerkaHistoryManager: events = list(self.sheerka.sdp.load_events(depth_or_digest, start)) for event in events: try: - result = self.sheerka.sdp.load_result(self.sheerka, event.get_digest()) + result = self.sheerka.sdp.load_result(event.get_digest()) except (IOError, KeyError): result = None yield History(event, result) diff --git a/src/core/sheerka/Services/SheerkaModifyConcept.py b/src/core/sheerka/Services/SheerkaModifyConcept.py new file mode 100644 index 0000000..d6dcbb9 --- /dev/null +++ b/src/core/sheerka/Services/SheerkaModifyConcept.py @@ -0,0 +1,15 @@ +from core.builtin_concepts import BuiltinConcepts + + +class SheerkaModifyConcept: + def __init__(self, sheerka): + self.sheerka = sheerka + self.logger_name = self.modify_concept.__name__ + + def modify_concept(self, context, concept, logger=None): + logger = logger or self.sheerka.log + + self.sheerka.sdp.modify(context.event.get_digest(), self.sheerka.CONCEPTS_ENTRY, concept.key, concept) + + ret = self.sheerka.ret(self.logger_name, True, self.sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept)) + return ret diff --git a/src/core/sheerka/Services/SheerkaSetsManager.py b/src/core/sheerka/Services/SheerkaSetsManager.py index 180ce61..9ecd020 100644 --- a/src/core/sheerka/Services/SheerkaSetsManager.py +++ b/src/core/sheerka/Services/SheerkaSetsManager.py @@ -1,5 +1,7 @@ +from core.ast.nodes import python_to_concept from core.builtin_concepts import BuiltinConcepts, ErrorConcept -from core.concept import Concept +from core.concept import Concept, ConceptParts +import core.builtin_helpers GROUP_PREFIX = 'All_' @@ -9,6 +11,30 @@ class SheerkaSetsManager: self.sheerka = sheerka self.logger_name = self.add_concept_to_set.__name__ + def set_isa(self, context, concept, concept_set, logger=None): + """ + Defines that concept a is b is another concept + :param context: + :param concept: + :param concept_set: + :param logger: + :return: + """ + + logger = logger or self.sheerka.log + context.log(logger, f"Setting that concept {concept} is a {concept_set}", who=self.logger_name) + + isa = [] if BuiltinConcepts.ISA not in concept.props else concept.get_prop(BuiltinConcepts.ISA) + if concept_set not in isa: + isa.append(concept_set) + + concept.set_prop(BuiltinConcepts.ISA, isa) + res = self.sheerka.modify_concept(context, concept, logger) + if not res.status: + return res + + return self.add_concept_to_set(context, concept, concept_set, logger) + def add_concept_to_set(self, context, concept, concept_set, logger=None): """ Add an entry in sdp to tell that concept isa concept_set @@ -53,24 +79,51 @@ class SheerkaSetsManager: context.log_error(logger, "Failed to add to set.", who=self.logger_name) return self.sheerka.ret(self.logger_name, False, ErrorConcept(error), error.args[0]) - def get_set_elements(self, concept): + def get_set_elements(self, context, concept, logger=None): """ Concept is supposed to be a set Returns all elements if the set + :param context: :param concept: + :param logger: :return: """ - assert concept.id + logger = logger or self.sheerka.log - ids = self.sheerka.sdp.get_safe(GROUP_PREFIX + concept.id) - if ids is None: - return self.sheerka.new(BuiltinConcepts.NOT_A_SET, body=concept) + # noinspection PyShadowingNames + def _get_set_elements(context, concept, sub_concept): + if not (isinstance(sub_concept, Concept) and sub_concept.id): + return self.sheerka.new(BuiltinConcepts.NOT_A_SET, body=concept) - elements = [self.sheerka.get_by_id(element_id) for element_id in ids] - return elements + ids = self.sheerka.sdp.get_safe(GROUP_PREFIX + sub_concept.id) + if ids: + if concept.metadata.where: + new_condition = self._validate_where_clause(concept) + if not new_condition: + return self.sheerka.new(BuiltinConcepts.WHERE_CLAUSE_FAILED, body=concept) + else: + # This methods sucks, but I don't have enough tools (like proper AST manipulation functions) + # to do it properly now. It will be enhanced later + concepts = self._get_concepts(context, ids, True, logger) + globals_ = {"xx__concepts__xx": concepts, "sheerka": self.sheerka} + locals_ = {} + exec(new_condition, globals_, locals_) + return locals_["result"] + else: + return self._get_concepts(context, ids, False, logger) - def isa(self, a, b): + # it may be a concept that references a set + if not sub_concept.metadata.is_evaluated: + with context.push(desc=f"Evaluating concept {sub_concept}") as sub_context: + evaluated = self.sheerka.evaluate_concept(sub_context, sub_concept) + if evaluated.key != concept.key: + return False + return _get_set_elements(context, concept, sub_concept.body) + + return _get_set_elements(context, concept, concept) + + def isinset(self, a, b): """ return true if the concept a is a b Will handle when the keyword isa will be implemented @@ -82,17 +135,101 @@ class SheerkaSetsManager: if isinstance(a, BuiltinConcepts): # common KSI error ;-) raise SyntaxError("Remember that the first parameter of isinstance MUST be a concept") - assert isinstance(a, Concept) - assert isinstance(b, Concept) + if not (isinstance(a, Concept) and isinstance(b, Concept)): + return False # TODO, first check the 'isa' property of a + if not (a.id and b.id): + return False - return self.sheerka.sdp.exists(GROUP_PREFIX + b.id, a.id) + if self.sheerka.sdp.exists(GROUP_PREFIX + b.id, a.id): + return True - def isaset(self, concept): - """True if exists All_ in sdp""" - if not concept.id: + return False + + def isa(self, a, b): + + if BuiltinConcepts.ISA not in a.props: + return False + + for c in a.get_prop(BuiltinConcepts.ISA): + if c == b: + return True + if self.isa(c, b): + return True + + return False + + def isaset(self, context, concept, logger=None): + """ + True if exists All_ in sdp or if concept references to a concept that has all_ + :param context: + :param concept: + :param logger: + :return: + """ + """""" + + logger = logger or self.sheerka.log + + if not (isinstance(concept, Concept) and concept.id): return None + # it may be a concept that references a set + if not concept.metadata.is_evaluated: + with context.push(desc=f"Evaluating concept {concept}") as sub_context: + evaluated = self.sheerka.evaluate_concept(sub_context, concept, logger) + if evaluated.key != concept.key: + return False + + if concept.body: + return self.isaset(context, concept.body, logger) + res = self.sheerka.sdp.get_safe(GROUP_PREFIX + concept.id) return res is not None + + def _validate_where_clause(self, concept): + python_parser_result = [r for r in concept.compiled[ConceptParts.WHERE] if r.who == "parsers.Python"] + if not python_parser_result or not python_parser_result[0].status: + return None + + ast_ = python_parser_result[0].body.body.ast_ + ast_as_concepts = python_to_concept(ast_) + names = core.builtin_helpers.get_names(self.sheerka, ast_as_concepts) + if len(names) != 1 or names[0] != concept.metadata.body: + return None + + condition = concept.metadata.where.replace(concept.metadata.body, "sheerka.value(x)") + expression = f""" +result=[] +for x in xx__concepts__xx: + try: + if {condition}: + result.append(x) + except Exception: + pass +""" + return expression + + def _get_concepts(self, context, ids, evaluate, logger): + """ + Gets concepts from a list of concepts ids + :param ids: + :param evaluate: if True, all the elements are evaluated before returned + :return: + """ + + if not ids: + return [] + + if not evaluate: + return [self.sheerka.get_by_id(element_id) for element_id in ids] + + result = [] + with context.push(desc=f"Evaluating concepts of a set") as sub_context: + for element_id in ids: + concept = self.sheerka.get_by_id(element_id) + evaluated = self.sheerka.evaluate_concept(context, concept, logger) + result.append(evaluated) + + return result diff --git a/src/core/sheerka/Sheerka.py b/src/core/sheerka/Sheerka.py index feb037c..8a07342 100644 --- a/src/core/sheerka/Sheerka.py +++ b/src/core/sheerka/Sheerka.py @@ -7,6 +7,7 @@ from core.sheerka.Services.SheerkaDump import SheerkaDump from core.sheerka.Services.SheerkaEvaluateConcept import SheerkaEvaluateConcept from core.sheerka.Services.SheerkaExecute import SheerkaExecute from core.sheerka.Services.SheerkaHistoryManager import SheerkaHistoryManager +from core.sheerka.Services.SheerkaModifyConcept import SheerkaModifyConcept from core.sheerka.Services.SheerkaSetsManager import SheerkaSetsManager from sdp.sheerkaDataProvider import SheerkaDataProvider, Event import core.utils @@ -81,12 +82,14 @@ class Sheerka(Concept): self.execute_handler = SheerkaExecute(self) self.create_new_concept_handler = SheerkaCreateNewConcept(self) + self.modify_concept_handler = SheerkaModifyConcept(self) self.dump_handler = SheerkaDump(self) self.sets_handler = SheerkaSetsManager(self) self.evaluate_concept_handler = SheerkaEvaluateConcept(self) self.history_handler = SheerkaHistoryManager(self) self.during_restore = False + self._builtins_classes_cache = None def initialize(self, root_folder: str = None): """ @@ -101,7 +104,7 @@ class Sheerka(Concept): from sheerkapickle.sheerka_handlers import initialize_pickle_handlers initialize_pickle_handlers() - self.sdp = SheerkaDataProvider(root_folder) + self.sdp = SheerkaDataProvider(root_folder, self) if self.sdp.first_time: self.sdp.set_key(self.USER_CONCEPTS_KEYS, 1000) @@ -117,7 +120,7 @@ class Sheerka(Concept): exec_context.add_values(return_values=res) if not self.skip_builtins_in_db: - self.sdp.save_result(self, exec_context) + self.sdp.save_result(exec_context) except IOError as e: res = ReturnValueConcept(self, False, self.get(BuiltinConcepts.ERROR), e) @@ -151,6 +154,7 @@ class Sheerka(Concept): if from_db is None: self.init_log.debug(f"'{concept.name}' concept is not found in db. Adding.") self.set_id_if_needed(concept, True) + concept.metadata.full_serialization = True self.sdp.add("init", self.CONCEPTS_ENTRY, concept, use_ref=True) else: self.init_log.debug(f"Found concept '{from_db}' in db. Updating.") @@ -247,9 +251,9 @@ class Sheerka(Concept): execution_context.add_values(return_values=ret) if not self.skip_builtins_in_db: - self.sdp.save_result(self, execution_context) + self.sdp.save_result(execution_context) - # hack to save valid concept definition + # # hack to save valid concept definition # if not self.during_restore: # if len(ret) == 1 and ret[0].status and self.isinstance(ret[0].value, BuiltinConcepts.NEW_CONCEPT): # with open(CONCEPTS_FILE, "a") as f: @@ -294,6 +298,9 @@ class Sheerka(Concept): return self.create_new_concept_handler.create_new_concept(context, concept, logger) + def modify_concept(self, context, concept: Concept, logger): + return self.modify_concept_handler.modify_concept(context, concept, logger) + def add_concept_to_set(self, context, concept, concept_set, logger=None): """ Add an entry in sdp to tell that concept isa concept_set @@ -305,15 +312,27 @@ class Sheerka(Concept): """ return self.sets_handler.add_concept_to_set(context, concept, concept_set, logger) - def get_set_elements(self, concept): + def set_isa(self, context, concept, concept_set, logger=None): + """ + + :param context: + :param concept: + :param concept_set: + :param logger: + :return: + """ + return self.sets_handler.set_isa(context, concept, concept_set, logger) + + def get_set_elements(self, context, concept): """ Concept is supposed to be a set Returns all elements if the set + :param context: :param concept: :return: """ - return self.sets_handler.get_set_elements(concept) + return self.sets_handler.get_set_elements(context, concept) def evaluate_concept(self, context, concept: Concept, logger=None): """ @@ -524,7 +543,10 @@ class Sheerka(Concept): self.isinstance(objs, BuiltinConcepts.ENUMERATION)): objs = [objs] - return (self.value(obj) for obj in objs) + if isinstance(objs, list): + return (self.value(obj) for obj in objs) + + return (self.value(obj) for obj in objs.body) def is_success(self, obj): if isinstance(obj, bool): # quick win @@ -562,11 +584,14 @@ class Sheerka(Concept): return a.key == b_key + def isinset(self, a, b): + return self.sets_handler.isinset(a, b) + def isa(self, a, b): return self.sets_handler.isa(a, b) - def isaset(self, concept): - return self.sets_handler.isaset(concept) + def isaset(self, context, concept): + return self.sets_handler.isaset(context, concept) def get_evaluator_name(self, name): if self.evaluators_prefix is None: diff --git a/src/evaluators/AddConceptEvaluator.py b/src/evaluators/AddConceptEvaluator.py index b1abfce..dd4fa8a 100644 --- a/src/evaluators/AddConceptEvaluator.py +++ b/src/evaluators/AddConceptEvaluator.py @@ -61,7 +61,7 @@ class AddConceptEvaluator(OneReturnValueEvaluator): if not isinstance(part_ret_val, ReturnValueConcept) or not part_ret_val.status: continue # Nothing to do is not initialized - # update the parts + # update the metadata source = self.get_source(part_ret_val) setattr(concept.metadata, prop, source) diff --git a/src/evaluators/EvalEvaluator.py b/src/evaluators/EvalEvaluator.py index f84703a..541501e 100644 --- a/src/evaluators/EvalEvaluator.py +++ b/src/evaluators/EvalEvaluator.py @@ -29,13 +29,21 @@ class EvalEvaluator(AllReturnValuesEvaluator): for ret_val in return_values: if ret_val.status and isinstance(ret_val.body, Concept) and ret_val.body.body: - context.log(self.verbose_log, f"Evaluating {ret_val}", who=self) + context.log(self.verbose_log, f"Evaluating {ret_val.body}", who=self) result.append(sheerka.ret(self.name, True, ret_val.body.body, parents=[ret_val, self.eval_requested])) + elif ret_val.status and sheerka.isaset(context, ret_val.body): + context.log(self.verbose_log, f"Evaluating set {ret_val.body}", who=self) + result.append(sheerka.ret( + self.name, + True, + sheerka.get_set_elements(context, ret_val.body), + parents=[ret_val, self.eval_requested])) if len(result) > 0: return result else: # suppress the successful BuiltinConcepts.CONCEPT_EVAL_REQUESTED + # status of CONCEPT_EVAL_REQUESTED is now set to False return sheerka.ret( self.name, False, diff --git a/src/evaluators/PythonEvaluator.py b/src/evaluators/PythonEvaluator.py index 64166f2..7c705ad 100644 --- a/src/evaluators/PythonEvaluator.py +++ b/src/evaluators/PythonEvaluator.py @@ -42,17 +42,18 @@ class PythonEvaluator(OneReturnValueEvaluator): return sheerka.ret(self.name, False, not_for_me, parents=[return_value]) # get locals - my_locals = self.get_locals(context, node) - context.log(self.verbose_log, f"locals={my_locals}", self.name) + my_globals = self.get_globals(context, node) + my_locals = {} + context.log(self.verbose_log, f"globals={my_globals}", self.name) # eval if isinstance(node.ast_, ast.Expression): context.log(self.verbose_log, "Evaluating using 'eval'.", self.name) compiled = compile(node.ast_, "", "eval") - evaluated = eval(compiled, {}, my_locals) + evaluated = eval(compiled, my_globals, my_locals) else: context.log(self.verbose_log, "Evaluating using 'exec'.", self.name) - evaluated = self.exec_with_return(node.ast_, my_locals) + evaluated = self.exec_with_return(node.ast_, my_globals, my_locals) context.log(self.verbose_log, f"{evaluated=}", self.name) return sheerka.ret(self.name, True, evaluated, parents=[return_value]) @@ -62,7 +63,7 @@ class PythonEvaluator(OneReturnValueEvaluator): error = sheerka.new(BuiltinConcepts.ERROR, body=error) return sheerka.ret(self.name, False, error, parents=[return_value]) - def get_locals(self, context, node): + def get_globals(self, context, node): my_locals = { "sheerka": context.sheerka, "desc": context.sheerka.dump_handler.dump_desc, @@ -70,6 +71,7 @@ class PythonEvaluator(OneReturnValueEvaluator): "definitions": context.sheerka.dump_handler.dump_definitions, "history": context.sheerka.dump_handler.dump_history, "state": context.sheerka.dump_handler.dump_state, + "Concept": core.concept.Concept } if context.obj: context.log(self.verbose_log, @@ -189,7 +191,7 @@ class PythonEvaluator(OneReturnValueEvaluator): return result - def exec_with_return(self, code_ast, my_locals): + def exec_with_return(self, code_ast, my_globals, my_locals): init_ast = copy.deepcopy(code_ast) init_ast.body = code_ast.body[:-1] @@ -199,6 +201,6 @@ class PythonEvaluator(OneReturnValueEvaluator): exec(compile(init_ast, "", "exec"), {}, my_locals) if type(last_ast.body[0]) == ast.Expr: - return eval(compile(self.expr_to_expression(last_ast.body[0]), "", "eval"), {}, my_locals) + return eval(compile(self.expr_to_expression(last_ast.body[0]), "", "eval"), my_globals, my_locals) else: - exec(compile(last_ast, "", "exec"), {}, my_locals) + exec(compile(last_ast, "", "exec"), my_globals, my_locals) diff --git a/src/parsers/BnfParser.py b/src/parsers/BnfParser.py index c8a8628..3067f0a 100644 --- a/src/parsers/BnfParser.py +++ b/src/parsers/BnfParser.py @@ -236,7 +236,8 @@ class BnfParser(BaseParser): if token.type == TokenKind.CONCEPT: self.next_token() concept = self.sheerka.new((token.value[0], token.value[1])) - expr = ConceptGroupExpression(concept) if self.sheerka.isaset(concept) else ConceptExpression(concept) + expr = ConceptGroupExpression(concept) if self.sheerka.isaset(self.context, concept) \ + else ConceptExpression(concept) return self.eat_rule_name_if_needed(expr) if token.type == TokenKind.IDENTIFIER: @@ -260,7 +261,8 @@ class BnfParser(BaseParser): body=("key", concept_name))) return None else: - expr = ConceptGroupExpression(concept) if self.sheerka.isaset(concept) else ConceptExpression(concept) + expr = ConceptGroupExpression(concept) if self.sheerka.isaset(self.context, concept) \ + else ConceptExpression(concept) expr.rule_name = concept.name return expr diff --git a/src/parsers/ConceptLexerParser.py b/src/parsers/ConceptLexerParser.py index 284d3fd..ffe5df1 100644 --- a/src/parsers/ConceptLexerParser.py +++ b/src/parsers/ConceptLexerParser.py @@ -318,7 +318,7 @@ class ConceptGroupExpression(ConceptExpression): self.concept = to_match # Memoize if to_match not in parser.concepts_grammars: - concepts_in_group = parser.sheerka.get_set_elements(self.concept) + concepts_in_group = parser.sheerka.get_set_elements(parser.context, self.concept) nodes = [ConceptExpression(c, rule_name=c.name) for c in concepts_in_group] expr = OrderedChoice(nodes) expr.nodes = nodes @@ -697,7 +697,7 @@ class ConceptLexerParser(BaseParser): # A copy must be created def inner_get_model(expression): if isinstance(expression, Concept): - if self.sheerka.isaset(expression): + if self.sheerka.isaset(self.context, expression): ret = ConceptGroupExpression(expression, rule_name=expression.name) else: ret = ConceptExpression(expression, rule_name=expression.name) diff --git a/src/parsers/DefaultParser.py b/src/parsers/DefaultParser.py index 5837227..b09e367 100644 --- a/src/parsers/DefaultParser.py +++ b/src/parsers/DefaultParser.py @@ -246,7 +246,7 @@ class DefaultParser(BaseParser): # the definition of a concept consists of several parts # Keywords.CONCEPT to get the name of the concept - # Keywords.FROM [Keywords.REGEX] to get the definition of the concept + # Keywords.FROM [Keywords.BNF] to get the definition of the concept # Keywords.AS to get the body # Keywords.WHERE to get the conditions to recognize for the variables # Keywords.PRE to know if the conditions to evaluate the concept diff --git a/src/sdp/sheerkaDataProvider.py b/src/sdp/sheerkaDataProvider.py index 1c47ee6..4666889 100644 --- a/src/sdp/sheerkaDataProvider.py +++ b/src/sdp/sheerkaDataProvider.py @@ -294,16 +294,18 @@ class SheerkaDataProvider: KeysFile = "keys" REF_PREFIX = "##REF##:" - def __init__(self, root=None): + def __init__(self, root=None, sheerka=None): self.log = get_logger(__name__) self.init_log = get_logger("init." + __name__) self.init_log.debug("Initializing sdp.") + self.sheerka = sheerka self.io = SheerkaDataProviderIO.get(root) self.first_time = self.io.first_time self.serializer = Serializer() + @staticmethod def get_obj_key(obj): """ @@ -729,13 +731,12 @@ class SheerkaDataProvider: digest = event.parents[0] count += 1 - def save_result(self, sheerka, execution_context): + def save_result(self, execution_context): """ Save the execution context associated with an event To make a long story short, for every single user input, there is an event (which is the first thing that is created) and a result (the ExecutionContext created by sheerka.evaluate_user_input() - :param sheerka: :param execution_context: :return: """ @@ -745,15 +746,15 @@ class SheerkaDataProvider: if self.io.exists(target_path): return digest - context = SerializerContext(sheerka=sheerka) + context = SerializerContext(sheerka=self.sheerka) self.io.write_binary(target_path, self.serializer.serialize(execution_context, context).read()) return digest - def load_result(self, sheerka, digest): + def load_result(self, digest): target_path = self.io.get_obj_path(SheerkaDataProvider.EventFolder, digest) + "_result" with self.io.open(target_path, "rb") as f: - context = SerializerContext(sheerka=sheerka) + context = SerializerContext(sheerka=self.sheerka) return self.serializer.deserialize(f, context) def save_state(self, state: State): @@ -763,7 +764,8 @@ class SheerkaDataProvider: if self.io.exists(target_path): return digest - self.io.write_binary(target_path, self.serializer.serialize(state, None).read()) + context = SerializerContext(sheerka=self.sheerka) + self.io.write_binary(target_path, self.serializer.serialize(state, context).read()) return digest def load_state(self, digest): @@ -772,11 +774,13 @@ class SheerkaDataProvider: target_path = self.io.get_obj_path(SheerkaDataProvider.StateFolder, digest) with self.io.open(target_path, "rb") as f: - return self.serializer.deserialize(f, None) + context = SerializerContext(sheerka=self.sheerka) + return self.serializer.deserialize(f, context) def save_obj(self, obj): self.log.debug(f"Saving '{obj}' as reference...") - stream = self.serializer.serialize(obj, SerializerContext(user_name="kodjo")) + context = SerializerContext(user_name="kodjo", sheerka=self.sheerka) + stream = self.serializer.serialize(obj, context) digest = obj.get_digest() if hasattr(obj, "get_digest") else self.get_stream_digest(stream) target_path = self.io.get_obj_path(SheerkaDataProvider.ObjectsFolder, digest) @@ -798,7 +802,8 @@ class SheerkaDataProvider: return None with self.io.open(target_path, "rb") as f: - obj = self.serializer.deserialize(f, SerializerContext(origin=digest)) + context = SerializerContext(origin=digest, sheerka=self.sheerka) + obj = self.serializer.deserialize(f, context) # set the origin of the object if add_origin: diff --git a/src/sdp/sheerkaSerializer.py b/src/sdp/sheerkaSerializer.py index 2ceddfc..949571f 100644 --- a/src/sdp/sheerkaSerializer.py +++ b/src/sdp/sheerkaSerializer.py @@ -231,13 +231,24 @@ class StateSerializer(PickleSerializer): 1) -class ConceptSerializer(JsonSerializer): +class ConceptSerializer(BaseSerializer): + def __init__(self): - JsonSerializer.__init__(self, "core.concept.Concept", "C", 1) + BaseSerializer.__init__(self, "C", 1) def matches(self, obj): return isinstance(obj, Concept) + def dump(self, stream, obj, context): + stream.write(sheerkapickle.encode(context.sheerka, obj).encode("utf-8")) + stream.seek(0) + return stream + + def load(self, stream, context): + json_stream = stream.read().decode("utf-8") + obj = sheerkapickle.decode(context.sheerka, json_stream) + return obj + class DictionarySerializer(BaseSerializer): def __init__(self): @@ -275,7 +286,7 @@ class ExecutionContextSerializer(BaseSerializer): def load(self, stream, context): json_stream = stream.read().decode("utf-8") obj = sheerkapickle.decode(context.sheerka, json_stream) - #json_message = json.loads(json_stream) + # json_message = json.loads(json_stream) return obj # diff --git a/src/sheerkapickle/sheerka_handlers.py b/src/sheerkapickle/sheerka_handlers.py index 9670fa6..0d23c6e 100644 --- a/src/sheerkapickle/sheerka_handlers.py +++ b/src/sheerkapickle/sheerka_handlers.py @@ -16,11 +16,11 @@ class ConceptHandler(BaseHandler): pickler = self.context sheerka = self.sheerka - if obj.id: + if obj.metadata.full_serialization: + ref = default_concept + else: ref = sheerka.get_by_id(obj.id) data[CONCEPT_ID] = (obj.key, obj.id) - else: - ref = default_concept # transform metadata for prop in CONCEPT_PROPERTIES_TO_SERIALIZE: @@ -41,7 +41,7 @@ class ConceptHandler(BaseHandler): if prop not in ref.props or value != ref.props[prop].value: if "props" not in data: data["props"] = [] - data["props"].append((prop, pickler.flatten(value))) + data["props"].append((pickler.flatten(prop), pickler.flatten(value))) return data @@ -77,7 +77,7 @@ class ConceptHandler(BaseHandler): return instance -class UserInputHandler(BaseHandler): +class UserInputHandler(ConceptHandler): def flatten(self, obj: UserInputConcept, data): data[CONCEPT_ID] = (obj.key, obj.id) @@ -86,12 +86,12 @@ class UserInputHandler(BaseHandler): return data def new(self, data): - sheerka = self.sheerka - - instance = sheerka.new(tuple(data[CONCEPT_ID]), body=data["text"], user_name=data["user_name"]) - return instance + return UserInputConcept.__new__(UserInputConcept) def restore(self, data, instance): + instance.__init__(data["text"], data["user_name"]) + instance.metadata.key = data[CONCEPT_ID][0] + instance.metadata.id = data[CONCEPT_ID][1] return instance @@ -100,6 +100,7 @@ class ReturnValueHandler(BaseHandler): def flatten(self, obj: ReturnValueConcept, data): pickler = self.context + data[CONCEPT_ID] = (obj.key, obj.id) data["who"] = f"c:{obj.who.id}:" if isinstance(obj.who, Concept) else \ obj.who.name if isinstance(obj.who, (BaseParser, BaseEvaluator)) else \ obj.who @@ -110,38 +111,30 @@ class ReturnValueHandler(BaseHandler): return data def new(self, data): - sheerka = self.sheerka - instance = sheerka.ret(data["who"], data["status"], None) - return instance + return ReturnValueConcept.__new__(ReturnValueConcept) def restore(self, data, instance): pickler = self.context - instance.value = pickler.restore(data["value"]) + instance.__init__(data["who"], data["status"], pickler.restore(data["value"])) if "parents" in data: instance.parents = pickler.restore(data["parents"]) + + instance.metadata.key = data[CONCEPT_ID][0] + instance.metadata.id = data[CONCEPT_ID][1] return instance -# class BuiltinConceptsHandler(BaseHandler): -# -# def flatten(self, obj: BuiltinConcepts, data): -# return data -# -# def restore(self, obj): -# pass +class SheerkaHandler(ConceptHandler): - -class SheerkaHandler(BaseHandler): - - def flatten(self, obj: BuiltinConcepts, data): - return data + # def flatten(self, obj: BuiltinConcepts, data): + # return ConceptHandler().flatten()data def new(self, data): return self.sheerka - def restore(self, data, instance): - return instance + # def restore(self, data, instance): + # return instance class ExecutionContextHandler(BaseHandler): diff --git a/tests/BaseTest.py b/tests/BaseTest.py index 0ac8234..5896084 100644 --- a/tests/BaseTest.py +++ b/tests/BaseTest.py @@ -3,11 +3,12 @@ import ast from core.builtin_concepts import ReturnValueConcept, ParserResultConcept from core.concept import Concept from core.sheerka.ExecutionContext import ExecutionContext +from parsers.BnfParser import BnfParser from sdp.sheerkaDataProvider import Event class BaseTest: - def get_sheerka(self): + def get_sheerka(self, **kwargs): pass def get_context(self, sheerka=None): @@ -32,6 +33,30 @@ class BaseTest: dump = dump.replace(to_remove, "") return dump + def init_concepts(self, *concepts, **kwargs): + sheerka = self.get_sheerka(**kwargs) + context = self.get_context(sheerka) + + result = [] + for c in concepts: + if isinstance(c, str): + c = Concept(c) + c.init_key() + sheerka.set_id_if_needed(c, False) + + # manage concepts with bnf definitions + if c.metadata.definition: + bnf_parser = BnfParser() + res = bnf_parser.parse(context, c.metadata.definition) + if res.status: + c.bnf = res.value.value + sheerka.create_new_concept(context, c) + + sheerka.add_in_cache(c) + result.append(c) + + return sheerka, context, *result + @staticmethod def retval(obj, who="who", status=True): """ret_val""" diff --git a/tests/TestUsingFileBasedSheerka.py b/tests/TestUsingFileBasedSheerka.py index cd3e620..6c23638 100644 --- a/tests/TestUsingFileBasedSheerka.py +++ b/tests/TestUsingFileBasedSheerka.py @@ -26,7 +26,9 @@ class TestUsingFileBasedSheerka(BaseTest): os.chdir(current_pwd) - def get_sheerka(self, use_dict=True, skip_builtins_in_db=True): + def get_sheerka(self, **kwargs): + use_dict = kwargs.get("use_dict", True) + skip_builtins_in_db = kwargs.get("skip_builtins_in_db", True) root = "mem://" if use_dict else self.root_folder sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db) sheerka.initialize(root) diff --git a/tests/TestUsingMemoryBasedSheerka.py b/tests/TestUsingMemoryBasedSheerka.py index 8c9b37e..01deeaf 100644 --- a/tests/TestUsingMemoryBasedSheerka.py +++ b/tests/TestUsingMemoryBasedSheerka.py @@ -4,7 +4,8 @@ from tests.BaseTest import BaseTest class TestUsingMemoryBasedSheerka(BaseTest): - def get_sheerka(self, skip_builtins_in_db=True): + def get_sheerka(self, **kwargs): + skip_builtins_in_db = kwargs.get("skip_builtins_in_db", True) sheerka = Sheerka(skip_builtins_in_db=skip_builtins_in_db) sheerka.initialize("mem://") return sheerka diff --git a/tests/core/test_SheerkaEvaluateConcept.py b/tests/core/test_SheerkaEvaluateConcept.py index 55b44b0..7d480fb 100644 --- a/tests/core/test_SheerkaEvaluateConcept.py +++ b/tests/core/test_SheerkaEvaluateConcept.py @@ -457,3 +457,4 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka): evaluated = sheerka.evaluate_concept(self.get_context(sheerka), one) assert evaluated.key == one.key assert evaluated.body == 1 + diff --git a/tests/core/test_SheerkaSetsManager.py b/tests/core/test_SheerkaSetsManager.py index 61e9663..56a5f83 100644 --- a/tests/core/test_SheerkaSetsManager.py +++ b/tests/core/test_SheerkaSetsManager.py @@ -6,41 +6,39 @@ from tests.TestUsingFileBasedSheerka import TestUsingFileBasedSheerka class TestSheerkaSetsManager(TestUsingFileBasedSheerka): - def init(self, use_dict, *concepts): - sheerka = self.get_sheerka(use_dict, True) - for c in concepts: - sheerka.set_id_if_needed(c, False) - sheerka.add_in_cache(c) - - context = self.get_context(sheerka) - return sheerka, context + # def init(self, use_dict, *concepts): + # sheerka = self.get_sheerka(use_dict, True) + # for c in concepts: + # sheerka.set_id_if_needed(c, False) + # sheerka.add_in_cache(c) + # + # context = self.get_context(sheerka) + # return sheerka, context def test_i_can_add_concept_to_set(self): - foo = Concept("foo") - all_foos = Concept("all_foos") - sheerka, context = self.init(False, foo, all_foos) + sheerka, context, foo, all_foos = self.init_concepts(Concept("foo"), Concept("all_foo"), use_dict=False) res = sheerka.add_concept_to_set(context, foo, all_foos) assert res.status assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS) - all_entries = self.get_sheerka(False, True).sdp.get("All_" + all_foos.id, None, False) + all_entries = self.get_sheerka(use_dict=False).sdp.get("All_" + all_foos.id, None, False) assert len(all_entries) == 1 assert foo.id in all_entries def test_i_can_add_several_concepts_to_set(self): - foo1 = Concept("foo1") - foo2 = Concept("foo2") - all_foos = Concept("all_foos") - sheerka, context = self.init(False, foo1, foo2, all_foos) + sheerka, context, foo1, foo2, all_foos = self.init_concepts( + Concept("foo1"), + Concept("foo2"), + Concept("all_foo"), use_dict=False) res = sheerka.sets_handler.add_concepts_to_set(context, (foo1, foo2), all_foos) assert res.status assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS) - all_entries = self.get_sheerka(False, False).sdp.get("All_" + all_foos.id, None, False) + all_entries = self.get_sheerka(use_dict=False).sdp.get("All_" + all_foos.id, None, False) assert len(all_entries) == 2 assert foo1.id in all_entries assert foo2.id in all_entries @@ -57,7 +55,7 @@ class TestSheerkaSetsManager(TestUsingFileBasedSheerka): assert res.status assert sheerka.isinstance(res.body, BuiltinConcepts.SUCCESS) - all_entries = self.get_sheerka(False, False).sdp.get("All_" + all_foos.id, None, False) + all_entries = self.get_sheerka(use_dict=False).sdp.get("All_" + all_foos.id, None, False) assert len(all_entries) == 4 assert foo1.id in all_entries assert foo2.id in all_entries @@ -65,9 +63,7 @@ class TestSheerkaSetsManager(TestUsingFileBasedSheerka): assert foo4.id in all_entries def test_i_cannot_add_the_same_concept_twice_in_a_set(self): - foo = Concept("foo") - all_foos = Concept("all_foos") - sheerka, context = self.init(True, foo, all_foos) + sheerka, context, foo, all_foos = self.init_concepts(Concept("foo"), Concept("all_foos")) sheerka.add_concept_to_set(context, foo, all_foos) res = sheerka.add_concept_to_set(context, foo, all_foos) @@ -80,40 +76,179 @@ class TestSheerkaSetsManager(TestUsingFileBasedSheerka): assert foo.id in all_entries def test_i_get_elements_from_a_set(self): - one = Concept("one") - two = Concept("two") - three = Concept("three") - number = Concept("number") - sheerka, context = self.init(True, one, two, three, number) + sheerka, context, one, two, three, number = self.init_concepts( + Concept("one"), Concept("two"), Concept("three"), Concept("number")) for c in [one, two, three]: sheerka.add_concept_to_set(context, c, number) - elements = sheerka.get_set_elements(number) + elements = sheerka.get_set_elements(context, number) assert set(elements) == {one, two, three} def test_i_cannot_get_elements_if_not_a_set(self): - one = Concept("one") - sheerka, context = self.init(True, one) + sheerka, context, one = self.init_concepts(Concept("one")) - error = sheerka.get_set_elements(one) + error = sheerka.get_set_elements(context, one) assert sheerka.isinstance(error, BuiltinConcepts.NOT_A_SET) assert error.body == one def test_isa_and_isa_group(self): - group = Concept("group") - foo = Concept("foo") - sheerka, context = self.init(True, group, foo) + sheerka, context, group, foo = self.init_concepts(Concept("group"), Concept("foo")) - assert not sheerka.isaset(group) - assert not sheerka.isa(foo, group) + assert not sheerka.isaset(context, group) + assert not sheerka.isinset(foo, group) context = self.get_context(sheerka) sheerka.add_concept_to_set(context, foo, group) - assert sheerka.isaset(group) - assert sheerka.isa(foo, group) + assert sheerka.isaset(context, group) + assert sheerka.isinset(foo, group) - def test_i_can_a_multiples_concepts(self): - pass + def test_i_can_define_a_group_from_another_group(self): + sheerka, context, foo, bar, group1, group2 = self.init_concepts( + Concept("foo"), Concept("bar"), Concept("group1"), Concept("group2", body="group1")) + + sheerka.sets_handler.add_concepts_to_set(context, [foo, bar], group1) + + assert sheerka.isaset(context, group2) + assert sheerka.get_set_elements(context, group2) == [foo, bar] + + def test_i_can_define_subset_of_another_group(self): + sheerka, context, one, two, three, four, five, number, sub_number = self.init_concepts( + Concept("one", body="1"), + Concept("two", body="2"), + Concept("three", body="3"), + Concept("four", body="4"), + Concept("five", body="5"), + Concept("number"), + Concept("sub_number", body="number", where="number < 4") + ) + sheerka.sets_handler.add_concepts_to_set(context, [one, two, three, four, five], number) + + assert sheerka.isaset(context, sub_number) + assert sheerka.get_set_elements(context, sub_number) == [one, two, three] + + def test_i_can_define_subset_of_another_set_when_some_concept_dont_have_a_defined_body(self): + """ + Example + def concept unit from number where number <10 + It implies that all elements in number have a (numeric) body + What if its not the case ? + :return: + """ + + sheerka, context, one, two, three, four, five, number, sub_number = self.init_concepts( + Concept("one", body="1"), + Concept("two"), + Concept("three", body="3"), + Concept("four"), + Concept("five", body="5"), + Concept("number"), + Concept("sub_number", body="number", where="number < 4") + ) + sheerka.sets_handler.add_concepts_to_set(context, [one, two, three, four, five], number) + + assert sheerka.isaset(context, sub_number) + assert sheerka.get_set_elements(context, sub_number) == [one, three] + + def test_i_can_define_subset_of_another_set_when_some_concept_are_bnf(self): + """ + Other case: + twenties isa number, but its body is a source code, not a constant numeric number + is it included ? + :return: + """ + + sheerka, context, one, two, twenty, twenties, number, sub_number = self.init_concepts( + Concept("one", body="1"), + Concept("two", body="2"), + Concept("twenty", body="20"), + Concept("twenties", definition="twenty (one|two)=unit", body="twenty + unit"), + Concept("number"), + Concept("sub_number", body="number", where="number >= 20") + ) + + sheerka.sets_handler.add_concepts_to_set(context, [one, two, twenty, twenties], number) + + assert sheerka.isaset(context, sub_number) + assert sheerka.get_set_elements(context, sub_number) == [twenty] # what is expected ? + + def test_bnf_elements_can_be_part_of_a_set(self): + """ + The purpose of this test is + def concept twenties from bnf ... + twenties isa number + + assert twenty two isa number + We want that all elements of the twenties are number + :return: + """ + + sheerka, context, one, two, twenty, twenties, number = self.init_concepts( + Concept("one", body="1"), + Concept("two", body="2"), + Concept("twenty", body="20"), + Concept("twenties", definition="twenty (one|two)=unit", body="twenty + unit").def_prop("unit"), + Concept("number"), + ) + + sheerka.sets_handler.add_concepts_to_set(context, [one, two, twenty, twenties], number) + assert sheerka.isinset(twenties, number) + + twenty_one = sheerka.evaluate_user_input("twenty one", "")[0].body + assert sheerka.isinset(twenty_one, number) + + def test_i_can_set_isa(self): + sheerka, context, foo, all_foos = self.init_concepts(Concept("foo"), Concept("all_foo"), use_dict=False) + sheerka.create_new_concept(context, foo) + + assert BuiltinConcepts.ISA not in foo.props + + sheerka.set_isa(context, foo, all_foos) + + assert foo.get_prop(BuiltinConcepts.ISA) == [all_foos] + assert sheerka.isa(foo, all_foos) + assert sheerka.isinset(foo, all_foos) + assert sheerka.isaset(context, all_foos) + + def test_a_concept_can_be_in_multiple_sets(self): + sheerka, context, foo, all_foos, all_bars = self.init_concepts( + Concept("foo"), + Concept("all_foo"), + Concept("all_bar"), + use_dict=False) + sheerka.create_new_concept(context, foo) + + sheerka.set_isa(context, foo, all_foos) + sheerka.set_isa(context, foo, all_bars) + + assert foo.get_prop(BuiltinConcepts.ISA) == [all_foos, all_bars] + assert sheerka.isa(foo, all_foos) + assert sheerka.isa(foo, all_bars) + assert sheerka.isinset(foo, all_foos) + assert sheerka.isinset(foo, all_bars) + assert sheerka.isaset(context, all_foos) + assert sheerka.isaset(context, all_bars) + + def test_i_can_manage_isa_transitivity(self): + """ + if foo isa bar and bar isa baz, then foo isa baz + :return: + """ + + sheerka, context, foo, bar, baz = self.init_concepts( + Concept("foo"), + Concept("bar"), + Concept("baz"), + ) + sheerka.create_new_concept(context, foo) + sheerka.create_new_concept(context, bar) + sheerka.create_new_concept(context, baz) + + sheerka.set_isa(context, foo, bar) + sheerka.set_isa(context, bar, baz) + + assert sheerka.isa(foo, bar) + assert sheerka.isa(bar, baz) + assert sheerka.isa(foo, baz) diff --git a/tests/core/test_ast.py b/tests/core/test_ast.py index 2e73bd6..e44cb38 100644 --- a/tests/core/test_ast.py +++ b/tests/core/test_ast.py @@ -42,7 +42,7 @@ def my_function(a,b): assert tree_as_concept.node_type == "Module" assert sheerka.isinstance(tree_as_concept.get_prop("body"), BuiltinConcepts.LIST) - def_func = tree_as_concept.get_prop("body")[0] + def_func = tree_as_concept.get_prop("body").body[0] assert sheerka.isinstance(def_func, BuiltinConcepts.GENERIC_NODE) assert def_func.node_type == "FunctionDef" assert def_func.parent == NodeParent(tree_as_concept, "body") @@ -54,27 +54,27 @@ def my_function(a,b): def_func_args_real_args = def_func_args.get_prop("args") assert sheerka.isinstance(def_func_args_real_args, BuiltinConcepts.LIST) - assert len(def_func_args_real_args) == 2 + assert len(def_func_args_real_args.body) == 2 - assert sheerka.isinstance(def_func_args_real_args[0], BuiltinConcepts.GENERIC_NODE) - assert def_func_args_real_args[0].node_type == "arg" - assert def_func_args_real_args[0].parent == NodeParent(def_func_args, "args") - assert def_func_args_real_args[0].get_prop("arg") == "a" - assert sheerka.isinstance(def_func_args_real_args[1], BuiltinConcepts.GENERIC_NODE) - assert def_func_args_real_args[1].node_type == "arg" - assert def_func_args_real_args[1].parent == NodeParent(def_func_args, "args") - assert def_func_args_real_args[1].get_prop("arg") == "b" + assert sheerka.isinstance(def_func_args_real_args.body[0], BuiltinConcepts.GENERIC_NODE) + assert def_func_args_real_args.body[0].node_type == "arg" + assert def_func_args_real_args.body[0].parent == NodeParent(def_func_args, "args") + assert def_func_args_real_args.body[0].get_prop("arg") == "a" + assert sheerka.isinstance(def_func_args_real_args.body[1], BuiltinConcepts.GENERIC_NODE) + assert def_func_args_real_args.body[1].node_type == "arg" + assert def_func_args_real_args.body[1].parent == NodeParent(def_func_args, "args") + assert def_func_args_real_args.body[1].get_prop("arg") == "b" def_fun_body = def_func.get_prop("body") assert sheerka.isinstance(def_fun_body, BuiltinConcepts.LIST) - assert len(def_fun_body) == 2 + assert len(def_fun_body.body) == 2 - def_fun_body_for = def_fun_body[0] + def_fun_body_for = def_fun_body.body[0] assert sheerka.isinstance(def_fun_body_for, BuiltinConcepts.GENERIC_NODE) assert def_fun_body_for.node_type == "For" assert def_fun_body_for.parent == NodeParent(def_func, "body") - def_fun_body_return = def_fun_body[1] + def_fun_body_return = def_fun_body.body[1] assert sheerka.isinstance(def_fun_body_return, BuiltinConcepts.GENERIC_NODE) assert def_fun_body_return.node_type == "Return" assert def_fun_body_return.parent == NodeParent(def_func, "body") diff --git a/tests/core/test_builtin_helpers.py b/tests/core/test_builtin_helpers.py index 14dc593..92f93f8 100644 --- a/tests/core/test_builtin_helpers.py +++ b/tests/core/test_builtin_helpers.py @@ -134,6 +134,7 @@ class TestBuiltinHelpers(TestUsingMemoryBasedSheerka): ("a == 1 or b == 2", ["b"], [], ["b == 2"]), ("a == 1 or b == 2", ["a", "b"], [], ["a == 1 or b == 2"]), ("predicate(a,c) or predicate(b,c)", ["a", "b"], [], ["predicate(a,c) or predicate(b,c)"]), + ("a < 1 and a > b", ["a"], [], ["a < 1 and a > b"]), ]) def test_i_can_extract_predicates(self, expression, vars_to_include, vars_to_exclude, expected_expr): sheerka = self.get_sheerka() diff --git a/tests/core/test_sheerka.py b/tests/core/test_sheerka.py index 80e8eac..c6ab4ea 100644 --- a/tests/core/test_sheerka.py +++ b/tests/core/test_sheerka.py @@ -38,12 +38,12 @@ class TestSheerka(TestUsingFileBasedSheerka): assert isinstance(sheerka.cache_by_key[key], concept_class) def test_builtin_concepts_can_be_updated(self): - sheerka = self.get_sheerka(False, False) + sheerka = self.get_sheerka(use_dict=False, skip_builtins_in_db=False) loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA) loaded_sheerka.metadata.desc = "I have a description" sheerka.sdp.modify("Test", sheerka.CONCEPTS_ENTRY, loaded_sheerka.key, loaded_sheerka) - sheerka = self.get_sheerka(False, False) + sheerka = self.get_sheerka(use_dict=False, skip_builtins_in_db=False) loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA) assert loaded_sheerka.metadata.desc == "I have a description" @@ -233,7 +233,7 @@ class TestSheerka(TestUsingFileBasedSheerka): assert sheerka.value(concept, reduce_simple_list) == expected def test_list_of_concept_is_sorted_by_id(self): - sheerka = self.get_sheerka(False, False) + sheerka = self.get_sheerka(use_dict=False, skip_builtins_in_db=False) concepts = sheerka.concepts() assert concepts[0].id < concepts[-1].id diff --git a/tests/evaluators/test_EvalEvaluator.py b/tests/evaluators/test_EvalEvaluator.py index d02f868..365350e 100644 --- a/tests/evaluators/test_EvalEvaluator.py +++ b/tests/evaluators/test_EvalEvaluator.py @@ -70,3 +70,21 @@ class TestEvalEvaluator(TestUsingMemoryBasedSheerka): False, context.sheerka.new(BuiltinConcepts.CONCEPT_EVAL_REQUESTED)) assert evaluated.parents == [eval_requested] + + def test_i_can_evaluate_sets(self): + sheerka, context, foo, bar, baz, number = self.init_concepts( + Concept("foo"), + Concept("bar"), + Concept("baz"), + Concept("number")) + sheerka.sets_handler.add_concepts_to_set(context, [foo, bar, baz], number) + + return_values = [retval(number), eval_requested] + + evaluator = EvalEvaluator() + evaluator.matches(context, return_values) + evaluated = evaluator.eval(context, return_values) + + assert len(evaluated) == 1 + assert evaluated[0].status + assert evaluated[0].body == [foo, bar, baz] diff --git a/tests/non_reg/test_sheerka_non_reg.py b/tests/non_reg/test_sheerka_non_reg.py index f4b0f05..92c6a54 100644 --- a/tests/non_reg/test_sheerka_non_reg.py +++ b/tests/non_reg/test_sheerka_non_reg.py @@ -255,7 +255,7 @@ as: assert isinstance(res[0].value.body, list) def test_i_can_create_concept_with_bnf_definition(self): - sheerka = self.get_sheerka(False, False) + sheerka = self.get_sheerka(use_dict=False, skip_builtins_in_db=False) a = Concept("a") sheerka.add_in_cache(a) sheerka.concepts_definition_cache = {a: OrderedChoice("one", "two")} @@ -317,10 +317,10 @@ as: Same test then before, but make sure that the BNF are correctly persisted and loaded """ - sheerka = self.get_sheerka(False) + sheerka = self.get_sheerka(use_dict=False) concept_a = sheerka.evaluate_user_input("def concept a from bnf 'one' 'two'")[0].body.body - res = self.get_sheerka(False).evaluate_user_input("one two") + res = self.get_sheerka(use_dict=False).evaluate_user_input("one two") assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_a) @@ -328,12 +328,12 @@ as: # add another bnf definition concept_b = sheerka.evaluate_user_input("def concept b from bnf a 'three'")[0].body.body - res = self.get_sheerka(False).evaluate_user_input("one two") # previous one still works + res = self.get_sheerka(use_dict=False).evaluate_user_input("one two") # previous one still works assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_a) - res = self.get_sheerka(False).evaluate_user_input("one two three") # new one works + res = self.get_sheerka(use_dict=False).evaluate_user_input("one two three") # new one works assert len(res) == 1 assert res[0].status assert sheerka.isinstance(res[0].value, concept_b) diff --git a/tests/sheerkapickle/test_SheerkaPickler.py b/tests/sheerkapickle/test_SheerkaPickler.py index c7dff5a..66ed573 100644 --- a/tests/sheerkapickle/test_SheerkaPickler.py +++ b/tests/sheerkapickle/test_SheerkaPickler.py @@ -151,15 +151,6 @@ class TestSheerkaPickler(TestUsingFileBasedSheerka): decoded = SheerkaUnpickler(sheerka).restore(flatten) assert decoded == obj - def test_serialize_concept(self): - sheerka = self.get_sheerka() - - foo = Concept("doo") - flatten = SheerkaPickler(sheerka).flatten(foo) - restored = SheerkaUnpickler(sheerka).restore(flatten) - - assert restored == foo - def test_i_do_not_encode_logger(self): sheerka = self.get_sheerka() diff --git a/tests/sheerkapickle/test_sheerka_handlers.py b/tests/sheerkapickle/test_sheerka_handlers.py index fbbefd6..c4d8b33 100644 --- a/tests/sheerkapickle/test_sheerka_handlers.py +++ b/tests/sheerkapickle/test_sheerka_handlers.py @@ -10,85 +10,98 @@ from sdp.sheerkaDataProvider import Event from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka +def set_full_serialization(concept): + concept.metadata.full_serialization = True + return concept + + class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka): def test_i_can_encode_decode_unknown_concept_metadata(self): sheerka = self.get_sheerka() - concept = Concept(name="foo", key="my_key") + concept = set_full_serialization(Concept(name="foo", key="my_key")) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "meta.key": "my_key"}' assert decoded == concept - concept = Concept("foo", is_builtin=True, is_unique=True) + concept = set_full_serialization(Concept("foo", is_builtin=True, is_unique=True)) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "meta.is_builtin": true, "meta.is_unique": true}' - concept = Concept("foo", body="my_body") + concept = set_full_serialization(Concept("foo", body="my_body")) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "meta.body": "my_body"}' - concept = Concept("foo", pre="my_pre") + concept = set_full_serialization(Concept("foo", pre="my_pre")) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "meta.pre": "my_pre"}' - concept = Concept("foo", post="my_post") + concept = set_full_serialization(Concept("foo", post="my_post")) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "meta.post": "my_post"}' - concept = Concept("foo", where="my_where") + concept = set_full_serialization(Concept("foo", where="my_where")) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "meta.where": "my_where"}' - concept = Concept("foo").def_prop("a", "value_a").def_prop("b", "value_b") + concept = set_full_serialization(Concept("foo").def_prop("a", "value_a").def_prop("b", "value_b")) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept + concept = Concept("foo").init_key() + sheerka.create_new_concept(self.get_context(sheerka), concept) + concept.metadata.full_serialization = True + to_string = sheerkapickle.encode(sheerka, concept) + decoded = sheerkapickle.decode(sheerka, to_string) + assert decoded == concept + assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "meta.key": "foo", "meta.id": "1001"}' + def test_i_can_encode_decode_unknown_concept_values(self): sheerka = self.get_sheerka() - concept = Concept("foo") + concept = set_full_serialization(Concept("foo")) concept.values[ConceptParts.PRE] = 10 # an int to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "pre": 10}' - concept = Concept("foo") - concept.values[ConceptParts.POST] = 'a string' # an int + concept = set_full_serialization(Concept("foo")) + concept.values[ConceptParts.POST] = 'a string' # an string to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "post": "a string"}' - concept = Concept("foo") + concept = set_full_serialization(Concept("foo")) concept.values[ConceptParts.WHERE] = ['a string', 3.14] # a list to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "where": ["a string", 3.14]}' - concept = Concept("foo") + concept = set_full_serialization(Concept("foo")) concept.values[ConceptParts.WHERE] = ('a string', 3.14) # a tuple to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "where": {"_sheerka/tuple": ["a string", 3.14]}}' - concept = Concept("foo") - concept.values[ConceptParts.BODY] = Concept("foo", body="foo_body") + concept = set_full_serialization(Concept("foo")) + concept.values[ConceptParts.BODY] = set_full_serialization(Concept("foo", body="foo_body")) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept @@ -97,28 +110,28 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka): def test_i_can_encode_decode_unknown_concept_properties(self): sheerka = self.get_sheerka() - concept = Concept("foo") + concept = set_full_serialization(Concept("foo")) concept.set_prop("a", "value_a") # string to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "props": [["a", "value_a"]]}' - concept = Concept("foo") + concept = set_full_serialization(Concept("foo")) concept.set_prop("a", 10) # int to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "props": [["a", 10]]}' - concept = Concept("foo") - concept.set_prop("a", Concept("bar")) # another concept + concept = set_full_serialization(Concept("foo")) + concept.set_prop("a", set_full_serialization(Concept("bar"))) # another concept to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "foo", "props": [["a", {"_sheerka/obj": "core.concept.Concept", "meta.name": "bar"}]]}' - concept = Concept("foo") + concept = set_full_serialization(Concept("foo")) concept.set_prop("a", "a").set_prop("b", "b") # at least two props to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) @@ -138,19 +151,38 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka): assert decoded == ref_concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "concept/id": ["my_key", "1001"]}' + # same test, modify a value and check if this modification is correctly saved concept = Concept().update_from(sheerka.get_by_id(ref_concept.id)) - concept.set_metadata_value(ConceptParts.BODY, Concept("bar")) + concept.set_metadata_value(ConceptParts.BODY, set_full_serialization(Concept("bar"))) to_string = sheerkapickle.encode(sheerka, concept) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == concept assert to_string == '{"_sheerka/obj": "core.concept.Concept", "concept/id": ["my_key", "1001"], "body": {"_sheerka/obj": "core.concept.Concept", "meta.name": "bar"}}' + def test_i_can_encode_decode_when_property_is_a_concept(self): + sheerka = self.get_sheerka() + + foo = Concept("foo").init_key() + sheerka.create_new_concept(self.get_context(sheerka), foo) + + concept = Concept("my_name").init_key() + sheerka.create_new_concept(self.get_context(sheerka), concept) + concept.def_prop(foo, "a value") + concept.metadata.full_serialization = True + + to_string = sheerkapickle.encode(sheerka, concept) + decoded = sheerkapickle.decode(sheerka, to_string) + assert decoded == concept + assert to_string == '{"_sheerka/obj": "core.concept.Concept", "meta.name": "my_name", "meta.key": "my_name", ' + \ + '"meta.props": [{"_sheerka/tuple": [{"_sheerka/obj": "core.concept.Concept", "concept/id": ["foo", "1001"]}, "a value"]}], ' + \ + '"meta.id": "1002", "props": [[{"_sheerka/id": 1}, null]]}' + def test_i_can_manage_reference_of_the_same_object(self): sheerka = self.get_sheerka() - concept_ref = Concept("foo") + concept_ref = set_full_serialization(Concept("foo")) - concept = Concept("bar") + concept = set_full_serialization(Concept("bar")) concept.set_metadata_value(ConceptParts.PRE, concept_ref) concept.set_metadata_value(ConceptParts.BODY, concept_ref) @@ -189,7 +221,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka): to_string = sheerkapickle.encode(sheerka, ret_val) decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == ret_val - assert to_string == '{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept", "who": "who", "status": true, "value": 10}' + assert to_string == '{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept", "concept/id": ["__RETURN_VALUE", null], "who": "who", "status": true, "value": 10}' def test_i_can_encode_decode_return_value_with_parent(self): sheerka = self.get_sheerka() @@ -202,13 +234,14 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka): decoded = sheerkapickle.decode(sheerka, to_string) assert decoded == ret_val assert decoded.parents == ret_val.parents - parents_str = '[{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept", "who": "parent_who", "status": true, "value": "10"}, {"_sheerka/id": 1}]' - assert to_string == '{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept", "who": "who", "status": true, "value": 10, "parents": ' + parents_str + '}' + id_str = ', "concept/id": ["__RETURN_VALUE", null]' + parents_str = '[{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept"' + id_str + ', "who": "parent_who", "status": true, "value": "10"}, {"_sheerka/id": 1}]' + assert to_string == '{"_sheerka/obj": "core.builtin_concepts.ReturnValueConcept"' + id_str + ', "who": "who", "status": true, "value": 10, "parents": ' + parents_str + '}' def test_i_can_encode_decode_return_values_with_complex_body(self): sheerka = self.get_sheerka() - ret_val = sheerka.ret("who", True, Concept("foo", body="bar")) + ret_val = sheerka.ret("who", True, set_full_serialization(Concept("foo", body="bar"))) to_string = sheerkapickle.encode(sheerka, ret_val) decoded = sheerkapickle.decode(sheerka, to_string) @@ -239,9 +272,9 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka): context = ExecutionContext("who", Event("xxx"), sheerka, "my desc") input_list = [ReturnValueConcept("who", True, 10), ReturnValueConcept("who2", False, 20)] - context.inputs = {"a": input_list, "b": Concept("foo")} - context.values = {"c": input_list, "d": Concept("bar")} - context.obj = Concept("baz") + context.inputs = {"a": input_list, "b": set_full_serialization(Concept("foo"))} + context.values = {"c": input_list, "d": set_full_serialization(Concept("bar"))} + context.obj = set_full_serialization(Concept("baz")) context.push("who3", "sub_child1") context.push("who4", "sub_child2") @@ -278,7 +311,7 @@ class TestSheerkaPickleHandler(TestUsingMemoryBasedSheerka): def test_encode_simple_concept(self): sheerka = self.get_sheerka() - foo = Concept("foo") + foo = set_full_serialization(Concept("foo")) to_string = sheerkapickle.encode(sheerka, foo) decoded = sheerkapickle.decode(sheerka, to_string)