summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLin Tan <lin.tan@intel.com>2015-12-01 15:52:28 +0800
committerLin Tan <lin.tan@intel.com>2015-12-08 14:38:20 +0800
commitf8aa239ddc83a442a9b1d1929d1ee68003227022 (patch)
treecd2eb00ef4014f58d4de7f3e50fa3c375cf8b801
parent9dd719f83aa888efee7f9bcbc8799e86b2ec8504 (diff)
downloadoslo-config-f8aa239ddc83a442a9b1d1929d1ee68003227022.tar.gz
Add new parameter `choices` to Integer type
There is a need to add choices to PortOpt. So make Integer type accept choices parameter first. Choices options will provide a set of valid values. min/max provide a range of values. They are mutally exclusive. Change-Id: I0906b6e2309d751d7c31589fc3da8e5f3026bf64
-rw-r--r--oslo_config/tests/test_types.py43
-rw-r--r--oslo_config/types.py52
2 files changed, 84 insertions, 11 deletions
diff --git a/oslo_config/tests/test_types.py b/oslo_config/tests/test_types.py
index 5f114c7..38e16e7 100644
--- a/oslo_config/tests/test_types.py
+++ b/oslo_config/tests/test_types.py
@@ -242,6 +242,10 @@ class IntegerTypeTests(TypeTestHelper, unittest.TestCase):
t = types.Integer(min=0, max=0)
self.assertEqual('Integer(min=0, max=0)', repr(t))
+ def test_repr_with_choices(self):
+ t = types.Integer(choices=[80, 457])
+ self.assertEqual('Integer(choices=[80, 457])', repr(t))
+
def test_equal(self):
self.assertTrue(types.Integer() == types.Integer())
@@ -256,12 +260,35 @@ class IntegerTypeTests(TypeTestHelper, unittest.TestCase):
t2 = types.Integer(min=1, max=123)
self.assertTrue(t1 == t2)
+ def test_equal_with_same_choices(self):
+ t1 = types.Integer(choices=[80, 457])
+ t2 = types.Integer(choices=[457, 80])
+ self.assertTrue(t1 == t2)
+
def test_not_equal(self):
self.assertFalse(types.Integer(min=123) == types.Integer(min=456))
+ self.assertFalse(types.Integer(choices=[80, 457]) ==
+ types.Integer(choices=[80, 40]))
+ self.assertFalse(types.Integer(choices=[80, 457]) ==
+ types.Integer())
def test_not_equal_to_other_class(self):
self.assertFalse(types.Integer() == types.String())
+ def test_choices_with_min_max(self):
+ self.assertRaises(ValueError,
+ types.Integer,
+ min=10,
+ choices=[50, 60])
+ self.assertRaises(ValueError,
+ types.Integer,
+ max=100,
+ choices=[50, 60])
+ self.assertRaises(ValueError,
+ types.Integer,
+ min=10, max=100,
+ choices=[50, 60])
+
def test_min_greater_max(self):
self.assertRaises(ValueError,
types.Integer,
@@ -305,6 +332,22 @@ class IntegerTypeTests(TypeTestHelper, unittest.TestCase):
self.assertRaises(ValueError, t, 201)
self.assertRaises(ValueError, t, -457)
+ def test_with_choices_list(self):
+ t = types.Integer(choices=[80, 457])
+ self.assertRaises(ValueError, t, 1)
+ self.assertRaises(ValueError, t, 200)
+ self.assertRaises(ValueError, t, -457)
+ t(80)
+ t(457)
+
+ def test_with_choices_tuple(self):
+ t = types.Integer(choices=(80, 457))
+ self.assertRaises(ValueError, t, 1)
+ self.assertRaises(ValueError, t, 200)
+ self.assertRaises(ValueError, t, -457)
+ t(80)
+ t(457)
+
class FloatTypeTests(TypeTestHelper, unittest.TestCase):
type = types.Float()
diff --git a/oslo_config/types.py b/oslo_config/types.py
index 671272e..7d96ec0 100644
--- a/oslo_config/types.py
+++ b/oslo_config/types.py
@@ -192,23 +192,37 @@ class Integer(ConfigType):
Converts value to an integer optionally doing range checking.
If value is whitespace or empty string will return None.
- :param min: Optional check that value is greater than or equal to min
- :param max: Optional check that value is less than or equal to max
+ :param min: Optional check that value is greater than or equal to min.
+ Mutually exclusive with 'choices'.
+ :param max: Optional check that value is less than or equal to max.
+ Mutually exclusive with 'choices'.
:param type_name: Type name to be used in the sample config file.
+ :param choices: Optional sequence of valid values. Mutually exclusive
+ with 'min/max'.
.. versionchanged:: 2.4
The class now honors zero for *min* and *max* parameters.
.. versionchanged:: 2.7
Added *type_name* parameter.
+
+ .. versionchanged:: 3.2
+ Added *choices* parameter.
"""
- def __init__(self, min=None, max=None, type_name='integer value'):
+ def __init__(self, min=None, max=None, type_name='integer value',
+ choices=None):
super(Integer, self).__init__(type_name=type_name)
+ if choices is not None:
+ if min is not None or max is not None:
+ raise ValueError("'choices' and 'min/max' cannot both be "
+ "specified")
+ else:
+ if min is not None and max is not None and max < min:
+ raise ValueError('Max value is less than min value')
self.min = min
self.max = max
- if min is not None and max is not None and max < min:
- raise ValueError('Max value is less than min value')
+ self.choices = choices
def __call__(self, value):
if not isinstance(value, int):
@@ -219,10 +233,20 @@ class Integer(ConfigType):
value = int(value)
if value is not None:
- self._check_range(value)
+ if self.choices is not None:
+ self._check_choices(value)
+ else:
+ self._check_range(value)
return value
+ def _check_choices(self, value):
+ if value in self.choices:
+ return
+ else:
+ raise ValueError('Valid values are %r, but found %d' % (
+ self.choices, value))
+
def _check_range(self, value):
if self.min is not None and value < self.min:
raise ValueError('Should be greater than or equal to %d' %
@@ -232,10 +256,13 @@ class Integer(ConfigType):
def __repr__(self):
props = []
- if self.min is not None:
- props.append('min=%d' % self.min)
- if self.max is not None:
- props.append('max=%d' % self.max)
+ if self.choices is not None:
+ props.append("choices=%r" % (self.choices,))
+ else:
+ if self.min is not None:
+ props.append('min=%d' % self.min)
+ if self.max is not None:
+ props.append('max=%d' % self.max)
if props:
return 'Integer(%s)' % ', '.join(props)
@@ -245,7 +272,10 @@ class Integer(ConfigType):
return (
(self.__class__ == other.__class__) and
(self.min == other.min) and
- (self.max == other.max)
+ (self.max == other.max) and
+ (set(self.choices) == set(other.choices) if
+ self.choices and other.choices else
+ self.choices == other.choices)
)