First but not optimized version of AstFormatDict

This commit is contained in:
2020-11-24 13:43:04 +01:00
parent ab30ab3345
commit cac732bd93
21 changed files with 898 additions and 217 deletions
+2
View File
@@ -105,6 +105,7 @@ class BuiltinConcepts:
# formatting # formatting
TO_LIST = "__TO_LIST" TO_LIST = "__TO_LIST"
TO_DICT = "__TO_DICT"
AllBuiltinConcepts = [v for n, v in BuiltinConcepts.__dict__.items() if not n.startswith("__")] AllBuiltinConcepts = [v for n, v in BuiltinConcepts.__dict__.items() if not n.startswith("__")]
@@ -176,6 +177,7 @@ BuiltinContainers = [
BuiltinConcepts.FILTERED, BuiltinConcepts.FILTERED,
BuiltinConcepts.EXPLANATION, BuiltinConcepts.EXPLANATION,
BuiltinConcepts.TO_LIST, BuiltinConcepts.TO_LIST,
BuiltinConcepts.TO_DICT,
] ]
""" """
+16 -1
View File
@@ -113,7 +113,8 @@ class Sheerka(Concept):
self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods self.methods_with_context = {"test_using_context"} # only the names, the method is defined in sheerka_methods
self.sheerka_methods = { self.sheerka_methods = {
"test": SheerkaMethod(self.test, False), "test": SheerkaMethod(self.test, False),
"test_using_context": SheerkaMethod(self.test_using_context, False) "test_using_context": SheerkaMethod(self.test_using_context, False),
"test_dict": SheerkaMethod(self.test_dict, False)
} }
self.sheerka_pipeables = {} self.sheerka_pipeables = {}
@@ -1028,6 +1029,20 @@ class Sheerka(Concept):
# uncomment the following line to enable colors # uncomment the following line to enable colors
# logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit) # logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)
def test_dict(self):
bag2 = {
"alpha": "value4",
"beta": ["item1", "item2", "item3", ]
}
bag = {
"a": "value1",
"baba": "value2",
"c": "value1",
"de": ["item1", "item2", "item3", ],
"e": bag2
}
return self.new(BuiltinConcepts.TO_DICT, body=bag)
def to_profile(): def to_profile():
sheerka = Sheerka() sheerka = Sheerka()
+24
View File
@@ -3,6 +3,7 @@ import time
from os import path from os import path
from core.builtin_concepts import BuiltinConcepts, BuiltinContainers from core.builtin_concepts import BuiltinConcepts, BuiltinContainers
from core.builtin_helpers import ensure_concept
from core.concept import Concept from core.concept import Concept
from core.sheerka.services.sheerka_service import BaseService from core.sheerka.services.sheerka_service import BaseService
@@ -22,6 +23,7 @@ class SheerkaAdmin(BaseService):
self.sheerka.bind_service_method(self.cache, False) self.sheerka.bind_service_method(self.cache, False)
self.sheerka.bind_service_method(self.restore, True) self.sheerka.bind_service_method(self.restore, True)
self.sheerka.bind_service_method(self.concepts, False) 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_created_concept, False)
self.sheerka.bind_service_method(self.last_ret, 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.last_error_ret, False)
@@ -122,6 +124,28 @@ class SheerkaAdmin(BaseService):
def concepts(self): def concepts(self):
return self.sheerka.new(BuiltinConcepts.TO_LIST, body=self.sheerka.sdp.list(self.sheerka.CONCEPTS_BY_ID_ENTRY)) return self.sheerka.new(BuiltinConcepts.TO_LIST, body=self.sheerka.sdp.list(self.sheerka.CONCEPTS_BY_ID_ENTRY))
def desc(self, *concepts):
ensure_concept(*concepts)
res = []
for c in concepts:
bag = {
"id": c.id,
"name": c.name,
"key": c.key,
"definition": c.get_metadata().definition,
"type": c.get_metadata().definition_type,
"body": c.get_metadata().body,
"where": c.get_metadata().where,
"pre": c.get_metadata().pre,
"post": c.get_metadata().post,
"ret": c.get_metadata().ret,
"vars": c.get_metadata().variables,
"props": c.get_metadata().props,
}
res.append(self.sheerka.new(BuiltinConcepts.TO_DICT, body=bag))
return res[0] if len(res) == 1 else self.sheerka.new(BuiltinConcepts.TO_LIST, body=res)
def format_rules(self): def format_rules(self):
return self.sheerka.new(BuiltinConcepts.TO_LIST, items=self.sheerka.get_format_rules()) return self.sheerka.new(BuiltinConcepts.TO_LIST, items=self.sheerka.get_format_rules())
@@ -34,13 +34,16 @@ class BaseDebugLogger:
def debug_entering(self, **kwargs): def debug_entering(self, **kwargs):
pass pass
def debug_var(self, name, value, is_error=False): def debug_var(self, name, value, is_error=False, hint=None):
pass pass
def debug_rule(self, rule, results): def debug_rule(self, rule, results):
pass pass
def debug_log(self, text): def debug_log(self, text, is_error=False):
pass
def is_enabled(self):
pass pass
@@ -48,6 +51,9 @@ class NullDebugLogger(BaseDebugLogger):
def __init__(self): def __init__(self):
pass pass
def is_enabled(self):
return False
class ConsoleDebugLogger(BaseDebugLogger): class ConsoleDebugLogger(BaseDebugLogger):
@@ -60,6 +66,9 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_id = debug_id self.debug_id = debug_id
self.is_highlighted = "" self.is_highlighted = ""
def is_enabled(self):
return True
def debug_entering(self, **kwargs): def debug_entering(self, **kwargs):
super().debug_entering(**kwargs) super().debug_entering(**kwargs)
@@ -71,8 +80,8 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_manager.debug(self.prefix() + str_text) self.debug_manager.debug(self.prefix() + str_text)
self.debug_manager.debug(self.prefix() + str_vars) self.debug_manager.debug(self.prefix() + str_vars)
def debug_var(self, name, value, is_error=False): def debug_var(self, name, value, is_error=False, hint=None):
enabled = self.debug_manager.compute_var_debug(self.service_name, enabled = is_error or self.debug_manager.compute_var_debug(self.service_name,
self.method_name, self.method_name,
self.context_id, self.context_id,
name, name,
@@ -81,7 +90,8 @@ class ConsoleDebugLogger(BaseDebugLogger):
return return
color = 'red' if is_error else 'green' color = 'red' if is_error else 'green'
str_text = f"{CCM[color]}..{name}={CCM['reset']}" hint_str = f"({hint})" if hint is not None else ""
str_text = f"{CCM[color]}..{name}{hint_str}={CCM['reset']}"
str_vars = "" if isinstance(enabled, str) else pp.pformat(value) str_vars = "" if isinstance(enabled, str) else pp.pformat(value)
if "\n" not in str(str_vars): if "\n" not in str(str_vars):
self.debug_manager.debug(self.prefix() + str_text + str_vars) self.debug_manager.debug(self.prefix() + str_text + str_vars)
@@ -101,8 +111,9 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_manager.debug(self.prefix() + str_text) self.debug_manager.debug(self.prefix() + str_text)
self.debug_manager.debug(self.prefix() + str_vars) self.debug_manager.debug(self.prefix() + str_vars)
def debug_log(self, text): def debug_log(self, text, is_error=False):
self.debug_manager.debug(self.prefix() + f"{CCM['blue']}..{text}{CCM['reset']}") color = 'red' if is_error else 'blue'
self.debug_manager.debug(self.prefix() + f"{CCM[color]}..{text}{CCM['reset']}")
def prefix(self): def prefix(self):
return f"[{self.context_id:2}][{self.debug_id:2}] {self.is_highlighted}" return f"[{self.context_id:2}][{self.debug_id:2}] {self.is_highlighted}"
@@ -271,12 +282,8 @@ class SheerkaDebugManager(BaseService):
res = {} res = {}
for prop in props: for prop in props:
res[prop] = evaluate_expression(prop, bag) res[prop] = evaluate_expression(prop, bag)
# res = {
# "return_values": to_inspect.values.get("return_values", None)
# }
pp.pprint(res) return self.sheerka.new(BuiltinConcepts.TO_DICT, body=res)
return None
def debug(self, *args, **kwargs): def debug(self, *args, **kwargs):
print(*args, **kwargs) print(*args, **kwargs)
@@ -437,4 +444,4 @@ class SheerkaDebugManager(BaseService):
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS)) return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def get_debug_settings(self): def get_debug_settings(self):
return self.debug_vars_settings return self.sheerka.new(BuiltinConcepts.TO_LIST, body=self.debug_vars_settings)
+1 -1
View File
@@ -21,7 +21,7 @@ class SheerkaDump(BaseService):
super().__init__(sheerka) super().__init__(sheerka)
def initialize(self): def initialize(self):
self.sheerka.bind_service_method(self.dump_desc, True, "desc") # has_side_effect 'cause concept is evaluated self.sheerka.bind_service_method(self.dump_desc, True, "old_desc") # has_side_effect 'cause concept is evaluated
self.sheerka.bind_service_method(self.dump_sdp, False, "dump_sdp") self.sheerka.bind_service_method(self.dump_sdp, False, "dump_sdp")
def dump_desc(self, *concept_names, eval=False): def dump_desc(self, *concept_names, eval=False):
+4 -6
View File
@@ -16,7 +16,8 @@ class SheerkaOut(BaseService):
self.sheerka.bind_service_method(self.process_return_values, False) self.sheerka.bind_service_method(self.process_return_values, False)
def create_out_tree(self, context, obj): def create_out_tree(self, context, obj):
return self.create_out_tree_recursive(context, {'__obj': obj}, DeveloperVisitor(self, set(), 0)) debugger = context.get_debugger("Out.visitor", "create_out_tree")
return self.create_out_tree_recursive(context, {'__obj': obj}, DeveloperVisitor(self, debugger, set(), 0))
def create_out_tree_recursive(self, context, bag, visitor): def create_out_tree_recursive(self, context, bag, visitor):
debugger = context.get_debugger(SheerkaOut.NAME, "create_out_tree") debugger = context.get_debugger(SheerkaOut.NAME, "create_out_tree")
@@ -40,8 +41,7 @@ class SheerkaOut(BaseService):
visitor.already_seen.add(rule.id) visitor.already_seen.add(rule.id)
bag.update(as_bag(current_obj)) # update with the current obj attributes bag.update(as_bag(current_obj)) # update with the current obj attributes
visitor.visit(context, rule.compiled_action, bag) res = visitor.visit(context, rule.compiled_action, bag)
res = visitor.get_result()
if res is None: if res is None:
debugger.debug_log(f"No matching rule.") debugger.debug_log(f"No matching rule.")
@@ -67,9 +67,7 @@ class SheerkaOut(BaseService):
if out_tree: if out_tree:
for visitor in self.out_visitors: for visitor in self.out_visitors:
visitor.visit(context, out_tree, None) visitor.visit(out_tree)
if hasattr(visitor, "finalize"):
visitor.finalize()
return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS)) return self.sheerka.ret(self.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
return self.sheerka.ret(self.NAME, False, self.sheerka.new(BuiltinConcepts.NO_RESULT)) return self.sheerka.ret(self.NAME, False, self.sheerka.new(BuiltinConcepts.NO_RESULT))
+106 -26
View File
@@ -63,6 +63,15 @@ class FormatAstNode:
return ", ".join(repr(item) for item in items) return ", ".join(repr(item) for item in items)
def clone(self, instance, props, **kwargs):
for prop_name in props:
setattr(instance, prop_name, getattr(self, prop_name))
for k, v in kwargs.items():
setattr(instance, k, v)
return instance
@dataclass @dataclass
class FormatAstRawText(FormatAstNode): class FormatAstRawText(FormatAstNode):
@@ -73,9 +82,15 @@ class FormatAstRawText(FormatAstNode):
class FormatAstVariable(FormatAstNode): class FormatAstVariable(FormatAstNode):
name: str name: str
format: Union[str, None] = None format: Union[str, None] = None
debug: bool = False
value: object = None value: object = None
index: object = None index: object = None
def clone(self, **kwargs):
return super().clone(FormatAstVariable(self.name),
("format", "debug", "value", "index"),
**kwargs)
@dataclass @dataclass
class FormatAstVariableNotFound(FormatAstNode): class FormatAstVariableNotFound(FormatAstNode):
@@ -93,29 +108,50 @@ class FormatAstList(FormatAstNode):
items_prop: str = None # where to search the list if variable does not resolve to an iterable items_prop: str = None # where to search the list if variable does not resolve to an iterable
recurse_on: str = None recurse_on: str = None
recursion_depth: int = 0 recursion_depth: int = 0
debug: bool = False
prefix: str = None
suffix: str = None
show_index: bool = False
items: 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"),
**kwargs)
@dataclass
class FormatAstDict(FormatAstNode):
variable: str
items_prop: str = None # where to search the dict if variable does not resolve to an iterable
debug: bool = False
prefix: str = None
suffix: str = None
items: object = None
def clone(self, **kwargs):
return super().clone(
FormatAstDict(self.variable),
("items_prop", "debug", "prefix", "suffix", "items"),
**kwargs)
@dataclass @dataclass
class FormatAstColor(FormatAstNode): class FormatAstColor(FormatAstNode):
def __init__(self, color, format_ast): color: str
self.color = color format_ast: FormatAstNode
self.format_ast = format_ast
def __repr__(self): def __repr__(self):
return f"{self.color}({self.format_ast})" return f"{self.color}({self.format_ast})"
def __eq__(self, other): def clone(self, **kwargs):
if id(self) == id(other): return super().clone(
return True FormatAstColor(self.color, self.format_ast),
(),
if not isinstance(other, FormatAstColor): **kwargs)
return False
return self.color == other.color and self.format_ast == other.format_ast
def __hash__(self):
return hash((self.color, self.format_ast))
@dataclass @dataclass
@@ -125,21 +161,19 @@ class FormatAstFunction(FormatAstNode):
kwargs: dict = None kwargs: dict = None
@dataclass
class FormatAstSequence(FormatAstNode): class FormatAstSequence(FormatAstNode):
def __init__(self, items): items: list
self.items = items debug: bool = False
def __repr__(self): def __repr__(self):
return "FormatAstSequence(" + self.repr_value(self.items) + ")" return "FormatAstSequence(" + self.repr_value(self.items) + ")"
def __eq__(self, other): def clone(self, **kwargs):
if id(self) == id(other): return super().clone(
return True FormatAstSequence(self.items),
("debug",),
if not isinstance(other, FormatAstSequence): **kwargs)
return False
return self.items == other.items
class FormatRuleParser(IterParser): class FormatRuleParser(IterParser):
@@ -170,6 +204,9 @@ class FormatRuleParser(IterParser):
if value[0] in ("'", '"'): if value[0] in ("'", '"'):
return value[1:-1] return value[1:-1]
if value in ("True", "False"):
return bool(value)
try: try:
return int(value) return int(value)
except ValueError: except ValueError:
@@ -278,6 +315,8 @@ class FormatRuleParser(IterParser):
return self.return_color(func_name.value, args, kwargs) return self.return_color(func_name.value, args, kwargs)
elif func_name.value == "list": elif func_name.value == "list":
return self.return_list(args, kwargs) return self.return_list(args, kwargs)
elif func_name.value == "dict":
return self.return_dict(args, kwargs)
return FormatAstFunction(func_name.value, self.to_text(args), self.to_text(kwargs)) return FormatAstFunction(func_name.value, self.to_text(args), self.to_text(kwargs))
@@ -347,13 +386,19 @@ class FormatRuleParser(IterParser):
return FormatAstColor(color, FormatAstVariable(variable, vformat)) return FormatAstColor(color, FormatAstVariable(variable, vformat))
def return_list(self, args, kwargs): def return_list(self, args, kwargs):
"""
Looking for variable_name, [recurse_on], [recursion_depth], [items_prop]
:param args:
:param kwargs:
:return:
"""
len_args = len(args) len_args = len(args)
if len_args < 1: if len_args < 1:
self.error_sink = FormatRuleSyntaxError("variable name not found", None) self.error_sink = FormatRuleSyntaxError("variable name not found", None)
return None return None
if len_args > 3: if len_args > 4:
self.error_sink = FormatRuleSyntaxError("too many positional arguments", args[3][0]) self.error_sink = FormatRuleSyntaxError("too many positional arguments", args[4][0])
return None return None
variable_name = get_text_from_tokens(args[0]) variable_name = get_text_from_tokens(args[0])
@@ -364,6 +409,10 @@ class FormatRuleParser(IterParser):
elif len_args == 3: elif len_args == 3:
recursion_depth = self.to_value(args[1]) recursion_depth = self.to_value(args[1])
recurse_on = self.to_value(args[2]) recurse_on = self.to_value(args[2])
elif len_args == 4:
recursion_depth = self.to_value(args[1])
recurse_on = self.to_value(args[2])
items_prop = self.to_value(args[3])
if "recurse_on" in kwargs: if "recurse_on" in kwargs:
recurse_on = self.to_value(kwargs["recurse_on"]) recurse_on = self.to_value(kwargs["recurse_on"])
@@ -383,6 +432,34 @@ class FormatRuleParser(IterParser):
return FormatAstList(variable_name, items_prop, recurse_on, recursion_depth) return FormatAstList(variable_name, items_prop, recurse_on, recursion_depth)
def return_dict(self, args, kwargs):
len_args = len(args)
if len_args < 1:
self.error_sink = FormatRuleSyntaxError("variable name not found", None)
return None
if len_args > 1:
self.error_sink = FormatRuleSyntaxError("too many positional arguments", args[1][0])
return None
variable_name = get_text_from_tokens(args[0])
kwargs_parameters = {}
for prop in ("items_prop", "prefix", "suffix", "debug"):
if prop in kwargs:
kwargs_parameters[prop] = self.to_value(kwargs[prop])
if "debug" in kwargs_parameters:
if "prefix" not in kwargs_parameters:
kwargs_parameters["prefix"] = "{"
if "suffix" not in kwargs_parameters:
kwargs_parameters["suffix"] = "}"
if self.error_sink:
return None
return FormatAstDict(variable_name, **kwargs_parameters)
@dataclass() @dataclass()
class RulePredicate: class RulePredicate:
@@ -556,6 +633,9 @@ class SheerkaRuleManager(BaseService):
Rule("print", "Display formatted list", Rule("print", "Display formatted list",
"isinstance(__ret_container, BuiltinConcepts.TO_LIST)", "isinstance(__ret_container, BuiltinConcepts.TO_LIST)",
"list(__ret_container)"), "list(__ret_container)"),
Rule("print", "Display formatted dict",
"isinstance(__ret_container, BuiltinConcepts.TO_DICT)",
"dict(__ret_container)"),
] ]
for r in rules: for r in rules:
+10 -1
View File
@@ -2,11 +2,11 @@ import ast
import importlib import importlib
import inspect import inspect
import pkgutil import pkgutil
import re
from cache.Cache import Cache from cache.Cache import Cache
from core.ast_helpers import ast_to_props from core.ast_helpers import ast_to_props
from core.tokenizer import TokenKind, Tokenizer from core.tokenizer import TokenKind, Tokenizer
from pyparsing import *
default_debug_name = "*default*" default_debug_name = "*default*"
debug_activated = set() debug_activated = set()
@@ -38,6 +38,15 @@ PRIMITIVES_TYPES = (str, bool, type(None), int, float, list, dict, set, bytes, t
expressions_cache = Cache() expressions_cache = Cache()
ESC = Literal('\x1b')
integer = Word(nums)
escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer, ';')) +
oneOf(list(alphas)))
def no_color_str(text):
return Suppress(escapeSeq).transformString(str(text))
def my_debug(*args, check_started=None): def my_debug(*args, check_started=None):
""" """
+5
View File
@@ -129,12 +129,14 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
This function can only be a draft, as there may be tons of different situations This function can only be a draft, as there may be tons of different situations
I guess that it can only be complete when will we have access to Sheerka memory I guess that it can only be complete when will we have access to Sheerka memory
""" """
debugger = context.get_debugger(DefConceptEvaluator.NAME, "get_variables")
# #
# Case of NameNode # Case of NameNode
# #
if isinstance(ret_value, NameNode): if isinstance(ret_value, NameNode):
names = [str(t.value) for t in ret_value.tokens if t.type in ( names = [str(t.value) for t in ret_value.tokens if t.type in (
TokenKind.IDENTIFIER, TokenKind.STRING, TokenKind.KEYWORD)] TokenKind.IDENTIFIER, TokenKind.STRING, TokenKind.KEYWORD)]
debugger.debug_var("names", names, hint="from NameNode")
return set(filter(lambda x: x in concept_name and context.sheerka.not_is_variable(x), names)) return set(filter(lambda x: x in concept_name and context.sheerka.not_is_variable(x), names))
# #
@@ -143,6 +145,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, ParsingExpression): if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, ParsingExpression):
visitor = ConceptOrRuleNameVisitor() visitor = ConceptOrRuleNameVisitor()
visitor.visit(ret_value.value.value) visitor.visit(ret_value.value.value)
debugger.debug_var("names", visitor.names, hint="from BNF")
return set(visitor.names) return set(visitor.names)
# #
@@ -152,6 +155,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
if len(concept_name) > 1: if len(concept_name) > 1:
visitor = UnreferencedVariablesVisitor(context) visitor = UnreferencedVariablesVisitor(context)
names = visitor.get_names(python_node.ast_) names = visitor.get_names(python_node.ast_)
debugger.debug_var("names", names, hint="from python node")
return set(filter(lambda x: x in concept_name and context.sheerka.not_is_variable(x), names)) return set(filter(lambda x: x in concept_name and context.sheerka.not_is_variable(x), names))
else: else:
return set() return set()
@@ -168,6 +172,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
for identifier in [i for i in concept_name if str(i).isalnum()]: for identifier in [i for i in concept_name if str(i).isalnum()]:
if identifier in tokens: if identifier in tokens:
variables.add(identifier) variables.add(identifier)
debugger.debug_var("names", variables, hint="from concept")
return variables return variables
return set() return set()
+9 -3
View File
@@ -92,6 +92,9 @@ class PythonEvaluator(OneReturnValueEvaluator):
debugger = context.get_debugger(PythonEvaluator.NAME, "eval") debugger = context.get_debugger(PythonEvaluator.NAME, "eval")
debugger.debug_entering(node=node) debugger.debug_entering(node=node)
exception_debugger = context.get_debugger("Exceptions", PythonEvaluator.NAME + ".eval")
get_trace_back = context.debug_enabled or exception_debugger.is_enabled()
context.log(f"Evaluating python node {node}.", self.name) context.log(f"Evaluating python node {node}.", self.name)
# If we evaluate a Concept metadata which is NOT the body ex (pre, post, where...) # If we evaluate a Concept metadata which is NOT the body ex (pre, post, where...)
@@ -136,9 +139,12 @@ class PythonEvaluator(OneReturnValueEvaluator):
except Exception as ex: except Exception as ex:
if concepts_entries is None: if concepts_entries is None:
concepts_entries = self.get_concepts_entries_from_globals(my_globals) concepts_entries = self.get_concepts_entries_from_globals(my_globals)
errors.append(PythonEvalError(ex, eval_error = PythonEvalError(ex,
traceback.format_exc() if context.debug_enabled else None, traceback.format_exc() if get_trace_back else None,
self.get_concepts_values_from_globals(globals_, concepts_entries))) self.get_concepts_values_from_globals(globals_, concepts_entries))
errors.append(eval_error)
exception_debugger.debug_var("exception", eval_error.error, is_error=True)
exception_debugger.debug_var("trace", eval_error.traceback, is_error=True)
if evaluated == NotInit: if evaluated == NotInit:
if len(errors) == 1: if len(errors) == 1:
+13 -6
View File
@@ -1,7 +1,8 @@
from IPython.core.display import JSON
from core.sheerka.Sheerka import Sheerka from core.sheerka.Sheerka import Sheerka
from ipykernel.ipkernel import IPythonKernel from ipykernel.ipkernel import IPythonKernel
from ipykernel.kernelapp import IPKernelApp from ipykernel.kernelapp import IPKernelApp
from sheerkapickle import encode from sheerkapickle.SheerkaPickler import SheerkaPickler
class SheerkaKernel(IPythonKernel): class SheerkaKernel(IPythonKernel):
@@ -25,12 +26,19 @@ class SheerkaKernel(IPythonKernel):
def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False): def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False):
result = self.sheerka.evaluate_user_input(code) result = self.sheerka.evaluate_user_input(code)
# md = Markdown(f'<span style="color: {hexcolor}">{input_data}</span>')
as_json = JSON(SheerkaPickler(self.sheerka).flatten(result))
a = {
"a": "value1",
"b": "value2",
}
if not silent: if not silent:
display_data_content = { display_data_content = {
'data': { 'data': {
'text/plain': str(result), # 'text/plain': str(result),
'application/json': encode(self.sheerka, result) # "text/markdown": md._repr_markdown_(),
'application/json': JSON(a)._repr_json_(),
}, },
'metadata': { 'metadata': {
'application/json': {'expanded': True} 'application/json': {'expanded': True}
@@ -38,9 +46,8 @@ class SheerkaKernel(IPythonKernel):
'execution_count': self.sheerka.execution_count, 'execution_count': self.sheerka.execution_count,
} }
stream_content = {'name': 'stdout', 'text': display_data_content}
# self.send_response(self.iopub_socket, "stream", stream_content) self.send_response(self.iopub_socket, "display_data", display_data_content)
self.send_response(self.iopub_socket, "execute_result", display_data_content)
return {'status': 'ok', return {'status': 'ok',
# The base class increments the execution count # The base class increments the execution count
+93
View File
@@ -0,0 +1,93 @@
import re
from core.sheerka.services.SheerkaRuleManager import FormatAstNode
from core.utils import CONSOLE_COLORS_MAP as CCM, no_color_str
from out.OutVisitor import OutVisitor
get_start = re.compile(r"^([\(\{\[]\w*)")
class AsStrVisitor(OutVisitor):
def __init__(self, expand=False):
self.expand = expand
def visit_FormatAstRawText(self, format_ast):
return str(format_ast.text)
def visit_FormatAstVariable(self, format_ast):
if isinstance(format_ast.value, FormatAstNode):
value = self.visit(format_ast.value)
else:
value = format_ast.value
if format_ast.debug and isinstance(value, str):
value = "'" + str(value).translate(str.maketrans({"'": r"\'"})) + "'"
return str(value)
def visit_FormatAstVariableNotFound(self, format_ast):
return CCM["red"] + format_ast.name + CCM["reset"]
def visit_FormatAstColor(self, format_ast):
return CCM[format_ast.color] + self.visit(format_ast.format_ast) + CCM["reset"]
def visit_FormatAstSequence(self, format_ast):
return "".join([self.visit(item) for item in format_ast.items])
def visit_FormatAstList(self, format_ast):
first = True
result = ""
if format_ast.prefix:
result += format_ast.prefix
sep = ",\n" if self.expand else ", " if format_ast.debug else "\n"
for item in format_ast.items:
if not first:
result += sep
if format_ast.show_index:
result += f"[{item.index}] "
result += self.visit(item)
first = False
if format_ast.suffix:
result += format_ast.suffix
return result
def visit_FormatAstDict(self, format_ast):
first = True
result = ""
keys_values = []
max_len = 0
for k, v in format_ast.items:
value = self.visit(k)
len_value = len(no_color_str(value))
if len_value > max_len:
max_len = len_value
keys_values.append(value)
if format_ast.prefix:
result += format_ast.prefix
sep = ",\n" if self.expand else ", " if format_ast.debug else "\n"
for i, (k, v) in enumerate(format_ast.items):
start = "" if first else sep
key = f"{keys_values[i]:<{max_len}}" if (self.expand or not format_ast.debug) else keys_values[i]
colon = ": "
indent = len(no_color_str(key)) + len(colon)
value = self.visit(v)
if self.expand:
if m := get_start.match(no_color_str(value)):
indent += len(m.group(1))
value = value.replace("\n", "\n" + " " * indent)
first = False
result += start + key + colon + value
if format_ast.suffix:
result += format_ast.suffix
return result
+28 -40
View File
@@ -1,55 +1,43 @@
from out.OutVisitor import OutVisitor from out.AsStrVisitor import AsStrVisitor
from core.sheerka.services.SheerkaRuleManager import FormatAstNode
class ConsoleVisitor(OutVisitor): class ConsoleVisitor(AsStrVisitor):
""" """
Prints to the console Prints to the console
""" """
COLORS = {
"reset": "\u001b[0m",
"black": "\u001b[30m",
"red": "\u001b[31m",
"green": "\u001b[32m",
"yellow": "\u001b[33m",
"blue": "\u001b[34m",
"magenta": "\u001b[35m",
"cyan": "\u001b[36m",
"white": "\u001b[37m",
}
def __init__(self): def __init__(self, expand_mode="auto"):
super().__init__()
self.out = print self.out = print
self.expand_mode = expand_mode
def finalize(self): def visit_FormatAstRawText(self, format_ast):
self.out("") self.out(super().visit_FormatAstRawText(format_ast))
def visit_FormatAstRawText(self, context, format_ast, bag): def visit_FormatAstVariable(self, format_ast):
self.out(format_ast.text, end="") self.out(super().visit_FormatAstVariable(format_ast))
def visit_FormatAstVariable(self, context, format_ast, bag): def visit_FormatAstVariableNotFound(self, format_ast):
if isinstance(format_ast.value, FormatAstNode): self.out(super().visit_FormatAstVariableNotFound(format_ast))
self.visit(context, format_ast.value, bag)
return
self.out(format_ast.value, end="")
def visit_FormatAstVariableNotFound(self, context, format_ast, bag): def visit_FormatAstColor(self, format_ast):
self.out(self.COLORS["red"] + format_ast.name + self.COLORS["reset"], end="") self.out(super().visit_FormatAstColor(format_ast))
def visit_FormatAstSequence(self, context, format_ast, bag): def visit_FormatAstSequence(self, format_ast):
for item in format_ast.items: visitor = AsStrVisitor()
self.visit(context, item, bag) self.out(visitor.visit_FormatAstSequence(format_ast))
def visit_FormatAstList(self, context, format_ast, bag): def visit_FormatAstList(self, format_ast):
first = True visitor = AsStrVisitor()
for item in format_ast.items: res = visitor.visit_FormatAstList(format_ast)
if not first: self.out(res)
self.out("") # print new line
self.visit(context, item, bag)
first = False
def visit_FormatAstColor(self, context, format_ast, bag): def visit_FormatAstDict(self, format_ast):
self.out(self.COLORS[format_ast.color], end="") if self.expand_mode == "always":
self.visit(context, format_ast.format_ast, bag) expand = True
self.out(self.COLORS["reset"], end="") else:
expand = False
visitor = AsStrVisitor(expand)
res = visitor.visit_FormatAstDict(format_ast)
self.out(res)
+101 -46
View File
@@ -1,31 +1,43 @@
from core.sheerka.services.SheerkaRuleManager import FormatAstVariable, FormatAstVariableNotFound, FormatAstSequence, \ from core.sheerka.services.SheerkaRuleManager import FormatAstVariable, FormatAstVariableNotFound, FormatAstColor, \
FormatAstColor, FormatAstList, FormatAstRawText FormatAstList, FormatAstRawText, FormatAstDict
from core.utils import evaluate_expression, as_bag from core.utils import evaluate_expression, as_bag
from out.OutVisitor import OutVisitor
fstring = compile('f"{value:{format}}"', "DeveloperVisitor.fstring", mode="eval") fstring = compile('f"{value:{format}}"', "DeveloperVisitor.fstring", mode="eval")
class DeveloperVisitor(OutVisitor): class DeveloperVisitor:
""" """
This visitor is used to resolve all the variables as well as all the lists This visitor is used to resolve all the variables as well as all the lists
Once completed, it will be passed to the ConsoleVisitor for console print Once completed, it will be passed to the ConsoleVisitor for console print
""" """
def __init__(self, sheerka_out, already_seen, list_recursion_depth): def __init__(self, sheerka_out, debugger, already_seen, list_recursion_depth):
self._result = None self._result = None
self.sheerka_out = sheerka_out self.sheerka_out = sheerka_out
self.debugger = debugger
self.already_seen = already_seen self.already_seen = already_seen
self.list_recursion_depth = list_recursion_depth self.list_recursion_depth = list_recursion_depth
def visit(self, context, format_ast, bag):
name = format_ast.__class__.__name__
method = 'visit_' + name
visit_method = getattr(self, method, self.generic_visit)
return visit_method(context, format_ast, bag)
def generic_visit(self, context, format_ast, bag):
pass
def visit_FormatAstRawText(self, context, format_ast, bag): def visit_FormatAstRawText(self, context, format_ast, bag):
if context.debug_enabled: if self.debugger.is_enabled():
context.debug_entering("DeveloperVisitor", "visit_FormatAstRawText", format_ast=format_ast, bag=bag) debug_bag = {"format_ast": format_ast, "bag": bag}
return self.set_result(format_ast) self.debugger.debug_log(f"Entering visit_FormatAstRawText with {debug_bag}")
return format_ast
def visit_FormatAstVariable(self, context, format_ast, bag): def visit_FormatAstVariable(self, context, format_ast, bag):
if context.debug_enabled: if self.debugger.is_enabled():
context.debug_entering("DeveloperVisitor", "visit_FormatAstVariable", format_ast=format_ast, bag=bag) debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstVariable with {debug_bag}")
try: try:
value = evaluate_expression(format_ast.name, bag) value = evaluate_expression(format_ast.name, bag)
@@ -33,6 +45,8 @@ class DeveloperVisitor(OutVisitor):
"__obj": value, "__obj": value,
format_ast.name: value format_ast.name: value
} }
# create a bag entry with the last part of the variable
# eg: for __ret.status, creates an entry status
try: try:
index = format_ast.name.rindex(".") index = format_ast.name.rindex(".")
sub_bag[format_ast.name[index + 1:]] = value sub_bag[format_ast.name[index + 1:]] = value
@@ -43,32 +57,31 @@ class DeveloperVisitor(OutVisitor):
if format_ast.format: if format_ast.format:
res = eval(fstring, {"value": res, "format": format_ast.format}) res = eval(fstring, {"value": res, "format": format_ast.format})
return self.set_result(FormatAstVariable(format_ast.name, return format_ast.clone(value=res)
format_ast.format,
res,
format_ast.index))
except NameError as error: except NameError as error:
context.debug("DeveloperVisitor", "visit_FormatAstList", "evaluate_expression", error, is_error=True) context.debug("DeveloperVisitor", "visit_FormatAstList", "evaluate_expression", error, is_error=True)
return self.set_result(FormatAstVariableNotFound(format_ast.name)) return FormatAstVariableNotFound(format_ast.name)
def visit_FormatAstSequence(self, context, format_ast, bag): def visit_FormatAstSequence(self, context, format_ast, bag):
if context.debug_enabled: if self.debugger.is_enabled():
context.debug_entering("DeveloperVisitor", "visit_FormatAstSequence", format_ast=format_ast, bag=bag) debug_bag = {"format_ast": format_ast, "bag": bag}
return self.set_result(FormatAstSequence([self.visit(context, item, bag) for item in format_ast.items])) self.debugger.debug_log(f"Entering visit_FormatAstSequence with {debug_bag}")
return format_ast.clone(items=[self.visit(context, item, bag) for item in format_ast.items])
def visit_FormatAstColor(self, context, format_ast, bag): def visit_FormatAstColor(self, context, format_ast, bag):
if context.debug_enabled: if self.debugger.is_enabled():
context.debug_entering("DeveloperVisitor", "visit_FormatAstColor", format_ast=format_ast, bag=bag) debug_bag = {"format_ast": format_ast, "bag": bag}
return self.set_result(FormatAstColor(format_ast.color, self.visit(context, format_ast.format_ast, bag))) self.debugger.debug_log(f"Entering visit_FormatAstColor with {debug_bag}")
return format_ast.clone(format_ast=self.visit(context, format_ast.format_ast, bag))
def visit_FormatAstList(self, context, format_ast, bag): def visit_FormatAstList(self, context, format_ast, bag):
if context.debug_enabled: if self.debugger.is_enabled():
context.debug_entering("DeveloperVisitor", "visit_FormatAstList", format_ast=format_ast, bag=bag) debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstList with {debug_bag}")
try: try:
value = evaluate_expression(format_ast.variable, bag) value = evaluate_expression(format_ast.variable, bag)
if value is None: if value is None:
return self.set_result(FormatAstVariable(format_ast.variable, format_ast.format, None)) return FormatAstVariable(format_ast.variable, format_ast.format, value=None)
if hasattr(value, "__iter__"): if hasattr(value, "__iter__"):
items = value items = value
@@ -78,22 +91,29 @@ class DeveloperVisitor(OutVisitor):
items = evaluate_expression(f"self.{items_props}", {"self": value}) items = evaluate_expression(f"self.{items_props}", {"self": value})
if not hasattr(items, "__iter__"): if not hasattr(items, "__iter__"):
# Definition error ? No list found # Definition error ? No list found
return self.set_result(FormatAstVariable(format_ast.variable, None, value)) return FormatAstVariable(format_ast.variable, value=value)
recursion_depth, recurse_on = self.get_recurse_info(value, format_ast.recursion_depth, format_ast.recurse_on) recursion_depth, recurse_on = self.get_recurse_info(value, format_ast.recursion_depth,
format_ast.recurse_on)
result = [] # TODO change into generator result = [] # TODO change into generator
for i, item in enumerate(items): for i, item in enumerate(items):
bag["__item"] = item bag["__item"] = item
sub_visitor = DeveloperVisitor(self.sheerka_out, set(), self.list_recursion_depth) sub_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth)
result.append(sub_visitor.visit(context, FormatAstVariable("__item", None, item, i), bag)) result.append(sub_visitor.visit(context, FormatAstVariable("__item",
debug=format_ast.debug,
value=item,
index=i), bag))
# recursion management # recursion management
recursion_depth, recurse_on = self.get_recurse_info(item, recursion_depth, recurse_on) recursion_depth, recurse_on = self.get_recurse_info(item, recursion_depth, recurse_on)
if recursion_depth > 0: if recursion_depth > 0:
sub_items = evaluate_expression(recurse_on, as_bag(item)) sub_items = evaluate_expression(recurse_on, as_bag(item))
if sub_items and hasattr(sub_items, "__iter__"): if sub_items and hasattr(sub_items, "__iter__"):
sub_visitor = DeveloperVisitor(self.sheerka_out, set(), self.list_recursion_depth + 1) sub_visitor = DeveloperVisitor(self.sheerka_out,
self.debugger,
set(),
self.list_recursion_depth + 1)
bag[f"__{recurse_on}"] = sub_items bag[f"__{recurse_on}"] = sub_items
sub_items = sub_visitor.visit(context, FormatAstList(f"__{recurse_on}", sub_items = sub_visitor.visit(context, FormatAstList(f"__{recurse_on}",
@@ -102,29 +122,64 @@ class DeveloperVisitor(OutVisitor):
recursion_depth - 1), bag) recursion_depth - 1), bag)
result.append(sub_items) result.append(sub_items)
return self.set_result(FormatAstList(variable=format_ast.variable, return format_ast.clone(recurse_on=recurse_on, recursion_depth=recursion_depth, items=result)
items_prop=format_ast.items_prop,
recurse_on=recurse_on,
recursion_depth=recursion_depth,
items=result))
except NameError as error: except NameError as error:
context.debug("DeveloperVisitor", "visit_FormatAstList", "evaluate_expression", error, is_error=True) self.debugger.debug_log(error, is_error=True)
var_name = format_ast.variable if error.args[0] == format_ast.variable else \ var_name = format_ast.variable if error.args[0] == format_ast.variable else \
format_ast.variable + "." + error.args[0] format_ast.variable + "." + error.args[0]
return self.set_result(FormatAstVariableNotFound(var_name)) return FormatAstVariableNotFound(var_name)
def visit_FormatAstFunction(self, context, format_ast, bag): def visit_FormatAstFunction(self, context, format_ast, bag):
if context.debug_enabled: if self.debugger.is_enabled():
context.debug_entering("DeveloperVisitor", "visit_FormatAstFunction", format_ast=format_ast, bag=bag) debug_bag = {"format_ast": format_ast, "bag": bag}
unknown = FormatAstColor("red", FormatAstRawText(f"function '{format_ast.name}' is unknown")) self.debugger.debug_log(f"Entering visit_FormatAstFunction with {debug_bag}")
return self.set_result(unknown) return FormatAstColor("red", FormatAstRawText(f"function '{format_ast.name}' is unknown"))
def set_result(self, result): def visit_FormatAstDict(self, context, format_ast, bag):
self._result = result if self.debugger.is_enabled():
return result debug_bag = {"format_ast": format_ast, "bag": bag}
self.debugger.debug_log(f"Entering visit_FormatAstDict with {debug_bag}")
def get_result(self): try:
return self._result result = []
value = evaluate_expression(format_ast.variable, bag)
if value is None:
return FormatAstVariable(format_ast.variable, format_ast.format, value=None)
if isinstance(value, dict):
items = value
else:
# the variable does not resolve to a dictionary, let's look at one of its attribute
items_props = format_ast.items_prop or "body"
items = evaluate_expression(f"self.{items_props}", {"self": value})
if not isinstance(items, dict):
# Definition error ? No Dictionary found
return FormatAstVariable(format_ast.variable, value=value)
for i, (k, v) in enumerate(items.items()):
bag["__key"] = k
key_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth)
key_res = key_visitor.visit(context,
FormatAstVariable("__key", value=k, index=i, debug=format_ast.debug),
bag)
bag["__value"] = v
value_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth)
to_visit = FormatAstDict("__value", debug=True, prefix='{', suffix='}') if isinstance(v, dict) else \
FormatAstList("__value", debug=True, prefix='[', suffix=']') if isinstance(v, list) else \
FormatAstVariable("__value", value=v, index=k, debug=format_ast.debug)
value_res = value_visitor.visit(context, to_visit, bag)
result.append((key_res, value_res))
return format_ast.clone(items=result)
except NameError as error:
self.debugger.debug_log(error, is_error=True)
var_name = format_ast.variable if error.args[0] == format_ast.variable else \
format_ast.variable + "." + error.args[0]
return FormatAstVariableNotFound(var_name)
@staticmethod @staticmethod
def get_recurse_info(obj, recursion_depth, recurse_on): def get_recurse_info(obj, recursion_depth, recurse_on):
+3 -3
View File
@@ -1,10 +1,10 @@
class OutVisitor: class OutVisitor:
def visit(self, context, format_ast, bag): def visit(self, format_ast):
name = format_ast.__class__.__name__ name = format_ast.__class__.__name__
method = 'visit_' + name method = 'visit_' + name
visit_method = getattr(self, method, self.generic_visit) visit_method = getattr(self, method, self.generic_visit)
return visit_method(context, format_ast, bag) return visit_method(format_ast)
def generic_visit(self, context, format_ast, bag): def generic_visit(self, format_ast):
pass pass
+9 -2
View File
@@ -37,10 +37,15 @@ class PythonNode(Node):
def __init__(self, source, ast_=None, objects=None): def __init__(self, source, ast_=None, objects=None):
self.source = source self.source = source
self.ast_ = ast_ if ast_ else ast.parse(source, mode="eval") if source else None self.ast_ = ast_ # if ast_ else ast.parse(source, mode="eval") if source else None
self.objects = objects or {} # when objects (mainly concepts or rules) are recognized in the expression self.objects = objects or {} # when objects (mainly concepts or rules) are recognized in the expression
self.compiled = None self.compiled = None
def init_ast(self):
if self.ast_ is None and self.source:
self.ast_ = ast.parse(self.source, mode="eval")
return self
def get_compiled(self): def get_compiled(self):
if self.compiled is None: if self.compiled is None:
self.compiled = compile(self.ast_, "<string>", "eval") self.compiled = compile(self.ast_, "<string>", "eval")
@@ -60,11 +65,13 @@ class PythonNode(Node):
if self.source != other.source: if self.source != other.source:
return False return False
if self.ast_ and other.ast_:
self_dump = self.get_dump(self.ast_) self_dump = self.get_dump(self.ast_)
other_dump = self.get_dump(other.ast_) other_dump = self.get_dump(other.ast_)
return self_dump == other_dump return self_dump == other_dump
return True
def __hash__(self): def __hash__(self):
return hash((self.source, self.ast_.hash)) return hash((self.source, self.ast_.hash))
+1 -1
View File
@@ -318,7 +318,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
def test_i_can_evaluate_when_compiled_is_set_up_with_return_value(self): def test_i_can_evaluate_when_compiled_is_set_up_with_return_value(self):
sheerka = self.get_sheerka() sheerka = self.get_sheerka()
python_node = PythonNode("1 +1 ") python_node = PythonNode("1 +1 ").init_ast()
parser_result = ParserResultConcept(parser="who", value=python_node) parser_result = ParserResultConcept(parser="who", value=python_node)
concept = Concept("to_eval").def_var("prop") concept = Concept("to_eval").def_var("prop")
+7 -3
View File
@@ -7,7 +7,7 @@ from core.global_symbols import RULE_COMPARISON_CONTEXT
from core.rule import Rule from core.rule import Rule
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, FormatRuleParser, \ from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, FormatRuleParser, \
FormatAstRawText, FormatAstVariable, FormatAstSequence, FormatAstFunction, \ FormatAstRawText, FormatAstVariable, FormatAstSequence, FormatAstFunction, \
FormatRuleSyntaxError, FormatAstList, UnexpectedEof, FormatAstColor, RulePredicate FormatRuleSyntaxError, FormatAstList, UnexpectedEof, FormatAstColor, RulePredicate, FormatAstDict
from core.tokenizer import Token, TokenKind from core.tokenizer import Token, TokenKind
from parsers.BaseNodeParser import SourceCodeWithConceptNode, SourceCodeNode from parsers.BaseNodeParser import SourceCodeWithConceptNode, SourceCodeNode
from parsers.PythonParser import PythonNode from parsers.PythonParser import PythonNode
@@ -112,6 +112,9 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
("{variable:format}", FormatAstVariable("variable", "format")), ("{variable:format}", FormatAstVariable("variable", "format")),
("{variable:3}", FormatAstVariable("variable", "3")), ("{variable:3}", FormatAstVariable("variable", "3")),
(r"\not_a_function(a={var})", seq([raw("not_a_function(a="), var("var"), raw(")")])), (r"\not_a_function(a={var})", seq([raw("not_a_function(a="), var("var"), raw(")")])),
("dict(var_name)", FormatAstDict("var_name")),
("dict(var_name, items_prop='props')", FormatAstDict("var_name", items_prop='props')),
("dict(var_name, debug=True)", FormatAstDict("var_name", debug=True, prefix="{", suffix="}"))
]) ])
def test_i_can_parse_format_rule(self, text, expected): def test_i_can_parse_format_rule(self, text, expected):
assert FormatRuleParser(text).parse() == expected assert FormatRuleParser(text).parse() == expected
@@ -129,10 +132,11 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
("red(xy {v})", FormatRuleSyntaxError("Invalid identifier", None)), ("red(xy {v})", FormatRuleSyntaxError("Invalid identifier", None)),
("list()", FormatRuleSyntaxError("variable name not found", None)), ("list()", FormatRuleSyntaxError("variable name not found", None)),
("list(recursion_depth=2)", FormatRuleSyntaxError("variable name not found", None)), ("list(recursion_depth=2)", FormatRuleSyntaxError("variable name not found", None)),
("list(a,b,c,d)", FormatRuleSyntaxError("too many positional arguments", ("list(a,b,c,d,e)", FormatRuleSyntaxError("too many positional arguments",
Token(TokenKind.IDENTIFIER, "d", 11, 1, 12))), Token(TokenKind.IDENTIFIER, "e", 13, 1, 14))),
("list(a, recursion_depth=hello)", FormatRuleSyntaxError("'hello' is not numeric", None)), ("list(a, recursion_depth=hello)", FormatRuleSyntaxError("'hello' is not numeric", None)),
("list(a, recursion_depth='hello')", FormatRuleSyntaxError("'recursion_depth' must be an integer", None)), ("list(a, recursion_depth='hello')", FormatRuleSyntaxError("'recursion_depth' must be an integer", None)),
("dict()", FormatRuleSyntaxError("variable name not found", None)),
]) ])
def test_i_cannot_parse_invalid_format(self, text, expected_error): def test_i_cannot_parse_invalid_format(self, text, expected_error):
parser = FormatRuleParser(text) parser = FormatRuleParser(text)
+168
View File
@@ -0,0 +1,168 @@
import pytest
from core.sheerka.services.SheerkaRuleManager import FormatAstRawText, FormatAstVariable, FormatAstVariableNotFound, \
FormatAstSequence, FormatAstList, FormatAstDict
from out.AsStrVisitor import AsStrVisitor
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestAsStrVisitor(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("format_text, expected", [
(FormatAstRawText("hello word"), "hello word"),
(FormatAstVariable('xxx', value="hello word"), "hello word"),
(FormatAstVariable('xxx', value=1), "1"),
(FormatAstVariable('xxx', value=1.5), "1.5"),
(FormatAstVariable('xxx', value="hello word", debug=True), "'hello word'"),
(FormatAstVariable('xxx', value=1, debug=True), "1"),
(FormatAstVariable('xxx', value=1.5, debug=True), "1.5"),
(FormatAstVariable('xxx', value=False, debug=True), "False"),
(FormatAstVariableNotFound('va_name'), "\x1b[31mva_name\x1b[0m"),
])
def test_i_can_print_simple_ast(self, format_text, expected):
visitor = AsStrVisitor()
res = visitor.visit(format_text)
assert res == expected
def test_i_can_print_sequence(self, capsys):
visitor = AsStrVisitor()
sequence = FormatAstSequence([
FormatAstRawText("hello word"),
FormatAstVariable('xxx', value=1),
FormatAstVariableNotFound('va_name'),
FormatAstVariable('xxx', value="hello word", debug=True)
])
res = visitor.visit(sequence)
assert res == "hello word1\x1b[31mva_name\x1b[0m'hello word'"
def test_i_can_print_list(self, capsys):
visitor = AsStrVisitor()
lst = FormatAstList("xxx", items=[
FormatAstVariable('__item', index=0, value="first element"),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])
res = visitor.visit(lst)
assert res == "first element\n'second element'"
def test_i_can_print_list_with_sub_items(self, capsys):
visitor = AsStrVisitor()
lst = FormatAstList("xxx", items=[
FormatAstVariable('__item', index=0, value="first element"),
FormatAstList("__sub", items=[
FormatAstVariable('__item', index=0, value="sub item 1"),
FormatAstVariable('__item', index=1, value="sub item 2"),
]),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])
res = visitor.visit(lst)
assert res == "first element\nsub item 1\nsub item 2\n'second element'"
def test_i_can_print_list_in_debug_mode(self, capsys):
visitor = AsStrVisitor()
lst = FormatAstList("xxx", debug=True, prefix="[", suffix="]", items=[
FormatAstVariable('__item', index=0, value="first element", debug=True),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])
res = visitor.visit(lst)
assert res == "['first element', 'second element']"
def test_i_can_print_expanded_list_in_debug_mode(self):
visitor = AsStrVisitor(expand=True)
lst = FormatAstList("xxx", debug=True, prefix="[", suffix="]", items=[
FormatAstVariable('__item', index=0, value="first element", debug=True),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])
res = visitor.visit(lst)
assert res == "['first element',\n'second element']"
def test_i_can_print_list_with_index(self):
visitor = AsStrVisitor()
lst = FormatAstList("xxx", show_index=True, items=[
FormatAstVariable('__item', index=0, value="first element"),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])
res = visitor.visit(lst)
assert res == "[0] first element\n[1] 'second element'"
def test_i_can_print_dict(self, capsys):
visitor = AsStrVisitor()
bag = FormatAstDict("xxx", items=[
(FormatAstVariable('__key', index=0, value="key1"),
FormatAstVariable('__value', index="key1", value=1)),
(FormatAstVariable('__key', index=1, value="long_key2"),
FormatAstVariable('__value', index="key2", value="value2")),
])
res = visitor.visit(bag)
assert res == "key1 : 1\nlong_key2: value2"
def test_i_can_print_dict_in_debug_mode(self, capsys):
visitor = AsStrVisitor()
bag = FormatAstDict("xxx", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="key1", debug=True),
FormatAstVariable('__value', index="key1", value=1, debug=True)),
(FormatAstVariable('__key', index=1, value="long_key2", debug=True),
FormatAstVariable('__value', index="key2", value="value2", debug=True)),
])
res = visitor.visit(bag)
assert res == "{'key1': 1, 'long_key2': 'value2'}"
def test_i_can_print_expanded_dict_in_debug_mode(self):
visitor = AsStrVisitor(expand=True)
bag = FormatAstDict("xxx", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="key1", debug=True),
FormatAstVariable('__value', index="key1", value=1, debug=True)),
(FormatAstVariable('__key', index=1, value="long_key2", debug=True),
FormatAstVariable('__value', index="key2", value="value2", debug=True)),
])
res = visitor.visit(bag)
assert res == "{'key1' : 1,\n'long_key2': 'value2'}"
def test_i_can_print_sub_level_of_dict_in_expand_mode(self):
visitor = AsStrVisitor(expand=True)
bag = FormatAstDict("xxx", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="key1", debug=True),
FormatAstVariable('__value', index="key1", value=1, debug=True)),
(FormatAstVariable('__key', index=0, value="key2", debug=True),
FormatAstDict("__value", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="sub_key1", debug=True),
FormatAstVariable('__value', index="sub_key1", value=1, debug=True)),
(FormatAstVariable('__key', index=1, value="sub_long_key2", debug=True),
FormatAstDict("__value", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="sub_sub_key1", debug=True),
FormatAstVariable('__value', index="sub_sub_key1", value=1, debug=True)),
(FormatAstVariable('__key', index=1, value="sub_sub_key2", debug=True),
FormatAstVariable('__value', index="sub_sub_key2", value="sub_sub_value", debug=True)),
])),
])),
(FormatAstVariable('__key', index=1, value="long_key3", debug=True),
FormatAstVariable('__value', index="key2", value="value2", debug=True)),
])
res = visitor.visit(bag)
assert res == """{'key1' : 1,
'key2' : {'sub_key1' : 1,
'sub_long_key2': {'sub_sub_key1': 1,
'sub_sub_key2': 'sub_sub_value'}},
'long_key3': 'value2'}"""
+143
View File
@@ -0,0 +1,143 @@
import pytest
from core.sheerka.services.SheerkaRuleManager import FormatAstRawText, FormatAstVariable, FormatAstVariableNotFound, \
FormatAstSequence, FormatAstList, FormatAstDict
from out.ConsoleVisistor import ConsoleVisitor
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
class TestConsoleVisitor(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("format_text, expected", [
(FormatAstRawText("hello word"), "hello word"),
(FormatAstVariable('xxx', value="hello word"), "hello word"),
(FormatAstVariable('xxx', value=1), "1"),
(FormatAstVariable('xxx', value=1.5), "1.5"),
(FormatAstVariable('xxx', value="hello word", debug=True), "'hello word'"),
(FormatAstVariable('xxx', value=1, debug=True), "1"),
(FormatAstVariable('xxx', value=1.5, debug=True), "1.5"),
(FormatAstVariable('xxx', value=False, debug=True), "False"),
(FormatAstVariableNotFound('va_name'), "\x1b[31mva_name\x1b[0m"),
])
def test_i_can_print_simple_ast(self, format_text, expected, capsys):
visitor = ConsoleVisitor()
visitor.visit(format_text)
captured = capsys.readouterr()
assert captured.out == expected + "\n"
def test_i_can_print_sequence(self, capsys):
visitor = ConsoleVisitor()
sequence = FormatAstSequence([
FormatAstRawText("hello word"),
FormatAstVariable('xxx', value=1),
FormatAstVariableNotFound('va_name'),
FormatAstVariable('xxx', value="hello word", debug=True)
])
visitor.visit(sequence)
captured = capsys.readouterr()
assert captured.out == "hello word1\x1b[31mva_name\x1b[0m'hello word'\n"
def test_i_can_print_list(self, capsys):
visitor = ConsoleVisitor()
lst = FormatAstList("xxx", items=[
FormatAstVariable('__item', index=0, value="first element"),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])
visitor.visit(lst)
captured = capsys.readouterr()
assert captured.out == "first element\n'second element'\n"
def test_i_can_print_list_with_sub_items(self, capsys):
visitor = ConsoleVisitor()
lst = FormatAstList("xxx", items=[
FormatAstVariable('__item', index=0, value="first element"),
FormatAstList("__sub", items=[
FormatAstVariable('__item', index=0, value="sub item 1"),
FormatAstVariable('__item', index=1, value="sub item 2"),
]),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])
visitor.visit(lst)
captured = capsys.readouterr()
assert captured.out == "first element\nsub item 1\nsub item 2\n'second element'\n"
def test_i_can_print_list_in_debug_mode(self, capsys):
visitor = ConsoleVisitor()
lst = FormatAstList("xxx", debug=True, prefix="[", suffix="]", items=[
FormatAstVariable('__item', index=0, value="first element", debug=True),
FormatAstVariable('__item', index=1, value="second element", debug=True),
])
visitor.visit(lst)
captured = capsys.readouterr()
assert captured.out == "['first element', 'second element']\n"
def test_i_can_print_dict(self, capsys):
visitor = ConsoleVisitor()
bag = FormatAstDict("xxx", items=[
(FormatAstVariable('__key', index=0, value="key1"),
FormatAstVariable('__value', index="key1", value=1)),
(FormatAstVariable('__key', index=1, value="long_key2"),
FormatAstVariable('__value', index="key2", value="value2")),
])
visitor.visit(bag)
captured = capsys.readouterr()
assert captured.out == "key1 : 1\nlong_key2: value2\n"
def test_i_can_print_dict_in_debug_mode(self, capsys):
visitor = ConsoleVisitor()
bag = FormatAstDict("xxx", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="key1", debug=True),
FormatAstVariable('__value', index="key1", value=1, debug=True)),
(FormatAstVariable('__key', index=1, value="long_key2", debug=True),
FormatAstVariable('__value', index="key2", value="value2", debug=True)),
])
visitor.visit(bag)
captured = capsys.readouterr()
assert captured.out == "{'key1': 1, 'long_key2': 'value2'}\n"
def test_i_can_print_sub_level_of_dict_in_expand_mode(self, capsys):
visitor = ConsoleVisitor(expand_mode='always')
bag = FormatAstDict("xxx", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="key1", debug=True),
FormatAstVariable('__value', index="key1", value=1, debug=True)),
(FormatAstVariable('__key', index=0, value="key2", debug=True),
FormatAstDict("__value", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="sub_key1", debug=True),
FormatAstVariable('__value', index="sub_key1", value=1, debug=True)),
(FormatAstVariable('__key', index=1, value="sub_long_key2", debug=True),
FormatAstDict("__value", debug=True, prefix="{", suffix="}", items=[
(FormatAstVariable('__key', index=0, value="sub_sub_key1", debug=True),
FormatAstVariable('__value', index="sub_sub_key1", value=1, debug=True)),
(FormatAstVariable('__key', index=1, value="sub_sub_key2", debug=True),
FormatAstVariable('__value', index="sub_sub_key2", value="sub_sub_value", debug=True)),
])),
])),
(FormatAstVariable('__key', index=1, value="long_key3", debug=True),
FormatAstVariable('__value', index="key2", value="value2", debug=True)),
])
visitor.visit(bag)
captured = capsys.readouterr()
assert captured.out == """{'key1' : 1,
'key2' : {'sub_key1' : 1,
'sub_long_key2': {'sub_sub_key1': 1,
'sub_sub_key2': 'sub_sub_value'}},
'long_key3': 'value2'}
"""
+127 -57
View File
@@ -1,10 +1,12 @@
from dataclasses import dataclass
import pytest import pytest
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
from core.concept import Concept from core.concept import Concept
from core.rule import Rule from core.rule import Rule
from core.sheerka.services.SheerkaOut import SheerkaOut from core.sheerka.services.SheerkaOut import SheerkaOut
from core.sheerka.services.SheerkaRuleManager import FormatAstRawText, FormatAstVariable, FormatAstSequence, \ from core.sheerka.services.SheerkaRuleManager import FormatAstRawText, FormatAstVariable, FormatAstSequence, \
FormatAstColor, FormatAstVariableNotFound, FormatAstList FormatAstColor, FormatAstVariableNotFound, FormatAstList, FormatAstDict
from core.utils import flatten_all_children from core.utils import flatten_all_children
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -14,6 +16,12 @@ raw = FormatAstRawText
var = FormatAstVariable var = FormatAstVariable
@dataclass
class DummyObj:
prop_1: float
prop_2: str
class TestSheerkaOut(TestUsingMemoryBasedSheerka): class TestSheerkaOut(TestUsingMemoryBasedSheerka):
def init_service_with_rules(self, *rules, **kwargs): def init_service_with_rules(self, *rules, **kwargs):
@@ -24,20 +32,20 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("rule, expected", [ @pytest.mark.parametrize("rule, expected", [
(("__ret", "hello world"), FormatAstRawText("hello world")), (("__ret", "hello world"), FormatAstRawText("hello world")),
(("__ret", "{__ret_value}"), FormatAstVariable("__ret_value", None, 1)), (("__ret", "{__ret_value}"), FormatAstVariable("__ret_value", value=1)),
(("__ret", "{status}"), FormatAstVariable("status", None, True)), (("__ret", "{status}"), FormatAstVariable("status", value=True)),
(("__ret", "{foo}"), FormatAstVariableNotFound("foo")), (("__ret", "{foo}"), FormatAstVariableNotFound("foo")),
(("__ret", "hello world {__ret_value} !"), seq([FormatAstRawText("hello world "), (("__ret", "hello world {__ret_value} !"), seq([FormatAstRawText("hello world "),
FormatAstVariable("__ret_value", None, 1), FormatAstVariable("__ret_value", value=1),
FormatAstRawText(" !")])), FormatAstRawText(" !")])),
(("__ret", "red(__ret_value)"), FormatAstColor("red", FormatAstVariable("__ret_value", None, 1))), (("__ret", "red(__ret_value)"), FormatAstColor("red", FormatAstVariable("__ret_value", value=1))),
(("__ret", "blue('hello world {__ret_value} !')"), FormatAstColor("blue", (("__ret", "blue('hello world {__ret_value} !')"), FormatAstColor("blue",
seq([FormatAstRawText("hello world "), seq([FormatAstRawText("hello world "),
FormatAstVariable("__ret_value", None, FormatAstVariable("__ret_value",
1), value=1),
FormatAstRawText(" !")]))), FormatAstRawText(" !")]))),
(("__ret", "list(foo)"), FormatAstVariableNotFound("foo")), (("__ret", "list(foo)"), FormatAstVariableNotFound("foo")),
(("__ret", "{__ret_value:3}"), FormatAstVariable("__ret_value", "3", " 1")) (("__ret", "{__ret_value:3}"), FormatAstVariable("__ret_value", "3", value=" 1"))
]) ])
def test_i_can_develop_when_simple_rules(self, rule, expected): def test_i_can_develop_when_simple_rules(self, rule, expected):
sheerka, context, service, rule = self.init_service_with_rules(rule) sheerka, context, service, rule = self.init_service_with_rules(rule)
@@ -59,14 +67,14 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, ret) res = service.create_out_tree(context, ret)
assert res == seq([FormatAstRawText("status: "), assert res == seq([FormatAstRawText("status: "),
FormatAstVariable("status", None, True), FormatAstVariable("status", value=True),
FormatAstRawText(", value: "), FormatAstRawText(", value: "),
FormatAstVariable("__ret_value", None, seq([FormatAstVariable("id", None, "1001"), FormatAstVariable("__ret_value", value=seq([FormatAstVariable("id", value="1001"),
FormatAstRawText("-"), FormatAstRawText("-"),
FormatAstVariable("name", None, "foo"), FormatAstVariable("name", value="foo"),
FormatAstRawText(":"), FormatAstRawText(":"),
FormatAstVariable("body", None, FormatAstVariable("body",
"hello world")]))]) value="hello world")]))])
@pytest.mark.parametrize('second_rule', [ @pytest.mark.parametrize('second_rule', [
("__ret.body", "{id}-{name}:{body}"), ("__ret.body", "{id}-{name}:{body}"),
@@ -84,23 +92,32 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, ret) res = service.create_out_tree(context, ret)
assert res == seq([FormatAstRawText("status: "), assert res == seq([FormatAstRawText("status: "),
FormatAstVariable("status", None, True), FormatAstVariable("status", value=True),
FormatAstRawText(", value: "), FormatAstRawText(", value: "),
FormatAstVariable("__ret.body", None, seq([FormatAstVariable("id", None, "1001"), FormatAstVariable("__ret.body", value=seq([FormatAstVariable("id", value="1001"),
FormatAstRawText("-"), FormatAstRawText("-"),
FormatAstVariable("name", None, "foo"), FormatAstVariable("name", value="foo"),
FormatAstRawText(":"), FormatAstRawText(":"),
FormatAstVariable("body", None, FormatAstVariable("body",
"hello world")]))]) value="hello world")]))])
def test_i_can_develop_using_variable_properties(self):
sheerka, context, service, *rules = self.init_service_with_rules(
("isinstance(__obj, Concept)", "{__obj.body}"),
)
obj = Concept("bar", body="value for bar").auto_init()
res = service.create_out_tree(context, obj)
assert res == FormatAstVariable("__obj.body", value="value for bar")
def test_i_can_develop_list(self): def test_i_can_develop_list(self):
sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, list)", "list(__obj)")) sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, list)", "list(__obj)"))
lst = list(flatten_all_children(context, lambda item: item.achildren))[:3] lst = list(flatten_all_children(context, lambda item: item.achildren))[:3]
res = service.create_out_tree(context, lst) res = service.create_out_tree(context, lst)
assert res == FormatAstList(variable="__obj", items=[FormatAstVariable("__item", None, lst[0], 0), assert res == FormatAstList(variable="__obj", items=[FormatAstVariable("__item", value=lst[0], index=0),
FormatAstVariable("__item", None, lst[1], 1), FormatAstVariable("__item", value=lst[1], index=1),
FormatAstVariable("__item", None, lst[2], 2)]) FormatAstVariable("__item", value=lst[2], index=2)])
def test_i_can_develop_list_using_the_default_items_prop(self): def test_i_can_develop_list_using_the_default_items_prop(self):
sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, 'foo')", "list(__obj)")) sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, 'foo')", "list(__obj)"))
@@ -108,9 +125,9 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
foo = Concept("foo", body=lst, key="foo").auto_init() foo = Concept("foo", body=lst, key="foo").auto_init()
res = service.create_out_tree(context, foo) res = service.create_out_tree(context, foo)
assert res == FormatAstList(variable="__obj", items=[FormatAstVariable("__item", None, lst[0], 0), assert res == FormatAstList(variable="__obj", items=[FormatAstVariable("__item", value=lst[0], index=0),
FormatAstVariable("__item", None, lst[1], 1), FormatAstVariable("__item", value=lst[1], index=1),
FormatAstVariable("__item", None, lst[2], 2)]) FormatAstVariable("__item", value=lst[2], index=2)])
def test_i_can_develop_list_using_the_custom_items_prop(self): def test_i_can_develop_list_using_the_custom_items_prop(self):
sheerka, context, service, *rules = self.init_service_with_rules( sheerka, context, service, *rules = self.init_service_with_rules(
@@ -121,16 +138,16 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, foo) res = service.create_out_tree(context, foo)
assert res == FormatAstList(variable="__obj", items_prop='custom', assert res == FormatAstList(variable="__obj", items_prop='custom',
items=[FormatAstVariable("__item", None, lst[0], 0), items=[FormatAstVariable("__item", value=lst[0], index=0),
FormatAstVariable("__item", None, lst[1], 1), FormatAstVariable("__item", value=lst[1], index=1),
FormatAstVariable("__item", None, lst[2], 2)]) FormatAstVariable("__item", value=lst[2], index=2)])
def test_not_a_list_are_resolved_into_variable_ast(self): def test_not_a_list_are_resolved_into_variable_ast(self):
sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, 'foo')", "list(__obj)")) sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, 'foo')", "list(__obj)"))
foo = Concept("foo", key="foo") foo = Concept("foo", key="foo")
res = service.create_out_tree(context, foo) res = service.create_out_tree(context, foo)
assert res == FormatAstVariable("__obj", None, foo) assert res == FormatAstVariable("__obj", value=foo)
def test_i_can_develop_list_of_return_values(self): def test_i_can_develop_list_of_return_values(self):
sheerka, context, service, *rules = self.init_service_with_rules(("__rets", "list(__rets)")) sheerka, context, service, *rules = self.init_service_with_rules(("__rets", "list(__rets)"))
@@ -140,9 +157,9 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
] ]
res = service.create_out_tree(context, lst) res = service.create_out_tree(context, lst)
assert res == FormatAstList(variable="__rets", items=[FormatAstVariable("__item", None, lst[0], 0), assert res == FormatAstList(variable="__rets", items=[FormatAstVariable("__item", value=lst[0], index=0),
FormatAstVariable("__item", None, lst[1], 1), FormatAstVariable("__item", value=lst[1], index=1),
FormatAstVariable("__item", None, lst[2], 2)]) FormatAstVariable("__item", value=lst[2], index=2)])
def test_rules_are_correctly_reset_when_list(self): def test_rules_are_correctly_reset_when_list(self):
sheerka, context, service, *rules = self.init_service_with_rules( sheerka, context, service, *rules = self.init_service_with_rules(
@@ -156,8 +173,8 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, lst) res = service.create_out_tree(context, lst)
assert res == FormatAstList(variable="__rets", items=[ assert res == FormatAstList(variable="__rets", items=[
FormatAstVariable("__item", None, FormatAstColor("red", FormatAstVariable("__ret", None, lst[0])), 0), FormatAstVariable("__item", value=FormatAstColor("red", FormatAstVariable("__ret", value=lst[0])), index=0),
FormatAstVariable("__item", None, FormatAstColor("red", FormatAstVariable("__ret", None, lst[1])), 1), FormatAstVariable("__item", value=FormatAstColor("red", FormatAstVariable("__ret", value=lst[1])), index=1),
]) ])
def test_i_can_develop_list_with_recurse(self): def test_i_can_develop_list_with_recurse(self):
@@ -174,15 +191,15 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, lst) res = service.create_out_tree(context, lst)
assert res == FormatAstList(variable="__rets", recurse_on='parents', recursion_depth=2, items=[ assert res == FormatAstList(variable="__rets", recurse_on='parents', recursion_depth=2, items=[
FormatAstVariable("__item", None, r1, 0), FormatAstVariable("__item", value=r1, index=0),
FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=1, items=[ FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=1, items=[
FormatAstVariable("__item", None, r11, 0), FormatAstVariable("__item", value=r11, index=0),
FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=0, items=[ FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=0, items=[
FormatAstVariable("__item", None, r111, 0)]) FormatAstVariable("__item", value=r111, index=0)])
]), ]),
FormatAstVariable("__item", None, r2, 1), FormatAstVariable("__item", value=r2, index=1),
FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=1, items=[ FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=1, items=[
FormatAstVariable("__item", None, r22, 0)]), FormatAstVariable("__item", value=r22, index=0)]),
]) ])
def test_i_can_develop_list_with_recurse_using_container_format_instr(self): def test_i_can_develop_list_with_recurse_using_container_format_instr(self):
@@ -200,29 +217,53 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, foo) res = service.create_out_tree(context, foo)
assert res == FormatAstList(variable="__obj", recurse_on='parents', recursion_depth=2, items=[ assert res == FormatAstList(variable="__obj", recurse_on='parents', recursion_depth=2, items=[
FormatAstVariable("__item", None, r1, 0), FormatAstVariable("__item", value=r1, index=0),
FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=1, items=[ FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=1, items=[
FormatAstVariable("__item", None, r11, 0), FormatAstVariable("__item", value=r11, index=0),
FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=0, items=[ FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=0, items=[
FormatAstVariable("__item", None, r111, 0)]) FormatAstVariable("__item", value=r111, index=0)])
]), ]),
FormatAstVariable("__item", None, r2, 1), FormatAstVariable("__item", value=r2, index=1),
FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=1, items=[ FormatAstList(variable="__parents", recurse_on='parents', recursion_depth=1, items=[
FormatAstVariable("__item", None, r22, 0)]), FormatAstVariable("__item", value=r22, index=0)]),
]) ])
def test_i_can_develop_using_variable_properties(self): def test_i_can_develop_dict(self):
sheerka, context, service, *rules = self.init_service_with_rules( sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, dict)", "dict(__obj)"))
("isinstance(__obj, Concept)", "{__obj.body}"), obj = {
) "key1": "value1",
lst = Concept("bar", body="value for bar").auto_init() "key2": "value2",
}
res = service.create_out_tree(context, lst) res = service.create_out_tree(context, obj)
assert res == FormatAstVariable("__obj.body", None, "value for bar") assert res == FormatAstDict(variable="__obj", items=[
(FormatAstVariable("__key", value="key1", index=0),
FormatAstVariable("__value", value="value1", index="key1")),
(FormatAstVariable("__key", value="key2", index=1),
FormatAstVariable("__value", value="value2", index="key2")),
])
def test_i_can_develop_dict_with_list(self):
sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, dict)", "dict(__obj)"))
obj = {
"key1": "value1",
"key2": ["item1", "item2"],
}
res = service.create_out_tree(context, obj)
assert res == FormatAstDict(variable="__obj", items=[
(FormatAstVariable("__key", value="key1", index=0),
FormatAstVariable("__value", value="value1", index="key1")),
(FormatAstVariable("__key", value="key2", index=1),
FormatAstList(variable='__value', debug=True, prefix='[', suffix=']', items=[
FormatAstVariable("__item", debug=True, value="item1", index=0),
FormatAstVariable("__item", debug=True, value="item2", index=1),
])),
])
@pytest.mark.parametrize("rule, expected", [ @pytest.mark.parametrize("rule, expected", [
(("__ret", "red('hello world')"), FormatAstColor("red", FormatAstRawText("hello world"))), (("__ret", "red('hello world')"), FormatAstColor("red", FormatAstRawText("hello world"))),
(("__ret", "blue(__ret_value)"), FormatAstColor("blue", FormatAstVariable("__ret_value", None, 1))), (("__ret", "blue(__ret_value)"), FormatAstColor("blue", FormatAstVariable("__ret_value", value=1))),
]) ])
def test_i_can_develop_color(self, rule, expected): def test_i_can_develop_color(self, rule, expected):
sheerka, context, service, rule = self.init_service_with_rules(rule) sheerka, context, service, rule = self.init_service_with_rules(rule)
@@ -233,12 +274,10 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
assert res == expected assert res == expected
@pytest.mark.parametrize("rule, expected", [ @pytest.mark.parametrize("rule, expected", [
(("__ret", "{__ret}"), FormatAstVariable("__ret", None, (("__ret", "{__ret}"), FormatAstVariable("__ret", value=ReturnValueConcept(who="Test", status=True, value=1))),
ReturnValueConcept(who="Test", status=True, value=1))),
(("__ret", "magenta(__ret)"), (("__ret", "magenta(__ret)"),
FormatAstColor("magenta", FormatAstColor("magenta",
FormatAstVariable("__ret", None, FormatAstVariable("__ret", value=ReturnValueConcept(who="Test", status=True, value=1)))),
ReturnValueConcept(who="Test", status=True, value=1)))),
]) ])
def test_i_can_manage_infinite_recursion(self, rule, expected): def test_i_can_manage_infinite_recursion(self, rule, expected):
sheerka, context, service, rule = self.init_service_with_rules(rule) sheerka, context, service, rule = self.init_service_with_rules(rule)
@@ -258,8 +297,8 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, ret) res = service.create_out_tree(context, ret)
assert res == FormatAstColor("white", assert res == FormatAstColor("white",
FormatAstVariable("__ret", None, FormatAstVariable("__ret",
FormatAstVariable("__ret.value", None, "hello world!"))) value=FormatAstVariable("__ret.value", value="hello world!")))
def test_i_can_print_out_the_result(self, capsys): def test_i_can_print_out_the_result(self, capsys):
sheerka, context, service, *rules = self.init_service_with_rules( sheerka, context, service, *rules = self.init_service_with_rules(
@@ -379,3 +418,34 @@ ReturnValue(who=Test, status=True, value=r2, message=None)
service.process_return_values(context, ret) service.process_return_values(context, ret)
captured = capsys.readouterr() captured = capsys.readouterr()
assert captured.out == "status: \x1b[32mTrue\x1b[0m, \x1b[34mvalue: (1001)foo\x1b[0m\n" assert captured.out == "status: \x1b[32mTrue\x1b[0m, \x1b[34mvalue: (1001)foo\x1b[0m\n"
def test_i_can_print_out_dict(self, capsys):
sheerka, context, service, *rules = self.init_service_with_rules(
("isinstance(__obj, dict)", "dict(__obj)"),
("__key=='key1'", "green(__key)")
)
obj = {
"key1": "value1",
"key2": "value2",
}
service.process_return_values(context, obj)
captured = capsys.readouterr()
assert captured.out == """\x1b[32mkey1\x1b[0m: value1
key2: value2
"""
def test_i_can_print_out_dict_in_debug_mode(self, capsys):
sheerka, context, service, *rules = self.init_service_with_rules(
("isinstance(__obj, dict)", "dict(__obj, debug=True)"),
("__key=='key1'", "green(__key)")
)
obj = {
"key1": "value1",
"key2": 1,
"key3": DummyObj(3.15, "a string"),
"key4": ["alpha", 0]
}
service.process_return_values(context, obj)
captured = capsys.readouterr()
assert captured.out == """{'\x1b[32mkey1\x1b[0m': 'value1', 'key2': 1, 'key3': DummyObj(prop_1=3.15, prop_2='a string'), 'key4': ['alpha', 0]}
"""