Refactord Concept class to regroup all builtins properties into a ConceptMetadata class
This commit is contained in:
+18
-16
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
+79
-39
@@ -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)
|
||||
|
||||
+48
-51
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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 !")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
+18
-18
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user