add force_metaconfig_on_freeze special properties

This commit is contained in:
Emmanuel Garette 2019-02-23 22:10:43 +01:00
parent c3f968dbde
commit 1bbcea60ab
8 changed files with 129 additions and 34 deletions

View File

@ -972,3 +972,64 @@ def test_meta_properties_meta_set_value():
assert isinstance(ret[0], ValueError) assert isinstance(ret[0], ValueError)
del ret[0] del ret[0]
del ret 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'

View File

@ -120,6 +120,14 @@ def test_force_default_on_freeze_leader():
raises(ConfigError, "Config(descr)") 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(): def test_force_default_on_freeze_leader_frozen():
dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_default_on_freeze', 'frozen')) dummy1 = BoolOption('dummy1', 'Test int option', multi=True, properties=('force_default_on_freeze', 'frozen'))
dummy2 = BoolOption('dummy2', 'Test string option', multi=True) 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')") 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(): def test_force_default_on_freeze_follower():
dummy1 = BoolOption('dummy1', 'Test int option', multi=True) dummy1 = BoolOption('dummy1', 'Test int option', multi=True)
dummy2 = BoolOption('dummy2', 'Test string option', multi=True, properties=('force_default_on_freeze',)) dummy2 = BoolOption('dummy2', 'Test string option', multi=True, properties=('force_default_on_freeze',))

View File

@ -185,6 +185,7 @@ def test_forbidden_permissive():
api = Config(descr) api = Config(descr)
api.property.read_write() api.property.read_write()
raises(ConfigError, "api.permissive.set(frozenset(['force_default_on_freeze']))") raises(ConfigError, "api.permissive.set(frozenset(['force_default_on_freeze']))")
raises(ConfigError, "api.permissive.set(frozenset(['force_metaconfig_on_freeze']))")
def test_permissive_option(): def test_permissive_option():

View File

@ -350,11 +350,11 @@ class TiramisuOptionProperty(CommonTiramisuOption):
raise ConfigError(_('cannot add this property: "{0}"').format( raise ConfigError(_('cannot add this property: "{0}"').format(
' '.join(prop))) ' '.join(prop)))
props = self._settings.getproperties(self._option_bag, props = self._settings.getproperties(self._option_bag,
apply_requires=False) apply_requires=False)
self._settings.setproperties(self._option_bag.path, self._settings.setproperties(self._option_bag.path,
props | {prop}, props | {prop},
self._option_bag, self._option_bag,
self._option_bag.config_bag.context) self._option_bag.config_bag.context)
def pop(self, prop): def pop(self, prop):
"""Remove new property for an option""" """Remove new property for an option"""

View File

@ -167,7 +167,7 @@ class Leadership(OptionDescription):
follower_path, follower_path,
index, index,
config_bag) 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() soption_bag.properties = set()
if not values.is_default_owner(soption_bag, if not values.is_default_owner(soption_bag,
validate_meta=False) and followerlen > index: validate_meta=False) and followerlen > index:

View File

