better unique support

This commit is contained in:
Emmanuel Garette 2019-11-20 08:26:41 +01:00
parent 973b81d339
commit ce297ed804

View file

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