Enhanced PythonEvaluator to accept concepts

This commit is contained in:
2019-11-21 11:52:15 +01:00
parent cb6be9fec7
commit 714f4f5dd0
20 changed files with 964 additions and 208 deletions
+104 -50
View File
@@ -5,11 +5,14 @@ from evaluators.BaseEvaluator import OneReturnValueEvaluator
from parsers.BaseParser import BaseParser
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event, SheerkaDataProviderDuplicateKeyError
import core.utils
import core.builtin_helpers
import logging
log = logging.getLogger(__name__)
concept_evaluation_steps = [BuiltinConcepts.EVALUATION, BuiltinConcepts.AFTER_EVALUATION]
class Sheerka(Concept):
"""
@@ -150,6 +153,12 @@ class Sheerka(Concept):
logging.basicConfig(format=log_format, level=log_level)
def eval(self, text):
"""
Note to KSI: If you try to add execution context to this function,
You may end in an infinite loop
:param text:
:return:
"""
evt_digest = self.sdp.save_event(Event(text))
exec_context = ExecutionContext(self.key, evt_digest, self)
@@ -174,32 +183,6 @@ class Sheerka(Concept):
return return_values
def expect_one(self, context, items):
if not isinstance(items, list):
items = [items]
if len(items) == 0:
return self.ret(context.who, False, self.new(BuiltinConcepts.IS_EMPTY, obj=items))
successful_results = [item for item in items if item.status]
number_of_successful = len(successful_results)
total_items = len(items)
# remove errors when a winner is found
if number_of_successful == 1:
# log.debug(f"1 / {total_items} good item found.")
return successful_results[0]
# too many winners, which one to choose ?
if number_of_successful > 1:
log.debug(f"{number_of_successful} / {total_items} good items. Too many success")
return self.ret(context.who, False, self.new(BuiltinConcepts.TOO_MANY_SUCCESS, obj=successful_results))
# only errors, i cannot help you
log.debug(f"{total_items} items. Only errors")
return self.ret(context.who, False, self.new(BuiltinConcepts.TOO_MANY_ERRORS, obj=items))
def parse(self, context, text):
result = []
if log.isEnabledFor(logging.DEBUG):
@@ -356,6 +339,33 @@ class Sheerka(Concept):
for prop in concept.props:
concept.codes[prop] = self.parse(context, concept.props[prop].value)
# updates the code of the reference when possible
if concept.key in self.concepts_cache:
entry = self.concepts_cache[concept.key]
if isinstance(entry, list):
# TODO : manage when there are multiple entries
pass
else:
self.concepts_cache[concept.key].codes = concept.codes
def eval_concept(self, context, concept, properties_to_eval=None):
if len(concept.codes) == 0:
self.add_codes_to_concept(context, concept)
if properties_to_eval is None:
properties_to_eval = ["where", "pre", "post", "body", "props"]
for prop in properties_to_eval:
if prop == "props":
pass
else:
part_key = ConceptParts(prop)
if concept.codes[part_key] is None:
continue
res = self.chain_process(context, concept.codes[part_key], concept_evaluation_steps)
res = core.builtin_helpers.expect_one(context, res)
setattr(concept, prop, res.value)
def add_in_cache(self, concept):
"""
Adds a concept template in cache.
@@ -413,16 +423,37 @@ class Sheerka(Concept):
"""
template = self.get(concept_key)
def new_from_template(t, k, **kwargs_):
# manage singleton
if t.is_unique:
return t
# otherwise, create another instance
concept = self.builtin_cache[k]() if k in self.builtin_cache else Concept()
concept.update_from(t)
# update the properties
for k, v in kwargs_.items():
if k in concept.props:
concept.set_prop(k, v)
elif hasattr(concept, k):
setattr(concept, k, v)
else:
return self.new(BuiltinConcepts.UNKNOWN_PROPERTY, body=k, concept=concept)
# TODO : add the concept to the list of known concepts (self.instances)
return concept
# manage concept not found
if self.isinstance(template, BuiltinConcepts.UNKNOWN_CONCEPT) and \
concept_key != BuiltinConcepts.UNKNOWN_CONCEPT:
return template
if not isinstance(template, list):
return self._new_from_template(template, concept_key, **kwargs)
return new_from_template(template, concept_key, **kwargs)
# if template is a list, it means that there a multiple concepts under the same key
concepts = [self._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)
def ret(self, who, status, value, message=None, parents=None):
@@ -443,6 +474,29 @@ class Sheerka(Concept):
message=message,
parents=parents)
def value(self, obj, allow_none_body=False):
if obj is None:
return None
if not isinstance(obj, Concept):
return obj
if hasattr(obj, "get_value"):
return obj.get_value()
if obj.body is not None:
return obj.body
return obj if allow_none_body else self.new(BuiltinConcepts.CANNOT_RESOLVE_VALUE_ERROR, body=obj)
def values(self, objs):
if not (isinstance(objs, list) or
self.isinstance(objs, BuiltinConcepts.LIST) or
self.isinstance(objs, BuiltinConcepts.ENUMERATION)):
objs = [objs]
return (self.value(obj) for obj in objs)
def isinstance(self, a, b):
"""
return true if the concept a is an instance of the concept b
@@ -463,6 +517,27 @@ class Sheerka(Concept):
# for example, if a is a color, it will be found the entry 'All_Colors'
return a.key == b_key
def isa(self, a, b):
"""
return true if the concept a is a b
Will handle when the keyword isa will be implemented
:param a:
:param b:
:return:
"""
if isinstance(a, BuiltinConcepts): # common KSI error ;-)
raise SyntaxError("Remember that the first parameter of isinstance MUST be a concept")
if not isinstance(a, Concept):
return False
b_key = b.key if isinstance(b, Concept) else str(b)
# TODO : manage when a is the list of all possible b
# for example, if a is a color, it will be found the entry 'All_Colors'
return a.key == b_key
def get_evaluator_name(self, name):
if self.evaluators_prefix is None:
base_evaluator_class = core.utils.get_class("evaluators.BaseEvaluator.BaseEvaluator")
@@ -486,28 +561,7 @@ class Sheerka(Concept):
else:
res.append(item)
return sorted(res, key=lambda i: i.key)
def _new_from_template(self, template, concept_key, **kwargs):
# manage singleton
if template.is_unique:
return template
# otherwise, create another instance
concept = self.builtin_cache[concept_key]() if concept_key in self.builtin_cache else Concept()
concept.update_from(template)
# update the properties
for k, v in kwargs.items():
if k in concept.props:
concept.set_prop(k, v)
elif hasattr(concept, k):
setattr(concept, k, v)
else:
return self.new(BuiltinConcepts.UNKNOWN_PROPERTY, body=k, concept=concept)
# TODO : add the concept to the list of known concepts (self.instances)
return concept
return sorted(res, key=lambda i: int(i.id))
@staticmethod
def get_builtins_classes_as_dict():