Compare commits

...

4 commits

18 changed files with 364 additions and 171 deletions

View file

@ -402,12 +402,12 @@ def test_prefix_error():
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
assert str(err) == _('"{0}" is an invalid {1} for "{2}", which is not an integer').format('yes', _('integer'), 'test1') assert str(err) == _('"{0}" is an invalid {1} for "{2}", it\'s not an integer').format('yes', _('integer'), 'test1')
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
err.prefix = '' err.prefix = ''
assert str(err) == _('which is not an integer') assert str(err) == _('it\'s not an integer')
# assert not list_sessions() # assert not list_sessions()

View file

@ -39,6 +39,18 @@ def return_true(value, param=None, identifier=None):
raise ValueError('no value') raise ValueError('no value')
def return_no_dyn(value):
if value in [['val', 'val'], ['yes', 'yes']]:
return
raise ValueError('no value')
def return_no_dyn_properties(value, identifier):
idx = int(identifier)
if idx and not value[idx - 1]:
return 'disabled'
def return_dynval(value='val', identifier=None): def return_dynval(value='val', identifier=None):
return value return value
@ -344,6 +356,17 @@ def test_prop_dyndescription_force_store_value():
# assert not list_sessions() # assert not list_sessions()
def test_prop_dyndescription_force_store_value_disabled():
st = StrOption('st', '', properties=('force_store_value', 'disabled'))
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list))
od = OptionDescription('od', '', [dod])
od2 = OptionDescription('od', '', [od])
cfg = Config(od2)
cfg.property.read_write()
assert parse_od_get(cfg.value.get()) == {}
# assert not list_sessions()
def test_prop_dyndescription_force_store_value_calculation_prefix(): def test_prop_dyndescription_force_store_value_calculation_prefix():
lst = StrOption('lst', '', ['val1', 'val2'], multi=True) lst = StrOption('lst', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '', Calculation(return_list, Params(ParamIdentifier())) , properties=('force_store_value',)) st = StrOption('st', '', Calculation(return_list, Params(ParamIdentifier())) , properties=('force_store_value',))
@ -1117,6 +1140,43 @@ def test_validator_dyndescription():
# assert not list_sessions() # assert not list_sessions()
def test_validator_param_self_option():
out = StrOption('out', '', 'val')
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
st_in = StrOption('st_in', '', Calculation(return_dynval, Params(ParamOption(out))))
st = StrOption('st', '', Calculation(return_dynval, Params(ParamOption(st_in))), validators=[Calculation(return_no_dyn, Params((ParamSelfOption(dynamic=False),)))])
dod = DynOptionDescription('dod', '', [st_in, st], identifiers=Calculation(return_list))
od = OptionDescription('od', '', [dod, val1, out])
od2 = OptionDescription('od', '', [od])
cfg = Config(od2)
assert cfg.option('od.dodval1.st').value.get() == 'val'
with pytest.raises(ValueError):
cfg.option('od.dodval1.st').value.set('no')
cfg.option('od.out').value.set('yes')
def test_properties_param_self_option():
out = StrOption('out', '', 'val')
val1 = StrOption('val1', '', ["0", "1", "2"], multi=True)
disabled_property = Calculation(return_no_dyn_properties, Params((ParamSelfOption(dynamic=False), ParamIdentifier())))
st = StrOption('st', '', None, properties=(disabled_property,))
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list, Params(ParamOption(val1))))
od = OptionDescription('od', '', [dod, val1, out])
od2 = OptionDescription('od', '', [od])
cfg = Config(od2)
cfg.property.read_write()
assert cfg.option('od.dod0.st').value.get() is None
with pytest.raises(PropertiesOptionError):
cfg.option('od.dod1.st').value.get()
with pytest.raises(PropertiesOptionError):
cfg.option('od.dod2.st').value.get()
cfg.option('od.dod0.st').value.set('val')
assert cfg.option('od.dod0.st').value.get() == 'val'
assert cfg.option('od.dod1.st').value.get() is None
with pytest.raises(PropertiesOptionError):
cfg.option('od.dod2.st').value.get()
def test_makedict_dyndescription_context(): def test_makedict_dyndescription_context():
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True) val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '') st = StrOption('st', '')

View file

