diff --git a/tests/test_dyn_optiondescription.py b/tests/test_dyn_optiondescription.py index 04755f8..5ab8240 100644 --- a/tests/test_dyn_optiondescription.py +++ b/tests/test_dyn_optiondescription.py @@ -39,6 +39,12 @@ def return_true(value, param=None, identifier=None): raise ValueError('no value') +def return_no_dyn(value, identifier): + if value in [['val', 'val'], ['yes', 'yes']]: + return + raise ValueError('no value') + + def return_dynval(value='val', identifier=None): return value @@ -1128,6 +1134,21 @@ def test_validator_dyndescription(): # 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), ParamIdentifier())))]) + 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_makedict_dyndescription_context(): val1 = StrOption('val1', '', ['val1', 'val2'], multi=True) st = StrOption('st', '') diff --git a/tests/test_freeze.py b/tests/test_freeze.py index 0622709..332035f 100644 --- a/tests/test_freeze.py +++ b/tests/test_freeze.py @@ -165,15 +165,15 @@ def test_force_store_value(): cfg = Config(od1) compare(cfg.value.exportation(), {}) 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('wantref').value.set(True) 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('wantref').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() diff --git a/tests/test_option_setting.py b/tests/test_option_setting.py index 2c13699..9b120d7 100644 --- a/tests/test_option_setting.py +++ b/tests/test_option_setting.py @@ -932,6 +932,45 @@ def test_none_is_not_modified(): # 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(): msg_error = _("cannot access to {0} {1} because has {2} {3}") msg_is_not = _('the value of "{0}" is not {1}') diff --git a/tiramisu/api.py b/tiramisu/api.py index 553f098..78b44a2 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -1454,6 +1454,7 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet): with_default_owner: bool = False, ): """Export all values""" + self._force_store_value() exportation = deepcopy(self._config_bag.context.get_values()._values) if not with_default_owner: del exportation[None] @@ -1469,6 +1470,10 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet): if None not in values: 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): """Global owner""" diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index eb8d671..6da0267 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -29,7 +29,7 @@ from ..setting import ConfigBag, groups, undefined, owners, Undefined from .baseoption import BaseOption # from .syndynoption import SubDynOptionDescription, SynDynOptionDescription -from ..error import ConfigError, ConflictError, AttributeOptionError +from ..error import ConfigError, ConflictError, AttributeOptionError, PropertiesOptionError class CacheOptionDescription(BaseOption): @@ -164,34 +164,39 @@ class CacheOptionDescription(BaseOption): parent, allow_dynoption=True, ) - if doption.impl_is_dynoptiondescription(): - new_parents.extend( - parent.dyn_to_subconfig( - doption, - True, + try: + if doption.impl_is_dynoptiondescription(): + new_parents.extend( + parent.dyn_to_subconfig( + doption, + True, + ) ) - ) - else: - new_parents.append( - parent.get_child( - doption, - None, - name=name, - validate_properties=False, + else: + new_parents.append( + parent.get_child( + doption, + None, + name=name, + validate_properties=True, + ) ) - ) + except PropertiesOptionError: + continue parents = new_parents subconfigs = new_parents else: - subconfigs = [ - context.get_sub_config( - config_bag, - option.impl_getpath(), - None, - properties=None, - validate_properties=False, - ) - ] + try: + subconfigs = [ + context.get_sub_config( + config_bag, + option.impl_getpath(), + None, + validate_properties=True, + ) + ] + except PropertiesOptionError: + continue if option.impl_is_follower(): for follower_subconfig in subconfigs: @@ -206,32 +211,14 @@ class CacheOptionDescription(BaseOption): idx_follower_subconfig = parent.get_child( follower_subconfig.option, index, - validate_properties=False, - ) - - value = values.get_value(idx_follower_subconfig)[0] - if value is None: - continue - values.set_storage_value( - follower_subconfig.path, - index, - value, - owners.forced, + validate_properties=True, ) + values.set_force_store_value(idx_follower_subconfig) else: for subconfig in subconfigs: - subconfig.properties = frozenset() - value = values.get_value(subconfig)[0] - if value is None: - continue if values.hasvalue(subconfig.path): continue - values.set_storage_value( - subconfig.path, - None, - value, - owners.forced, - ) + values.set_force_store_value(subconfig) class OptionDescriptionWalk(CacheOptionDescription): diff --git a/tiramisu/value.py b/tiramisu/value.py index a59a361..7fd4bc0 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -110,22 +110,52 @@ class Values: value, owner = self._values.get(subconfig.path, {}).get( subconfig.index, default_value ) - if owner == owners.default or ( - "frozen" in subconfig.properties + self_properties = subconfig.properties or tuple() + if owner != owners.default and ( + "frozen" in self_properties and ( - "force_default_on_freeze" in subconfig.properties + "force_default_on_freeze" in self_properties or self.check_force_to_metaconfig(subconfig) ) ): # the value is a default value # get it 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( subconfig, value, ) 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( self, subconfig: "SubConfig", @@ -523,12 +553,18 @@ class Values: was present :returns: a `setting.owners.Owner` object """ - s_properties = subconfig.properties + self_properties = subconfig.properties if ( - "frozen" in s_properties - and "force_default_on_freeze" in s_properties + "frozen" in self_properties + and "force_default_on_freeze" in self_properties ): 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 self.hasvalue( subconfig.path, @@ -544,8 +580,8 @@ class Values: )[1] if validate_meta is not False and ( owner is owners.default - or "frozen" in s_properties - and "force_metaconfig_on_freeze" in s_properties + or "frozen" in self_properties + and "force_metaconfig_on_freeze" in self_properties ): msubconfig = self._get_modified_parent(subconfig) if msubconfig is not None: @@ -554,7 +590,7 @@ class Values: msubconfig, only_default=only_default, ) - elif "force_metaconfig_on_freeze" in s_properties: + elif "force_metaconfig_on_freeze" in self_properties: owner = owners.default return owner @@ -630,13 +666,7 @@ class Values: "force_store_value" in setting_properties and "force_store_value" in self_properties ): - value = self.get_default_value(subconfig) - - self._setvalue( - subconfig, - value, - owners.forced, - ) + self.set_force_store_value(subconfig) else: value = None if subconfig.path in self._values: @@ -702,15 +732,9 @@ class Values: "force_store_value" in setting_properties and "force_store_value" in self_properties ): - value = self.get_default_value( - subconfig, - ) - - self._setvalue( - subconfig, - value, - owners.forced, - ) + force_store_value = self.set_force_store_value(subconfig) + if force_store_value: + value, owner = force_store_value else: self.resetvalue_index(subconfig) context.reset_cache(subconfig)