Added first version of DebugManager. Implemented draft of the rule engine

This commit is contained in:
2020-11-20 13:41:45 +01:00
parent cd066881b4
commit 315f8ea09b
156 changed files with 8388 additions and 2852 deletions
@@ -0,0 +1,440 @@
import os
import pprint
import re
from dataclasses import dataclass
from core.builtin_concepts import BuiltinConcepts
from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.services.sheerka_service import BaseService
from core.utils import CONSOLE_COLORS_MAP as CCM
from core.utils import evaluate_expression, as_bag
try:
rows, columns = os.popen('stty size', 'r').read().split()
except ValueError:
rows, columns = 50, 80
pp = pprint.PrettyPrinter(indent=2, width=columns)
class BaseDebugLogger:
ids = {}
@staticmethod
def next_id(hint):
if hint in BaseDebugLogger.ids:
BaseDebugLogger.ids[hint] += 1
else:
BaseDebugLogger.ids[hint] = 0
return BaseDebugLogger.ids[hint]
def __init__(self, debug_manager, who, method_name, context_id, debug_id):
pass
def debug_entering(self, **kwargs):
pass
def debug_var(self, name, value, is_error=False):
pass
def debug_rule(self, rule, results):
pass
def debug_log(self, text):
pass
class NullDebugLogger(BaseDebugLogger):
def __init__(self):
pass
class ConsoleDebugLogger(BaseDebugLogger):
def __init__(self, debug_manager, service_name, method_name, context_id, debug_id):
BaseDebugLogger.__init__(self, debug_manager, service_name, method_name, context_id, debug_id)
self.debug_manager = debug_manager
self.service_name = service_name
self.method_name = method_name
self.context_id = context_id
self.debug_id = debug_id
self.is_highlighted = ""
def debug_entering(self, **kwargs):
super().debug_entering(**kwargs)
str_text = f"{CCM['blue']}Entering {self.service_name}.{self.method_name} with {CCM['reset']}"
str_vars = pp.pformat(kwargs)
if "\n" not in str(str_vars):
self.debug_manager.debug(self.prefix() + str_text + str_vars)
else:
self.debug_manager.debug(self.prefix() + str_text)
self.debug_manager.debug(self.prefix() + str_vars)
def debug_var(self, name, value, is_error=False):
enabled = self.debug_manager.compute_var_debug(self.service_name,
self.method_name,
self.context_id,
name,
self.context_id)
if enabled == False:
return
color = 'red' if is_error else 'green'
str_text = f"{CCM[color]}..{name}={CCM['reset']}"
str_vars = "" if isinstance(enabled, str) else pp.pformat(value)
if "\n" not in str(str_vars):
self.debug_manager.debug(self.prefix() + str_text + str_vars)
else:
self.debug_manager.debug(self.prefix() + str_text)
self.debug_manager.debug(self.prefix() + str_vars)
def debug_rule(self, rule, results):
if not self.debug_manager.compute_debug_rule(rule.id, self.context_id, self.debug_id):
return
str_text = f"{CCM['green']}..results({rule.id})={CCM['reset']}"
str_vars = pp.pformat(results)
if "\n" not in str(str_vars):
self.debug_manager.debug(self.prefix() + str_text + str_vars)
else:
self.debug_manager.debug(self.prefix() + str_text)
self.debug_manager.debug(self.prefix() + str_vars)
def debug_log(self, text):
self.debug_manager.debug(self.prefix() + f"{CCM['blue']}..{text}{CCM['reset']}")
def prefix(self):
return f"[{self.context_id:2}][{self.debug_id:2}] {self.is_highlighted}"
@dataclass
class DebugVarSetting:
service_name: str
method_name: str
variable_name: str
context_id: int
context_children: bool
debug_id: int
debug_children: bool
enabled: bool
@dataclass
class DebugRuleSetting:
rule_id: str
context_id: int
debug_id: int
enabled: bool
class SheerkaDebugManager(BaseService):
NAME = "Debug"
PREFIX = "debug."
children_activation_regex = re.compile(r"(\d+)\+")
def __init__(self, sheerka):
super().__init__(sheerka)
self.activated = False # is debug activated
self.explicit = False # No need to activate context debug when debug mode is on
self.context_cache = set() # debug for specific context
self.variable_cache = set() # debug for specific variable
self.debug_vars_settings = []
self.debug_rules_settings = []
def initialize(self):
self.sheerka.bind_service_method(self.set_debug, True)
self.sheerka.bind_service_method(self.set_explicit, True)
self.sheerka.bind_service_method(self.activate_debug_for, True)
self.sheerka.bind_service_method(self.deactivate_debug_for, True)
self.sheerka.bind_service_method(self.debug_activated, False)
self.sheerka.bind_service_method(self.debug_activated_for, False)
self.sheerka.bind_service_method(self.get_context_debug_mode, False)
self.sheerka.bind_service_method(self.debug_rule, True)
self.sheerka.bind_service_method(self.debug_rule_activated, False)
self.sheerka.bind_service_method(self.inspect, False)
self.sheerka.bind_service_method(self.debug, False, visible=False)
self.sheerka.bind_service_method(self.get_debugger, False)
self.sheerka.bind_service_method(self.debug_var, False)
self.sheerka.bind_service_method(self.reset_debug, False)
self.sheerka.bind_service_method(self.get_debug_settings, False, as_name="debug_settings")
def initialize_deferred(self, context, is_first_time):
self.restore_values("activated",
"explicit",
"context_cache",
"variable_cache",
"debug_vars_settings",
"debug_rules_settings")
def reset(self):
"""
For test purpose
:return:
"""
self.activated = False
self.context_cache.clear()
self.variable_cache.clear()
self.debug_vars_settings.clear()
self.debug_rules_settings.clear()
def set_debug(self, context, value=True):
self.activated = value
self.sheerka.record_var(context, self.NAME, "activated", self.activated)
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def set_explicit(self, context, value=True):
self.explicit = value
self.sheerka.record_var(context, self.NAME, "explicit", self.explicit)
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def activate_debug_for(self, context, debug_id, children=False):
"""
:param context:
:param debug_id: if debug_id is str, activate variable cache, context_cache otherwise
:param children:
:return:
"""
# preprocess
if isinstance(debug_id, str) and (m := self.children_activation_regex.match(debug_id)):
debug_id = int(m.group(1))
children = True
if isinstance(debug_id, str):
self.variable_cache.add(debug_id)
self.sheerka.record_var(context, self.NAME, "variable_cache", self.variable_cache)
else:
self.context_cache.add(debug_id)
if children:
self.context_cache.add(str(debug_id) + "+")
self.sheerka.record_var(context, self.NAME, "context_cache", self.context_cache)
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def deactivate_debug_for(self, context, debug_id, children=False):
if isinstance(debug_id, str):
self.variable_cache.discard(debug_id)
self.sheerka.record_var(context, self.NAME, "variable_cache", self.variable_cache)
else:
self.context_cache.discard(debug_id)
if children:
self.context_cache.discard(str(debug_id) + "+")
self.sheerka.record_var(context, self.NAME, "context_cache", self.context_cache)
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def debug_activated(self):
return self.activated
def debug_activated_for(self, debug_id):
if not self.activated:
return None
return debug_id in self.variable_cache
def debug_rule_activated(self, rule_id, context_id):
"""
:param rule_id:
:param context_id:
:return:
"""
key = f"{rule_id}|{context_id}"
return key in self.rules_cache
def get_context_debug_mode(self, context_id):
if not self.activated:
return None, None
debug_for_children = "protected" if str(context_id) + "+" in self.context_cache else None
debug_for_self = "private" if not self.explicit or context_id in self.context_cache else None
return debug_for_self, debug_for_children
def inspect(self, context, context_id, *props):
"""
Print
:param context:
:param context_id:
:return:
"""
to_inspect = self.sheerka.get_execution_item(context, context_id)
if not isinstance(to_inspect, ExecutionContext):
return to_inspect
if not props:
props = ["inputs", "values.return_values"]
bag = as_bag(to_inspect)
res = {}
for prop in props:
res[prop] = evaluate_expression(prop, bag)
# res = {
# "return_values": to_inspect.values.get("return_values", None)
# }
pp.pprint(res)
return None
def debug(self, *args, **kwargs):
print(*args, **kwargs)
def get_debugger(self, context, who, method_name):
if self.compute_debug(who, method_name, context):
debug_id = ConsoleDebugLogger.next_id(context.event.get_digest() + str(context.id))
return ConsoleDebugLogger(self, who, method_name, context.id, debug_id)
return NullDebugLogger()
def debug_var(self, context,
service=None,
method=None,
variable=None,
context_id=None,
context_children=False,
debug_id=None,
debug_children=False,
enabled=True):
for setting in self.debug_vars_settings:
if setting.service_name == service and \
setting.method_name == method and \
setting.variable_name == variable and \
setting.context_id == context_id and \
setting.context_children == context_children and \
setting.debug_id == debug_id and \
setting.debug_children == debug_children:
setting.enabled = enabled
break
else:
self.debug_vars_settings.append(DebugVarSetting(service,
method,
variable,
context_id,
context_children,
debug_id,
debug_children,
enabled))
self.sheerka.record_var(context, self.NAME, "debug_vars_settings", self.debug_vars_settings)
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def reset_debug(self, context):
self.debug_vars_settings.clear()
self.debug_rules_settings.clear()
self.sheerka.record_var(context, self.NAME, "debug_vars_settings", self.debug_vars_settings)
self.sheerka.record_var(context, self.NAME, "debug_rules_settings", self.debug_vars_settings)
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def compute_debug(self, service_name, method_name, context):
if not self.activated:
return False
selected = []
for setting in self.debug_vars_settings:
if setting.service_name is None and setting.method_name is None and setting.context_id is None:
continue
if (setting.service_name is None or setting.service_name == service_name) and \
(setting.method_name is None or setting.method_name == method_name) and \
(setting.context_id is None or setting.context_id == context.id or (
setting.context_children and context.has_parent(setting.context_id))):
selected.append(setting.enabled)
if len(selected) == 0:
return False
res = selected[0]
for enabled in selected[1:]:
res &= enabled
return res
def compute_var_debug(self, service_name, method_name, context_id, variable_name, debug_id):
if not self.activated:
return False
selected = []
for setting in self.debug_vars_settings:
if setting.variable_name is None and setting.debug_id is None:
continue
if (setting.service_name is None or setting.service_name == service_name) and \
(setting.method_name is None or setting.method_name == method_name) and \
(setting.context_id is None or setting.context_id == context_id) and \
(setting.variable_name is None or
setting.variable_name == "*" or
setting.variable_name == variable_name) and \
(setting.debug_id is None or setting.debug_id == debug_id):
selected.append(setting.enabled)
if len(selected) == 0:
return False
res = selected[0]
for enabled in selected[1:]:
if res == False or enabled == False:
return False
if isinstance(res, str):
continue
res = enabled
return res
def debug_rule(self, context, rule=None, context_id=None, debug_id=None, enabled=True):
"""
Add a debug rule request
:param context:
:param rule:
:param context_id:
:param debug_id:
:param enabled:
:return:
"""
rule = str(rule) if rule is not None else None
for setting in self.debug_rules_settings:
if setting.rule_id == rule and \
setting.context_id == context_id and \
setting.debug_id == debug_id:
setting.enabled = enabled
break
else:
self.debug_rules_settings.append(DebugRuleSetting(rule,
context_id,
debug_id,
enabled))
self.sheerka.record_var(context, self.NAME, "debug_rules_settings", self.debug_rules_settings)
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def compute_debug_rule(self, rule_id, context_id, debug_id):
if not self.activated:
return False
selected = []
for setting in self.debug_rules_settings:
if (setting.rule_id is None or setting.rule_id == rule_id) and \
(setting.context_id is None or setting.context_id == context_id) and \
(setting.debug_id is None or setting.debug_id == debug_id):
selected.append(setting.enabled)
if len(selected) == 0:
return False
res = selected[0]
for enabled in selected[1:]:
res &= enabled
return res
def reset_debug_rules(self, context):
self.debug_rules_settings.clear()
self.sheerka.record_var(context, self.NAME, "debug_rules_settings", self.debug_rules_settings)
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def get_debug_settings(self):
return self.debug_vars_settings