Added chicken and egg recursion detection
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
from core.builtin_concepts import BuiltinConcepts
|
||||
from core.concept import Concept, DoNotResolve, ConceptParts
|
||||
from core.concept import Concept, DoNotResolve, ConceptParts, InfiniteRecursionResolved
|
||||
import core.builtin_helpers
|
||||
|
||||
CONCEPT_EVALUATION_STEPS = [
|
||||
@@ -13,6 +13,55 @@ class SheerkaEvaluateConcept:
|
||||
self.sheerka = sheerka
|
||||
self.logger_name = self.evaluate_concept.__name__
|
||||
|
||||
@staticmethod
|
||||
def infinite_recursion_detected(context, concept):
|
||||
if concept is None:
|
||||
return False
|
||||
|
||||
parent = context.get_parent()
|
||||
while parent is not None:
|
||||
if parent.who == context.who and parent.obj == concept:
|
||||
return True
|
||||
|
||||
parent = parent.get_parent()
|
||||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_infinite_recursion_resolution(obj):
|
||||
if isinstance(obj, InfiniteRecursionResolved):
|
||||
return obj
|
||||
|
||||
if isinstance(obj, Concept) and isinstance(obj.body, InfiniteRecursionResolved):
|
||||
return obj.body
|
||||
|
||||
return None
|
||||
|
||||
def manage_infinite_recursion(self, context):
|
||||
"""
|
||||
We look for the fist parent that has a body that means something
|
||||
We use the eval function with no local or global
|
||||
:param context:
|
||||
:return:
|
||||
"""
|
||||
|
||||
parent = context
|
||||
concepts_found = set()
|
||||
while parent and parent.obj:
|
||||
if parent.who == context.who and parent.desc == context.desc:
|
||||
body = parent.obj.metadata.body
|
||||
try:
|
||||
return self.sheerka.ret(self.logger_name, True, InfiniteRecursionResolved(eval(body)))
|
||||
except Exception:
|
||||
pass
|
||||
concepts_found.add(parent.obj)
|
||||
parent = parent.get_parent()
|
||||
|
||||
return self.sheerka.ret(
|
||||
self.logger_name,
|
||||
False,
|
||||
self.sheerka.new(BuiltinConcepts.CHICKEN_AND_EGG, body=concepts_found))
|
||||
|
||||
def initialize_concept_asts(self, context, concept: Concept, logger=None):
|
||||
"""
|
||||
Updates the codes of the newly created concept
|
||||
@@ -75,6 +124,15 @@ class SheerkaEvaluateConcept:
|
||||
if isinstance(to_resolve, DoNotResolve):
|
||||
return to_resolve.value
|
||||
|
||||
# manage infinite loop
|
||||
if self.infinite_recursion_detected(context, current_concept):
|
||||
with context.push(desc="Infinite recursion detected", obj=current_concept) as sub_context:
|
||||
# I create a sub context in order to log what happened
|
||||
sub_context.log_new(logger)
|
||||
ret_val = self.manage_infinite_recursion(context)
|
||||
sub_context.add_values(return_values=ret_val)
|
||||
return ret_val.body
|
||||
|
||||
desc = f"Evaluating {current_prop} (concept={current_concept})"
|
||||
context.log(logger, desc, self.logger_name)
|
||||
with context.push(desc=desc, obj=current_concept) as sub_context:
|
||||
@@ -92,7 +150,8 @@ class SheerkaEvaluateConcept:
|
||||
|
||||
# otherwise, execute all return values to find out what is the value
|
||||
else:
|
||||
r = self.sheerka.execute(sub_context, to_resolve, CONCEPT_EVALUATION_STEPS, logger)
|
||||
use_copy = [r for r in to_resolve] if hasattr(to_resolve, "__iter__") else to_resolve
|
||||
r = self.sheerka.execute(sub_context, use_copy, CONCEPT_EVALUATION_STEPS, logger)
|
||||
one_r = core.builtin_helpers.expect_one(context, r)
|
||||
sub_context.add_values(return_values=one_r)
|
||||
if one_r.status:
|
||||
@@ -100,10 +159,11 @@ class SheerkaEvaluateConcept:
|
||||
else:
|
||||
error = one_r.value
|
||||
|
||||
return self.sheerka.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
body=error,
|
||||
concept=current_concept,
|
||||
property_name=current_prop)
|
||||
return error if self.sheerka.isinstance(error, BuiltinConcepts.CHICKEN_AND_EGG) \
|
||||
else self.sheerka.new(BuiltinConcepts.CONCEPT_EVAL_ERROR,
|
||||
body=error,
|
||||
concept=current_concept,
|
||||
property_name=current_prop)
|
||||
|
||||
def resolve_list(self, context, list_to_resolve, current_prop, current_concept, logger):
|
||||
"""When dealing with a list, there are two possibilities"""
|
||||
@@ -168,7 +228,7 @@ class SheerkaEvaluateConcept:
|
||||
else:
|
||||
# Do not send the current concept for the properties
|
||||
resolved = self.resolve(context, prop_ast, prop_name, None, logger)
|
||||
if context.sheerka.isinstance(resolved, BuiltinConcepts.CONCEPT_EVAL_ERROR):
|
||||
if isinstance(resolved, Concept) and not context.sheerka.is_success(resolved):
|
||||
resolved.set_prop("concept", concept) # since current concept was not sent
|
||||
return resolved
|
||||
else:
|
||||
@@ -178,10 +238,10 @@ class SheerkaEvaluateConcept:
|
||||
if part_key in concept.compiled and concept.compiled[part_key] is not None:
|
||||
metadata_ast = concept.compiled[part_key]
|
||||
resolved = self.resolve(context, metadata_ast, part_key, concept, logger)
|
||||
if context.sheerka.isinstance(resolved, BuiltinConcepts.CONCEPT_EVAL_ERROR):
|
||||
if isinstance(resolved, Concept) and not context.sheerka.is_success(resolved):
|
||||
return resolved
|
||||
else:
|
||||
concept.values[part_key] = resolved
|
||||
concept.values[part_key] = self.get_infinite_recursion_resolution(resolved) or resolved
|
||||
|
||||
# validate where clause
|
||||
if concept.metadata.where is not None:
|
||||
|
||||
Reference in New Issue
Block a user