Refactoring and fixes around ioformats.
This commit is contained in:
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
|||||||
# This file has been auto-generated.
|
# This file has been auto-generated.
|
||||||
# All changes will be lost, see Projectfile.
|
# All changes will be lost, see Projectfile.
|
||||||
#
|
#
|
||||||
# Updated at 2017-06-05 09:21:02.910936
|
# Updated at 2017-06-08 21:45:05.840502
|
||||||
|
|
||||||
PACKAGE ?= bonobo
|
PACKAGE ?= bonobo
|
||||||
PYTHON ?= $(shell which python)
|
PYTHON ?= $(shell which python)
|
||||||
|
|||||||
@ -44,8 +44,9 @@ python.add_requirements(
|
|||||||
'requests >=2.0,<3.0',
|
'requests >=2.0,<3.0',
|
||||||
'stevedore >=1.21,<2.0',
|
'stevedore >=1.21,<2.0',
|
||||||
dev=[
|
dev=[
|
||||||
'pytest-timeout >=1,<2',
|
|
||||||
'cookiecutter >=1.5,<1.6',
|
'cookiecutter >=1.5,<1.6',
|
||||||
|
'pytest-sugar >=0.8,<0.9',
|
||||||
|
'pytest-timeout >=1,<2',
|
||||||
],
|
],
|
||||||
docker=[
|
docker=[
|
||||||
'bonobo-docker',
|
'bonobo-docker',
|
||||||
|
|||||||
@ -6,6 +6,7 @@ DEFAULT_SERVICES_ATTR = 'get_services'
|
|||||||
DEFAULT_GRAPH_FILENAME = '__main__.py'
|
DEFAULT_GRAPH_FILENAME = '__main__.py'
|
||||||
DEFAULT_GRAPH_ATTR = 'get_graph'
|
DEFAULT_GRAPH_ATTR = 'get_graph'
|
||||||
|
|
||||||
|
|
||||||
def get_default_services(filename, services=None):
|
def get_default_services(filename, services=None):
|
||||||
dirname = os.path.dirname(filename)
|
dirname = os.path.dirname(filename)
|
||||||
services_filename = os.path.join(dirname, DEFAULT_SERVICES_FILENAME)
|
services_filename = os.path.join(dirname, DEFAULT_SERVICES_FILENAME)
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import bonobo
|
|||||||
from bonobo.commands.run import get_default_services
|
from bonobo.commands.run import get_default_services
|
||||||
|
|
||||||
graph = bonobo.Graph(
|
graph = bonobo.Graph(
|
||||||
bonobo.CsvReader('datasets/coffeeshops.txt', headers=('item',)),
|
bonobo.CsvReader('datasets/coffeeshops.txt', headers=('item', )),
|
||||||
bonobo.PrettyPrinter(),
|
bonobo.PrettyPrinter(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -52,12 +52,7 @@ graph = bonobo.Graph(
|
|||||||
|
|
||||||
|
|
||||||
def get_services():
|
def get_services():
|
||||||
return {
|
return {'fs': TarFS(bonobo.get_examples_path('datasets/spam.tgz'))}
|
||||||
'fs':
|
|
||||||
TarFS(
|
|
||||||
bonobo.get_examples_path('datasets/spam.tgz')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import bonobo
|
import bonobo
|
||||||
|
|
||||||
graph = bonobo.Graph(
|
graph = bonobo.Graph(
|
||||||
['foo', 'bar', 'baz', ],
|
[
|
||||||
|
'foo',
|
||||||
|
'bar',
|
||||||
|
'baz',
|
||||||
|
],
|
||||||
str.upper,
|
str.upper,
|
||||||
print,
|
print,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -59,7 +59,7 @@ class CsvReader(CsvHandler, FileReader):
|
|||||||
|
|
||||||
for row in reader:
|
for row in reader:
|
||||||
if len(row) != field_count:
|
if len(row) != field_count:
|
||||||
raise ValueError('Got a line with %d fields, expecting %d.' % (len(row), field_count,))
|
raise ValueError('Got a line with %d fields, expecting %d.' % (len(row), field_count, ))
|
||||||
|
|
||||||
yield self.get_output(dict(zip(_headers, row)))
|
yield self.get_output(dict(zip(_headers, row)))
|
||||||
|
|
||||||
|
|||||||
@ -21,10 +21,8 @@ class FileHandler(Configurable):
|
|||||||
eol = Option(str, default='\n') # type: str
|
eol = Option(str, default='\n') # type: str
|
||||||
mode = Option(str) # type: str
|
mode = Option(str) # type: str
|
||||||
encoding = Option(str, default='utf-8') # type: str
|
encoding = Option(str, default='utf-8') # type: str
|
||||||
|
|
||||||
fs = Service('fs') # type: str
|
fs = Service('fs') # type: str
|
||||||
|
ioformat = Option(default=settings.IOFORMAT.get)
|
||||||
ioformat = Option(settings.validate_io_format, default=settings.IOFORMAT)
|
|
||||||
|
|
||||||
@ContextProcessor
|
@ContextProcessor
|
||||||
def file(self, context, fs):
|
def file(self, context, fs):
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from bonobo.errors import ValidationError
|
from bonobo.errors import ValidationError
|
||||||
|
|
||||||
@ -13,6 +12,36 @@ def to_bool(s):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class Setting:
|
||||||
|
def __init__(self, name, default=None, validator=None):
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if default:
|
||||||
|
self.default = default if callable(default) else lambda: default
|
||||||
|
else:
|
||||||
|
self.default = lambda: None
|
||||||
|
|
||||||
|
if validator:
|
||||||
|
self.validator = validator
|
||||||
|
else:
|
||||||
|
self.validator = None
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Setting {}={!r}>'.format(self.name, self.value)
|
||||||
|
|
||||||
|
def set(self, value):
|
||||||
|
if self.validator and not self.validator(value):
|
||||||
|
raise ValidationError('Invalid value {!r} for setting {}.'.format(value, self.name))
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
try:
|
||||||
|
return self.value
|
||||||
|
except AttributeError:
|
||||||
|
self.value = self.default()
|
||||||
|
return self.value
|
||||||
|
|
||||||
|
|
||||||
# Debug/verbose mode.
|
# Debug/verbose mode.
|
||||||
DEBUG = to_bool(os.environ.get('DEBUG', 'f'))
|
DEBUG = to_bool(os.environ.get('DEBUG', 'f'))
|
||||||
|
|
||||||
@ -34,21 +63,9 @@ IOFORMATS = {
|
|||||||
IOFORMAT_KWARGS,
|
IOFORMAT_KWARGS,
|
||||||
}
|
}
|
||||||
|
|
||||||
IOFORMAT = os.environ.get('IOFORMAT', IOFORMAT_KWARGS)
|
IOFORMAT = Setting('IOFORMAT', default=IOFORMAT_KWARGS, validator=IOFORMATS.__contains__)
|
||||||
|
|
||||||
|
|
||||||
def validate_io_format(v):
|
|
||||||
if callable(v):
|
|
||||||
return v
|
|
||||||
if v in IOFORMATS:
|
|
||||||
return v
|
|
||||||
raise ValidationError('Unsupported format {!r}.'.format(v))
|
|
||||||
|
|
||||||
|
|
||||||
def check():
|
def check():
|
||||||
if DEBUG and QUIET:
|
if DEBUG and QUIET:
|
||||||
raise RuntimeError('I cannot be verbose and quiet at the same time.')
|
raise RuntimeError('I cannot be verbose and quiet at the same time.')
|
||||||
|
|
||||||
if IOFORMAT not in IOFORMATS:
|
|
||||||
raise RuntimeError('Invalid default input/output format.')
|
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ def force_iterator(mixed):
|
|||||||
def ensure_tuple(tuple_or_mixed):
|
def ensure_tuple(tuple_or_mixed):
|
||||||
if isinstance(tuple_or_mixed, tuple):
|
if isinstance(tuple_or_mixed, tuple):
|
||||||
return tuple_or_mixed
|
return tuple_or_mixed
|
||||||
return (tuple_or_mixed,)
|
return (tuple_or_mixed, )
|
||||||
|
|
||||||
|
|
||||||
def tuplize(generator):
|
def tuplize(generator):
|
||||||
|
|||||||
@ -4,7 +4,7 @@ arrow==0.10.0
|
|||||||
babel==2.4.0
|
babel==2.4.0
|
||||||
binaryornot==0.4.3
|
binaryornot==0.4.3
|
||||||
certifi==2017.4.17
|
certifi==2017.4.17
|
||||||
chardet==3.0.3
|
chardet==3.0.4
|
||||||
click==6.7
|
click==6.7
|
||||||
cookiecutter==1.5.1
|
cookiecutter==1.5.1
|
||||||
coverage==4.4.1
|
coverage==4.4.1
|
||||||
@ -19,6 +19,7 @@ poyo==0.4.1
|
|||||||
py==1.4.34
|
py==1.4.34
|
||||||
pygments==2.2.0
|
pygments==2.2.0
|
||||||
pytest-cov==2.5.1
|
pytest-cov==2.5.1
|
||||||
|
pytest-sugar==0.8.0
|
||||||
pytest-timeout==1.2.0
|
pytest-timeout==1.2.0
|
||||||
pytest==3.1.1
|
pytest==3.1.1
|
||||||
python-dateutil==2.6.0
|
python-dateutil==2.6.0
|
||||||
@ -28,5 +29,6 @@ six==1.10.0
|
|||||||
snowballstemmer==1.2.1
|
snowballstemmer==1.2.1
|
||||||
sphinx==1.6.2
|
sphinx==1.6.2
|
||||||
sphinxcontrib-websupport==1.0.1
|
sphinxcontrib-websupport==1.0.1
|
||||||
|
termcolor==1.1.0
|
||||||
urllib3==1.21.1
|
urllib3==1.21.1
|
||||||
whichcraft==0.4.1
|
whichcraft==0.4.1
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
-e .[docker]
|
-e .[docker]
|
||||||
appdirs==1.4.3
|
appdirs==1.4.3
|
||||||
bonobo-docker==0.2.4
|
bonobo-docker==0.2.5
|
||||||
bonobo==0.3.1
|
bonobo==0.3.1
|
||||||
certifi==2017.4.17
|
certifi==2017.4.17
|
||||||
chardet==3.0.3
|
chardet==3.0.4
|
||||||
colorama==0.3.9
|
colorama==0.3.9
|
||||||
docker-pycreds==0.2.1
|
docker-pycreds==0.2.1
|
||||||
docker==2.3.0
|
docker==2.3.0
|
||||||
@ -17,6 +17,6 @@ pyparsing==2.2.0
|
|||||||
pytz==2017.2
|
pytz==2017.2
|
||||||
requests==2.17.3
|
requests==2.17.3
|
||||||
six==1.10.0
|
six==1.10.0
|
||||||
stevedore==1.22.0
|
stevedore==1.23.0
|
||||||
urllib3==1.21.1
|
urllib3==1.21.1
|
||||||
websocket-client==0.40.0
|
websocket-client==0.40.0
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
appnope==0.1.0
|
appnope==0.1.0
|
||||||
bleach==2.0.0
|
bleach==2.0.0
|
||||||
decorator==4.0.11
|
decorator==4.0.11
|
||||||
entrypoints==0.2.2
|
entrypoints==0.2.3
|
||||||
html5lib==0.999999999
|
html5lib==0.999999999
|
||||||
ipykernel==4.6.1
|
ipykernel==4.6.1
|
||||||
ipython-genutils==0.2.0
|
ipython-genutils==0.2.0
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
-e .
|
-e .
|
||||||
appdirs==1.4.3
|
appdirs==1.4.3
|
||||||
certifi==2017.4.17
|
certifi==2017.4.17
|
||||||
chardet==3.0.3
|
chardet==3.0.4
|
||||||
colorama==0.3.9
|
colorama==0.3.9
|
||||||
enum34==1.1.6
|
enum34==1.1.6
|
||||||
fs==2.0.3
|
fs==2.0.3
|
||||||
@ -13,5 +13,5 @@ pyparsing==2.2.0
|
|||||||
pytz==2017.2
|
pytz==2017.2
|
||||||
requests==2.17.3
|
requests==2.17.3
|
||||||
six==1.10.0
|
six==1.10.0
|
||||||
stevedore==1.22.0
|
stevedore==1.23.0
|
||||||
urllib3==1.21.1
|
urllib3==1.21.1
|
||||||
|
|||||||
3
setup.py
3
setup.py
@ -59,7 +59,8 @@ setup(
|
|||||||
extras_require={
|
extras_require={
|
||||||
'dev': [
|
'dev': [
|
||||||
'cookiecutter (>= 1.5, < 1.6)', 'coverage (>= 4.4, < 5.0)', 'pytest (>= 3.1, < 4.0)',
|
'cookiecutter (>= 1.5, < 1.6)', 'coverage (>= 4.4, < 5.0)', 'pytest (>= 3.1, < 4.0)',
|
||||||
'pytest-cov (>= 2.5, < 3.0)', 'pytest-timeout (>= 1, < 2)', 'sphinx (>= 1.6, < 2.0)'
|
'pytest-cov (>= 2.5, < 3.0)', 'pytest-sugar (>= 0.8, < 0.9)', 'pytest-timeout (>= 1, < 2)',
|
||||||
|
'sphinx (>= 1.6, < 2.0)'
|
||||||
],
|
],
|
||||||
'docker': ['bonobo-docker'],
|
'docker': ['bonobo-docker'],
|
||||||
'jupyter': ['ipywidgets (>= 6.0.0, < 7)', 'jupyter (>= 1.0, < 1.1)']
|
'jupyter': ['ipywidgets (>= 6.0.0, < 7)', 'jupyter (>= 1.0, < 1.1)']
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from bonobo import Bag, CsvReader, CsvWriter, open_fs
|
from bonobo import Bag, CsvReader, CsvWriter, open_fs, settings
|
||||||
from bonobo.constants import BEGIN, END
|
from bonobo.constants import BEGIN, END
|
||||||
from bonobo.execution.node import NodeExecutionContext
|
from bonobo.execution.node import NodeExecutionContext
|
||||||
from bonobo.util.testing import CapturingNodeExecutionContext
|
from bonobo.util.testing import CapturingNodeExecutionContext
|
||||||
@ -9,7 +9,7 @@ from bonobo.util.testing import CapturingNodeExecutionContext
|
|||||||
def test_write_csv_to_file(tmpdir):
|
def test_write_csv_to_file(tmpdir):
|
||||||
fs, filename = open_fs(tmpdir), 'output.csv'
|
fs, filename = open_fs(tmpdir), 'output.csv'
|
||||||
|
|
||||||
writer = CsvWriter(path=filename)
|
writer = CsvWriter(path=filename, ioformat=settings.IOFORMAT_ARG0)
|
||||||
context = NodeExecutionContext(writer, services={'fs': fs})
|
context = NodeExecutionContext(writer, services={'fs': fs})
|
||||||
|
|
||||||
context.write(BEGIN, Bag({'foo': 'bar'}), Bag({'foo': 'baz', 'ignore': 'this'}), END)
|
context.write(BEGIN, Bag({'foo': 'bar'}), Bag({'foo': 'baz', 'ignore': 'this'}), END)
|
||||||
@ -19,7 +19,7 @@ def test_write_csv_to_file(tmpdir):
|
|||||||
context.step()
|
context.step()
|
||||||
context.stop()
|
context.stop()
|
||||||
|
|
||||||
with fs.open(filename)as fp:
|
with fs.open(filename) as fp:
|
||||||
assert fp.read() == 'foo\nbar\nbaz\n'
|
assert fp.read() == 'foo\nbar\nbaz\n'
|
||||||
|
|
||||||
with pytest.raises(AttributeError):
|
with pytest.raises(AttributeError):
|
||||||
@ -31,7 +31,7 @@ def test_read_csv_from_file(tmpdir):
|
|||||||
with fs.open(filename, 'w') as fp:
|
with fs.open(filename, 'w') as fp:
|
||||||
fp.write('a,b,c\na foo,b foo,c foo\na bar,b bar,c bar')
|
fp.write('a,b,c\na foo,b foo,c foo\na bar,b bar,c bar')
|
||||||
|
|
||||||
reader = CsvReader(path=filename, delimiter=',')
|
reader = CsvReader(path=filename, delimiter=',', ioformat=settings.IOFORMAT_ARG0)
|
||||||
|
|
||||||
context = CapturingNodeExecutionContext(reader, services={'fs': fs})
|
context = CapturingNodeExecutionContext(reader, services={'fs': fs})
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ def test_read_csv_kwargs_output_formater(tmpdir):
|
|||||||
with fs.open(filename, 'w') as fp:
|
with fs.open(filename, 'w') as fp:
|
||||||
fp.write('a,b,c\na foo,b foo,c foo\na bar,b bar,c bar')
|
fp.write('a,b,c\na foo,b foo,c foo\na bar,b bar,c bar')
|
||||||
|
|
||||||
reader = CsvReader(path=filename, delimiter=',', output_format='kwargs')
|
reader = CsvReader(path=filename, delimiter=',')
|
||||||
|
|
||||||
context = CapturingNodeExecutionContext(reader, services={'fs': fs})
|
context = CapturingNodeExecutionContext(reader, services={'fs': fs})
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from bonobo import Bag, JsonReader, JsonWriter, open_fs
|
from bonobo import Bag, JsonReader, JsonWriter, open_fs, settings
|
||||||
from bonobo.constants import BEGIN, END
|
from bonobo.constants import BEGIN, END
|
||||||
from bonobo.execution.node import NodeExecutionContext
|
from bonobo.execution.node import NodeExecutionContext
|
||||||
from bonobo.util.testing import CapturingNodeExecutionContext
|
from bonobo.util.testing import CapturingNodeExecutionContext
|
||||||
@ -9,7 +9,7 @@ from bonobo.util.testing import CapturingNodeExecutionContext
|
|||||||
def test_write_json_to_file(tmpdir):
|
def test_write_json_to_file(tmpdir):
|
||||||
fs, filename = open_fs(tmpdir), 'output.json'
|
fs, filename = open_fs(tmpdir), 'output.json'
|
||||||
|
|
||||||
writer = JsonWriter(path=filename)
|
writer = JsonWriter(filename, ioformat=settings.IOFORMAT_ARG0)
|
||||||
context = NodeExecutionContext(writer, services={'fs': fs})
|
context = NodeExecutionContext(writer, services={'fs': fs})
|
||||||
|
|
||||||
context.start()
|
context.start()
|
||||||
@ -31,7 +31,7 @@ def test_read_json_from_file(tmpdir):
|
|||||||
fs, filename = open_fs(tmpdir), 'input.json'
|
fs, filename = open_fs(tmpdir), 'input.json'
|
||||||
with fs.open(filename, 'w') as fp:
|
with fs.open(filename, 'w') as fp:
|
||||||
fp.write('[{"x": "foo"},{"x": "bar"}]')
|
fp.write('[{"x": "foo"},{"x": "bar"}]')
|
||||||
reader = JsonReader(path=filename)
|
reader = JsonReader(filename, ioformat=settings.IOFORMAT_ARG0)
|
||||||
|
|
||||||
context = CapturingNodeExecutionContext(reader, services={'fs': fs})
|
context = CapturingNodeExecutionContext(reader, services={'fs': fs})
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from bonobo import Bag, PickleReader, PickleWriter, open_fs
|
from bonobo import Bag, PickleReader, PickleWriter, open_fs, settings
|
||||||
from bonobo.constants import BEGIN, END
|
from bonobo.constants import BEGIN, END
|
||||||
from bonobo.execution.node import NodeExecutionContext
|
from bonobo.execution.node import NodeExecutionContext
|
||||||
from bonobo.util.testing import CapturingNodeExecutionContext
|
from bonobo.util.testing import CapturingNodeExecutionContext
|
||||||
@ -10,7 +11,7 @@ from bonobo.util.testing import CapturingNodeExecutionContext
|
|||||||
def test_write_pickled_dict_to_file(tmpdir):
|
def test_write_pickled_dict_to_file(tmpdir):
|
||||||
fs, filename = open_fs(tmpdir), 'output.pkl'
|
fs, filename = open_fs(tmpdir), 'output.pkl'
|
||||||
|
|
||||||
writer = PickleWriter(path=filename)
|
writer = PickleWriter(filename, ioformat=settings.IOFORMAT_ARG0)
|
||||||
context = NodeExecutionContext(writer, services={'fs': fs})
|
context = NodeExecutionContext(writer, services={'fs': fs})
|
||||||
|
|
||||||
context.write(BEGIN, Bag({'foo': 'bar'}), Bag({'foo': 'baz', 'ignore': 'this'}), END)
|
context.write(BEGIN, Bag({'foo': 'bar'}), Bag({'foo': 'baz', 'ignore': 'this'}), END)
|
||||||
@ -32,7 +33,7 @@ def test_read_pickled_list_from_file(tmpdir):
|
|||||||
with fs.open(filename, 'wb') as fp:
|
with fs.open(filename, 'wb') as fp:
|
||||||
fp.write(pickle.dumps([['a', 'b', 'c'], ['a foo', 'b foo', 'c foo'], ['a bar', 'b bar', 'c bar']]))
|
fp.write(pickle.dumps([['a', 'b', 'c'], ['a foo', 'b foo', 'c foo'], ['a bar', 'b bar', 'c bar']]))
|
||||||
|
|
||||||
reader = PickleReader(path=filename)
|
reader = PickleReader(filename, ioformat=settings.IOFORMAT_ARG0)
|
||||||
|
|
||||||
context = CapturingNodeExecutionContext(reader, services={'fs': fs})
|
context = CapturingNodeExecutionContext(reader, services={'fs': fs})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user