feat: new alternate syntax and switch to black + isort (yeah, maybe not the best time, but that is done).

This commit is contained in:
Romain Dorgueil
2018-07-29 18:21:56 +01:00
parent 3094e43f9f
commit 89dda0dca6
123 changed files with 1672 additions and 1640 deletions

View File

@ -6,20 +6,14 @@ from bonobo.util.testing import all_runners
def test_entrypoint():
commands = {}
for command in pkg_resources.iter_entry_points('bonobo.commands'):
for command in pkg_resources.iter_entry_points("bonobo.commands"):
commands[command.name] = command
assert not {
'convert',
'init',
'inspect',
'run',
'version',
}.difference(set(commands))
assert not {"convert", "init", "inspect", "run", "version"}.difference(set(commands))
@all_runners
def test_no_command(runner):
_, err, exc = runner(catch_errors=True)
assert type(exc) == SystemExit
assert 'error: the following arguments are required: command' in err
assert "error: the following arguments are required: command" in err

View File

@ -8,10 +8,10 @@ from bonobo.util.testing import all_runners
@all_runners
def test_convert(runner, tmpdir):
csv_content = 'id;name\n1;Romain'
tmpdir.join('in.csv').write(csv_content)
csv_content = "id;name\n1;Romain"
tmpdir.join("in.csv").write(csv_content)
with change_working_directory(tmpdir):
runner('convert', 'in.csv', 'out.csv')
runner("convert", "in.csv", "out.csv")
assert tmpdir.join('out.csv').read().strip() == csv_content
assert tmpdir.join("out.csv").read().strip() == csv_content

View File

@ -9,7 +9,7 @@ from bonobo.util.testing import all_runners
@all_runners
def test_download_works_for_examples(runner):
expected_bytes = b'hello world'
expected_bytes = b"hello world"
class MockResponse(object):
def __init__(self):
@ -27,12 +27,13 @@ def test_download_works_for_examples(runner):
fout = io.BytesIO()
fout.close = lambda: None
with patch('bonobo.commands.download._open_url') as mock_open_url, \
patch('bonobo.commands.download.open') as mock_open:
with patch("bonobo.commands.download._open_url") as mock_open_url, patch(
"bonobo.commands.download.open"
) as mock_open:
mock_open_url.return_value = MockResponse()
mock_open.return_value = fout
runner('download', 'examples/datasets/coffeeshops.txt')
expected_url = EXAMPLES_BASE_URL + 'datasets/coffeeshops.txt'
runner("download", "examples/datasets/coffeeshops.txt")
expected_url = EXAMPLES_BASE_URL + "datasets/coffeeshops.txt"
mock_open_url.assert_called_once_with(expected_url)
assert fout.getvalue() == expected_bytes
@ -41,4 +42,4 @@ def test_download_works_for_examples(runner):
@all_runners
def test_download_fails_non_example(runner):
with pytest.raises(ValueError):
runner('download', 'something/entirely/different.txt')
runner("download", "something/entirely/different.txt")

View File

@ -8,22 +8,22 @@ from bonobo.util.testing import all_runners
@all_runners
def test_init_file(runner, tmpdir):
target = tmpdir.join('foo.py')
target = tmpdir.join("foo.py")
target_filename = str(target)
runner('init', target_filename)
runner("init", target_filename)
assert os.path.exists(target_filename)
out, err = runner('run', target_filename)
assert out.replace('\n', ' ').strip() == 'Hello World'
out, err = runner("run", target_filename)
assert out.replace("\n", " ").strip() == "Hello World"
assert not err
@all_runners
@pytest.mark.parametrize('template', InitCommand.TEMPLATES)
@pytest.mark.parametrize("template", InitCommand.TEMPLATES)
def test_init_file_templates(runner, template, tmpdir):
target = tmpdir.join('foo.py')
target = tmpdir.join("foo.py")
target_filename = str(target)
runner('init', target_filename)
runner("init", target_filename)
assert os.path.exists(target_filename)
out, err = runner('run', target_filename)
out, err = runner("run", target_filename)
assert not err

View File

@ -7,42 +7,42 @@ from bonobo.util.testing import all_runners
@all_runners
def test_run(runner):
out, err = runner('run', '--quiet', get_examples_path('types/strings.py'))
out = out.split('\n')
assert out[0].startswith('Foo ')
assert out[1].startswith('Bar ')
assert out[2].startswith('Baz ')
out, err = runner("run", "--quiet", get_examples_path("types/strings.py"))
out = out.split("\n")
assert out[0].startswith("Foo ")
assert out[1].startswith("Bar ")
assert out[2].startswith("Baz ")
@all_runners
def test_run_module(runner):
out, err = runner('run', '--quiet', '-m', 'bonobo.examples.types.strings')
out = out.split('\n')
assert out[0].startswith('Foo ')
assert out[1].startswith('Bar ')
assert out[2].startswith('Baz ')
out, err = runner("run", "--quiet", "-m", "bonobo.examples.types.strings")
out = out.split("\n")
assert out[0].startswith("Foo ")
assert out[1].startswith("Bar ")
assert out[2].startswith("Baz ")
@all_runners
def test_run_path(runner):
out, err = runner('run', '--quiet', get_examples_path('types'))
out = out.split('\n')
assert out[0].startswith('Foo ')
assert out[1].startswith('Bar ')
assert out[2].startswith('Baz ')
out, err = runner("run", "--quiet", get_examples_path("types"))
out = out.split("\n")
assert out[0].startswith("Foo ")
assert out[1].startswith("Bar ")
assert out[2].startswith("Baz ")
@all_runners
def test_install_requirements_for_dir(runner):
dirname = get_examples_path('types')
with patch('bonobo.commands.run._install_requirements') as install_mock:
runner('run', '--install', dirname)
install_mock.assert_called_once_with(os.path.join(dirname, 'requirements.txt'))
dirname = get_examples_path("types")
with patch("bonobo.commands.run._install_requirements") as install_mock:
runner("run", "--install", dirname)
install_mock.assert_called_once_with(os.path.join(dirname, "requirements.txt"))
@all_runners
def test_install_requirements_for_file(runner):
dirname = get_examples_path('types')
with patch('bonobo.commands.run._install_requirements') as install_mock:
runner('run', '--install', os.path.join(dirname, 'strings.py'))
install_mock.assert_called_once_with(os.path.join(dirname, 'requirements.txt'))
dirname = get_examples_path("types")
with patch("bonobo.commands.run._install_requirements") as install_mock:
runner("run", "--install", os.path.join(dirname, "strings.py"))
install_mock.assert_called_once_with(os.path.join(dirname, "requirements.txt"))

View File

@ -5,103 +5,104 @@ from bonobo.util.testing import EnvironmentTestCase
@pytest.fixture
def env1(tmpdir):
env_file = tmpdir.join('.env_one')
env_file.write('\n'.join((
'SECRET=unknown',
'PASSWORD=sweet',
'PATH=first',
)))
env_file = tmpdir.join(".env_one")
env_file.write("\n".join(("SECRET=unknown", "PASSWORD=sweet", "PATH=first")))
return str(env_file)
@pytest.fixture
def env2(tmpdir):
env_file = tmpdir.join('.env_two')
env_file.write('\n'.join((
'PASSWORD=bitter',
"PATH='second'",
)))
env_file = tmpdir.join(".env_two")
env_file.write("\n".join(("PASSWORD=bitter", "PATH='second'")))
return str(env_file)
class TestDefaultEnvFile(EnvironmentTestCase):
def test_run_with_default_env_file(self, runner, target, env1):
env = self.run_environ(runner, *target, '--default-env-file', env1)
assert env.get('SECRET') == 'unknown'
assert env.get('PASSWORD') == 'sweet'
assert env.get('PATH') == '/usr/bin'
env = self.run_environ(runner, *target, "--default-env-file", env1)
assert env.get("SECRET") == "unknown"
assert env.get("PASSWORD") == "sweet"
assert env.get("PATH") == "/usr/bin"
def test_run_with_multiple_default_env_files(self, runner, target, env1, env2):
env = self.run_environ(runner, *target, '--default-env-file', env1, '--default-env-file', env2)
assert env.get('SECRET') == 'unknown'
assert env.get('PASSWORD') == 'sweet'
assert env.get('PATH') == '/usr/bin'
env = self.run_environ(runner, *target, "--default-env-file", env1, "--default-env-file", env2)
assert env.get("SECRET") == "unknown"
assert env.get("PASSWORD") == "sweet"
assert env.get("PATH") == "/usr/bin"
env = self.run_environ(runner, *target, '--default-env-file', env2, '--default-env-file', env1)
assert env.get('SECRET') == 'unknown'
assert env.get('PASSWORD') == 'bitter'
assert env.get('PATH') == '/usr/bin'
env = self.run_environ(runner, *target, "--default-env-file", env2, "--default-env-file", env1)
assert env.get("SECRET") == "unknown"
assert env.get("PASSWORD") == "bitter"
assert env.get("PATH") == "/usr/bin"
class TestEnvFile(EnvironmentTestCase):
def test_run_with_file(self, runner, target, env1):
env = self.run_environ(runner, *target, '--env-file', env1)
assert env.get('SECRET') == 'unknown'
assert env.get('PASSWORD') == 'sweet'
assert env.get('PATH') == 'first'
env = self.run_environ(runner, *target, "--env-file", env1)
assert env.get("SECRET") == "unknown"
assert env.get("PASSWORD") == "sweet"
assert env.get("PATH") == "first"
def test_run_with_multiple_files(self, runner, target, env1, env2):
env = self.run_environ(runner, *target, '--env-file', env1, '--env-file', env2)
assert env.get('SECRET') == 'unknown'
assert env.get('PASSWORD') == 'bitter'
assert env.get('PATH') == 'second'
env = self.run_environ(runner, *target, "--env-file", env1, "--env-file", env2)
assert env.get("SECRET") == "unknown"
assert env.get("PASSWORD") == "bitter"
assert env.get("PATH") == "second"
env = self.run_environ(runner, *target, '--env-file', env2, '--env-file', env1)
assert env.get('SECRET') == 'unknown'
assert env.get('PASSWORD') == 'sweet'
assert env.get('PATH') == 'first'
env = self.run_environ(runner, *target, "--env-file", env2, "--env-file", env1)
assert env.get("SECRET") == "unknown"
assert env.get("PASSWORD") == "sweet"
assert env.get("PATH") == "first"
class TestEnvFileCombinations(EnvironmentTestCase):
def test_run_with_both_env_files(self, runner, target, env1, env2):
env = self.run_environ(runner, *target, '--default-env-file', env1, '--env-file', env2)
assert env.get('SECRET') == 'unknown'
assert env.get('PASSWORD') == 'bitter'
assert env.get('PATH') == 'second'
env = self.run_environ(runner, *target, "--default-env-file", env1, "--env-file", env2)
assert env.get("SECRET") == "unknown"
assert env.get("PASSWORD") == "bitter"
assert env.get("PATH") == "second"
def test_run_with_both_env_files_then_overrides(self, runner, target, env1, env2):
env = self.run_environ(
runner, *target, '--default-env-file', env1, '--env-file', env2, '--env', 'PASSWORD=mine', '--env',
'SECRET=s3cr3t'
runner,
*target,
"--default-env-file",
env1,
"--env-file",
env2,
"--env",
"PASSWORD=mine",
"--env",
"SECRET=s3cr3t"
)
assert env.get('SECRET') == 's3cr3t'
assert env.get('PASSWORD') == 'mine'
assert env.get('PATH') == 'second'
assert env.get("SECRET") == "s3cr3t"
assert env.get("PASSWORD") == "mine"
assert env.get("PATH") == "second"
class TestEnvVars(EnvironmentTestCase):
def test_run_no_env(self, runner, target):
env = self.run_environ(runner, *target, environ={'USER': 'romain'})
assert env.get('USER') == 'romain'
env = self.run_environ(runner, *target, environ={"USER": "romain"})
assert env.get("USER") == "romain"
def test_run_env(self, runner, target):
env = self.run_environ(runner, *target, '--env', 'USER=serious', environ={'USER': 'romain'})
assert env.get('USER') == 'serious'
env = self.run_environ(runner, *target, "--env", "USER=serious", environ={"USER": "romain"})
assert env.get("USER") == "serious"
def test_run_env_mixed(self, runner, target):
env = self.run_environ(runner, *target, '--env', 'ONE=1', '--env', 'TWO="2"', environ={'USER': 'romain'})
assert env.get('USER') == 'romain'
assert env.get('ONE') == '1'
assert env.get('TWO') == '2'
env = self.run_environ(runner, *target, "--env", "ONE=1", "--env", 'TWO="2"', environ={"USER": "romain"})
assert env.get("USER") == "romain"
assert env.get("ONE") == "1"
assert env.get("TWO") == "2"
def test_run_default_env(self, runner, target):
env = self.run_environ(runner, *target, '--default-env', 'USER=clown')
assert env.get('USER') == 'clown'
env = self.run_environ(runner, *target, "--default-env", "USER=clown")
assert env.get("USER") == "clown"
env = self.run_environ(runner, *target, '--default-env', 'USER=clown', environ={'USER': 'romain'})
assert env.get('USER') == 'romain'
env = self.run_environ(runner, *target, "--default-env", "USER=clown", environ={"USER": "romain"})
assert env.get("USER") == "romain"
env = self.run_environ(
runner, *target, '--env', 'USER=serious', '--default-env', 'USER=clown', environ={'USER': 'romain'}
runner, *target, "--env", "USER=serious", "--default-env", "USER=clown", environ={"USER": "romain"}
)
assert env.get('USER') == 'serious'
assert env.get("USER") == "serious"