@ -165,15 +165,15 @@ def test_force_store_value():
cfg = Config(od1) cfg = Config(od1)
compare(cfg.value.exportation(), {}) compare(cfg.value.exportation(), {})
cfg.property.read_write() cfg.property.read_write()
compare(cfg.value.exportation(), {'wantref': {None: [False, 'forced']}, 'wantref2': {None: [False, 'forced']}, 'wantref3': {None: [[False], 'forced']}}) compare(cfg.value.exportation(), {'wantref3': {None: [[False], 'forced']}})
cfg.option('bool').value.set(False) cfg.option('bool').value.set(False)
cfg.option('wantref').value.set(True) cfg.option('wantref').value.set(True)
cfg.option('bool').value.reset() cfg.option('bool').value.reset()
compare(cfg.value.exportation(), {'wantref': {None: [True, 'user']}, 'wantref2': {None: [False, 'forced']}, 'wantref3': {None: [[False], 'forced']}}) compare(cfg.value.exportation(), {'wantref': {None: [True, 'user']}, 'wantref3': {None: [[False], 'forced']}})
cfg.option('bool').value.set(False) cfg.option('bool').value.set(False)
cfg.option('wantref').value.reset() cfg.option('wantref').value.reset()
cfg.option('bool').value.reset() cfg.option('bool').value.reset()
compare(cfg.value.exportation(), {'wantref': {None: [False, 'forced']}, 'wantref2': {None: [False, 'forced']}, 'wantref3': {None: [[False], 'forced']}}) compare(cfg.value.exportation(), {'wantref': {None: [False, 'forced']}, 'wantref3': {None: [[False], 'forced']}})
# assert not list_sessions() # assert not list_sessions()

View file

@ -932,6 +932,45 @@ def test_none_is_not_modified():
# assert not list_sessions() # assert not list_sessions()
def test_force_store_value_disabled_value():
gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value', 'disabled'))
gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.value.exportation() == {}
cfg.option('gc.dummy1').permissive.add('disabled')
assert cfg.option('gc.dummy1').value.get() == 'str'
# do not export
assert cfg._config_bag.context.get_values()._values == {None: {None: [None, 'user']},'gc.dummy1': {None: ['str', 'forced']}}
def test_force_store_value_disabled_owner():
gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value', 'disabled'))
gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.value.exportation() == {}
cfg.option('gc.dummy1').permissive.add('disabled')
assert cfg.option('gc.dummy1').owner.get() == owners.forced
assert cfg.value.exportation() == {'gc.dummy1': {None: ['str', 'forced']}}
def test_force_store_value_disabled_exportation():
gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',))
gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value', 'disabled'))
gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1])
od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.value.exportation() == {}
cfg.option('gc.dummy1').permissive.add('disabled')
assert cfg.value.exportation() == {'gc.dummy1': {None: ['str', 'forced']}}
def test_pprint(): def test_pprint():
msg_error = _("cannot access to {0} {1} because has {2} {3}") msg_error = _("cannot access to {0} {1} because has {2} {3}")
msg_is_not = _('the value of "{0}" is not {1}') msg_is_not = _('the value of "{0}" is not {1}')

View file

@ -6,7 +6,7 @@ import pytest
from tiramisu import BoolOption, StrOption, IPOption, NetmaskOption, NetworkOption, BroadcastOption, \ from tiramisu import BoolOption, StrOption, IPOption, NetmaskOption, NetworkOption, BroadcastOption, \
IntOption, OptionDescription, Leadership, Config, Params, ParamValue, ParamOption, \ IntOption, OptionDescription, Leadership, Config, Params, ParamValue, ParamOption, \
ParamSelfOption, ParamIndex, ParamInformation, ParamSelfInformation, ParamSelfOption, Calculation, \ ParamSelfOption, ParamIndex, ParamInformation, ParamSelfInformation, Calculation, \
valid_ip_netmask, valid_network_netmask, \ valid_ip_netmask, valid_network_netmask, \
valid_in_network, valid_broadcast, valid_not_equal valid_in_network, valid_broadcast, valid_not_equal
from tiramisu.setting import groups from tiramisu.setting import groups

View file

