diff --git a/bonobo/config/configurables.py b/bonobo/config/configurables.py index 1d4fdb2..77801fa 100644 --- a/bonobo/config/configurables.py +++ b/bonobo/config/configurables.py @@ -26,16 +26,19 @@ class ConfigurableMeta(type): if isinstance(value, ContextProcessor): cls.__processors__.append(value) else: + if not value.name: + value.name = name + if isinstance(value, Method): if cls.__wrappable__: raise ConfigurationError( 'Cannot define more than one "Method" option in a configurable. That may change in the future.' ) cls.__wrappable__ = name - if not value.name: - value.name = name + if not name in cls.__options__: cls.__options__[name] = value + if value.positional: cls.__positional_options__.append(name) @@ -53,11 +56,9 @@ class Configurable(metaclass=ConfigurableMeta): def __new__(cls, *args, **kwargs): if cls.__wrappable__ and len(args) == 1 and hasattr(args[0], '__call__'): - wrapped, args = args[0], args[1:] - return type(wrapped.__name__, (cls, ), {cls.__wrappable__: wrapped}) + return type(args[0].__name__, (cls,), {cls.__wrappable__: args[0]}) - # XXX is that correct ??? how does it pass args/kwargs to __init__ ??? - return super().__new__(cls) + return super(Configurable, cls).__new__(cls) def __init__(self, *args, **kwargs): super().__init__() diff --git a/bonobo/config/options.py b/bonobo/config/options.py index 7fbdf54..545d1ca 100644 --- a/bonobo/config/options.py +++ b/bonobo/config/options.py @@ -67,11 +67,12 @@ class Option: def __set__(self, inst, value): inst.__options_values__[self.name] = self.clean(value) + def clean(self, value): + return self.type(value) if self.type else value + def get_default(self): return self.default() if callable(self.default) else self.default - def clean(self, value): - return self.type(value) if self.type else value class Method(Option): @@ -106,7 +107,7 @@ class Method(Option): """ def __init__(self): - super().__init__(None, required=False, positional=True) + super().__init__(None, required=False) def __get__(self, inst, typ): if not self.name in inst.__options_values__: @@ -114,6 +115,8 @@ class Method(Option): return inst.__options_values__[self.name] def __set__(self, inst, value): + if isinstance(value, str): + raise ValueError('should be callable') inst.__options_values__[self.name] = self.type(value) if self.type else value def clean(self, value): diff --git a/tests/test_config_method.py b/tests/test_config_method.py index 2798b31..dbca538 100644 --- a/tests/test_config_method.py +++ b/tests/test_config_method.py @@ -15,7 +15,6 @@ class MethodBasedConfigurable(Configurable): def test_one_wrapper_only(): with pytest.raises(ConfigurationError): - class TwoMethods(Configurable): h1 = Method() h2 = Method() @@ -28,7 +27,12 @@ def test_define_with_decorator(): def Concrete(self, *args, **kwargs): calls.append((args, kwargs, )) + print('handler', Concrete.handler) + + assert callable(Concrete.handler) t = Concrete('foo', bar='baz') + + assert callable(t.handler) assert len(calls) == 0 t() assert len(calls) == 1 @@ -41,6 +45,7 @@ def test_define_with_argument(): calls.append((args, kwargs, )) t = MethodBasedConfigurable('foo', bar='baz', handler=concrete_handler) + assert callable(t.handler) assert len(calls) == 0 t() assert len(calls) == 1 @@ -54,6 +59,7 @@ def test_define_with_inheritance(): calls.append((args, kwargs, )) t = Inheriting('foo', bar='baz') + assert callable(t.handler) assert len(calls) == 0 t() assert len(calls) == 1 @@ -69,7 +75,9 @@ def test_inheritance_then_decorate(): def Concrete(self, *args, **kwargs): calls.append((args, kwargs, )) + assert callable(Concrete.handler) t = Concrete('foo', bar='baz') + assert callable(t.handler) assert len(calls) == 0 t() assert len(calls) == 1