View File

@ -4,17 +4,17 @@ from bonobo.util.testing import all_runners
@all_runners
def test_version(runner):
out, err = runner('version')
out, err = runner("version")
out = out.strip()
assert out.startswith('bonobo ')
assert out.startswith("bonobo ")
assert __version__ in out
out, err = runner('version', '-q')
out, err = runner("version", "-q")
out = out.strip()
assert out.startswith('bonobo ')
assert out.startswith("bonobo ")
assert __version__ in out
out, err = runner('version', '-qq')
out, err = runner("version", "-qq")
out = out.strip()
assert not out.startswith('bonobo ')
assert __version__ in out
assert not out.startswith("bonobo ")
assert __version__ in out

View File

@ -11,7 +11,7 @@ class NoOptConfigurable(Configurable):
class MyConfigurable(Configurable):
required_str = Option(str)
default_str = Option(str, default='foo')
default_str = Option(str, default="foo")
integer = Option(int, required=False)
@ -20,7 +20,7 @@ class MyHarderConfigurable(MyConfigurable):
class MyBetterConfigurable(MyConfigurable):
required_str = Option(str, required=False, default='kaboom')
required_str = Option(str, required=False, default="kaboom")
class MyConfigurableUsingPositionalOptions(MyConfigurable):
@ -35,7 +35,7 @@ def test_missing_required_option_error():
with pytest.raises(TypeError) as exc:
MyConfigurable(_final=True)
assert exc.match('missing 1 required option:')
assert exc.match("missing 1 required option:")
def test_missing_required_options_error():
@ -44,29 +44,29 @@ def test_missing_required_options_error():
with pytest.raises(TypeError) as exc:
MyHarderConfigurable(_final=True)
assert exc.match('missing 2 required options:')
assert exc.match("missing 2 required options:")
def test_extraneous_option_error():
with pytest.raises(TypeError) as exc:
MyConfigurable(required_str='foo', hello='world')
assert exc.match('got 1 unexpected option:')
MyConfigurable(required_str="foo", hello="world")
assert exc.match("got 1 unexpected option:")
def test_extraneous_options_error():
with pytest.raises(TypeError) as exc:
MyConfigurable(required_str='foo', hello='world', acme='corp')
assert exc.match('got 2 unexpected options:')
MyConfigurable(required_str="foo", hello="world", acme="corp")
assert exc.match("got 2 unexpected options:")
def test_defaults():
o = MyConfigurable(required_str='hello')
o = MyConfigurable(required_str="hello")
with inspect_node(o) as ni:
assert not ni.partial
assert o.required_str == 'hello'
assert o.default_str == 'foo'
assert o.required_str == "hello"
assert o.default_str == "foo"
assert o.integer is None
@ -76,30 +76,30 @@ def test_str_type_factory():
with inspect_node(o) as ni:
assert not ni.partial
assert o.required_str == '42'
assert o.default_str == 'foo'
assert o.required_str == "42"
assert o.default_str == "foo"
assert o.integer is None
def test_int_type_factory():
o = MyConfigurable(required_str='yo', default_str='bar', integer='42')
o = MyConfigurable(required_str="yo", default_str="bar", integer="42")
with inspect_node(o) as ni:
assert not ni.partial
assert o.required_str == 'yo'
assert o.default_str == 'bar'
assert o.required_str == "yo"
assert o.default_str == "bar"
assert o.integer == 42
def test_bool_type_factory():
o = MyHarderConfigurable(required_str='yes', also_required='True')
o = MyHarderConfigurable(required_str="yes", also_required="True")
with inspect_node(o) as ni:
assert not ni.partial
assert o.required_str == 'yes'
assert o.default_str == 'foo'
assert o.required_str == "yes"
assert o.default_str == "foo"
assert o.integer is None
assert o.also_required is True
@ -110,22 +110,22 @@ def test_option_resolution_order():
with inspect_node(o) as ni:
assert not ni.partial
assert o.required_str == 'kaboom'
assert o.default_str == 'foo'
assert o.required_str == "kaboom"
assert o.default_str == "foo"
assert o.integer is None
def test_option_positional():
o = MyConfigurableUsingPositionalOptions('1', '2', '3', required_str='hello')
o = MyConfigurableUsingPositionalOptions("1", "2", "3", required_str="hello")
with inspect_node(o) as ni:
assert not ni.partial
assert o.first == '1'
assert o.second == '2'
assert o.third == '3'
assert o.required_str == 'hello'
assert o.default_str == 'foo'
assert o.first == "1"
assert o.second == "2"
assert o.third == "3"
assert o.required_str == "hello"
assert o.default_str == "foo"
assert o.integer is None

View File

@ -50,10 +50,7 @@ def test_define_with_decorator():
calls = []
def my_handler(*args, **kwargs):
calls.append((
args,
kwargs,
))
calls.append((args, kwargs))
Concrete = MethodBasedConfigurable(my_handler)
@ -64,7 +61,7 @@ def test_define_with_decorator():
assert ci.type == MethodBasedConfigurable
assert ci.partial
t = Concrete('foo', bar='baz')
t = Concrete("foo", bar="baz")
assert callable(t.handler)
assert len(calls) == 0
@ -75,15 +72,12 @@ def test_define_with_decorator():
def test_late_binding_method_decoration():
calls = []
@MethodBasedConfigurable(foo='foo')
@MethodBasedConfigurable(foo="foo")
def Concrete(*args, **kwargs):
calls.append((
args,
kwargs,
))
calls.append((args, kwargs))
assert callable(Concrete.handler)
t = Concrete(bar='baz')
t = Concrete(bar="baz")
assert callable(t.handler)
assert len(calls) == 0
@ -95,12 +89,9 @@ def test_define_with_argument():
calls = []
def concrete_handler(*args, **kwargs):
calls.append((
args,
kwargs,
))
calls.append((args, kwargs))
t = MethodBasedConfigurable(concrete_handler, 'foo', bar='baz')
t = MethodBasedConfigurable(concrete_handler, "foo", bar="baz")
assert callable(t.handler)
assert len(calls) == 0
t()
@ -112,12 +103,9 @@ def test_define_with_inheritance():
class Inheriting(MethodBasedConfigurable):
def handler(self, *args, **kwargs):
calls.append((
args,
kwargs,
))
calls.append((args, kwargs))
t = Inheriting('foo', bar='baz')
t = Inheriting("foo", bar="baz")
assert callable(t.handler)
assert len(calls) == 0
t()
@ -132,13 +120,10 @@ def test_inheritance_then_decorate():
@Inheriting
def Concrete(*args, **kwargs):
calls.append((
args,
kwargs,
))
calls.append((args, kwargs))
assert callable(Concrete.handler)
t = Concrete('foo', bar='baz')
t = Concrete("foo", bar="baz")
assert callable(t.handler)
assert len(calls) == 0
t()

View File

@ -12,11 +12,11 @@ class Bobby(Configurable):
@ContextProcessor
def think(self, context):
yield 'different'
yield "different"
def __call__(self, think, *args, **kwargs):
self.handler('1', *args, **kwargs)
self.handler2('2', *args, **kwargs)
self.handler("1", *args, **kwargs)
self.handler2("2", *args, **kwargs)
def test_partial():
@ -40,7 +40,7 @@ def test_partial():
assert len(ci.options) == 4
assert len(ci.processors) == 1
assert ci.partial
assert ci.partial[0] == (f1, )
assert ci.partial[0] == (f1,)
assert not len(ci.partial[1])
# instanciate a more complete partial instance ...
@ -53,13 +53,10 @@ def test_partial():
assert len(ci.options) == 4
assert len(ci.processors) == 1
assert ci.partial
assert ci.partial[0] == (
f1,
f2,
)
assert ci.partial[0] == (f1, f2)
assert not len(ci.partial[1])
c = C('foo')
c = C("foo")
with inspect_node(c) as ci:
assert ci.type == Bobby

View File

@ -1,7 +1,7 @@
from operator import attrgetter
from bonobo.config import Configurable
from bonobo.config.processors import ContextProcessor, resolve_processors, ContextCurrifier, use_context_processor
from bonobo.config.processors import ContextCurrifier, ContextProcessor, resolve_processors, use_context_processor
class CP1(Configurable):
@ -11,11 +11,11 @@ class CP1(Configurable):
@ContextProcessor
def a(self):
yield 'this is A'
yield "this is A"
@ContextProcessor
def b(self, a):
yield a.upper()[:-1] + 'b'
yield a.upper()[:-1] + "b"
def __call__(self, a, b):
return a, b
@ -46,20 +46,20 @@ class CP3(CP2):
def get_all_processors_names(cls):
return list(map(attrgetter('__name__'), resolve_processors(cls)))
return list(map(attrgetter("__name__"), resolve_processors(cls)))
def test_inheritance_and_ordering():
assert get_all_processors_names(CP1) == ['c', 'a', 'b']
assert get_all_processors_names(CP2) == ['c', 'a', 'b', 'f', 'e', 'd']
assert get_all_processors_names(CP3) == ['c', 'a', 'b', 'f', 'e', 'd', 'c', 'b']
assert get_all_processors_names(CP1) == ["c", "a", "b"]
assert get_all_processors_names(CP2) == ["c", "a", "b", "f", "e", "d"]
assert get_all_processors_names(CP3) == ["c", "a", "b", "f", "e", "d", "c", "b"]
def test_setup_teardown():
o = CP1()
stack = ContextCurrifier(o)
stack.setup()
assert o(*stack.args) == ('this is A', 'THIS IS b')
assert o(*stack.args) == ("this is A", "THIS IS b")
stack.teardown()
@ -71,4 +71,4 @@ def test_processors_on_func():
def node(context):
pass
assert get_all_processors_names(node) == ['cp']
assert get_all_processors_names(node) == ["cp"]

View File

