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()