From b2f93b24166f5ee4d5dd6e7e2f27fab5b0da5ee7 Mon Sep 17 00:00:00 2001 From: Romain Dorgueil Date: Thu, 5 Oct 2017 07:17:16 +0200 Subject: [PATCH] [config/dx] bundle a default fs (and http?) service if none is provided (#179) --- bonobo/_api.py | 4 ++-- bonobo/config/__init__.py | 3 ++- bonobo/config/configurables.py | 3 +-- bonobo/config/services.py | 24 ++++++++++++++++++++++++ bonobo/constants.py | 2 +- bonobo/execution/base.py | 4 ++-- bonobo/execution/graph.py | 4 ++-- bonobo/nodes/basics.py | 3 ++- bonobo/util/__init__.py | 11 ++++++++++- bonobo/util/inspect.py | 2 +- tests/config/test_services.py | 23 ++++++++++++++++++++++- tests/test_basics.py | 1 - tests/test_commands.py | 1 - 13 files changed, 69 insertions(+), 16 deletions(-) diff --git a/bonobo/_api.py b/bonobo/_api.py index de75bd2..84b5e19 100644 --- a/bonobo/_api.py +++ b/bonobo/_api.py @@ -1,10 +1,10 @@ import logging -from bonobo.structs import Bag, ErrorBag, Graph, Token from bonobo.nodes import CsvReader, CsvWriter, FileReader, FileWriter, Filter, JsonReader, JsonWriter, Limit, \ PickleReader, PickleWriter, PrettyPrinter, RateLimited, Tee, arg0_to_kwargs, count, identity, kwargs_to_arg0, noop from bonobo.strategies import create_strategy -from bonobo.util.objects import get_name +from bonobo.structs import Bag, ErrorBag, Graph, Token +from bonobo.util import get_name __all__ = [] diff --git a/bonobo/config/__init__.py b/bonobo/config/__init__.py index bd00845..a86e8ba 100644 --- a/bonobo/config/__init__.py +++ b/bonobo/config/__init__.py @@ -1,7 +1,7 @@ from bonobo.config.configurables import Configurable from bonobo.config.options import Method, Option from bonobo.config.processors import ContextProcessor -from bonobo.config.services import Container, Exclusive, Service, requires +from bonobo.config.services import Container, Exclusive, Service, requires, create_container use = requires @@ -14,6 +14,7 @@ __all__ = [ 'Method', 'Option', 'Service', + 'create_container', 'requires', 'use', ] diff --git a/bonobo/config/configurables.py b/bonobo/config/configurables.py index 7b40303..1b0201f 100644 --- a/bonobo/config/configurables.py +++ b/bonobo/config/configurables.py @@ -1,6 +1,5 @@ -from bonobo.util.inspect import isoption, iscontextprocessor +from bonobo.util import isoption, iscontextprocessor, sortedlist from bonobo.errors import AbstractError -from bonobo.util.collections import sortedlist __all__ = [ 'Configurable', diff --git a/bonobo/config/services.py b/bonobo/config/services.py index 1fe066d..1810ebc 100644 --- a/bonobo/config/services.py +++ b/bonobo/config/services.py @@ -95,6 +95,30 @@ class Container(dict): return value +def create_container(services=None, factory=Container): + """ + Create a container with reasonable default service implementations for commonly use, standard-named, services. + + Services: + - `fs` defaults to a fs2 instance based on current working directory + - `http`defaults to requests + + :param services: + :return: + """ + container = factory(services) if services else factory() + + if not 'fs' in container: + import bonobo + container.setdefault('fs', bonobo.open_fs()) + + if not 'http' in container: + import requests + container.setdefault('http', requests) + + return container + + class Exclusive(ContextDecorator): """ Decorator and context manager used to require exclusive usage of an object, most probably a service. It's usefull diff --git a/bonobo/constants.py b/bonobo/constants.py index 4a02f5e..8c6eba5 100644 --- a/bonobo/constants.py +++ b/bonobo/constants.py @@ -6,4 +6,4 @@ INHERIT_INPUT = Token('InheritInput') LOOPBACK = Token('Loopback') NOT_MODIFIED = Token('NotModified') DEFAULT_SERVICES_FILENAME = '_services.py' -DEFAULT_SERVICES_ATTR = 'get_services' \ No newline at end of file +DEFAULT_SERVICES_ATTR = 'get_services' diff --git a/bonobo/execution/base.py b/bonobo/execution/base.py index 641d761..abb3516 100644 --- a/bonobo/execution/base.py +++ b/bonobo/execution/base.py @@ -2,7 +2,7 @@ import traceback from contextlib import contextmanager from time import sleep -from bonobo.config import Container +from bonobo.config import create_container from bonobo.config.processors import ContextCurrifier from bonobo.plugins import get_enhancers from bonobo.util.errors import print_error @@ -48,7 +48,7 @@ class LoopingExecutionContext(Wrapper): raise RuntimeError( 'Having services defined both in GraphExecutionContext and child NodeExecutionContext is not supported, for now.' ) - self.services = Container(services) if services else Container() + self.services = create_container(services) else: self.services = None diff --git a/bonobo/execution/graph.py b/bonobo/execution/graph.py index 1859adc..91e4aef 100644 --- a/bonobo/execution/graph.py +++ b/bonobo/execution/graph.py @@ -1,6 +1,6 @@ from functools import partial -from bonobo.config.services import Container +from bonobo.config import create_container from bonobo.constants import BEGIN, END from bonobo.execution.node import NodeExecutionContext from bonobo.execution.plugin import PluginExecutionContext @@ -23,7 +23,7 @@ class GraphExecutionContext: self.graph = graph self.nodes = [NodeExecutionContext(node, parent=self) for node in self.graph] self.plugins = [PluginExecutionContext(plugin, parent=self) for plugin in plugins or ()] - self.services = Container(services) if services else Container() + self.services = create_container(services) # Probably not a good idea to use it unless you really know what you're doing. But you can access the context. self.services['__graph_context'] = self diff --git a/bonobo/nodes/basics.py b/bonobo/nodes/basics.py index ea05c29..e23dd05 100644 --- a/bonobo/nodes/basics.py +++ b/bonobo/nodes/basics.py @@ -4,11 +4,12 @@ import itertools from bonobo import settings from bonobo.config import Configurable, Option from bonobo.config.processors import ContextProcessor -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 +from bonobo.constants import NOT_MODIFIED + __all__ = [ 'Limit', 'PrettyPrinter', diff --git a/bonobo/util/__init__.py b/bonobo/util/__init__.py index 4a5e8dc..682cbe7 100644 --- a/bonobo/util/__init__.py +++ b/bonobo/util/__init__.py @@ -1,3 +1,4 @@ +from bonobo.util.collections import sortedlist from bonobo.util.inspect import ( inspect_node, isbag, @@ -10,11 +11,18 @@ from bonobo.util.inspect import ( isoption, istype, ) +from bonobo.util.objects import ( + get_name, + get_attribute_or_create, + ValueHolder +) from bonobo.util.python import require # Bonobo's util API __all__ = [ - 'require', + 'ValueHolder', + 'get_attribute_or_create', + 'get_name', 'inspect_node', 'isbag', 'isconfigurable', @@ -25,4 +33,5 @@ __all__ = [ 'ismethod', 'isoption', 'istype', + 'require', ] diff --git a/bonobo/util/inspect.py b/bonobo/util/inspect.py index 1594d1e..2a19803 100644 --- a/bonobo/util/inspect.py +++ b/bonobo/util/inspect.py @@ -1,6 +1,5 @@ from collections import namedtuple -from bonobo.constants import LOOPBACK def isconfigurable(mixed): @@ -99,6 +98,7 @@ def isloopbackbag(mixed): :param mixed: :return: bool """ + from bonobo.constants import LOOPBACK return isbag(mixed) and LOOPBACK in mixed.flags diff --git a/tests/config/test_services.py b/tests/config/test_services.py index 66f554e..fb74098 100644 --- a/tests/config/test_services.py +++ b/tests/config/test_services.py @@ -3,8 +3,9 @@ import time import pytest +from bonobo.util import get_name from bonobo.config import Configurable, Container, Exclusive, Service, requires -from bonobo.config.services import validate_service_name +from bonobo.config.services import validate_service_name, create_container class PrinterInterface(): @@ -108,3 +109,23 @@ def test_requires(): svcargs = services.args_for(append) assert len(svcargs) == 1 assert svcargs[0] == vcr.append + + +@pytest.mark.parametrize('services', [None, {}]) +def test_create_container_empty_values(services): + c = create_container(services) + assert len(c) == 2 + assert 'fs' in c and get_name(c['fs']) == 'OSFS' + assert 'http' in c and get_name(c['http']) == 'requests' + + +def test_create_container_override(): + c = create_container({ + 'http': 'http', + 'fs': 'fs', + }) + assert len(c) == 2 + assert 'fs' in c and c['fs'] == 'fs' + assert 'http' in c and c['http'] == 'http' + + diff --git a/tests/test_basics.py b/tests/test_basics.py index 5230b0b..283e3d7 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -5,7 +5,6 @@ import pytest import bonobo from bonobo.config.processors import ContextCurrifier from bonobo.constants import NOT_MODIFIED -from bonobo.util.inspect import inspect_node def test_count(): diff --git a/tests/test_commands.py b/tests/test_commands.py index 1fca75a..a29465c 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -3,7 +3,6 @@ import runpy import sys from unittest.mock import patch -import pathlib import pkg_resources import pytest