@ -4,11 +4,11 @@ import time
import pytest
from bonobo.config import Configurable, Container, Exclusive, Service, use
from bonobo.config.services import validate_service_name, create_container
from bonobo.config.services import create_container, validate_service_name
from bonobo.util import get_name
class PrinterInterface():
class PrinterInterface:
def print(self, *args):
raise NotImplementedError()
@ -18,46 +18,43 @@ class ConcretePrinter(PrinterInterface):
self.prefix = prefix
def print(self, *args):
return ';'.join((self.prefix, *args))
return ";".join((self.prefix, *args))
SERVICES = Container(
printer0=ConcretePrinter(prefix='0'),
printer1=ConcretePrinter(prefix='1'),
)
SERVICES = Container(printer0=ConcretePrinter(prefix="0"), printer1=ConcretePrinter(prefix="1"))
class MyServiceDependantConfigurable(Configurable):
printer = Service(PrinterInterface, )
printer = Service(PrinterInterface)
def __call__(self, *args, printer: PrinterInterface):
return printer.print(*args)
def test_service_name_validator():
assert validate_service_name('foo') == 'foo'
assert validate_service_name('foo.bar') == 'foo.bar'
assert validate_service_name('Foo') == 'Foo'
assert validate_service_name('Foo.Bar') == 'Foo.Bar'
assert validate_service_name('Foo.a0') == 'Foo.a0'
assert validate_service_name("foo") == "foo"
assert validate_service_name("foo.bar") == "foo.bar"
assert validate_service_name("Foo") == "Foo"
assert validate_service_name("Foo.Bar") == "Foo.Bar"
assert validate_service_name("Foo.a0") == "Foo.a0"
with pytest.raises(ValueError):
validate_service_name('foo.0')
validate_service_name("foo.0")
with pytest.raises(ValueError):
validate_service_name('0.foo')
validate_service_name("0.foo")
def test_service_dependency():
o = MyServiceDependantConfigurable(printer='printer0')
o = MyServiceDependantConfigurable(printer="printer0")
assert o('foo', 'bar', printer=SERVICES.get('printer0')) == '0;foo;bar'
assert o('bar', 'baz', printer=SERVICES.get('printer1')) == '1;bar;baz'
assert o('foo', 'bar', **SERVICES.kwargs_for(o)) == '0;foo;bar'
assert o("foo", "bar", printer=SERVICES.get("printer0")) == "0;foo;bar"
assert o("bar", "baz", printer=SERVICES.get("printer1")) == "1;bar;baz"
assert o("foo", "bar", **SERVICES.kwargs_for(o)) == "0;foo;bar"
def test_service_dependency_unavailable():
o = MyServiceDependantConfigurable(printer='printer2')
o = MyServiceDependantConfigurable(printer="printer2")
with pytest.raises(KeyError):
SERVICES.kwargs_for(o)
@ -72,15 +69,15 @@ class VCR:
def test_exclusive():
vcr = VCR()
vcr.append('hello')
vcr.append("hello")
def record(prefix, vcr=vcr):
with Exclusive(vcr):
for i in range(5):
vcr.append(' '.join((prefix, str(i))))
vcr.append(" ".join((prefix, str(i))))
time.sleep(0.05)
threads = [threading.Thread(target=record, args=(str(i), )) for i in range(5)]
threads = [threading.Thread(target=record, args=(str(i),)) for i in range(5)]
for thread in threads:
thread.start()
@ -90,8 +87,32 @@ def test_exclusive():
thread.join()
assert vcr.tape == [
'hello', '0 0', '0 1', '0 2', '0 3', '0 4', '1 0', '1 1', '1 2', '1 3', '1 4', '2 0', '2 1', '2 2', '2 3',
'2 4', '3 0', '3 1', '3 2', '3 3', '3 4', '4 0', '4 1', '4 2', '4 3', '4 4'
"hello",
"0 0",
"0 1",
"0 2",
"0 3",
"0 4",
"1 0",
"1 1",
"1 2",
"1 3",
"1 4",
"2 0",
"2 1",
"2 2",
"2 3",
"2 4",
"3 0",
"3 1",
"3 2",
"3 3",
"3 4",
"4 0",
"4 1",
"4 2",
"4 3",
"4 4",
]
@ -100,28 +121,25 @@ def test_requires():
services = Container(output=vcr.append)
@use('output')
@use("output")
def append(out, x):
out(x)
svcargs = services.kwargs_for(append)
assert len(svcargs) == 1
assert svcargs['output'] == vcr.append
assert svcargs["output"] == vcr.append
@pytest.mark.parametrize('services', [None, {}])
@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'
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',
})
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'
assert "fs" in c and c["fs"] == "fs"
assert "http" in c and c["http"] == "http"

View File

@ -5,22 +5,22 @@ from bonobo.config import use_raw_input
from bonobo.execution.contexts import GraphExecutionContext
from bonobo.util.bags import BagType
Extracted = namedtuple('Extracted', ['id', 'name', 'value'])
ExtractedBT = BagType('ExtractedBT', ['id', 'name', 'value'])
Extracted = namedtuple("Extracted", ["id", "name", "value"])
ExtractedBT = BagType("ExtractedBT", ["id", "name", "value"])
def extract_nt():
yield Extracted(id=1, name='Guido', value='.py')
yield Extracted(id=2, name='Larry', value='.pl')
yield Extracted(id=3, name='Dennis', value='.c')
yield Extracted(id=4, name='Yukihiro', value='.rb')
yield Extracted(id=1, name="Guido", value=".py")
yield Extracted(id=2, name="Larry", value=".pl")
yield Extracted(id=3, name="Dennis", value=".c")
yield Extracted(id=4, name="Yukihiro", value=".rb")
def extract_bt():
yield ExtractedBT(id=1, name='Guido', value='.py')
yield ExtractedBT(id=2, name='Larry', value='.pl')
yield ExtractedBT(id=3, name='Dennis', value='.c')
yield ExtractedBT(id=4, name='Yukihiro', value='.rb')
yield ExtractedBT(id=1, name="Guido", value=".py")
yield ExtractedBT(id=2, name="Larry", value=".pl")
yield ExtractedBT(id=3, name="Dennis", value=".c")
yield ExtractedBT(id=4, name="Yukihiro", value=".rb")
def transform_using_args(id, name, value):
@ -53,10 +53,18 @@ def test_execution():
with GraphExecutionContext(graph) as context:
context.run_until_complete()
assert result_args == [(2, 'Guido', 'guido.py'), (4, 'Larry', 'larry.pl'), (6, 'Dennis', 'dennis.c'),
(8, 'Yukihiro', 'yukihiro.rb')]
assert result_args == [
(2, "Guido", "guido.py"),
(4, "Larry", "larry.pl"),
(6, "Dennis", "dennis.c"),
(8, "Yukihiro", "yukihiro.rb"),
]
assert result_nt == [(1, 'GUIDO', '.py'), (2, 'LARRY', '.pl'), (3, 'DENNIS', '.c'), (4, 'YUKIHIRO', '.rb')]
assert result_nt == [(1, "GUIDO", ".py"), (2, "LARRY", ".pl"), (3, "DENNIS", ".c"), (4, "YUKIHIRO", ".rb")]
assert result_bt == [(2, 'Guido', 'guido.py'), (4, 'Larry', 'larry.pl'), (6, 'Dennis', 'dennis.c'),
(8, 'Yukihiro', 'yukihiro.rb')]
assert result_bt == [
(2, "Guido", "guido.py"),
(4, "Larry", "larry.pl"),
(6, "Dennis", "dennis.c"),
(8, "Yukihiro", "yukihiro.rb"),
]

View File

@ -1,14 +1,14 @@
from bonobo import Graph
from bonobo.constants import EMPTY, BEGIN, END
from bonobo.constants import BEGIN, EMPTY, END
from bonobo.execution.contexts import GraphExecutionContext
def raise_an_error(*args, **kwargs):
raise Exception('Careful, man, there\'s a beverage here!')
raise Exception("Careful, man, there's a beverage here!")
def raise_an_unrecoverrable_error(*args, **kwargs):
raise Exception('You are entering a world of pain!')
raise Exception("You are entering a world of pain!")
def test_lifecycle_of_empty_graph():

View File

@ -6,131 +6,131 @@ from bonobo import Graph
from bonobo.constants import EMPTY
from bonobo.execution.contexts.node import NodeExecutionContext, split_token
from bonobo.execution.strategies import NaiveStrategy
from bonobo.util.envelopes import F_NOT_MODIFIED, F_INHERIT
from bonobo.util.testing import BufferingNodeExecutionContext, BufferingGraphExecutionContext
from bonobo.util.envelopes import F_INHERIT, F_NOT_MODIFIED
from bonobo.util.testing import BufferingGraphExecutionContext, BufferingNodeExecutionContext
def test_node_string():
def f():
return 'foo'
return "foo"
with BufferingNodeExecutionContext(f) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 1
assert output[0] == ('foo', )
assert output[0] == ("foo",)
def g():
yield 'foo'
yield 'bar'
yield "foo"
yield "bar"
with BufferingNodeExecutionContext(g) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 2
assert output[0] == ('foo', )
assert output[1] == ('bar', )
assert output[0] == ("foo",)
assert output[1] == ("bar",)
def test_node_bytes():
def f():
return b'foo'
return b"foo"
with BufferingNodeExecutionContext(f) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 1
assert output[0] == (b'foo', )
assert output[0] == (b"foo",)
def g():
yield b'foo'
yield b'bar'
yield b"foo"
yield b"bar"
with BufferingNodeExecutionContext(g) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 2
assert output[0] == (b'foo', )
assert output[1] == (b'bar', )
assert output[0] == (b"foo",)
assert output[1] == (b"bar",)
def test_node_dict():
def f():
return {'id': 1, 'name': 'foo'}
return {"id": 1, "name": "foo"}
with BufferingNodeExecutionContext(f) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 1
assert output[0] == ({'id': 1, 'name': 'foo'}, )
assert output[0] == ({"id": 1, "name": "foo"},)
def g():
yield {'id': 1, 'name': 'foo'}
yield {'id': 2, 'name': 'bar'}
yield {"id": 1, "name": "foo"}
yield {"id": 2, "name": "bar"}
with BufferingNodeExecutionContext(g) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 2
assert output[0] == ({'id': 1, 'name': 'foo'}, )
assert output[1] == ({'id': 2, 'name': 'bar'}, )
assert output[0] == ({"id": 1, "name": "foo"},)
assert output[1] == ({"id": 2, "name": "bar"},)
def test_node_dict_chained():
strategy = NaiveStrategy(GraphExecutionContextType=BufferingGraphExecutionContext)
def f():
return {'id': 1, 'name': 'foo'}
return {"id": 1, "name": "foo"}
def uppercase_name(values):
return {**values, 'name': values['name'].upper()}
return {**values, "name": values["name"].upper()}
graph = Graph(f, uppercase_name)
context = strategy.execute(graph)
output = context.get_buffer()
assert len(output) == 1
assert output[0] == ({'id': 1, 'name': 'FOO'}, )
assert output[0] == ({"id": 1, "name": "FOO"},)
def g():
yield {'id': 1, 'name': 'foo'}
yield {'id': 2, 'name': 'bar'}
yield {"id": 1, "name": "foo"}
yield {"id": 2, "name": "bar"}
graph = Graph(g, uppercase_name)
context = strategy.execute(graph)
output = context.get_buffer()
assert len(output) == 2
assert output[0] == ({'id': 1, 'name': 'FOO'}, )
assert output[1] == ({'id': 2, 'name': 'BAR'}, )
assert output[0] == ({"id": 1, "name": "FOO"},)
assert output[1] == ({"id": 2, "name": "BAR"},)
def test_node_tuple():
def f():
return 'foo', 'bar'
return "foo", "bar"
with BufferingNodeExecutionContext(f) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 1
assert output[0] == ('foo', 'bar')
assert output[0] == ("foo", "bar")
def g():
yield 'foo', 'bar'
yield 'foo', 'baz'
yield "foo", "bar"
yield "foo", "baz"
with BufferingNodeExecutionContext(g) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 2
assert output[0] == ('foo', 'bar')
assert output[1] == ('foo', 'baz')
assert output[0] == ("foo", "bar")
assert output[1] == ("foo", "baz")
def test_node_tuple_chained():
@ -140,50 +140,50 @@ def test_node_tuple_chained():
return tuple(map(str.upper, args))
def f():
return 'foo', 'bar'
return "foo", "bar"
graph = Graph(f, uppercase)
context = strategy.execute(graph)
output = context.get_buffer()
assert len(output) == 1
assert output[0] == ('FOO', 'BAR')
assert output[0] == ("FOO", "BAR")
def g():
yield 'foo', 'bar'
yield 'foo', 'baz'
yield "foo", "bar"
yield "foo", "baz"
graph = Graph(g, uppercase)
context = strategy.execute(graph)
output = context.get_buffer()
assert len(output) == 2
assert output[0] == ('FOO', 'BAR')
assert output[1] == ('FOO', 'BAZ')
assert output[0] == ("FOO", "BAR")
assert output[1] == ("FOO", "BAZ")
def test_node_tuple_dict():
def f():
return 'foo', 'bar', {'id': 1}
return "foo", "bar", {"id": 1}
with BufferingNodeExecutionContext(f) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 1
assert output[0] == ('foo', 'bar', {'id': 1})
assert output[0] == ("foo", "bar", {"id": 1})
def g():
yield 'foo', 'bar', {'id': 1}
yield 'foo', 'baz', {'id': 2}
yield "foo", "bar", {"id": 1}
yield "foo", "baz", {"id": 2}
with BufferingNodeExecutionContext(g) as context:
context.write_sync(EMPTY)
output = context.get_buffer()
assert len(output) == 2
assert output[0] == ('foo', 'bar', {'id': 1})
assert output[1] == ('foo', 'baz', {'id': 2})
assert output[0] == ("foo", "bar", {"id": 1})
assert output[1] == ("foo", "baz", {"id": 2})
def test_node_lifecycle_natural():
@ -229,9 +229,9 @@ def test_node_lifecycle_with_kill():
def test_split_token():
with pytest.deprecated_call():
assert split_token(('foo', 'bar')) == (set(), ('foo', 'bar'))
assert split_token(("foo", "bar")) == (set(), ("foo", "bar"))
assert split_token(()) == (set(), ())
assert split_token('') == (set(), ('', ))
assert split_token("") == (set(), ("",))
def test_split_token_duplicate():
@ -247,17 +247,17 @@ def test_split_token_duplicate():
def test_split_token_not_modified():
with pytest.deprecated_call():
with pytest.raises(ValueError):
split_token((F_NOT_MODIFIED, 'foo', 'bar'))
split_token((F_NOT_MODIFIED, "foo", "bar"))
with pytest.raises(ValueError):
split_token((F_NOT_MODIFIED, F_INHERIT))
with pytest.raises(ValueError):
split_token((F_INHERIT, F_NOT_MODIFIED))
assert split_token(F_NOT_MODIFIED) == ({F_NOT_MODIFIED}, ())
assert split_token((F_NOT_MODIFIED, )) == ({F_NOT_MODIFIED}, ())
assert split_token((F_NOT_MODIFIED,)) == ({F_NOT_MODIFIED}, ())
def test_split_token_inherit():
with pytest.deprecated_call():
assert split_token(F_INHERIT) == ({F_INHERIT}, ())
assert split_token((F_INHERIT, )) == ({F_INHERIT}, ())
assert split_token((F_INHERIT, 'foo', 'bar')) == ({F_INHERIT}, ('foo', 'bar'))
assert split_token((F_INHERIT,)) == ({F_INHERIT}, ())
assert split_token((F_INHERIT, "foo", "bar")) == ({F_INHERIT}, ("foo", "bar"))

