add warning ability
This commit is contained in:
parent
5ba97c5928
commit
06baff2f3b
|
@ -3,7 +3,6 @@ from py.test import raises
|
|||
|
||||
from tiramisu.config import Config
|
||||
from tiramisu.option import StrOption, OptionDescription
|
||||
from tiramisu.error import ConfigError
|
||||
|
||||
|
||||
def return_true(value, param=None):
|
||||
|
@ -13,37 +12,36 @@ def return_true(value, param=None):
|
|||
|
||||
def return_false(value, param=None):
|
||||
if value == 'val' and param in [None, 'yes']:
|
||||
return False
|
||||
raise ValueError('error')
|
||||
|
||||
|
||||
def return_val(value, param=None):
|
||||
return 'val'
|
||||
|
||||
|
||||
def return_if_val(value):
|
||||
if value != 'val':
|
||||
raise ValueError('error')
|
||||
|
||||
|
||||
def test_validator():
|
||||
opt1 = StrOption('opt1', '', validator=return_true, default='val')
|
||||
raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')")
|
||||
raises(ConfigError, "StrOption('opt3', '', validator=return_val, default='val')")
|
||||
opt2 = StrOption('opt2', '', validator=return_false)
|
||||
opt3 = StrOption('opt3', '', validator=return_val)
|
||||
root = OptionDescription('root', '', [opt1, opt2, opt3])
|
||||
root = OptionDescription('root', '', [opt1, opt2])
|
||||
cfg = Config(root)
|
||||
assert cfg.opt1 == 'val'
|
||||
raises(ValueError, "cfg.opt2 = 'val'")
|
||||
raises(ConfigError, "cfg.opt3 = 'val'")
|
||||
|
||||
|
||||
def test_validator_params():
|
||||
opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ('yes',)}, default='val')
|
||||
raises(ValueError, "StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}, default='val')")
|
||||
raises(ConfigError, "StrOption('opt3', '', validator=return_val, validator_params={'': ('yes',)}, default='val')")
|
||||
opt2 = StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)})
|
||||
opt3 = StrOption('opt3', '', validator=return_val, validator_params={'': ('yes',)})
|
||||
root = OptionDescription('root', '', [opt1, opt2, opt3])
|
||||
root = OptionDescription('root', '', [opt1, opt2])
|
||||
cfg = Config(root)
|
||||
assert cfg.opt1 == 'val'
|
||||
raises(ValueError, "cfg.opt2 = 'val'")
|
||||
raises(ConfigError, "cfg.opt3 = 'val'")
|
||||
|
||||
|
||||
def test_validator_params_key():
|
||||
|
@ -57,3 +55,36 @@ def test_validator_params_key():
|
|||
def test_validator_params_option():
|
||||
opt0 = StrOption('opt0', '', default='val')
|
||||
raises(ValueError, "opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ((opt0, False),)}, default='val')")
|
||||
|
||||
|
||||
def test_validator_multi():
|
||||
opt1 = StrOption('opt1', '', validator=return_if_val, multi=True)
|
||||
root = OptionDescription('root', '', [opt1])
|
||||
cfg = Config(root)
|
||||
assert cfg.opt1 == []
|
||||
cfg.opt1.append('val')
|
||||
assert cfg.opt1 == ['val']
|
||||
raises(ValueError, "cfg.opt1.append('val1')")
|
||||
raises(ValueError, "cfg.opt1 = ['val', 'val1']")
|
||||
|
||||
|
||||
def test_validator_warning():
|
||||
opt1 = StrOption('opt1', '', validator=return_true, default='val', only_warning=True)
|
||||
opt2 = StrOption('opt2', '', validator=return_false, only_warning=True)
|
||||
opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, only_warning=True)
|
||||
root = OptionDescription('root', '', [opt1, opt2, opt3])
|
||||
cfg = Config(root)
|
||||
assert cfg.opt1 == 'val'
|
||||
cfg.opt1 = 'val'
|
||||
assert cfg.cfgimpl_get_values().has_warning() is False
|
||||
cfg.opt2 = 'val'
|
||||
assert cfg.cfgimpl_get_values().has_warning() is True
|
||||
assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option opt2: error'
|
||||
assert cfg.cfgimpl_get_values().has_warning() is False
|
||||
cfg.opt3.append('val')
|
||||
assert cfg.cfgimpl_get_values().has_warning() is False
|
||||
cfg.opt3.append('val1')
|
||||
assert cfg.cfgimpl_get_values().has_warning() is True
|
||||
assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option opt3: error'
|
||||
assert cfg.cfgimpl_get_values().has_warning() is False
|
||||
raises(ValueError, "cfg.opt2 = 1")
|
||||
|
|
|
@ -26,7 +26,7 @@ from copy import copy, deepcopy
|
|||
from types import FunctionType
|
||||
from IPy import IP
|
||||
|
||||
from tiramisu.error import ConflictError, ConfigError
|
||||
from tiramisu.error import ConflictError
|
||||
from tiramisu.setting import groups, multitypes
|
||||
from tiramisu.i18n import _
|
||||
from tiramisu.autolib import carry_out_calculation
|
||||
|
@ -327,13 +327,13 @@ class Option(BaseOption):
|
|||
"""
|
||||
__slots__ = ('_multi', '_validator', '_default_multi', '_default',
|
||||
'_state_callback', '_callback', '_multitype',
|
||||
'_master_slaves', '__weakref__')
|
||||
'_only_warning', '_master_slaves', '__weakref__')
|
||||
_empty = ''
|
||||
|
||||
def __init__(self, name, doc, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, validator=None, validator_params=None,
|
||||
properties=None):
|
||||
properties=None, only_warning=False):
|
||||
"""
|
||||
:param name: the option's name
|
||||
:param doc: the option's description
|
||||
|
@ -351,6 +351,8 @@ class Option(BaseOption):
|
|||
validation of the value
|
||||
:param validator_params: the validator's parameters
|
||||
:param properties: tuple of default properties
|
||||
:param only_warning: _validator and _consistencies don't raise if True
|
||||
Values()._warning contain message
|
||||
|
||||
"""
|
||||
super(Option, self).__init__(name, doc, requires, properties)
|
||||
|
@ -388,6 +390,7 @@ class Option(BaseOption):
|
|||
default = []
|
||||
self._multitype = multitypes.default
|
||||
self._default_multi = default_multi
|
||||
self._only_warning = only_warning
|
||||
self.impl_validate(default)
|
||||
self._default = default
|
||||
|
||||
|
@ -436,10 +439,17 @@ class Option(BaseOption):
|
|||
if None not in (values, values_):
|
||||
getattr(self, func)(opt_._name, values, values_)
|
||||
|
||||
def impl_validate(self, value, context=None, validate=True):
|
||||
def impl_validate(self, value, context=None, validate=True,
|
||||
force_no_multi=False):
|
||||
"""
|
||||
:param value: the option's value
|
||||
:param context: Config's context
|
||||
:type context: :class:`tiramisu.config.Config`
|
||||
:param validate: if true enables ``self._validator`` validation
|
||||
:type validate: boolean
|
||||
:param force_no_multi: if multi, value has to be a list
|
||||
not if force_no_multi is True
|
||||
:type force_no_multi: boolean
|
||||
"""
|
||||
if not validate:
|
||||
return
|
||||
|
@ -456,46 +466,49 @@ class Option(BaseOption):
|
|||
validator_params[''] = (val,)
|
||||
else:
|
||||
validator_params = {'': (val,)}
|
||||
ret = carry_out_calculation(self._name, config=context,
|
||||
# Raise ValueError if not valid
|
||||
carry_out_calculation(self._name, config=context,
|
||||
callback=self._validator[0],
|
||||
callback_params=validator_params)
|
||||
if ret not in [False, True]:
|
||||
raise ConfigError(_('validator should return a boolean, '
|
||||
'not {0}').format(ret))
|
||||
return ret
|
||||
else:
|
||||
return True
|
||||
|
||||
def do_validation(_value, _index=None):
|
||||
if _value is None:
|
||||
return True
|
||||
if not val_validator(_value):
|
||||
raise ValueError(_("invalid value {0} "
|
||||
"for option {1} for object {2}"
|
||||
).format(_value,
|
||||
self._name,
|
||||
self.__class__.__name__))
|
||||
ret_validation = None
|
||||
try:
|
||||
self._validate(_value)
|
||||
except ValueError as err:
|
||||
raise ValueError(_("invalid value {0} for option {1}: {2}"
|
||||
"").format(_value, self._name, err))
|
||||
# valid with self._validator
|
||||
val_validator(_value)
|
||||
# if not context launch consistency validation
|
||||
if context is not None:
|
||||
descr._valid_consistency(self, _value, context, _index)
|
||||
except ValueError as err:
|
||||
msg = _("invalid value {0} for option {1}: {2}").format(
|
||||
_value, self._name, err)
|
||||
if self._only_warning:
|
||||
ret_validation = msg
|
||||
else:
|
||||
raise ValueError(msg)
|
||||
# option validation
|
||||
self._validate(_value)
|
||||
return ret_validation
|
||||
|
||||
# generic calculation
|
||||
if context is not None:
|
||||
descr = context.cfgimpl_get_description()
|
||||
if not self._multi:
|
||||
do_validation(value)
|
||||
|
||||
ret = None
|
||||
if not self._multi or force_no_multi:
|
||||
ret = do_validation(value)
|
||||
else:
|
||||
if not isinstance(value, list):
|
||||
raise ValueError(_("invalid value {0} for option {1} "
|
||||
"which must be a list").format(value,
|
||||
self._name))
|
||||
for index in range(0, len(value)):
|
||||
val = value[index]
|
||||
do_validation(val, index)
|
||||
for index, val in enumerate(value):
|
||||
ret_ = do_validation(val, index)
|
||||
if ret_ is not None:
|
||||
ret = ret_
|
||||
return ret
|
||||
|
||||
def impl_getdefault(self, default_multi=False):
|
||||
"accessing the default value"
|
||||
|
@ -610,7 +623,7 @@ class ChoiceOption(Option):
|
|||
def __init__(self, name, doc, values, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, open_values=False, validator=None,
|
||||
validator_params=None, properties=()):
|
||||
validator_params=None, properties=None, only_warning=False):
|
||||
"""
|
||||
:param values: is a list of values the option can possibly take
|
||||
"""
|
||||
|
@ -629,7 +642,8 @@ class ChoiceOption(Option):
|
|||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
properties=properties)
|
||||
properties=properties,
|
||||
only_warning=only_warning)
|
||||
|
||||
def impl_get_values(self):
|
||||
return self._values
|
||||
|
@ -747,7 +761,8 @@ class IPOption(Option):
|
|||
def __init__(self, name, doc, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, validator=None, validator_params=None,
|
||||
properties=None, only_private=False, allow_reserved=False):
|
||||
properties=None, only_private=False, allow_reserved=False,
|
||||
only_warning=False):
|
||||
self._only_private = only_private
|
||||
self._allow_reserved = allow_reserved
|
||||
super(IPOption, self).__init__(name, doc, default=default,
|
||||
|
@ -758,7 +773,8 @@ class IPOption(Option):
|
|||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
properties=properties)
|
||||
properties=properties,
|
||||
only_warning=only_warning)
|
||||
|
||||
def _validate(self, value):
|
||||
ip = IP('{0}/32'.format(value))
|
||||
|
@ -786,7 +802,7 @@ class PortOption(Option):
|
|||
callback_params=None, validator=None, validator_params=None,
|
||||
properties=None, allow_range=False, allow_zero=False,
|
||||
allow_wellknown=True, allow_registred=True,
|
||||
allow_private=False):
|
||||
allow_private=False, only_warning=False):
|
||||
self._allow_range = allow_range
|
||||
self._min_value = None
|
||||
self._max_value = None
|
||||
|
@ -818,7 +834,8 @@ class PortOption(Option):
|
|||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
properties=properties)
|
||||
properties=properties,
|
||||
only_warning=only_warning)
|
||||
|
||||
def _validate(self, value):
|
||||
if self._allow_range and ":" in str(value):
|
||||
|
@ -864,7 +881,6 @@ class NetmaskOption(Option):
|
|||
#opts must be (netmask, ip) options
|
||||
self.__cons_netmask(optname, value, value_, True)
|
||||
|
||||
#def __cons_netmask(self, opt, value, context, index, opts, make_net):
|
||||
def __cons_netmask(self, optname, val_netmask, val_ipnetwork, make_net):
|
||||
msg = None
|
||||
try:
|
||||
|
@ -903,7 +919,8 @@ class DomainnameOption(Option):
|
|||
def __init__(self, name, doc, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, validator=None, validator_params=None,
|
||||
properties=None, allow_ip=False, type_='domainname'):
|
||||
properties=None, allow_ip=False, type_='domainname',
|
||||
only_warning=False):
|
||||
#netbios: for MS domain
|
||||
#hostname: to identify the device
|
||||
#domainname:
|
||||
|
@ -922,7 +939,8 @@ class DomainnameOption(Option):
|
|||
multi=multi,
|
||||
validator=validator,
|
||||
validator_params=validator_params,
|
||||
properties=properties)
|
||||
properties=properties,
|
||||
only_warning=only_warning)
|
||||
|
||||
def _validate(self, value):
|
||||
if self._allow_ip is True:
|
||||
|
|
|
@ -33,7 +33,7 @@ class Values(object):
|
|||
but the values are physicaly located here, in `Values`, wich is also
|
||||
responsible of a caching utility.
|
||||
"""
|
||||
__slots__ = ('context', '_p_', '__weakref__')
|
||||
__slots__ = ('context', '_warning', '_p_', '__weakref__')
|
||||
|
||||
def __init__(self, context, storage):
|
||||
"""
|
||||
|
@ -106,7 +106,8 @@ class Values(object):
|
|||
path = self._get_opt_path(opt)
|
||||
if self._p_.hasvalue(path):
|
||||
setting = self.context().cfgimpl_get_settings()
|
||||
opt.impl_validate(opt.impl_getdefault(), self.context(),
|
||||
self._warning = opt.impl_validate(opt.impl_getdefault(),
|
||||
self.context(),
|
||||
'validator' in setting)
|
||||
self.context().cfgimpl_reset_cache()
|
||||
if (opt.impl_is_multi() and
|
||||
|
@ -229,7 +230,8 @@ class Values(object):
|
|||
else:
|
||||
value = self._getvalue(opt, path, validate)
|
||||
if config_error is None and validate:
|
||||
opt.impl_validate(value, self.context(), 'validator' in setting)
|
||||
self._warning = opt.impl_validate(value, self.context(),
|
||||
'validator' in setting)
|
||||
if config_error is None and self._is_default_owner(path) and \
|
||||
'force_store_value' in setting[opt]:
|
||||
self.setitem(opt, value, path, is_write=False)
|
||||
|
@ -250,8 +252,9 @@ class Values(object):
|
|||
# is_write is, for example, used with "force_store_value"
|
||||
# user didn't change value, so not write
|
||||
# valid opt
|
||||
opt.impl_validate(value, self.context(),
|
||||
'validator' in self.context().cfgimpl_get_settings())
|
||||
self._warning = opt.impl_validate(value, self.context(),
|
||||
'validator' in self.context(
|
||||
).cfgimpl_get_settings())
|
||||
if opt.impl_is_multi() and not isinstance(value, Multi):
|
||||
value = Multi(value, self.context, opt, path, setitem=True)
|
||||
self._setvalue(opt, path, value, force_permissive=force_permissive,
|
||||
|
@ -370,6 +373,22 @@ class Values(object):
|
|||
def __setstate__(self, states):
|
||||
self._p_ = states['_p_']
|
||||
|
||||
def has_warning(self):
|
||||
"""If option is "only_warning", validation error is store in
|
||||
self._warning.
|
||||
has_warning just indicate that a warning message is store
|
||||
"""
|
||||
return self._warning is not None
|
||||
|
||||
def get_last_warning(self):
|
||||
"""Get last warning message in self._warning.
|
||||
We can get only one time this message.
|
||||
"""
|
||||
ret = self._warning
|
||||
self._warning = None
|
||||
return ret
|
||||
|
||||
|
||||
# ____________________________________________________________
|
||||
# multi types
|
||||
|
||||
|
@ -476,7 +495,9 @@ class Multi(list):
|
|||
value = None
|
||||
self._validate(value)
|
||||
super(Multi, self).append(value)
|
||||
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force)
|
||||
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path,
|
||||
self,
|
||||
validate_properties=not force)
|
||||
if not force and self.opt.impl_get_multitype() == multitypes.master:
|
||||
for slave in self.opt.impl_get_master_slaves():
|
||||
path = values._get_opt_path(slave)
|
||||
|
@ -537,7 +558,9 @@ class Multi(list):
|
|||
def _validate(self, value):
|
||||
if value is not None:
|
||||
try:
|
||||
self.opt._validate(value)
|
||||
self.context().cfgimpl_get_values()._warning = \
|
||||
self.opt.impl_validate(value, context=self.context(),
|
||||
force_no_multi=True)
|
||||
except ValueError as err:
|
||||
raise ValueError(_("invalid value {0} "
|
||||
"for option {1}: {2}"
|
||||
|
|
Loading…
Reference in New Issue