Rename TupleOfTupleValue to SingleNestedTupleValue; Add SingleNestedListValue and do a DRY refactor of ListValue, TupleValue, and their SingleNested subclasses

This commit is contained in:
Christian Abbott 2015-01-07 03:08:30 -08:00
parent c4ba5ca559
commit e0a68fdbb6
3 changed files with 128 additions and 67 deletions

View file

@ -161,39 +161,97 @@ class DecimalValue(CastingMixin, Value):
exception = decimal.InvalidOperation
class ListValue(Value):
class SequenceValue(Value):
"""
Common code for sequence-type values (lists and tuples).
Do not use this class directly. Instead use a subclass.
"""
# Specify this value in subclasses, e.g. with 'list' or 'tuple'
sequence_type = None
converter = None
message = 'Cannot interpret list item {0!r} in list {1!r}'
def __init__(self, *args, **kwargs):
msg = 'Cannot interpret {0} item {{0!r}} in {0} {{1!r}}'
self.message = msg.format(self.sequence_type.__name__)
self.separator = kwargs.pop('separator', ',')
converter = kwargs.pop('converter', None)
if converter is not None:
self.converter = converter
super(ListValue, self).__init__(*args, **kwargs)
# make sure the default is a list
super(SequenceValue, self).__init__(*args, **kwargs)
# make sure the default is the correct sequence type
if self.default is None:
self.default = []
self.default = self.sequence_type()
else:
self.default = self.sequence_type(self.default)
# initial conversion
if self.converter is not None:
self.default = self._convert(self.default)
def _convert(self, list_):
def _convert(self, sequence):
converted_values = []
for value in list_:
for value in sequence:
try:
converted_values.append(self.converter(value))
except (TypeError, ValueError):
raise ValueError(self.message.format(value, value))
return converted_values
return self.sequence_type(converted_values)
def to_python(self, value):
split_value = [v.strip() for v in value.strip().split(self.separator)]
# removing empty items
value_list = filter(None, split_value)
if self.converter is None:
return list(value_list)
return self._convert(value_list)
if self.converter is not None:
value_list = self._convert(value_list)
return self.sequence_type(value_list)
class ListValue(SequenceValue):
sequence_type = list
class TupleValue(SequenceValue):
sequence_type = tuple
class SingleNestedSequenceValue(SequenceValue):
"""
Common code for nested sequences (list of lists, or tuple of tuples).
Do not use this class directly. Instead use a subclass.
"""
def __init__(self, *args, **kwargs):
self.seq_separator = kwargs.pop('seq_separator', ';')
super(SingleNestedSequenceValue, self).__init__(*args, **kwargs)
def _convert(self, items):
# This could receive either a bare or nested sequence
if items and isinstance(items[0], self.sequence_type):
converted_sequences = [
super(SingleNestedSequenceValue, self)._convert(i) for i in items
]
return self.sequence_type(converted_sequences)
return self.sequence_type(
super(SingleNestedSequenceValue, self)._convert(items))
def to_python(self, value):
split_value = [
v.strip() for v in value.strip().split(self.seq_separator)
]
# Remove empty items
filtered = filter(None, split_value)
sequence = [
super(SingleNestedSequenceValue, self).to_python(f) for f in filtered
]
return self.sequence_type(sequence)
class SingleNestedListValue(SingleNestedSequenceValue):
sequence_type = list
class SingleNestedTupleValue(SingleNestedSequenceValue):
sequence_type = tuple
class BackendsValue(ListValue):
@ -206,47 +264,6 @@ class BackendsValue(ListValue):
return value
class TupleValue(ListValue):
message = 'Cannot interpret tuple item {0!r} in tuple {1!r}'
def __init__(self, *args, **kwargs):
super(TupleValue, self).__init__(*args, **kwargs)
if self.default is None:
self.default = ()
else:
self.default = tuple(self.default)
def to_python(self, value):
return tuple(super(TupleValue, self).to_python(value))
class TupleOfTuplesValue(TupleValue):
def __init__(self, *args, **kwargs):
self.tuple_separator = kwargs.pop('tuple_separator', ';')
super(TupleOfTuplesValue, self).__init__(*args, **kwargs)
def _convert(self, items):
# This could receive either a bare tuple or tuple of tuples
if items and isinstance(items[0], tuple):
converted_tuples = []
for inner in items:
converted = super(TupleOfTuplesValue, self)._convert(inner)
converted_tuples.append(tuple(converted))
return tuple(converted_tuples)
return tuple(super(TupleOfTuplesValue, self)._convert(items))
def to_python(self, value):
split_value = [
v.strip() for v in value.strip().split(self.tuple_separator)
]
# Remove empty items
value_list = filter(None, split_value)
tuples = [
super(TupleOfTuplesValue, self).to_python(v) for v in value_list
]
return tuple(tuples)
class SetValue(ListValue):
message = 'Cannot interpret set item {0!r} in set {1!r}'

View file