View File

@ -6,9 +6,9 @@ from bonobo.execution import events
def test_names():
# This test looks useless, but as it's becoming the pliugin API, I want to make sure that nothing changes here, or
# notice it otherwise.
for name in 'start', 'started', 'tick', 'stop', 'stopped', 'kill':
for name in "start", "started", "tick", "stop", "stopped", "kill":
event_name = getattr(events, name.upper())
assert event_name == '.'.join(('execution', name))
assert event_name == ".".join(("execution", name))
def test_event_object():

View File

@ -14,16 +14,11 @@ class ResponseMock:
return {}
else:
self.count += 1
return {
'records': self.json_value,
}
return {"records": self.json_value}
def test_read_from_opendatasoft_api():
extract = OpenDataSoftAPI(dataset='test-a-set')
with patch('requests.get', return_value=ResponseMock([
{'fields': {'foo': 'bar'}},
{'fields': {'foo': 'zab'}},
])):
for line in extract('http://example.com/', ValueHolder(0)):
assert 'foo' in line
extract = OpenDataSoftAPI(dataset="test-a-set")
with patch("requests.get", return_value=ResponseMock([{"fields": {"foo": "bar"}}, {"fields": {"foo": "zab"}}])):
for line in extract("http://example.com/", ValueHolder(0)):
assert "foo" in line

View File

@ -1,27 +1,24 @@
from bonobo.util.envelopes import AppendingEnvelope
from bonobo.util.testing import BufferingNodeExecutionContext
messages = [
('Hello', ),
('Goodbye', ),
]
messages = [("Hello",), ("Goodbye",)]
def append(*args):
return AppendingEnvelope('!')
return AppendingEnvelope("!")
def test_inherit():
with BufferingNodeExecutionContext(append) as context:
context.write_sync(*messages)
assert context.get_buffer() == list(map(lambda x: x + ('!', ), messages))
assert context.get_buffer() == list(map(lambda x: x + ("!",), messages))
def test_inherit_bag_tuple():
with BufferingNodeExecutionContext(append) as context:
context.set_input_fields(['message'])
context.set_input_fields(["message"])
context.write_sync(*messages)
assert context.get_output_fields() == ('message', '0')
assert context.get_buffer() == list(map(lambda x: x + ('!', ), messages))
assert context.get_output_fields() == ("message", "0")
assert context.get_buffer() == list(map(lambda x: x + ("!",), messages))

View File

@ -7,10 +7,7 @@ def useless(*args, **kwargs):
def test_not_modified():
input_messages = [
('foo', 'bar'),
('foo', 'baz'),
]
input_messages = [("foo", "bar"), ("foo", "baz")]
with BufferingNodeExecutionContext(useless) as context:
context.write_sync(*input_messages)

View File

@ -6,13 +6,14 @@ import pytest
from bonobo import CsvReader, CsvWriter
from bonobo.constants import EMPTY
from bonobo.util.testing import FilesystemTester, BufferingNodeExecutionContext, WriterTest, ConfigurableNodeTest, \
ReaderTest
from bonobo.util.testing import (
BufferingNodeExecutionContext, ConfigurableNodeTest, FilesystemTester, ReaderTest, WriterTest
)
csv_tester = FilesystemTester('csv')
csv_tester.input_data = 'a,b,c\na foo,b foo,c foo\na bar,b bar,c bar'
csv_tester = FilesystemTester("csv")
csv_tester.input_data = "a,b,c\na foo,b foo,c foo\na bar,b bar,c bar"
defaults = {'lineterminator': '\n'}
defaults = {"lineterminator": "\n"}
incontext = ConfigurableNodeTest.incontext
@ -23,15 +24,10 @@ def test_read_csv_from_file_kwargs(tmpdir):
with BufferingNodeExecutionContext(CsvReader(filename, **defaults), services=services) as context:
context.write_sync(EMPTY)
assert context.get_buffer_args_as_dicts() == [{
'a': 'a foo',
'b': 'b foo',
'c': 'c foo',
}, {
'a': 'a bar',
'b': 'b bar',
'c': 'c bar',
}]
assert context.get_buffer_args_as_dicts() == [
{"a": "a foo", "b": "b foo", "c": "c foo"},
{"a": "a bar", "b": "b bar", "c": "c bar"},
]
###
@ -40,86 +36,66 @@ def test_read_csv_from_file_kwargs(tmpdir):
class Csv:
extension = 'csv'
extension = "csv"
ReaderNodeType = CsvReader
WriterNodeType = CsvWriter
L1, L2, L3, L4 = ('a', 'hey'), ('b', 'bee'), ('c', 'see'), ('d', 'dee')
LL = ('i', 'have', 'more', 'values')
L1, L2, L3, L4 = ("a", "hey"), ("b", "bee"), ("c", "see"), ("d", "dee")
LL = ("i", "have", "more", "values")
class CsvReaderTest(Csv, ReaderTest, TestCase):
input_data = '\n'.join((
'id,name',
'1,John Doe',
'2,Jane Doe',
',DPR',
'42,Elon Musk',
))
input_data = "\n".join(("id,name", "1,John Doe", "2,Jane Doe", ",DPR", "42,Elon Musk"))
def check_output(self, context, *, prepend=None):
out = context.get_buffer()
assert out == (prepend or list()) + [
('1', 'John Doe'),
('2', 'Jane Doe'),
('', 'DPR'),
('42', 'Elon Musk'),
]
assert out == (prepend or list()) + [("1", "John Doe"), ("2", "Jane Doe"), ("", "DPR"), ("42", "Elon Musk")]
@incontext()
def test_nofields(self, context):
context.write_sync(EMPTY)
context.stop()
self.check_output(context)
assert context.get_output_fields() == ('id', 'name')
assert context.get_output_fields() == ("id", "name")
@incontext(output_type=tuple)
def test_output_type(self, context):
context.write_sync(EMPTY)
context.stop()
self.check_output(context, prepend=[('id', 'name')])
self.check_output(context, prepend=[("id", "name")])
@incontext(
output_fields=(
'x',
'y',
), skip=1
)
@incontext(output_fields=("x", "y"), skip=1)
def test_output_fields(self, context):
context.write_sync(EMPTY)
context.stop()
self.check_output(context)
assert context.get_output_fields() == ('x', 'y')
assert context.get_output_fields() == ("x", "y")
@incontext(quoting=QUOTE_ALL)
def test_quoting(self, context):
context.write_sync(EMPTY)
context.stop()
self.check_output(context)
assert context.get_output_fields() == ('id', 'name')
assert context.get_output_fields() == ("id", "name")
class CsvWriterTest(Csv, WriterTest, TestCase):
@incontext()
def test_fields(self, context):
context.set_input_fields(['foo', 'bar'])
context.write_sync(('a', 'b'), ('c', 'd'))
context.set_input_fields(["foo", "bar"])
context.write_sync(("a", "b"), ("c", "d"))
context.stop()
assert self.readlines() == (
'foo,bar',
'a,b',
'c,d',
)
assert self.readlines() == ("foo,bar", "a,b", "c,d")
@incontext()
def test_fields_from_type(self, context):
context.set_input_type(namedtuple('Point', 'x y'))
context.set_input_type(namedtuple("Point", "x y"))
context.write_sync((1, 2), (3, 4))
context.stop()
assert self.readlines() == ('x,y', '1,2', '3,4')
assert self.readlines() == ("x,y", "1,2", "3,4")
@incontext()
def test_nofields_multiple_args(self, context):
@ -127,30 +103,21 @@ class CsvWriterTest(Csv, WriterTest, TestCase):
context.write_sync((L1, L2), (L3, L4))
context.stop()
assert self.readlines() == (
'a,hey',
'b,bee',
'c,see',
'd,dee',
)
assert self.readlines() == ("a,hey", "b,bee", "c,see", "d,dee")
@incontext()
def test_nofields_multiple_args_length_mismatch(self, context):
# if length of input vary, then we get a TypeError (unrecoverable)
with pytest.raises(TypeError):
context.write_sync((L1, L2), (L3, ))
context.write_sync((L1, L2), (L3,))
@incontext()
def test_nofields_single_arg(self, context):
# single args are just dumped, shapes can vary.
context.write_sync((L1, ), (LL, ), (L3, ))
context.write_sync((L1,), (LL,), (L3,))
context.stop()
assert self.readlines() == (
'a,hey',
'i,have,more,values',
'c,see',
)
assert self.readlines() == ("a,hey", "i,have,more,values", "c,see")
@incontext()
def test_nofields_empty_args(self, context):

View File

@ -5,26 +5,23 @@ from bonobo.constants import EMPTY
from bonobo.execution.contexts.node import NodeExecutionContext
from bonobo.util.testing import BufferingNodeExecutionContext, FilesystemTester
txt_tester = FilesystemTester('txt')
txt_tester.input_data = 'Hello\nWorld\n'
txt_tester = FilesystemTester("txt")
txt_tester.input_data = "Hello\nWorld\n"
def test_file_writer_contextless(tmpdir):
fs, filename, services = txt_tester.get_services_for_writer(tmpdir)
with FileWriter(path=filename).open(fs) as fp:
fp.write('Yosh!')
fp.write("Yosh!")
with fs.open(filename) as fp:
assert fp.read() == 'Yosh!'
assert fp.read() == "Yosh!"
@pytest.mark.parametrize(
'lines,output',
[
(('ACME', ), 'ACME'), # one line...
(('Foo', 'Bar', 'Baz'), 'Foo\nBar\nBaz'), # more than one line...
]
"lines,output",
[(("ACME",), "ACME"), (("Foo", "Bar", "Baz"), "Foo\nBar\nBaz")], # one line... # more than one line...
)
def test_file_writer_in_context(tmpdir, lines, output):
fs, filename, services = txt_tester.get_services_for_writer(tmpdir)
@ -44,5 +41,5 @@ def test_file_reader(tmpdir):
output = context.get_buffer()
assert len(output) == 2
assert output[0] == ('Hello', )
assert output[1] == ('World', )
assert output[0] == ("Hello",)
assert output[1] == ("World",)

