Check config default value is correct type
Given a type of Opt, check that the default value supplied to the Opt is of the same type as the Opt. E.g. cfg.StrOpt('user_attribute_ignore', default=99) will log a warning as the default is not of type string. Closes-Bug: 1261792 Change-Id: I12cd5f4be82ac5ed0ebb348b358dd8eaf04e4e0d
This commit is contained in:
parent
e82f6bb933
commit
f7c54d9ae2
@ -661,6 +661,27 @@ class Opt(object):
|
||||
if deprecated_name is not None or deprecated_group is not None:
|
||||
self.deprecated_opts.append(DeprecatedOpt(deprecated_name,
|
||||
group=deprecated_group))
|
||||
self._assert_default_is_of_opt_type()
|
||||
|
||||
def _default_is_ref(self):
|
||||
"""Check if default is a reference to another var."""
|
||||
if isinstance(self.default, six.string_types):
|
||||
tmpl = self.default.replace('\$', '').replace('$$', '')
|
||||
return '$' in tmpl
|
||||
return False
|
||||
|
||||
def _assert_default_is_of_opt_type(self):
|
||||
if (self.default is not None
|
||||
and not self._default_is_ref()
|
||||
and hasattr(self.type, 'is_base_type')
|
||||
and not self.type.is_base_type(self.default)):
|
||||
# NOTE(tcammann) Change this to raise error after K relase
|
||||
expected_types = ", ".join(
|
||||
[t.__name__ for t in self.type.BASE_TYPES])
|
||||
LOG.debug(('Expected default value of type(s) {0} but got '
|
||||
'"{1}" of type {2}').format(expected_types,
|
||||
self.default,
|
||||
type(self.default).__name__))
|
||||
|
||||
def __ne__(self, another):
|
||||
return vars(self) != vars(another)
|
||||
@ -999,11 +1020,11 @@ class MultiOpt(Opt):
|
||||
|
||||
class MultiStrOpt(MultiOpt):
|
||||
|
||||
"""Multi opt with String item type (for backward compatibility)."""
|
||||
"""Multi opt with MultiString item type (for backward compatibility)."""
|
||||
|
||||
def __init__(self, name, **kwargs):
|
||||
super(MultiStrOpt, self).__init__(name,
|
||||
item_type=types.String(),
|
||||
item_type=types.MultiString(),
|
||||
**kwargs)
|
||||
|
||||
|
||||
|
@ -19,9 +19,18 @@ Use these classes as values for the `type` argument to
|
||||
|
||||
"""
|
||||
import netaddr
|
||||
import six
|
||||
|
||||
|
||||
class String(object):
|
||||
class ConfigType(object):
|
||||
|
||||
BASE_TYPES = (None,)
|
||||
|
||||
def is_base_type(self, other):
|
||||
return isinstance(other, self.BASE_TYPES)
|
||||
|
||||
|
||||
class String(ConfigType):
|
||||
|
||||
"""String type.
|
||||
|
||||
@ -35,6 +44,8 @@ class String(object):
|
||||
container types like List.
|
||||
"""
|
||||
|
||||
BASE_TYPES = six.string_types
|
||||
|
||||
def __init__(self, choices=None, quotes=False):
|
||||
super(String, self).__init__()
|
||||
self.choices = choices
|
||||
@ -69,17 +80,23 @@ class String(object):
|
||||
)
|
||||
|
||||
|
||||
class Boolean(object):
|
||||
class MultiString(String):
|
||||
|
||||
BASE_TYPES = six.string_types + (list,)
|
||||
|
||||
|
||||
class Boolean(ConfigType):
|
||||
|
||||
"""Boolean type.
|
||||
|
||||
Values are case insensitive and can be set using
|
||||
1/0, yes/no, true/false or on/off.
|
||||
"""
|
||||
|
||||
TRUE_VALUES = ['true', '1', 'on', 'yes']
|
||||
FALSE_VALUES = ['false', '0', 'off', 'no']
|
||||
|
||||
BASE_TYPES = (bool,)
|
||||
|
||||
def __call__(self, value):
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
@ -99,7 +116,7 @@ class Boolean(object):
|
||||
return self.__class__ == other.__class__
|
||||
|
||||
|
||||
class Integer(object):
|
||||
class Integer(ConfigType):
|
||||
|
||||
"""Integer type.
|
||||
|
||||
@ -110,6 +127,8 @@ class Integer(object):
|
||||
:param max: Optional check that value is less than or equal to max
|
||||
"""
|
||||
|
||||
BASE_TYPES = six.integer_types
|
||||
|
||||
def __init__(self, min=None, max=None):
|
||||
super(Integer, self).__init__()
|
||||
self.min = min
|
||||
@ -156,10 +175,13 @@ class Integer(object):
|
||||
)
|
||||
|
||||
|
||||
class Float(object):
|
||||
class Float(ConfigType):
|
||||
|
||||
"""Float type."""
|
||||
|
||||
# allow float to be set from int
|
||||
BASE_TYPES = six.integer_types + (float,)
|
||||
|
||||
def __call__(self, value):
|
||||
if isinstance(value, float):
|
||||
return value
|
||||
@ -173,7 +195,7 @@ class Float(object):
|
||||
return self.__class__ == other.__class__
|
||||
|
||||
|
||||
class List(object):
|
||||
class List(ConfigType):
|
||||
|
||||
"""List type.
|
||||
|
||||
@ -189,6 +211,8 @@ class List(object):
|
||||
:param bounds: if True, value should be inside "[" and "]" pair
|
||||
"""
|
||||
|
||||
BASE_TYPES = (list,)
|
||||
|
||||
def __init__(self, item_type=None, bounds=False):
|
||||
super(List, self).__init__()
|
||||
|
||||
@ -247,7 +271,7 @@ class List(object):
|
||||
)
|
||||
|
||||
|
||||
class Dict(object):
|
||||
class Dict(ConfigType):
|
||||
|
||||
"""Dictionary type.
|
||||
|
||||
@ -260,6 +284,8 @@ class Dict(object):
|
||||
:param bounds: if True, value should be inside "{" and "}" pair
|
||||
"""
|
||||
|
||||
BASE_TYPES = (dict,)
|
||||
|
||||
def __init__(self, value_type=None, bounds=False):
|
||||
super(Dict, self).__init__()
|
||||
|
||||
@ -335,7 +361,7 @@ class Dict(object):
|
||||
)
|
||||
|
||||
|
||||
class IPAddress(object):
|
||||
class IPAddress(ConfigType):
|
||||
|
||||
"""IP address type
|
||||
|
||||
@ -346,7 +372,10 @@ class IPAddress(object):
|
||||
|
||||
"""
|
||||
|
||||
BASE_TYPES = six.string_types
|
||||
|
||||
def __init__(self, version=None):
|
||||
super(IPAddress, self).__init__()
|
||||
version_checkers = {
|
||||
None: self._check_both_versions,
|
||||
4: self._check_ipv4,
|
||||
|
@ -368,6 +368,9 @@ class CliOptsTestCase(BaseTestCase):
|
||||
('float_arg_deprecated_group_and_name',
|
||||
dict(opt_class=cfg.FloatOpt, default=None,
|
||||
cli_args=['--old-oof', '2.0'], value=2.0, deps=('oof', 'old'))),
|
||||
('float_default_as_integer',
|
||||
dict(opt_class=cfg.FloatOpt, default=2,
|
||||
cli_args=['--old-oof', '2.0'], value=2.0, deps=('oof', 'old'))),
|
||||
('ipv4addr_arg',
|
||||
dict(opt_class=IPv4Opt, default=None,
|
||||
cli_args=['--foo', '192.168.0.1'], value='192.168.0.1',
|
||||
@ -849,17 +852,10 @@ class ConfigFileOptsTestCase(BaseTestCase):
|
||||
self.assertTrue(hasattr(self.conf, 'foo'))
|
||||
self.assertEqual(self.conf.foo, 666)
|
||||
|
||||
def test_conf_file_int_wrong_default(self):
|
||||
self.conf.register_opt(cfg.IntOpt('foo', default='t666'))
|
||||
|
||||
paths = self.create_tempfiles([('test',
|
||||
'[DEFAULT]\n')])
|
||||
|
||||
self.conf(['--config-file', paths[0]])
|
||||
self.assertRaises(AttributeError,
|
||||
getattr,
|
||||
self.conf,
|
||||
'foo')
|
||||
@mock.patch.object(cfg, 'LOG')
|
||||
def test_conf_file_int_wrong_default(self, mock_log):
|
||||
cfg.IntOpt('foo', default='666')
|
||||
mock_log.debug.assert_call_count(1)
|
||||
|
||||
def test_conf_file_int_value(self):
|
||||
self.conf.register_opt(cfg.IntOpt('foo'))
|
||||
@ -922,17 +918,10 @@ class ConfigFileOptsTestCase(BaseTestCase):
|
||||
self.assertTrue(hasattr(self.conf, 'foo'))
|
||||
self.assertEqual(self.conf.foo, 6.66)
|
||||
|
||||
def test_conf_file_float_default_wrong_type(self):
|
||||
self.conf.register_opt(cfg.FloatOpt('foo', default='foobar6.66'))
|
||||
|
||||
paths = self.create_tempfiles([('test',
|
||||
'[DEFAULT]\n')])
|
||||
|
||||
self.conf(['--config-file', paths[0]])
|
||||
self.assertRaises(AttributeError,
|
||||
getattr,
|
||||
self.conf,
|
||||
'foo')
|
||||
@mock.patch.object(cfg, 'LOG')
|
||||
def test_conf_file_float_default_wrong_type(self, mock_log):
|
||||
cfg.FloatOpt('foo', default='foobar6.66')
|
||||
mock_log.debug.assert_call_count(1)
|
||||
|
||||
def test_conf_file_float_value(self):
|
||||
self.conf.register_opt(cfg.FloatOpt('foo'))
|
||||
@ -995,6 +984,13 @@ class ConfigFileOptsTestCase(BaseTestCase):
|
||||
self.assertTrue(hasattr(self.conf, 'foo'))
|
||||
self.assertEqual(self.conf.foo, ['bar'])
|
||||
|
||||
@mock.patch.object(cfg, 'LOG')
|
||||
def test_conf_file_list_default_wrong_type(self, mock_log):
|
||||
cfg.ListOpt('foo', default=25)
|
||||
mock_log.debug.assert_called_once_with(
|
||||
'Expected default value of type(s) list but '
|
||||
'got "25" of type int')
|
||||
|
||||
def test_conf_file_list_value(self):
|
||||
self.conf.register_opt(cfg.ListOpt('foo'))
|
||||
|
||||
|
@ -55,8 +55,9 @@ class GeneratorTestCase(base.BaseTestCase):
|
||||
deprecated_group='group1',
|
||||
deprecated_name='foobar',
|
||||
help='deprecated'),
|
||||
# Unknown Opt default must be a string
|
||||
'unknown_type': cfg.Opt('unknown_opt',
|
||||
default=123,
|
||||
default='123',
|
||||
help='unknown'),
|
||||
'str_opt': cfg.StrOpt('str_opt',
|
||||
default='foo bar',
|
||||
|
Loading…
x
Reference in New Issue
Block a user