Fix Method to be non positional as there is a randomly happening bug that I cannot trace.

This commit is contained in:
Romain Dorgueil
2017-05-22 10:35:24 +02:00
parent 88df694dc1
commit a45a6830c7
3 changed files with 22 additions and 10 deletions

View File

@ -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__()

View File

@ -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):

View File

@ -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