diff --git a/src/core/builtin_concepts.py b/src/core/builtin_concepts.py
index 8d2e4e5..6afdf2e 100644
--- a/src/core/builtin_concepts.py
+++ b/src/core/builtin_concepts.py
@@ -105,6 +105,7 @@ class BuiltinConcepts:
# formatting
TO_LIST = "__TO_LIST"
+ TO_DICT = "__TO_DICT"
AllBuiltinConcepts = [v for n, v in BuiltinConcepts.__dict__.items() if not n.startswith("__")]
@@ -176,6 +177,7 @@ BuiltinContainers = [
BuiltinConcepts.FILTERED,
BuiltinConcepts.EXPLANATION,
BuiltinConcepts.TO_LIST,
+ BuiltinConcepts.TO_DICT,
]
"""
diff --git a/src/core/sheerka/Sheerka.py b/src/core/sheerka/Sheerka.py
index 6458ff2..68a805b 100644
--- a/src/core/sheerka/Sheerka.py
+++ b/src/core/sheerka/Sheerka.py
@@ -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.sheerka_methods = {
"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 = {}
@@ -1028,6 +1029,20 @@ class Sheerka(Concept):
# uncomment the following line to enable colors
# 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():
sheerka = Sheerka()
diff --git a/src/core/sheerka/services/SheerkaAdmin.py b/src/core/sheerka/services/SheerkaAdmin.py
index e762704..3087b77 100644
--- a/src/core/sheerka/services/SheerkaAdmin.py
+++ b/src/core/sheerka/services/SheerkaAdmin.py
@@ -3,6 +3,7 @@ import time
from os import path
from core.builtin_concepts import BuiltinConcepts, BuiltinContainers
+from core.builtin_helpers import ensure_concept
from core.concept import Concept
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.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)
@@ -122,6 +124,28 @@ class SheerkaAdmin(BaseService):
def concepts(self):
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):
return self.sheerka.new(BuiltinConcepts.TO_LIST, items=self.sheerka.get_format_rules())
diff --git a/src/core/sheerka/services/SheerkaDebugManager.py b/src/core/sheerka/services/SheerkaDebugManager.py
index b6e905f..dab2d90 100644
--- a/src/core/sheerka/services/SheerkaDebugManager.py
+++ b/src/core/sheerka/services/SheerkaDebugManager.py
@@ -34,13 +34,16 @@ class BaseDebugLogger:
def debug_entering(self, **kwargs):
pass
- def debug_var(self, name, value, is_error=False):
+ def debug_var(self, name, value, is_error=False, hint=None):
pass
def debug_rule(self, rule, results):
pass
- def debug_log(self, text):
+ def debug_log(self, text, is_error=False):
+ pass
+
+ def is_enabled(self):
pass
@@ -48,6 +51,9 @@ class NullDebugLogger(BaseDebugLogger):
def __init__(self):
pass
+ def is_enabled(self):
+ return False
+
class ConsoleDebugLogger(BaseDebugLogger):
@@ -60,6 +66,9 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_id = debug_id
self.is_highlighted = ""
+ def is_enabled(self):
+ return True
+
def debug_entering(self, **kwargs):
super().debug_entering(**kwargs)
@@ -71,17 +80,18 @@ class ConsoleDebugLogger(BaseDebugLogger):
self.debug_manager.debug(self.prefix() + str_text)
self.debug_manager.debug(self.prefix() + str_vars)
- def debug_var(self, name, value, is_error=False):
- enabled = self.debug_manager.compute_var_debug(self.service_name,
- self.method_name,
- self.context_id,
- name,
- self.context_id)
+ def debug_var(self, name, value, is_error=False, hint=None):
+ enabled = is_error or self.debug_manager.compute_var_debug(self.service_name,
+ self.method_name,
+ self.context_id,
+ name,
+ self.context_id)
if enabled == False:
return
color = 'red' if is_error else 'green'
- str_text = f"{CCM[color]}..{name}={CCM['reset']}"
+ 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)
if "\n" not in str(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_vars)
- def debug_log(self, text):
- self.debug_manager.debug(self.prefix() + f"{CCM['blue']}..{text}{CCM['reset']}")
+ def debug_log(self, text, is_error=False):
+ color = 'red' if is_error else 'blue'
+ self.debug_manager.debug(self.prefix() + f"{CCM[color]}..{text}{CCM['reset']}")
def prefix(self):
return f"[{self.context_id:2}][{self.debug_id:2}] {self.is_highlighted}"
@@ -271,12 +282,8 @@ class SheerkaDebugManager(BaseService):
res = {}
for prop in props:
res[prop] = evaluate_expression(prop, bag)
- # res = {
- # "return_values": to_inspect.values.get("return_values", None)
- # }
- pp.pprint(res)
- return None
+ return self.sheerka.new(BuiltinConcepts.TO_DICT, body=res)
def debug(self, *args, **kwargs):
print(*args, **kwargs)
@@ -437,4 +444,4 @@ class SheerkaDebugManager(BaseService):
return self.sheerka.ret(SheerkaDebugManager.NAME, True, self.sheerka.new(BuiltinConcepts.SUCCESS))
def get_debug_settings(self):
- return self.debug_vars_settings
+ return self.sheerka.new(BuiltinConcepts.TO_LIST, body=self.debug_vars_settings)
diff --git a/src/core/sheerka/services/SheerkaDump.py b/src/core/sheerka/services/SheerkaDump.py
index 946ad5e..129cbef 100644
--- a/src/core/sheerka/services/SheerkaDump.py
+++ b/src/core/sheerka/services/SheerkaDump.py
@@ -21,7 +21,7 @@ class SheerkaDump(BaseService):
super().__init__(sheerka)
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")
def dump_desc(self, *concept_names, eval=False):
diff --git a/src/core/sheerka/services/SheerkaOut.py b/src/core/sheerka/services/SheerkaOut.py
index 0e7f0c7..fea958c 100644
--- a/src/core/sheerka/services/SheerkaOut.py
+++ b/src/core/sheerka/services/SheerkaOut.py
@@ -16,7 +16,8 @@ class SheerkaOut(BaseService):
self.sheerka.bind_service_method(self.process_return_values, False)
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):
debugger = context.get_debugger(SheerkaOut.NAME, "create_out_tree")
@@ -40,8 +41,7 @@ class SheerkaOut(BaseService):
visitor.already_seen.add(rule.id)
bag.update(as_bag(current_obj)) # update with the current obj attributes
- visitor.visit(context, rule.compiled_action, bag)
- res = visitor.get_result()
+ res = visitor.visit(context, rule.compiled_action, bag)
if res is None:
debugger.debug_log(f"No matching rule.")
@@ -67,9 +67,7 @@ class SheerkaOut(BaseService):
if out_tree:
for visitor in self.out_visitors:
- visitor.visit(context, out_tree, None)
- if hasattr(visitor, "finalize"):
- visitor.finalize()
+ visitor.visit(out_tree)
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))
diff --git a/src/core/sheerka/services/SheerkaRuleManager.py b/src/core/sheerka/services/SheerkaRuleManager.py
index 6beed59..211af5f 100644
--- a/src/core/sheerka/services/SheerkaRuleManager.py
+++ b/src/core/sheerka/services/SheerkaRuleManager.py
@@ -63,6 +63,15 @@ class FormatAstNode:
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
class FormatAstRawText(FormatAstNode):
@@ -73,9 +82,15 @@ class FormatAstRawText(FormatAstNode):
class FormatAstVariable(FormatAstNode):
name: str
format: Union[str, None] = None
+ debug: bool = False
value: object = None
index: object = None
+ def clone(self, **kwargs):
+ return super().clone(FormatAstVariable(self.name),
+ ("format", "debug", "value", "index"),
+ **kwargs)
+
@dataclass
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
recurse_on: str = None
recursion_depth: int = 0
+ debug: bool = False
+ prefix: str = None
+ suffix: str = None
+ show_index: bool = False
+
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
class FormatAstColor(FormatAstNode):
- def __init__(self, color, format_ast):
- self.color = color
- self.format_ast = format_ast
+ color: str
+ format_ast: FormatAstNode
def __repr__(self):
return f"{self.color}({self.format_ast})"
- def __eq__(self, other):
- if id(self) == id(other):
- return True
-
- if not isinstance(other, FormatAstColor):
- return False
-
- return self.color == other.color and self.format_ast == other.format_ast
-
- def __hash__(self):
- return hash((self.color, self.format_ast))
+ def clone(self, **kwargs):
+ return super().clone(
+ FormatAstColor(self.color, self.format_ast),
+ (),
+ **kwargs)
@dataclass
@@ -125,21 +161,19 @@ class FormatAstFunction(FormatAstNode):
kwargs: dict = None
+@dataclass
class FormatAstSequence(FormatAstNode):
- def __init__(self, items):
- self.items = items
+ items: list
+ debug: bool = False
def __repr__(self):
return "FormatAstSequence(" + self.repr_value(self.items) + ")"
- def __eq__(self, other):
- if id(self) == id(other):
- return True
-
- if not isinstance(other, FormatAstSequence):
- return False
-
- return self.items == other.items
+ def clone(self, **kwargs):
+ return super().clone(
+ FormatAstSequence(self.items),
+ ("debug",),
+ **kwargs)
class FormatRuleParser(IterParser):
@@ -170,6 +204,9 @@ class FormatRuleParser(IterParser):
if value[0] in ("'", '"'):
return value[1:-1]
+ if value in ("True", "False"):
+ return bool(value)
+
try:
return int(value)
except ValueError:
@@ -278,6 +315,8 @@ class FormatRuleParser(IterParser):
return self.return_color(func_name.value, args, kwargs)
elif func_name.value == "list":
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))
@@ -347,13 +386,19 @@ class FormatRuleParser(IterParser):
return FormatAstColor(color, FormatAstVariable(variable, vformat))
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)
if len_args < 1:
self.error_sink = FormatRuleSyntaxError("variable name not found", None)
return None
- if len_args > 3:
- self.error_sink = FormatRuleSyntaxError("too many positional arguments", args[3][0])
+ if len_args > 4:
+ self.error_sink = FormatRuleSyntaxError("too many positional arguments", args[4][0])
return None
variable_name = get_text_from_tokens(args[0])
@@ -364,6 +409,10 @@ class FormatRuleParser(IterParser):
elif len_args == 3:
recursion_depth = self.to_value(args[1])
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:
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)
+ 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()
class RulePredicate:
@@ -556,6 +633,9 @@ class SheerkaRuleManager(BaseService):
Rule("print", "Display formatted list",
"isinstance(__ret_container, BuiltinConcepts.TO_LIST)",
"list(__ret_container)"),
+ Rule("print", "Display formatted dict",
+ "isinstance(__ret_container, BuiltinConcepts.TO_DICT)",
+ "dict(__ret_container)"),
]
for r in rules:
diff --git a/src/core/utils.py b/src/core/utils.py
index 60b5f0b..b70e0a1 100644
--- a/src/core/utils.py
+++ b/src/core/utils.py
@@ -2,11 +2,11 @@ import ast
import importlib
import inspect
import pkgutil
-import re
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()
@@ -38,6 +38,15 @@ PRIMITIVES_TYPES = (str, bool, type(None), int, float, list, dict, set, bytes, t
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):
"""
diff --git a/src/evaluators/DefConceptEvaluator.py b/src/evaluators/DefConceptEvaluator.py
index 233a222..16fca9d 100644
--- a/src/evaluators/DefConceptEvaluator.py
+++ b/src/evaluators/DefConceptEvaluator.py
@@ -129,12 +129,14 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
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
"""
+ debugger = context.get_debugger(DefConceptEvaluator.NAME, "get_variables")
#
# Case of NameNode
#
if isinstance(ret_value, NameNode):
names = [str(t.value) for t in ret_value.tokens if t.type in (
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))
#
@@ -143,6 +145,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
if isinstance(ret_value.value, ParserResultConcept) and isinstance(ret_value.value.value, ParsingExpression):
visitor = ConceptOrRuleNameVisitor()
visitor.visit(ret_value.value.value)
+ debugger.debug_var("names", visitor.names, hint="from BNF")
return set(visitor.names)
#
@@ -152,6 +155,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
if len(concept_name) > 1:
visitor = UnreferencedVariablesVisitor(context)
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))
else:
return set()
@@ -168,6 +172,7 @@ class DefConceptEvaluator(OneReturnValueEvaluator):
for identifier in [i for i in concept_name if str(i).isalnum()]:
if identifier in tokens:
variables.add(identifier)
+ debugger.debug_var("names", variables, hint="from concept")
return variables
return set()
diff --git a/src/evaluators/PythonEvaluator.py b/src/evaluators/PythonEvaluator.py
index 0671f51..857d296 100644
--- a/src/evaluators/PythonEvaluator.py
+++ b/src/evaluators/PythonEvaluator.py
@@ -92,6 +92,9 @@ class PythonEvaluator(OneReturnValueEvaluator):
debugger = context.get_debugger(PythonEvaluator.NAME, "eval")
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)
# 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:
if concepts_entries is None:
concepts_entries = self.get_concepts_entries_from_globals(my_globals)
- errors.append(PythonEvalError(ex,
- traceback.format_exc() if context.debug_enabled else None,
- self.get_concepts_values_from_globals(globals_, concepts_entries)))
+ eval_error = PythonEvalError(ex,
+ traceback.format_exc() if get_trace_back else None,
+ 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 len(errors) == 1:
diff --git a/src/jupyter/SheerkaKernel.py b/src/jupyter/SheerkaKernel.py
index 5218259..5eb5ef5 100644
--- a/src/jupyter/SheerkaKernel.py
+++ b/src/jupyter/SheerkaKernel.py
@@ -1,7 +1,8 @@
+from IPython.core.display import JSON
from core.sheerka.Sheerka import Sheerka
from ipykernel.ipkernel import IPythonKernel
from ipykernel.kernelapp import IPKernelApp
-from sheerkapickle import encode
+from sheerkapickle.SheerkaPickler import SheerkaPickler
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):
result = self.sheerka.evaluate_user_input(code)
+ # md = Markdown(f'{input_data}')
+ as_json = JSON(SheerkaPickler(self.sheerka).flatten(result))
+ a = {
+ "a": "value1",
+ "b": "value2",
+ }
if not silent:
display_data_content = {
'data': {
- 'text/plain': str(result),
- 'application/json': encode(self.sheerka, result)
+ # 'text/plain': str(result),
+ # "text/markdown": md._repr_markdown_(),
+ 'application/json': JSON(a)._repr_json_(),
},
'metadata': {
'application/json': {'expanded': True}
@@ -38,9 +46,8 @@ class SheerkaKernel(IPythonKernel):
'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, "execute_result", display_data_content)
+
+ self.send_response(self.iopub_socket, "display_data", display_data_content)
return {'status': 'ok',
# The base class increments the execution count
diff --git a/src/out/AsStrVisitor.py b/src/out/AsStrVisitor.py
new file mode 100644
index 0000000..4db5b19
--- /dev/null
+++ b/src/out/AsStrVisitor.py
@@ -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
diff --git a/src/out/ConsoleVisistor.py b/src/out/ConsoleVisistor.py
index 630d692..b6d2079 100644
--- a/src/out/ConsoleVisistor.py
+++ b/src/out/ConsoleVisistor.py
@@ -1,55 +1,43 @@
-from out.OutVisitor import OutVisitor
-from core.sheerka.services.SheerkaRuleManager import FormatAstNode
+from out.AsStrVisitor import AsStrVisitor
-class ConsoleVisitor(OutVisitor):
+class ConsoleVisitor(AsStrVisitor):
"""
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.expand_mode = expand_mode
- def finalize(self):
- self.out("")
+ def visit_FormatAstRawText(self, format_ast):
+ self.out(super().visit_FormatAstRawText(format_ast))
- def visit_FormatAstRawText(self, context, format_ast, bag):
- self.out(format_ast.text, end="")
+ def visit_FormatAstVariable(self, format_ast):
+ self.out(super().visit_FormatAstVariable(format_ast))
- def visit_FormatAstVariable(self, context, format_ast, bag):
- if isinstance(format_ast.value, FormatAstNode):
- self.visit(context, format_ast.value, bag)
- return
- self.out(format_ast.value, end="")
+ def visit_FormatAstVariableNotFound(self, format_ast):
+ self.out(super().visit_FormatAstVariableNotFound(format_ast))
- def visit_FormatAstVariableNotFound(self, context, format_ast, bag):
- self.out(self.COLORS["red"] + format_ast.name + self.COLORS["reset"], end="")
+ def visit_FormatAstColor(self, format_ast):
+ self.out(super().visit_FormatAstColor(format_ast))
- def visit_FormatAstSequence(self, context, format_ast, bag):
- for item in format_ast.items:
- self.visit(context, item, bag)
+ def visit_FormatAstSequence(self, format_ast):
+ visitor = AsStrVisitor()
+ self.out(visitor.visit_FormatAstSequence(format_ast))
- def visit_FormatAstList(self, context, format_ast, bag):
- first = True
- for item in format_ast.items:
- if not first:
- self.out("") # print new line
- self.visit(context, item, bag)
- first = False
+ def visit_FormatAstList(self, format_ast):
+ visitor = AsStrVisitor()
+ res = visitor.visit_FormatAstList(format_ast)
+ self.out(res)
- def visit_FormatAstColor(self, context, format_ast, bag):
- self.out(self.COLORS[format_ast.color], end="")
- self.visit(context, format_ast.format_ast, bag)
- self.out(self.COLORS["reset"], end="")
+ def visit_FormatAstDict(self, format_ast):
+ if self.expand_mode == "always":
+ expand = True
+ else:
+ expand = False
+ visitor = AsStrVisitor(expand)
+ res = visitor.visit_FormatAstDict(format_ast)
+ self.out(res)
diff --git a/src/out/DeveloperVisitor.py b/src/out/DeveloperVisitor.py
index 7c9d631..cfe99fe 100644
--- a/src/out/DeveloperVisitor.py
+++ b/src/out/DeveloperVisitor.py
@@ -1,31 +1,43 @@
-from core.sheerka.services.SheerkaRuleManager import FormatAstVariable, FormatAstVariableNotFound, FormatAstSequence, \
- FormatAstColor, FormatAstList, FormatAstRawText
+from core.sheerka.services.SheerkaRuleManager import FormatAstVariable, FormatAstVariableNotFound, FormatAstColor, \
+ FormatAstList, FormatAstRawText, FormatAstDict
from core.utils import evaluate_expression, as_bag
-from out.OutVisitor import OutVisitor
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
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.sheerka_out = sheerka_out
+ self.debugger = debugger
self.already_seen = already_seen
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):
- if context.debug_enabled:
- context.debug_entering("DeveloperVisitor", "visit_FormatAstRawText", format_ast=format_ast, bag=bag)
- return self.set_result(format_ast)
+ if self.debugger.is_enabled():
+ debug_bag = {"format_ast": format_ast, "bag": bag}
+ self.debugger.debug_log(f"Entering visit_FormatAstRawText with {debug_bag}")
+ return format_ast
def visit_FormatAstVariable(self, context, format_ast, bag):
- if context.debug_enabled:
- context.debug_entering("DeveloperVisitor", "visit_FormatAstVariable", format_ast=format_ast, bag=bag)
+ if self.debugger.is_enabled():
+ debug_bag = {"format_ast": format_ast, "bag": bag}
+ self.debugger.debug_log(f"Entering visit_FormatAstVariable with {debug_bag}")
try:
value = evaluate_expression(format_ast.name, bag)
@@ -33,6 +45,8 @@ class DeveloperVisitor(OutVisitor):
"__obj": 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:
index = format_ast.name.rindex(".")
sub_bag[format_ast.name[index + 1:]] = value
@@ -43,32 +57,31 @@ class DeveloperVisitor(OutVisitor):
if format_ast.format:
res = eval(fstring, {"value": res, "format": format_ast.format})
- return self.set_result(FormatAstVariable(format_ast.name,
- format_ast.format,
- res,
- format_ast.index))
-
+ return format_ast.clone(value=res)
except NameError as error:
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):
- if context.debug_enabled:
- context.debug_entering("DeveloperVisitor", "visit_FormatAstSequence", format_ast=format_ast, bag=bag)
- return self.set_result(FormatAstSequence([self.visit(context, item, bag) for item in format_ast.items]))
+ if self.debugger.is_enabled():
+ debug_bag = {"format_ast": format_ast, "bag": bag}
+ 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):
- if context.debug_enabled:
- context.debug_entering("DeveloperVisitor", "visit_FormatAstColor", format_ast=format_ast, bag=bag)
- return self.set_result(FormatAstColor(format_ast.color, self.visit(context, format_ast.format_ast, bag)))
+ if self.debugger.is_enabled():
+ debug_bag = {"format_ast": format_ast, "bag": 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):
- if context.debug_enabled:
- context.debug_entering("DeveloperVisitor", "visit_FormatAstList", format_ast=format_ast, bag=bag)
+ if self.debugger.is_enabled():
+ debug_bag = {"format_ast": format_ast, "bag": bag}
+ self.debugger.debug_log(f"Entering visit_FormatAstList with {debug_bag}")
try:
value = evaluate_expression(format_ast.variable, bag)
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__"):
items = value
@@ -78,22 +91,29 @@ class DeveloperVisitor(OutVisitor):
items = evaluate_expression(f"self.{items_props}", {"self": value})
if not hasattr(items, "__iter__"):
# 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
for i, item in enumerate(items):
bag["__item"] = item
- sub_visitor = DeveloperVisitor(self.sheerka_out, set(), self.list_recursion_depth)
- result.append(sub_visitor.visit(context, FormatAstVariable("__item", None, item, i), bag))
+ sub_visitor = DeveloperVisitor(self.sheerka_out, self.debugger, set(), self.list_recursion_depth)
+ result.append(sub_visitor.visit(context, FormatAstVariable("__item",
+ debug=format_ast.debug,
+ value=item,
+ index=i), bag))
# recursion management
recursion_depth, recurse_on = self.get_recurse_info(item, recursion_depth, recurse_on)
if recursion_depth > 0:
sub_items = evaluate_expression(recurse_on, as_bag(item))
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
sub_items = sub_visitor.visit(context, FormatAstList(f"__{recurse_on}",
@@ -102,29 +122,64 @@ class DeveloperVisitor(OutVisitor):
recursion_depth - 1), bag)
result.append(sub_items)
- return self.set_result(FormatAstList(variable=format_ast.variable,
- items_prop=format_ast.items_prop,
- recurse_on=recurse_on,
- recursion_depth=recursion_depth,
- items=result))
+ return format_ast.clone(recurse_on=recurse_on, recursion_depth=recursion_depth, items=result)
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 \
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):
- if context.debug_enabled:
- context.debug_entering("DeveloperVisitor", "visit_FormatAstFunction", format_ast=format_ast, bag=bag)
- unknown = FormatAstColor("red", FormatAstRawText(f"function '{format_ast.name}' is unknown"))
- return self.set_result(unknown)
+ if self.debugger.is_enabled():
+ debug_bag = {"format_ast": format_ast, "bag": bag}
+ self.debugger.debug_log(f"Entering visit_FormatAstFunction with {debug_bag}")
+ return FormatAstColor("red", FormatAstRawText(f"function '{format_ast.name}' is unknown"))
- def set_result(self, result):
- self._result = result
- return result
+ def visit_FormatAstDict(self, context, format_ast, bag):
+ if self.debugger.is_enabled():
+ debug_bag = {"format_ast": format_ast, "bag": bag}
+ self.debugger.debug_log(f"Entering visit_FormatAstDict with {debug_bag}")
- def get_result(self):
- return self._result
+ try:
+ 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
def get_recurse_info(obj, recursion_depth, recurse_on):
diff --git a/src/out/OutVisitor.py b/src/out/OutVisitor.py
index 1b5b23f..b00e3e5 100644
--- a/src/out/OutVisitor.py
+++ b/src/out/OutVisitor.py
@@ -1,10 +1,10 @@
class OutVisitor:
- def visit(self, context, format_ast, bag):
+ def visit(self, format_ast):
name = format_ast.__class__.__name__
method = 'visit_' + name
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
diff --git a/src/parsers/PythonParser.py b/src/parsers/PythonParser.py
index 4b14f41..f1dc9aa 100644
--- a/src/parsers/PythonParser.py
+++ b/src/parsers/PythonParser.py
@@ -37,10 +37,15 @@ class PythonNode(Node):
def __init__(self, source, ast_=None, objects=None):
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.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):
if self.compiled is None:
self.compiled = compile(self.ast_, "", "eval")
@@ -60,10 +65,12 @@ class PythonNode(Node):
if self.source != other.source:
return False
- self_dump = self.get_dump(self.ast_)
- other_dump = self.get_dump(other.ast_)
+ if self.ast_ and other.ast_:
+ self_dump = self.get_dump(self.ast_)
+ other_dump = self.get_dump(other.ast_)
+ return self_dump == other_dump
- return self_dump == other_dump
+ return True
def __hash__(self):
return hash((self.source, self.ast_.hash))
diff --git a/tests/core/test_SheerkaEvaluateConcept.py b/tests/core/test_SheerkaEvaluateConcept.py
index 16fe67a..10c4a1e 100644
--- a/tests/core/test_SheerkaEvaluateConcept.py
+++ b/tests/core/test_SheerkaEvaluateConcept.py
@@ -318,7 +318,7 @@ class TestSheerkaEvaluateConcept(TestUsingMemoryBasedSheerka):
def test_i_can_evaluate_when_compiled_is_set_up_with_return_value(self):
sheerka = self.get_sheerka()
- python_node = PythonNode("1 +1 ")
+ python_node = PythonNode("1 +1 ").init_ast()
parser_result = ParserResultConcept(parser="who", value=python_node)
concept = Concept("to_eval").def_var("prop")
diff --git a/tests/core/test_SheerkaRuleManager.py b/tests/core/test_SheerkaRuleManager.py
index 152df45..32372d6 100644
--- a/tests/core/test_SheerkaRuleManager.py
+++ b/tests/core/test_SheerkaRuleManager.py
@@ -7,7 +7,7 @@ from core.global_symbols import RULE_COMPARISON_CONTEXT
from core.rule import Rule
from core.sheerka.services.SheerkaRuleManager import SheerkaRuleManager, FormatRuleParser, \
FormatAstRawText, FormatAstVariable, FormatAstSequence, FormatAstFunction, \
- FormatRuleSyntaxError, FormatAstList, UnexpectedEof, FormatAstColor, RulePredicate
+ FormatRuleSyntaxError, FormatAstList, UnexpectedEof, FormatAstColor, RulePredicate, FormatAstDict
from core.tokenizer import Token, TokenKind
from parsers.BaseNodeParser import SourceCodeWithConceptNode, SourceCodeNode
from parsers.PythonParser import PythonNode
@@ -102,8 +102,8 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
('green("")', FormatAstColor("green", raw(""))),
("list(var_name, 2, 'children')", FormatAstList("var_name", recurse_on="children", recursion_depth=2)),
("list(var_name, recursion_depth=2, recurse_on='children')", FormatAstList("var_name",
- recurse_on="children",
- recursion_depth=2)),
+ recurse_on="children",
+ recursion_depth=2)),
("list(var_name, recursion_depth=2, 'children')", FormatAstList("var_name", recursion_depth=2)),
("list(var_name, 'children', recursion_depth=2)", FormatAstList("var_name", recursion_depth=2)),
("list(var_name)", FormatAstList("var_name")),
@@ -112,6 +112,9 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
("{variable:format}", FormatAstVariable("variable", "format")),
("{variable:3}", FormatAstVariable("variable", "3")),
(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):
assert FormatRuleParser(text).parse() == expected
@@ -129,10 +132,11 @@ class TestSheerkaRuleManager(TestUsingMemoryBasedSheerka):
("red(xy {v})", FormatRuleSyntaxError("Invalid identifier", None)),
("list()", 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",
- Token(TokenKind.IDENTIFIER, "d", 11, 1, 12))),
+ ("list(a,b,c,d,e)", FormatRuleSyntaxError("too many positional arguments",
+ Token(TokenKind.IDENTIFIER, "e", 13, 1, 14))),
("list(a, recursion_depth=hello)", FormatRuleSyntaxError("'hello' is not numeric", 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):
parser = FormatRuleParser(text)
diff --git a/tests/out/test_AsStrVisitor.py b/tests/out/test_AsStrVisitor.py
new file mode 100644
index 0000000..8070674
--- /dev/null
+++ b/tests/out/test_AsStrVisitor.py
@@ -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'}"""
diff --git a/tests/out/test_ConsoleVisitor.py b/tests/out/test_ConsoleVisitor.py
new file mode 100644
index 0000000..2da5cbb
--- /dev/null
+++ b/tests/out/test_ConsoleVisitor.py
@@ -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'}
+"""
diff --git a/tests/out/test_SheerkaOut.py b/tests/out/test_SheerkaOut.py
index b2a08c4..c5ff3c8 100644
--- a/tests/out/test_SheerkaOut.py
+++ b/tests/out/test_SheerkaOut.py
@@ -1,10 +1,12 @@
+from dataclasses import dataclass
+
import pytest
from core.builtin_concepts import ReturnValueConcept, BuiltinConcepts
from core.concept import Concept
from core.rule import Rule
from core.sheerka.services.SheerkaOut import SheerkaOut
from core.sheerka.services.SheerkaRuleManager import FormatAstRawText, FormatAstVariable, FormatAstSequence, \
- FormatAstColor, FormatAstVariableNotFound, FormatAstList
+ FormatAstColor, FormatAstVariableNotFound, FormatAstList, FormatAstDict
from core.utils import flatten_all_children
from tests.TestUsingMemoryBasedSheerka import TestUsingMemoryBasedSheerka
@@ -14,6 +16,12 @@ raw = FormatAstRawText
var = FormatAstVariable
+@dataclass
+class DummyObj:
+ prop_1: float
+ prop_2: str
+
+
class TestSheerkaOut(TestUsingMemoryBasedSheerka):
def init_service_with_rules(self, *rules, **kwargs):
@@ -24,20 +32,20 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
@pytest.mark.parametrize("rule, expected", [
(("__ret", "hello world"), FormatAstRawText("hello world")),
- (("__ret", "{__ret_value}"), FormatAstVariable("__ret_value", None, 1)),
- (("__ret", "{status}"), FormatAstVariable("status", None, True)),
+ (("__ret", "{__ret_value}"), FormatAstVariable("__ret_value", value=1)),
+ (("__ret", "{status}"), FormatAstVariable("status", value=True)),
(("__ret", "{foo}"), FormatAstVariableNotFound("foo")),
(("__ret", "hello world {__ret_value} !"), seq([FormatAstRawText("hello world "),
- FormatAstVariable("__ret_value", None, 1),
+ FormatAstVariable("__ret_value", value=1),
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",
seq([FormatAstRawText("hello world "),
- FormatAstVariable("__ret_value", None,
- 1),
+ FormatAstVariable("__ret_value",
+ value=1),
FormatAstRawText(" !")]))),
(("__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):
sheerka, context, service, rule = self.init_service_with_rules(rule)
@@ -59,14 +67,14 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, ret)
assert res == seq([FormatAstRawText("status: "),
- FormatAstVariable("status", None, True),
+ FormatAstVariable("status", value=True),
FormatAstRawText(", value: "),
- FormatAstVariable("__ret_value", None, seq([FormatAstVariable("id", None, "1001"),
+ FormatAstVariable("__ret_value", value=seq([FormatAstVariable("id", value="1001"),
FormatAstRawText("-"),
- FormatAstVariable("name", None, "foo"),
+ FormatAstVariable("name", value="foo"),
FormatAstRawText(":"),
- FormatAstVariable("body", None,
- "hello world")]))])
+ FormatAstVariable("body",
+ value="hello world")]))])
@pytest.mark.parametrize('second_rule', [
("__ret.body", "{id}-{name}:{body}"),
@@ -84,23 +92,32 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, ret)
assert res == seq([FormatAstRawText("status: "),
- FormatAstVariable("status", None, True),
+ FormatAstVariable("status", value=True),
FormatAstRawText(", value: "),
- FormatAstVariable("__ret.body", None, seq([FormatAstVariable("id", None, "1001"),
+ FormatAstVariable("__ret.body", value=seq([FormatAstVariable("id", value="1001"),
FormatAstRawText("-"),
- FormatAstVariable("name", None, "foo"),
+ FormatAstVariable("name", value="foo"),
FormatAstRawText(":"),
- FormatAstVariable("body", None,
- "hello world")]))])
+ FormatAstVariable("body",
+ 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):
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]
res = service.create_out_tree(context, lst)
- assert res == FormatAstList(variable="__obj", items=[FormatAstVariable("__item", None, lst[0], 0),
- FormatAstVariable("__item", None, lst[1], 1),
- FormatAstVariable("__item", None, lst[2], 2)])
+ assert res == FormatAstList(variable="__obj", items=[FormatAstVariable("__item", value=lst[0], index=0),
+ FormatAstVariable("__item", value=lst[1], index=1),
+ FormatAstVariable("__item", value=lst[2], index=2)])
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)"))
@@ -108,9 +125,9 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
foo = Concept("foo", body=lst, key="foo").auto_init()
res = service.create_out_tree(context, foo)
- assert res == FormatAstList(variable="__obj", items=[FormatAstVariable("__item", None, lst[0], 0),
- FormatAstVariable("__item", None, lst[1], 1),
- FormatAstVariable("__item", None, lst[2], 2)])
+ assert res == FormatAstList(variable="__obj", items=[FormatAstVariable("__item", value=lst[0], index=0),
+ FormatAstVariable("__item", value=lst[1], index=1),
+ FormatAstVariable("__item", value=lst[2], index=2)])
def test_i_can_develop_list_using_the_custom_items_prop(self):
sheerka, context, service, *rules = self.init_service_with_rules(
@@ -121,16 +138,16 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, foo)
assert res == FormatAstList(variable="__obj", items_prop='custom',
- items=[FormatAstVariable("__item", None, lst[0], 0),
- FormatAstVariable("__item", None, lst[1], 1),
- FormatAstVariable("__item", None, lst[2], 2)])
+ items=[FormatAstVariable("__item", value=lst[0], index=0),
+ FormatAstVariable("__item", value=lst[1], index=1),
+ FormatAstVariable("__item", value=lst[2], index=2)])
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)"))
foo = Concept("foo", key="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):
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)
- assert res == FormatAstList(variable="__rets", items=[FormatAstVariable("__item", None, lst[0], 0),
- FormatAstVariable("__item", None, lst[1], 1),
- FormatAstVariable("__item", None, lst[2], 2)])
+ assert res == FormatAstList(variable="__rets", items=[FormatAstVariable("__item", value=lst[0], index=0),
+ FormatAstVariable("__item", value=lst[1], index=1),
+ FormatAstVariable("__item", value=lst[2], index=2)])
def test_rules_are_correctly_reset_when_list(self):
sheerka, context, service, *rules = self.init_service_with_rules(
@@ -156,8 +173,8 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, lst)
assert res == FormatAstList(variable="__rets", items=[
- FormatAstVariable("__item", None, FormatAstColor("red", FormatAstVariable("__ret", None, lst[0])), 0),
- FormatAstVariable("__item", None, FormatAstColor("red", FormatAstVariable("__ret", None, lst[1])), 1),
+ FormatAstVariable("__item", value=FormatAstColor("red", FormatAstVariable("__ret", value=lst[0])), index=0),
+ FormatAstVariable("__item", value=FormatAstColor("red", FormatAstVariable("__ret", value=lst[1])), index=1),
])
def test_i_can_develop_list_with_recurse(self):
@@ -174,15 +191,15 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, lst)
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=[
- FormatAstVariable("__item", None, r11, 0),
+ FormatAstVariable("__item", value=r11, index=0),
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=[
- FormatAstVariable("__item", None, r22, 0)]),
+ FormatAstVariable("__item", value=r22, index=0)]),
])
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)
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=[
- FormatAstVariable("__item", None, r11, 0),
+ FormatAstVariable("__item", value=r11, index=0),
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=[
- FormatAstVariable("__item", None, r22, 0)]),
+ FormatAstVariable("__item", value=r22, index=0)]),
])
- def test_i_can_develop_using_variable_properties(self):
- sheerka, context, service, *rules = self.init_service_with_rules(
- ("isinstance(__obj, Concept)", "{__obj.body}"),
- )
- lst = Concept("bar", body="value for bar").auto_init()
+ def test_i_can_develop_dict(self):
+ sheerka, context, service, *rules = self.init_service_with_rules(("isinstance(__obj, dict)", "dict(__obj)"))
+ obj = {
+ "key1": "value1",
+ "key2": "value2",
+ }
- res = service.create_out_tree(context, lst)
- assert res == FormatAstVariable("__obj.body", None, "value for bar")
+ 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),
+ 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", [
(("__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):
sheerka, context, service, rule = self.init_service_with_rules(rule)
@@ -233,12 +274,10 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
assert res == expected
@pytest.mark.parametrize("rule, expected", [
- (("__ret", "{__ret}"), FormatAstVariable("__ret", None,
- ReturnValueConcept(who="Test", status=True, value=1))),
+ (("__ret", "{__ret}"), FormatAstVariable("__ret", value=ReturnValueConcept(who="Test", status=True, value=1))),
(("__ret", "magenta(__ret)"),
FormatAstColor("magenta",
- FormatAstVariable("__ret", None,
- ReturnValueConcept(who="Test", status=True, value=1)))),
+ FormatAstVariable("__ret", value=ReturnValueConcept(who="Test", status=True, value=1)))),
])
def test_i_can_manage_infinite_recursion(self, rule, expected):
sheerka, context, service, rule = self.init_service_with_rules(rule)
@@ -258,8 +297,8 @@ class TestSheerkaOut(TestUsingMemoryBasedSheerka):
res = service.create_out_tree(context, ret)
assert res == FormatAstColor("white",
- FormatAstVariable("__ret", None,
- FormatAstVariable("__ret.value", None, "hello world!")))
+ FormatAstVariable("__ret",
+ value=FormatAstVariable("__ret.value", value="hello world!")))
def test_i_can_print_out_the_result(self, capsys):
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)
captured = capsys.readouterr()
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]}
+"""