[core] Moves bonobo.execution context related package to new bonobo.execution.contexts package, also moves bonobo.strategies to new bonobo.execution.strategies package, so everything related to execution is now contained under the bonobo.execution package.
This commit is contained in:
42
bonobo/execution/strategies/__init__.py
Normal file
42
bonobo/execution/strategies/__init__.py
Normal file
@ -0,0 +1,42 @@
|
||||
from bonobo.execution.strategies.executor import ProcessPoolExecutorStrategy, ThreadPoolExecutorStrategy
|
||||
from bonobo.execution.strategies.naive import NaiveStrategy
|
||||
|
||||
__all__ = [
|
||||
'create_strategy',
|
||||
]
|
||||
|
||||
STRATEGIES = {
|
||||
'naive': NaiveStrategy,
|
||||
'processpool': ProcessPoolExecutorStrategy,
|
||||
'threadpool': ThreadPoolExecutorStrategy,
|
||||
}
|
||||
|
||||
DEFAULT_STRATEGY = 'threadpool'
|
||||
|
||||
|
||||
def create_strategy(name=None):
|
||||
"""
|
||||
Create a strategy, or just returns it if it's already one.
|
||||
|
||||
:param name:
|
||||
:return: Strategy
|
||||
"""
|
||||
import logging
|
||||
from bonobo.execution.strategies.base import Strategy
|
||||
|
||||
if isinstance(name, Strategy):
|
||||
return name
|
||||
|
||||
if name is None:
|
||||
name = DEFAULT_STRATEGY
|
||||
|
||||
logging.debug('Creating strategy {}...'.format(name))
|
||||
|
||||
try:
|
||||
factory = STRATEGIES[name]
|
||||
except KeyError as exc:
|
||||
raise RuntimeError(
|
||||
'Invalid strategy {}. Available choices: {}.'.format(repr(name), ', '.join(sorted(STRATEGIES.keys())))
|
||||
) from exc
|
||||
|
||||
return factory()
|
||||
18
bonobo/execution/strategies/base.py
Normal file
18
bonobo/execution/strategies/base.py
Normal file
@ -0,0 +1,18 @@
|
||||
from bonobo.execution.contexts.graph import GraphExecutionContext
|
||||
|
||||
|
||||
class Strategy:
|
||||
"""
|
||||
Base class for execution strategies.
|
||||
|
||||
"""
|
||||
GraphExecutionContextType = GraphExecutionContext
|
||||
|
||||
def __init__(self, GraphExecutionContextType=None):
|
||||
self.GraphExecutionContextType = GraphExecutionContextType or self.GraphExecutionContextType
|
||||
|
||||
def create_graph_execution_context(self, graph, *args, GraphExecutionContextType=None, **kwargs):
|
||||
return (GraphExecutionContextType or self.GraphExecutionContextType)(graph, *args, **kwargs)
|
||||
|
||||
def execute(self, graph, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
68
bonobo/execution/strategies/executor.py
Normal file
68
bonobo/execution/strategies/executor.py
Normal file
@ -0,0 +1,68 @@
|
||||
import functools
|
||||
import logging
|
||||
import sys
|
||||
from concurrent.futures import Executor, ProcessPoolExecutor, ThreadPoolExecutor
|
||||
|
||||
from bonobo.structs.bags import Bag
|
||||
from bonobo.constants import BEGIN, END
|
||||
from bonobo.execution.strategies.base import Strategy
|
||||
from bonobo.util import get_name
|
||||
|
||||
|
||||
class ExecutorStrategy(Strategy):
|
||||
"""
|
||||
Strategy based on a concurrent.futures.Executor subclass (or similar interface).
|
||||
|
||||
"""
|
||||
|
||||
executor_factory = Executor
|
||||
|
||||
def create_executor(self):
|
||||
return self.executor_factory()
|
||||
|
||||
def execute(self, graph, **kwargs):
|
||||
context = self.create_graph_execution_context(graph, **kwargs)
|
||||
context.write(BEGIN, Bag(), END)
|
||||
|
||||
futures = []
|
||||
|
||||
with self.create_executor() as executor:
|
||||
context.start(self.get_starter(executor, futures))
|
||||
|
||||
while context.alive:
|
||||
try:
|
||||
context.tick()
|
||||
except KeyboardInterrupt:
|
||||
logging.getLogger(__name__).warning(
|
||||
'KeyboardInterrupt received. Trying to terminate the nodes gracefully.'
|
||||
)
|
||||
context.kill()
|
||||
break
|
||||
|
||||
context.stop()
|
||||
|
||||
return context
|
||||
|
||||
def get_starter(self, executor, futures):
|
||||
def starter(node):
|
||||
@functools.wraps(node)
|
||||
def _runner():
|
||||
try:
|
||||
with node:
|
||||
node.loop()
|
||||
except BaseException as exc:
|
||||
logging.getLogger(__name__).info(
|
||||
'Got {} in {} runner.'.format(get_name(exc), node), exc_info=sys.exc_info()
|
||||
)
|
||||
|
||||
futures.append(executor.submit(_runner))
|
||||
|
||||
return starter
|
||||
|
||||
|
||||
class ThreadPoolExecutorStrategy(ExecutorStrategy):
|
||||
executor_factory = ThreadPoolExecutor
|
||||
|
||||
|
||||
class ProcessPoolExecutorStrategy(ExecutorStrategy):
|
||||
executor_factory = ProcessPoolExecutor
|
||||
26
bonobo/execution/strategies/naive.py
Normal file
26
bonobo/execution/strategies/naive.py
Normal file
@ -0,0 +1,26 @@
|
||||
from bonobo.constants import BEGIN, END
|
||||
from bonobo.execution.strategies.base import Strategy
|
||||
from bonobo.structs.bags import Bag
|
||||
|
||||
|
||||
class NaiveStrategy(Strategy):
|
||||
# TODO: how to run plugins in "naive" mode ?
|
||||
|
||||
def execute(self, graph, **kwargs):
|
||||
context = self.create_graph_execution_context(graph, **kwargs)
|
||||
context.write(BEGIN, Bag(), END)
|
||||
|
||||
# start
|
||||
context.start()
|
||||
|
||||
# loop
|
||||
nodes = list(context.nodes)
|
||||
while len(nodes):
|
||||
for node in nodes:
|
||||
node.loop()
|
||||
nodes = list(node for node in nodes if node.alive)
|
||||
|
||||
# stop
|
||||
context.stop()
|
||||
|
||||
return context
|
||||
Reference in New Issue
Block a user