diff options
author | Lin Tan <lin.tan@intel.com> | 2015-12-01 15:52:28 +0800 |
---|---|---|
committer | Lin Tan <lin.tan@intel.com> | 2015-12-08 14:38:20 +0800 |
commit | f8aa239ddc83a442a9b1d1929d1ee68003227022 (patch) | |
tree | cd2eb00ef4014f58d4de7f3e50fa3c375cf8b801 | |
parent | 9dd719f83aa888efee7f9bcbc8799e86b2ec8504 (diff) | |
download | oslo-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.py | 43 | ||||
-rw-r--r-- | oslo_config/types.py | 52 |
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) ) |