You must now use 'eval' to get the body of a concept

This commit is contained in:
2019-12-24 16:58:09 +01:00
parent 5c90b07e1a
commit 44e4b75cf8
37 changed files with 1003 additions and 383 deletions
+110 -40
View File
@@ -1,13 +1,13 @@
from dataclasses import dataclass, field
from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConcept, BuiltinErrors
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_DIGEST
from core.concept import Concept, ConceptParts, PROPERTIES_FOR_NEW
from parsers.BaseParser import BaseParser
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError
import core.utils
import core.builtin_helpers
from core.sheerka_logger import console_handler, get_logger
from core.sheerka_logger import console_handler
import logging
@@ -85,8 +85,9 @@ class Sheerka(Concept):
if self.sdp.first_time:
self.sdp.set_key(self.USER_CONCEPTS_KEYS, 1000)
evt_digest = self.sdp.save_event(Event("Initializing Sheerka."))
exec_context = ExecutionContext(self.key, evt_digest, self)
event = Event("Initializing Sheerka.")
self.sdp.save_event(event)
exec_context = ExecutionContext(self.key, event, self)
self.initialize_builtin_concepts()
self.initialize_builtin_parsers()
@@ -181,19 +182,24 @@ class Sheerka(Concept):
:return:
"""
self.log.debug(f"Processing user input '{text}', {user_name=}.")
evt_digest = self.sdp.save_event(Event(text, user_name))
event = Event(text, user_name)
evt_digest = self.sdp.save_event(event)
self.log.debug(f"{evt_digest=}")
execution_context = ExecutionContext(self.key, evt_digest, self)
execution_context = ExecutionContext(self.key, event, self)
user_input = self.ret(self.name, True, self.new(BuiltinConcepts.USER_INPUT, body=text, user_name=user_name))
reduce_requested = self.ret(self.name, True, self.new(BuiltinConcepts.REDUCE_REQUESTED))
steps = [
BuiltinConcepts.BEFORE_PARSING,
BuiltinConcepts.PARSING,
BuiltinConcepts.AFTER_PARSING,
BuiltinConcepts.BEFORE_EVALUATION,
BuiltinConcepts.EVALUATION,
BuiltinConcepts.AFTER_EVALUATION
]
return self.execute(execution_context, user_input, steps)
return self.execute(execution_context, [user_input, reduce_requested], steps)
def _call_parsers(self, execution_context, return_values, logger=None):
@@ -205,6 +211,7 @@ class Sheerka(Concept):
for return_value in return_values:
# make sure we only parse user input
if not return_value.status or not self.isinstance(return_value.body, BuiltinConcepts.USER_INPUT):
result.append(return_value)
continue
to_parse = self.value(return_value)
@@ -232,6 +239,26 @@ class Sheerka(Concept):
def _call_evaluators(self, execution_context, return_values, process_step, evaluation_context=None, logger=None):
def _preprocess_evaluators(context, evaluators):
if not context.preprocess:
return evaluators
if not hasattr(evaluators, "__iter__"):
single_one = True
evaluators = [evaluators]
else:
single_one = False
for preprocess in context.preprocess:
for e in evaluators:
if preprocess.props["name"].value == e.name:
for prop, value in preprocess.props.items():
if prop == "name":
continue
if hasattr(e, prop):
setattr(e, prop, value.value)
return evaluators[0] if single_one else evaluators
# return_values must be a list
if not isinstance(return_values, list):
return_values = [return_values]
@@ -255,6 +282,10 @@ class Sheerka(Concept):
# The first one to be applied will be the one with the highest priority
grouped_evaluators = {}
instantiated_evaluators = [e_class() for e_class in self.evaluators]
# pre-process evaluators if needed
instantiated_evaluators = _preprocess_evaluators(execution_context, instantiated_evaluators)
for evaluator in [e for e in instantiated_evaluators if e.enabled and process_step in e.steps]:
if logger:
evaluator.log = logger
@@ -273,6 +304,7 @@ class Sheerka(Concept):
evaluated_items = []
to_delete = []
for evaluator in grouped_evaluators[priority]:
evaluator = _preprocess_evaluators(execution_context, evaluator.__class__()) # fresh copy
# process evaluators that work on return value
from evaluators.BaseEvaluator import OneReturnValueEvaluator
@@ -334,12 +366,16 @@ class Sheerka(Concept):
for step in execution_steps:
sub_context = execution_context.push(step=step)
sub_context.log(logger or self.log, f"{step=}, context='{sub_context}'")
copy = return_values[:] if hasattr(return_values, "__iter__") else return_values
if step == BuiltinConcepts.PARSING:
return_values = self._call_parsers(sub_context, return_values, logger)
else:
return_values = self._call_evaluators(sub_context, return_values, step, None, logger)
sub_context.log_result(logger or self.log, return_values)
if copy != return_values:
sub_context.log_result(logger or self.log, return_values)
return return_values
@@ -374,7 +410,11 @@ class Sheerka(Concept):
# TODO checks if it exists in cache first
if self.sdp.exists(self.CONCEPTS_ENTRY, concept.key, concept.get_digest()):
error = SheerkaDataProviderDuplicateKeyError(self.CONCEPTS_ENTRY + "." + concept.key, concept)
return self.ret(self.create_new_concept.__name__, False, ErrorConcept(error), error.args[0])
return self.ret(
self.create_new_concept.__name__,
False,
self.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept),
error.args[0])
# set id before saving in db
self.set_id_if_needed(concept, False)
@@ -394,12 +434,18 @@ class Sheerka(Concept):
# save the new context in sdp
try:
self.sdp.add(context.event_digest, self.CONCEPTS_ENTRY, concept, use_ref=True)
self.sdp.add(context.event.get_digest(), self.CONCEPTS_ENTRY, concept, use_ref=True)
if concepts_definitions is not None:
self.sdp.set(context.event_digest, self.CONCEPTS_DEFINITIONS_ENTRY, concepts_definitions, use_ref=True)
self.sdp.set(context.event.get_digest(),
self.CONCEPTS_DEFINITIONS_ENTRY,
concepts_definitions, use_ref=True)
except SheerkaDataProviderDuplicateKeyError as error:
context.log_error(logger, "Failed to create a new concept.", who=self.create_new_concept.__name__)
return self.ret(self.create_new_concept.__name__, False, ErrorConcept(error), error.args[0])
return self.ret(
self.create_new_concept.__name__,
False,
self.new(BuiltinConcepts.CONCEPT_ALREADY_DEFINED, body=concept),
error.args[0])
# Updates the caches
self.concepts_cache[concept.key] = self.sdp.get_safe(self.CONCEPTS_ENTRY, concept.key)
@@ -427,8 +473,8 @@ class Sheerka(Concept):
assert concept_set.id
try:
ret = self.sdp.add_unique(context.event_digest, "All_" + str(concept_set.id), concept.id)
if ret == (None, None): # concept already in set
ret = self.sdp.add_unique(context.event.get_digest(), "All_" + str(concept_set.id), concept.id)
if ret == (None, None): # concept already in set
return self.ret(
self.add_concept_to_set.__name__,
False,
@@ -506,12 +552,13 @@ class Sheerka(Concept):
# to make sure of the order, it don't use ConceptParts.get_parts()
# props must be evaluated first
properties_to_eval = ["props", "where", "pre", "post", "body"]
all_metadata_to_eval = ["props", "where", "pre", "post", "body"]
for prop_to_eval in properties_to_eval:
if prop_to_eval == "props":
for metadata_to_eval in all_metadata_to_eval:
if metadata_to_eval == "props":
for prop_name in (p for p in concept.props if p in concept.cached_asts):
sub_context = context.push(desc=f"Evaluating property '{prop_name}'")
sub_context.add_preprocess(self.get_evaluator_name("Concept"), return_body=True)
res = _resolve(sub_context, concept.cached_asts[prop_name])
if res.status:
concept.set_prop(prop_name, res.value)
@@ -521,12 +568,14 @@ class Sheerka(Concept):
concept=concept,
property_name=prop_name)
else:
part_key = ConceptParts(prop_to_eval)
part_key = ConceptParts(metadata_to_eval)
if part_key in concept.cached_asts and concept.cached_asts[part_key] is not None:
sub_context = context.push(desc=f"Evaluating '{part_key}'", obj=concept)
sub_context.add_preprocess(self.get_evaluator_name("Concept"), return_body=True)
res = _resolve(sub_context, concept.cached_asts[part_key])
if res.status:
setattr(concept.metadata, prop_to_eval, res.value)
setattr(concept.metadata, metadata_to_eval, res.value)
else:
return self.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
body=res.value,
@@ -611,7 +660,7 @@ 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:
elif k in PROPERTIES_FOR_NEW:
setattr(concept.metadata, k, v)
elif hasattr(concept, k):
setattr(concept, k, v)
@@ -651,28 +700,25 @@ class Sheerka(Concept):
message=message,
parents=parents)
def value(self, obj, allow_none_body=False):
def value(self, obj, reduce_simple_list=False):
if obj is None:
return None
if self.isinstance(obj, BuiltinConcepts.RETURN_VALUE) and \
obj.status and \
self.isinstance(obj.value, BuiltinConcepts.USER_INPUT):
return obj.value.body
if not isinstance(obj, Concept):
return obj
if hasattr(obj, "get_value"):
return obj.get_value()
if obj.body is not None:
if (isinstance(obj.body, list) or isinstance(obj.body, set)) and len(obj.body) == 1:
return obj.body[0]
else:
return obj.body
if not isinstance(obj, Concept):
return obj
return obj if allow_none_body else self.new(BuiltinConcepts.CANNOT_RESOLVE_VALUE_ERROR, body=obj)
if obj.body is None:
return obj
if reduce_simple_list and (isinstance(obj.body, list) or isinstance(obj.body, set)) and len(obj.body) == 1:
body_to_use = obj.body[0]
else:
body_to_use = obj.body
return self.value(body_to_use)
def values(self, objs):
if not (isinstance(objs, list) or
@@ -786,6 +832,17 @@ class Sheerka(Concept):
defs = self.sdp.get(self.CONCEPTS_DEFINITIONS_ENTRY)
self.log.info(defs)
def dump_desc(self, concept_name):
c = self.get(concept_name)
if self.isinstance(c, BuiltinConcepts.UNKNOWN_CONCEPT):
self.log.error("Concept unknown")
return False
self.log.info(f"name : {c.name}")
self.log.info(f"bnf : {c.metadata.definition}")
self.log.info(f"key : {c.key}")
self.log.info(f"body : {c.body}")
@staticmethod
def get_builtins_classes_as_dict():
res = {}
@@ -817,7 +874,7 @@ class ExecutionContext:
def __init__(self,
who,
event_digest: str,
event: Event,
sheerka: Sheerka,
/,
desc: str = None,
@@ -827,20 +884,32 @@ class ExecutionContext:
concepts: dict = None):
self.who = who # who is asking
self.event_digest = event_digest # what was the (original) trigger
self.event = event # what was the (original) trigger
self.sheerka = sheerka # sheerka
self.step = step
self.iteration = iteration
self.preprocess = None
self.desc = desc # human description of what is going on
self.obj = obj # what is the subject of the execution context (if known)
self.concepts = concepts or {}
self.concepts = concepts or {} # cache for concepts that are specific to this execution
self._id = ExecutionContextIdManager.get_id(event_digest)
self._id = ExecutionContextIdManager.get_id(event.get_digest())
self._tab = ""
def add_preprocess(self, name, **kwargs):
preprocess = self.sheerka.new(BuiltinConcepts.EVALUATOR_PRE_PROCESS)
preprocess.set_prop("name", name)
for k, v in kwargs.items():
preprocess.set_prop(k, v)
if not self.preprocess:
self.preprocess = set()
self.preprocess.add(preprocess)
return self
@property
def id(self):
return self._id
@@ -854,7 +923,7 @@ class ExecutionContext:
iteration = kwargs.get("iteration", self.iteration)
new = ExecutionContext(
who,
self.event_digest,
self.event,
self.sheerka,
desc=desc,
obj=obj,
@@ -863,6 +932,7 @@ class ExecutionContext:
iteration=iteration,
)
new._tab = self._tab + " " * DEBUG_TAB_SIZE
new.preprocess = self.preprocess
return new
def log_new(self, logger):