@ -583,6 +583,7 @@ class _TiramisuOptionOptionDescription:
convert: bool = False, convert: bool = False,
): ):
"""Get identifiers for dynamic option""" """Get identifiers for dynamic option"""
subconfig = self._subconfig
if not only_self: if not only_self:
if self._subconfig.is_dynamic_without_identifiers and not uncalculated: if self._subconfig.is_dynamic_without_identifiers and not uncalculated:
raise AttributeOptionError(self._subconfig.path, "option-dynamic") raise AttributeOptionError(self._subconfig.path, "option-dynamic")
@ -590,7 +591,6 @@ class _TiramisuOptionOptionDescription:
return self._subconfig.identifiers return self._subconfig.identifiers
identifiers = [] identifiers = []
dynconfig = None dynconfig = None
subconfig = self._subconfig
while not dynconfig: while not dynconfig:
if subconfig.option.impl_is_optiondescription() and subconfig.option.impl_is_dynoptiondescription(): if subconfig.option.impl_is_optiondescription() and subconfig.option.impl_is_dynoptiondescription():
dynconfig = subconfig dynconfig = subconfig
@ -607,7 +607,8 @@ class _TiramisuOptionOptionDescription:
raise ConfigError( raise ConfigError(
_( _(
"the option {0} is not a dynamic option, cannot get identifiers with only_self parameter to True" "the option {0} is not a dynamic option, cannot get identifiers with only_self parameter to True"
).format(self._subconfig.path) ).format(self._subconfig.path),
subconfig=subconfig,
) )
return self._subconfig.option.get_identifiers( return self._subconfig.option.get_identifiers(
self._subconfig.parent, self._subconfig.parent,
@ -1454,6 +1455,7 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet):
with_default_owner: bool = False, with_default_owner: bool = False,
): ):
"""Export all values""" """Export all values"""
self._force_store_value()
exportation = deepcopy(self._config_bag.context.get_values()._values) exportation = deepcopy(self._config_bag.context.get_values()._values)
if not with_default_owner: if not with_default_owner:
del exportation[None] del exportation[None]
@ -1469,6 +1471,10 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet):
if None not in values: if None not in values:
cvalues._values[None] = {None: [None, current_owner]} cvalues._values[None] = {None: [None, current_owner]}
def _force_store_value(self):
descr = self._config_bag.context.get_description()
descr.impl_build_force_store_values(self._config_bag)
class TiramisuContextOwner(TiramisuConfig): class TiramisuContextOwner(TiramisuConfig):
"""Global owner""" """Global owner"""

View file

