add warning ability

This commit is contained in:
Emmanuel Garette 2013-09-24 23:19:20 +02:00
parent 5ba97c5928
commit 06baff2f3b
3 changed files with 128 additions and 56 deletions

View file

@ -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")

View file

@ -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,
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
# Raise ValueError if not valid
carry_out_calculation(self._name, config=context,
callback=self._validator[0],
callback_params=validator_params)
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)
# 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:
raise ValueError(_("invalid value {0} for option {1}: {2}"
"").format(_value, self._name, err))
if context is not None:
descr._valid_consistency(self, _value, context, _index)
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:

View file

@ -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,8 +106,9 @@ 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(),
'validator' in setting)
self._warning = opt.impl_validate(opt.impl_getdefault(),
self.context(),
'validator' in setting)
self.context().cfgimpl_reset_cache()
if (opt.impl_is_multi() and
opt.impl_get_multitype() == multitypes.master):
@ -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}"