@ -90,12 +90,15 @@ class CacheOptionDescription(BaseOption):
'"force_store_value" property').format( '"force_store_value" property').format(
option.impl_get_display_name())) option.impl_get_display_name()))
force_store_values.append((subpath, option)) 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 \ 'frozen' not in properties and \
option.impl_is_leader(): option.impl_is_leader():
raise ConfigError(_('a leader ({0}) cannot have ' raise ConfigError(_('a leader ({0}) cannot have '
'"force_default_on_freeze" property without "frozen"' '"force_default_on_freeze" or '
'').format(subpath)) '"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(): for cons_id, func, all_cons_opts, params in option.get_consistencies():
option._valid_consistencies(all_cons_opts[1:], init=False) option._valid_consistencies(all_cons_opts[1:], init=False)
if func not in ALLOWED_CONST_LIST and is_multi: if func not in ALLOWED_CONST_LIST and is_multi:

View File

@ -113,6 +113,7 @@ RW_REMOVE = frozenset(['permissive', 'everything_frozen', 'mandatory',
FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value']) FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value'])
FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze', FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze',
'force_metaconfig_on_freeze',
'force_store_value']) 'force_store_value'])
@ -164,11 +165,14 @@ class OptionBag:
def __delattr__(self, key): def __delattr__(self, key):
if key in ['properties', 'permissives']: if key in ['properties', 'permissives']:
try:
super().__delattr__(key)
except AttributeError:
pass
return return
raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover
def copy(self): def copy(self):
kwargs = {}
option_bag = OptionBag() option_bag = OptionBag()
for key in self.__slots__: for key in self.__slots__:
if key == 'properties' and self.config_bag is undefined: if key == 'properties' and self.config_bag is undefined:
@ -182,7 +186,7 @@ class ConfigBag:
'properties', # properties for current context 'properties', # properties for current context
'true_properties', # properties for current context 'true_properties', # properties for current context
'permissives', # permissives for current context 'permissives', # permissives for current context
) )
def __init__(self, context, **kwargs): def __init__(self, context, **kwargs):
self.context = context self.context = context
for key, value in kwargs.items(): for key, value in kwargs.items():
@ -302,10 +306,10 @@ groups = GroupModule()
groups.default = groups.DefaultGroupType('default') groups.default = groups.DefaultGroupType('default')
"""groups.leadership """groups.leadership
leadership group is a special optiondescription, all suboptions should be leadership group is a special optiondescription, all suboptions should
multi option and all values should have same length, to find leader's be multi option and all values should have same length, to find
option, the optiondescription's name should be same than de leader's leader's option, the optiondescription's name should be same than de
option""" leader's option"""
groups.leadership = groups.LeadershipGroupType('leadership') groups.leadership = groups.LeadershipGroupType('leadership')
""" groups.family """ groups.family
@ -607,10 +611,9 @@ class Settings(object):
def set_context_properties(self, def set_context_properties(self,
properties, properties,
context): context):
self.setproperties(None, self._p_.setproperties(None,
properties, properties)
None, context.cfgimpl_reset_cache(None)
context)
def setproperties(self, def setproperties(self,
path, path,
@ -621,33 +624,29 @@ class Settings(object):
(never save properties if same has option properties) (never save properties if same has option properties)
""" """
# should have index !!! # 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 & \ not_allowed_props = properties & \
getattr(option_bag.option, '_calc_properties', static_set) getattr(opt, '_calc_properties', static_set)
if not_allowed_props: if not_allowed_props:
raise ValueError(_('cannot set property {} for option "{}" this property is ' raise ValueError(_('cannot set property {} for option "{}" this property is '
'calculated').format(display_list(list(not_allowed_props), 'calculated').format(display_list(list(not_allowed_props),
add_quote=True), add_quote=True),
option_bag.option.impl_get_display_name())) opt.impl_get_display_name()))
if option_bag is None: if opt.impl_is_symlinkoption():
opt = None
else:
opt = option_bag.option
if opt and opt.impl_is_symlinkoption():
raise TypeError(_("can't assign property to the symlinkoption \"{}\"" raise TypeError(_("can't assign property to the symlinkoption \"{}\""
"").format(opt.impl_get_display_name())) "").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 \ 'frozen' not in properties and \
opt.impl_is_leader(): opt.impl_is_leader():
raise ConfigError(_('a leader ({0}) cannot have ' 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())) '').format(opt.impl_get_display_name()))
self._p_.setproperties(path, self._p_.setproperties(path,
properties) properties)
# values too because of follower values could have a PropertiesOptionError has value # values too because of follower values could have a PropertiesOptionError has value
context.cfgimpl_reset_cache(option_bag) 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, def set_context_permissives(self,
permissives): permissives):

View File

@ -114,10 +114,12 @@ class Values(object):
owners.default, owners.default,
index=_index, index=_index,
with_value=True) with_value=True)
if owner != owners.default and not ('frozen' in option_bag.properties and \ if owner != owners.default and \
'force_default_on_freeze' in option_bag.properties): 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 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_default_on_freeze => force default value
# if frozen + force_metaconfig_on_freeze => force value of metaconfig
return value return value
return self.getdefaultvalue(option_bag) return self.getdefaultvalue(option_bag)
@ -325,8 +327,17 @@ class Values(object):
doption_bag = option_bag.copy() doption_bag = option_bag.copy()
config_bag = option_bag.config_bag.copy() config_bag = option_bag.config_bag.copy()
config_bag.context = meta config_bag.context = meta
config_bag.unrestraint()
doption_bag.config_bag = config_bag 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, meta_option_bag = meta.cfgimpl_get_values().getowner(doption_bag,
only_default=True) only_default=True)
if meta_option_bag == owners.default: if meta_option_bag == owners.default:
@ -378,11 +389,14 @@ class Values(object):
owner = self._p_.getowner(option_bag.path, owner = self._p_.getowner(option_bag.path,
owners.default, owners.default,
index=option_bag.index) 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) moption_bag = self._get_meta(option_bag)
if moption_bag: if moption_bag:
owner = moption_bag.config_bag.context.cfgimpl_get_values().getowner(moption_bag, owner = moption_bag.config_bag.context.cfgimpl_get_values().getowner(moption_bag,
only_default=only_default) only_default=only_default)
elif 'force_metaconfig_on_freeze' in option_bag.properties:
return owners.default
return owner return owner
def setowner(self, def setowner(self,