From 329296ce6b35881ddfa191d759f727bfa7a2dca8 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Mon, 22 May 2017 15:57:11 +0200 Subject: [PATCH] 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) +