consistencies can make a warning instead of raises

for that, you have to set something like:
  a.impl_add_consistency('not_equal', b, warnings_only=True)

warning product now adapted message
This commit is contained in:
2014-03-12 21:56:53 +01:00
parent d7b04ebed0
commit ff802b46e5
5 changed files with 391 additions and 282 deletions

View File

@ -336,7 +336,7 @@ class Option(BaseOption):
self._consistencies = None
def _launch_consistency(self, func, option, value, context, index,
all_cons_opts):
all_cons_opts, warnings_only):
"""Launch consistency now
:param func: function name, this name should start with _cons_
@ -351,6 +351,8 @@ class Option(BaseOption):
:type index: `int`
:param all_cons_opts: all options concerne by this consistency
:type all_cons_opts: `list` of `tiramisu.option.Option`
:param warnings_only: specific raise error for warning
:type warnings_only: `boolean`
"""
if context is not None:
descr = context.cfgimpl_get_description()
@ -379,7 +381,7 @@ class Option(BaseOption):
except IndexError:
#so return if no value
return
getattr(self, func)(all_cons_opts, all_cons_vals)
getattr(self, func)(all_cons_opts, all_cons_vals, warnings_only)
def impl_validate(self, value, context=None, validate=True,
force_index=None):
@ -422,22 +424,31 @@ class Option(BaseOption):
except ValueError as err:
raise ValueError(_('invalid value for option {0}: {1}'
'').format(self._name, err))
error = None
warning = None
try:
# valid with self._validator
val_validator(_value)
# if not context launch consistency validation
# if context launch consistency validation
if context is not None:
descr._valid_consistency(self, _value, context, _index)
self._second_level_validation(_value)
except ValueError as err:
msg = _("invalid value for option {0}: {1}").format(
self._name, err)
descr._valid_consistency(self, _value, context, _index,
self._warnings_only)
self._second_level_validation(_value, self._warnings_only)
except ValueError as error:
if self._warnings_only:
warnings.warn_explicit(ValueWarning(msg, self),
ValueWarning,
self.__class__.__name__, 0)
else:
raise ValueError(msg)
warning = error
error = None
except ValueWarning as warning:
pass
if warning:
msg = _("warning on the value of the option {0}: {1}").format(
self._name, warning)
warnings.warn_explicit(ValueWarning(msg, self),
ValueWarning,
self.__class__.__name__, 0)
elif error:
raise ValueError(_("invalid value for option {0}: {1}").format(
self._name, error))
# generic calculation
if context is not None:
@ -490,7 +501,7 @@ class Option(BaseOption):
def impl_is_multi(self):
return self._multi
def impl_add_consistency(self, func, *other_opts):
def impl_add_consistency(self, func, *other_opts, **params):
"""Add consistency means that value will be validate with other_opts
option's values.
@ -498,16 +509,18 @@ class Option(BaseOption):
:type func: `str`
:param other_opts: options used to validate value
:type other_opts: `list` of `tiramisu.option.Option`
:param params: extra params (only warnings_only are allowed)
"""
if self._consistencies is None:
self._consistencies = []
warnings_only = params.get('warnings_only', False)
for opt in other_opts:
if not isinstance(opt, Option):
raise ConfigError(_('consistency should be set with an option'))
raise ConfigError(_('consistency must be set with an option'))
if self is opt:
raise ConfigError(_('cannot add consistency with itself'))
if self.impl_is_multi() != opt.impl_is_multi():
raise ConfigError(_('every options in consistency should be '
raise ConfigError(_('every options in consistency must be '
'multi or none'))
func = '_cons_{0}'.format(func)
all_cons_opts = tuple([self] + list(other_opts))
@ -516,19 +529,23 @@ class Option(BaseOption):
if self.impl_is_multi():
for idx, val in enumerate(value):
self._launch_consistency(func, self, val, None,
idx, all_cons_opts)
idx, all_cons_opts, warnings_only)
else:
self._launch_consistency(func, self, value, None,
None, all_cons_opts)
self._consistencies.append((func, all_cons_opts))
None, all_cons_opts, warnings_only)
self._consistencies.append((func, all_cons_opts, params))
self.impl_validate(self.impl_getdefault())
def _cons_not_equal(self, opts, vals):
def _cons_not_equal(self, opts, vals, warnings_only):
for idx_inf, val_inf in enumerate(vals):
for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
if val_inf == val_sup is not None:
raise ValueError(_("same value for {0} and {1}").format(
opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name))
if warnings_only:
msg = _("same value for {0} and {1}, should be different")
else:
msg = _("same value for {0} and {1}, must be different")
raise ValueError(msg.format(opts[idx_inf]._name,
opts[idx_inf + idx_sup + 1]._name))
def _impl_convert_callbacks(self, descr, load=False):
if not load and self._callback is None:
@ -592,14 +609,14 @@ class Option(BaseOption):
values.append(descr.impl_get_opt_by_path(obj))
else:
values.append(descr.impl_get_path_by_opt(obj))
new_value.append((consistency[0], tuple(values)))
new_value.append((consistency[0], tuple(values), consistency[2]))
if load:
del(self._state_consistencies)
self._consistencies = new_value
else:
self._state_consistencies = new_value
def _second_level_validation(self, value):
def _second_level_validation(self, value, warnings_only):
pass
@ -777,24 +794,36 @@ class IPOption(Option):
except ValueError:
raise ValueError(_('invalid IP'))
def _second_level_validation(self, value):
def _second_level_validation(self, value, warnings_only):
ip = IP('{0}/32'.format(value))
if not self._allow_reserved and ip.iptype() == 'RESERVED':
raise ValueError(_("invalid IP, mustn't not be in reserved class"))
if warnings_only:
msg = _("IP shouldn't be in reserved class")
else:
msg = _("invalid IP, mustn't be in reserved class")
raise ValueError(msg)
if self._private_only and not ip.iptype() == 'PRIVATE':
raise ValueError(_("invalid IP, must be in private class"))
if warnings_only:
msg = _("IP should be in private class")
else:
msg = _("invalid IP, must be in private class")
raise ValueError(msg)
def _cons_in_network(self, opts, vals):
def _cons_in_network(self, opts, vals, warnings_only):
if len(vals) != 3:
raise ConfigError(_('invalid len for vals'))
if None in vals:
return
ip, network, netmask = vals
if IP(ip) not in IP('{0}/{1}'.format(network, netmask)):
raise ValueError(_('invalid IP {0} ({1}) with network {2} '
'({3}) and netmask {4} ({5})').format(
ip, opts[0]._name, network,
opts[1]._name, netmask, opts[2]._name))
if warnings_only:
msg = _('IP {0} ({1}) not in network {2} ({3}) with netmask {4}'
' ({5})')
else:
msg = _('invalid IP {0} ({1}) not in network {2} ({3}) with '
'netmask {4} ({5})')
raise ValueError(msg.format(ip, opts[0]._name, network,
opts[1]._name, netmask, opts[2]._name))
class PortOption(Option):
@ -884,10 +913,14 @@ class NetworkOption(Option):
except ValueError:
raise ValueError(_('invalid network address'))
def _second_level_validation(self, value):
def _second_level_validation(self, value, warnings_only):
ip = IP(value)
if ip.iptype() == 'RESERVED':
raise ValueError(_("invalid network address, must not be in reserved class"))
if warnings_only:
msg = _("network address shouldn't be in reserved class")
else:
msg = _("invalid network address, mustn't be in reserved class")
raise ValueError(msg)
class NetmaskOption(Option):
@ -901,19 +934,20 @@ class NetmaskOption(Option):
except ValueError:
raise ValueError(_('invalid netmask address'))
def _cons_network_netmask(self, opts, vals):
def _cons_network_netmask(self, opts, vals, warnings_only):
#opts must be (netmask, network) options
if None in vals:
return
self.__cons_netmask(opts, vals[0], vals[1], False)
self.__cons_netmask(opts, vals[0], vals[1], False, warnings_only)
def _cons_ip_netmask(self, opts, vals):
def _cons_ip_netmask(self, opts, vals, warnings_only):
#opts must be (netmask, ip) options
if None in vals:
return
self.__cons_netmask(opts, vals[0], vals[1], True)
self.__cons_netmask(opts, vals[0], vals[1], True, warnings_only)
def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net):
def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net,
warnings_only):
if len(opts) != 2:
raise ConfigError(_('invalid len for opts'))
msg = None
@ -950,7 +984,7 @@ class BroadcastOption(Option):
except ValueError:
raise ValueError(_('invalid broadcast address'))
def _cons_broadcast(self, opts, vals):
def _cons_broadcast(self, opts, vals, warnings_only):
if len(vals) != 3:
raise ConfigError(_('invalid len for vals'))
if None in vals:
@ -1047,7 +1081,7 @@ class EmailOption(DomainnameOption):
try:
username, domain = splitted
except ValueError:
raise ValueError(_('invalid email address, should contains one @'
raise ValueError(_('invalid email address, must contains one @'
))
if not self.username_re.search(username):
raise ValueError(_('invalid username in email address'))
@ -1063,7 +1097,7 @@ class URLOption(DomainnameOption):
def _validate(self, value):
match = self.proto_re.search(value)
if not match:
raise ValueError(_('invalid url, should start with http:// or '
raise ValueError(_('invalid url, must start with http:// or '
'https://'))
value = value[len(match.group(0)):]
# get domain/files
@ -1088,7 +1122,7 @@ class URLOption(DomainnameOption):
super(URLOption, self)._validate(domain)
# validate file
if files is not None and files != '' and not self.path_re.search(files):
raise ValueError(_('invalid url, should ends with filename'))
raise ValueError(_('invalid url, must ends with filename'))
class UsernameOption(Option):
@ -1217,11 +1251,12 @@ class OptionDescription(BaseOption):
if not force_no_consistencies and \
option._consistencies is not None:
for consistency in option._consistencies:
func, all_cons_opts = consistency
func, all_cons_opts, params = consistency
for opt in all_cons_opts:
_consistencies.setdefault(opt,
[]).append((func,
all_cons_opts))
all_cons_opts,
params))
else:
_currpath.append(attr)
option.impl_build_cache(cache_path,
@ -1311,18 +1346,29 @@ class OptionDescription(BaseOption):
def impl_get_group_type(self):
return self._group_type
def _valid_consistency(self, option, value, context, index):
def _valid_consistency(self, option, value, context, index, warnings_only):
if self._cache_consistencies is None:
return True
#consistencies is something like [('_cons_not_equal', (opt1, opt2))]
consistencies = self._cache_consistencies.get(option)
if consistencies is not None:
for func, all_cons_opts in consistencies:
for func, all_cons_opts, params in consistencies:
if not warnings_only:
l_warnings_only = params.get('warnings_only', False)
else:
l_warnings_only = warnings_only
#all_cons_opts[0] is the option where func is set
all_cons_opts[0]._launch_consistency(func, option,
value,
context, index,
all_cons_opts)
try:
all_cons_opts[0]._launch_consistency(func, option,
value,
context, index,
all_cons_opts,
l_warnings_only)
except ValueError as err:
if l_warnings_only:
raise ValueWarning(err.message, option)
else:
raise err
def _impl_getstate(self, descr=None):
"""enables us to export into a dict
@ -1432,7 +1478,7 @@ def validate_requires_arg(requires, name):
'must be an option in option {0}').format(name))
if option.impl_is_multi():
raise ValueError(_('malformed requirements option {0} '
'should not be a multi').format(name))
'must not be a multi').format(name))
if expected is not None:
try:
option._validate(expected)
@ -1467,17 +1513,17 @@ def validate_requires_arg(requires, name):
def validate_callback(callback, callback_params, type_):
if type(callback) != FunctionType:
raise ValueError(_('{0} should be a function').format(type_))
raise ValueError(_('{0} must be a function').format(type_))
if callback_params is not None:
if not isinstance(callback_params, dict):
raise ValueError(_('{0}_params should be a dict').format(type_))
raise ValueError(_('{0}_params must be a dict').format(type_))
for key, callbacks in callback_params.items():
if key != '' and len(callbacks) != 1:
raise ValueError(_('{0}_params with key {1} should not have '
'length different to 1').format(type_,
raise ValueError(_("{0}_params with key {1} mustn't have "
"length different to 1").format(type_,
key))
if not isinstance(callbacks, tuple):
raise ValueError(_('{0}_params should be tuple for key "{1}"'
raise ValueError(_('{0}_params must be tuple for key "{1}"'
).format(type_, key))
for callbk in callbacks:
if isinstance(callbk, tuple):
@ -1486,11 +1532,11 @@ def validate_callback(callback, callback_params, type_):
raise ValueError(_('validator not support tuple'))
if not isinstance(option, Option) and not \
isinstance(option, SymLinkOption):
raise ValueError(_('{0}_params should have an option '
raise ValueError(_('{0}_params must have an option '
'not a {0} for first argument'
).format(type_, type(option)))
if force_permissive not in [True, False]:
raise ValueError(_('{0}_params should have a boolean'
raise ValueError(_('{0}_params must have a boolean'
' not a {0} for second argument'
).format(type_, type(
force_permissive)))