View File

@ -4,14 +4,13 @@ from unittest import TestCase
import pytest
from bonobo import JsonReader, JsonWriter
from bonobo import LdjsonReader, LdjsonWriter
from bonobo import JsonReader, JsonWriter, LdjsonReader, LdjsonWriter
from bonobo.constants import EMPTY
from bonobo.util.testing import WriterTest, ReaderTest, ConfigurableNodeTest
from bonobo.util.testing import ConfigurableNodeTest, ReaderTest, WriterTest
FOOBAR = {'foo': 'bar'}
OD_ABC = OrderedDict((('a', 'A'), ('b', 'B'), ('c', 'C')))
FOOBAZ = {'foo': 'baz'}
FOOBAR = {"foo": "bar"}
OD_ABC = OrderedDict((("a", "A"), ("b", "B"), ("c", "C")))
FOOBAZ = {"foo": "baz"}
incontext = ConfigurableNodeTest.incontext
@ -21,7 +20,7 @@ incontext = ConfigurableNodeTest.incontext
class Json:
extension = 'json'
extension = "json"
ReaderNodeType = JsonReader
WriterNodeType = JsonWriter
@ -34,88 +33,61 @@ class JsonReaderDictsTest(Json, ReaderTest, TestCase):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
({
"foo": "bar"
}, ),
({
"baz": "boz"
}, ),
]
assert context.get_buffer() == [({"foo": "bar"},), ({"baz": "boz"},)]
class JsonReaderListsTest(Json, ReaderTest, TestCase):
input_data = '[[1,2,3],\n[4,5,6]]'
input_data = "[[1,2,3],\n[4,5,6]]"
@incontext()
def test_nofields(self, context):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
([1, 2, 3], ),
([4, 5, 6], ),
]
assert context.get_buffer() == [([1, 2, 3],), ([4, 5, 6],)]
@incontext(output_type=tuple)
def test_output_type(self, context):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
([1, 2, 3], ),
([4, 5, 6], ),
]
assert context.get_buffer() == [([1, 2, 3],), ([4, 5, 6],)]
class JsonReaderStringsTest(Json, ReaderTest, TestCase):
input_data = '[' + ',\n'.join(map(json.dumps, ('foo', 'bar', 'baz'))) + ']'
input_data = "[" + ",\n".join(map(json.dumps, ("foo", "bar", "baz"))) + "]"
@incontext()
def test_nofields(self, context):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
('foo', ),
('bar', ),
('baz', ),
]
assert context.get_buffer() == [("foo",), ("bar",), ("baz",)]
@incontext(output_type=tuple)
def test_output_type(self, context):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
('foo', ),
('bar', ),
('baz', ),
]
assert context.get_buffer() == [("foo",), ("bar",), ("baz",)]
class JsonWriterTest(Json, WriterTest, TestCase):
@incontext()
def test_fields(self, context):
context.set_input_fields(['foo', 'bar'])
context.write_sync(('a', 'b'), ('c', 'd'))
context.set_input_fields(["foo", "bar"])
context.write_sync(("a", "b"), ("c", "d"))
context.stop()
assert self.readlines() == (
'[{"foo": "a", "bar": "b"},',
'{"foo": "c", "bar": "d"}]',
)
assert self.readlines() == ('[{"foo": "a", "bar": "b"},', '{"foo": "c", "bar": "d"}]')
@incontext()
def test_fields_from_type(self, context):
context.set_input_type(namedtuple('Point', 'x y'))
context.set_input_type(namedtuple("Point", "x y"))
context.write_sync((1, 2), (3, 4))
context.stop()
assert self.readlines() == (
'[{"x": 1, "y": 2},',
'{"x": 3, "y": 4}]',
)
assert self.readlines() == ('[{"x": 1, "y": 2},', '{"x": 3, "y": 4}]')
@incontext()
def test_nofields_multiple_args(self, context):
@ -144,11 +116,7 @@ class JsonWriterTest(Json, WriterTest, TestCase):
context.write_sync(FOOBAR, OD_ABC, FOOBAZ)
context.stop()
assert self.readlines() == (
'[{"foo": "bar"},',
'{"a": "A", "b": "B", "c": "C"},',
'{"foo": "baz"}]',
)
assert self.readlines() == ('[{"foo": "bar"},', '{"a": "A", "b": "B", "c": "C"},', '{"foo": "baz"}]')
@incontext()
def test_nofields_empty_args(self, context):
@ -156,7 +124,7 @@ class JsonWriterTest(Json, WriterTest, TestCase):
context.write_sync(EMPTY, EMPTY, EMPTY)
context.stop()
assert self.readlines() == ('[]', )
assert self.readlines() == ("[]",)
###
@ -165,7 +133,7 @@ class JsonWriterTest(Json, WriterTest, TestCase):
class Ldjson:
extension = 'ldjson'
extension = "ldjson"
ReaderNodeType = LdjsonReader
WriterNodeType = LdjsonWriter
@ -178,85 +146,61 @@ class LdjsonReaderDictsTest(Ldjson, ReaderTest, TestCase):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
({
"foo": "bar"
}, ),
({
"baz": "boz"
}, ),
]
assert context.get_buffer() == [({"foo": "bar"},), ({"baz": "boz"},)]
class LdjsonReaderListsTest(Ldjson, ReaderTest, TestCase):
input_data = '[1,2,3]\n[4,5,6]'
input_data = "[1,2,3]\n[4,5,6]"
@incontext()
def test_nofields(self, context):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
([1, 2, 3], ),
([4, 5, 6], ),
]
assert context.get_buffer() == [([1, 2, 3],), ([4, 5, 6],)]
@incontext(output_type=tuple)
def test_output_type(self, context):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
([1, 2, 3], ),
([4, 5, 6], ),
]
assert context.get_buffer() == [([1, 2, 3],), ([4, 5, 6],)]
class LdjsonReaderStringsTest(Ldjson, ReaderTest, TestCase):
input_data = '\n'.join(map(json.dumps, ('foo', 'bar', 'baz')))
input_data = "\n".join(map(json.dumps, ("foo", "bar", "baz")))
@incontext()
def test_nofields(self, context):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
('foo', ),
('bar', ),
('baz', ),
]
assert context.get_buffer() == [("foo",), ("bar",), ("baz",)]
@incontext(output_type=tuple)
def test_output_type(self, context):
context.write_sync(EMPTY)
context.stop()
assert context.get_buffer() == [
('foo', ),
('bar', ),
('baz', ),
]
assert context.get_buffer() == [("foo",), ("bar",), ("baz",)]
class LdjsonWriterTest(Ldjson, WriterTest, TestCase):
@incontext()
def test_fields(self, context):
context.set_input_fields(['foo', 'bar'])
context.write_sync(('a', 'b'), ('c', 'd'))
context.set_input_fields(["foo", "bar"])
context.write_sync(("a", "b"), ("c", "d"))
context.stop()
assert self.readlines() == ('{"foo": "a", "bar": "b"}', '{"foo": "c", "bar": "d"}')
@incontext()
def test_fields_from_type(self, context):
context.set_input_type(namedtuple('Point', 'x y'))
context.set_input_type(namedtuple("Point", "x y"))
context.write_sync((1, 2), (3, 4))
context.stop()
assert self.readlines() == (
'{"x": 1, "y": 2}',
'{"x": 3, "y": 4}',
)
assert self.readlines() == ('{"x": 1, "y": 2}', '{"x": 3, "y": 4}')
@incontext()
def test_nofields_multiple_args(self, context):
@ -285,11 +229,7 @@ class LdjsonWriterTest(Ldjson, WriterTest, TestCase):
context.write_sync(FOOBAR, OD_ABC, FOOBAZ)
context.stop()
assert self.readlines() == (
'{"foo": "bar"}',
'{"a": "A", "b": "B", "c": "C"}',
'{"foo": "baz"}',
)
assert self.readlines() == ('{"foo": "bar"}', '{"a": "A", "b": "B", "c": "C"}', '{"foo": "baz"}')
@incontext()
def test_nofields_empty_args(self, context):

View File

@ -7,21 +7,21 @@ from bonobo.constants import EMPTY
from bonobo.execution.contexts.node import NodeExecutionContext
from bonobo.util.testing import BufferingNodeExecutionContext, FilesystemTester
pickle_tester = FilesystemTester('pkl', mode='wb')
pickle_tester.input_data = pickle.dumps([['a', 'b', 'c'], ['a foo', 'b foo', 'c foo'], ['a bar', 'b bar', 'c bar']])
pickle_tester = FilesystemTester("pkl", mode="wb")
pickle_tester.input_data = pickle.dumps([["a", "b", "c"], ["a foo", "b foo", "c foo"], ["a bar", "b bar", "c bar"]])
def test_write_pickled_dict_to_file(tmpdir):
fs, filename, services = pickle_tester.get_services_for_writer(tmpdir)
with NodeExecutionContext(PickleWriter(filename), services=services) as context:
context.write_sync({'foo': 'bar'}, {'foo': 'baz', 'ignore': 'this'})
context.write_sync({"foo": "bar"}, {"foo": "baz", "ignore": "this"})
with fs.open(filename, 'rb') as fp:
assert pickle.loads(fp.read()) == {'foo': 'bar'}
with fs.open(filename, "rb") as fp:
assert pickle.loads(fp.read()) == {"foo": "bar"}
with pytest.raises(AttributeError):
getattr(context, 'file')
getattr(context, "file")
def test_read_pickled_list_from_file(tmpdir):
@ -31,8 +31,5 @@ def test_read_pickled_list_from_file(tmpdir):
context.write_sync(EMPTY)
output = context.get_buffer()
assert context.get_output_fields() == ('a', 'b', 'c')
assert output == [
('a foo', 'b foo', 'c foo'),
('a bar', 'b bar', 'c bar'),
]
assert context.get_output_fields() == ("a", "b", "c")
assert output == [("a foo", "b foo", "c foo"), ("a bar", "b bar", "c bar")]

View File

@ -5,9 +5,9 @@ from unittest.mock import MagicMock
import pytest
import bonobo
from bonobo.constants import NOT_MODIFIED, EMPTY
from bonobo.util import ensure_tuple, ValueHolder
from bonobo.util.testing import BufferingNodeExecutionContext, StaticNodeTest, ConfigurableNodeTest
from bonobo.constants import EMPTY, NOT_MODIFIED
from bonobo.util import ValueHolder, ensure_tuple
from bonobo.util.testing import BufferingNodeExecutionContext, ConfigurableNodeTest, StaticNodeTest
class CountTest(StaticNodeTest, TestCase):
@ -26,7 +26,7 @@ class CountTest(StaticNodeTest, TestCase):
def test_execution(self):
with self.execute() as context:
context.write_sync(*([EMPTY] * 42))
assert context.get_buffer() == [(42, )]
assert context.get_buffer() == [(42,)]
class IdentityTest(StaticNodeTest, TestCase):
@ -81,14 +81,14 @@ def test_tee():
tee = bonobo.Tee(inner)
results = []
for i in range(10):
results.append(tee('foo'))
results.append(tee("foo"))
assert results == [NOT_MODIFIED] * 10
assert len(inner.mock_calls) == 10
def test_noop():
assert bonobo.noop(1, 2, 3, 4, foo='bar') == NOT_MODIFIED
assert bonobo.noop(1, 2, 3, 4, foo="bar") == NOT_MODIFIED
def test_fixedwindow():
@ -98,21 +98,18 @@ def test_fixedwindow():
with BufferingNodeExecutionContext(bonobo.FixedWindow(2)) as context:
context.write_sync(*range(9))
assert context.get_buffer() == [(0, 1), (2, 3), (4, 5), (6, 7), (
8,
None,
)]
assert context.get_buffer() == [(0, 1), (2, 3), (4, 5), (6, 7), (8, None)]
with BufferingNodeExecutionContext(bonobo.FixedWindow(1)) as context:
context.write_sync(*range(3))
assert context.get_buffer() == [(0, ), (1, ), (2, )]
assert context.get_buffer() == [(0,), (1,), (2,)]
def test_methodcaller():
with BufferingNodeExecutionContext(methodcaller('swapcase')) as context:
context.write_sync('aaa', 'bBb', 'CcC')
assert context.get_buffer() == list(map(ensure_tuple, ['AAA', 'BbB', 'cCc']))
with BufferingNodeExecutionContext(methodcaller("swapcase")) as context:
context.write_sync("aaa", "bBb", "CcC")
assert context.get_buffer() == list(map(ensure_tuple, ["AAA", "BbB", "cCc"]))
with BufferingNodeExecutionContext(methodcaller('zfill', 5)) as context:
context.write_sync('a', 'bb', 'ccc')
assert context.get_buffer() == list(map(ensure_tuple, ['0000a', '000bb', '00ccc']))
with BufferingNodeExecutionContext(methodcaller("zfill", 5)) as context:
context.write_sync("a", "bb", "ccc")
assert context.get_buffer() == list(map(ensure_tuple, ["0000a", "000bb", "00ccc"]))

