add unique parameter to option

This commit is contained in:
2016-11-19 19:16:31 +01:00
parent fc36f674eb
commit 42d830687d
7 changed files with 116 additions and 33 deletions

View File

@ -102,7 +102,7 @@ class Base(StorageBase):
__slots__ = tuple()
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
requires=None, multi=False, unique=undefined, callback=None,
callback_params=None, validator=None, validator_params=None,
properties=None, warnings_only=False, extra=None,
allow_empty_list=undefined, session=None):
@ -122,6 +122,10 @@ class Base(StorageBase):
_multi = submulti
else:
raise ValueError(_('invalid multi value'))
if unique != undefined and not isinstance(unique, bool):
raise ValueError(_('unique must be a boolean'))
if not is_multi and unique == True:
raise ValueError(_('unique must be set only with multi value'))
if requires is not None:
calc_properties, requires = validate_requires_arg(is_multi,
requires, name)
@ -149,7 +153,7 @@ class Base(StorageBase):
session = self.getsession()
StorageBase.__init__(self, name, _multi, warnings_only, doc, extra,
calc_properties, requires, properties,
allow_empty_list, session=session)
allow_empty_list, unique, session=session)
if multi is not False and default is None:
default = []
err = self.impl_validate(default, is_multi=is_multi)
@ -175,7 +179,7 @@ class Base(StorageBase):
" yet for option {0}").format(
self.impl_getname()))
if not _init and self.impl_get_callback()[0] is not None:
raise ConfigError(_("a callback is already set for option {0}, "
raise ConfigError(_("a callback is already set for {0}, "
"cannot set another one's").format(self.impl_getname()))
self._validate_callback(callback, callback_params)
if callback is not None:
@ -415,9 +419,8 @@ class Option(OnlyOption):
all_cons_opts = []
val_consistencies = True
for opt in opts:
is_multi = opt.impl_is_multi() and not opt.impl_is_master_slaves()
if not is_multi and ((isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \
option == opt):
if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \
option == opt:
# option is current option
# we have already value, so use it
all_cons_vals.append(value)
@ -425,6 +428,7 @@ class Option(OnlyOption):
else:
#if context, calculate value, otherwise get default value
path = None
is_multi = opt.impl_is_multi() and not opt.impl_is_master_slaves()
if context is not undefined:
if isinstance(opt, DynSymLinkOption):
path = opt.impl_getpath(context)
@ -470,7 +474,7 @@ class Option(OnlyOption):
def impl_validate(self, value, context=undefined, validate=True,
force_index=None, force_submulti_index=None,
current_opt=undefined, is_multi=None,
display_warnings=True):
display_warnings=True, multi=None):
"""
:param value: the option's value
:param context: Config's context
@ -489,6 +493,13 @@ class Option(OnlyOption):
if current_opt is undefined:
current_opt = self
def _is_not_unique(value):
if 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()))
def calculation_validator(val):
validator, validator_params = self.impl_get_validator()
if validator is not None:
@ -589,43 +600,60 @@ class Option(OnlyOption):
if is_multi is None:
is_multi = self.impl_is_multi()
if not is_multi:
return do_validation(value, None, None)
elif force_index is not None:
if self.impl_is_submulti() and force_submulti_index is None:
err = _is_not_unique(value)
if err:
return err
if not isinstance(value, list): # pragma: optional cover
raise ValueError(_("invalid value {0} for option {1} which"
" must be a list").format(
value, self.impl_getname()))
return ValueError(_('invalid value "{0}" for "{1}" which'
' must be a list').format(
value, self.impl_get_display_name()))
for idx, val in enumerate(value):
if isinstance(val, list):
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)
if err:
return err
else:
if self.impl_is_unique() and value in multi:
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)
elif not isinstance(value, list): # pragma: optional cover
return ValueError(_("invalid value {0} for option {1} which "
"must be a list").format(value,
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:
for idx, val in enumerate(value):
err = _is_not_unique(val)
if err:
return err
if not isinstance(val, list): # pragma: optional cover
return ValueError(_("invalid value {0} for option {1} "
"which must be a list of list"
"").format(value,
return ValueError(_('invalid value "{0}" for "{1}" '
'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)
if err:
return err
else:
err = _is_not_unique(value)
if err:
return err
for idx, val in enumerate(value):
err = do_validation(val, idx, force_submulti_index)
if err:
return err
else:
return self._valid_consistency(current_opt, None, context,
None, None)
return self._valid_consistency(current_opt, None, context,
None, None)
def impl_is_dynsymlinkoption(self):
return False
@ -717,6 +745,10 @@ class Option(OnlyOption):
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
#consistency could generate warnings or errors
self._set_has_dependency()
@ -948,7 +980,7 @@ class SymLinkOption(OnlyOption):
session = self.getsession()
super(Base, self).__init__(name, undefined, undefined, undefined,
undefined, undefined, undefined, undefined,
undefined, opt, session=session)
undefined, undefined, opt=opt, session=session)
opt._set_has_dependency()
self.commit(session)
@ -1030,13 +1062,14 @@ class DynSymLinkOption(object):
def impl_validate(self, value, context=undefined, validate=True,
force_index=None, force_submulti_index=None, is_multi=None,
display_warnings=True):
display_warnings=True, multi=None):
return self._impl_getopt().impl_validate(value, context, validate,
force_index,
force_submulti_index,
current_opt=self,
is_multi=is_multi,
display_warnings=display_warnings)
display_warnings=display_warnings,
multi=multi)
def impl_is_dynsymlinkoption(self):
return True

View File

@ -310,7 +310,7 @@ class OptionDescription(BaseOption, StorageOptionDescription):
if isinstance(values, Exception):
raise values
if len(values) > len(set(values)):
raise ConfigError(_('DynOptionDescription callback return not uniq value'))
raise ConfigError(_('DynOptionDescription callback return not unique value'))
for val in values:
if not isinstance(val, str) or re.match(name_regexp, val) is None:
raise ValueError(_("invalid suffix: {0} for option").format(val))

View File

@ -34,10 +34,12 @@ if sys.version_info[0] >= 3: # pragma: optional cover
class StorageBase(object):
__slots__ = ('_name',
'_informations',
'_multi',
'_extra',
'_warnings_only',
'_allow_empty_list',
#multi
'_multi',
'_unique',
#value
'_default',
'_default_multi',
@ -66,7 +68,7 @@ class StorageBase(object):
)
def __init__(self, name, multi, warnings_only, doc, extra, calc_properties,
requires, properties, allow_empty_list, opt=undefined,
requires, properties, allow_empty_list, unique, opt=undefined,
session=None):
_setattr = object.__setattr__
_setattr(self, '_name', name)
@ -89,6 +91,8 @@ class StorageBase(object):
_setattr(self, '_opt', opt)
if allow_empty_list is not undefined:
_setattr(self, '_allow_empty_list', allow_empty_list)
if unique is not undefined:
setattr(self, '_unique', unique)
def _set_default_values(self, default, default_multi, is_multi):
_setattr = object.__setattr__
@ -343,6 +347,9 @@ class StorageBase(object):
def impl_allow_empty_list(self):
return getattr(self, '_allow_empty_list', undefined)
def impl_is_unique(self):
return getattr(self, '_unique', False)
def _get_extra(self, key):
extra = self._extra
if isinstance(extra, tuple):

View File

@ -298,11 +298,11 @@ class Values(object):
def _get_validated_value(self, opt, path, validate, force_permissive,
validate_properties, setting_properties,
self_properties,
self_properties,
index=None, submulti_index=undefined,
with_meta=True,
masterlen=undefined,
check_frozen=False,
check_frozen=False,
session=None, display_warnings=True):
"""same has getitem but don't touch the cache
index is None for slave value, if value returned is not a list, just return []
@ -498,7 +498,7 @@ class Values(object):
self_properties=self_properties, session=session)
if isinstance(value, Exception):
raise value
owner = self._p_.getowner(path, owners.default, session, only_default=only_default, index=index)
if validate_meta is undefined:
if opt.impl_is_master_slaves('slave'):
@ -862,14 +862,19 @@ class Multi(list):
fake_context = context._gen_fake_values(session)
fake_multi = fake_context.cfgimpl_get_values()._get_cached_value(
self.opt, path=self.path, validate=False)
fake_multi.extend(iterable, validate=False)
self._validate(iterable, fake_context, index)
if index is None:
fake_multi.extend(iterable, validate=False)
self._validate(fake_multi, fake_context, index)
else:
fake_multi[index].extend(iterable, validate=False)
self._validate(fake_multi[index], fake_context, index)
super(Multi, self).extend(iterable)
self._store()
def _validate(self, value, fake_context, force_index, submulti=False):
err = self.opt.impl_validate(value, context=fake_context,
force_index=force_index)
force_index=force_index,
multi=self)
if err:
raise err
@ -939,7 +944,8 @@ class SubMulti(Multi):
else:
err = self.opt.impl_validate(value, context=fake_context,
force_index=self._index,
force_submulti_index=force_index)
force_submulti_index=force_index,
multi=self)
if err:
raise err