@ -172,15 +172,17 @@ class ParamDynOption(ParamOption):
class ParamSelfOption(Param): class ParamSelfOption(Param):
__slots__ = "whole" __slots__ = ("whole", "dynamic")
def __init__( def __init__(
self, self,
whole: bool = undefined, whole: bool = undefined,
dynamic: bool = True,
) -> None: ) -> None:
"""whole: send all value for a multi, not only indexed value""" """whole: send all value for a multi, not only indexed value"""
if whole is not undefined: if whole is not undefined:
self.whole = whole self.whole = whole
self.dynamic = dynamic
class ParamValue(Param): class ParamValue(Param):
@ -460,7 +462,7 @@ def manager_callback(
or param.raisepropertyerror or param.raisepropertyerror
): ):
raise err from err raise err from err
raise ConfigError(err) raise ConfigError(err, subconfig=subconfig)
except ValueError as err: except ValueError as err:
display_name = subconfig.option.impl_get_display_name( display_name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True subconfig, with_quote=True
@ -613,18 +615,45 @@ def manager_callback(
return subconfig.identifiers[param.identifier_index] return subconfig.identifiers[param.identifier_index]
if isinstance(param, ParamSelfOption): if isinstance(param, ParamSelfOption):
value = calc_self( search_option = subconfig.option
param, if subconfig.option.issubdyn() and not param.dynamic:
index, subconfigs = subconfig.parent.parent.get_common_child(
orig_value, search_option,
config_bag, true_path=subconfig.path,
) validate_properties=False,
if callback.__name__ not in FUNCTION_WAITING_FOR_DICT: )
return value values = []
return { properties = config_bag.context.get_settings().getproperties(
"name": option.impl_get_display_name(subconfig), subconfig,
"value": value, uncalculated=True,
} ) - {'validator', 'mandatory', 'empty'}
for subconfig_ in subconfigs:
if subconfig.path == subconfig_.path:
values.append(orig_value)
else:
subconfig_.properties = properties
values.append(get_value(
config_bag,
subconfig_,
param,
True,
))
if callback.__name__ not in FUNCTION_WAITING_FOR_DICT:
return values
return {"name": search_option.impl_get_display_name(subconfig), "value": values}
else:
value = calc_self(
param,
index,
orig_value,
config_bag,
)
if callback.__name__ not in FUNCTION_WAITING_FOR_DICT:
return value
return {
"name": option.impl_get_display_name(subconfig),
"value": value,
}
if isinstance(param, ParamOption): if isinstance(param, ParamOption):
callbk_option = param.option callbk_option = param.option
@ -762,13 +791,15 @@ def manager_callback(
] ]
values = None values = None
for subconfig in subconfigs: for subconfig in subconfigs:
callbk_option = subconfig.option if isinstance(subconfig, PropertiesOptionError):
value = get_value( value = subconfig
config_bag, else:
subconfig, value = get_value(
param, config_bag,
False, subconfig,
) param,
False,
)
if with_index: if with_index:
value = value[index] value = value[index]
if values is not None: if values is not None:
@ -777,6 +808,8 @@ def manager_callback(
value = values value = values
if callback.__name__ not in FUNCTION_WAITING_FOR_DICT: if callback.__name__ not in FUNCTION_WAITING_FOR_DICT:
return value return value
# FIXME the last one?
callbk_option = subconfig.option
return {"name": callbk_option.impl_get_display_name(subconfig), "value": value} return {"name": callbk_option.impl_get_display_name(subconfig), "value": value}
@ -925,6 +958,7 @@ def calculate(
raise err from err raise err from err
error = err error = err
except ConfigError as err: except ConfigError as err:
err.subconfig = subconfig
raise err from err raise err from err
except Exception as err: except Exception as err:
error = err error = err

View file

@ -42,9 +42,15 @@ from . import autolib
def get_common_path(path1, path2): def get_common_path(path1, path2):
if None in (path1, path2):
return None
common_path = commonprefix([path1, path2]) common_path = commonprefix([path1, path2])
if common_path in [path1, path2]: all_paths = [path1, path2]
return common_path if common_path in all_paths:
# od.st is not the common_path of od.st_in
all_paths.remove(common_path)
if all_paths[0].startswith(common_path + '.'):
return common_path
if common_path.endswith("."): if common_path.endswith("."):
return common_path[:-1] return common_path[:-1]
elif "." in common_path: elif "." in common_path:
@ -88,45 +94,52 @@ class CCache:
subconfig, subconfig,
resetted_opts, resetted_opts,
is_default, is_default,
*,
force=False,
): ):
"""reset cache for one option""" """reset cache for one option"""
if subconfig.path in resetted_opts: if not force and subconfig.path in resetted_opts:
return return
resetted_opts.append(subconfig.path) resetted_opts.append(subconfig.path)
config_bag = subconfig.config_bag config_bag = subconfig.config_bag
# if is_default and config_bag.context.get_owner(subconfig) != owners.default: if not force:
# return # if is_default and config_bag.context.get_owner(subconfig) != owners.default:
for is_default, woption in subconfig.option.get_dependencies(subconfig.option): # return
option = woption() for is_default, woption in subconfig.option.get_dependencies(subconfig.option):
if option.issubdyn(): option = woption()
# it's an option in dynoptiondescription, remove cache for all generated option if option.issubdyn():
self.reset_cache_dyn_option( # it's an option in dynoptiondescription, remove cache for all generated option
subconfig, if option.impl_getpath() == subconfig.option.impl_getpath():
option, force = True
resetted_opts, subconfig = subconfig.parent.parent
is_default, self.reset_cache_dyn_option(
) subconfig,
elif option.impl_is_dynoptiondescription(): option,
self.reset_cache_dyn_optiondescription( resetted_opts,
option, is_default,
config_bag, force,
resetted_opts, )
is_default, elif option.impl_is_dynoptiondescription():
) self.reset_cache_dyn_optiondescription(
else: option,
option_subconfig = self.get_sub_config( config_bag,
config_bag, resetted_opts,
option.impl_getpath(), is_default,
None, )
properties=None, else:
validate_properties=False, option_subconfig = self.get_sub_config(
) config_bag,
self.reset_one_option_cache( option.impl_getpath(),
option_subconfig, None,
resetted_opts, properties=None,
is_default, validate_properties=False,
) )
del option self.reset_one_option_cache(
option_subconfig,
resetted_opts,
is_default,
)
del option
subconfig.option.reset_cache( subconfig.option.reset_cache(
subconfig.path, subconfig.path,
config_bag, config_bag,
@ -182,14 +195,18 @@ class CCache:
def get_dynamic_from_dyn_option(self, subconfig, option): def get_dynamic_from_dyn_option(self, subconfig, option):
config_bag = subconfig.config_bag config_bag = subconfig.config_bag
sub_paths = option.impl_getpath() sub_paths = option.impl_getpath()
current_paths = subconfig.path.split(".") if not subconfig.path:
current_paths_max_index = len(current_paths) - 1 current_paths = []
current_paths_max_index = 0
else:
current_paths = subconfig.path.split(".")
current_paths_max_index = len(current_paths) - 1
current_subconfigs = [] current_subconfigs = []
parent = subconfig parent = subconfig
while True: while True:
current_subconfigs.insert(0, parent) current_subconfigs.insert(0, parent)
parent = parent.parent parent = parent.parent
if parent.path is None: if not parent or parent.path is None:
break break
currents = [self.get_root(config_bag)] currents = [self.get_root(config_bag)]
for idx, sub_path in enumerate(sub_paths.split(".")): for idx, sub_path in enumerate(sub_paths.split(".")):
@ -234,12 +251,14 @@ class CCache:
option, option,
resetted_opts, resetted_opts,
is_default, is_default,
force,
): ):
for dyn_option_subconfig in self.get_dynamic_from_dyn_option(subconfig, option): for dyn_option_subconfig in self.get_dynamic_from_dyn_option(subconfig, option):
self.reset_one_option_cache( self.reset_one_option_cache(
dyn_option_subconfig, dyn_option_subconfig,
resetted_opts, resetted_opts,
is_default, is_default,
force=force,
) )
@ -503,7 +522,7 @@ class SubConfig:
) )
if check_index and index is not None: if check_index and index is not None:
if option.impl_is_optiondescription() or not option.impl_is_follower(): if option.impl_is_optiondescription() or not option.impl_is_follower():
raise ConfigError("index must be set only with a follower option") raise ConfigError("index must be set only with a follower option", subconfig=subsubconfig,)
length = self.get_length_leadership() length = self.get_length_leadership()
if index >= length: if index >= length:
raise LeadershipError( raise LeadershipError(
@ -569,10 +588,17 @@ class SubConfig:
parents = [self.parent] parents = [self.parent]
else: else:
if common_path: if common_path:
parent = self.parent
common_parent_number = common_path.count(".") + 1 common_parent_number = common_path.count(".") + 1
for idx in range(current_option_path.count(".") - common_parent_number): parent_count = current_option_path.count(".") - common_parent_number
parent = parent.parent if parent_count >= 0:
parent = self.parent
for idx in range(parent_count):
parent = parent.parent
elif parent_count == 0:
parent = self.parent
else:
# so -1
parent = self
parents = [parent] parents = [parent]
else: else:
common_parent_number = 0 common_parent_number = 0
@ -617,14 +643,16 @@ class SubConfig:
parents = new_parents parents = new_parents
subconfigs = [] subconfigs = []
for parent in parents: for parent in parents:
subconfigs.append( try:
parent.get_child( ret = parent.get_child(
search_option, search_option,
index, index,
validate_properties, validate_properties,
check_dynamic_without_identifiers=check_dynamic_without_identifiers, check_dynamic_without_identifiers=check_dynamic_without_identifiers,
) )
) except PropertiesOptionError as err:
ret = err
subconfigs.append(ret)
if subconfigs_is_a_list: if subconfigs_is_a_list:
return subconfigs return subconfigs
return subconfigs[0] return subconfigs[0]
@ -1398,7 +1426,7 @@ class KernelGroupConfig(_CommonConfig):
# pylint: disable=protected-access # pylint: disable=protected-access
ret.append( ret.append(
PropertiesOptionError( PropertiesOptionError(
err._subconfig, err.subconfig,
err.proptype, err.proptype,
err._settings, err._settings,
err._opt_type, err._opt_type,
@ -1646,7 +1674,7 @@ class KernelMixConfig(KernelGroupConfig):
# pylint: disable=protected-access # pylint: disable=protected-access
ret.append( ret.append(
PropertiesOptionError( PropertiesOptionError(
err._subconfig, err.subconfig,
err.proptype, err.proptype,
err._settings, err._settings,
err._opt_type, err._opt_type,

View file

@ -18,7 +18,7 @@
import weakref import weakref
from .i18n import _ from .i18n import _
from typing import Literal, Union from typing import Literal, Union, Optional
TiramisuErrorCode = Literal[ TiramisuErrorCode = Literal[
@ -101,7 +101,7 @@ class PropertiesOptionError(AttributeError):
subconfig, with_quote=True subconfig, with_quote=True
) )
self._orig_opt = None self._orig_opt = None
self._subconfig = subconfig self.subconfig = subconfig
self.proptype = proptype self.proptype = proptype
self.help_properties = help_properties self.help_properties = help_properties
self._settings = settings self._settings = settings
@ -136,7 +136,7 @@ class PropertiesOptionError(AttributeError):
self._orig_opt.impl_get_display_name(subconfig, with_quote=True) self._orig_opt.impl_get_display_name(subconfig, with_quote=True)
) )
arguments.append(self._name) arguments.append(self._name)
index = self._subconfig.index index = self.subconfig.index
if index is not None: if index is not None:
arguments.append(index) arguments.append(index)
if self.code == "property-frozen": if self.code == "property-frozen":
@ -215,10 +215,22 @@ class ConfigError(Exception):
def __init__( def __init__(
self, self,
exp, exp,
ori_err=None, *,
prefix: Optional[str] = None,
subconfig: Optional["Subconfig"]=None,
): ):
super().__init__(exp) super().__init__(exp)
self.ori_err = ori_err self.err_msg = exp
self.subconfig = subconfig
self.prefix = prefix
def __str__(self):
msg = self.prefix
if msg:
msg += ", {}".format(self.err_msg)
else:
msg = self.err_msg
return msg
class ConflictError(Exception): class ConflictError(Exception):
@ -419,9 +431,9 @@ class Errors:
display_name = option.impl_get_display_name(subconfig, with_quote=True) display_name = option.impl_get_display_name(subconfig, with_quote=True)
if original_error: if original_error:
raise ConfigError( raise ConfigError(
message.format(display_name, original_error, *extra_keys) message.format(display_name, original_error, *extra_keys), subconfig=subconfig,
) from original_error ) from original_error
raise ConfigError(message.format(display_name, extra_keys)) raise ConfigError(message.format(display_name, extra_keys), subconfig=subconfig)
errors = Errors() errors = Errors()

View file

@ -27,7 +27,7 @@ from itertools import chain
from ..i18n import _ from ..i18n import _
from ..setting import undefined from ..setting import undefined
from ..autolib import Calculation, ParamOption, ParamInformation, ParamSelfInformation from ..autolib import Calculation, ParamOption, ParamSelfOption, ParamInformation, ParamSelfInformation
STATIC_TUPLE = frozenset() STATIC_TUPLE = frozenset()
@ -104,9 +104,7 @@ class Base:
"Calculation" "Calculation"
).format(type(prop), name) ).format(type(prop), name)
) )
for param in chain(prop.params.args, prop.params.kwargs.values()): self.value_dependency(prop, type_="property")
if isinstance(param, ParamOption):
param.option._add_dependency(self, "property")
if properties: if properties:
_setattr(self, "_properties", properties) _setattr(self, "_properties", properties)
self.set_informations(informations) self.set_informations(informations)
@ -395,16 +393,22 @@ class BaseOption(Base):
self, self,
value: Any, value: Any,
is_identifier: bool = False, is_identifier: bool = False,
type_: str = 'default'
) -> Any: ) -> Any:
if not isinstance(is_identifier, bool):
raise Exception()
"""parse dependancy to add dependencies""" """parse dependancy to add dependencies"""
for param in chain(value.params.args, value.params.kwargs.values()): for param in chain(value.params.args, value.params.kwargs.values()):
if isinstance(param, ParamOption): if isinstance(param, ParamOption):
# pylint: disable=protected-access # pylint: disable=protected-access
if is_identifier: if is_identifier:
type_ = "identifier" _type_ = "identifier"
else: else:
type_ = "default" _type_ = type_
param.option._add_dependency(self, type_, is_identifier=is_identifier) param.option._add_dependency(self, _type_, is_identifier=is_identifier)
self._has_dependency = True
elif isinstance(param, ParamSelfOption) and not param.dynamic:
self._add_dependency(self, "self")
self._has_dependency = True self._has_dependency = True
elif isinstance(param, ParamInformation): elif isinstance(param, ParamInformation):
dest = self dest = self

View file

@ -45,9 +45,7 @@ class ChoiceOption(Option):
:param values: is a list of values the option can possibly take :param values: is a list of values the option can possibly take
""" """
if isinstance(values, Calculation): if isinstance(values, Calculation):
for param in chain(values.params.args, values.params.kwargs.values()): self.value_dependency(values, type_="choice")
if isinstance(param, ParamOption):
param.option._add_dependency(self, "choice")
elif not isinstance(values, tuple): elif not isinstance(values, tuple):
raise TypeError( raise TypeError(
_("values must be a tuple or a calculation for {0}").format(name) _("values must be a tuple or a calculation for {0}").format(name)
@ -75,7 +73,8 @@ class ChoiceOption(Option):
raise ConfigError( raise ConfigError(
_('the calculated values "{0}" for "{1}" is not a list' "").format( _('the calculated values "{0}" for "{1}" is not a list' "").format(
values, self.impl_getname() values, self.impl_getname()
) ),
subconfig=subconfig,
) )
return values return values

View file

@ -31,7 +31,6 @@ from ..i18n import _
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .baseoption import BaseOption from .baseoption import BaseOption
from ..setting import ConfigBag, undefined from ..setting import ConfigBag, undefined
from ..error import ConfigError
from ..autolib import Calculation, get_calculated_value from ..autolib import Calculation, get_calculated_value

View file

@ -56,7 +56,7 @@ class IntOption(Option):
value: int, value: int,
) -> None: ) -> None:
if not isinstance(value, int): if not isinstance(value, int):
raise ValueError(_("which is not an integer")) raise ValueError(_("it's not an integer"))
def second_level_validation(self, value, warnings_only): def second_level_validation(self, value, warnings_only):
min_integer = self.impl_get_extra("min_integer") min_integer = self.impl_get_extra("min_integer")

View file

@ -29,7 +29,7 @@ from ..setting import ConfigBag, groups, undefined, owners, Undefined
from .baseoption import BaseOption from .baseoption import BaseOption
# from .syndynoption import SubDynOptionDescription, SynDynOptionDescription # from .syndynoption import SubDynOptionDescription, SynDynOptionDescription
from ..error import ConfigError, ConflictError, AttributeOptionError from ..error import ConfigError, ConflictError, AttributeOptionError, PropertiesOptionError
class CacheOptionDescription(BaseOption): class CacheOptionDescription(BaseOption):
@ -164,34 +164,39 @@ class CacheOptionDescription(BaseOption):
parent, parent,
allow_dynoption=True, allow_dynoption=True,
) )
if doption.impl_is_dynoptiondescription(): try:
new_parents.extend( if doption.impl_is_dynoptiondescription():
parent.dyn_to_subconfig( new_parents.extend(
doption, parent.dyn_to_subconfig(
True, doption,
True,
)
) )
) else:
else: new_parents.append(
new_parents.append( parent.get_child(
parent.get_child( doption,
doption, None,
None, name=name,
True, validate_properties=True,
name=name, )
) )
) except PropertiesOptionError:
continue
parents = new_parents parents = new_parents
subconfigs = new_parents subconfigs = new_parents
else: else:
subconfigs = [ try:
context.get_sub_config( subconfigs = [
config_bag, context.get_sub_config(
option.impl_getpath(), config_bag,
None, option.impl_getpath(),
properties=None, None,
validate_properties=False, validate_properties=True,
) )
] ]
except PropertiesOptionError:
continue
if option.impl_is_follower(): if option.impl_is_follower():
for follower_subconfig in subconfigs: for follower_subconfig in subconfigs:
@ -206,32 +211,14 @@ class CacheOptionDescription(BaseOption):
idx_follower_subconfig = parent.get_child( idx_follower_subconfig = parent.get_child(
follower_subconfig.option, follower_subconfig.option,
index, index,
validate_properties=False, validate_properties=True,
)
value = values.get_value(idx_follower_subconfig)[0]
if value is None:
continue
values.set_storage_value(
follower_subconfig.path,
index,
value,
owners.forced,
) )
values.set_force_store_value(idx_follower_subconfig)
else: else:
for subconfig in subconfigs: for subconfig in subconfigs:
subconfig.properties = frozenset()
value = values.get_value(subconfig)[0]
if value is None:
continue
if values.hasvalue(subconfig.path): if values.hasvalue(subconfig.path):
continue continue
values.set_storage_value( values.set_force_store_value(subconfig)
subconfig.path,
None,
value,
owners.forced,
)
class OptionDescriptionWalk(CacheOptionDescription): class OptionDescriptionWalk(CacheOptionDescription):

