Refactord Concept class to regroup all builtins properties into a ConceptMetadata class

This commit is contained in:
2019-11-30 18:16:20 +01:00
parent 5e539a4b28
commit 75c8793d53
13 changed files with 186 additions and 147 deletions
+18 -16
View File
@@ -58,7 +58,7 @@ class SuccessConcept(Concept):
class ErrorConcept(Concept): class ErrorConcept(Concept):
def __init__(self, error=None): 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): def __repr__(self):
return f"({self.id}){self.name}: {self.body}" 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): 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("who", who)
self.set_prop("status", status) self.set_prop("status", status)
self.body = value
self.set_prop("message", message) self.set_prop("message", message)
self.set_prop("parents", parents) self.set_prop("parents", parents)
@@ -100,7 +99,7 @@ class ReturnValueConcept(Concept):
@value.setter @value.setter
def value(self, value): def value(self, value):
self.body = value self.metadata.body = value
@property @property
def message(self): def message(self):
@@ -140,9 +139,8 @@ class UnknownPropertyConcept(Concept):
""" """
def __init__(self, property_name=None, concept=None): 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.set_prop("concept", concept)
self.body = property_name
def __repr__(self): def __repr__(self):
return f"UnknownProperty(property={self.property_name}, concept={self.concept})" 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): 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("parser", parser)
self.set_prop("source", source) self.set_prop("source", source)
self.set_prop("try_parsed", try_parsed) # in case of error, what was found before the error self.set_prop("try_parsed", try_parsed) # in case of error, what was found before the error
self.body = value
def __repr__(self): def __repr__(self):
return f"ParserResult({self.body})" return f"ParserResult({self.body})"
@@ -205,9 +202,13 @@ class InvalidReturnValueConcept(Concept):
""" """
def __init__(self, return_value=None, evaluator=None): 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.set_prop("evaluator", evaluator)
self.body = return_value
class BeforeParsingConcept(Concept): class BeforeParsingConcept(Concept):
@@ -227,10 +228,13 @@ class AfterEvaluationConcept(Concept):
class PropertyEvalError(Concept): class PropertyEvalError(Concept):
def __init__(self, property_name=None, concept=None, error=None): 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("concept", concept)
self.set_prop("error", error) self.set_prop("error", error)
self.body = property_name
def __repr__(self): def __repr__(self):
return f"PropertyEvalError(property={self.property_name}, concept={self.concept}), error={self.error})" return f"PropertyEvalError(property={self.property_name}, concept={self.concept}), error={self.error})"
@@ -250,8 +254,7 @@ class PropertyEvalError(Concept):
class EnumerationConcept(Concept): class EnumerationConcept(Concept):
def __init__(self, iteration=None): def __init__(self, iteration=None):
super().__init__(BuiltinConcepts.ENUMERATION, True, False, BuiltinConcepts.ENUMERATION) super().__init__(BuiltinConcepts.ENUMERATION, True, False, BuiltinConcepts.ENUMERATION, iteration)
self.body = iteration
def __iter__(self): def __iter__(self):
return iter(self.body) return iter(self.body)
@@ -259,8 +262,7 @@ class EnumerationConcept(Concept):
class ListConcept(Concept): class ListConcept(Concept):
def __init__(self, items=None): def __init__(self, items=None):
super().__init__(BuiltinConcepts.LIST, True, False, BuiltinConcepts.LIST) super().__init__(BuiltinConcepts.LIST, True, False, BuiltinConcepts.LIST, items or [])
self.body = items or []
def append(self, obj): def append(self, obj):
self.body.append(obj) self.body.append(obj)
+3 -3
View File
@@ -49,7 +49,7 @@ def expect_one(context, return_values):
return sheerka.ret( return sheerka.ret(
context.who, context.who,
False, False,
sheerka.new(BuiltinConcepts.IS_EMPTY, obj=return_values), sheerka.new(BuiltinConcepts.IS_EMPTY, body=return_values),
parents=return_values) parents=return_values)
successful_results = [item for item in return_values if item.status] successful_results = [item for item in return_values if item.status]
@@ -77,14 +77,14 @@ def expect_one(context, return_values):
return sheerka.ret( return sheerka.ret(
context.who, context.who,
False, False,
sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS, obj=successful_results), sheerka.new(BuiltinConcepts.TOO_MANY_SUCCESS, body=successful_results),
parents=return_values) parents=return_values)
# only errors, i cannot help you # only errors, i cannot help you
return sheerka.ret( return sheerka.ret(
context.who, context.who,
False, False,
sheerka.new(BuiltinConcepts.TOO_MANY_ERRORS, obj=return_values), sheerka.new(BuiltinConcepts.TOO_MANY_ERRORS, body=return_values),
parents=return_values) parents=return_values)
+79 -39
View File
@@ -1,4 +1,5 @@
import hashlib import hashlib
from dataclasses import dataclass
from enum import Enum from enum import Enum
import logging import logging
@@ -6,6 +7,14 @@ from core.tokenizer import Tokenizer, TokenKind
log = logging.getLogger(__name__) 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): class ConceptParts(Enum):
""" """
@@ -17,6 +26,26 @@ class ConceptParts(Enum):
POST = "post" POST = "post"
BODY = "body" 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: class Concept:
""" """
@@ -24,51 +53,49 @@ class Concept:
A concept is a the base object of our universe A concept is a the base object of our universe
Everything is a concept 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, def __init__(self, name=None,
is_builtin=False, is_builtin=False,
is_unique=False, is_unique=False,
key=None, key=None,
body=None,
where=None, where=None,
pre=None, pre=None,
post=None, post=None,
body=None, definition=None,
definition_type=None,
desc=None, desc=None,
obj=None): id=None):
self.name = str(name) if name else None metadata = ConceptMetadata(
self.is_builtin = is_builtin str(name) if name else None,
self.is_unique = is_unique is_builtin,
self.key = str(key) if key else None # name od the concept, where prop are replaced. to ease search 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.metadata = metadata
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.props = {} # list of Property for this concept self.props = {} # list of Property for this concept
self.functions = {} # list of helper functions self.cached_asts = {} # cached ast for the where, pre, post and body parts
self.codes = {} # cached ast for the where, pre, post and body parts
def __repr__(self): def __repr__(self):
return f"({self.id}){self.name}" return f"({self.metadata.id}){self.metadata.name}"
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Concept): if not isinstance(other, Concept):
return False return False
# check the attributes # check the attributes
for prop in self.props_to_serialize: for prop in PROPERTIES_TO_SERIALIZE:
if getattr(self, prop) != getattr(other, prop): if getattr(self.metadata, prop) != getattr(other.metadata, prop):
# print(prop) # use full to know which id does not match # print(prop) # use full to know which id does not match
return False return False
@@ -80,10 +107,19 @@ class Concept:
return True return True
def __hash__(self): def __hash__(self):
return hash(self.name) return hash(self.metadata.name)
def get_key(self): @property
return self.key 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): def init_key(self, tokens=None):
""" """
@@ -94,11 +130,11 @@ class Concept:
:param tokens: :param tokens:
:return: :return:
""" """
if self.key is not None: if self.metadata.key is not None:
return self return self
if tokens is None: if tokens is None:
tokens = iter(Tokenizer(self.name)) tokens = iter(Tokenizer(self.metadata.name))
variables = list(self.props.keys()) variables = list(self.props.keys())
@@ -112,14 +148,18 @@ class Concept:
if not first: if not first:
key += " " key += " "
if variables is not None and token.value in variables: 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: else:
key += token.value[1:-1] if token.type == TokenKind.STRING else token.value key += token.value[1:-1] if token.type == TokenKind.STRING else token.value
first = False first = False
self.key = key self.metadata.key = key
return self return self
@property
def body(self):
return self.metadata.body
def add_codes(self, codes): def add_codes(self, codes):
""" """
Gets the ASTs for 'where', 'pre', 'post' and 'body' Gets the ASTs for 'where', 'pre', 'post' and 'body'
@@ -131,12 +171,12 @@ class Concept:
:param codes: :param codes:
:return: :return:
""" """
possibles_codes = self.concept_parts possibles_codes = ConceptParts.get_parts()
if codes is None: if codes is None:
return return
for key in codes: for key in codes:
if key in possibles_codes: if key in possibles_codes:
self.codes[ConceptParts(key)] = codes[key] self.cached_asts[ConceptParts(key)] = codes[key]
return self return self
@@ -145,7 +185,7 @@ class Concept:
Returns the digest of the event Returns the digest of the event
:return: hexa form of the sha256 :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): def to_dict(self, props_to_use=None):
""" """
@@ -153,9 +193,9 @@ class Concept:
:return: :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] props_as_dict["props"] = [(p, self.props[p].value) for p in self.props]
return props_as_dict return props_as_dict
@@ -165,9 +205,9 @@ class Concept:
:param as_dict: :param as_dict:
:return: :return:
""" """
for prop in self.props_to_serialize: for prop in PROPERTIES_TO_SERIALIZE:
if prop in as_dict: if prop in as_dict:
setattr(self, prop, as_dict[prop]) setattr(self.metadata, prop, as_dict[prop])
if "props" in as_dict: if "props" in as_dict:
for n, v in as_dict["props"]: for n, v in as_dict["props"]:
self.set_prop(n, v) self.set_prop(n, v)
+48 -51
View File
@@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept 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 evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.BaseParser import BaseParser from parsers.BaseParser import BaseParser
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError
@@ -55,7 +55,7 @@ class Sheerka(Concept):
self.debug = debug self.debug = debug
self.skip_builtins_in_db = skip_builtins_in_db self.skip_builtins_in_db = skip_builtins_in_db
def initialize(self, root_folder=None): def initialize(self, root_folder: str = None):
""" """
Starting Sheerka Starting Sheerka
Loads the current configuration Loads the current configuration
@@ -80,18 +80,6 @@ class Sheerka(Concept):
return ReturnValueConcept(self, True, self) 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): def initialize_builtin_concepts(self):
""" """
Initializes the builtin concepts Initializes the builtin concepts
@@ -107,11 +95,11 @@ class Sheerka(Concept):
else builtins_classes[str(key)]() if str(key) in builtins_classes \ else builtins_classes[str(key)]() if str(key) in builtins_classes \
else Concept(key, True, False, key) 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)] self.builtin_cache[key] = builtins_classes[str(key)]
if not self.skip_builtins_in_db: 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: if from_db is None:
log.debug(f"'{concept.name}' concept is not found in db. Adding.") log.debug(f"'{concept.name}' concept is not found in db. Adding.")
self.set_id_if_needed(concept, True) self.set_id_if_needed(concept, True)
@@ -158,7 +146,7 @@ class Sheerka(Concept):
logging.basicConfig(format=log_format, level=log_level) 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, Note to KSI: If you try to add execution context to this function,
You may end in an infinite loop You may end in an infinite loop
@@ -294,7 +282,19 @@ class Sheerka(Concept):
return return_values 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 Adds a new concept to the system
:param context: :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)) ret = self.ret(self.create_new_concept.__name__, True, self.new(BuiltinConcepts.NEW_CONCEPT, body=concept))
return ret 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 Updates the codes of the newly created concept
Basically, it runs the parsers on all parts Basically, it runs the parsers on all parts
@@ -334,16 +334,16 @@ class Sheerka(Concept):
:return: :return:
""" """
for part_key in ConceptParts: 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 == "": if source is None or not isinstance(source, str) or source == "":
# the only sources that I am sure to parse are strings # 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 # I refuse empty strings for performance, I don't want to handle useless NOPConcepts
continue continue
else: else:
concept.codes[part_key] = self.parse(context, source) concept.cached_asts[part_key] = self.parse(context, source)
for prop in concept.props: 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 # updates the code of the reference when possible
if concept.key in self.concepts_cache: if concept.key in self.concepts_cache:
@@ -352,11 +352,20 @@ class Sheerka(Concept):
# TODO : manage when there are multiple entries # TODO : manage when there are multiple entries
pass pass
else: 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): def eval_concept(self, context, concept: Concept, properties_to_eval=None):
if len(concept.codes) == 0: """
self.add_codes_to_concept(context, concept) 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: if properties_to_eval is None:
properties_to_eval = ["where", "pre", "post", "body", "props"] properties_to_eval = ["where", "pre", "post", "body", "props"]
@@ -366,13 +375,13 @@ class Sheerka(Concept):
pass pass
else: else:
part_key = ConceptParts(prop) part_key = ConceptParts(prop)
if concept.codes[part_key] is None: if concept.cached_asts[part_key] is None:
continue 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) 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. Adds a concept template in cache.
The cache is used as a proxy before looking at sdp The cache is used as a proxy before looking at sdp
@@ -416,7 +425,7 @@ class Sheerka(Concept):
unknown_concept = Concept() unknown_concept = Concept()
template = self.concepts_cache[str(BuiltinConcepts.UNKNOWN_CONCEPT)] template = self.concepts_cache[str(BuiltinConcepts.UNKNOWN_CONCEPT)]
unknown_concept.update_from(template) unknown_concept.update_from(template)
unknown_concept.body = concept_key unknown_concept.metadata.body = concept_key
return unknown_concept return unknown_concept
def new(self, concept_key, **kwargs): def new(self, concept_key, **kwargs):
@@ -431,7 +440,7 @@ class Sheerka(Concept):
def new_from_template(t, k, **kwargs_): def new_from_template(t, k, **kwargs_):
# manage singleton # manage singleton
if t.is_unique: if t.metadata.is_unique:
return t return t
# otherwise, create another instance # otherwise, create another instance
@@ -442,6 +451,8 @@ class Sheerka(Concept):
for k, v in kwargs_.items(): for k, v in kwargs_.items():
if k in concept.props: if k in concept.props:
concept.set_prop(k, v) concept.set_prop(k, v)
elif k in PROPERTIES_FOR_DIGEST:
setattr(concept.metadata, k, v)
elif hasattr(concept, k): elif hasattr(concept, k):
setattr(concept, k, v) setattr(concept, k, v)
else: else:
@@ -462,7 +473,7 @@ class Sheerka(Concept):
concepts = [new_from_template(t, concept_key, **kwargs) for t in template] concepts = [new_from_template(t, concept_key, **kwargs) for t in template]
return self.new(BuiltinConcepts.ENUMERATION, body=concepts) 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 Creates and returns a ReturnValue concept
:param who: :param who:
@@ -569,32 +580,18 @@ class Sheerka(Concept):
return sorted(res, key=lambda i: int(i.id)) return sorted(res, key=lambda i: int(i.id))
def test(self):
return f"I have access to Sheerka !"
@staticmethod @staticmethod
def get_builtins_classes_as_dict(): def get_builtins_classes_as_dict():
res = {} res = {}
for c in core.utils.get_classes("core.builtin_concepts"): for c in core.utils.get_classes("core.builtin_concepts"):
if issubclass(c, Concept) and c != Concept: if issubclass(c, Concept) and c != Concept:
res[c().key] = c res[c().metadata.key] = c
return res 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 @dataclass
class ExecutionContext: class ExecutionContext:
+1 -1
View File
@@ -40,7 +40,7 @@ class AddConceptEvaluator(OneReturnValueEvaluator):
# update the parts # update the parts
source = self.get_source(part_ret_val) source = self.get_source(part_ret_val)
setattr(concept, prop, source) setattr(concept.metadata, prop, source)
# try to find what can be a property # try to find what can be a property
for p in self.get_props(part_ret_val): for p in self.get_props(part_ret_val):
+5 -5
View File
@@ -29,8 +29,8 @@ class ConceptEvaluator(OneReturnValueEvaluator):
# pre condition should already be validated by the parser. # pre condition should already be validated by the parser.
# It's a mandatory condition for the concept before it can be recognized # It's a mandatory condition for the concept before it can be recognized
if len(concept.codes) == 0: if len(concept.cached_asts) == 0:
sheerka.add_codes_to_concept(context, concept) sheerka.initialize_concept_asts(context, concept)
# TODO; check pre # TODO; check pre
# if pre is not true, return Concept with a false value # if pre is not true, return Concept with a false value
@@ -38,7 +38,7 @@ class ConceptEvaluator(OneReturnValueEvaluator):
# Evaluate the properties # Evaluate the properties
for prop in concept.props: for prop in concept.props:
sub_context = context.push(self.name, f"Evaluating property '{prop}'", concept) 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: if res.status:
concept.set_prop(prop, res.value) concept.set_prop(prop, res.value)
else: else:
@@ -49,11 +49,11 @@ class ConceptEvaluator(OneReturnValueEvaluator):
parents=[return_value]) parents=[return_value])
# Returns the concept when no body # 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]) return sheerka.ret(self.name, True, concept, parents=[return_value])
# Evaluate the body otherwise # Evaluate the body otherwise
body = concept.codes[ConceptParts.BODY] body = concept.cached_asts[ConceptParts.BODY]
if body is None: if body is None:
raise NotImplementedError("Seems weird !") raise NotImplementedError("Seems weird !")
+1 -1
View File
@@ -40,5 +40,5 @@ class DuplicateConceptEvaluator(AllReturnValuesEvaluator):
return sheerka.ret( return sheerka.ret(
self.name, self.name,
False, False,
sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, obj=self.already_defined), sheerka.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=self.already_defined),
parents=return_values) parents=return_values)
+1 -1
View File
@@ -48,7 +48,7 @@ class TooManySuccessEvaluator(AllReturnValuesEvaluator):
def eval(self, context, return_values): def eval(self, context, return_values):
sheerka = context.sheerka sheerka = context.sheerka
if not core.builtin_helpers.is_same_success(sheerka, self.success): 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 sheerka.ret(self.name, False, too_many_success, parents=return_values)
return None return None
+6 -6
View File
@@ -1,7 +1,7 @@
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
from parsers.BaseParser import BaseParser from parsers.BaseParser import BaseParser
from core.tokenizer import Tokenizer, Keywords, TokenKind from core.tokenizer import Tokenizer, Keywords, TokenKind
from core.concept import Concept from core.concept import Concept, VARIABLE_PREFIX
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -28,7 +28,7 @@ class ExactConceptParser(BaseParser):
sheerka = context.sheerka sheerka = context.sheerka
words = self.get_words(text) words = self.get_words(text)
if len(words) > self.MAX_WORDS_SIZE: 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 recognized = False
for combination in self.combinations(words): for combination in self.combinations(words):
@@ -44,8 +44,8 @@ class ExactConceptParser(BaseParser):
for concept in concepts: for concept in concepts:
# update the properties if needed # update the properties if needed
for i, token in enumerate(combination): for i, token in enumerate(combination):
if token.startswith(Concept.PROPERTY_PREFIX): if token.startswith(VARIABLE_PREFIX):
index = int(token[len(Concept.PROPERTY_PREFIX):]) index = int(token[len(VARIABLE_PREFIX):])
concept.set_prop_by_index(index, words[i]) concept.set_prop_by_index(index, words[i])
res.append(ReturnValueConcept(self.name, True, concept)) res.append(ReturnValueConcept(self.name, True, concept))
log.debug(f"Recognized '{text}' as '{concept}'") log.debug(f"Recognized '{text}' as '{concept}'")
@@ -55,7 +55,7 @@ class ExactConceptParser(BaseParser):
return res return res
log.debug(f"Failed to recognize {words}") 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 @staticmethod
def get_words(text): def get_words(text):
@@ -111,7 +111,7 @@ class ExactConceptParser(BaseParser):
for i in indices: for i in indices:
value = pool[i] value = pool[i]
if value not in vars: if value not in vars:
vars[pool[i]] = f"{Concept.PROPERTY_PREFIX}{k}" vars[pool[i]] = f"{VARIABLE_PREFIX}{k}"
k += 1 k += 1
# create tuple # create tuple
+2 -2
View File
@@ -98,7 +98,7 @@ def test_i_can_manage_unknown_concept():
assert not res.status assert not res.status
assert context.sheerka.isinstance(res.value, BuiltinConcepts.UNKNOWN_CONCEPT) 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(): 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 not res.status
assert context.sheerka.isinstance(res.value, BuiltinConcepts.CONCEPT_TOO_LONG) 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(): def test_i_can_detect_concept_from_tokens():
+3 -3
View File
@@ -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) res = core.builtin_helpers.expect_one(get_context(sheerka), items)
assert not res.status assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_SUCCESS) assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_SUCCESS)
assert res.value.obj == items assert res.value.body == items
assert res.parents == 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) res = core.builtin_helpers.expect_one(get_context(sheerka), items)
assert not res.status assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS) assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS)
assert res.value.obj == items assert res.value.body == items
assert res.parents == 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) res = core.builtin_helpers.expect_one(get_context(sheerka), items)
assert not res.status assert not res.status
assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS) assert sheerka.isinstance(res.value, BuiltinConcepts.TOO_MANY_ERRORS)
assert res.value.obj == items assert res.value.body == items
assert res.parents == items assert res.parents == items
+1 -1
View File
@@ -17,7 +17,7 @@ def test_i_can_get_concept_key(name, variables, expected):
concept.set_prop(v, None) concept.set_prop(v, None)
concept.init_key() concept.init_key()
assert concept.key == expected assert concept.metadata.key == expected
def test_i_can_serialize(): def test_i_can_serialize():
+17 -17
View File
@@ -6,7 +6,7 @@ from os import path
import shutil import shutil
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept 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 core.sheerka import Sheerka, ExecutionContext
from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator from evaluators.MutipleSameSuccessEvaluator import MultipleSameSuccessEvaluator
from sdp.sheerkaDataProvider import SheerkaDataProvider from sdp.sheerkaDataProvider import SheerkaDataProvider
@@ -63,13 +63,13 @@ def test_builtin_concepts_are_initialized():
def test_builtin_concepts_can_be_updated(): def test_builtin_concepts_can_be_updated():
sheerka = get_sheerka(root_folder, skip_builtins_in_db=False) sheerka = get_sheerka(root_folder, skip_builtins_in_db=False)
loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA) 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.sdp.modify("Test", sheerka.CONCEPTS_ENTRY, loaded_sheerka.key, loaded_sheerka)
sheerka = get_sheerka(root_folder) sheerka = get_sheerka(root_folder)
loaded_sheerka = sheerka.get(BuiltinConcepts.SHEERKA) 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(): 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) assert sheerka.isinstance(res.value, BuiltinConcepts.NEW_CONCEPT)
concept_found = res.value.body concept_found = res.value.body
for prop in Concept.props_to_serialize: for prop in PROPERTIES_TO_SERIALIZE:
assert getattr(concept_found, prop) == getattr(concept, prop) assert getattr(concept_found.metadata, prop) == getattr(concept.metadata, prop)
assert concept_found.key == "__var__0 + __var__1" assert concept_found.key == "__var__0 + __var__1"
assert concept_found.id == "1001" 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() sheerka = get_sheerka()
concept1 = get_default_concept() concept1 = get_default_concept()
concept2 = 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) res1 = sheerka.create_new_concept(get_context(sheerka), concept1)
res2 = sheerka.create_new_concept(get_context(sheerka), concept2) 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() sheerka = get_sheerka()
concept1 = get_default_concept() concept1 = get_default_concept()
concept2 = 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) res1 = sheerka.create_new_concept(get_context(sheerka), concept1)
res2 = sheerka.create_new_concept(get_context(sheerka), concept2) 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") new = sheerka.new(concept.key, a=10, b="value")
assert sheerka.isinstance(new, concept) assert sheerka.isinstance(new, concept)
for prop in Concept.props_to_serialize: for prop in PROPERTIES_TO_SERIALIZE:
assert getattr(new, prop) == getattr(concept, prop) assert getattr(new.metadata, prop) == getattr(concept.metadata, prop)
assert new.props["a"].value == 10 assert new.props["a"].value == 10
assert new.props["b"].value == "value" assert new.props["b"].value == "value"
@@ -398,8 +398,8 @@ as:
""" """
expected = get_default_concept() expected = get_default_concept()
expected.id = "1001" expected.metadata.id = "1001"
expected.desc = None expected.metadata.desc = None
expected.init_key() expected.init_key()
sheerka = get_sheerka() sheerka = get_sheerka()
@@ -411,8 +411,8 @@ as:
concept_saved = res[0].value.body concept_saved = res[0].value.body
for prop in Concept.props_to_serialize: for prop in PROPERTIES_TO_SERIALIZE:
assert getattr(concept_saved, prop) == getattr(expected, prop) assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop)
assert concept_saved.key in sheerka.concepts_cache assert concept_saved.key in sheerka.concepts_cache
assert sheerka.sdp.io.exists( 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") 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 = 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 len(res) == 1
assert res[0].status 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 concept_saved = res[0].value.body
for prop in Concept.props_to_serialize: for prop in PROPERTIES_TO_SERIALIZE:
assert getattr(concept_saved, prop) == getattr(expected, prop) assert getattr(concept_saved.metadata, prop) == getattr(expected.metadata, prop)
assert concept_saved.key in sheerka.concepts_cache assert concept_saved.key in sheerka.concepts_cache
assert sheerka.sdp.io.exists( 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 not res[0].status
assert sheerka.isinstance(res[0].value, BuiltinConcepts.TOO_MANY_SUCCESS) assert sheerka.isinstance(res[0].value, BuiltinConcepts.TOO_MANY_SUCCESS)
concepts = res[0].value.obj concepts = res[0].value.body
assert len(concepts) == 2 assert len(concepts) == 2
sorted_values = sorted(concepts, key=lambda x: x.value) sorted_values = sorted(concepts, key=lambda x: x.value)
assert sorted_values[0].value == "hello another value" assert sorted_values[0].value == "hello another value"