mirror of
https://github.com/jazzband/django-configurations.git
synced 2026-03-16 22:20:27 +00:00
Rename TupleOfTupleValue to SingleNestedTupleValue; Add SingleNestedListValue and do a DRY refactor of ListValue, TupleValue, and their SingleNested subclasses
This commit is contained in:
parent
c4ba5ca559
commit
e0a68fdbb6
3 changed files with 128 additions and 67 deletions
|
|
@ -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}'
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in a new issue