From 1bbcea60ab85e1a7915a5de2e8366b36d528dd66 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 23 Feb 2019 22:10:43 +0100 Subject: [PATCH] add force_metaconfig_on_freeze special properties --- test/test_metaconfig.py | 61 ++++++++++++++++++++++++++++ test/test_option_default.py | 17 ++++++++ test/test_permissive.py | 1 + tiramisu/api.py | 8 ++-- tiramisu/option/leadership.py | 2 +- tiramisu/option/optiondescription.py | 9 ++-- tiramisu/setting.py | 43 ++++++++++---------- tiramisu/value.py | 22 ++++++++-- 8 files changed, 129 insertions(+), 34 deletions(-) diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index fcf10e2..27c5f8e 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -972,3 +972,64 @@ def test_meta_properties_meta_set_value(): assert isinstance(ret[0], ValueError) del ret[0] del ret + + +def test_metaconfig_force_metaconfig_on_freeze(): + dummy1 = StrOption('dummy1', 'doc dummy', default='default') + dummy2 = StrOption('dummy2', 'doc dummy', default='default', properties=('force_default_on_freeze',)) + group = OptionDescription('group', '', [dummy1, dummy2]) + config = Config(group) + meta1 = MetaConfig([config], session_id='meta1') + meta1.owner.set(owners.meta1) + meta2 = MetaConfig([meta1], session_id='meta2') + meta2.owner.set(owners.meta2) + config.property.read_write() + + config.option('dummy1').property.add('frozen') + config.option('dummy1').property.add('force_metaconfig_on_freeze') + config.option('dummy2').property.add('frozen') + # + assert config.option('dummy1').value.get() == 'default' + assert config.option('dummy1').owner.get() == 'default' + assert config.option('dummy2').value.get() == 'default' + assert config.option('dummy2').owner.get() == 'default' + # + meta2.option('dummy1').value.set('meta2') + meta2.option('dummy2').value.set('meta2') + # + assert config.option('dummy1').value.get() == 'meta2' + assert config.option('dummy1').owner.get() == 'meta2' + assert config.option('dummy2').value.get() == 'default' + assert config.option('dummy2').owner.get() == 'default' + # + config.option('dummy1').property.pop('frozen') + config.option('dummy2').property.pop('frozen') + config.option('dummy1').value.set('config') + config.option('dummy2').value.set('config') + config.option('dummy1').property.add('frozen') + config.option('dummy2').property.add('frozen') + # + assert config.option('dummy1').value.get() == 'meta2' + assert config.option('dummy1').owner.get() == 'meta2' + assert config.option('dummy2').value.get() == 'default' + assert config.option('dummy2').owner.get() == 'default' + # + meta1.option('dummy1').value.set('meta1') + meta1.option('dummy2').value.set('meta1') + # + assert config.option('dummy1').value.get() == 'meta1' + assert config.option('dummy1').owner.get() == 'meta1' + assert config.option('dummy2').value.get() == 'default' + assert config.option('dummy2').owner.get() == 'default' + # + meta1.option('dummy1').property.add('force_metaconfig_on_freeze') + assert config.option('dummy1').value.get() == 'meta2' + assert config.option('dummy1').owner.get() == 'meta2' + # + meta2.option('dummy1').property.add('force_metaconfig_on_freeze') + assert config.option('dummy1').value.get() == 'default' + assert config.option('dummy1').owner.get() == 'default' + # + meta1.option('dummy1').property.pop('force_metaconfig_on_freeze') + assert config.option('dummy1').value.get() == 'meta1' + assert config.option('dummy1').owner.get() == 'meta1' diff --git a/test/test_option_default.py b/test/test_option_default.py index 31923be..c6454b3 100644 --- a/test/test_option_default.py +++ b/test/test_option_default.py @@ -120,6 +120,14 @@ def test_force_default_on_freeze_leader(): raises(ConfigError, "Config(descr)") +def test_force_metaconfig_on_freeze_leader(): + dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze',)) + dummy2 = BoolOption('dummy2', 'Test string option', multi=True) + descr = Leadership("dummy1", "", [dummy1, dummy2]) + descr = OptionDescription("root", "", [descr]) + raises(ConfigError, "Config(descr)") + + def test_force_default_on_freeze_leader_frozen(): dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_default_on_freeze', 'frozen')) dummy2 = BoolOption('dummy2', 'Test string option', multi=True) @@ -129,6 +137,15 @@ def test_force_default_on_freeze_leader_frozen(): raises(ConfigError, "api.option('dummy1.dummy1').property.pop('frozen')") +def test_force_metaconfig_on_freeze_leader_frozen(): + dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_metaconfig_on_freeze', 'frozen')) + dummy2 = BoolOption('dummy2', 'Test string option', multi=True) + descr = Leadership("dummy1", "", [dummy1, dummy2]) + descr = OptionDescription("root", "", [descr]) + api = Config(descr) + raises(ConfigError, "api.option('dummy1.dummy1').property.pop('frozen')") + + def test_force_default_on_freeze_follower(): dummy1 = BoolOption('dummy1', 'Test int option', multi=True) dummy2 = BoolOption('dummy2', 'Test string option', multi=True, properties=('force_default_on_freeze',)) diff --git a/test/test_permissive.py b/test/test_permissive.py index c8aebe7..64107cb 100644 --- a/test/test_permissive.py +++ b/test/test_permissive.py @@ -185,6 +185,7 @@ def test_forbidden_permissive(): api = Config(descr) api.property.read_write() raises(ConfigError, "api.permissive.set(frozenset(['force_default_on_freeze']))") + raises(ConfigError, "api.permissive.set(frozenset(['force_metaconfig_on_freeze']))") def test_permissive_option(): diff --git a/tiramisu/api.py b/tiramisu/api.py index 9a58c5d..1daf269 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -350,11 +350,11 @@ class TiramisuOptionProperty(CommonTiramisuOption): raise ConfigError(_('cannot add this property: "{0}"').format( ' '.join(prop))) props = self._settings.getproperties(self._option_bag, - apply_requires=False) + apply_requires=False) self._settings.setproperties(self._option_bag.path, - props | {prop}, - self._option_bag, - self._option_bag.config_bag.context) + props | {prop}, + self._option_bag, + self._option_bag.config_bag.context) def pop(self, prop): """Remove new property for an option""" diff --git a/tiramisu/option/leadership.py b/tiramisu/option/leadership.py index 4303976..d7c0a3a 100644 --- a/tiramisu/option/leadership.py +++ b/tiramisu/option/leadership.py @@ -167,7 +167,7 @@ class Leadership(OptionDescription): follower_path, index, config_bag) - # do not check force_default_on_freeze + # do not check force_default_on_freeze or force_metaconfig_on_freeze soption_bag.properties = set() if not values.is_default_owner(soption_bag, validate_meta=False) and followerlen > index: diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index 2944dbe..bd577d3 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -90,12 +90,15 @@ class CacheOptionDescription(BaseOption): '"force_store_value" property').format( option.impl_get_display_name())) force_store_values.append((subpath, option)) - if __debug__ and 'force_default_on_freeze' in properties and \ + if __debug__ and ('force_default_on_freeze' in properties or \ + 'force_metaconfig_on_freeze' in properties) and \ 'frozen' not in properties and \ option.impl_is_leader(): raise ConfigError(_('a leader ({0}) cannot have ' - '"force_default_on_freeze" property without "frozen"' - '').format(subpath)) + '"force_default_on_freeze" or ' + '"force_metaconfig_on_freeze" ' + 'property without "frozen"' + '').format(option.impl_get_display_name())) for cons_id, func, all_cons_opts, params in option.get_consistencies(): option._valid_consistencies(all_cons_opts[1:], init=False) if func not in ALLOWED_CONST_LIST and is_multi: diff --git a/tiramisu/setting.py b/tiramisu/setting.py index a0f5d39..790f2d6 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -113,6 +113,7 @@ RW_REMOVE = frozenset(['permissive', 'everything_frozen', 'mandatory', FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value']) FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze', + 'force_metaconfig_on_freeze', 'force_store_value']) @@ -164,11 +165,14 @@ class OptionBag: def __delattr__(self, key): if key in ['properties', 'permissives']: + try: + super().__delattr__(key) + except AttributeError: + pass return raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover def copy(self): - kwargs = {} option_bag = OptionBag() for key in self.__slots__: if key == 'properties' and self.config_bag is undefined: @@ -182,7 +186,7 @@ class ConfigBag: 'properties', # properties for current context 'true_properties', # properties for current context 'permissives', # permissives for current context - ) + ) def __init__(self, context, **kwargs): self.context = context for key, value in kwargs.items(): @@ -302,10 +306,10 @@ groups = GroupModule() groups.default = groups.DefaultGroupType('default') """groups.leadership - leadership group is a special optiondescription, all suboptions should be - multi option and all values should have same length, to find leader's - option, the optiondescription's name should be same than de leader's - option""" + leadership group is a special optiondescription, all suboptions should + be multi option and all values should have same length, to find + leader's option, the optiondescription's name should be same than de + leader's option""" groups.leadership = groups.LeadershipGroupType('leadership') """ groups.family @@ -607,10 +611,9 @@ class Settings(object): def set_context_properties(self, properties, context): - self.setproperties(None, - properties, - None, - context) + self._p_.setproperties(None, + properties) + context.cfgimpl_reset_cache(None) def setproperties(self, path, @@ -621,33 +624,29 @@ class Settings(object): (never save properties if same has option properties) """ # should have index !!! - if path is not None and option_bag.option.impl_getrequires() is not None: + opt = option_bag.option + if opt.impl_getrequires() is not None: not_allowed_props = properties & \ - getattr(option_bag.option, '_calc_properties', static_set) + getattr(opt, '_calc_properties', static_set) if not_allowed_props: raise ValueError(_('cannot set property {} for option "{}" this property is ' 'calculated').format(display_list(list(not_allowed_props), add_quote=True), - option_bag.option.impl_get_display_name())) - if option_bag is None: - opt = None - else: - opt = option_bag.option - if opt and opt.impl_is_symlinkoption(): + opt.impl_get_display_name())) + if opt.impl_is_symlinkoption(): raise TypeError(_("can't assign property to the symlinkoption \"{}\"" "").format(opt.impl_get_display_name())) - if 'force_default_on_freeze' in properties and \ + if ('force_default_on_freeze' in properties or 'force_metaconfig_on_freeze' in properties) and \ 'frozen' not in properties and \ opt.impl_is_leader(): raise ConfigError(_('a leader ({0}) cannot have ' - '"force_default_on_freeze" property without "frozen"' + '"force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"' '').format(opt.impl_get_display_name())) self._p_.setproperties(path, properties) # values too because of follower values could have a PropertiesOptionError has value context.cfgimpl_reset_cache(option_bag) - if option_bag is not None: - del option_bag.properties + del option_bag.properties def set_context_permissives(self, permissives): diff --git a/tiramisu/value.py b/tiramisu/value.py index d8e4ecf..6a2a8cd 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -114,10 +114,12 @@ class Values(object): owners.default, index=_index, with_value=True) - if owner != owners.default and not ('frozen' in option_bag.properties and \ - 'force_default_on_freeze' in option_bag.properties): + if owner != owners.default and \ + not ('frozen' in option_bag.properties and 'force_default_on_freeze' in option_bag.properties) and \ + not ('frozen' in option_bag.properties and 'force_metaconfig_on_freeze' in option_bag.properties): # if a value is store in storage, check if not frozen + force_default_on_freeze # if frozen + force_default_on_freeze => force default value + # if frozen + force_metaconfig_on_freeze => force value of metaconfig return value return self.getdefaultvalue(option_bag) @@ -325,8 +327,17 @@ class Values(object): doption_bag = option_bag.copy() config_bag = option_bag.config_bag.copy() config_bag.context = meta - config_bag.unrestraint() doption_bag.config_bag = config_bag + if 'force_metaconfig_on_freeze' in option_bag.properties: + # remove force_metaconfig_on_freeze only if option in metaconfig + # hasn't force_metaconfig_on_freeze properties + ori_properties = doption_bag.properties + del doption_bag.properties + if 'force_metaconfig_on_freeze' not in doption_bag.properties: + doption_bag.properties = ori_properties - {'force_metaconfig_on_freeze'} + else: + doption_bag.properties = ori_properties + config_bag.unrestraint() meta_option_bag = meta.cfgimpl_get_values().getowner(doption_bag, only_default=True) if meta_option_bag == owners.default: @@ -378,11 +389,14 @@ class Values(object): owner = self._p_.getowner(option_bag.path, owners.default, index=option_bag.index) - if owner is owners.default and validate_meta is not False: + if validate_meta is not False and (owner is owners.default or \ + 'force_metaconfig_on_freeze' in option_bag.properties): moption_bag = self._get_meta(option_bag) if moption_bag: owner = moption_bag.config_bag.context.cfgimpl_get_values().getowner(moption_bag, only_default=only_default) + elif 'force_metaconfig_on_freeze' in option_bag.properties: + return owners.default return owner def setowner(self,