From 329296ce6b35881ddfa191d759f727bfa7a2dca8 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Mon, 22 May 2017 15:57:11 +0200 Subject: [PATCH 1/3] Better error display. --- bonobo/config/services.py | 3 ++- bonobo/errors.py | 3 +++ bonobo/examples/nodes/_services.py | 5 +++++ bonobo/execution/base.py | 7 +++---- bonobo/nodes/io/xml.py | 0 bonobo/strategies/executor.py | 8 +++----- bonobo/util/errors.py | 26 +++++++++++++++++++++----- 7 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 bonobo/examples/nodes/_services.py create mode 100644 bonobo/nodes/io/xml.py diff --git a/bonobo/config/services.py b/bonobo/config/services.py index 8d6c95e..107130e 100644 --- a/bonobo/config/services.py +++ b/bonobo/config/services.py @@ -2,6 +2,7 @@ import re import types from bonobo.config.options import Option +from bonobo.errors import MissingServiceImplementationError _service_name_re = re.compile(r"^[^\d\W]\w*(:?\.[^\d\W]\w*)*$", re.UNICODE) @@ -78,7 +79,7 @@ class Container(dict): if not name in self: if default: return default - raise KeyError('Cannot resolve service {!r} using provided service collection.'.format(name)) + raise MissingServiceImplementationError('Cannot resolve service {!r} using provided service collection.'.format(name)) value = super().get(name) if isinstance(value, types.LambdaType): value = value(self) diff --git a/bonobo/errors.py b/bonobo/errors.py index 4a2e9c5..cdb4db5 100644 --- a/bonobo/errors.py +++ b/bonobo/errors.py @@ -55,4 +55,7 @@ class ProhibitedOperationError(RuntimeError): class ConfigurationError(Exception): + pass + +class MissingServiceImplementationError(KeyError): pass \ No newline at end of file diff --git a/bonobo/examples/nodes/_services.py b/bonobo/examples/nodes/_services.py new file mode 100644 index 0000000..337bf6b --- /dev/null +++ b/bonobo/examples/nodes/_services.py @@ -0,0 +1,5 @@ +from bonobo import get_examples_path, open_fs + + +def get_services(): + return {'fs': open_fs(get_examples_path())} diff --git a/bonobo/execution/base.py b/bonobo/execution/base.py index 6ca22f2..d500e28 100644 --- a/bonobo/execution/base.py +++ b/bonobo/execution/base.py @@ -55,10 +55,9 @@ class LoopingExecutionContext(Wrapper): raise RuntimeError('Cannot start a node twice ({}).'.format(get_name(self))) self._started = True - self._stack = ContextCurrifier(self.wrapped, *self._get_initial_context()) - with unrecoverable(self.handle_error): - self._stack.setup(self) + self._stack = ContextCurrifier(self.wrapped, *self._get_initial_context()) + self._stack.setup(self) for enhancer in self._enhancers: with unrecoverable(self.handle_error): @@ -82,7 +81,7 @@ class LoopingExecutionContext(Wrapper): return try: - with unrecoverable(self.handle_error): + if self._stack: self._stack.teardown() finally: self._stopped = True diff --git a/bonobo/nodes/io/xml.py b/bonobo/nodes/io/xml.py new file mode 100644 index 0000000..e69de29 diff --git a/bonobo/strategies/executor.py b/bonobo/strategies/executor.py index 3f34862..26b810b 100644 --- a/bonobo/strategies/executor.py +++ b/bonobo/strategies/executor.py @@ -36,7 +36,7 @@ class ExecutorStrategy(Strategy): plugin_context.loop() plugin_context.stop() except Exception as exc: - print_error(exc, traceback.format_exc(), prefix='Error in plugin context', context=plugin_context) + print_error(exc, traceback.format_exc(), context=plugin_context) futures.append(executor.submit(_runner)) @@ -46,9 +46,7 @@ class ExecutorStrategy(Strategy): try: node_context.start() except Exception as exc: - print_error( - exc, traceback.format_exc(), prefix='Could not start node context', context=node_context - ) + print_error(exc, traceback.format_exc(), context=node_context, method='start') node_context.input.on_end() else: node_context.loop() @@ -56,7 +54,7 @@ class ExecutorStrategy(Strategy): try: node_context.stop() except Exception as exc: - print_error(exc, traceback.format_exc(), prefix='Could not stop node context', context=node_context) + print_error(exc, traceback.format_exc(), context=node_context, method='stop') futures.append(executor.submit(_runner)) diff --git a/bonobo/util/errors.py b/bonobo/util/errors.py index bd1f51f..3160926 100644 --- a/bonobo/util/errors.py +++ b/bonobo/util/errors.py @@ -1,5 +1,7 @@ import sys +from textwrap import indent +from bonobo import settings from bonobo.structs.bags import ErrorBag @@ -7,7 +9,14 @@ def is_error(bag): return isinstance(bag, ErrorBag) -def print_error(exc, trace, context=None, prefix=''): +def _get_error_message(exc): + if hasattr(exc, '__str__'): + message = str(exc) + return message[0].upper() + message[1:] + return '\n'.join(exc.args), + + +def print_error(exc, trace, context=None, method=None): """ Error handler. Whatever happens in a plugin or component, if it looks like an exception, taste like an exception or somehow make me think it is an exception, I'll handle it. @@ -18,14 +27,21 @@ def print_error(exc, trace, context=None, prefix=''): """ from colorama import Fore, Style + + prefix = '{}{} | {}'.format(Fore.RED, Style.BRIGHT, Style.RESET_ALL) + print( Style.BRIGHT, Fore.RED, - '\U0001F4A3 {}{}{}'.format( - (prefix + ': ') if prefix else '', type(exc).__name__, ' in {!r}'.format(context) if context else '' - ), + type(exc).__name__, + ' (in {}{})'.format(type(context).__name__, '.{}()'.format(method) if method else '') if context else '', + Style.RESET_ALL, + '\n', + indent(_get_error_message(exc), prefix+Style.BRIGHT), Style.RESET_ALL, sep='', file=sys.stderr, ) - print(trace) + print(prefix, file=sys.stderr) + print(indent(trace, prefix, predicate=lambda line: True), file=sys.stderr) + From c56d497a8c66e58711e888a20202c2f31dcbf02c Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Mon, 22 May 2017 15:57:47 +0200 Subject: [PATCH 2/3] New simpler pretty printer (experimental) that supports all kind of bags. --- bonobo/nodes/basics.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/bonobo/nodes/basics.py b/bonobo/nodes/basics.py index 195cd8e..025b99f 100644 --- a/bonobo/nodes/basics.py +++ b/bonobo/nodes/basics.py @@ -1,6 +1,7 @@ import functools from pprint import pprint as _pprint +import itertools from colorama import Fore, Style from bonobo.config import Configurable, Option @@ -69,6 +70,12 @@ def _count_counter(self, context): context.send(Bag(counter._value)) +class PrettyPrinter(Configurable): + def call(self, *args, **kwargs): + for i, (item, value) in enumerate(itertools.chain(enumerate(args), kwargs.items())): + print(' ' if i else '•', item, '=', str(value).strip().replace('\n', '\n'+CLEAR_EOL), CLEAR_EOL) + + pprint = Tee(_pprint) From ecfdc81db97494aec712db8715d48b3c70a1fb6a Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Mon, 22 May 2017 15:58:14 +0200 Subject: [PATCH 3/3] Removes unneeded __all__ definitions in subpackages. --- bonobo/nodes/io/file.py | 5 ----- bonobo/nodes/io/json.py | 4 ---- 2 files changed, 9 deletions(-) diff --git a/bonobo/nodes/io/file.py b/bonobo/nodes/io/file.py index c88f601..f3cb1b0 100644 --- a/bonobo/nodes/io/file.py +++ b/bonobo/nodes/io/file.py @@ -4,11 +4,6 @@ from bonobo.config.processors import ContextProcessor from bonobo.constants import NOT_MODIFIED from bonobo.util.objects import ValueHolder -__all__ = [ - 'FileReader', - 'FileWriter', -] - class FileHandler(Configurable): """Abstract component factory for file-related components. diff --git a/bonobo/nodes/io/json.py b/bonobo/nodes/io/json.py index fdb49b8..b2db708 100644 --- a/bonobo/nodes/io/json.py +++ b/bonobo/nodes/io/json.py @@ -3,10 +3,6 @@ import json from bonobo.config.processors import ContextProcessor from .file import FileWriter, FileReader -__all__ = [ - 'JsonWriter', -] - class JsonHandler(): eol = ',\n'