@ -231,7 +231,7 @@ Type values
.. class:: ListValue(default, [separator=',', converter=None])
A :class:`~Value` subclass that handles list values.
A :class:`~SequenceValue` subclass that handles list values.
:param separator: the separator to split environment variables with
:param converter: the optional converter callable to apply for each list
@ -266,7 +266,7 @@ Type values
.. class:: TupleValue
A :class:`~Value` subclass that handles tuple values.
A :class:`~SequenceValue` subclass that handles tuple values.
:param separator: the separator to split environment variables with
:param converter: the optional converter callable to apply for each tuple
@ -274,18 +274,19 @@ Type values
See the :class:`~ListValue` examples above.
.. class:: TupleOfTuplesValue(default, [tuple_separator=';', separator=',', converter=None])
.. class:: SingleNestedTupleValue(default, [seq_separator=';', separator=',', converter=None])
A :class:`~TupleValue` subclass that handles tuple of tuples values.
A :class:`~SingleNestedSequenceValue` subclass that handles single nested tuple values,
e.g. ``((a, b), (c, d))``.
:param tuple_separator: the separator to split each tuple with
:param seq_separator: the separator to split each tuple with
:param separator: the separator to split the inner tuple contents with
:param converter: the optional converter callable to apply for each inner
tuple item
Useful for ADMINS, MANAGERS, and the like. For example::
ADMINS = TupleOfTuplesValue((
ADMINS = SingleNestedTupleValue((
('John', 'jcleese@site.com'),
('Eric', 'eidle@site.com'),
))
@ -294,6 +295,17 @@ Type values
DJANGO_ADMINS=Terry,tjones@site.com;Graham,gchapman@site.com
.. class:: SingleNestedListValue(default, [seq_separator=';', separator=',', converter=None])
A :class:`~SingleNestedSequenceValue` subclass that handles single nested list values,
e.g. ``[[a, b], [c, d]]``.
:param seq_separator: the separator to split each list with
:param separator: the separator to split the inner list contents with
:param converter: the optional converter callable to apply for each inner
list item
See the :class:`~SingleNestedTupleValue` examples above.
.. class:: SetValue

View file

@ -9,7 +9,8 @@ from mock import patch
from configurations.values import (Value, BooleanValue, IntegerValue,
FloatValue, DecimalValue, ListValue,
TupleValue, TupleOfTuplesValue, SetValue,
TupleValue, SingleNestedTupleValue,
SingleNestedListValue, SetValue,
DictValue, URLValue, EmailValue, IPValue,
RegexValue, PathValue, SecretValue,
DatabaseURLValue, EmailURLValue,
@ -175,8 +176,39 @@ class ValueTests(TestCase):
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), ())
def test_tuple_of_tuples_values_default(self):
value = TupleOfTuplesValue()
def test_single_nested_list_values_default(self):
value = SingleNestedListValue()
with env(DJANGO_TEST='2,3;4,5'):
expected = [['2', '3'], ['4', '5']]
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2;3;4;5'):
expected = [['2'], ['3'], ['4'], ['5']]
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2,3,4,5'):
expected = [['2', '3', '4', '5']]
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST='2, 3 , ; 4 , 5 ; '):
expected = [['2', '3'], ['4', '5']]
self.assertEqual(value.setup('TEST'), expected)
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), [])
def test_single_nested_list_values_separator(self):
value = SingleNestedListValue(seq_separator=':')
with env(DJANGO_TEST='2,3:4,5'):
self.assertEqual(value.setup('TEST'), [['2', '3'], ['4', '5']])
def test_single_nested_list_values_converter(self):
value = SingleNestedListValue(converter=int)
with env(DJANGO_TEST='2,3;4,5'):
self.assertEqual(value.setup('TEST'), [[2, 3], [4, 5]])
def test_single_nested_list_values_converter_default(self):
value = SingleNestedListValue([['2', '3'], ['4', '5']], converter=int)
self.assertEqual(value.value, [[2, 3], [4, 5]])
def test_single_nested_tuple_values_default(self):
value = SingleNestedTupleValue()
with env(DJANGO_TEST='2,3;4,5'):
expected = (('2', '3'), ('4', '5'))
self.assertEqual(value.setup('TEST'), expected)
@ -192,18 +224,18 @@ class ValueTests(TestCase):
with env(DJANGO_TEST=''):
self.assertEqual(value.setup('TEST'), ())
def test_tuple_of_tuples_values_separator(self):
value = TupleOfTuplesValue(tuple_separator=':')
def test_single_nested_tuple_values_separator(self):
value = SingleNestedTupleValue(seq_separator=':')
with env(DJANGO_TEST='2,3:4,5'):
self.assertEqual(value.setup('TEST'), (('2', '3'), ('4', '5')))
def test_tuple_of_tuples_values_converter(self):
value = TupleOfTuplesValue(converter=int)
def test_single_nested_tuple_values_converter(self):
value = SingleNestedTupleValue(converter=int)
with env(DJANGO_TEST='2,3;4,5'):
self.assertEqual(value.setup('TEST'), ((2, 3), (4, 5)))
def test_tuple_of_tuples_values_converter_default(self):
value = TupleOfTuplesValue((('2', '3'), ('4', '5')), converter=int)
def test_single_nested_tuple_values_converter_default(self):
value = SingleNestedTupleValue((('2', '3'), ('4', '5')), converter=int)
self.assertEqual(value.value, ((2, 3), (4, 5)))
def test_set_values_default(self):