View File

@ -8,11 +8,11 @@ from bonobo.util.bags import BagType
from bonobo.util.envelopes import Envelope
from bonobo.util.testing import BufferingNodeExecutionContext
MyTuple = namedtuple('MyTuple', ['a', 'b', 'c'])
MyBag = BagType('MyBag', ['a', 'b', 'c'])
MyTuple = namedtuple("MyTuple", ["a", "b", "c"])
MyBag = BagType("MyBag", ["a", "b", "c"])
class MyCustomType():
class MyCustomType:
def __init__(self, *args):
self.args = args
@ -20,16 +20,19 @@ class MyCustomType():
return MyBag(*self.args)
@pytest.mark.parametrize(['factory', 'expected', 'expected_item0'], [
[lambda: (1, 2, 3), tuple, int],
[lambda: Envelope((1, 2, 3)), tuple, int],
[lambda: MyTuple(1, 2, 3), MyTuple, int],
[lambda: Envelope(MyTuple(1, 2, 3)), MyTuple, int],
[lambda: MyBag(1, 2, 3), MyBag, int],
[lambda: Envelope(MyBag(1, 2, 3)), MyBag, int],
[lambda: MyCustomType(1, 2, 3), tuple, MyCustomType],
[lambda: Envelope(MyCustomType(1, 2, 3)), tuple, MyCustomType],
])
@pytest.mark.parametrize(
["factory", "expected", "expected_item0"],
[
[lambda: (1, 2, 3), tuple, int],
[lambda: Envelope((1, 2, 3)), tuple, int],
[lambda: MyTuple(1, 2, 3), MyTuple, int],
[lambda: Envelope(MyTuple(1, 2, 3)), MyTuple, int],
[lambda: MyBag(1, 2, 3), MyBag, int],
[lambda: Envelope(MyBag(1, 2, 3)), MyBag, int],
[lambda: MyCustomType(1, 2, 3), tuple, MyCustomType],
[lambda: Envelope(MyCustomType(1, 2, 3)), tuple, MyCustomType],
],
)
def test_casts_after_output(factory: Callable, expected, expected_item0):
def transform():
yield factory()

View File

@ -1,10 +1,11 @@
from unittest.mock import MagicMock
from whistle import EventDispatcher
import bonobo
from bonobo.execution import events
from bonobo.execution.contexts.graph import GraphExecutionContext
from bonobo.plugins.console import ConsoleOutputPlugin
from whistle import EventDispatcher
def test_register_unregister():

View File

@ -1,7 +1,7 @@
import pytest
from unittest.mock import sentinel
import pytest
from bonobo.constants import BEGIN
from bonobo.structs.graphs import Graph
@ -48,24 +48,14 @@ def test_graph_add_chain():
def test_graph_topological_sort():
g = Graph()
g.add_chain(
sentinel.a1,
sentinel.a2,
sentinel.a3,
_input=None,
_output=None,
)
g.add_chain(sentinel.a1, sentinel.a2, sentinel.a3, _input=None, _output=None)
assert g.topologically_sorted_indexes == (0, 1, 2)
assert g[0] == sentinel.a1
assert g[1] == sentinel.a2
assert g[2] == sentinel.a3
g.add_chain(
sentinel.b1,
sentinel.b2,
_output=sentinel.a2,
)
g.add_chain(sentinel.b1, sentinel.b2, _output=sentinel.a2)
assert g.topologically_sorted_indexes[-2:] == (1, 2)
assert g.topologically_sorted_indexes.index(3) < g.topologically_sorted_indexes.index(4)

View File

@ -19,7 +19,7 @@ from queue import Empty
import pytest
from bonobo.constants import BEGIN, END
from bonobo.errors import InactiveWritableError, InactiveReadableError
from bonobo.errors import InactiveReadableError, InactiveWritableError
from bonobo.structs.inputs import Input
@ -29,22 +29,22 @@ def test_input_runlevels():
# Before BEGIN, noone should be able to write in an Input queue.
assert not q.alive
with pytest.raises(InactiveWritableError):
q.put('hello, unborn queue.')
q.put("hello, unborn queue.")
# Begin
q.put(BEGIN)
assert q.alive and q._runlevel == 1
q.put('foo')
q.put("foo")
# Second Begin
q.put(BEGIN)
assert q.alive and q._runlevel == 2
q.put('bar')
q.put("bar")
q.put(END)
# FIFO
assert q.get() == 'foo'
assert q.get() == 'bar'
assert q.get() == "foo"
assert q.get() == "bar"
# self.assertEqual(q.alive, False) XXX queue don't know it's dead yet, but it is ...
# Async get raises Empty (End is not returned)
@ -53,14 +53,14 @@ def test_input_runlevels():
assert q.alive
# Before killing, let's slide some data in.
q.put('baz')
q.put("baz")
# Now kill the queue...
q.put(END)
with pytest.raises(InactiveWritableError):
q.put('foo')
q.put("foo")
# Still can get remaining data
assert q.get() == 'baz'
assert q.get() == "baz"
with pytest.raises(InactiveReadableError):
q.get()

View File

@ -2,5 +2,5 @@ from bonobo.structs.tokens import Token
def test_token_repr():
t = Token('Acme')
assert repr(t) == '<Acme>'
t = Token("Acme")
assert repr(t) == "<Acme>"

View File

@ -11,7 +11,7 @@ def test_run_graph_noop():
graph = bonobo.Graph(bonobo.noop)
assert len(graph) == 1
with patch('bonobo._api._is_interactive_console', side_effect=lambda: False):
with patch("bonobo._api._is_interactive_console", side_effect=lambda: False):
result = bonobo.run(graph)
assert isinstance(result, GraphExecutionContext)

View File

@ -10,7 +10,7 @@ def generate_integers():
def square(i):
return i**2
return i ** 2
def results(f, context):

View File

@ -2,12 +2,12 @@ import inspect
def test_wildcard_import():
bonobo = __import__('bonobo')
bonobo = __import__("bonobo")
assert bonobo.__version__
for name in dir(bonobo):
# ignore attributes starting by underscores
if name.startswith('_'):
if name.startswith("_"):
continue
attr = getattr(bonobo, name)
if inspect.ismodule(attr):

View File

@ -1,18 +1,18 @@
from bonobo.nodes import CsvReader, JsonReader, CsvWriter, JsonWriter
from bonobo import create_reader, create_writer
from bonobo.nodes import CsvReader, CsvWriter, JsonReader, JsonWriter
def test_create_reader():
t = create_reader('foo.csv')
t = create_reader("foo.csv")
assert isinstance(t, CsvReader)
t = create_reader('foo.txt', format='json')
t = create_reader("foo.txt", format="json")
assert isinstance(t, JsonReader)
def test_create_writer():
t = create_writer('foo.csv')
t = create_writer("foo.csv")
assert isinstance(t, CsvWriter)
t = create_writer('foo.txt', format='json')
t = create_writer("foo.txt", format="json")
assert isinstance(t, JsonWriter)

View File

@ -6,37 +6,37 @@ import pytest
from bonobo import settings
TEST_SETTING = 'TEST_SETTING'
TEST_SETTING = "TEST_SETTING"
def test_to_bool():
assert not settings.to_bool('')
assert not settings.to_bool('FALSE')
assert not settings.to_bool('NO')
assert not settings.to_bool('0')
assert not settings.to_bool("")
assert not settings.to_bool("FALSE")
assert not settings.to_bool("NO")
assert not settings.to_bool("0")
assert settings.to_bool('yup')
assert settings.to_bool('True')
assert settings.to_bool('yes')
assert settings.to_bool('1')
assert settings.to_bool("yup")
assert settings.to_bool("True")
assert settings.to_bool("yes")
assert settings.to_bool("1")
def test_setting():
s = settings.Setting(TEST_SETTING)
assert s.get() is None
with patch.dict(environ, {TEST_SETTING: 'hello'}):
with patch.dict(environ, {TEST_SETTING: "hello"}):
assert s.get() is None
s.clear()
assert s.get() == 'hello'
assert s.get() == "hello"
s = settings.Setting(TEST_SETTING, default='nope')
assert s.get() is 'nope'
s = settings.Setting(TEST_SETTING, default="nope")
assert s.get() is "nope"
with patch.dict(environ, {TEST_SETTING: 'hello'}):
assert s.get() == 'nope'
with patch.dict(environ, {TEST_SETTING: "hello"}):
assert s.get() == "nope"
s.clear()
assert s.get() == 'hello'
assert s.get() == "hello"
def test_default_settings():
@ -45,18 +45,18 @@ def test_default_settings():
assert settings.DEBUG.get() is False
assert settings.PROFILE.get() is False
assert settings.QUIET.get() is False
assert settings.LOGGING_LEVEL.get() == logging._checkLevel('INFO')
assert settings.LOGGING_LEVEL.get() == logging._checkLevel("INFO")
with patch.dict(environ, {'DEBUG': 't'}):
with patch.dict(environ, {"DEBUG": "t"}):
settings.clear_all()
assert settings.LOGGING_LEVEL.get() == logging._checkLevel('DEBUG')
assert settings.LOGGING_LEVEL.get() == logging._checkLevel("DEBUG")
settings.clear_all()
def test_check():
settings.check()
with patch.dict(environ, {'DEBUG': 't', 'PROFILE': 't', 'QUIET': 't'}):
with patch.dict(environ, {"DEBUG": "t", "PROFILE": "t", "QUIET": "t"}):
settings.clear_all()
with pytest.raises(RuntimeError):
settings.check()

View File

@ -1 +1 @@
foo = 'bar'
foo = "bar"

View File

