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
+48 -51
View File
@@ -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: