This commit is contained in:
2017-11-20 17:01:36 +01:00
parent 5d1be8a11a
commit 119ca85041
11 changed files with 1141 additions and 1199 deletions

View File

@ -32,7 +32,7 @@ if sys.version_info[0] >= 3: # pragma: no cover
else:
from inspect import getargspec
STATIC_TUPLE = tuple()
STATIC_TUPLE = frozenset()
submulti = 2
@ -145,6 +145,8 @@ class Base(object):
requires = undefined
if properties is None:
properties = tuple()
if is_multi and 'empty' not in properties:
properties = tuple(list(properties) + ['empty'])
if not isinstance(properties, tuple):
raise TypeError(_('invalid properties type {0} for {1},'
' must be a tuple').format(
@ -406,11 +408,15 @@ class BaseOption(Base):
name = name.encode('utf8')
return name
def reset_cache(self, opt, obj, type_, resetted_opts):
def reset_cache(self,
opt,
path,
obj,
type_,
resetted_opts):
if opt in resetted_opts:
return
if not type_ == 'values' or not opt.impl_is_optiondescription():
path = opt.impl_getpath(obj._getcontext())
if type_ != 'permissives':
obj._p_.delcache(path)
if type_ in ['settings', 'permissives']:

View File

@ -21,6 +21,7 @@
# ____________________________________________________________
import warnings
import sys
import weakref
from .baseoption import OnlyOption, submulti, DynSymLinkOption, validate_callback, STATIC_TUPLE
from ..i18n import _
@ -146,9 +147,17 @@ class Option(OnlyOption):
def impl_is_multi(self):
return getattr(self, '_multi', 1) != 1
def _launch_consistency(self, current_opt, func, option, value, context,
index, submulti_index, opts, warnings_only,
transitive):
def _launch_consistency(self,
current_opt,
func,
option,
value,
context,
index,
opts,
warnings_only,
transitive,
setting_properties):
"""Launch consistency now
:param func: function name, this name should start with _cons_
@ -174,7 +183,8 @@ class Option(OnlyOption):
all_cons_vals = []
all_cons_opts = []
val_consistencies = True
for opt in opts:
for wopt in opts:
opt = wopt()
if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \
option == opt:
# option is current option
@ -195,7 +205,9 @@ class Option(OnlyOption):
else:
_index = index
try:
opt_value = context.getattr(path, validate=False,
opt_value = context.getattr(path,
setting_properties,
validate=False,
index=_index,
force_permissive=True)
except PropertiesOptionError as err:
@ -254,7 +266,6 @@ class Option(OnlyOption):
context=undefined,
validate=True,
force_index=None,
force_submulti_index=None,
current_opt=undefined,
is_multi=None,
display_error=True,
@ -270,9 +281,6 @@ class Option(OnlyOption):
:param force_index: if multi, value has to be a list
not if force_index is not None
:type force_index: integer
:param force_submulti_index: if submulti, value has to be a list
not if force_submulti_index is not None
:type force_submulti_index: integer
"""
if not validate:
return
@ -287,10 +295,12 @@ class Option(OnlyOption):
if display_error and self.impl_is_unique() and len(set(value)) != len(value):
for idx, val in enumerate(value):
if val in value[idx+1:]:
return ValueError(_('invalid value "{}", this value is already in "{}"').format(
val, self.impl_get_display_name()))
return ValueError(_('invalid value "{}", this value is already in "{}"'
'').format(val,
self.impl_get_display_name()))
def calculation_validator(val, _index):
def calculation_validator(val,
_index):
validator, validator_params = self.impl_get_validator()
if validator is not None:
if validator_params != {}:
@ -316,9 +326,10 @@ class Option(OnlyOption):
if isinstance(value, Exception):
return value
def do_validation(_value, _index, submulti_index):
def do_validation(_value,
_index):
if _value is None:
error = warning = None
error = None
else:
if display_error:
# option validation
@ -327,35 +338,41 @@ class Option(OnlyOption):
current_opt)
if err:
if debug: # pragma: no cover
log.debug('do_validation: value: {0}, index: {1}, '
'submulti_index: {2}'.format(_value,
_index,
submulti_index),
log.debug('do_validation: value: {0}, index: {1}:'
' {2}'.format(_value,
_index),
exc_info=True)
err_msg = '{0}'.format(err)
if err_msg:
msg = _('"{0}" is an invalid {1} for "{2}", {3}'
'').format(_value, self._display_name,
'').format(_value,
self._display_name,
self.impl_get_display_name(), err_msg)
else:
msg = _('"{0}" is an invalid {1} for "{2}"'
'').format(_value, self._display_name,
'').format(_value,
self._display_name,
self.impl_get_display_name())
return ValueError(msg)
error = None
is_warnings_only = getattr(self, '_warnings_only', False)
if ((display_error and not is_warnings_only) or
(display_warnings and is_warnings_only)):
error = calculation_validator(_value, _index)
error = calculation_validator(_value,
_index)
if not error:
error = self._second_level_validation(_value, is_warnings_only)
error = self._second_level_validation(_value,
is_warnings_only)
if error:
if debug: # pragma: no cover
log.debug(_('do_validation for {0}: error in value').format(
self.impl_getname()), exc_info=True)
if is_warnings_only:
msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}').format(
_value, self._display_name, self.impl_get_display_name(), error)
msg = _('attention, "{0}" could be an invalid {1} for "{2}", {3}'
'').format(_value,
self._display_name,
self.impl_get_display_name(),
error)
warnings.warn_explicit(ValueWarning(msg, self),
ValueWarning,
self.__class__.__name__, 0)
@ -367,9 +384,9 @@ class Option(OnlyOption):
_value,
context,
_index,
submulti_index,
display_warnings,
display_error)
display_error,
setting_properties)
if isinstance(ret, ValueError):
error = ret
elif ret:
@ -390,22 +407,22 @@ class Option(OnlyOption):
is_multi = self.impl_is_multi()
if not is_multi:
return do_validation(value, None, None)
return do_validation(value, None)
elif force_index is not None:
if self.impl_is_submulti() and force_submulti_index is None:
if self.impl_is_submulti():
err = _is_not_unique(value)
if err:
return err
if not isinstance(value, list):
return ValueError(_('invalid value "{0}" for "{1}" which'
' must be a list').format(
value, self.impl_get_display_name()))
value, self.impl_get_display_name()))
for idx, val in enumerate(value):
if isinstance(val, list): # pragma: no cover
return ValueError(_('invalid value "{}" for "{}" '
'which must not be a list').format(val,
self.impl_get_display_name()))
err = do_validation(val, force_index, idx)
self.impl_get_display_name()))
err = do_validation(val, force_index)
if err:
return err
else:
@ -419,12 +436,12 @@ class Option(OnlyOption):
return ValueError(_('invalid value "{}", this value is already'
' in "{}"').format(value,
self.impl_get_display_name()))
return do_validation(value, force_index, force_submulti_index)
return do_validation(value, force_index)
elif not isinstance(value, list):
return ValueError(_('invalid value "{0}" for "{1}" which '
'must be a list').format(value,
self.impl_getname()))
elif self.impl_is_submulti() and force_submulti_index is None:
elif self.impl_is_submulti():
for idx, val in enumerate(value):
err = _is_not_unique(val)
if err:
@ -434,8 +451,9 @@ class Option(OnlyOption):
'which must be a list of list'
'').format(val,
self.impl_getname()))
for slave_idx, slave_val in enumerate(val):
err = do_validation(slave_val, idx, slave_idx)
for slave_val in val:
err = do_validation(slave_val,
idx)
if err:
return err
else:
@ -443,16 +461,17 @@ class Option(OnlyOption):
if err:
return err
for idx, val in enumerate(value):
err = do_validation(val, idx, force_submulti_index)
err = do_validation(val,
idx)
if err:
return err
return self._valid_consistency(current_opt,
None,
context,
None,
None,
display_warnings,
display_error)
display_error,
setting_properties)
def impl_is_dynsymlinkoption(self):
return False
@ -480,7 +499,10 @@ class Option(OnlyOption):
"accesses the Option's doc"
return self.impl_get_information('doc')
def _valid_consistencies(self, other_opts, init=True, func=None):
def _valid_consistencies(self,
other_opts,
init=True,
func=None):
if self._is_subdyn():
dynod = self._subdyn()
else:
@ -488,7 +510,11 @@ class Option(OnlyOption):
if self.impl_is_submulti():
raise ConfigError(_('cannot add consistency with submulti option'))
is_multi = self.impl_is_multi()
for opt in other_opts:
for wopt in other_opts:
if isinstance(wopt, weakref.ReferenceType):
opt = wopt()
else:
opt = wopt
if opt.impl_is_submulti():
raise ConfigError(_('cannot add consistency with submulti option'))
if not isinstance(opt, Option):
@ -515,7 +541,10 @@ class Option(OnlyOption):
if func != 'not_equal':
opt._has_dependency = True
def impl_add_consistency(self, func, *other_opts, **params):
def impl_add_consistency(self,
func,
*other_opts,
**params):
"""Add consistency means that value will be validate with other_opts
option's values.
@ -525,39 +554,51 @@ class Option(OnlyOption):
:type other_opts: `list` of `tiramisu.option.Option`
:param params: extra params (warnings_only and transitive are allowed)
"""
if self.impl_is_readonly():
if self.impl_is_readonly():
raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is"
" read-only").format(
self.__class__.__name__,
self.impl_getname()))
self._valid_consistencies(other_opts, func=func)
self._valid_consistencies(other_opts,
func=func)
func = '_cons_{0}'.format(func)
if func not in dir(self):
raise ConfigError(_('consistency {0} not available for this option').format(func))
all_cons_opts = tuple([self] + list(other_opts))
options = [weakref.ref(self)]
for option in other_opts:
options.append(weakref.ref(option))
all_cons_opts = tuple(options)
unknown_params = set(params.keys()) - set(['warnings_only', 'transitive'])
if unknown_params != set():
raise ValueError(_('unknown parameter {0} in consistency').format(unknown_params))
self._add_consistency(func, all_cons_opts, params)
self._add_consistency(func,
all_cons_opts,
params)
#validate default value when add consistency
err = self.impl_validate(self.impl_getdefault())
if err:
self._del_consistency()
raise err
if func in ALLOWED_CONST_LIST:
for opt in all_cons_opts:
if getattr(opt, '_unique', undefined) == undefined:
opt._unique = True
if func != '_cons_not_equal':
#consistency could generate warnings or errors
self._has_dependency = True
for opt in all_cons_opts:
for wopt in all_cons_opts:
opt = wopt()
if func in ALLOWED_CONST_LIST:
if getattr(opt, '_unique', undefined) == undefined:
opt._unique = True
if opt != self:
self._add_dependency(opt)
opt._add_dependency(self)
def _valid_consistency(self, option, value, context, index, submulti_idx,
display_warnings, display_error):
def _valid_consistency(self,
option,
value,
context,
index,
display_warnings,
display_error,
setting_properties):
if context is not undefined:
descr = context.cfgimpl_get_description()
if descr._cache_consistencies is None:
@ -586,10 +627,16 @@ class Option(OnlyOption):
opts.append(opt._impl_to_dyn(name, path))
else:
opts = all_cons_opts
err = opts[0]._launch_consistency(self, func, option, value,
context, index, submulti_idx,
opts, warnings_only,
transitive)
err = opts[0]()._launch_consistency(self,
func,
option,
value,
context,
index,
opts,
warnings_only,
transitive,
setting_properties)
if err:
return err
@ -680,7 +727,10 @@ class Option(OnlyOption):
#____________________________________________________________
# consistency
def _add_consistency(self, func, all_cons_opts, params):
def _add_consistency(self,
func,
all_cons_opts,
params):
cons = (func, all_cons_opts, params)
consistencies = getattr(self, '_consistencies', None)
if consistencies is None:

