better unique support

This commit is contained in:
Emmanuel Garette 2019-11-20 08:26:41 +01:00
parent 973b81d339
commit ce297ed804
1 changed files with 42 additions and 41 deletions

View File

@ -42,10 +42,8 @@ class Option(BaseOption):
"""
__slots__ = ('_extra',
'_warnings_only',
'_allow_empty_list',
# multi
'_multi',
'_unique',
# value
'_default',
'_default_multi',
@ -63,12 +61,10 @@ class Option(BaseOption):
default: Any=undefined,
default_multi: Any=None,
multi: bool=False,
unique: bool=undefined,
validators: Optional[List[Calculation]]=None,
properties: Optional[List[str]]=None,
warnings_only: bool=False,
extra: Optional[Dict]=None,
allow_empty_list: bool=undefined) -> None:
extra: Optional[Dict]=None):
_setattr = object.__setattr__
if not multi and default_multi is not None:
raise ValueError(_("default_multi is set whereas multi is False"
@ -114,14 +110,8 @@ class Option(BaseOption):
self._validators = tuple(validators)
if extra is not None and extra != {}:
_setattr(self, '_extra', extra)
if unique != undefined and not isinstance(unique, bool):
raise ValueError(_('unique must be a boolean, not "{}"').format(unique))
if not is_multi and unique is True:
raise ValueError(_('unique must be set only with multi value'))
if warnings_only is True:
_setattr(self, '_warnings_only', warnings_only)
if allow_empty_list is not undefined:
_setattr(self, '_allow_empty_list', allow_empty_list)
if is_multi and default_multi is not None:
def test_multi_value(value):
if isinstance(value, Calculation):
@ -132,8 +122,9 @@ class Option(BaseOption):
None,
undefined)
try:
self._validate(value,
option_bag)
self.validate(value)
self.validate_with_option(value,
option_bag)
except ValueError as err:
str_err = str(err)
if not str_err:
@ -157,8 +148,6 @@ class Option(BaseOption):
else:
test_multi_value(default_multi)
_setattr(self, '_default_multi', default_multi)
if unique is not undefined:
_setattr(self, '_unique', unique)
option_bag = OptionBag()
option_bag.set_option(self,
undefined,
@ -166,6 +155,9 @@ class Option(BaseOption):
undefined)
self.impl_validate(default,
option_bag)
self.impl_validate(default,
option_bag,
check_error=False)
self.value_dependencies(default)
if (is_multi and default != []) or \
(not is_multi and default is not None):
@ -201,12 +193,6 @@ class Option(BaseOption):
def impl_is_submulti(self) -> bool:
return getattr(self, '_multi', 1) == 2
def impl_is_unique(self) -> bool:
return getattr(self, '_unique', False)
def impl_allow_empty_list(self) -> Union[Undefined, bool]:
return getattr(self, '_allow_empty_list', undefined)
def impl_is_dynsymlinkoption(self) -> bool:
return False
@ -267,15 +253,16 @@ class Option(BaseOption):
return
def _is_not_unique(value):
def _is_not_unique(value, option_bag):
# if set(value) has not same length than value
if check_error and self.impl_is_unique() and \
len(set(value)) != len(value):
for idx, val in enumerate(value):
if val in value[idx+1:]:
raise ValueError(_('invalid value "{}", this value is already in "{}"'
'').format(val,
self.impl_get_display_name()))
if config_bag is not undefined and check_error and \
'unique' in option_bag.properties:
lvalue = [val for val in value if val is not None]
if len(set(lvalue)) != len(lvalue):
for idx, val in enumerate(value):
if val in value[idx+1:]:
raise ValueError(_('the value "{}" is not unique'
'').format(val))
def calculation_validator(val,
_index):
@ -303,9 +290,17 @@ class Option(BaseOption):
'{0}'.format(err),
_index),
ValueWarning,
self.__class__.__name__, 0)
self.__class__.__name__, 306)
else:
raise err
except ValueWarning as warn:
warnings.warn_explicit(ValueWarning(val,
self._display_name,
self,
'{0}'.format(warn),
_index),
ValueWarning,
self.__class__.__name__, 316)
def do_validation(_value,
_index):
@ -317,14 +312,14 @@ class Option(BaseOption):
if _value is not None:
if check_error:
# option validation
self._validate(_value,
option_bag,
option_bag.ori_option)
self.validate(_value)
self.validate_with_option(_value,
option_bag)
if ((check_error and not is_warnings_only) or
(not check_error and is_warnings_only)):
try:
self._second_level_validation(_value,
is_warnings_only)
self.second_level_validation(_value,
is_warnings_only)
except ValueError as err:
if is_warnings_only:
warnings.warn_explicit(ValueWarning(_value,
@ -347,7 +342,7 @@ class Option(BaseOption):
if self.impl_is_submulti():
if not isinstance(value, list):
raise ValueError(_('which must be a list'))
_is_not_unique(value)
_is_not_unique(value, option_bag)
for val in value:
do_validation(val,
force_index)
@ -360,7 +355,7 @@ class Option(BaseOption):
raise ValueError(_('which must be a list'))
elif self.impl_is_submulti():
for err_index, lval in enumerate(value):
_is_not_unique(lval)
_is_not_unique(lval, option_bag)
if isinstance(lval, Calculation):
continue
if not isinstance(lval, list):
@ -370,7 +365,8 @@ class Option(BaseOption):
do_validation(val,
err_index)
else:
_is_not_unique(value)
_is_not_unique(value, option_bag)
# FIXME subtimal, not several time is whole=True!
for err_index, val in enumerate(value):
do_validation(val,
err_index)
@ -403,9 +399,14 @@ class Option(BaseOption):
raise ValueError(_('default value not allowed if option "{0}" '
'is calculated').format(self.impl_getname()))
def _second_level_validation(self,
value: Any,
warnings_only: bool) -> None:
def validate_with_option(self,
value: Any,
option_bag: OptionBag) -> None:
pass
def second_level_validation(self,
value: Any,
warnings_only: bool) -> None:
pass
def impl_is_leader(self):