diff --git a/core/builtin_concepts.py b/core/builtin_concepts.py index 6b17618..4d01da2 100644 --- a/core/builtin_concepts.py +++ b/core/builtin_concepts.py @@ -58,7 +58,7 @@ class SuccessConcept(Concept): class ErrorConcept(Concept): def __init__(self, error=None): - super().__init__(BuiltinConcepts.ERROR, True, False, BuiltinConcepts.ERROR, body=error) + super().__init__(BuiltinConcepts.ERROR, True, False, BuiltinConcepts.ERROR, error) def __repr__(self): return f"({self.id}){self.name}: {self.body}" @@ -71,10 +71,9 @@ class ReturnValueConcept(Concept): """ def __init__(self, who=None, status=None, value=None, message=None, parents=None): - super().__init__(BuiltinConcepts.RETURN_VALUE, True, False, BuiltinConcepts.RETURN_VALUE) + super().__init__(BuiltinConcepts.RETURN_VALUE, True, False, BuiltinConcepts.RETURN_VALUE, value) self.set_prop("who", who) self.set_prop("status", status) - self.body = value self.set_prop("message", message) self.set_prop("parents", parents) @@ -100,7 +99,7 @@ class ReturnValueConcept(Concept): @value.setter def value(self, value): - self.body = value + self.metadata.body = value @property def message(self): @@ -140,9 +139,8 @@ class UnknownPropertyConcept(Concept): """ def __init__(self, property_name=None, concept=None): - super().__init__(BuiltinConcepts.UNKNOWN_PROPERTY, True, False, BuiltinConcepts.UNKNOWN_PROPERTY) + super().__init__(BuiltinConcepts.UNKNOWN_PROPERTY, True, False, BuiltinConcepts.UNKNOWN_PROPERTY, property_name) self.set_prop("concept", concept) - self.body = property_name def __repr__(self): return f"UnknownProperty(property={self.property_name}, concept={self.concept})" @@ -162,11 +160,10 @@ class ParserResultConcept(Concept): """ def __init__(self, parser=None, source=None, value=None, try_parsed=None): - super().__init__(BuiltinConcepts.PARSER_RESULT, True, False, BuiltinConcepts.PARSER_RESULT) + super().__init__(BuiltinConcepts.PARSER_RESULT, True, False, BuiltinConcepts.PARSER_RESULT, value) self.set_prop("parser", parser) self.set_prop("source", source) self.set_prop("try_parsed", try_parsed) # in case of error, what was found before the error - self.body = value def __repr__(self): return f"ParserResult({self.body})" @@ -205,9 +202,13 @@ class InvalidReturnValueConcept(Concept): """ def __init__(self, return_value=None, evaluator=None): - super().__init__(BuiltinConcepts.INVALID_RETURN_VALUE, True, False, BuiltinConcepts.INVALID_RETURN_VALUE) + super().__init__( + BuiltinConcepts.INVALID_RETURN_VALUE, + True, + False, + BuiltinConcepts.INVALID_RETURN_VALUE, + return_value) self.set_prop("evaluator", evaluator) - self.body = return_value class BeforeParsingConcept(Concept): @@ -227,10 +228,13 @@ class AfterEvaluationConcept(Concept): class PropertyEvalError(Concept): def __init__(self, property_name=None, concept=None, error=None): - super().__init__(BuiltinConcepts.PROPERTY_EVAL_ERROR, True, False, BuiltinConcepts.PROPERTY_EVAL_ERROR) + super().__init__(BuiltinConcepts.PROPERTY_EVAL_ERROR, + True, + False, + BuiltinConcepts.PROPERTY_EVAL_ERROR, + property_name) self.set_prop("concept", concept) self.set_prop("error", error) - self.body = property_name def __repr__(self): return f"PropertyEvalError(property={self.property_name}, concept={self.concept}), error={self.error})" @@ -250,8 +254,7 @@ class PropertyEvalError(Concept): class EnumerationConcept(Concept): def __init__(self, iteration=None): - super().__init__(BuiltinConcepts.ENUMERATION, True, False, BuiltinConcepts.ENUMERATION) - self.body = iteration + super().__init__(BuiltinConcepts.ENUMERATION, True, False, BuiltinConcepts.ENUMERATION, iteration) def __iter__(self): return iter(self.body) @@ -259,8 +262,7 @@ class EnumerationConcept(Concept): class ListConcept(Concept): def __init__(self, items=None): - super().__init__(BuiltinConcepts.LIST, True, False, BuiltinConcepts.LIST) - self.body = items or [] + super().__init__(BuiltinConcepts.LIST, True, False, BuiltinConcepts.LIST, items or []) def append(self, obj): self.body.append(obj) diff --git a/core/builtin_helpers.py b/core/builtin_helpers.py index 481a458..8f47202 100644 --- a/core/builtin_helpers.py +++ b/core/builtin_helpers.py @@ -49,7 +49,7 @@ def expect_one(context, return_values): return sheerka.ret( context.who, False, - sheerka.new(BuiltinConcepts.IS_EMPTY, obj=return_values), + sheerka.new(BuiltinConcepts.IS_EMPTY, body=return_values), parents=return_values) successful_results = [item for item in return_values if item.status] @@ -77,14 +77,14 @@ def expect_one(context, return_values): return sheerka.ret( context.who, False, - sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS, obj=successful_results), + sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS, body=successful_results), parents=return_values) # only errors, i cannot help you return sheerka.ret( context.who, False, - sheerka.new(BuiltinConcepts.TOO_MANY_ERRORS, obj=return_values), + sheerka.new(BuiltinConcepts.TOO_MANY_ERRORS, body=return_values), parents=return_values) diff --git a/core/concept.py b/core/concept.py index f855835..dc96abb 100644 --- a/core/concept.py +++ b/core/concept.py @@ -1,4 +1,5 @@ import hashlib +from dataclasses import dataclass from enum import Enum import logging @@ -6,6 +7,14 @@ from core.tokenizer import Tokenizer, TokenKind log = logging.getLogger(__name__) +PROPERTIES_FOR_DIGEST = ("name", "key", + "definition", "definition_type", + "is_builtin", "is_unique", + "where", "pre", "post", "body", + "desc") +PROPERTIES_TO_SERIALIZE = PROPERTIES_FOR_DIGEST + tuple(["id"]) +VARIABLE_PREFIX = "__var__" + class ConceptParts(Enum): """ @@ -17,6 +26,26 @@ class ConceptParts(Enum): POST = "post" BODY = "body" + @staticmethod + def get_parts(): + return set(item.value for item in ConceptParts) + + +@dataclass +class ConceptMetadata: + name: str + is_builtin: bool + is_unique: bool + key: str # name od the concept, where prop are replaced. to ease search + body: str # main method, can also be the value of the concept + where: str # condition to recognize variables in name + pre: str # list of pre conditions before calling the main function + post: str # list of post conditions after calling the main function + definition: str # regex used to define the concept + definition_type: str # definition can be done with something else than regex + desc: str # possible description for the concept + id: str # unique identifier for a concept. The id will never be modified (but the key can) + class Concept: """ @@ -24,51 +53,49 @@ class Concept: A concept is a the base object of our universe Everything is a concept """ - props_for_digest = ("is_builtin", "is_unique", "key", "name", "where", "pre", "post", "body", "desc") - props_to_serialize = ("id", "is_builtin", "is_unique", "key", "name", "where", "pre", "post", "body", "desc") - concept_parts = set(item.value for item in ConceptParts) - - PROPERTY_PREFIX = "__var__" def __init__(self, name=None, is_builtin=False, is_unique=False, key=None, + body=None, where=None, pre=None, post=None, - body=None, + definition=None, + definition_type=None, desc=None, - obj=None): + id=None): - self.name = str(name) if name else None - self.is_builtin = is_builtin - self.is_unique = is_unique - self.key = str(key) if key else None # name od the concept, where prop are replaced. to ease search + metadata = ConceptMetadata( + str(name) if name else None, + is_builtin, + is_unique, + str(key) if key else None, + body, + where, + pre, + post, + definition, + definition_type, + desc, + id + ) - self.where = where # condition to recognize variables in name - self.pre = pre # list of pre conditions before calling the main function - self.post = post # list of post conditions after calling the main function - self.body = body # main method, can also be the value of the concept - self.desc = desc - self.id = None # unique identifier for a concept. The id will never be modified - - self.obj = obj # main of principal property of the concept + self.metadata = metadata self.props = {} # list of Property for this concept - self.functions = {} # list of helper functions - - self.codes = {} # cached ast for the where, pre, post and body parts + self.cached_asts = {} # cached ast for the where, pre, post and body parts def __repr__(self): - return f"({self.id}){self.name}" + return f"({self.metadata.id}){self.metadata.name}" def __eq__(self, other): if not isinstance(other, Concept): return False # check the attributes - for prop in self.props_to_serialize: - if getattr(self, prop) != getattr(other, prop): + for prop in PROPERTIES_TO_SERIALIZE: + if getattr(self.metadata, prop) != getattr(other.metadata, prop): # print(prop) # use full to know which id does not match return False @@ -80,10 +107,19 @@ class Concept: return True def __hash__(self): - return hash(self.name) + return hash(self.metadata.name) - def get_key(self): - return self.key + @property + def name(self): + return self.metadata.name + + @property + def id(self): + return self.metadata.id + + @property + def key(self): + return self.metadata.key def init_key(self, tokens=None): """ @@ -94,11 +130,11 @@ class Concept: :param tokens: :return: """ - if self.key is not None: + if self.metadata.key is not None: return self if tokens is None: - tokens = iter(Tokenizer(self.name)) + tokens = iter(Tokenizer(self.metadata.name)) variables = list(self.props.keys()) @@ -112,14 +148,18 @@ class Concept: if not first: key += " " if variables is not None and token.value in variables: - key += self.PROPERTY_PREFIX + str(variables.index(token.value)) + key += VARIABLE_PREFIX + str(variables.index(token.value)) else: key += token.value[1:-1] if token.type == TokenKind.STRING else token.value first = False - self.key = key + self.metadata.key = key return self + @property + def body(self): + return self.metadata.body + def add_codes(self, codes): """ Gets the ASTs for 'where', 'pre', 'post' and 'body' @@ -131,12 +171,12 @@ class Concept: :param codes: :return: """ - possibles_codes = self.concept_parts + possibles_codes = ConceptParts.get_parts() if codes is None: return for key in codes: if key in possibles_codes: - self.codes[ConceptParts(key)] = codes[key] + self.cached_asts[ConceptParts(key)] = codes[key] return self @@ -145,7 +185,7 @@ class Concept: Returns the digest of the event :return: hexa form of the sha256 """ - return hashlib.sha256(f"Concept:{self.to_dict(self.props_for_digest)}".encode("utf-8")).hexdigest() + return hashlib.sha256(f"Concept:{self.to_dict(PROPERTIES_FOR_DIGEST)}".encode("utf-8")).hexdigest() def to_dict(self, props_to_use=None): """ @@ -153,9 +193,9 @@ class Concept: :return: """ - props_to_use = props_to_use or self.props_to_serialize + props_to_use = props_to_use or PROPERTIES_TO_SERIALIZE - props_as_dict = dict((prop, getattr(self, prop)) for prop in props_to_use) + props_as_dict = dict((prop, getattr(self.metadata, prop)) for prop in props_to_use) props_as_dict["props"] = [(p, self.props[p].value) for p in self.props] return props_as_dict @@ -165,9 +205,9 @@ class Concept: :param as_dict: :return: """ - for prop in self.props_to_serialize: + for prop in PROPERTIES_TO_SERIALIZE: if prop in as_dict: - setattr(self, prop, as_dict[prop]) + setattr(self.metadata, prop, as_dict[prop]) if "props" in as_dict: for n, v in as_dict["props"]: self.set_prop(n, v) diff --git a/core/sheerka.py b/core/sheerka.py index fbd8613..8b9c6c1 100644 --- a/core/sheerka.py +++ b/core/sheerka.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept -from core.concept import Concept, ConceptParts +from core.concept import Concept, ConceptParts, PROPERTIES_FOR_DIGEST from evaluators.BaseEvaluator import OneReturnValueEvaluator from parsers.BaseParser import BaseParser from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError @@ -55,7 +55,7 @@ class Sheerka(Concept): self.debug = debug self.skip_builtins_in_db = skip_builtins_in_db - def initialize(self, root_folder=None): + def initialize(self, root_folder: str = None): """ Starting Sheerka Loads the current configuration @@ -80,18 +80,6 @@ class Sheerka(Concept): return ReturnValueConcept(self, True, self) - def set_id_if_needed(self, obj, is_builtin): - """ - Set the key for the concept if needed - :param obj: - :param is_builtin: - :return: - """ - if obj.id is not None: - return - obj.id = self.sdp.get_next_key(self.BUILTIN_CONCEPTS_KEYS if is_builtin else self.USER_CONCEPTS_KEYS) - log.debug(f"Setting id '{obj.id}' to concept '{obj.name}'.") - def initialize_builtin_concepts(self): """ Initializes the builtin concepts @@ -107,11 +95,11 @@ class Sheerka(Concept): else builtins_classes[str(key)]() if str(key) in builtins_classes \ else Concept(key, True, False, key) - if not concept.is_unique and str(key) in builtins_classes: + if not concept.metadata.is_unique and str(key) in builtins_classes: self.builtin_cache[key] = builtins_classes[str(key)] if not self.skip_builtins_in_db: - from_db = self.sdp.get_safe(self.CONCEPTS_ENTRY, concept.key) + from_db = self.sdp.get_safe(self.CONCEPTS_ENTRY, concept.metadata.key) if from_db is None: log.debug(f"'{concept.name}' concept is not found in db. Adding.") self.set_id_if_needed(concept, True) @@ -158,7 +146,7 @@ class Sheerka(Concept): logging.basicConfig(format=log_format, level=log_level) - def eval(self, text): + def eval(self, text: str): """ Note to KSI: If you try to add execution context to this function, You may end in an infinite loop @@ -294,7 +282,19 @@ class Sheerka(Concept): return return_values - def create_new_concept(self, context, concept): + def set_id_if_needed(self, obj: Concept, is_builtin: bool): + """ + Set the key for the concept if needed + :param obj: + :param is_builtin: + :return: + """ + if obj.metadata.id is not None: + return + obj.metadata.id = self.sdp.get_next_key(self.BUILTIN_CONCEPTS_KEYS if is_builtin else self.USER_CONCEPTS_KEYS) + log.debug(f"Setting id '{obj.metadata.id}' to concept '{obj.metadata.name}'.") + + def create_new_concept(self, context, concept: Concept): """ Adds a new concept to the system :param context: @@ -325,7 +325,7 @@ class Sheerka(Concept): ret = self.ret(self.create_new_concept.__name__, True, self.new(BuiltinConcepts.NEW_CONCEPT, body=concept)) return ret - def add_codes_to_concept(self, context, concept): + def initialize_concept_asts(self, context, concept: Concept): """ Updates the codes of the newly created concept Basically, it runs the parsers on all parts @@ -334,16 +334,16 @@ class Sheerka(Concept): :return: """ for part_key in ConceptParts: - source = getattr(concept, part_key.value) + source = getattr(concept.metadata, part_key.value) if source is None or not isinstance(source, str) or source == "": # the only sources that I am sure to parse are strings # I refuse empty strings for performance, I don't want to handle useless NOPConcepts continue else: - concept.codes[part_key] = self.parse(context, source) + concept.cached_asts[part_key] = self.parse(context, source) for prop in concept.props: - concept.codes[prop] = self.parse(context, concept.props[prop].value) + concept.cached_asts[prop] = self.parse(context, concept.props[prop].value) # updates the code of the reference when possible if concept.key in self.concepts_cache: @@ -352,11 +352,20 @@ class Sheerka(Concept): # TODO : manage when there are multiple entries pass else: - self.concepts_cache[concept.key].codes = concept.codes + self.concepts_cache[concept.key].cached_asts = concept.cached_asts - def eval_concept(self, context, concept, properties_to_eval=None): - if len(concept.codes) == 0: - self.add_codes_to_concept(context, concept) + def eval_concept(self, context, concept: Concept, properties_to_eval=None): + """ + Evaluation a concept + It means that if the where clause is True, will evaluate the body + Also chc + :param context: + :param concept: + :param properties_to_eval: + :return: + """ + if len(concept.cached_asts) == 0: + self.initialize_concept_asts(context, concept) if properties_to_eval is None: properties_to_eval = ["where", "pre", "post", "body", "props"] @@ -366,13 +375,13 @@ class Sheerka(Concept): pass else: part_key = ConceptParts(prop) - if concept.codes[part_key] is None: + if concept.cached_asts[part_key] is None: continue - res = self.chain_process(context, concept.codes[part_key], concept_evaluation_steps) + res = self.chain_process(context, concept.cached_asts[part_key], concept_evaluation_steps) res = core.builtin_helpers.expect_one(context, res) - setattr(concept, prop, res.value) + setattr(concept.metadata, prop, res.value) - def add_in_cache(self, concept): + def add_in_cache(self, concept: Concept): """ Adds a concept template in cache. The cache is used as a proxy before looking at sdp @@ -416,7 +425,7 @@ class Sheerka(Concept): unknown_concept = Concept() template = self.concepts_cache[str(BuiltinConcepts.UNKNOWN_CONCEPT)] unknown_concept.update_from(template) - unknown_concept.body = concept_key + unknown_concept.metadata.body = concept_key return unknown_concept def new(self, concept_key, **kwargs): @@ -431,7 +440,7 @@ class Sheerka(Concept): def new_from_template(t, k, **kwargs_): # manage singleton - if t.is_unique: + if t.metadata.is_unique: return t # otherwise, create another instance @@ -442,6 +451,8 @@ class Sheerka(Concept): for k, v in kwargs_.items(): if k in concept.props: concept.set_prop(k, v) + elif k in PROPERTIES_FOR_DIGEST: + setattr(concept.metadata, k, v) elif hasattr(concept, k): setattr(concept, k, v) else: @@ -462,7 +473,7 @@ class Sheerka(Concept): concepts = [new_from_template(t, concept_key, **kwargs) for t in template] return self.new(BuiltinConcepts.ENUMERATION, body=concepts) - def ret(self, who, status, value, message=None, parents=None): + def ret(self, who: str, status: bool, value, message=None, parents=None): """ Creates and returns a ReturnValue concept :param who: @@ -569,32 +580,18 @@ class Sheerka(Concept): return sorted(res, key=lambda i: int(i.id)) + def test(self): + return f"I have access to Sheerka !" + @staticmethod def get_builtins_classes_as_dict(): res = {} for c in core.utils.get_classes("core.builtin_concepts"): if issubclass(c, Concept) and c != Concept: - res[c().key] = c + res[c().metadata.key] = c return res - @staticmethod - def get_builtin_parsers(): - res = [] - # modules = core.utils.get_module("parsers") - # for m in modules: - base_class = core.utils.get_class("parsers.BaseParser.BaseParser") - - for c in core.utils.get_classes_recursive("parsers"): - # if issubclass(c, base_class) and c != base_class: - res.append(c) - - return res - - @staticmethod - def test(): - return "I have access to Sheerka !" - @dataclass class ExecutionContext: diff --git a/evaluators/AddConceptEvaluator.py b/evaluators/AddConceptEvaluator.py index 4b288a5..63f43b6 100644 --- a/evaluators/AddConceptEvaluator.py +++ b/evaluators/AddConceptEvaluator.py @@ -40,7 +40,7 @@ class AddConceptEvaluator(OneReturnValueEvaluator): # update the parts source = self.get_source(part_ret_val) - setattr(concept, prop, source) + setattr(concept.metadata, prop, source) # try to find what can be a property for p in self.get_props(part_ret_val): diff --git a/evaluators/ConceptEvaluator.py b/evaluators/ConceptEvaluator.py index 508830e..75dabba 100644 --- a/evaluators/ConceptEvaluator.py +++ b/evaluators/ConceptEvaluator.py @@ -29,8 +29,8 @@ class ConceptEvaluator(OneReturnValueEvaluator): # pre condition should already be validated by the parser. # It's a mandatory condition for the concept before it can be recognized - if len(concept.codes) == 0: - sheerka.add_codes_to_concept(context, concept) + if len(concept.cached_asts) == 0: + sheerka.initialize_concept_asts(context, concept) # TODO; check pre # if pre is not true, return Concept with a false value @@ -38,7 +38,7 @@ class ConceptEvaluator(OneReturnValueEvaluator): # Evaluate the properties for prop in concept.props: sub_context = context.push(self.name, f"Evaluating property '{prop}'", concept) - res = self.evaluate_parsing(sheerka, sub_context, concept.codes[prop]) + res = self.evaluate_parsing(sheerka, sub_context, concept.cached_asts[prop]) if res.status: concept.set_prop(prop, res.value) else: @@ -49,11 +49,11 @@ class ConceptEvaluator(OneReturnValueEvaluator): parents=[return_value]) # Returns the concept when no body - if ConceptParts.BODY not in concept.codes: + if ConceptParts.BODY not in concept.cached_asts: return sheerka.ret(self.name, True, concept, parents=[return_value]) # Evaluate the body otherwise - body = concept.codes[ConceptParts.BODY] + body = concept.cached_asts[ConceptParts.BODY] if body is None: raise NotImplementedError("Seems weird !") diff --git a/evaluators/DuplicateConceptEvaluator.py b/evaluators/DuplicateConceptEvaluator.py index 8570863..554cbd1 100644 --- a/evaluators/DuplicateConceptEvaluator.py +++ b/evaluators/DuplicateConceptEvaluator.py @@ -40,5 +40,5 @@ class DuplicateConceptEvaluator(AllReturnValuesEvaluator): return sheerka.ret( self.name, False, - sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, obj=self.already_defined), + sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=self.already_defined), parents=return_values) diff --git a/evaluators/TooManySuccessEvaluator.py b/evaluators/TooManySuccessEvaluator.py index 617f258..7ae0939 100644 --- a/evaluators/TooManySuccessEvaluator.py +++ b/evaluators/TooManySuccessEvaluator.py @@ -48,7 +48,7 @@ class TooManySuccessEvaluator(AllReturnValuesEvaluator): def eval(self, context, return_values): sheerka = context.sheerka if not core.builtin_helpers.is_same_success(sheerka, self.success): - too_many_success = sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS, obj=self.success) + too_many_success = sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS, body=self.success) return sheerka.ret(self.name, False, too_many_success, parents=return_values) return None diff --git a/parsers/ExactConceptParser.py b/parsers/ExactConceptParser.py index 08472fd..bfe45b6 100644 --- a/parsers/ExactConceptParser.py +++ b/parsers/ExactConceptParser.py @@ -1,7 +1,7 @@ from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts from parsers.BaseParser import BaseParser from core.tokenizer import Tokenizer, Keywords, TokenKind -from core.concept import Concept +from core.concept import Concept, VARIABLE_PREFIX import logging log = logging.getLogger(__name__) @@ -28,7 +28,7 @@ class ExactConceptParser(BaseParser): sheerka = context.sheerka words = self.get_words(text) if len(words) > self.MAX_WORDS_SIZE: - return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.CONCEPT_TOO_LONG, obj=text)) + return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.CONCEPT_TOO_LONG, body=text)) recognized = False for combination in self.combinations(words): @@ -44,8 +44,8 @@ class ExactConceptParser(BaseParser): for concept in concepts: # update the properties if needed for i, token in enumerate(combination): - if token.startswith(Concept.PROPERTY_PREFIX): - index = int(token[len(Concept.PROPERTY_PREFIX):]) + if token.startswith(VARIABLE_PREFIX): + index = int(token[len(VARIABLE_PREFIX):]) concept.set_prop_by_index(index, words[i]) res.append(ReturnValueConcept(self.name, True, concept)) log.debug(f"Recognized '{text}' as '{concept}'") @@ -55,7 +55,7 @@ class ExactConceptParser(BaseParser): return res log.debug(f"Failed to recognize {words}") - return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, obj=text)) + return sheerka.ret(self.name, False, sheerka.new(BuiltinConcepts.UNKNOWN_CONCEPT, body=text)) @staticmethod def get_words(text): @@ -111,7 +111,7 @@ class ExactConceptParser(BaseParser): for i in indices: value = pool[i] if value not in vars: - vars[pool[i]] = f"{Concept.PROPERTY_PREFIX}{k}" + vars[pool[i]] = f"{VARIABLE_PREFIX}{k}" k += 1 # create tuple diff --git a/tests/test_ExactConceptParser.py b/tests/test_ExactConceptParser.py index bff0f69..320c58a 100644 --- a/tests/test_ExactConceptParser.py +++ b/tests/test_ExactConceptParser.py @@ -98,7 +98,7 @@ def test_i_can_manage_unknown_concept(): assert not res.status assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT) - assert res.value.obj == "def concept hello world" + assert res.value.body == "def concept hello world" def test_i_can_detect_concepts_too_long(): @@ -108,7 +108,7 @@ def test_i_can_detect_concepts_too_long(): assert not res.status assert context.sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_TOO_LONG) - assert res.value.obj == "a very very long concept that cannot be an unique one" + assert res.value.body == "a very very long concept that cannot be an unique one" def test_i_can_detect_concept_from_tokens(): diff --git a/tests/test_builtin_helpers.py b/tests/test_builtin_helpers.py index d8a7082..3abd1a8 100644 --- a/tests/test_builtin_helpers.py +++ b/tests/test_builtin_helpers.py @@ -25,7 +25,7 @@ def test_i_can_use_expect_one_when_too_many_success(): res = core.builtin_helpers.expect_one(get_context(sheerka), items) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_SUCCESS) - assert res.value.obj == items + assert res.value.body == items assert res.parents == items @@ -51,7 +51,7 @@ def test_i_can_use_expect_when_only_errors_1(): res = core.builtin_helpers.expect_one(get_context(sheerka), items) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS) - assert res.value.obj == items + assert res.value.body == items assert res.parents == items @@ -65,7 +65,7 @@ def test_i_can_use_expect_when_only_errors_2(): res = core.builtin_helpers.expect_one(get_context(sheerka), items) assert not res.status assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS) - assert res.value.obj == items + assert res.value.body == items assert res.parents == items diff --git a/tests/test_concept.py b/tests/test_concept.py index a940289..8d31fb2 100644 --- a/tests/test_concept.py +++ b/tests/test_concept.py @@ -17,7 +17,7 @@ def test_i_can_get_concept_key(name, variables, expected): concept.set_prop(v, None) concept.init_key() - assert concept.key == expected + assert concept.metadata.key == expected def test_i_can_serialize(): diff --git a/tests/test_sheerka.py b/tests/test_sheerka.py index d72a66c..9074117 100644 --- a/tests/test_sheerka.py +++ b/tests/test_sheerka.py @@ -6,7 +6,7 @@ from os import path import shutil from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept -from core.concept import Concept +from core.concept import Concept, PROPERTIES_TO_SERIALIZE from core.sheerka import Sheerka, ExecutionContext from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator from sdp.sheerkaDataProvider import SheerkaDataProvider @@ -63,13 +63,13 @@ def test_builtin_concepts_are_initialized(): def test_builtin_concepts_can_be_updated(): sheerka = get_sheerka(root_folder, skip_builtins_in_db=False) loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA) - loaded_sheerka.desc = "I have a description" + loaded_sheerka.metadata.desc = "I have a description" sheerka.sdp.modify("Test", sheerka.CONCEPTS_ENTRY, loaded_sheerka.key, loaded_sheerka) sheerka = get_sheerka(root_folder) loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA) - assert loaded_sheerka.desc == "I have a description" + assert loaded_sheerka.metadata.desc == "I have a description" def test_i_can_add_a_concept(): @@ -82,8 +82,8 @@ def test_i_can_add_a_concept(): assert sheerka.isinstance(res.value, BuiltinConcepts.NEW_CONCEPT) concept_found = res.value.body - for prop in Concept.props_to_serialize: - assert getattr(concept_found, prop) == getattr(concept, prop) + for prop in PROPERTIES_TO_SERIALIZE: + assert getattr(concept_found.metadata, prop) == getattr(concept.metadata, prop) assert concept_found.key == "__var__0 + __var__1" assert concept_found.id == "1001" @@ -166,7 +166,7 @@ def test_i_can_get_list_of_concept_when_same_key_when_no_cache(): sheerka = get_sheerka() concept1 = get_default_concept() concept2 = get_default_concept() - concept2.body = "a+b" + concept2.metadata.body = "a+b" res1 = sheerka.create_new_concept(get_context(sheerka), concept1) res2 = sheerka.create_new_concept(get_context(sheerka), concept2) @@ -185,7 +185,7 @@ def test_i_can_get_list_of_concept_when_same_key_when_cache(): sheerka = get_sheerka() concept1 = get_default_concept() concept2 = get_default_concept() - concept2.body = "a+b" + concept2.metadata.body = "a+b" res1 = sheerka.create_new_concept(get_context(sheerka), concept1) res2 = sheerka.create_new_concept(get_context(sheerka), concept2) @@ -239,8 +239,8 @@ def test_i_can_instantiate_a_concept(): new = sheerka.new(concept.key, a=10, b="value") assert sheerka.isinstance(new, concept) - for prop in Concept.props_to_serialize: - assert getattr(new, prop) == getattr(concept, prop) + for prop in PROPERTIES_TO_SERIALIZE: + assert getattr(new.metadata, prop) == getattr(concept.metadata, prop) assert new.props["a"].value == 10 assert new.props["b"].value == "value" @@ -398,8 +398,8 @@ as: """ expected = get_default_concept() - expected.id = "1001" - expected.desc = None + expected.metadata.id = "1001" + expected.metadata.desc = None expected.init_key() sheerka = get_sheerka() @@ -411,8 +411,8 @@ as: concept_saved = res[0].value.body - for prop in Concept.props_to_serialize: - assert getattr(concept_saved, prop) == getattr(expected, prop) + for prop in PROPERTIES_TO_SERIALIZE: + assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop) assert concept_saved.key in sheerka.concepts_cache assert sheerka.sdp.io.exists( @@ -433,7 +433,7 @@ def test_i_can_eval_def_concept_part_when_one_part_is_a_ref_of_another_concept() res = sheerka.eval("def concept a xx b as a plus b") expected = Concept(name="a xx b", body="a plus b").set_prop("a").set_prop("b").init_key() - expected.id = "1001" + expected.metadata.id = "1001" assert len(res) == 1 assert res[0].status @@ -441,8 +441,8 @@ def test_i_can_eval_def_concept_part_when_one_part_is_a_ref_of_another_concept() concept_saved = res[0].value.body - for prop in Concept.props_to_serialize: - assert getattr(concept_saved, prop) == getattr(expected, prop) + for prop in PROPERTIES_TO_SERIALIZE: + assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop) assert concept_saved.key in sheerka.concepts_cache assert sheerka.sdp.io.exists( @@ -537,7 +537,7 @@ def test_i_cannot_manage_duplicate_concepts_when_the_values_are_different(): assert not res[0].status assert sheerka.isinstance(res[0].value, BuiltinConcepts.TOO_MANY_SUCCESS) - concepts = res[0].value.obj + concepts = res[0].value.body assert len(concepts) == 2 sorted_values = sorted(concepts, key=lambda x: x.value) assert sorted_values[0].value == "hello another value" @@ -554,7 +554,7 @@ def test_i_can_manage_concepts_with_the_same_key_when_values_are_the_same(): res = sheerka.eval("hello 'foo'") assert len(res) == 1 assert res[0].status - assert res[0].value == "hello foo" + assert res[0].value == "hello foo" assert res[0].who == sheerka.get_evaluator_name(MultipleSameSuccessEvaluator.NAME)