Merge branch 'services_init' into 17_positional_options
Conflicts: .coveragerc
This commit is contained in:
4
.codacy.yml
Normal file
4
.codacy.yml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
exclude_paths:
|
||||
- bonobo/examples/
|
||||
- bonobo/ext/
|
||||
@ -2,6 +2,7 @@
|
||||
branch = True
|
||||
omit =
|
||||
bonobo/examples/**
|
||||
bonobo/ext/**
|
||||
|
||||
[report]
|
||||
# Regexes for lines to exclude from consideration
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,6 +25,7 @@
|
||||
/.idea
|
||||
/.release
|
||||
/bonobo.iml
|
||||
/bonobo/examples/work_in_progress/
|
||||
/bonobo/ext/jupyter/js/node_modules/
|
||||
/build/
|
||||
/coverage.xml
|
||||
|
||||
7
Makefile
7
Makefile
@ -1,7 +1,7 @@
|
||||
# This file has been auto-generated.
|
||||
# All changes will be lost, see Projectfile.
|
||||
#
|
||||
# Updated at 2017-04-30 10:12:39.793759
|
||||
# Updated at 2017-05-01 08:35:15.162008
|
||||
|
||||
PYTHON ?= $(shell which python)
|
||||
PYTHON_BASENAME ?= $(shell basename $(PYTHON))
|
||||
@ -10,6 +10,7 @@ PYTHON_REQUIREMENTS_DEV_FILE ?= requirements-dev.txt
|
||||
QUICK ?=
|
||||
VIRTUAL_ENV ?= .virtualenv-$(PYTHON_BASENAME)
|
||||
PIP ?= $(VIRTUAL_ENV)/bin/pip
|
||||
PIP_INSTALL_OPTIONS ?=
|
||||
PYTEST ?= $(VIRTUAL_ENV)/bin/pytest
|
||||
PYTEST_OPTIONS ?= --capture=no --cov=bonobo --cov-report html
|
||||
SPHINX_OPTS ?=
|
||||
@ -24,13 +25,13 @@ YAPF_OPTIONS ?= -rip
|
||||
# Installs the local project dependencies.
|
||||
install: $(VIRTUAL_ENV)
|
||||
if [ -z "$(QUICK)" ]; then \
|
||||
$(PIP) install -U pip wheel -r $(PYTHON_REQUIREMENTS_FILE) ; \
|
||||
$(PIP) install -U pip wheel $(PIP_INSTALL_OPTIONS) -r $(PYTHON_REQUIREMENTS_FILE) ; \
|
||||
fi
|
||||
|
||||
# Installs the local project dependencies, including development-only libraries.
|
||||
install-dev: $(VIRTUAL_ENV)
|
||||
if [ -z "$(QUICK)" ]; then \
|
||||
$(PIP) install -U pip wheel -r $(PYTHON_REQUIREMENTS_DEV_FILE) ; \
|
||||
$(PIP) install -U pip wheel $(PIP_INSTALL_OPTIONS) -r $(PYTHON_REQUIREMENTS_DEV_FILE) ; \
|
||||
fi
|
||||
|
||||
# Cleans up the local mess.
|
||||
|
||||
16
appveyor.yml
16
appveyor.yml
@ -24,6 +24,8 @@ environment:
|
||||
PYTHON_VERSION: "3.6.1"
|
||||
PYTHON_ARCH: "64"
|
||||
|
||||
build: false
|
||||
|
||||
install:
|
||||
# If there is a newer build queued for the same PR, cancel this one.
|
||||
# The AppVeyor 'rollout builds' option is supposed to serve the same
|
||||
@ -61,18 +63,18 @@ install:
|
||||
# compiled extensions and are not provided as pre-built wheel packages,
|
||||
# pip will build them from source using the MSVC compiler matching the
|
||||
# target Python version and architecture
|
||||
- "%CMD_IN_ENV% pip install -e ."
|
||||
- "%CMD_IN_ENV% pip install -e .[dev]"
|
||||
|
||||
test_script:
|
||||
# Run the project tests
|
||||
- "%CMD_IN_ENV% pytest --capture=no --cov=bonobo --cov-report html"
|
||||
|
||||
after_test:
|
||||
# If tests are successful, create binary packages for the project.
|
||||
- "%CMD_IN_ENV% python setup.py bdist_wheel"
|
||||
- "%CMD_IN_ENV% python setup.py bdist_wininst"
|
||||
- "%CMD_IN_ENV% python setup.py bdist_msi"
|
||||
- ps: "ls dist"
|
||||
#after_test:
|
||||
# If tests are successful, create binary packages for the project.
|
||||
# - "%CMD_IN_ENV% python setup.py bdist_wheel"
|
||||
# - "%CMD_IN_ENV% python setup.py bdist_wininst"
|
||||
# - "%CMD_IN_ENV% python setup.py bdist_msi"
|
||||
# - ps: "ls dist"
|
||||
|
||||
artifacts:
|
||||
# Archive the generated packages in the ci.appveyor.com build report.
|
||||
|
||||
@ -11,6 +11,9 @@ import sys
|
||||
assert (sys.version_info >= (3, 5)), 'Python 3.5+ is required to use Bonobo.'
|
||||
from bonobo._api import *
|
||||
from bonobo._api import __all__
|
||||
from bonobo._version import __version__
|
||||
|
||||
__all__ = ['__version__'] + __all__
|
||||
__version__ = __version__
|
||||
|
||||
__all__ = __all__
|
||||
del sys
|
||||
|
||||
150
bonobo/_api.py
150
bonobo/_api.py
@ -1,72 +1,52 @@
|
||||
from bonobo._version import __version__
|
||||
import warnings
|
||||
|
||||
__all__ = [
|
||||
'__version__',
|
||||
]
|
||||
|
||||
from bonobo.structs import Bag, Graph
|
||||
|
||||
__all__ += ['Bag', 'Graph']
|
||||
|
||||
# Filesystem. This is a shortcut from the excellent filesystem2 library, that we make available there for convenience.
|
||||
from fs import open_fs as _open_fs
|
||||
|
||||
open_fs = lambda url, *args, **kwargs: _open_fs(str(url), *args, **kwargs)
|
||||
__all__ += ['open_fs']
|
||||
|
||||
# Basic transformations.
|
||||
from bonobo.basics import *
|
||||
from bonobo.basics import __all__ as _all_basics
|
||||
|
||||
__all__ += _all_basics
|
||||
|
||||
# Execution strategies.
|
||||
from bonobo.basics import Limit, PrettyPrint, Tee, count, identity, noop, pprint
|
||||
from bonobo.strategies import create_strategy
|
||||
from bonobo.structs import Bag, Graph
|
||||
from bonobo.util.objects import get_name
|
||||
from bonobo.io import CsvReader, CsvWriter, FileReader, FileWriter, JsonReader, JsonWriter
|
||||
|
||||
__all__ += ['create_strategy']
|
||||
|
||||
# Extract and loads from stdlib.
|
||||
from bonobo.io import *
|
||||
from bonobo.io import __all__ as _all_io
|
||||
|
||||
__all__ += _all_io
|
||||
__all__ = []
|
||||
|
||||
|
||||
# XXX This may be belonging to the bonobo.examples package.
|
||||
def get_examples_path(*pathsegments):
|
||||
import os
|
||||
import pathlib
|
||||
return str(pathlib.Path(os.path.dirname(__file__), 'examples', *pathsegments))
|
||||
def register_api(x, __all__=__all__):
|
||||
__all__.append(get_name(x))
|
||||
return x
|
||||
|
||||
|
||||
def open_examples_fs(*pathsegments):
|
||||
return open_fs(get_examples_path(*pathsegments))
|
||||
|
||||
__all__.append(get_examples_path.__name__)
|
||||
__all__.append(open_examples_fs.__name__)
|
||||
def register_api_group(*args):
|
||||
for attr in args:
|
||||
register_api(attr)
|
||||
|
||||
|
||||
def _is_interactive_console():
|
||||
import sys
|
||||
return sys.stdout.isatty()
|
||||
|
||||
|
||||
def _is_jupyter_notebook():
|
||||
try:
|
||||
return get_ipython().__class__.__name__ == 'ZMQInteractiveShell'
|
||||
except NameError:
|
||||
return False
|
||||
|
||||
|
||||
# @api
|
||||
@register_api
|
||||
def run(graph, *chain, strategy=None, plugins=None, services=None):
|
||||
"""
|
||||
Main entry point of bonobo. It takes a graph and creates all the necessary plumbery around to execute it.
|
||||
|
||||
The only necessary argument is a :class:`Graph` instance, containing the logic you actually want to execute.
|
||||
|
||||
By default, this graph will be executed using the "threadpool" strategy: each graph node will be wrapped in a
|
||||
thread, and executed in a loop until there is no more input to this node.
|
||||
|
||||
You can provide plugins factory objects in the plugins list, this function will add the necessary plugins for
|
||||
interactive console execution and jupyter notebook execution if it detects correctly that it runs in this context.
|
||||
|
||||
You'll probably want to provide a services dictionary mapping service names to service instances.
|
||||
|
||||
:param Graph graph: The :class:`Graph` to execute.
|
||||
:param str strategy: The :class:`bonobo.strategies.base.Strategy` to use.
|
||||
:param list plugins: The list of plugins to enhance execution.
|
||||
:param dict services: The implementations of services this graph will use.
|
||||
:return bonobo.execution.graph.GraphExecutionContext:
|
||||
"""
|
||||
if len(chain):
|
||||
warnings.warn('DEPRECATED. You should pass a Graph instance instead of a chain.')
|
||||
from bonobo import Graph
|
||||
graph = Graph(graph, *chain)
|
||||
|
||||
strategy = create_strategy(strategy)
|
||||
plugins = []
|
||||
plugins = plugins or []
|
||||
|
||||
if _is_interactive_console():
|
||||
from bonobo.ext.console import ConsoleOutputPlugin
|
||||
@ -81,4 +61,66 @@ def run(graph, *chain, strategy=None, plugins=None, services=None):
|
||||
return strategy.execute(graph, plugins=plugins, services=services)
|
||||
|
||||
|
||||
__all__.append(run.__name__)
|
||||
# bonobo.structs
|
||||
register_api_group(Bag, Graph)
|
||||
|
||||
# bonobo.strategies
|
||||
register_api(create_strategy)
|
||||
|
||||
|
||||
# Shortcut to filesystem2's open_fs, that we make available there for convenience.
|
||||
@register_api
|
||||
def open_fs(fs_url, *args, **kwargs):
|
||||
"""
|
||||
Wraps :func:`fs.open_fs` function with a few candies.
|
||||
|
||||
:param str fs_url: A filesystem URL
|
||||
:param parse_result: A parsed filesystem URL.
|
||||
:type parse_result: :class:`ParseResult`
|
||||
:param bool writeable: True if the filesystem must be writeable.
|
||||
:param bool create: True if the filesystem should be created if it does not exist.
|
||||
:param str cwd: The current working directory (generally only relevant for OS filesystems).
|
||||
:param str default_protocol: The protocol to use if one is not supplied in the FS URL (defaults to ``"osfs"``).
|
||||
:returns: :class:`~fs.base.FS` object
|
||||
"""
|
||||
from fs import open_fs as _open_fs
|
||||
return _open_fs(str(fs_url), *args, **kwargs)
|
||||
|
||||
|
||||
# bonobo.basics
|
||||
register_api_group(
|
||||
Limit,
|
||||
PrettyPrint,
|
||||
Tee,
|
||||
count,
|
||||
identity,
|
||||
noop,
|
||||
pprint,
|
||||
)
|
||||
|
||||
# bonobo.io
|
||||
register_api_group(CsvReader, CsvWriter, FileReader, FileWriter, JsonReader, JsonWriter)
|
||||
|
||||
|
||||
def _is_interactive_console():
|
||||
import sys
|
||||
return sys.stdout.isatty()
|
||||
|
||||
|
||||
def _is_jupyter_notebook():
|
||||
try:
|
||||
return get_ipython().__class__.__name__ == 'ZMQInteractiveShell'
|
||||
except NameError:
|
||||
return False
|
||||
|
||||
|
||||
@register_api
|
||||
def get_examples_path(*pathsegments):
|
||||
import os
|
||||
import pathlib
|
||||
return str(pathlib.Path(os.path.dirname(__file__), 'examples', *pathsegments))
|
||||
|
||||
|
||||
@register_api
|
||||
def open_examples_fs(*pathsegments):
|
||||
return open_fs(get_examples_path(*pathsegments))
|
||||
|
||||
@ -4,7 +4,6 @@ from pprint import pprint as _pprint
|
||||
from colorama import Fore, Style
|
||||
|
||||
from bonobo.config.processors import contextual
|
||||
from bonobo.constants import NOT_MODIFIED
|
||||
from bonobo.structs.bags import Bag
|
||||
from bonobo.util.objects import ValueHolder
|
||||
from bonobo.util.term import CLEAR_EOL
|
||||
@ -25,6 +24,7 @@ def identity(x):
|
||||
|
||||
|
||||
def Limit(n=10):
|
||||
from bonobo.constants import NOT_MODIFIED
|
||||
i = 0
|
||||
|
||||
def _limit(*args, **kwargs):
|
||||
@ -38,6 +38,8 @@ def Limit(n=10):
|
||||
|
||||
|
||||
def Tee(f):
|
||||
from bonobo.constants import NOT_MODIFIED
|
||||
|
||||
@functools.wraps(f)
|
||||
def wrapped(*args, **kwargs):
|
||||
nonlocal f
|
||||
@ -63,6 +65,8 @@ pprint = Tee(_pprint)
|
||||
|
||||
|
||||
def PrettyPrint(title_keys=('title', 'name', 'id'), print_values=True, sort=True):
|
||||
from bonobo.constants import NOT_MODIFIED
|
||||
|
||||
def _pprint(*args, **kwargs):
|
||||
nonlocal title_keys, sort, print_values
|
||||
|
||||
@ -98,4 +102,5 @@ def PrettyPrint(title_keys=('title', 'name', 'id'), print_values=True, sort=True
|
||||
|
||||
|
||||
def noop(*args, **kwargs): # pylint: disable=unused-argument
|
||||
from bonobo.constants import NOT_MODIFIED
|
||||
return NOT_MODIFIED
|
||||
|
||||
@ -3,14 +3,12 @@ import os
|
||||
|
||||
def execute():
|
||||
try:
|
||||
import edgy.project
|
||||
from edgy.project.__main__ import handle_init
|
||||
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'))
|
||||
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ def get_default_services(filename, services=None):
|
||||
}
|
||||
try:
|
||||
exec(code, context)
|
||||
except Exception as exc:
|
||||
except Exception:
|
||||
raise
|
||||
return {
|
||||
**context[DEFAULT_SERVICES_ATTR](),
|
||||
@ -55,7 +55,7 @@ def execute(file, quiet=False):
|
||||
'but it is something that will be implemented in the future.\n\nExpected: 1, got: {}.'
|
||||
).format(len(graphs))
|
||||
|
||||
name, graph = list(graphs.items())[0]
|
||||
graph = list(graphs.values())[0]
|
||||
|
||||
# todo if console and not quiet, then add the console plugin
|
||||
# todo when better console plugin, add it if console and just disable display
|
||||
|
||||
@ -83,4 +83,4 @@ class Configurable(metaclass=ConfigurableMeta):
|
||||
)
|
||||
|
||||
for name, value in kwargs.items():
|
||||
setattr(self, name, kwargs[name])
|
||||
setattr(self, name, value)
|
||||
|
||||
@ -52,9 +52,9 @@ def contextual(cls_or_func):
|
||||
setattr(cls_or_func, _CONTEXT_PROCESSORS_ATTR, [])
|
||||
|
||||
_processors = getattr(cls_or_func, _CONTEXT_PROCESSORS_ATTR)
|
||||
for name, value in cls_or_func.__dict__.items():
|
||||
if isinstance(value, ContextProcessor):
|
||||
_processors.append(value)
|
||||
for processor in cls_or_func.__dict__.values():
|
||||
if isinstance(processor, ContextProcessor):
|
||||
_processors.append(processor)
|
||||
|
||||
# This is needed for python 3.5, python 3.6 should be fine, but it's considered an implementation detail.
|
||||
_processors.sort(key=lambda proc: proc._creation_counter)
|
||||
|
||||
4
bonobo/examples/.style.yapf
Normal file
4
bonobo/examples/.style.yapf
Normal file
@ -0,0 +1,4 @@
|
||||
[style]
|
||||
based_on_style = pep8
|
||||
column_limit = 74
|
||||
dedent_closing_brackets = true
|
||||
@ -1,3 +1,18 @@
|
||||
"""
|
||||
Extracts a list of parisian bars where you can buy a coffee for a reasonable price, and store them in a flat text file.
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
BEGIN -> "ODS()" -> "transform" -> "FileWriter()";
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
import bonobo
|
||||
from bonobo.commands.run import get_default_services
|
||||
from bonobo.ext.opendatasoft import OpenDataSoftAPI
|
||||
|
||||
@ -1,3 +1,19 @@
|
||||
"""
|
||||
Extracts a list of fablabs in the world, restrict to the ones in france, then format it both for a nice console output
|
||||
and a flat txt file.
|
||||
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
BEGIN -> "ODS()" -> "normalize" -> "filter_france" -> "Tee()" -> "JsonWriter()";
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from colorama import Fore, Style
|
||||
@ -9,7 +25,9 @@ from bonobo.ext.opendatasoft import OpenDataSoftAPI
|
||||
try:
|
||||
import pycountry
|
||||
except ImportError as exc:
|
||||
raise ImportError('You must install package "pycountry" to run this example.') from exc
|
||||
raise ImportError(
|
||||
'You must install package "pycountry" to run this example.'
|
||||
) from exc
|
||||
|
||||
API_DATASET = 'fablabs-in-the-world'
|
||||
API_NETLOC = 'datanova.laposte.fr'
|
||||
@ -41,20 +59,41 @@ def display(row):
|
||||
address = list(
|
||||
filter(
|
||||
None, (
|
||||
' '.join(filter(None, (row.get('postal_code', None), row.get('city', None)))), row.get('county', None),
|
||||
row.get('country'),
|
||||
' '.join(
|
||||
filter(
|
||||
None, (
|
||||
row.get('postal_code', None),
|
||||
row.get('city', None)
|
||||
)
|
||||
)
|
||||
), row.get('county', None), row.get('country'),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
print(' - {}address{}: {address}'.format(Fore.BLUE, Style.RESET_ALL, address=', '.join(address)))
|
||||
print(' - {}links{}: {links}'.format(Fore.BLUE, Style.RESET_ALL, links=', '.join(row['links'])))
|
||||
print(' - {}geometry{}: {geometry}'.format(Fore.BLUE, Style.RESET_ALL, **row))
|
||||
print(' - {}source{}: {source}'.format(Fore.BLUE, Style.RESET_ALL, source='datanova/' + API_DATASET))
|
||||
print(
|
||||
' - {}address{}: {address}'.
|
||||
format(Fore.BLUE, Style.RESET_ALL, address=', '.join(address))
|
||||
)
|
||||
print(
|
||||
' - {}links{}: {links}'.
|
||||
format(Fore.BLUE, Style.RESET_ALL, links=', '.join(row['links']))
|
||||
)
|
||||
print(
|
||||
' - {}geometry{}: {geometry}'.
|
||||
format(Fore.BLUE, Style.RESET_ALL, **row)
|
||||
)
|
||||
print(
|
||||
' - {}source{}: {source}'.format(
|
||||
Fore.BLUE, Style.RESET_ALL, source='datanova/' + API_DATASET
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
graph = bonobo.Graph(
|
||||
OpenDataSoftAPI(dataset=API_DATASET, netloc=API_NETLOC, timezone='Europe/Paris'),
|
||||
OpenDataSoftAPI(
|
||||
dataset=API_DATASET, netloc=API_NETLOC, timezone='Europe/Paris'
|
||||
),
|
||||
normalize,
|
||||
filter_france,
|
||||
bonobo.Tee(display),
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
[
|
||||
{"city": "Lannion", "kind_name": "fab_lab", "links": ["http://fablab-lannion.org"], "capabilities": "three_d_printing;cnc_milling;circuit_production", "url": "https://www.fablabs.io/labs/fablablannion", "coordinates": [48.7317261, -3.4509764], "name": "Fablab Lannion - KerNEL", "phone": "+33 2 96 37 84 46", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/13/27/c6c015ba-26c6-4620-833f-8441123a4afc/Fablab Lannion - KerNEL.jpg", "postal_code": "22300", "longitude": -3.45097639999994, "country_code": "fr", "latitude": 48.7317261, "address_notes": "Use the small portal", "email": "contact@fablab-lannion.org", "address_1": "14 Rue de Beauchamp", "geometry": {"type": "Point", "coordinates": [-3.4509764, 48.7317261]}, "country": "France"},
|
||||
[{"city": "Lannion", "kind_name": "fab_lab", "links": ["http://fablab-lannion.org"], "capabilities": "three_d_printing;cnc_milling;circuit_production", "url": "https://www.fablabs.io/labs/fablablannion", "coordinates": [48.7317261, -3.4509764], "name": "Fablab Lannion - KerNEL", "phone": "+33 2 96 37 84 46", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/13/27/c6c015ba-26c6-4620-833f-8441123a4afc/Fablab Lannion - KerNEL.jpg", "postal_code": "22300", "longitude": -3.45097639999994, "country_code": "fr", "latitude": 48.7317261, "address_notes": "Use the small portal", "email": "contact@fablab-lannion.org", "address_1": "14 Rue de Beauchamp", "geometry": {"type": "Point", "coordinates": [-3.4509764, 48.7317261]}, "country": "France"},
|
||||
{"city": "Villeneuve-d'Ascq", "kind_name": "fab_lab", "links": ["http://www.flickr.com/photos/fablablille/", "https://twitter.com/FabLab_Lille", "http://www.fablablille.fr"], "url": "https://www.fablabs.io/labs/fablablille", "coordinates": [50.642869867, 3.1386641], "county": "Nord-Pas-de-Calais", "phone": "+33 9 72 29 47 65", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/10/34/147c88ca-2acd-42a4-aeb0-17b2dc830903/FabLab Lille.jpg", "postal_code": "59650", "longitude": 3.13866410000003, "country_code": "fr", "latitude": 50.6428698670218, "address_1": "2 All\u00e9e Lakanal", "name": "FabLab Lille", "geometry": {"type": "Point", "coordinates": [3.1386641, 50.642869867]}, "country": "France"},
|
||||
{"city": "Dijon", "name": "L'abscisse", "links": ["http://fablab.coagul.org"], "url": "https://www.fablabs.io/labs/lab6", "longitude": 5.04147999999998, "county": "France", "parent_id": 545, "kind_name": "mini_fab_lab", "postal_code": "2100", "coordinates": [47.322047, 5.04148], "address_2": "6, impasse Quentin", "latitude": 47.322047, "country_code": "fr", "email": "c-bureau@outils.coagul.org", "address_1": "Dijon", "geometry": {"type": "Point", "coordinates": [5.04148, 47.322047]}, "country": "France"},
|
||||
{"city": "Montreuil", "kind_name": "fab_lab", "links": ["http://www.apedec.org ", "http://webtv.montreuil.fr/festival-m.u.s.i.c-et-fablab-video-415-12.html", "http://www.wedemain.fr/A-Montreuil-un-fab-lab-circulaire-dans-une-usine-verticale_a421.html"], "capabilities": "three_d_printing", "url": "https://www.fablabs.io/labs/ecodesignfablab", "name": "ECODESIGN FAB LAB", "email": "contact@apedec.org", "coordinates": [48.8693157, 2.4564764], "header_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/13/51/53/74898eb4-e94d-49fc-9e57-18246d1901c8/ECODESIGN FAB LAB.jpg", "phone": "+33 1 (0)9.81.29.17.31", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/15/13/33b98c6f-b6c1-4cfd-8b63-401c4441f964/ECODESIGN FAB LAB.jpg", "postal_code": "93106", "longitude": 2.45647640000004, "country_code": "fr", "latitude": 48.8693157, "address_1": "Montreuil", "address_notes": "lot 38 D", "address_2": "2 \u00e0 20 avenue Allende, MOZINOR", "blurb": "FAB LAB specialized in upcycling and ecodesign with furniture production based on diverted source of industrial waste, located in a industrial zone, in the heart of a popular city.", "description": "Based on the roof of an industrial zone of 50 SMEs (and 500 workers), Ecodesign Fab Lab is now open to address upcycling and eco-innovation, thanks waste collection, designers and classical wood equipment, but also 3D printers (and CNC equipment in the next weeks).", "geometry": {"type": "Point", "coordinates": [2.4564764, 48.8693157]}, "country": "France"},
|
||||
@ -133,5 +132,4 @@
|
||||
{"city": "Bron", "kind_name": "fab_lab", "links": ["http://fablab-lyon.fr"], "capabilities": "three_d_printing;cnc_milling;laser;vinyl_cutting", "url": "https://www.fablabs.io/labs/fabriquedobjetslibres", "name": "Fabrique d'Objets Libres", "email": "contact@fabriquedobjetslibres.fr", "coordinates": [45.7429334, 4.9082135], "header_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/13/50/01/0190e790-aaec-4f2f-9985-11156655145d/Fabrique d'Objets Libres.jpg", "county": "Rh\u00f4ne", "phone": "+33 7 68 01 40 26 (Tue-Sat 2pm-6pm)", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/13/49/73ea9f2d-0216-4f52-a6bf-2ff97ee474b2/Fabrique d'Objets Libres.jpg", "postal_code": "69500", "longitude": 4.90821349999999, "country_code": "fr", "latitude": 45.7429334, "address_1": "All\u00e9e Gaillard Romanet", "address_notes": "Au sous-sol de la MJC. Downstairs inside the MJC.", "address_2": "MJC Louis Aragon", "blurb": "Le fablab lyonnais, install\u00e9 \u00e0 la MJC Louis Aragon de Bron, ouvert tous les mercredis et formation hebdomadaire de fabrication num\u00e9rique. Projets autour du handicap, des arts et du recyclage.", "description": "La Fabrique d'Objets Libres est un fablab associatif sur Lyon et sa r\u00e9gion. Install\u00e9 \u00e0 la MJC Louis Aragon de Bron depuis janvier 2013, c'est un espace de cr\u00e9ation et de fabrication num\u00e9rique ouvert \u00e0 tous, qui permet \u00e0 chacun de d\u00e9couvrir, d'inventer et de fabriquer tout type d'objet.\r\n \r\nV\u00e9ritable laboratoire citoyen de fabrication, la Fabrique d\u2019Objets Libres met \u00e0 disposition de ses adh\u00e9rents des outils \u00e0 commande num\u00e9rique et des mati\u00e8res premi\u00e8res secondaires permettant de concevoir et de fabriquer localement des objets libres.\r\nC\u2019est une plate-forme pluridisciplinaire collaborative qui m\u00eale les profils (techniciens, informaticiens, ing\u00e9nieurs, scientifiques, bricoleurs, cr\u00e9ateurs...) et les g\u00e9n\u00e9rations afin de r\u00e9unir tous types de comp\u00e9tences.\r\n\r\nLe fablab est ouvert tous les mercredis pour les \"temps libres\", durant lesquels les adh\u00e9rents utilisent les machines librement. Par ailleurs, il propose un atelier hebdomadaire aux adh\u00e9rents de la MJC, \"De l'id\u00e9e \u00e0 l'objet\": en une dizaine de s\u00e9ances sur un trimestre, les participants apprennent \u00e0 utiliser toutes les machines du fablab pour r\u00e9aliser leurs objets, et r\u00e9fl\u00e9chissent autour d'une th\u00e9matique sociale comme le handicap, la musique, ou la ville.\r\n\r\nL'association organise \u00e9galement des \u00e9v\u00e9nements et ateliers th\u00e9matiques utilisant la fabrication num\u00e9rique autour de sujet plus vastes, comme l'art, avec les machines \u00e0 dessiner, ou le handicap, dans le cadre du projet Handilab, ou encore la fin de vie des objets, avec le Laboratoire de l'Obsolescence D\u00e9programm\u00e9e. Enfin, le fablab s'associe \u00e0 d'autres associations et des entreprises pour des projets communs.", "geometry": {"type": "Point", "coordinates": [4.9082135, 45.7429334]}, "country": "France"},
|
||||
{"city": "N\u00e9ons-sur-Creuse", "kind_name": "fab_lab", "links": ["http://www.rurallab.org"], "url": "https://www.fablabs.io/labs/rurallab", "coordinates": [46.744746, 0.931698], "header_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/13/49/00/95c7b9f2-a034-4b2b-931d-43ced33ddfb1/RuralLab.jpg", "phone": "+33603318810", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/12/49/ec5f7c54-e6ce-40fd-b5c5-c4142d208e6b/RuralLab.jpg", "postal_code": "36220", "longitude": 0.931697999999983, "country_code": "fr", "latitude": 46.744746, "address_1": "Rue de l'\u00c9cole", "email": "rurallab36@gmail.com", "blurb": "A FabLab in the countryside in Neons sur Creuse, France", "name": "RuralLab", "geometry": {"type": "Point", "coordinates": [0.931698, 46.744746]}, "country": "France"},
|
||||
{"city": "Gif-sur-Yvette", "kind_name": "supernode", "links": ["http://fablab.digiscope.fr/#!/", "http://fablabdigiscope.wordpress.com"], "url": "https://www.fablabs.io/labs/fablabdigiscope", "name": "(Fab)Lab Digiscope", "longitude": 2.16830979999997, "header_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/13/52/18/8d63351d-c2fb-4a90-8e58-bb45422202a6/(Fab)Lab Digiscope.jpg", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/15/46/51553da4-b295-426c-837f-934c311933ba/(Fab)Lab Digiscope.jpg", "postal_code": "91190", "coordinates": [48.7117632, 2.1683098], "country_code": "fr", "latitude": 48.7117632, "address_1": "660 Rue Noetzlin", "capabilities": "three_d_printing;cnc_milling;circuit_production;laser;precision_milling;vinyl_cutting", "email": "fablabdigiscope@gmail.com", "blurb": "(FAB)LAB DIGISCOPE is a fabrication laboratory dedicated to research in sciences | design | education | art | engineering and what ever field of research you come from. Open to Everyone. Book now!", "description": "(FAB)LAB DIGISCOPE is a fabrication laboratory dedicated to research in sciences | design | education | arts | engineering and what ever field of research you come from. We host Fab Academy and Bio Academy. We host Digital Fabrication Classes for EITC Master. Open to Everyone since the beginning.\r\n\r\nFablab Digiscope started in 2013 when Aviz-INRIA research team director Jean-Daniel Fekete and colleague researcher Pierre Dragicevic hired Romain Di Vozzo as a R&D Engineer to be the fablab manager of what would later become an attractive place on the new Campus Paris-Saclay. Fablab Digiscope is part of the Digiscope Project, a network of 10 high-performance platforms for interactive visualization of large datasets and complex computation for which Michel Beaudouin-Lafon is the scientific Director. Fablab Digiscope is mutualised between 10 institutions involved in research and education.\r\n\r\nRomain Di Vozzo runs and develops Fablab Digiscope everyday, trains publics, designs objects, shares creative thoughts, gives advices on designs, etc. Romain also actively collaborates to the globally distributed fablab network and with the Fab Foundation by operating as Fab Academy SuperNode, as Instructor for Fab Academy and Bio Academy, by giving conferences and workshops in France and abroad and by performing very small tasks that make the fablab network grow.", "geometry": {"type": "Point", "coordinates": [2.1683098, 48.7117632]}, "country": "France"},
|
||||
{"city": "Metz", "kind_name": "fab_lab", "links": ["http://graoulab.org/wiki", "http://graoulab.org"], "url": "https://www.fablabs.io/labs/graoulab", "coordinates": [49.1262692, 6.182086], "name": "GraouLab", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/18/24/af4709d8-1f60-48a7-ba35-4c42ef40a195/GraouLab.jpg", "postal_code": "57000", "longitude": 6.18208600000003, "country_code": "fr", "latitude": 49.1262692, "capabilities": "three_d_printing;laser", "email": "contact@graoulab.org", "blurb": "The FabLab of Metz. A place for folks innovation.", "address_1": "7 Avenue de Blida", "geometry": {"type": "Point", "coordinates": [6.182086, 49.1262692]}, "country": "France"}
|
||||
]
|
||||
{"city": "Metz", "kind_name": "fab_lab", "links": ["http://graoulab.org/wiki", "http://graoulab.org"], "url": "https://www.fablabs.io/labs/graoulab", "coordinates": [49.1262692, 6.182086], "name": "GraouLab", "avatar_url": "http://fablabs.io.s3.amazonaws.com/2017/01/28/11/18/24/af4709d8-1f60-48a7-ba35-4c42ef40a195/GraouLab.jpg", "postal_code": "57000", "longitude": 6.18208600000003, "country_code": "fr", "latitude": 49.1262692, "capabilities": "three_d_printing;laser", "email": "contact@graoulab.org", "blurb": "The FabLab of Metz. A place for folks innovation.", "address_1": "7 Avenue de Blida", "geometry": {"type": "Point", "coordinates": [6.182086, 49.1262692]}, "country": "France"}]
|
||||
@ -1,9 +1,11 @@
|
||||
import bonobo
|
||||
from bonobo.commands.run import get_default_services
|
||||
|
||||
|
||||
def get_fields(row):
|
||||
return row['fields']
|
||||
|
||||
|
||||
graph = bonobo.Graph(
|
||||
bonobo.JsonReader(path='datasets/theaters.json'),
|
||||
get_fields,
|
||||
|
||||
@ -6,6 +6,6 @@ graph = bonobo.Graph(
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
bonobo.run(graph, services={
|
||||
'fs': bonobo.open_examples_fs('datasets')
|
||||
})
|
||||
bonobo.run(
|
||||
graph, services={'fs': bonobo.open_examples_fs('datasets')}
|
||||
)
|
||||
|
||||
@ -12,6 +12,6 @@ graph = bonobo.Graph(
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
bonobo.run(graph, services={
|
||||
'fs': bonobo.open_examples_fs('datasets')
|
||||
})
|
||||
bonobo.run(
|
||||
graph, services={'fs': bonobo.open_examples_fs('datasets')}
|
||||
)
|
||||
|
||||
@ -22,6 +22,6 @@ graph = bonobo.Graph(
|
||||
)
|
||||
|
||||
if __name__ == '__main__':
|
||||
bonobo.run(graph, services={
|
||||
'fs': bonobo.open_examples_fs('datasets')
|
||||
})
|
||||
bonobo.run(
|
||||
graph, services={'fs': bonobo.open_examples_fs('datasets')}
|
||||
)
|
||||
|
||||
@ -1,7 +1,21 @@
|
||||
import bonobo
|
||||
import bonobo.basics
|
||||
"""
|
||||
Simple example of :func:`bonobo.count` usage.
|
||||
|
||||
graph = bonobo.Graph(range(42), bonobo.basics.count, print)
|
||||
.. graphviz::
|
||||
|
||||
digraph {
|
||||
rankdir = LR;
|
||||
stylesheet = "../_static/graphs.css";
|
||||
|
||||
BEGIN [shape="point"];
|
||||
BEGIN -> "range()" -> "count" -> "print";
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
import bonobo
|
||||
|
||||
graph = bonobo.Graph(range(42), bonobo.count, print)
|
||||
|
||||
if __name__ == '__main__':
|
||||
bonobo.run(graph)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import sys
|
||||
import traceback
|
||||
from time import sleep
|
||||
|
||||
from bonobo.config import Container
|
||||
from bonobo.config.processors import resolve_processors
|
||||
from bonobo.util.errors import print_error
|
||||
from bonobo.util.iterators import ensure_tuple
|
||||
from bonobo.util.objects import Wrapper
|
||||
|
||||
@ -43,16 +43,13 @@ class LoopingExecutionContext(Wrapper):
|
||||
False), ('{}.start() can only be called on a new node.').format(type(self).__name__)
|
||||
assert self._context is None
|
||||
self._started = True
|
||||
try:
|
||||
if self.parent:
|
||||
self._context = self.parent.services.args_for(self.wrapped)
|
||||
elif self.services:
|
||||
self._context = self.services.args_for(self.wrapped)
|
||||
else:
|
||||
self._context = ()
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
self.handle_error(exc, traceback.format_exc())
|
||||
raise
|
||||
|
||||
if self.parent:
|
||||
self._context = self.parent.services.args_for(self.wrapped)
|
||||
elif self.services:
|
||||
self._context = self.services.args_for(self.wrapped)
|
||||
else:
|
||||
self._context = ()
|
||||
|
||||
for processor in resolve_processors(self.wrapped):
|
||||
try:
|
||||
@ -80,41 +77,22 @@ class LoopingExecutionContext(Wrapper):
|
||||
if self._stopped:
|
||||
return
|
||||
|
||||
assert self._context is not None
|
||||
|
||||
self._stopped = True
|
||||
while len(self._stack):
|
||||
processor = self._stack.pop()
|
||||
try:
|
||||
# todo yield from ? how to ?
|
||||
next(processor)
|
||||
except StopIteration as exc:
|
||||
# This is normal, and wanted.
|
||||
pass
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
self.handle_error(exc, traceback.format_exc())
|
||||
raise
|
||||
else:
|
||||
# No error ? We should have had StopIteration ...
|
||||
raise RuntimeError('Context processors should not yield more than once.')
|
||||
if self._context is not None:
|
||||
while len(self._stack):
|
||||
processor = self._stack.pop()
|
||||
try:
|
||||
# todo yield from ? how to ?
|
||||
next(processor)
|
||||
except StopIteration as exc:
|
||||
# This is normal, and wanted.
|
||||
pass
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
self.handle_error(exc, traceback.format_exc())
|
||||
raise
|
||||
else:
|
||||
# No error ? We should have had StopIteration ...
|
||||
raise RuntimeError('Context processors should not yield more than once.')
|
||||
|
||||
def handle_error(self, exc, trace):
|
||||
"""
|
||||
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.
|
||||
|
||||
:param exc: the culprit
|
||||
:param trace: Hercule Poirot's logbook.
|
||||
:return: to hell
|
||||
"""
|
||||
|
||||
from colorama import Fore, Style
|
||||
print(
|
||||
Style.BRIGHT,
|
||||
Fore.RED,
|
||||
'\U0001F4A3 {} in {}'.format(type(exc).__name__, self.wrapped),
|
||||
Style.RESET_ALL,
|
||||
sep='',
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(trace)
|
||||
return print_error(exc, trace, context=self.wrapped)
|
||||
|
||||
@ -25,15 +25,15 @@ class GraphExecutionContext:
|
||||
self.plugins = [PluginExecutionContext(plugin, parent=self) for plugin in plugins or ()]
|
||||
self.services = Container(services) if services else Container()
|
||||
|
||||
for i, component_context in enumerate(self):
|
||||
for i, node_context in enumerate(self):
|
||||
try:
|
||||
component_context.outputs = [self[j].input for j in self.graph.outputs_of(i)]
|
||||
node_context.outputs = [self[j].input for j in self.graph.outputs_of(i)]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
component_context.input.on_begin = partial(component_context.send, BEGIN, _control=True)
|
||||
component_context.input.on_end = partial(component_context.send, END, _control=True)
|
||||
component_context.input.on_finalize = partial(component_context.stop)
|
||||
node_context.input.on_begin = partial(node_context.send, BEGIN, _control=True)
|
||||
node_context.input.on_end = partial(node_context.send, END, _control=True)
|
||||
node_context.input.on_finalize = partial(node_context.stop)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.nodes[item]
|
||||
|
||||
@ -7,7 +7,8 @@ from bonobo.core.inputs import Input
|
||||
from bonobo.core.statistics import WithStatistics
|
||||
from bonobo.errors import InactiveReadableError
|
||||
from bonobo.execution.base import LoopingExecutionContext
|
||||
from bonobo.structs.bags import Bag, ErrorBag
|
||||
from bonobo.structs.bags import Bag
|
||||
from bonobo.util.errors import is_error
|
||||
from bonobo.util.iterators import iter_if_not_sequence
|
||||
|
||||
|
||||
@ -32,7 +33,13 @@ class NodeExecutionContext(WithStatistics, LoopingExecutionContext):
|
||||
return (('+' if self.alive else '-') + ' ' + self.__name__ + ' ' + self.get_statistics_as_string()).strip()
|
||||
|
||||
def __repr__(self):
|
||||
return '<' + self.__str__() + '>'
|
||||
stats = self.get_statistics_as_string().strip()
|
||||
return '<{}({}{}){}>'.format(
|
||||
type(self).__name__,
|
||||
'+' if self.alive else '',
|
||||
self.__name__,
|
||||
(' ' + stats) if stats else '',
|
||||
)
|
||||
|
||||
def recv(self, *messages):
|
||||
"""
|
||||
@ -116,10 +123,6 @@ class NodeExecutionContext(WithStatistics, LoopingExecutionContext):
|
||||
self.push(_resolve(input_bag, result))
|
||||
|
||||
|
||||
def is_error(bag):
|
||||
return isinstance(bag, ErrorBag)
|
||||
|
||||
|
||||
def _resolve(input_bag, output):
|
||||
# NotModified means to send the input unmodified to output.
|
||||
if output is NOT_MODIFIED:
|
||||
|
||||
@ -14,10 +14,6 @@ from edgy.project.feature import Feature, SUPPORT_PRIORITY
|
||||
class BonoboFeature(Feature):
|
||||
requires = {'python'}
|
||||
|
||||
@subscribe('edgy.project.feature.make.on_generate', priority=SUPPORT_PRIORITY)
|
||||
def on_make_generate(self, event):
|
||||
makefile = event.makefile
|
||||
|
||||
@subscribe('edgy.project.on_start', priority=SUPPORT_PRIORITY)
|
||||
def on_start(self, event):
|
||||
package_path = event.setup['name'].replace('.', os.sep)
|
||||
|
||||
@ -22,13 +22,3 @@ class JupyterOutputPlugin(Plugin):
|
||||
self.widget.value = [repr(node) for node in self.context.parent.nodes]
|
||||
|
||||
finalize = run
|
||||
|
||||
|
||||
"""
|
||||
TODO JUPYTER WIDGET
|
||||
###################
|
||||
|
||||
# close the widget? what does it do?
|
||||
https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Basics.html#Closing-widgets
|
||||
|
||||
"""
|
||||
|
||||
@ -50,7 +50,7 @@ class CsvReader(CsvHandler, FileReader):
|
||||
field_count = len(headers.value)
|
||||
|
||||
if self.skip and self.skip > 0:
|
||||
for i in range(0, self.skip):
|
||||
for _ in range(0, self.skip):
|
||||
next(reader)
|
||||
|
||||
for row in reader:
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from concurrent.futures import Executor, ProcessPoolExecutor, ThreadPoolExecutor
|
||||
|
||||
from bonobo.constants import BEGIN, END
|
||||
from bonobo.strategies.base import Strategy
|
||||
from bonobo.structs.bags import Bag
|
||||
from bonobo.util.errors import print_error
|
||||
|
||||
|
||||
class ExecutorStrategy(Strategy):
|
||||
@ -29,18 +31,32 @@ class ExecutorStrategy(Strategy):
|
||||
for plugin_context in context.plugins:
|
||||
|
||||
def _runner(plugin_context=plugin_context):
|
||||
plugin_context.start()
|
||||
plugin_context.loop()
|
||||
plugin_context.stop()
|
||||
try:
|
||||
plugin_context.start()
|
||||
plugin_context.loop()
|
||||
plugin_context.stop()
|
||||
except Exception as exc:
|
||||
print_error(exc, traceback.format_exc(), prefix='Error in plugin context', context=plugin_context)
|
||||
|
||||
futures.append(executor.submit(_runner))
|
||||
|
||||
for node_context in context.nodes:
|
||||
|
||||
def _runner(node_context=node_context):
|
||||
node_context.start()
|
||||
node_context.loop()
|
||||
node_context.stop()
|
||||
try:
|
||||
node_context.start()
|
||||
except Exception as exc:
|
||||
print_error(
|
||||
exc, traceback.format_exc(), prefix='Could not start node context', context=node_context
|
||||
)
|
||||
node_context.input.on_end()
|
||||
else:
|
||||
node_context.loop()
|
||||
|
||||
try:
|
||||
node_context.stop()
|
||||
except Exception as exc:
|
||||
print_error(exc, traceback.format_exc(), prefix='Could not stop node context', context=node_context)
|
||||
|
||||
futures.append(executor.submit(_runner))
|
||||
|
||||
|
||||
@ -9,6 +9,30 @@ __all__ = [
|
||||
|
||||
|
||||
class Bag:
|
||||
"""
|
||||
Bags are simple datastructures that holds arguments and keyword arguments together, that may be applied to a
|
||||
callable.
|
||||
|
||||
Example:
|
||||
|
||||
>>> from bonobo import Bag
|
||||
>>> def myfunc(foo, *, bar):
|
||||
... print(foo, bar)
|
||||
...
|
||||
>>> bag = Bag('foo', bar='baz')
|
||||
>>> bag.apply(myfunc)
|
||||
foo baz
|
||||
|
||||
A bag can inherit another bag, allowing to override only a few arguments without touching the parent.
|
||||
|
||||
Example:
|
||||
|
||||
>>> bag2 = Bag(bar='notbaz', _parent=bag)
|
||||
>>> bag2.apply(myfunc)
|
||||
foo notbaz
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *args, _flags=None, _parent=None, **kwargs):
|
||||
self._flags = _flags or ()
|
||||
self._parent = _parent
|
||||
|
||||
31
bonobo/util/errors.py
Normal file
31
bonobo/util/errors.py
Normal file
@ -0,0 +1,31 @@
|
||||
import sys
|
||||
|
||||
from bonobo.structs.bags import ErrorBag
|
||||
|
||||
|
||||
def is_error(bag):
|
||||
return isinstance(bag, ErrorBag)
|
||||
|
||||
|
||||
def print_error(exc, trace, context=None, prefix=''):
|
||||
"""
|
||||
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.
|
||||
|
||||
:param exc: the culprit
|
||||
:param trace: Hercule Poirot's logbook.
|
||||
:return: to hell
|
||||
"""
|
||||
|
||||
from colorama import Fore, Style
|
||||
print(
|
||||
Style.BRIGHT,
|
||||
Fore.RED,
|
||||
'\U0001F4A3 {}{}{}'.format(
|
||||
(prefix + ': ') if prefix else '', type(exc).__name__, ' in {!r}'.format(context) if context else ''
|
||||
),
|
||||
Style.RESET_ALL,
|
||||
sep='',
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(trace)
|
||||
2
docs/_static/graphs.css
vendored
2
docs/_static/graphs.css
vendored
@ -1,2 +1,2 @@
|
||||
.node {
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,21 @@
|
||||
Contributing
|
||||
============
|
||||
|
||||
There's a lot of different ways you can contribute, and not all of them includes coding. Do not think that the codeless
|
||||
contributions have less value, all contributions are very important.
|
||||
|
||||
* You can contribute to the documentation.
|
||||
* You can help reproducing errors and giving more infos in the issues.
|
||||
* You can open issues with problems you're facing.
|
||||
* You can help creating better presentation material.
|
||||
* You can talk about it in your local python user group.
|
||||
* You can enhance examples.
|
||||
* You can enhance tests.
|
||||
* etc.
|
||||
|
||||
Code-related contributions (including tests and examples)
|
||||
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
||||
|
||||
Contributing to bonobo is usually done this way:
|
||||
|
||||
* Discuss ideas in the `issue tracker <https://github.com/python-bonobo/bonobo>`_ or on `Slack <https://bonobo-slack.herokuapp.com/>`_.
|
||||
|
||||
@ -22,5 +22,6 @@ available as an optional extra dependency, and the maturity stage of each extens
|
||||
:maxdepth: 2
|
||||
|
||||
ext/docker
|
||||
ext/jupyter
|
||||
ext/selenium
|
||||
ext/sqlalchemy
|
||||
|
||||
@ -1,54 +1,13 @@
|
||||
Public API
|
||||
Bonobo API
|
||||
==========
|
||||
|
||||
All the "public api" callables, classes and other callables are available under the root :mod:`bonobo` package, even if
|
||||
they are documented within their sub-namespace, for convenience.
|
||||
The Bonobo API, available directly under the :mod:`bonobo` package, contains all the tools you need to get started with
|
||||
bonobo.
|
||||
|
||||
The :mod:`bonobo` package
|
||||
:::::::::::::::::::::::::
|
||||
|
||||
.. automodule:: bonobo
|
||||
:members: create_strategy, get_examples_path, run
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Config
|
||||
------
|
||||
|
||||
.. automodule:: bonobo.config
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Context
|
||||
-------
|
||||
|
||||
.. automodule:: bonobo.context
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Core
|
||||
----
|
||||
|
||||
.. automodule:: bonobo.core
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
IO
|
||||
--
|
||||
|
||||
.. automodule:: bonobo.io
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Util
|
||||
----
|
||||
|
||||
.. automodule:: bonobo.util
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
10
docs/reference/api_config.rst
Normal file
10
docs/reference/api_config.rst
Normal file
@ -0,0 +1,10 @@
|
||||
Config API
|
||||
==========
|
||||
|
||||
The Config API, located under the :mod:`bonobo.config` namespace, contains all the tools you need to create
|
||||
configurable transformations, either class-based or function-based.
|
||||
|
||||
.. automodule:: bonobo.config
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@ -1,5 +1,5 @@
|
||||
Commands Reference
|
||||
==================
|
||||
Command-line
|
||||
============
|
||||
|
||||
Bonobo Init
|
||||
:::::::::::
|
||||
|
||||
@ -1,13 +1,44 @@
|
||||
Examples
|
||||
========
|
||||
|
||||
There are a few examples bundled with **bonobo**. You'll find them under the :mod:`bonobo.examples` package.
|
||||
There are a few examples bundled with **bonobo**. You'll find them under the :mod:`bonobo.examples` package, and
|
||||
you can try them in a clone of bonobo by typing::
|
||||
|
||||
$ bonobo run bonobo/examples/.../file.py
|
||||
|
||||
|
||||
Datasets
|
||||
::::::::
|
||||
|
||||
|
||||
.. module:: bonobo.examples.datasets
|
||||
|
||||
The :mod:`bonobo.examples.datasets` package contains examples that generates datasets locally for other examples to
|
||||
use. As of today, we commit the content of those datasets to git, even if that may be a bad idea, so all the examples
|
||||
are easily runnable. Later, we'll see if we favor a "missing dependency exception" approach.
|
||||
|
||||
|
||||
Coffeeshops
|
||||
-----------
|
||||
|
||||
.. automodule:: bonobo.examples.datasets.coffeeshops
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Fablabs
|
||||
-------
|
||||
|
||||
.. automodule:: bonobo.examples.datasets.fablabs
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Types
|
||||
:::::
|
||||
|
||||
bonobo.examples.types.strings
|
||||
-----------------------------
|
||||
Strings
|
||||
-------
|
||||
|
||||
.. automodule:: bonobo.examples.types.strings
|
||||
:members: graph, extract, transform, load
|
||||
@ -15,8 +46,8 @@ bonobo.examples.types.strings
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
bonobo.examples.types.dicts
|
||||
---------------------------
|
||||
Dicts
|
||||
-----
|
||||
|
||||
.. automodule:: bonobo.examples.types.dicts
|
||||
:members: graph, extract, transform, load
|
||||
@ -24,8 +55,8 @@ bonobo.examples.types.dicts
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
bonobo.examples.types.bags
|
||||
--------------------------
|
||||
Bags
|
||||
----
|
||||
|
||||
.. automodule:: bonobo.examples.types.bags
|
||||
:members: graph, extract, transform, load
|
||||
@ -33,4 +64,15 @@ bonobo.examples.types.bags
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Utils
|
||||
:::::
|
||||
|
||||
Count
|
||||
-----
|
||||
|
||||
.. automodule:: bonobo.examples.utils.count
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
References
|
||||
==========
|
||||
|
||||
.. todo:: write the fucking doc!
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
commands
|
||||
api
|
||||
api_config
|
||||
commands
|
||||
examples
|
||||
|
||||
@ -10,7 +10,7 @@ We strongly advice that even if you're an advanced python developper, you go thr
|
||||
reasons: that should be sufficient to do anything possible with **Bonobo** and that's a good moment to learn the few
|
||||
concepts you'll see everywhere in the software.
|
||||
|
||||
If you're not familiar with python, you should first read :doc:`./python`.
|
||||
If you're not familiar with python, you should first read :doc:`python`.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
@ -6,16 +6,6 @@ Just enough Python for Bonobo
|
||||
This is a work in progress and it is not yet available. Please come back later or even better, help us write this
|
||||
guide!
|
||||
|
||||
This guide is intended to help programmers or enthusiasts to grasp the python basics necessary to use Bonobo. It should
|
||||
definately not be considered as a general python introduction, neither a deep dive into details.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
python01
|
||||
python02
|
||||
python03
|
||||
python04
|
||||
python05
|
||||
|
||||
This guide is intended to help programmers or enthusiasts to grasp the python basics necessary to use Bonobo. It
|
||||
should definately not be considered as a general python introduction, neither a deep dive into details.
|
||||
|
||||
|
||||
91
setup.py
91
setup.py
@ -8,59 +8,74 @@ root_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
tolines = lambda c: list(filter(None, map(lambda s: s.strip(), c.split('\n'))))
|
||||
|
||||
|
||||
def read(filename, flt=None):
|
||||
try:
|
||||
with open(filename) as f:
|
||||
with open(filename, 'rt') as f:
|
||||
content = f.read().strip()
|
||||
return flt(content) if callable(flt) else content
|
||||
except FileNotFoundError:
|
||||
except EnvironmentError:
|
||||
# File not found? Let's say it's empty.
|
||||
return ''
|
||||
except UnicodeError:
|
||||
# Problem decoding the file? Let's not stop on this (but it's a temp fix).
|
||||
return ''
|
||||
|
||||
|
||||
# Py3 compatibility hacks, borrowed from IPython.
|
||||
try:
|
||||
execfile
|
||||
except NameError:
|
||||
|
||||
def execfile(fname, globs, locs=None):
|
||||
locs = locs or globs
|
||||
exec(compile(open(fname).read(), fname, "exec"), globs, locs)
|
||||
|
||||
|
||||
version_ns = {}
|
||||
execfile(os.path.join(root_dir, 'bonobo/_version.py'), version_ns)
|
||||
version = version_ns.get('__version__', 'dev')
|
||||
try:
|
||||
execfile(os.path.join(root_dir, 'bonobo/_version.py'), version_ns)
|
||||
except EnvironmentError:
|
||||
version = 'dev'
|
||||
else:
|
||||
version = version_ns.get('__version__', 'dev')
|
||||
|
||||
setup(
|
||||
name = 'bonobo',
|
||||
description = 'Bonobo',
|
||||
license = 'Apache License, Version 2.0',
|
||||
install_requires = ['colorama >=0.3,<1.0',
|
||||
'fs >=2.0,<3.0',
|
||||
'psutil >=5.2,<6.0',
|
||||
'requests >=2.0,<3.0',
|
||||
'stevedore >=1.21,<2.0'],
|
||||
version = version,
|
||||
long_description = read('README.rst'),
|
||||
classifiers = read('classifiers.txt', tolines),
|
||||
packages = find_packages(exclude=['ez_setup', 'example', 'test']),
|
||||
include_package_data = True,
|
||||
data_files = [('share/jupyter/nbextensions/bonobo-jupyter',
|
||||
['bonobo/ext/jupyter/static/extension.js',
|
||||
'bonobo/ext/jupyter/static/index.js',
|
||||
'bonobo/ext/jupyter/static/index.js.map'])],
|
||||
extras_require = {'dev': ['coverage >=4,<5',
|
||||
'pylint >=1,<2',
|
||||
'pytest >=3,<4',
|
||||
'pytest-cov >=2,<3',
|
||||
'pytest-timeout >=1,<2',
|
||||
'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',
|
||||
'version = bonobo.commands.version:register'],
|
||||
'console_scripts': ['bonobo = bonobo.commands:entrypoint'],
|
||||
'edgy.project.features': ['bonobo = '
|
||||
'bonobo.ext.edgy.project.feature:BonoboFeature']},
|
||||
url = 'https://www.bonobo-project.org/',
|
||||
download_url = 'https://github.com/python-bonobo/bonobo/tarball/{version}'.format(version=version),
|
||||
name='bonobo',
|
||||
description='Bonobo',
|
||||
license='Apache License, Version 2.0',
|
||||
install_requires=[
|
||||
'colorama >=0.3,<1.0', 'fs >=2.0,<3.0', 'psutil >=5.2,<6.0', 'requests >=2.0,<3.0', 'stevedore >=1.21,<2.0'
|
||||
],
|
||||
version=version,
|
||||
long_description=read('README.rst'),
|
||||
classifiers=read('classifiers.txt', tolines),
|
||||
packages=find_packages(exclude=['ez_setup', 'example', 'test']),
|
||||
include_package_data=True,
|
||||
data_files=[
|
||||
(
|
||||
'share/jupyter/nbextensions/bonobo-jupyter', [
|
||||
'bonobo/ext/jupyter/static/extension.js', 'bonobo/ext/jupyter/static/index.js',
|
||||
'bonobo/ext/jupyter/static/index.js.map'
|
||||
]
|
||||
)
|
||||
],
|
||||
extras_require={
|
||||
'dev': [
|
||||
'coverage >=4,<5', 'pylint >=1,<2', 'pytest >=3,<4', 'pytest-cov >=2,<3', 'pytest-timeout >=1,<2', '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',
|
||||
'version = bonobo.commands.version:register'
|
||||
],
|
||||
'console_scripts': ['bonobo = bonobo.commands:entrypoint'],
|
||||
'edgy.project.features': ['bonobo = '
|
||||
'bonobo.ext.edgy.project.feature:BonoboFeature']
|
||||
},
|
||||
url='https://www.bonobo-project.org/',
|
||||
download_url='https://github.com/python-bonobo/bonobo/tarball/{version}'.format(version=version),
|
||||
)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from mock import patch
|
||||
from unittest.mock import patch
|
||||
|
||||
from bonobo.ext.opendatasoft import OpenDataSoftAPI
|
||||
from bonobo.util.objects import ValueHolder
|
||||
|
||||
@ -21,7 +21,7 @@ def test_file_writer_in_context(tmpdir, lines, output):
|
||||
|
||||
context.start()
|
||||
context.recv(BEGIN, *map(Bag, lines), END)
|
||||
for i in range(len(lines)):
|
||||
for _ in range(len(lines)):
|
||||
context.step()
|
||||
context.stop()
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
from mock import Mock
|
||||
from unittest.mock import Mock
|
||||
|
||||
from bonobo import Bag
|
||||
from bonobo.constants import INHERIT_INPUT
|
||||
|
||||
@ -18,7 +18,7 @@ def test_entrypoint():
|
||||
def test_no_command(capsys):
|
||||
with pytest.raises(SystemExit):
|
||||
entrypoint([])
|
||||
out, err = capsys.readouterr()
|
||||
_, err = capsys.readouterr()
|
||||
assert 'error: the following arguments are required: command' in err
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user