@ -16,49 +16,49 @@ from bonobo.util.bags import BagType
### Named Tuples
################################################################################
TBag = BagType('TBag', ('x', 'y', 'z')) # type used for pickle tests
TBag = BagType("TBag", ("x", "y", "z")) # type used for pickle tests
class TestBagType(unittest.TestCase):
def _create(self, *fields, typename='abc'):
def _create(self, *fields, typename="abc"):
bt = BagType(typename, fields)
assert bt._fields == fields
assert len(bt._fields) == len(bt._attrs)
return bt
def test_factory(self):
Point = BagType('Point', ('x', 'y'))
self.assertEqual(Point.__name__, 'Point')
Point = BagType("Point", ("x", "y"))
self.assertEqual(Point.__name__, "Point")
self.assertEqual(Point.__slots__, ())
self.assertEqual(Point.__module__, __name__)
self.assertEqual(Point.__getitem__, tuple.__getitem__)
assert Point._fields == ('x', 'y')
assert Point._attrs == ('x', 'y')
assert Point._fields == ("x", "y")
assert Point._attrs == ("x", "y")
self.assertRaises(ValueError, BagType, 'abc%', ('efg', 'ghi')) # type has non-alpha char
self.assertRaises(ValueError, BagType, 'class', ('efg', 'ghi')) # type has keyword
self.assertRaises(ValueError, BagType, '9abc', ('efg', 'ghi')) # type starts with digit
self.assertRaises(ValueError, BagType, "abc%", ("efg", "ghi")) # type has non-alpha char
self.assertRaises(ValueError, BagType, "class", ("efg", "ghi")) # type has keyword
self.assertRaises(ValueError, BagType, "9abc", ("efg", "ghi")) # type starts with digit
assert self._create('efg', 'g%hi')._attrs == ('efg', 'g_hi')
assert self._create('abc', 'class')._attrs == ('abc', '_class')
assert self._create('8efg', '9ghi')._attrs == ('_8efg', '_9ghi')
assert self._create('_efg', 'ghi')._attrs == ('_efg', 'ghi')
assert self._create("efg", "g%hi")._attrs == ("efg", "g_hi")
assert self._create("abc", "class")._attrs == ("abc", "_class")
assert self._create("8efg", "9ghi")._attrs == ("_8efg", "_9ghi")
assert self._create("_efg", "ghi")._attrs == ("_efg", "ghi")
self.assertRaises(ValueError, BagType, 'abc', ('efg', 'efg', 'ghi')) # duplicate field
self.assertRaises(ValueError, BagType, "abc", ("efg", "efg", "ghi")) # duplicate field
self._create('x1', 'y2', typename='Point0') # Verify that numbers are allowed in names
self._create('a', 'b', 'c', typename='_') # Test leading underscores in a typename
self._create("x1", "y2", typename="Point0") # Verify that numbers are allowed in names
self._create("a", "b", "c", typename="_") # Test leading underscores in a typename
bt = self._create('a!', 'a?')
assert bt._attrs == ('a0', 'a1')
x = bt('foo', 'bar')
assert x.get('a!') == 'foo'
assert x.a0 == 'foo'
assert x.get('a?') == 'bar'
assert x.a1 == 'bar'
bt = self._create("a!", "a?")
assert bt._attrs == ("a0", "a1")
x = bt("foo", "bar")
assert x.get("a!") == "foo"
assert x.a0 == "foo"
assert x.get("a?") == "bar"
assert x.a1 == "bar"
# check unicode output
bt = self._create('the', 'quick', 'brown', 'fox')
bt = self._create("the", "quick", "brown", "fox")
assert "u'" not in repr(bt._fields)
self.assertRaises(TypeError, Point._make, [11]) # catch too few args
@ -66,33 +66,33 @@ class TestBagType(unittest.TestCase):
@unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above")
def test_factory_doc_attr(self):
Point = BagType('Point', ('x', 'y'))
self.assertEqual(Point.__doc__, 'Point(x, y)')
Point = BagType("Point", ("x", "y"))
self.assertEqual(Point.__doc__, "Point(x, y)")
@unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above")
def test_doc_writable(self):
Point = BagType('Point', ('x', 'y'))
Point = BagType("Point", ("x", "y"))
self.assertEqual(Point.x.__doc__, "Alias for 'x'")
Point.x.__doc__ = 'docstring for Point.x'
self.assertEqual(Point.x.__doc__, 'docstring for Point.x')
Point.x.__doc__ = "docstring for Point.x"
self.assertEqual(Point.x.__doc__, "docstring for Point.x")
def test_name_fixer(self):
for spec, renamed in [
[('efg', 'g%hi'), ('efg', 'g_hi')], # field with non-alpha char
[('abc', 'class'), ('abc', '_class')], # field has keyword
[('8efg', '9ghi'), ('_8efg', '_9ghi')], # field starts with digit
[('abc', '_efg'), ('abc', '_efg')], # field with leading underscore
[('abc', '', 'x'), ('abc', '_0', 'x')], # fieldname is a space
[('&', '¨', '*'), ('_0', '_1', '_2')], # Duplicate attrs, in theory
[("efg", "g%hi"), ("efg", "g_hi")], # field with non-alpha char
[("abc", "class"), ("abc", "_class")], # field has keyword
[("8efg", "9ghi"), ("_8efg", "_9ghi")], # field starts with digit
[("abc", "_efg"), ("abc", "_efg")], # field with leading underscore
[("abc", "", "x"), ("abc", "_0", "x")], # fieldname is a space
[("&", "¨", "*"), ("_0", "_1", "_2")], # Duplicate attrs, in theory
]:
assert self._create(*spec)._attrs == renamed
def test_module_parameter(self):
NT = BagType('NT', ['x', 'y'], module=collections)
NT = BagType("NT", ["x", "y"], module=collections)
self.assertEqual(NT.__module__, collections)
def test_instance(self):
Point = self._create('x', 'y', typename='Point')
Point = self._create("x", "y", typename="Point")
p = Point(11, 22)
self.assertEqual(p, Point(x=11, y=22))
self.assertEqual(p, Point(11, y=22))
@ -101,12 +101,12 @@ class TestBagType(unittest.TestCase):
self.assertEqual(p, Point(**dict(x=11, y=22)))
self.assertRaises(TypeError, Point, 1) # too few args
self.assertRaises(TypeError, Point, 1, 2, 3) # too many args
self.assertRaises(TypeError, eval, 'Point(XXX=1, y=2)', locals()) # wrong keyword argument
self.assertRaises(TypeError, eval, 'Point(x=1)', locals()) # missing keyword argument
self.assertEqual(repr(p), 'Point(x=11, y=22)')
self.assertNotIn('__weakref__', dir(p))
self.assertRaises(TypeError, eval, "Point(XXX=1, y=2)", locals()) # wrong keyword argument
self.assertRaises(TypeError, eval, "Point(x=1)", locals()) # missing keyword argument
self.assertEqual(repr(p), "Point(x=11, y=22)")
self.assertNotIn("__weakref__", dir(p))
self.assertEqual(p, Point._make([11, 22])) # test _make classmethod
self.assertEqual(p._fields, ('x', 'y')) # test _fields attribute
self.assertEqual(p._fields, ("x", "y")) # test _fields attribute
self.assertEqual(p._replace(x=1), (1, 22)) # test _replace method
self.assertEqual(p._asdict(), dict(x=11, y=22)) # test _asdict method
@ -115,13 +115,13 @@ class TestBagType(unittest.TestCase):
except ValueError:
pass
else:
self._fail('Did not detect an incorrect fieldname')
self._fail("Did not detect an incorrect fieldname")
p = Point(x=11, y=22)
self.assertEqual(repr(p), 'Point(x=11, y=22)')
self.assertEqual(repr(p), "Point(x=11, y=22)")
def test_tupleness(self):
Point = BagType('Point', ('x', 'y'))
Point = BagType("Point", ("x", "y"))
p = Point(11, 22)
self.assertIsInstance(p, tuple)
@ -137,29 +137,29 @@ class TestBagType(unittest.TestCase):
self.assertEqual(p.x, x)
self.assertEqual(p.y, y)
self.assertRaises(AttributeError, eval, 'p.z', locals())
self.assertRaises(AttributeError, eval, "p.z", locals())
def test_odd_sizes(self):
Zero = BagType('Zero', ())
Zero = BagType("Zero", ())
self.assertEqual(Zero(), ())
self.assertEqual(Zero._make([]), ())
self.assertEqual(repr(Zero()), 'Zero()')
self.assertEqual(repr(Zero()), "Zero()")
self.assertEqual(Zero()._asdict(), {})
self.assertEqual(Zero()._fields, ())
Dot = BagType('Dot', ('d', ))
self.assertEqual(Dot(1), (1, ))
self.assertEqual(Dot._make([1]), (1, ))
Dot = BagType("Dot", ("d",))
self.assertEqual(Dot(1), (1,))
self.assertEqual(Dot._make([1]), (1,))
self.assertEqual(Dot(1).d, 1)
self.assertEqual(repr(Dot(1)), 'Dot(d=1)')
self.assertEqual(Dot(1)._asdict(), {'d': 1})
self.assertEqual(Dot(1)._replace(d=999), (999, ))
self.assertEqual(Dot(1)._fields, ('d', ))
self.assertEqual(repr(Dot(1)), "Dot(d=1)")
self.assertEqual(Dot(1)._asdict(), {"d": 1})
self.assertEqual(Dot(1)._replace(d=999), (999,))
self.assertEqual(Dot(1)._fields, ("d",))
n = 5000 if sys.version_info >= (3, 7) else 254
names = list(set(''.join([choice(string.ascii_letters) for j in range(10)]) for i in range(n)))
names = list(set("".join([choice(string.ascii_letters) for j in range(10)]) for i in range(n)))
n = len(names)
Big = BagType('Big', names)
Big = BagType("Big", names)
b = Big(*range(n))
self.assertEqual(b, tuple(range(n)))
self.assertEqual(Big._make(range(n)), tuple(range(n)))
@ -178,14 +178,14 @@ class TestBagType(unittest.TestCase):
def test_pickle(self):
p = TBag(x=10, y=20, z=30)
for module in (pickle, ):
loads = getattr(module, 'loads')
dumps = getattr(module, 'dumps')
for module in (pickle,):
loads = getattr(module, "loads")
dumps = getattr(module, "dumps")
for protocol in range(-1, module.HIGHEST_PROTOCOL + 1):
q = loads(dumps(p, protocol))
self.assertEqual(p, q)
self.assertEqual(p._fields, q._fields)
self.assertNotIn(b'OrderedDict', dumps(p, protocol))
self.assertNotIn(b"OrderedDict", dumps(p, protocol))
def test_copy(self):
p = TBag(x=10, y=20, z=30)
@ -197,7 +197,7 @@ class TestBagType(unittest.TestCase):
def test_name_conflicts(self):
# Some names like "self", "cls", "tuple", "itemgetter", and "property"
# failed when used as field names. Test to make sure these now work.
T = BagType('T', ('itemgetter', 'property', 'self', 'cls', 'tuple'))
T = BagType("T", ("itemgetter", "property", "self", "cls", "tuple"))
t = T(1, 2, 3, 4, 5)
self.assertEqual(t, (1, 2, 3, 4, 5))
newt = t._replace(itemgetter=10, property=20, self=30, cls=40, tuple=50)
@ -206,28 +206,194 @@ class TestBagType(unittest.TestCase):
# Broader test of all interesting names taken from the code, old
# template, and an example
words = {
'Alias', 'At', 'AttributeError', 'Build', 'Bypass', 'Create', 'Encountered', 'Expected', 'Field', 'For',
'Got', 'Helper', 'IronPython', 'Jython', 'KeyError', 'Make', 'Modify', 'Note', 'OrderedDict', 'Point',
'Return', 'Returns', 'Type', 'TypeError', 'Used', 'Validate', 'ValueError', 'Variables', 'a', 'accessible',
'add', 'added', 'all', 'also', 'an', 'arg_list', 'args', 'arguments', 'automatically', 'be', 'build',
'builtins', 'but', 'by', 'cannot', 'class_namespace', 'classmethod', 'cls', 'collections', 'convert',
'copy', 'created', 'creation', 'd', 'debugging', 'defined', 'dict', 'dictionary', 'doc', 'docstring',
'docstrings', 'duplicate', 'effect', 'either', 'enumerate', 'environments', 'error', 'example', 'exec', 'f',
'f_globals', 'field', 'field_names', 'fields', 'formatted', 'frame', 'function', 'functions', 'generate',
'getter', 'got', 'greater', 'has', 'help', 'identifiers', 'indexable', 'instance', 'instantiate',
'interning', 'introspection', 'isidentifier', 'isinstance', 'itemgetter', 'iterable', 'join', 'keyword',
'keywords', 'kwds', 'len', 'like', 'list', 'map', 'maps', 'message', 'metadata', 'method', 'methods',
'module', 'module_name', 'must', 'name', 'named', 'namedtuple', 'namedtuple_', 'names', 'namespace',
'needs', 'new', 'nicely', 'num_fields', 'number', 'object', 'of', 'operator', 'option', 'p', 'particular',
'pickle', 'pickling', 'plain', 'pop', 'positional', 'property', 'r', 'regular', 'rename', 'replace',
'replacing', 'repr', 'repr_fmt', 'representation', 'result', 'reuse_itemgetter', 's', 'seen', 'sequence',
'set', 'side', 'specified', 'split', 'start', 'startswith', 'step', 'str', 'string', 'strings', 'subclass',
'sys', 'targets', 'than', 'the', 'their', 'this', 'to', 'tuple_new', 'type', 'typename', 'underscore',
'unexpected', 'unpack', 'up', 'use', 'used', 'user', 'valid', 'values', 'variable', 'verbose', 'where',
'which', 'work', 'x', 'y', 'z', 'zip'
"Alias",
"At",
"AttributeError",
"Build",
"Bypass",
"Create",
"Encountered",
"Expected",
"Field",
"For",
"Got",
"Helper",
"IronPython",
"Jython",
"KeyError",
"Make",
"Modify",
"Note",
"OrderedDict",
"Point",
"Return",
"Returns",
"Type",
"TypeError",
"Used",
"Validate",
"ValueError",
"Variables",
"a",
"accessible",
"add",
"added",
"all",
"also",
"an",
"arg_list",
"args",
"arguments",
"automatically",
"be",
"build",
"builtins",
"but",
"by",
"cannot",
"class_namespace",
"classmethod",
"cls",
"collections",
"convert",
"copy",
"created",
"creation",
"d",
"debugging",
"defined",
"dict",
"dictionary",
"doc",
"docstring",
"docstrings",
"duplicate",
"effect",
"either",
"enumerate",
"environments",
"error",
"example",
"exec",
"f",
"f_globals",
"field",
"field_names",
"fields",
"formatted",
"frame",
"function",
"functions",
"generate",
"getter",
"got",
"greater",
"has",
"help",
"identifiers",
"indexable",
"instance",
"instantiate",
"interning",
"introspection",
"isidentifier",
"isinstance",
"itemgetter",
"iterable",
"join",
"keyword",
"keywords",
"kwds",
"len",
"like",
"list",
"map",
"maps",
"message",
"metadata",
"method",
"methods",
"module",
"module_name",
"must",
"name",
"named",
"namedtuple",
"namedtuple_",
"names",
"namespace",
"needs",
"new",
"nicely",
"num_fields",
"number",
"object",
"of",
"operator",
"option",
"p",
"particular",
"pickle",
"pickling",
"plain",
"pop",
"positional",
"property",
"r",
"regular",
"rename",
"replace",
"replacing",
"repr",
"repr_fmt",
"representation",
"result",
"reuse_itemgetter",
"s",
"seen",
"sequence",
"set",
"side",
"specified",
"split",
"start",
"startswith",
"step",
"str",
"string",
"strings",
"subclass",
"sys",
"targets",
"than",
"the",
"their",
"this",
"to",
"tuple_new",
"type",
"typename",
"underscore",
"unexpected",
"unpack",
"up",
"use",
"used",
"user",
"valid",
"values",
"variable",
"verbose",
"where",
"which",
"work",
"x",
"y",
"z",
"zip",
}
sorted_words = tuple(sorted(words))
T = BagType('T', sorted_words)
T = BagType("T", sorted_words)
# test __new__
values = tuple(range(len(words)))
t = T(*values)
@ -252,27 +418,39 @@ class TestBagType(unittest.TestCase):
self.assertEqual(t.__getnewargs__(), values)
def test_repr(self):
A = BagType('A', ('x', ))
self.assertEqual(repr(A(1)), 'A(x=1)')
A = BagType("A", ("x",))
self.assertEqual(repr(A(1)), "A(x=1)")
# repr should show the name of the subclass
class B(A):
pass
self.assertEqual(repr(B(1)), 'B(x=1)')
self.assertEqual(repr(B(1)), "B(x=1)")
def test_namedtuple_subclass_issue_24931(self):
class Point(BagType('_Point', ['x', 'y'])):
class Point(BagType("_Point", ["x", "y"])):
pass
a = Point(3, 4)
self.assertEqual(a._asdict(), OrderedDict([('x', 3), ('y', 4)]))
self.assertEqual(a._asdict(), OrderedDict([("x", 3), ("y", 4)]))
a.w = 5
self.assertEqual(a.__dict__, {'w': 5})
self.assertEqual(a.__dict__, {"w": 5})
def test_annoying_attribute_names(self):
self._create(
'__slots__', '__getattr__', '_attrs', '_fields', '__new__', '__getnewargs__', '__repr__', '_make', 'get',
'_replace', '_asdict', '_cls', 'self', 'tuple'
"__slots__",
"__getattr__",
"_attrs",
"_fields",
"__new__",
"__getnewargs__",
"__repr__",
"_make",
"get",
"_replace",
"_asdict",
"_cls",
"self",
"tuple",
)

