Merge remote-tracking branch 'origin/better_errors' into better_errors

This commit is contained in:
Romain Dorgueil
2018-07-22 07:55:49 +02:00
21 changed files with 96 additions and 82 deletions

View File

@ -1,4 +1,4 @@
# Generated by Medikit 0.6.3 on 2018-06-11. # Generated by Medikit 0.6.3 on 2018-07-22.
# All changes will be overriden. # All changes will be overriden.
# Edit Projectfile and run “make update” (or “medikit update”) to regenerate. # Edit Projectfile and run “make update” (or “medikit update”) to regenerate.

View File

@ -72,11 +72,12 @@ class ConfigurableMeta(type):
try: try:
import _functools import _functools
except: except ImportError:
import functools import functools
PartiallyConfigured = functools.partial PartiallyConfigured = functools.partial
else: else:
class PartiallyConfigured(_functools.partial): class PartiallyConfigured(_functools.partial):
@property # TODO XXX cache this @property # TODO XXX cache this
def _options_values(self): def _options_values(self):

View File

@ -1,5 +1,3 @@
import inspect
import pprint
import re import re
import threading import threading
import types import types

View File

@ -51,7 +51,7 @@ if __name__ == '__main__':
s3.head_object( s3.head_object(
Bucket='bonobo-examples', Key=s3_path Bucket='bonobo-examples', Key=s3_path
) )
except: except Exception:
s3.upload_file( s3.upload_file(
local_path, local_path,
'bonobo-examples', 'bonobo-examples',

View File

@ -1,7 +1,6 @@
import logging import logging
import sys import sys
from contextlib import contextmanager from contextlib import contextmanager
from logging import ERROR
from bonobo.util import deprecated from bonobo.util import deprecated
from bonobo.util.objects import Wrapper, get_name from bonobo.util.objects import Wrapper, get_name
@ -13,7 +12,7 @@ def recoverable(error_handler):
try: try:
yield yield
except Exception as exc: # pylint: disable=broad-except except Exception as exc: # pylint: disable=broad-except
error_handler(*sys.exc_info(), level=ERROR) error_handler(*sys.exc_info(), level=logging.ERROR)
@contextmanager @contextmanager
@ -21,7 +20,7 @@ def unrecoverable(error_handler):
try: try:
yield yield
except Exception as exc: # pylint: disable=broad-except except Exception as exc: # pylint: disable=broad-except
error_handler(*sys.exc_info(), level=ERROR) error_handler(*sys.exc_info(), level=logging.ERROR)
raise # raise unrecoverableerror from exc ? raise # raise unrecoverableerror from exc ?
@ -54,8 +53,7 @@ class Lifecycle:
@property @property
def should_loop(self): def should_loop(self):
# TODO XXX started/stopped? return self.alive and not any((self.defunct, self.killed))
return not any((self.defunct, self.killed))
@property @property
def status(self): def status(self):

View File

@ -1,14 +1,19 @@
import logging
from functools import partial from functools import partial
from queue import Empty
from time import sleep from time import sleep
from bonobo.config import create_container from bonobo.config import create_container
from bonobo.constants import BEGIN, END from bonobo.constants import BEGIN, END
from bonobo.errors import InactiveReadableError
from bonobo.execution import events from bonobo.execution import events
from bonobo.execution.contexts.base import BaseContext from bonobo.execution.contexts.base import BaseContext
from bonobo.execution.contexts.node import NodeExecutionContext from bonobo.execution.contexts.node import NodeExecutionContext
from bonobo.execution.contexts.plugin import PluginExecutionContext from bonobo.execution.contexts.plugin import PluginExecutionContext
from whistle import EventDispatcher from whistle import EventDispatcher
logger = logging.getLogger(__name__)
class GraphExecutionContext(BaseContext): class GraphExecutionContext(BaseContext):
""" """
@ -104,11 +109,16 @@ class GraphExecutionContext(BaseContext):
sleep(self.TICK_PERIOD) sleep(self.TICK_PERIOD)
def loop(self): def loop(self):
while self.should_loop: nodes = set(node for node in self.nodes if node.should_loop)
self.tick() while self.should_loop and len(nodes):
for node in self.nodes: self.tick(pause=False)
if node.should_loop: for node in list(nodes):
try:
node.step() node.step()
except Empty:
continue
except InactiveReadableError:
nodes.discard(node)
def stop(self, stopper=None): def stop(self, stopper=None):
super(GraphExecutionContext, self).stop() super(GraphExecutionContext, self).stop()

View File

@ -30,6 +30,7 @@ class NodeExecutionContext(BaseContext, WithStatistics):
a service implementation, or a value holder). a service implementation, or a value holder).
""" """
def __init__(self, wrapped, *, parent=None, services=None, _input=None, _outputs=None): def __init__(self, wrapped, *, parent=None, services=None, _input=None, _outputs=None):
""" """
Node execution context has the responsibility fo storing the state of a transformation during its execution. Node execution context has the responsibility fo storing the state of a transformation during its execution.
@ -92,11 +93,15 @@ class NodeExecutionContext(BaseContext, WithStatistics):
# Not normal to have a partially configured object here, so let's warn the user instead of having get into # Not normal to have a partially configured object here, so let's warn the user instead of having get into
# the hard trouble of understanding that by himself. # the hard trouble of understanding that by himself.
raise TypeError( raise TypeError(
'Configurables should be instanciated before execution starts.\nGot {!r}.\n'.format(self.wrapped) 'Configurables should be instanciated before execution starts.\nGot {!r}.\n'.format(
self.wrapped
)
) from exc ) from exc
else: else:
raise TypeError( raise TypeError(
'Configurables should be instanciated before execution starts.\nGot {!r}.\n'.format(self.wrapped) 'Configurables should be instanciated before execution starts.\nGot {!r}.\n'.format(
self.wrapped
)
) )
self._stack.setup(self) self._stack.setup(self)
except Exception: except Exception:
@ -120,7 +125,14 @@ class NodeExecutionContext(BaseContext, WithStatistics):
break break
except Empty: except Empty:
sleep(TICK_PERIOD) # XXX: How do we determine this constant? sleep(TICK_PERIOD) # XXX: How do we determine this constant?
continue
logger.debug('Node loop ends for {!r}.'.format(self))
def step(self):
try:
self._step()
except InactiveReadableError:
raise
except ( except (
NotImplementedError, NotImplementedError,
UnrecoverableError, UnrecoverableError,
@ -131,9 +143,7 @@ class NodeExecutionContext(BaseContext, WithStatistics):
except BaseException: except BaseException:
self.fatal(sys.exc_info()) # exit loop self.fatal(sys.exc_info()) # exit loop
logger.debug('Node loop ends for {!r}.'.format(self)) def _step(self):
def step(self):
""" """
A single step in the loop. A single step in the loop.
@ -280,7 +290,7 @@ class NodeExecutionContext(BaseContext, WithStatistics):
If Queue raises (like Timeout or Empty), stat won't be changed. If Queue raises (like Timeout or Empty), stat won't be changed.
""" """
input_bag = self.input.get() input_bag = self.input.get(timeout=0)
# Store or check input type # Store or check input type
if self._input_type is None: if self._input_type is None:

View File

@ -29,7 +29,7 @@ class ExecutorStrategy(Strategy):
with self.create_executor() as executor: with self.create_executor() as executor:
try: try:
context.start(self.get_starter(executor, futures)) context.start(self.get_starter(executor, futures))
except: except Exception:
logger.critical('Exception caught while starting execution context.', exc_info=sys.exc_info()) logger.critical('Exception caught while starting execution context.', exc_info=sys.exc_info())
while context.alive: while context.alive:
@ -53,14 +53,14 @@ class ExecutorStrategy(Strategy):
try: try:
with node: with node:
node.loop() node.loop()
except: except Exception:
logging.getLogger(__name__).critical( logging.getLogger(__name__).critical(
'Critical error in threadpool node starter.', exc_info=sys.exc_info() 'Critical error in threadpool node starter.', exc_info=sys.exc_info()
) )
try: try:
futures.append(executor.submit(_runner)) futures.append(executor.submit(_runner))
except: except Exception:
logging.getLogger(__name__).critical('futures.append', exc_info=sys.exc_info()) logging.getLogger(__name__).critical('futures.append', exc_info=sys.exc_info())
return starter return starter

View File

@ -1,12 +1,11 @@
import csv import csv
from bonobo.config import Option, use_raw_input, use_context from bonobo.config import Option, use_context
from bonobo.config.options import Method, RenamedOption from bonobo.config.options import Method, RenamedOption
from bonobo.constants import NOT_MODIFIED from bonobo.constants import NOT_MODIFIED
from bonobo.nodes.io.base import FileHandler from bonobo.nodes.io.base import FileHandler
from bonobo.nodes.io.file import FileReader, FileWriter from bonobo.nodes.io.file import FileReader, FileWriter
from bonobo.util import ensure_tuple from bonobo.util import ensure_tuple
from bonobo.util.bags import BagType
class CsvHandler(FileHandler): class CsvHandler(FileHandler):

View File

@ -1,5 +1,4 @@
from bonobo.plugins import Plugin from bonobo.plugins import Plugin
from raven import Client
class SentryPlugin(Plugin): class SentryPlugin(Plugin):

View File

@ -89,6 +89,7 @@ class Registry:
default_registry = Registry() default_registry = Registry()
def create_reader(name, *args, format=None, registry=default_registry, **kwargs): def create_reader(name, *args, format=None, registry=default_registry, **kwargs):
""" """
Create a reader instance, guessing its factory using filename (and eventually format). Create a reader instance, guessing its factory using filename (and eventually format).
@ -103,6 +104,7 @@ def create_reader(name, *args, format=None, registry=default_registry, **kwargs)
""" """
return registry.get_reader_factory_for(name, format=format)(name, *args, **kwargs) return registry.get_reader_factory_for(name, format=format)(name, *args, **kwargs)
def create_writer(name, *args, format=None, registry=default_registry, **kwargs): def create_writer(name, *args, format=None, registry=default_registry, **kwargs):
""" """
Create a writer instance, guessing its factory using filename (and eventually format). Create a writer instance, guessing its factory using filename (and eventually format).

View File

@ -52,7 +52,7 @@ class Setting:
def set(self, value): def set(self, value):
value = self.formatter(value) if self.formatter else value value = self.formatter(value) if self.formatter else value
if self.validator and not self.validator(value): if self.validator and not self.validator(value):
raise ValidationError('Invalid value {!r} for setting {}.'.format(value, self.name)) raise ValidationError(self, 'Invalid value {!r} for setting {!r}.'.format(value, self.name))
self.value = value self.value = value
def set_if_true(self, value): def set_if_true(self, value):

View File

@ -57,7 +57,6 @@ def get_argument_parser(parser=None):
:return: :return:
""" """
if parser is None: if parser is None:
import argparse
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
# Store globally to be able to warn the user about the fact he's probably wrong not to pass a parser to # Store globally to be able to warn the user about the fact he's probably wrong not to pass a parser to

View File

@ -1,6 +1,6 @@
-e .[dev] -e .[dev]
-r requirements.txt -r requirements.txt
alabaster==0.7.10 alabaster==0.7.11
arrow==0.12.1 arrow==0.12.1
atomicwrites==1.1.5 atomicwrites==1.1.5
attrs==18.1.0 attrs==18.1.0
@ -13,7 +13,7 @@ cookiecutter==1.5.1
coverage==4.5.1 coverage==4.5.1
docutils==0.14 docutils==0.14
future==0.16.0 future==0.16.0
idna==2.6 idna==2.7
imagesize==1.0.0 imagesize==1.0.0
jinja2-time==0.2.0 jinja2-time==0.2.0
jinja2==2.10 jinja2==2.10
@ -22,20 +22,20 @@ more-itertools==4.2.0
packaging==17.1 packaging==17.1
pluggy==0.6.0 pluggy==0.6.0
poyo==0.4.1 poyo==0.4.1
py==1.5.3 py==1.5.4
pygments==2.2.0 pygments==2.2.0
pyparsing==2.2.0 pyparsing==2.2.0
pytest-cov==2.5.1 pytest-cov==2.5.1
pytest-timeout==1.2.1 pytest-timeout==1.3.0
pytest==3.6.1 pytest==3.6.3
python-dateutil==2.7.3 python-dateutil==2.7.3
pytz==2018.4 pytz==2018.5
requests==2.18.4 requests==2.19.1
six==1.11.0 six==1.11.0
snowballstemmer==1.2.1 snowballstemmer==1.2.1
sphinx-sitemap==0.2 sphinx-sitemap==0.2
sphinx==1.7.5 sphinx==1.7.6
sphinxcontrib-websupport==1.1.0 sphinxcontrib-websupport==1.1.0
urllib3==1.22 urllib3==1.23
whichcraft==0.4.1 whichcraft==0.4.1
yapf==0.22.0 yapf==0.22.0

View File

@ -7,24 +7,23 @@ chardet==3.0.4
colorama==0.3.9 colorama==0.3.9
docker-pycreds==0.3.0 docker-pycreds==0.3.0
docker==2.7.0 docker==2.7.0
fs==2.0.23 fs==2.0.25
graphviz==0.8.3 graphviz==0.8.4
idna==2.6 idna==2.7
jinja2==2.10 jinja2==2.10
markupsafe==1.0 markupsafe==1.0
mondrian==0.7.0 mondrian==0.7.0
packaging==17.1 packaging==17.1
pbr==4.0.4 pbr==4.1.1
psutil==5.4.6 psutil==5.4.6
pyparsing==2.2.0 pyparsing==2.2.0
python-slugify==1.2.5 python-slugify==1.2.5
pytz==2018.4 pytz==2018.5
requests==2.18.4 requests==2.19.1
semantic-version==2.6.0 semantic-version==2.6.0
six==1.11.0 six==1.11.0
stevedore==1.28.0 stevedore==1.29.0
typing==3.6.4
unidecode==1.0.22 unidecode==1.0.22
urllib3==1.22 urllib3==1.23
websocket-client==0.48.0 websocket-client==0.48.0
whistle==1.0.1 whistle==1.0.1

View File

@ -10,7 +10,7 @@ ipykernel==4.8.2
ipython-genutils==0.2.0 ipython-genutils==0.2.0
ipython==6.4.0 ipython==6.4.0
ipywidgets==6.0.1 ipywidgets==6.0.1
jedi==0.12.0 jedi==0.12.1
jinja2==2.10 jinja2==2.10
jsonschema==2.6.0 jsonschema==2.6.0
jupyter-client==5.2.3 jupyter-client==5.2.3
@ -21,23 +21,24 @@ markupsafe==1.0
mistune==0.8.3 mistune==0.8.3
nbconvert==5.3.1 nbconvert==5.3.1
nbformat==4.4.0 nbformat==4.4.0
notebook==5.5.0 notebook==5.6.0
pandocfilters==1.4.2 pandocfilters==1.4.2
parso==0.2.1 parso==0.3.1
pexpect==4.6.0 pexpect==4.6.0
pickleshare==0.7.4 pickleshare==0.7.4
prometheus-client==0.3.0
prompt-toolkit==1.0.15 prompt-toolkit==1.0.15
ptyprocess==0.5.2 ptyprocess==0.6.0
pygments==2.2.0 pygments==2.2.0
python-dateutil==2.7.3 python-dateutil==2.7.3
pyzmq==17.0.0 pyzmq==17.1.0
qtconsole==4.3.1 qtconsole==4.3.1
send2trash==1.5.0 send2trash==1.5.0
simplegeneric==0.8.1 simplegeneric==0.8.1
six==1.11.0 six==1.11.0
terminado==0.8.1 terminado==0.8.1
testpath==0.3.1 testpath==0.3.1
tornado==5.0.2 tornado==5.1
traitlets==4.3.2 traitlets==4.3.2
wcwidth==0.1.7 wcwidth==0.1.7
webencodings==0.5.1 webencodings==0.5.1

View File

@ -5,23 +5,22 @@ bonobo-sqlalchemy==0.6.0
certifi==2018.4.16 certifi==2018.4.16
chardet==3.0.4 chardet==3.0.4
colorama==0.3.9 colorama==0.3.9
fs==2.0.23 fs==2.0.25
graphviz==0.8.3 graphviz==0.8.4
idna==2.6 idna==2.7
jinja2==2.10 jinja2==2.10
markupsafe==1.0 markupsafe==1.0
mondrian==0.7.0 mondrian==0.7.0
packaging==17.1 packaging==17.1
pbr==4.0.4 pbr==4.1.1
psutil==5.4.6 psutil==5.4.6
pyparsing==2.2.0 pyparsing==2.2.0
python-slugify==1.2.5 python-slugify==1.2.5
pytz==2018.4 pytz==2018.5
requests==2.18.4 requests==2.19.1
six==1.11.0 six==1.11.0
sqlalchemy==1.2.8 sqlalchemy==1.2.10
stevedore==1.28.0 stevedore==1.29.0
typing==3.6.4
unidecode==1.0.22 unidecode==1.0.22
urllib3==1.22 urllib3==1.23
whistle==1.0.1 whistle==1.0.1

View File

@ -3,22 +3,21 @@ appdirs==1.4.3
certifi==2018.4.16 certifi==2018.4.16
chardet==3.0.4 chardet==3.0.4
colorama==0.3.9 colorama==0.3.9
fs==2.0.23 fs==2.0.25
graphviz==0.8.3 graphviz==0.8.4
idna==2.6 idna==2.7
jinja2==2.10 jinja2==2.10
markupsafe==1.0 markupsafe==1.0
mondrian==0.7.0 mondrian==0.7.0
packaging==17.1 packaging==17.1
pbr==4.0.4 pbr==4.1.1
psutil==5.4.6 psutil==5.4.6
pyparsing==2.2.0 pyparsing==2.2.0
python-slugify==1.2.5 python-slugify==1.2.5
pytz==2018.4 pytz==2018.5
requests==2.18.4 requests==2.19.1
six==1.11.0 six==1.11.0
stevedore==1.28.0 stevedore==1.29.0
typing==3.6.4
unidecode==1.0.22 unidecode==1.0.22
urllib3==1.22 urllib3==1.23
whistle==1.0.1 whistle==1.0.1

View File

@ -1,4 +1,4 @@
# Generated by Medikit 0.6.3 on 2018-06-11. # Generated by Medikit 0.6.3 on 2018-07-22.
# All changes will be overriden. # All changes will be overriden.
# Edit Projectfile and run “make update” (or “medikit update”) to regenerate. # Edit Projectfile and run “make update” (or “medikit update”) to regenerate.

View File

@ -1,4 +1,5 @@
from bonobo import Graph from bonobo import Graph
from bonobo.constants import EMPTY, BEGIN, END
from bonobo.execution.contexts import GraphExecutionContext from bonobo.execution.contexts import GraphExecutionContext
@ -49,9 +50,8 @@ def test_lifecycle_of_graph_with_recoverable_error():
def test_lifecycle_of_graph_with_unrecoverable_error(): def test_lifecycle_of_graph_with_unrecoverable_error():
graph = Graph([1, 2, 3], raise_an_unrecoverrable_error, print) graph = Graph([1, 2, 3], raise_an_unrecoverrable_error, print)
with GraphExecutionContext(graph) as context: with GraphExecutionContext(graph) as context:
assert context.started assert context.started and context.alive and not context.stopped
assert context.alive context.write(BEGIN, EMPTY, END)
assert not context.stopped
context.loop() context.loop()
assert context.started assert context.started
assert not context.alive assert not context.alive