First implementation of Debugger for SyaNodeParser

This commit is contained in:
2020-12-03 21:50:48 +01:00
parent 4f899280c4
commit 8b86998225
48 changed files with 1781 additions and 1795 deletions
+21
View File
@@ -565,3 +565,24 @@ class ToListConcept(Concept):
self.set_value("recurse_on", recurse_on) # which sub items should we display
self.set_value("tab", tab) # customise tab (content and length)
self._metadata.is_evaluated = True
class NewConceptConcept(Concept):
ALL_ATTRIBUTES = ["concept"]
def __init__(self, concept=None):
Concept.__init__(self,
BuiltinConcepts.NEW_CONCEPT,
True,
False,
BuiltinConcepts.NEW_CONCEPT,
bound_body="concept")
self.set_value("concept", concept)
self._metadata.is_evaluated = True
def __repr__(self):
if self.concept:
return f"NewConcept(concept={self.concept}, key='{self.concept.key}')"
else:
return super().__repr__()
+22 -1
View File
@@ -393,7 +393,7 @@ class Concept:
for name, value in other.get_metadata().variables:
self.def_var(name, value)
elif prop == "props":
self._metadata.props = deepcopy(other.get_metadata().props)
self._metadata.props = core.utils.sheerka_deepcopy(other.get_metadata().props)
else:
setattr(self._metadata, prop, getattr(other.get_metadata(), prop))
@@ -534,6 +534,27 @@ class Concept:
bag[prop] = getattr(self, prop)
return bag
def as_debug_bag(self, new_obj, recurse):
bag = {"id": self.id, "name": self.name, "key": self.key}
# add variable metadata
for k, v in [(k, v) for k, v in self._metadata.variables if v]:
bag[f"meta.{k}"] = f"'{v}'"
# add compiled info
for k, v in [(k, v) for k, v in self._compiled.items() if isinstance(v, Concept)]:
bag[f"compiled.{k}"] = new_obj(v) if recurse else v
# add values
for prop in [p for p in self.__dict__ if not p.startswith("_") and not p.startswith("##")]:
v = self.get_value(prop)
if v == NotInit:
continue
bag[f"value.{prop}"] = new_obj(v) if (isinstance(v, Concept) and recurse) else core.utils.escape_str(v)
return bag
def get_format_instructions(self):
from core.builtin_concepts import BuiltinConcepts
return self.get_prop(BuiltinConcepts.FORMAT_INSTRUCTIONS)
+5 -3
View File
@@ -1,7 +1,9 @@
# events
CONCEPT_PRECEDENCE_MODIFIED = "cpm"
RULE_PRECEDENCE_MODIFIED = "rpm"
CONTEXT_DISPOSED = "cd"
EVENT_CONCEPT_PRECEDENCE_MODIFIED = "evt_cpm"
EVENT_RULE_PRECEDENCE_MODIFIED = "evt_rpm"
EVENT_CONTEXT_DISPOSED = "evt_cd"
EVENT_USER_INPUT_EVALUATED = "evt_uie"
EVENT_CONCEPT_CREATED = "evt_cc"
# comparison context
RULE_COMPARISON_CONTEXT = "Rule"
+5 -10
View File
@@ -5,18 +5,13 @@ import time
from core.builtin_concepts import BuiltinConcepts, ParserResultConcept
from core.concept import Concept, get_concept_attrs
from core.global_symbols import CONTEXT_DISPOSED
from core.global_symbols import EVENT_CONTEXT_DISPOSED
from core.sheerka.services.SheerkaExecute import NO_MATCH
from core.sheerka.services.SheerkaMemory import SheerkaMemory
from core.utils import CONSOLE_COLORS_MAP as CCM
from core.utils import CONSOLE_COLORS_MAP as CCM, CONSOLE_COLUMNS
from sdp.sheerkaDataProvider import Event
try:
rows, columns = os.popen('stty size', 'r').read().split()
except ValueError:
rows, columns = 50, 80
pp = pprint.PrettyPrinter(indent=2, width=columns)
pp = pprint.PrettyPrinter(indent=2, width=CONSOLE_COLUMNS)
DEBUG_TAB_SIZE = 4
@@ -134,7 +129,7 @@ class ExecutionContext:
return
if self.stm:
self.sheerka.publish(self, CONTEXT_DISPOSED)
self.sheerka.publish(self, EVENT_CONTEXT_DISPOSED)
self._stop = time.time_ns()
@@ -207,7 +202,7 @@ class ExecutionContext:
def activate_push(self):
if self._push:
if self._push.stm:
self.sheerka.publish(self._push, CONTEXT_DISPOSED)
self.sheerka.publish(self._push, EVENT_CONTEXT_DISPOSED)
self._push._stop = time.time_ns()
self._push = None
+11 -53
View File
@@ -14,9 +14,11 @@ from core.builtin_concepts import BuiltinConcepts, ErrorConcept, ReturnValueConc
UnknownConcept, AllBuiltinConcepts
from core.concept import Concept, ConceptParts, NotInit, get_concept_attrs
from core.error import ErrorObj
from core.global_symbols import EVENT_USER_INPUT_EVALUATED
from core.profiling import profile
from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka_logger import console_handler
from core.simple_debug import my_debug
from core.tokenizer import Token, TokenKind
from printer.SheerkaPrinter import SheerkaPrinter
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
@@ -81,15 +83,6 @@ class Sheerka(Concept):
self.return_value_concept_id = None
self.error_concept_id = None
# a concept can be instantiated
# ex: File is a concept, but File('foo.txt') is an instance
# TODO: manage contexts
self.instances = []
# List of the known rules by the system
# ex: hello => say('hello')
self.rules = []
self.sdp: SheerkaDataProvider = None
self.cache_manager = CacheManager(cache_only)
@@ -105,10 +98,11 @@ class Sheerka(Concept):
self.printer_handler = SheerkaPrinter(self)
self.during_restore = False
self.during_initialisation = False
self._builtins_classes_cache = None
self.save_execution_context = True
self.enable_process_return_values = False
self.enable_process_return_values = True
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
self.sheerka_methods = {
@@ -120,10 +114,6 @@ class Sheerka(Concept):
self.locals = {}
self.last_executions = []
self.last_return_values = []
self.execution_count = 0
@property
def resolved_concepts_by_first_keyword(self):
"""
@@ -196,6 +186,7 @@ class Sheerka(Concept):
self.enable_process_return_values = enable_process_return_values
try:
self.during_initialisation = True
from sheerkapickle.sheerka_handlers import initialize_pickle_handlers
initialize_pickle_handlers()
@@ -235,6 +226,9 @@ class Sheerka(Concept):
except IOError as e:
res = ReturnValueConcept(self, False, self.get(BuiltinConcepts.ERROR), e)
finally:
self.during_initialisation = False
return res
def initialize_caching(self):
@@ -445,6 +439,7 @@ class Sheerka(Concept):
:return:
"""
# self.log.debug(f"Processing user input '{text}', {user_name=}.")
my_debug(f"****************** Processing user input '{text}', {user_name=}.***********************************")
event = Event(text, user_name)
self.sdp.save_event(event)
@@ -456,6 +451,7 @@ class Sheerka(Concept):
desc=f"Evaluating '{text}'") as execution_context:
user_input = self.ret(self.name, True, self.new(BuiltinConcepts.USER_INPUT, body=text, user_name=user_name))
execution_context.add_inputs(user_input=user_input)
# TODO. Must be a context hint, not a return value
reduce_requested = self.ret(self.name, True, self.new(BuiltinConcepts.REDUCE_REQUESTED))
@@ -466,32 +462,12 @@ class Sheerka(Concept):
if self.cache_manager.is_dirty:
self.cache_manager.commit(execution_context)
# exec_count = ExecutionContext.ids[execution_context.event.get_digest()]
# print("Execution Context Count:", exec_count)
if self.save_execution_context:
try:
# if exec_count > 3400:
# print("Saving result. digest=", execution_context.event.get_digest())
self.sdp.save_result(execution_context)
except Exception as ex:
print(f"Failed to save execution context. Reason: {ex}")
pass
# self.log.error(f"Failed to save execution context. Reason: {ex}")
self.publish(execution_context, EVENT_USER_INPUT_EVALUATED)
# Do not save execution contexts from process_return_values
if self.enable_process_return_values:
self.process_return_values(execution_context, ret)
self.execution_count += 1
self._last_execution = execution_context
if len(self.last_executions) == self.MAX_EXECUTION_HISTORY:
del self.last_executions[0]
self.last_executions.append(execution_context)
if len(self.last_return_values) == self.MAX_RETURN_VALUES_HISTORY:
del self.last_return_values[0]
self.last_return_values.append(ret)
return ret
def print(self, result, instructions=None):
@@ -887,24 +863,6 @@ class Sheerka(Concept):
return self.parsers_prefix + name
# def concepts(self):
# """
# List of all known concepts (look up in sdp)
# :return:
# """
# res = []
# lst = self.sdp.list(self.CONCEPTS_BY_ID_ENTRY)
# for item in lst:
# if isinstance(item, list):
# res.extend(item)
# else:
# res.append(item)
#
# return sorted(res, key=lambda i: int(i.id))
def get_last_execution(self):
return self._last_execution
def test(self):
return f"I have access to Sheerka !"
-41
View File
@@ -24,9 +24,6 @@ class SheerkaAdmin(BaseService):
self.sheerka.bind_service_method(self.restore, True)
self.sheerka.bind_service_method(self.concepts, False)
self.sheerka.bind_service_method(self.desc, False)
self.sheerka.bind_service_method(self.last_created_concept, False)
self.sheerka.bind_service_method(self.last_ret, False)
self.sheerka.bind_service_method(self.last_error_ret, False)
self.sheerka.bind_service_method(self.extended_isinstance, False)
self.sheerka.bind_service_method(self.is_container, False)
self.sheerka.bind_service_method(self.format_rules, False)
@@ -149,44 +146,6 @@ class SheerkaAdmin(BaseService):
def format_rules(self):
return self.sheerka.new(BuiltinConcepts.TO_LIST, items=self.sheerka.get_format_rules())
def last_created_concept(self, use_history=False):
for exec_result in reversed(self.sheerka.last_executions):
return_values = exec_result.values["return_values"]
for ret in return_values:
if ret.status and self.sheerka.isinstance(ret.value, BuiltinConcepts.NEW_CONCEPT):
return ret.value.body
if use_history:
return self.sheerka.new(BuiltinConcepts.ERROR, body="Not yet implement")
return self.sheerka.new(BuiltinConcepts.NOT_FOUND)
def last_ret(self, context, index=-1):
try:
last = self.sheerka.last_return_values[index]
return last[0] if isinstance(last, list) and len(last) == 1 else last
except IndexError:
return None
def last_error_ret(self, context, index=-1):
while index >= -len(self.sheerka.last_return_values):
last = self.sheerka.last_return_values[index]
last = [last] if not hasattr(last, "__iter__") else last
last = [ret_val for ret_val in last if not ret_val.status]
if len(last) == 0:
index -= 1
continue
if len(last) > 1:
return context.sheerka.ret(SheerkaAdmin.NAME,
False,
context.sheerka.new(BuiltinConcepts.TOO_MANY_ERRORS, body=last))
return last[0]
return context.sheerka.ret(SheerkaAdmin.NAME,
False,
context.sheerka.new(BuiltinConcepts.NOT_FOUND))
def extended_isinstance(self, a, b):
"""
@@ -3,7 +3,7 @@ from dataclasses import dataclass
from cache.Cache import Cache
from cache.ListCache import ListCache
from core.builtin_concepts import BuiltinConcepts
from core.global_symbols import CONCEPT_PRECEDENCE_MODIFIED, RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT, \
from core.global_symbols import EVENT_CONCEPT_PRECEDENCE_MODIFIED, EVENT_RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT, \
CONCEPT_COMPARISON_CONTEXT
from core.builtin_helpers import ensure_concept_or_rule
from core.concept import Concept
@@ -183,9 +183,9 @@ class SheerkaComparisonManager(BaseService):
if comparison_obj.property == BuiltinConcepts.PRECEDENCE:
if comparison_obj.context == CONCEPT_COMPARISON_CONTEXT:
self.sheerka.publish(context, CONCEPT_PRECEDENCE_MODIFIED)
self.sheerka.publish(context, EVENT_CONCEPT_PRECEDENCE_MODIFIED)
elif comparison_obj.context == RULE_COMPARISON_CONTEXT:
self.sheerka.publish(context, RULE_PRECEDENCE_MODIFIED)
self.sheerka.publish(context, EVENT_RULE_PRECEDENCE_MODIFIED)
return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
@@ -2,6 +2,7 @@ import core.utils
from core.builtin_concepts import BuiltinConcepts, ErrorConcept
from core.builtin_helpers import ensure_concept
from core.concept import Concept, DEFINITION_TYPE_DEF, DEFINITION_TYPE_BNF, freeze_concept_attrs
from core.global_symbols import EVENT_CONCEPT_CREATED
from core.sheerka.services.sheerka_service import BaseService
from sdp.sheerkaDataProvider import SheerkaDataProviderDuplicateKeyError
@@ -85,6 +86,9 @@ class SheerkaCreateNewConcept(BaseService):
if concept.get_bnf() and init_bnf_ret_value is not None and init_bnf_ret_value.status:
sheerka.cache_manager.clear(sheerka.CONCEPTS_GRAMMARS_ENTRY)
# publish the new concept
sheerka.publish(context, EVENT_CONCEPT_CREATED, concept)
# process the return if needed
ret = sheerka.ret(self.NAME, True, sheerka.new(BuiltinConcepts.NEW_CONCEPT, body=concept))
return ret
+250 -163
View File
@@ -1,20 +1,57 @@
import os
import pprint
import re
from dataclasses import dataclass
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept, NotInit
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 CONSOLE_COLORS_MAP as CCM, CONSOLE_COLUMNS, PRIMITIVES_TYPES
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=CONSOLE_COLUMNS)
pp = pprint.PrettyPrinter(indent=2, width=columns)
NotFound = "** Not Found **"
class ConceptDebugObj:
def __init__(self, concept):
self.concept = concept
self.attrs = concept.as_debug_bag(lambda x: ConceptDebugObj(x), True)
def __repr__(self):
return f"({self.concept_repr()})"
def __eq__(self, other):
if not isinstance(other, ConceptDebugObj):
return False
return self.attrs == other.attrs
def __hash__(self):
return hash(self.concept)
def concept_repr(self):
res = f":{self.concept.name}|{self.concept.id}:"
# print metadata
first = True
for k, v in [(k, v) for k, v in self.attrs.items() if k not in ("id", "name", "key")]:
if not first:
res += ", "
res += f"{k}={v}"
first = False
return res
class ConceptNodeDebugObj:
def __init__(self, concept_node):
self.concept_node = concept_node
self.concept_debug = ConceptDebugObj(concept_node.concept)
def __repr__(self):
return f"ConceptNode({self.concept_debug.concept_repr()})"
class BaseDebugLogger:
@@ -49,6 +86,9 @@ class BaseDebugLogger:
def is_enabled(self):
pass
def get_enabled_vars(self):
pass
class NullDebugLogger(BaseDebugLogger):
def __init__(self):
@@ -57,6 +97,9 @@ class NullDebugLogger(BaseDebugLogger):
def is_enabled(self):
return False
def get_enabled_vars(self):
pass
class ConsoleDebugLogger(BaseDebugLogger):
@@ -70,9 +113,29 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.is_highlighted = ""
def is_enabled(self):
"""
True if the debug is activated for the current service, method and context
:return:
"""
return True
def get_enabled_vars(self):
"""
Returns the list of all enabled variables for this console debugger
:return:
"""
return self.debug_manager.get_enabled_items("vars",
self.context,
self.service_name,
self.method_name,
self.debug_id)
def debug_entering(self, **kwargs):
"""
Log that we start debugging a method (for a specified service and context)
:param kwargs:
:return:
"""
super().debug_entering(**kwargs)
str_text = f"{CCM['blue']}Entering {self.service_name}.{self.method_name} with {CCM['reset']}"
@@ -84,10 +147,24 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_manager.debug(self.prefix() + str_vars)
def debug_log(self, text, is_error=False):
"""
Prints a debug information (not related to a specific variable, concept or rule)
:param text:
:param is_error:
:return:
"""
color = 'red' if is_error else 'blue'
self.debug_manager.debug(self.prefix() + f"{CCM[color]}..{text}{CCM['reset']}")
def debug_var(self, name, value, is_error=False, hint=None):
"""
Prints the value of a variable
:param name:
:param value:
:param is_error:
:param hint:
:return:
"""
enabled = is_error or self.debug_manager.compute_debug_var(self.context,
self.service_name,
self.method_name,
@@ -103,6 +180,12 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug(str_text, str_vars)
def debug_rule(self, rule, results):
"""
Prints debug information related to a specific rule id
:param rule:
:param results:
:return:
"""
if not self.debug_manager.compute_debug_rule(self.context,
self.service_name,
self.method_name,
@@ -115,6 +198,13 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug(str_text, str_vars)
def debug_concept(self, concept, text=None, **kwargs):
"""
Prints debug information related to a specific concept
:param concept:
:param text:
:param kwargs:
:return:
"""
raw = kwargs.pop('raw', None)
if not self.debug_manager.compute_debug_concept(self.context,
self.service_name,
@@ -283,27 +373,6 @@ class SheerkaDebugManager(BaseService):
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)
return self.sheerka.new(BuiltinConcepts.TO_DICT, body=res)
def debug(self, *args, **kwargs):
print(*args, **kwargs)
@@ -428,6 +497,24 @@ class SheerkaDebugManager(BaseService):
return res
def get_enabled_items(self, item_type, context, service_name, method_name, debug_id):
if not self.activated:
return False
selected = set()
for setting in getattr(self, self.container_name(item_type)):
if not setting.enabled or setting.item 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))) and \
(setting.debug_id is None or setting.debug_id == debug_id):
selected.add(setting.item)
return selected
def reset_debug(self, context):
for item_type in ["vars", "rules", "concepts"]:
setting_name = self.container_name(item_type)
@@ -487,6 +574,123 @@ class SheerkaDebugManager(BaseService):
def compute_debug_rule(self, context, service_name, method_name, item, debug_id):
return self.compute_debug_item("rules", context, service_name, method_name, item, debug_id)
def inspect(self, context, *args, **kwargs):
"""
Print
:param context:
:param args: 1st parameter is what to display, the other are the properties to display
:param kwargs: how to display the result
:return:
"""
if len(args) == 0:
return
forced_prop, props = None, None
if isinstance(args[0], int):
to_inspect = self.sheerka.get_execution_item(context, int(args[0]))
if isinstance(to_inspect, ExecutionContext):
if len(args) == 1:
props = ["inputs", "values.return_values"]
else:
props = args[1:]
else:
props = None
else:
to_inspect = args[0]
if isinstance(to_inspect, Concept):
if len(args) > 1:
forced_prop = args[1:]
props = forced_prop
else:
props = args[1:]
bag = self.as_debug_bag(to_inspect, False, forced_prop)
props = props or list(bag.keys())
res = self.inspect_object(bag, props, False, **kwargs)
# Attributes that are not found are probably directly requested by the user
# Let's try for the full names of these attributes
not_found = {k: v for k, v in [(k, v) for k, v in res.items() if v == NotFound]}
if len(not_found) > 0:
to_add = {}
to_remove = []
for k, v in not_found.items():
alternate_props = ["meta." + k, "compiled." + k, "value." + k]
res2 = self.inspect_object(bag, alternate_props, True, **kwargs)
if len(res2) > 0:
to_add.update(res2)
to_remove.append(k)
res.update(to_add)
for k in to_remove:
del res[k]
return self.sheerka.new(BuiltinConcepts.TO_DICT, body=res)
def inspect_object(self, bag, props, discard_not_found, **kwargs):
values_required = kwargs.get("values", False)
as_bag_required = kwargs.get("as_bag", False)
res = {}
for prop in props:
if prop in res:
# discard duplicates
continue
try:
value = bag[prop] if prop.startswith("#") else evaluate_expression(prop, bag)
except NameError:
if discard_not_found:
continue
else:
value = NotFound
if callable(value):
# discard methods
continue
if values_required:
value = self.get_inner_values(value, **kwargs)
elif as_bag_required:
value = self.get_debug_repr(value, **kwargs)
res[prop] = value
return res
def get_inner_values(self, obj, **kwargs):
if obj is None:
return None
if isinstance(obj, list):
return [self.get_inner_values(item, **kwargs) for item in obj]
if hasattr(obj, "get_obj_value"):
return obj.get_obj_value()
if not isinstance(obj, Concept):
return self.get_debug_repr(obj, **kwargs)
if obj.body is NotInit:
return self.get_debug_repr(obj, **kwargs)
return self.get_inner_values(obj.body, **kwargs)
def get_debug_repr(self, obj, **kwargs):
if kwargs.get("as_bag", False):
forced_props = self.get_concept_forced_props(obj) if isinstance(obj, Concept) else None
return self.as_debug_bag(obj, True, forced_props)
from parsers.BaseNodeParser import ConceptNode
if isinstance(obj, Concept):
return ConceptDebugObj(obj)
elif isinstance(obj, ConceptNode):
return ConceptNodeDebugObj(obj)
else:
return obj
@staticmethod
def container_name(item_type):
return f"debug_{item_type}_settings"
@@ -505,7 +709,7 @@ class SheerkaDebugManager(BaseService):
if len(parts) > 1:
method_name = None if parts[1] == "*" else parts[1]
if len(parts) > 2:
item = parts[2]
item = ".".join(parts[2:])
if len(args) > 1:
context_part = args[1]
@@ -535,139 +739,22 @@ class SheerkaDebugManager(BaseService):
return item, service, method_name, context_id, context_children, debug_id, enabled
# 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 debug_concept(self, context, concept=None, context_id=None, debug_id=None, enabled=True):
# """
# Add a debug rule request
# :param context:
# :param concept:
# :param context_id:
# :param debug_id:
# :param enabled:
# :return:
# """
# concept_id = concept.id if isinstance(concept, Concept) else str(concept) if concept is not None else None
# for setting in self.debug_concepts_settings:
# if setting.concept_id == concept_id and \
# setting.context_id == context_id and \
# setting.debug_id == debug_id:
# setting.enabled = enabled
# break
# else:
# self.debug_concepts_settings.append(DebugConceptSetting(concept_id,
# context_id,
# debug_id,
# enabled))
#
# self.sheerka.record_var(context, self.NAME, "debug_concepts_settings", self.debug_concepts_settings)
# return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
#
@staticmethod
def get_concept_forced_props(concept):
return ["id", "name", "key"] + [p for p in concept.__dict__ if not p.startswith("_")]
#
@staticmethod
def as_debug_bag(obj, recurse=True, forced_props=None):
if type(obj) in PRIMITIVES_TYPES:
return obj
# def compute_debug_var(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 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 compute_debug_concept(self, concept_id, context_id, debug_id):
# if not self.activated:
# return False
#
# selected = []
# for setting in self.debug_concepts_settings:
# if (setting.concept_id is None or setting.concept_id == concept_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):
# lst = self.debug_vars_settings + self.debug_concepts_settings + self.debug_rules_settings
# return self.sheerka.new(BuiltinConcepts.TO_LIST, body=lst)
res = {'#type#': type(obj).__name__}
if isinstance(obj, Concept) and not obj.get_metadata().is_builtin:
res.update(obj.as_debug_bag(SheerkaDebugManager.as_debug_bag, recurse))
else:
forced_props_to_use = [p for p in forced_props if p != "#type#"] if forced_props else None
res.update(as_bag(obj, forced_props_to_use))
del res["self"]
return res
+2 -3
View File
@@ -1,16 +1,15 @@
import os
import pprint
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept
from core.sheerka.ExecutionContext import ExecutionContext
from core.sheerka.services.sheerka_service import BaseService
from core.utils import CONSOLE_COLUMNS
from sdp.sheerkaDataProvider import SheerkaDataProvider, Event
def get_pp():
rows, columns = os.popen('stty size', 'r').read().split()
pp = pprint.PrettyPrinter(width=columns, compact=True)
pp = pprint.PrettyPrinter(width=CONSOLE_COLUMNS, compact=True)
return pp
+17 -11
View File
@@ -4,7 +4,7 @@ from cache.FastCache import FastCache
from cache.ListIfNeededCache import ListIfNeededCache
from core.builtin_concepts import BuiltinConcepts
from core.concept import Concept
from core.global_symbols import CONTEXT_DISPOSED
from core.global_symbols import EVENT_CONTEXT_DISPOSED
from core.sheerka.services.sheerka_service import BaseService, ServiceObj
@@ -23,7 +23,7 @@ class SheerkaMemory(BaseService):
def __init__(self, sheerka):
super().__init__(sheerka)
self.short_term_objects = FastCache()
self.objects = ListIfNeededCache(default=lambda k: self.sheerka.sdp.get(self.OBJECTS_ENTRY, k))
self.memory_objects = ListIfNeededCache(default=lambda k: self.sheerka.sdp.get(self.OBJECTS_ENTRY, k))
self.registration = {}
def initialize(self):
@@ -37,14 +37,16 @@ class SheerkaMemory(BaseService):
self.sheerka.bind_service_method(self.unregister_object, True, visible=False)
self.sheerka.bind_service_method(self.add_registered_objects, True, visible=False)
self.sheerka.bind_service_method(self.memory, False)
self.sheerka.bind_service_method(self.mem, False)
self.sheerka.cache_manager.register_cache(self.OBJECTS_ENTRY, self.objects, persist=True, use_ref=True)
self.sheerka.cache_manager.register_cache(self.OBJECTS_ENTRY, self.memory_objects, persist=True, use_ref=True)
def reset(self):
self.short_term_objects.clear()
self.memory_objects.clear()
def initialize_deferred(self, context, is_first_time):
self.sheerka.subscribe(CONTEXT_DISPOSED, self.remove_context)
self.sheerka.subscribe(EVENT_CONTEXT_DISPOSED, self.remove_context)
def get_from_short_term_memory(self, context, key):
while True:
@@ -90,16 +92,16 @@ class SheerkaMemory(BaseService):
:param concept:
:return:
"""
self.objects.put(key, MemoryObject(context.event.get_digest(), concept))
self.memory_objects.put(key, MemoryObject(context.event.get_digest(), concept))
def get_from_memory(self, context, key):
""""
"""
return self.objects.get(key)
return self.memory_objects.get(key)
def register_object(self, context, key, concept):
"""
Before adding objects to memory, they first need to be registered
Before adding memory_objects to memory, they first need to be registered
More:
We don't want to add all evaluated concept into memory
(because some of them may be ref to concept already in memory)
@@ -126,7 +128,7 @@ class SheerkaMemory(BaseService):
def add_registered_objects(self, context):
"""
Adds all registered objects
Adds all registered memory_objects
:param context:
:return:
"""
@@ -136,7 +138,7 @@ class SheerkaMemory(BaseService):
def memory(self, context, name=None):
"""
Get the list of all objects in memory
Get the list of all memory_objects in memory
:param context:
:param name:
:return:
@@ -154,10 +156,14 @@ class SheerkaMemory(BaseService):
return obj.obj
res = {}
for k in self.objects:
obj = self.objects.get(k)
for k in self.memory_objects:
obj = self.memory_objects.get(k)
if isinstance(obj, list):
obj = obj[-1]
res[k] = obj.obj
return res
def mem(self):
keys = sorted([k for k in self.memory_objects])
return {"keys": keys, "len": len(keys)}
+145 -30
View File
@@ -1,9 +1,14 @@
import ast
from cache.Cache import Cache
from core.builtin_concepts import BuiltinConcepts
from core.global_symbols import EVENT_USER_INPUT_EVALUATED, EVENT_CONCEPT_CREATED
from core.sheerka.services.sheerka_service import BaseService
from core.utils import CONSOLE_COLORS_MAP as CCM
from core.utils import as_bag
MAX_EXECUTION_HISTORY = 100
class SheerkaResultConcept(BaseService):
NAME = "Result"
@@ -11,6 +16,10 @@ class SheerkaResultConcept(BaseService):
def __init__(self, sheerka, page_size=30):
super().__init__(sheerka)
self.page_size = page_size
self.executions_contexts_cache = Cache(MAX_EXECUTION_HISTORY)
self.last_execution = None
self.last_created_concept = None
self.last_created_concept_id = None
def initialize(self):
self.sheerka.bind_service_method(self.get_results_by_digest, True) # digest is recorded
@@ -18,6 +27,19 @@ class SheerkaResultConcept(BaseService):
self.sheerka.bind_service_method(self.get_last_results, True) # digest is recorded
self.sheerka.bind_service_method(self.get_results, False)
self.sheerka.bind_service_method(self.get_execution_item, False)
self.sheerka.bind_service_method(self.get_last_ret, False, as_name="last_ret")
self.sheerka.bind_service_method(self.get_last_created_concept, False, as_name="last_created_concept")
def initialize_deferred(self, context, is_first_time):
self.restore_values("last_created_concept_id")
self.sheerka.subscribe(EVENT_USER_INPUT_EVALUATED, self.user_input_evaluated)
self.sheerka.subscribe(EVENT_CONCEPT_CREATED, self.new_concept_created)
def reset(self):
self.executions_contexts_cache.clear()
self.last_execution = None
self.last_created_concept = None
self.last_created_concept_id = None
@staticmethod
def get_predicate(**kwargs):
@@ -38,12 +60,24 @@ class SheerkaResultConcept(BaseService):
predicate = " and ".join(res)
return compile(ast.parse(predicate, mode="eval"), "<SheerkaResultManager.get_predicate>", mode="eval")
@staticmethod
def as_list(execution_context, predicate):
def _yield_result(lst):
for e in lst:
if predicate is None or eval(predicate, as_bag(e)):
yield e
if e._children:
yield from _yield_result(e._children)
return _yield_result([execution_context])
def get_results_by_digest(self, context, digest, filter=None, record_digest=True, **kwargs):
"""
Gets the entire execution tree for the given event digest
:param filter:
:param context:
:param digest:
:param filter:
:param record_digest:
:return:
"""
@@ -54,8 +88,12 @@ class SheerkaResultConcept(BaseService):
kwargs["filter"] = filter
try:
result = self.sheerka.sdp.load_result(digest)
event = self.sheerka.sdp.load_event(digest)
if digest in self.executions_contexts_cache:
result = self.executions_contexts_cache.get(digest)
event = result.event
else:
result = self.sheerka.sdp.load_result(digest)
event = self.sheerka.sdp.load_event(digest) # there is no real need for a cache of the events
if record_digest:
context.log(f"Recording digest '{digest}'")
@@ -89,7 +127,18 @@ class SheerkaResultConcept(BaseService):
if command is None:
return None
start = 0
# first, search in cache
for event_id in self.executions_contexts_cache:
execution_context = self.executions_contexts_cache.get(event_id)
if execution_context.event.message.startswith(command):
return self.get_results_by_digest(context,
execution_context.event.get_digest(),
filter,
record_digest,
**kwargs)
# not found, search in db
start = len(self.executions_contexts_cache)
consumed = 0
while True:
for event in self.sheerka.sdp.load_events(self.page_size, start):
@@ -103,6 +152,7 @@ class SheerkaResultConcept(BaseService):
start += self.page_size
consumed = 0
# not found, return error
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"command": command})
def get_last_results(self, context, filter=None, record_digest=True, **kwargs):
@@ -114,23 +164,16 @@ class SheerkaResultConcept(BaseService):
:return:
"""
start = 0
page_size = 2
consumed = 0
while True:
for event in self.sheerka.sdp.load_events(page_size, start):
consumed += 1
if self.sheerka.sdp.has_result(event.get_digest()):
return self.get_results_by_digest(context, event.get_digest(), filter, record_digest, **kwargs)
if self.last_execution:
return self.get_results_by_digest(context,
self.last_execution.event.get_digest(),
filter,
record_digest,
**kwargs)
if consumed < page_size:
break
if page_size < 100:
page_size *= 2
start += page_size
consumed = 0
event_id = self._get_last_execution_result_event_id_from_db()
if event_id is not None:
return self.get_results_by_digest(context, event_id, filter, record_digest, **kwargs)
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "last"})
@@ -150,12 +193,21 @@ class SheerkaResultConcept(BaseService):
return self.get_results_by_digest(context, digest, filter, False, **kwargs)
def get_execution_item(self, context, item_id):
"""
Return the item_id'th element of the execution result under investigation
:param context:
:param item_id:
:return:
"""
digest = self.sheerka.load_var(self.NAME, "digest")
if digest is None:
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body="no digest")
try:
result = self.sheerka.sdp.load_result(digest)
if digest in self.executions_contexts_cache:
result = self.executions_contexts_cache.get(digest)
else:
result = self.sheerka.sdp.load_result(digest)
items = list(self.as_list(result, self.get_predicate(id=item_id)))
if len(items) == 0:
@@ -167,14 +219,77 @@ class SheerkaResultConcept(BaseService):
context.log_error(f"Digest {digest} is not found.", self.NAME, ex)
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"digest": digest})
@staticmethod
def as_list(execution_context, predicate):
def _yield_result(lst):
for e in lst:
if predicate is None or eval(predicate, as_bag(e)):
yield e
def user_input_evaluated(self, execution_context):
"""
Callback that updates the cache of execution contexts
:param execution_context:
:return:
"""
if self.sheerka.save_execution_context:
try:
self.sheerka.sdp.save_result(execution_context)
except Exception as ex:
print(f"{CCM['red']}Failed to save execution context. Reason: {ex}{CCM['reset']}")
pass
# self.log.error(f"Failed to save execution context. Reason: {ex}")
if e._children:
yield from _yield_result(e._children)
self.executions_contexts_cache.put(execution_context.event.get_digest(), execution_context)
self.last_execution = execution_context
return _yield_result([execution_context])
def get_last_ret(self, context):
"""
Return the last return value(s)
:return:
"""
if self.last_execution:
return self.last_execution.values["return_values"]
event_id = self._get_last_execution_result_event_id_from_db()
if event_id is not None:
try:
execution_result = self.sheerka.sdp.load_result(event_id)
return execution_result.values["return_values"]
except FileNotFoundError as ex:
context.log_error(f"Digest {event_id} is not found.", self.NAME, ex)
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"digest": event_id})
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "last"})
def new_concept_created(self, context, concept):
self.last_created_concept = concept
self.last_created_concept_id = concept.id
self.sheerka.record_var(context, self.NAME, "last_created_concept_id", concept.id)
def get_last_created_concept(self, context):
if self.last_created_concept:
return self.last_created_concept
if self.last_created_concept_id:
self.last_created_concept = self.sheerka.new((None, self.last_created_concept_id))
return self.last_created_concept
return self.sheerka.new(BuiltinConcepts.NOT_FOUND, body={"query": "last_created_concept"})
def _get_last_execution_result_event_id_from_db(self):
start = 0
page_size = 2
consumed = 0
while True:
for event in self.sheerka.sdp.load_events(page_size, start):
consumed += 1
if self.sheerka.sdp.has_result(event.get_digest()):
return event.get_digest()
if consumed < page_size:
break
if page_size < 100:
page_size *= 2
start += page_size
consumed = 0
return None
@@ -7,7 +7,7 @@ from cache.Cache import Cache
from core.builtin_concepts import BuiltinConcepts, ReturnValueConcept
from core.builtin_helpers import parse_unrecognized, only_successful, ensure_rule
from core.concept import Concept
from core.global_symbols import RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT
from core.global_symbols import EVENT_RULE_PRECEDENCE_MODIFIED, RULE_COMPARISON_CONTEXT
from core.rule import Rule
from core.sheerka.services.sheerka_service import BaseService
from core.tokenizer import Keywords, TokenKind, Token, IterParser
@@ -112,13 +112,14 @@ class FormatAstList(FormatAstNode):
prefix: str = None
suffix: str = None
show_index: bool = False
index: object = None
items: object = None
def clone(self, **kwargs):
return super().clone(
FormatAstList(self.variable),
("items_prop", "recurse_on", "recursion_depth", "debug", "prefix", "suffix", "show_index", "items"),
("items_prop", "recurse_on", "recursion_depth", "debug", "prefix", "suffix", "show_index", "index", "items"),
**kwargs)
@@ -496,10 +497,12 @@ class SheerkaRuleManager(BaseService):
if is_first_time:
# add builtin rules if it's the first initialization of Sheerka
self.init_builtin_rules(context)
# adds the other rules (when it's not the first time)
self.format_rule_cache.populate(lambda: self.sheerka.sdp.list(self.FORMAT_RULE_ENTRY), lambda rule: rule.id)
self.exec_rule_cache.populate(lambda: self.sheerka.sdp.list(self.EXEC_RULE_ENTRY), lambda rule: rule.id)
else:
# adds the other rules (when it's not the first time)
self.format_rule_cache.populate(lambda: self.sheerka.sdp.list(self.FORMAT_RULE_ENTRY), lambda rule: rule.id)
self.exec_rule_cache.populate(lambda: self.sheerka.sdp.list(self.EXEC_RULE_ENTRY), lambda rule: rule.id)
self.format_rule_cache.reset_events()
self.exec_rule_cache.reset_events()
# compile all the rules
for rule_id in self.format_rule_cache:
@@ -508,7 +511,7 @@ class SheerkaRuleManager(BaseService):
# update rules priorities
self.update_rules_priorities(context)
self.sheerka.subscribe(RULE_PRECEDENCE_MODIFIED, self.update_rules_priorities)
self.sheerka.subscribe(EVENT_RULE_PRECEDENCE_MODIFIED, self.update_rules_priorities)
def update_rules_priorities(self, context):
"""
@@ -43,6 +43,7 @@ class SheerkaSetsManager(BaseService):
context.log(f"Setting concept {concept} is a {concept_set}", who=self.NAME)
core.builtin_helpers.ensure_concept(concept, concept_set)
if BuiltinConcepts.ISA in concept.get_metadata().props and concept_set in concept.get_metadata().props[
BuiltinConcepts.ISA]:
return self.sheerka.ret(
+46
View File
@@ -0,0 +1,46 @@
default_debug_name = "*default*"
debug_activated = set()
def my_debug(*args, check_started=None):
"""
Write one line per arg in 'debug.txt'
:param args:
:param check_started:
True : first check if start_debug() was called
<name> : first check if start_debug(name) was called
list of <names> : first check if start_debug() is called for all names
:return:
"""
if check_started and default_debug_name not in debug_activated:
return
if isinstance(check_started, str) and check_started not in debug_activated:
return
if isinstance(check_started, list):
for debug_name in check_started:
if debug_name not in debug_activated:
return
# with open("debug.txt", "a") as f:
# for arg in args:
# if isinstance(arg, list):
# for item in arg:
# f.write(f"{item}\n")
# else:
# f.write(f"{arg}\n")
def start_debug(debug_name=default_debug_name, msg=None):
debug_activated.add(debug_name)
if msg:
with open("debug.txt", "a") as f:
f.write(f"{msg}\n")
def stop_debug(debug_name=default_debug_name, msg=None):
if msg:
with open("debug.txt", "a") as f:
f.write(f"{msg}\n")
debug_activated.remove(debug_name)
+88 -49
View File
@@ -1,16 +1,15 @@
import ast
import importlib
import inspect
import os
import pkgutil
from copy import deepcopy
from cache.Cache import Cache
from core.ast_helpers import ast_to_props
from core.tokenizer import TokenKind, Tokenizer
from pyparsing import *
default_debug_name = "*default*"
debug_activated = set()
COLORS = {
"black",
"red",
@@ -43,55 +42,17 @@ integer = Word(nums)
escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer, ';')) +
oneOf(list(alphas)))
try:
CONSOLE_ROWS, CONSOLE_COLUMNS = os.popen('stty size', 'r').read().split()
CONSOLE_ROWS, CONSOLE_COLUMNS = int(CONSOLE_ROWS), int(CONSOLE_COLUMNS)
except ValueError:
CONSOLE_ROWS, CONSOLE_COLUMNS = 50, 80
def no_color_str(text):
return Suppress(escapeSeq).transformString(str(text))
def my_debug(*args, check_started=None):
"""
Write one line per arg in 'debug.txt'
:param args:
:param check_started:
True : first check if start_debug() was called
<name> : first check if start_debug(name) was called
list of <names> : first check if start_debug() is called for all names
:return:
"""
if check_started and default_debug_name not in debug_activated:
return
if isinstance(check_started, str) and check_started not in debug_activated:
return
if isinstance(check_started, list):
for debug_name in check_started:
if debug_name not in debug_activated:
return
with open("debug.txt", "a") as f:
for arg in args:
if isinstance(arg, list):
for item in arg:
f.write(f"{item}\n")
else:
f.write(f"{arg}\n")
def start_debug(debug_name=default_debug_name, msg=None):
debug_activated.add(debug_name)
if msg:
with open("debug.txt", "a") as f:
f.write(f"{msg}\n")
def stop_debug(debug_name=default_debug_name, msg=None):
if msg:
with open("debug.txt", "a") as f:
f.write(f"{msg}\n")
debug_activated.remove(debug_name)
def sysarg_to_string(argv):
"""
Transform a list of strings into a single string
@@ -606,13 +567,17 @@ def tokens_index(tokens, sub_tokens, skip=0):
raise ValueError(f"sub tokens '{sub_tokens}' not found")
def as_bag(obj):
def as_bag(obj, forced_properties=None):
"""
Get the properties of an object (static and dynamic)
:param obj:
:param forced_properties:
:return:
"""
if hasattr(obj, "as_bag"):
if forced_properties:
bag = {p: getattr(obj, p) for p in forced_properties}
elif hasattr(obj, "as_bag"):
bag = obj.as_bag()
else:
bag = {} if type(obj) in PRIMITIVES_TYPES else {prop: getattr(obj, prop)
@@ -726,3 +691,77 @@ def dump_ast(node):
for to_remove in [", ctx=Load()", ", kind=None", ", type_ignores=[]"]:
dump = dump.replace(to_remove, "")
return dump
def sheerka_deepcopy(obj):
"""
Internal implementation of deepcopy that can handle Concept circular references
:param obj:
:return:
"""
already_seen = {}
def copy_concept(c):
id_c = id(c)
if id_c in already_seen:
ref = already_seen[id_c]
if ref == '_##_REF_##_':
raise Exception("Circular Ref not managed yet!")
else:
return ref
already_seen[id_c] = '_##_REF_##_'
cls = type(c)
instance = cls()
# update the metadata
for prop_name, prop_value in vars(c.get_metadata()).items():
if prop_name != "props":
setattr(instance.get_metadata(), prop_name, prop_value)
else:
setattr(instance.get_metadata(), prop_name, sheerka_deepcopy(prop_value))
# update the values
for prop_name, prop_value in c.values().items():
setattr(instance, prop_name, prop_value)
already_seen[id_c] = instance
return instance
from core.concept import Concept
if isinstance(obj, dict):
res = {sheerka_deepcopy(k): sheerka_deepcopy(v) for k, v in obj.items()}
return res
elif isinstance(obj, list):
return [sheerka_deepcopy(item) for item in obj]
elif isinstance(obj, set):
return {sheerka_deepcopy(item) for item in obj}
elif isinstance(obj, tuple):
return tuple((sheerka_deepcopy(item) for item in obj))
elif isinstance(obj, Concept):
return copy_concept(obj)
else:
return deepcopy(obj)
def escape_str(x):
"""
Returns a string representation that look like what would produce a debugger
:param x:
:return:
"""
if isinstance(x, str):
return f"'{x}'"
return x
class NextIdManager:
"""
solely return the next integer
"""
def __init__(self):
self.id = -1
def get_next_id(self):
self.id += 1
return self.id