From 9daefd72071efc9396b6b554a849448fe29700c3 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Tue, 10 Jan 2017 23:09:40 +0100 Subject: [PATCH] Implements commands as extensions using stevedore; adds run and init command. --- Makefile | 2 +- Projectfile | 15 ++++++++++++++- bonobo/__main__.py | 4 ++++ bonobo/commands/__init__.py | 22 ++++++++++++++++++++++ bonobo/commands/init.py | 18 ++++++++++++++++++ bonobo/commands/run.py | 29 +++++++++++++++++++++++++++++ bonobo/util/helpers.py | 9 +++++++-- examples/opendata_fablabs.py | 23 ++++++++++++----------- setup.py | 10 ++++++++-- 9 files changed, 115 insertions(+), 17 deletions(-) create mode 100644 bonobo/__main__.py create mode 100644 bonobo/commands/__init__.py create mode 100644 bonobo/commands/init.py create mode 100644 bonobo/commands/run.py diff --git a/Makefile b/Makefile index 1db98f5..16e06c2 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # This file has been auto-generated. # All changes will be lost, see Projectfile. # -# Updated at 2017-01-03 12:10:41.605435 +# Updated at 2017-01-10 23:12:36.230185 PYTHON ?= $(shell which python) PYTHON_BASENAME ?= $(shell basename $(PYTHON)) diff --git a/Projectfile b/Projectfile index 7b3ad10..2bc6dc7 100644 --- a/Projectfile +++ b/Projectfile @@ -23,6 +23,7 @@ enable_features = { install_requires = [ 'blessings >=1.6,<1.7', 'psutil >=5.0,<5.1', + 'stevedore >=1.19,<1.20', 'toolz >=0.8,<0.9', ] @@ -32,7 +33,7 @@ extras_require = { 'ipywidgets >=6.0.0.beta5' ], 'dev': [ - 'coverage >=4.2,<4.3', + 'coverage >=4.3,<4.4', 'mock >=2.0,<2.1', 'nose >=1.3,<1.4', 'pylint >=1.6,<1.7', @@ -52,6 +53,18 @@ data_files = [ ]), ] +entry_points = { + 'console_scripts': [ + 'bonobo = bonobo.commands:entrypoint' + ], + 'bonobo.commands': [ + 'init = bonobo.commands.init:register', + 'run = bonobo.commands.run:register', + ], + 'edgy.project.features': [ + 'bonobo = bonobo.ext.edgy.project.feature:BonoboFeature' + ] +} @listen('edgy.project.feature.make.on_generate', priority=10) def on_make_generate_docker_targets(event): diff --git a/bonobo/__main__.py b/bonobo/__main__.py new file mode 100644 index 0000000..05ed120 --- /dev/null +++ b/bonobo/__main__.py @@ -0,0 +1,4 @@ +from bonobo.commands import entrypoint + +if __name__ == '__main__': + entrypoint() diff --git a/bonobo/commands/__init__.py b/bonobo/commands/__init__.py new file mode 100644 index 0000000..bc61ebf --- /dev/null +++ b/bonobo/commands/__init__.py @@ -0,0 +1,22 @@ +import argparse + +from stevedore import ExtensionManager + + +def entrypoint(): + parser = argparse.ArgumentParser() + + subparsers = parser.add_subparsers(dest='command') + subparsers.required = True + + def register_extension(ext): + parser = subparsers.add_parser(ext.name) + command = ext.plugin(parser) + parser.set_defaults(command=command) + + mgr = ExtensionManager(namespace='bonobo.commands', ) + mgr.map(register_extension) + + args = parser.parse_args().__dict__ + command = args.pop('command') + command(**args) diff --git a/bonobo/commands/init.py b/bonobo/commands/init.py new file mode 100644 index 0000000..a1ab700 --- /dev/null +++ b/bonobo/commands/init.py @@ -0,0 +1,18 @@ +import os + + +def execute(): + try: + import edgy.project + except ImportError as exc: + raise ImportError( + 'You must install "edgy.project" to use this command.\n\n $ pip install edgy.project\n' + ) from exc + + from edgy.project.__main__ import handle_init + + return handle_init(os.path.join(os.getcwd(), 'Projectfile')) + + +def register(parser): + return execute diff --git a/bonobo/commands/run.py b/bonobo/commands/run.py new file mode 100644 index 0000000..c0f9e14 --- /dev/null +++ b/bonobo/commands/run.py @@ -0,0 +1,29 @@ +import argparse + +from bonobo import Graph, console_run + + +def execute(file): + with file: + code = compile(file.read(), file.name, 'exec') + + context = {} + + try: + exec(code, context) + except Exception as exc: + raise + + graphs = dict((k, v) for k, v in context.items() if isinstance(v, Graph)) + + assert len(graphs) == 1, 'Having more than one graph definition in one file is unsupported for now, but it is ' \ + 'something that will be implemented in the future. ' + + name, graph = list(graphs.items())[0] + + return console_run(graph) + + +def register(parser): + parser.add_argument('file', type=argparse.FileType()) + return execute diff --git a/bonobo/util/helpers.py b/bonobo/util/helpers.py index 35b6459..106b04e 100644 --- a/bonobo/util/helpers.py +++ b/bonobo/util/helpers.py @@ -1,8 +1,13 @@ def run(*chain, plugins=None, strategy=None): from bonobo import Graph, ThreadPoolExecutorStrategy - graph = Graph() - graph.add_chain(*chain) + if len(chain) == 1 and isinstance(chain[0], Graph): + graph = chain[0] + elif len(chain) >= 1: + graph = Graph() + graph.add_chain(*chain) + else: + raise RuntimeError('Empty chain.') executor = (strategy or ThreadPoolExecutorStrategy)() return executor.execute(graph, plugins=plugins or []) diff --git a/examples/opendata_fablabs.py b/examples/opendata_fablabs.py index 9dc7efa..6a5f082 100644 --- a/examples/opendata_fablabs.py +++ b/examples/opendata_fablabs.py @@ -2,7 +2,7 @@ import json from blessings import Terminal -from bonobo import console_run, tee, JsonWriter +from bonobo import console_run, tee, JsonWriter, Graph from bonobo.ext.opendatasoft import from_opendatasoft_api try: @@ -55,14 +55,15 @@ def display(row): print(' - {}: {source}'.format(t.blue('source'), source='datanova/' + API_DATASET)) +graph = Graph( + from_opendatasoft_api( + API_DATASET, netloc=API_NETLOC, timezone='Europe/Paris' + ), + normalize, + filter_france, + tee(display), + JsonWriter('fablabs.json'), +) + if __name__ == '__main__': - console_run( - from_opendatasoft_api( - API_DATASET, netloc=API_NETLOC, timezone='Europe/Paris' - ), - normalize, - filter_france, - tee(display), - JsonWriter('fablabs.json'), - output=True, - ) + console_run(graph, output=True) diff --git a/setup.py b/setup.py index 9abfacf..7e6858d 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name='bonobo', description='Bonobo', license='Apache License, Version 2.0', - install_requires=['blessings >=1.6,<1.7', 'psutil >=5.0,<5.1', 'toolz >=0.8,<0.9'], + install_requires=['blessings >=1.6,<1.7', 'psutil >=5.0,<5.1', 'stevedore >=1.19,<1.20', 'toolz >=0.8,<0.9'], version=version, long_description=read('README.rst'), classifiers=read('classifiers.txt', tolines), @@ -49,11 +49,17 @@ setup( ], extras_require={ 'dev': [ - 'coverage >=4.2,<4.3', 'mock >=2.0,<2.1', 'nose >=1.3,<1.4', 'pylint >=1.6,<1.7', 'pytest >=3,<4', + 'coverage >=4.3,<4.4', 'mock >=2.0,<2.1', 'nose >=1.3,<1.4', 'pylint >=1.6,<1.7', 'pytest >=3,<4', 'pytest-cov >=2.4,<2.5', 'sphinx', 'sphinx_rtd_theme', 'yapf' ], 'jupyter': ['jupyter >=1.0,<1.1', 'ipywidgets >=6.0.0.beta5'] }, + entry_points={ + 'bonobo.commands': ['init = bonobo.commands.init:register', 'run = bonobo.commands.run:register'], + 'console_scripts': ['bonobo = bonobo.commands:entrypoint'], + 'edgy.project.features': ['bonobo = ' + 'bonobo.ext.edgy.project.feature:BonoboFeature'] + }, url='https://bonobo-project.org/', download_url='https://github.com/python-bonobo/bonobo/tarball/{version}'.format(version=version), )