220 lines
6.0 KiB
Python
220 lines
6.0 KiB
Python
from __future__ import annotations
|
|
|
|
import time
|
|
|
|
from core.Event import Event
|
|
|
|
|
|
class ContextActions:
|
|
TESTING = "Testing"
|
|
INIT_SHEERKA = "Init Sheerka"
|
|
EVALUATE_USER_INPUT = "Evaluate user input"
|
|
EVALUATING_STEP = "Evaluating step"
|
|
EVALUATING_ITERATION = "Evaluating iteration"
|
|
BEFORE_PARSING = "Before parsing"
|
|
PARSING = "Parsing"
|
|
AFTER_PARSING = "After parsing"
|
|
BEFORE_EVALUATION = "Before evaluation"
|
|
EVALUATION = "Evaluation"
|
|
AFTER_EVALUATION = "After Evaluation"
|
|
|
|
EVALUATING_CONCEPT = "Evaluating concept"
|
|
BUILD_CONCEPT = "Building all attributes"
|
|
BUILD_CONCEPT_ATTR = "Building one attribute"
|
|
EVAL_CONCEPT = "Evaluating all attributes"
|
|
EVAL_CONCEPT_ATTR = "Evaluating one attribute"
|
|
|
|
|
|
class ContextHint:
|
|
REDUCE_CONCEPTS = "Reduce Concepts" # to tell the process to only keep the meaningful results
|
|
EXPRESSION_ONLY_REQUESTED = "Expression Only"
|
|
|
|
|
|
ids = {} # keep track of the next execution context id, for a given event id
|
|
|
|
|
|
def get_next_id(event_digest):
|
|
"""
|
|
For a given event, give the next id
|
|
:param event_digest:
|
|
:type event_digest:
|
|
:return:
|
|
:rtype:
|
|
"""
|
|
if event_digest in ids:
|
|
ids[event_digest] += 1
|
|
else:
|
|
ids[event_digest] = 0
|
|
return ids[event_digest]
|
|
|
|
|
|
class ExecutionContext:
|
|
"""
|
|
To keep track of the execution of a request
|
|
Note that the protected hints are working correctly only if the hint is added BEFORE the creation of the child
|
|
"""
|
|
|
|
def __init__(self,
|
|
who: str,
|
|
event: Event,
|
|
sheerka,
|
|
action: ContextActions,
|
|
action_context: object,
|
|
desc: str = None,
|
|
logger=None,
|
|
global_hints=None,
|
|
protected_hints=None,
|
|
parent: ExecutionContext = None):
|
|
self._id = get_next_id(event.get_digest())
|
|
self._parent = parent
|
|
self._children = []
|
|
self._start = 0 # when the execution starts (to measure elapsed time)
|
|
self._stop = 0 # when the execution stops (to measure elapses time)
|
|
self._logger = logger
|
|
|
|
self.who = who # who is asking
|
|
self.event = event # what was the (original) trigger
|
|
self.sheerka = sheerka # sheerka
|
|
self.action = action
|
|
self.action_context = action_context
|
|
self.desc = desc # human description of what is going on
|
|
|
|
self.private_hints = set()
|
|
self.protected_hints = set() if protected_hints is None else protected_hints.copy()
|
|
self.global_hints = set() if global_hints is None else global_hints
|
|
|
|
self.inputs = {} # what were the parameters of the execution context
|
|
self.values = {} # what was produced by the execution context
|
|
|
|
def __repr__(self):
|
|
msg = f"ExecutionContext(who={self.who}, id={self._id}, action={self.action}, context={self.action_context}"
|
|
if self.desc:
|
|
msg += f", desc='{self.desc}'"
|
|
msg += ")"
|
|
return msg
|
|
|
|
def __eq__(self, other):
|
|
if id(self) == id(other):
|
|
return True
|
|
|
|
if not isinstance(other, ExecutionContext):
|
|
return False
|
|
|
|
return self.long_id == other.long_id
|
|
|
|
def __hash__(self):
|
|
return hash(self.long_id)
|
|
|
|
@property
|
|
def long_id(self):
|
|
return f"{self.event.get_digest()}:{self._id}"
|
|
|
|
@property
|
|
def medium_id(self):
|
|
return f"{self.event.get_digest()[:8]}:{self._id}"
|
|
|
|
@property
|
|
def id(self):
|
|
return self._id
|
|
|
|
@property
|
|
def elapsed(self):
|
|
if self._start == 0:
|
|
return 0
|
|
|
|
return (self._stop if self._stop > 0 else time.time_ns()) - self._start
|
|
|
|
@property
|
|
def elapsed_str(self):
|
|
nano_sec = self.elapsed
|
|
dt = nano_sec / 1e6
|
|
return f"{dt} ms" if dt < 1000 else f"{dt / 1000} s"
|
|
|
|
def add_inputs(self, **kwargs):
|
|
"""
|
|
When entering stacking an ExecutionContext, list of variable that are worth to trace
|
|
:param kwargs:
|
|
:type kwargs:
|
|
:return:
|
|
:rtype:
|
|
"""
|
|
self.inputs.update(kwargs)
|
|
return self
|
|
|
|
def add_values(self, **kwargs):
|
|
"""
|
|
When popping from an ExecutionContext, list of variable that are worth to trace
|
|
:param kwargs:
|
|
:type kwargs:
|
|
:return:
|
|
:rtype:
|
|
"""
|
|
self.values.update(kwargs)
|
|
return self
|
|
|
|
def push(self,
|
|
who: str,
|
|
action: ContextActions,
|
|
action_context: object,
|
|
desc: str = None,
|
|
logger=None):
|
|
child = ExecutionContext(
|
|
who,
|
|
self.event,
|
|
self.sheerka,
|
|
action,
|
|
action_context,
|
|
desc,
|
|
logger or self._logger,
|
|
self.global_hints,
|
|
self.protected_hints,
|
|
self
|
|
)
|
|
self._children.append(child)
|
|
return child
|
|
|
|
def get_parent(self):
|
|
return self._parent
|
|
|
|
def get_children(self, level=-1):
|
|
"""
|
|
recursively look for children
|
|
:return:
|
|
:rtype:
|
|
"""
|
|
for child in self._children:
|
|
yield child
|
|
if level != 1:
|
|
yield from child.get_children(level - 1)
|
|
|
|
def get_parents(self, level=-1):
|
|
"""
|
|
recursively look for parent
|
|
:return:
|
|
:rtype:
|
|
"""
|
|
if level == 0 or self._parent is None:
|
|
return
|
|
|
|
yield self._parent
|
|
yield from self._parent.get_parents(level - 1)
|
|
|
|
def in_context(self, hint: ContextHint):
|
|
return hint in self.protected_hints or \
|
|
hint in self.global_hints or \
|
|
hint in self.private_hints
|
|
|
|
def get_from_short_term_memory(self, key):
|
|
return self.sheerka.get_from_short_term_memory(self, key)
|
|
|
|
def log(self, message: str, who: str = None):
|
|
"""Send debug information to logger"""
|
|
pass
|
|
|
|
def __enter__(self):
|
|
self._start = time.time_ns()
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
self._stop = time.time_ns()
|