View File

@ -125,8 +125,8 @@ class CacheOptionDescription(BaseOption):
'must be in same master/slaves for {1}').format(
require_opt.impl_getname(), option.impl_getname()))
else:
raise ValueError(_('malformed requirements option {0} '
'must not be a multi for {1}').format(
raise ValueError(_('malformed requirements option "{0}" '
'must not be a multi for "{1}"').format(
require_opt.impl_getname(), option.impl_getname()))
if init:
if len(cache_option) != len(set(cache_option)):
@ -137,7 +137,7 @@ class CacheOptionDescription(BaseOption):
if _consistencies != {}:
self._cache_consistencies = {}
for opt, cons in _consistencies.items():
if opt not in cache_option: # pragma: optional cover
if opt() not in cache_option: # pragma: optional cover
raise ConfigError(_('consistency with option {0} '
'which is not in Config').format(
opt.impl_getname()))
@ -437,12 +437,12 @@ class OptionDescription(OptionDescriptionWalk):
for child in valid_child:
if child == old: # pragma: optional cover
raise ConflictError(_('duplicate option name: '
'{0}').format(child))
'"{0}"').format(child))
if dynopt_names:
for dynopt in dynopt_names:
if child != dynopt and child.startswith(dynopt):
raise ConflictError(_('option must not start as '
'dynoptiondescription'))
raise ConflictError(_('the option\'s name "{}" start as '
'the dynoptiondescription\'s name "{}"').format(child, dynopt))
old = child
_setattr = object.__setattr__
_setattr(self, '_children', (tuple(child_names), tuple(children)))
@ -623,7 +623,7 @@ class MasterSlaves(OptionDescription):
name))
slaves.append(child)
child._add_dependency(self)
for child in children:
for idx, child in enumerate(children):
if child._is_symlinkoption(): # pragma: optional cover
raise ValueError(_("master group {0} shall not have "
"a symlinkoption").format(self.impl_getname()))
@ -635,6 +635,11 @@ class MasterSlaves(OptionDescription):
"in group {1}"
": this option is not a multi"
"").format(child.impl_getname(), self.impl_getname()))
# no empty property for save
if idx != 0:
properties = list(child._properties)
properties.remove('empty')
child._properties = tuple(properties)
callback, callback_params = master.impl_get_callback()
if callback is not None and callback_params != {}:
for callbacks in callback_params.values():