View file

@ -39,7 +39,7 @@ class StrOption(Option):
) -> None: ) -> None:
"""validation""" """validation"""
if not isinstance(value, str): if not isinstance(value, str):
raise ValueError(_("which is not a string")) raise ValueError(_("it's not a string"))
class RegexpOption(StrOption): class RegexpOption(StrOption):

View file

@ -22,7 +22,6 @@
""" """
from typing import Any, Optional, Dict from typing import Any, Optional, Dict
from .baseoption import BaseOption, valid_name from .baseoption import BaseOption, valid_name
from ..error import ConfigError
from ..i18n import _ from ..i18n import _

View file

@ -638,7 +638,8 @@ class Settings:
raise ConfigError( raise ConfigError(
_("cannot add those permissives: {0}").format( _("cannot add those permissives: {0}").format(
" ".join(forbidden_permissives) " ".join(forbidden_permissives)
) ),
subconfig=subconfig,
) )
self._permissives.setdefault(path, {})[index] = permissives self._permissives.setdefault(path, {})[index] = permissives
if subconfig is not None: if subconfig is not None:

View file

@ -110,22 +110,52 @@ class Values:
value, owner = self._values.get(subconfig.path, {}).get( value, owner = self._values.get(subconfig.path, {}).get(
subconfig.index, default_value subconfig.index, default_value
) )
if owner == owners.default or ( self_properties = subconfig.properties or tuple()
"frozen" in subconfig.properties if owner != owners.default and (
"frozen" in self_properties
and ( and (
"force_default_on_freeze" in subconfig.properties "force_default_on_freeze" in self_properties
or self.check_force_to_metaconfig(subconfig) or self.check_force_to_metaconfig(subconfig)
) )
): ):
# the value is a default value # the value is a default value
# get it # get it
value = self.get_default_value(subconfig) value = self.get_default_value(subconfig)
if owner == owners.default:
if(
"force_store_value" in subconfig.config_bag.properties
and "force_store_value" in self_properties
):
value = self.get_default_value(subconfig)
if value is not None:
owner = owners.forced
self._setvalue(
subconfig,
value,
owner,
)
else:
# the value is a default value
# get it
value = self.get_default_value(subconfig)
value, has_calculation = get_calculated_value( value, has_calculation = get_calculated_value(
subconfig, subconfig,
value, value,
) )
return value, has_calculation return value, has_calculation
def set_force_store_value(self, subconfig):
value = self.get_default_value(subconfig)
if value is None:
return None
owner = owners.forced
self._setvalue(
subconfig,
value,
owner,
)
return value, owner
def get_default_owner( def get_default_owner(
self, self,
subconfig: "SubConfig", subconfig: "SubConfig",
@ -523,12 +553,18 @@ class Values:
was present was present
:returns: a `setting.owners.Owner` object :returns: a `setting.owners.Owner` object
""" """
s_properties = subconfig.properties self_properties = subconfig.properties
if ( if (
"frozen" in s_properties "frozen" in self_properties
and "force_default_on_freeze" in s_properties and "force_default_on_freeze" in self_properties
): ):
return owners.default return owners.default
setting_properties = subconfig.config_bag.properties
if (
"force_store_value" in setting_properties
and "force_store_value" in self_properties
):
self.set_force_store_value(subconfig)
if only_default: if only_default:
if self.hasvalue( if self.hasvalue(
subconfig.path, subconfig.path,
@ -544,8 +580,8 @@ class Values:
)[1] )[1]
if validate_meta is not False and ( if validate_meta is not False and (
owner is owners.default owner is owners.default
or "frozen" in s_properties or "frozen" in self_properties
and "force_metaconfig_on_freeze" in s_properties and "force_metaconfig_on_freeze" in self_properties
): ):
msubconfig = self._get_modified_parent(subconfig) msubconfig = self._get_modified_parent(subconfig)
if msubconfig is not None: if msubconfig is not None:
@ -554,7 +590,7 @@ class Values:
msubconfig, msubconfig,
only_default=only_default, only_default=only_default,
) )
elif "force_metaconfig_on_freeze" in s_properties: elif "force_metaconfig_on_freeze" in self_properties:
owner = owners.default owner = owners.default
return owner return owner
@ -579,7 +615,8 @@ class Values:
raise ConfigError( raise ConfigError(
_( _(
'"{0}" is a default value, so we cannot change owner to "{1}"' '"{0}" is a default value, so we cannot change owner to "{1}"'
).format(subconfig.path, owner) ).format(subconfig.path, owner),
subconfig=subconfig,
) )
subconfig.config_bag.context.get_settings().validate_frozen(subconfig) subconfig.config_bag.context.get_settings().validate_frozen(subconfig)
self._values[subconfig.path][subconfig.index][1] = owner self._values[subconfig.path][subconfig.index][1] = owner
@ -630,13 +667,7 @@ class Values:
"force_store_value" in setting_properties "force_store_value" in setting_properties
and "force_store_value" in self_properties and "force_store_value" in self_properties
): ):
value = self.get_default_value(subconfig) self.set_force_store_value(subconfig)
self._setvalue(
subconfig,
value,
owners.forced,
)
else: else:
value = None value = None
if subconfig.path in self._values: if subconfig.path in self._values:
@ -702,15 +733,9 @@ class Values:
"force_store_value" in setting_properties "force_store_value" in setting_properties
and "force_store_value" in self_properties and "force_store_value" in self_properties
): ):
value = self.get_default_value( force_store_value = self.set_force_store_value(subconfig)
subconfig, if force_store_value:
) value, owner = force_store_value
self._setvalue(
subconfig,
value,
owners.forced,
)
else: else:
self.resetvalue_index(subconfig) self.resetvalue_index(subconfig)
context.reset_cache(subconfig) context.reset_cache(subconfig)