View File

@ -1,7 +1,7 @@
import pytest
from bonobo.util import sortedlist, ensure_tuple
from bonobo.util.collections import tuplize, cast
from bonobo.util import ensure_tuple, sortedlist
from bonobo.util.collections import cast, tuplize
def test_sortedlist():
@ -14,20 +14,20 @@ def test_sortedlist():
def test_ensure_tuple():
assert ensure_tuple('a') == ('a', )
assert ensure_tuple(('a', )) == ('a', )
assert ensure_tuple("a") == ("a",)
assert ensure_tuple(("a",)) == ("a",)
assert ensure_tuple(()) is ()
@pytest.mark.parametrize('tuplize', [tuplize, cast(tuple)])
@pytest.mark.parametrize("tuplize", [tuplize, cast(tuple)])
def test_tuplize(tuplize):
tuplized_lambda = tuplize(lambda: [1, 2, 3])
assert tuplized_lambda() == (1, 2, 3)
@tuplize
def some_generator():
yield 'c'
yield 'b'
yield 'a'
yield "c"
yield "b"
yield "a"
assert some_generator() == ('c', 'b', 'a')
assert some_generator() == ("c", "b", "a")

View File

@ -17,7 +17,7 @@ def test_deprecated_alias():
def foo():
pass
foo = deprecated_alias('bar', foo)
foo = deprecated_alias("bar", foo)
with pytest.warns(DeprecationWarning):
foo()

View File

@ -2,7 +2,7 @@ import operator
import pytest
from bonobo.util.objects import Wrapper, get_name, ValueHolder, get_attribute_or_create
from bonobo.util.objects import ValueHolder, Wrapper, get_attribute_or_create, get_name
from bonobo.util.testing import optional_contextmanager
@ -11,31 +11,31 @@ class foo:
class bar:
__name__ = 'baz'
__name__ = "baz"
def test_get_name():
assert get_name(42) == 'int'
assert get_name('eat at joe.') == 'str'
assert get_name(str) == 'str'
assert get_name(object) == 'object'
assert get_name(get_name) == 'get_name'
assert get_name(foo) == 'foo'
assert get_name(foo()) == 'foo'
assert get_name(bar) == 'bar'
assert get_name(bar()) == 'baz'
assert get_name(42) == "int"
assert get_name("eat at joe.") == "str"
assert get_name(str) == "str"
assert get_name(object) == "object"
assert get_name(get_name) == "get_name"
assert get_name(foo) == "foo"
assert get_name(foo()) == "foo"
assert get_name(bar) == "bar"
assert get_name(bar()) == "baz"
def test_wrapper_name():
assert get_name(Wrapper(42)) == 'int'
assert get_name(Wrapper('eat at joe.')) == 'str'
assert get_name(Wrapper(str)) == 'str'
assert get_name(Wrapper(object)) == 'object'
assert get_name(Wrapper(foo)) == 'foo'
assert get_name(Wrapper(foo())) == 'foo'
assert get_name(Wrapper(bar)) == 'bar'
assert get_name(Wrapper(bar())) == 'baz'
assert get_name(Wrapper(get_name)) == 'get_name'
assert get_name(Wrapper(42)) == "int"
assert get_name(Wrapper("eat at joe.")) == "str"
assert get_name(Wrapper(str)) == "str"
assert get_name(Wrapper(object)) == "object"
assert get_name(Wrapper(foo)) == "foo"
assert get_name(Wrapper(foo())) == "foo"
assert get_name(Wrapper(bar)) == "bar"
assert get_name(Wrapper(bar())) == "baz"
assert get_name(Wrapper(get_name)) == "get_name"
def test_valueholder():
@ -65,10 +65,7 @@ def test_valueholder_notequal():
assert not (x != 42)
@pytest.mark.parametrize('rlo,rhi', [
(1, 2),
('a', 'b'),
])
@pytest.mark.parametrize("rlo,rhi", [(1, 2), ("a", "b")])
def test_valueholder_ordering(rlo, rhi):
vlo, vhi = ValueHolder(rlo), ValueHolder(rhi)
@ -97,15 +94,15 @@ def test_valueholders_containers():
assert 5 in x
assert 42 not in x
y = ValueHolder({'foo': 'bar', 'corp': 'acme'})
y = ValueHolder({"foo": "bar", "corp": "acme"})
assert 'foo' in y
assert y['foo'] == 'bar'
assert "foo" in y
assert y["foo"] == "bar"
with pytest.raises(KeyError):
y['no']
y['no'] = 'oh, wait'
assert 'no' in y
assert 'oh, wait' == y['no']
y["no"]
y["no"] = "oh, wait"
assert "no" in y
assert "oh, wait" == y["no"]
def test_get_attribute_or_create():
@ -117,27 +114,39 @@ def test_get_attribute_or_create():
with pytest.raises(AttributeError):
x.foo
foo = get_attribute_or_create(x, 'foo', 'bar')
assert foo == 'bar'
assert x.foo == 'bar'
foo = get_attribute_or_create(x, "foo", "bar")
assert foo == "bar"
assert x.foo == "bar"
foo = get_attribute_or_create(x, 'foo', 'baz')
assert foo == 'bar'
assert x.foo == 'bar'
foo = get_attribute_or_create(x, "foo", "baz")
assert foo == "bar"
assert x.foo == "bar"
unsupported_operations = {
int: {operator.matmul},
str: {
operator.sub, operator.mul, operator.matmul, operator.floordiv, operator.truediv, operator.mod, divmod,
operator.pow, operator.lshift, operator.rshift, operator.and_, operator.xor, operator.or_
operator.sub,
operator.mul,
operator.matmul,
operator.floordiv,
operator.truediv,
operator.mod,
divmod,
operator.pow,
operator.lshift,
operator.rshift,
operator.and_,
operator.xor,
operator.or_,
},
}
@pytest.mark.parametrize('x,y', [(5, 3), (0, 10), (0, 0), (1, 1), ('foo', 'bar'), ('', 'baz!')])
@pytest.mark.parametrize("x,y", [(5, 3), (0, 10), (0, 0), (1, 1), ("foo", "bar"), ("", "baz!")])
@pytest.mark.parametrize(
'operation,inplace_operation', [
"operation,inplace_operation",
[
(operator.add, operator.iadd),
(operator.sub, operator.isub),
(operator.mul, operator.imul),
@ -152,14 +161,14 @@ unsupported_operations = {
(operator.and_, operator.iand),
(operator.xor, operator.ixor),
(operator.or_, operator.ior),
]
],
)
def test_valueholder_integer_operations(x, y, operation, inplace_operation):
v = ValueHolder(x)
is_supported = operation not in unsupported_operations.get(type(x), set())
isdiv = ('div' in operation.__name__) or ('mod' in operation.__name__)
isdiv = ("div" in operation.__name__) or ("mod" in operation.__name__)
# forward...
with optional_contextmanager(pytest.raises(TypeError), ignore=is_supported):

View File

@ -3,16 +3,16 @@ from bonobo.util.resolvers import _parse_option, _resolve_options, _resolve_tran
def test_parse_option():
assert _parse_option('foo=bar') == ('foo', 'bar')
assert _parse_option('foo="bar"') == ('foo', 'bar')
assert _parse_option('sep=";"') == ('sep', ';')
assert _parse_option('foo') == ('foo', True)
assert _parse_option("foo=bar") == ("foo", "bar")
assert _parse_option('foo="bar"') == ("foo", "bar")
assert _parse_option('sep=";"') == ("sep", ";")
assert _parse_option("foo") == ("foo", True)
def test_resolve_options():
assert _resolve_options(('foo=bar', 'bar="baz"')) == {'foo': 'bar', 'bar': 'baz'}
assert _resolve_options(("foo=bar", 'bar="baz"')) == {"foo": "bar", "bar": "baz"}
assert _resolve_options() == {}
def test_resolve_transformations():
assert _resolve_transformations(('PrettyPrinter', )) == (bonobo.PrettyPrinter, )
assert _resolve_transformations(("PrettyPrinter",)) == (bonobo.PrettyPrinter,)

View File

@ -3,12 +3,9 @@ from bonobo.util.statistics import WithStatistics
class MyThingWithStats(WithStatistics):
def get_statistics(self, *args, **kwargs):
return (
('foo', 42),
('bar', 69),
)
return (("foo", 42), ("bar", 69))
def test_with_statistics():
o = MyThingWithStats()
assert o.get_statistics_as_string() == 'foo=42 bar=69'
assert o.get_statistics_as_string() == "foo=42 bar=69"