Config could have multiple parents

This commit is contained in:
Emmanuel Garette 2019-08-05 22:31:56 +02:00
parent 113b7a3410
commit 2d7d237e19
5 changed files with 181 additions and 99 deletions

View File

@ -293,8 +293,8 @@ def test_meta_add_config_readd():
config = Config(od, session_id='new') config = Config(od, session_id='new')
# #
meta.config.add(config) meta.config.add(config)
# meta2.config.add(config)
raises(ConflictError, "meta2.config.add(config)") assert len(list(config.config.parents())) == 2
def test_meta_new_config_wrong_name(): def test_meta_new_config_wrong_name():
@ -429,7 +429,11 @@ def test_meta_unconsistent():
meta.owner.set(owners.meta1) meta.owner.set(owners.meta1)
raises(TypeError, 'MetaConfig("string")') raises(TypeError, 'MetaConfig("string")')
#same descr but conf1 already in meta #same descr but conf1 already in meta
raises(ValueError, "MetaConfig([conf1, conf3])") assert len(list(conf1.config.parents())) == 1
assert len(list(conf3.config.parents())) == 0
new_meta = MetaConfig([conf1, conf3])
assert len(list(conf1.config.parents())) == 2
assert len(list(conf3.config.parents())) == 1
#not same descr #not same descr
raises(ValueError, "MetaConfig([conf3, conf4])") raises(ValueError, "MetaConfig([conf3, conf4])")
@ -786,39 +790,54 @@ def test_meta_callback_follower():
interface1 = Leadership('val1', '', [val1, val3, val4]) interface1 = Leadership('val1', '', [val1, val3, val4])
od = OptionDescription('root', '', [interface1]) od = OptionDescription('root', '', [interface1])
maconfig = OptionDescription('rootconfig', '', [val, interface1]) maconfig = OptionDescription('rootconfig', '', [val, interface1])
cfg = Config(maconfig, session_id='cfg1') cfg1 = Config(maconfig, session_id='cfg1')
meta = MetaConfig([cfg]) meta = MetaConfig([cfg1])
meta.property.read_write() meta.property.read_write()
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} assert cfg1.value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
meta.config('cfg1').option('val').value.set('val1') #
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val3': ['val1'], 'val': 'val1'} cfg1.option('val').value.set('val1')
meta.config('cfg1').option('val').value.reset() assert cfg1.value.dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val3': ['val1'], 'val': 'val1'}
#
cfg1.option('val').value.reset()
meta.option('val').value.set('val1') meta.option('val').value.set('val1')
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val3': ['val1'], 'val': 'val1'} assert cfg1.value.dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val3': ['val1'], 'val': 'val1'}
#
meta.option('val').value.reset() meta.option('val').value.reset()
meta.config('cfg1').option('val1.val2', 0).value.set('val2') cfg1.option('val1.val2', 0).value.set('val2')
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} assert cfg1.value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
meta.config('cfg1').option('val1.val2', 0).value.reset() #
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} cfg1.option('val1.val2', 0).value.reset()
assert cfg1.value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
meta.option('val1.val2', 0).value.set('val2') meta.option('val1.val2', 0).value.set('val2')
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} assert cfg1.value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
meta.config('cfg1').option('val1.val3', 0).value.set('val6') #
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val6'], 'val': 'val'} meta.option('val1.val1').value.set(['val'])
assert cfg1.value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
cfg1.option('val1.val3', 0).value.set('val6')
assert cfg1.value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val6'], 'val': 'val'}
#
meta.option('val1.val2', 0).value.reset() meta.option('val1.val2', 0).value.reset()
meta.config('cfg1').option('val1.val3', 0).value.reset() cfg1.option('val1.val3', 0).value.reset()
meta.config('cfg1').option('val1.val1').value.set(['val3']) cfg1.option('val1.val1').value.set(['val3'])
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val3'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'} assert cfg1.value.dict() == {'val1.val2': ['val3'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'}
meta.config('cfg1').option('val1.val1').value.reset() #
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} cfg1.option('val1.val1').value.reset()
assert cfg1.value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
meta.option('val1.val1').value.set(['val3']) meta.option('val1.val1').value.set(['val3'])
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val3'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'} assert cfg1.value.dict() == {'val1.val2': ['val3'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'}
meta.config('cfg1').option('val1.val2', 0).value.set('val2') #
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'} cfg1.option('val1.val2', 0).value.set('val2')
assert cfg1.value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'}
#
meta.option('val1.val1').value.set(['val3', 'rah']) meta.option('val1.val1').value.set(['val3', 'rah'])
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val2', 'rah'], 'val1.val1': ['val3', 'rah'], 'val1.val3': ['val3', 'rah'], 'val': 'val'} assert cfg1.value.dict() == {'val1.val2': ['val2', 'rah'], 'val1.val1': ['val3', 'rah'], 'val1.val3': ['val3', 'rah'], 'val': 'val'}
#
meta.option('val1.val1').value.pop(1) meta.option('val1.val1').value.pop(1)
meta.option('val1.val1').value.set(['val4']) meta.option('val1.val1').value.set(['val4'])
assert meta.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val4'], 'val1.val3': ['val4'], 'val': 'val'} assert cfg1.value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val4'], 'val1.val3': ['val4'], 'val': 'val'}
def test_meta_reset(): def test_meta_reset():
@ -861,8 +880,13 @@ def test_meta_properties_meta_copy():
meta.property.read_write() meta.property.read_write()
conf3 = meta.config('conf1').config.copy(session_id='conf3') conf3 = meta.config('conf1').config.copy(session_id='conf3')
# old fashion
meta2 = conf3.config.metaconfig() meta2 = conf3.config.metaconfig()
assert meta.config.name() == meta2.config.name() assert meta.config.name() == meta2.config.name()
# new method
meta2 = list(conf3.config.parents())
assert len(meta2) == 1
assert meta.config.name() == meta2[0].config.name()
assert meta.config('conf1').value.dict() == {'ip_admin_eth0': ['192.168.1.1']} assert meta.config('conf1').value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert meta.config('conf2').value.dict() == {'ip_admin_eth0': ['192.168.1.1']} assert meta.config('conf2').value.dict() == {'ip_admin_eth0': ['192.168.1.1']}

View File

@ -306,7 +306,11 @@ def test_mix_unconsistent():
mix.owner.set(owners.mix1) mix.owner.set(owners.mix1)
raises(TypeError, 'MixConfig(od2, "string")') raises(TypeError, 'MixConfig(od2, "string")')
# same descr but conf1 already in mix # same descr but conf1 already in mix
raises(ValueError, "MixConfig(od2, [conf1, conf3])") assert len(list(conf1.config.parents())) == 1
assert len(list(conf3.config.parents())) == 0
new_mix = MixConfig(od2, [conf1, conf3])
assert len(list(conf1.config.parents())) == 2
assert len(list(conf3.config.parents())) == 1
# not same descr # not same descr
MixConfig(od2, [conf3, conf4]) MixConfig(od2, [conf3, conf4])
@ -647,32 +651,47 @@ def test_mix_callback_follower():
mix = MixConfig(maconfig, [cfg]) mix = MixConfig(maconfig, [cfg])
mix.property.read_write() mix.property.read_write()
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
mix.config('cfg1').option('val').value.set('val1') mix.config('cfg1').option('val').value.set('val1')
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val3': ['val1'], 'val': 'val1'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val3': ['val1'], 'val': 'val1'}
#
mix.config('cfg1').option('val').value.reset() mix.config('cfg1').option('val').value.reset()
mix.option('val').value.set('val1') mix.option('val').value.set('val1')
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val3': ['val1'], 'val': 'val1'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val3': ['val1'], 'val': 'val1'}
#
mix.option('val').value.reset() mix.option('val').value.reset()
mix.config('cfg1').option('val1.val2', 0).value.set('val2') mix.config('cfg1').option('val1.val2', 0).value.set('val2')
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
mix.config('cfg1').option('val1.val2', 0).value.reset() mix.config('cfg1').option('val1.val2', 0).value.reset()
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
mix.option('val1.val2', 0).value.set('val2') mix.option('val1.val2', 0).value.set('val2')
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
mix.option('val1.val1').value.set(['val'])
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
mix.config('cfg1').option('val1.val3', 0).value.set('val6') mix.config('cfg1').option('val1.val3', 0).value.set('val6')
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val6'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val'], 'val1.val3': ['val6'], 'val': 'val'}
#
mix.option('val1.val2', 0).value.reset() mix.option('val1.val2', 0).value.reset()
mix.config('cfg1').option('val1.val3', 0).value.reset() mix.config('cfg1').option('val1.val3', 0).value.reset()
mix.config('cfg1').option('val1.val1').value.set(['val3']) mix.config('cfg1').option('val1.val1').value.set(['val3'])
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val3'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val3'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'}
#
mix.config('cfg1').option('val1.val1').value.reset() mix.config('cfg1').option('val1.val1').value.reset()
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val'], 'val1.val1': ['val'], 'val1.val3': ['val'], 'val': 'val'}
#
mix.option('val1.val1').value.set(['val3']) mix.option('val1.val1').value.set(['val3'])
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val3'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val3'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'}
#
mix.config('cfg1').option('val1.val2', 0).value.set('val2') mix.config('cfg1').option('val1.val2', 0).value.set('val2')
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val3'], 'val1.val3': ['val3'], 'val': 'val'}
#
mix.option('val1.val1').value.set(['val3', 'rah']) mix.option('val1.val1').value.set(['val3', 'rah'])
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2', 'rah'], 'val1.val1': ['val3', 'rah'], 'val1.val3': ['val3', 'rah'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2', 'rah'], 'val1.val1': ['val3', 'rah'], 'val1.val3': ['val3', 'rah'], 'val': 'val'}
#
mix.option('val1.val1').value.pop(1) mix.option('val1.val1').value.pop(1)
mix.option('val1.val1').value.set(['val4']) mix.option('val1.val1').value.set(['val4'])
assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val4'], 'val1.val3': ['val4'], 'val': 'val'} assert mix.config('cfg1').value.dict() == {'val1.val2': ['val2'], 'val1.val1': ['val4'], 'val1.val3': ['val4'], 'val': 'val'}
@ -732,8 +751,13 @@ def test_mix_properties_mix_copy():
mix.property.read_write() mix.property.read_write()
conf3 = mix.config('conf1').config.copy(session_id='conf3') conf3 = mix.config('conf1').config.copy(session_id='conf3')
# old fashion
mix2 = conf3.config.metaconfig() mix2 = conf3.config.metaconfig()
assert mix.config.name() == mix2.config.name() assert mix.config.name() == mix2.config.name()
# new method
mix2 = list(conf3.config.parents())
assert len(mix2) == 1
assert mix.config.name() == mix2[0].config.name()
assert mix.config('conf1').value.dict() == {'ip_admin_eth0': ['192.168.1.1']} assert mix.config('conf1').value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
assert mix.config('conf2').value.dict() == {'ip_admin_eth0': ['192.168.1.1']} assert mix.config('conf2').value.dict() == {'ip_admin_eth0': ['192.168.1.1']}
@ -1121,7 +1145,8 @@ def test_mix_add_config_readd():
# #
config = Config(od, session_id='new') config = Config(od, session_id='new')
mix.config.add(config) mix.config.add(config)
raises(ConflictError, "mix2.config.add(config)") mix2.config.add(config)
assert len(list(config.config.parents())) == 2
def test_meta_new_mixconfig(): def test_meta_new_mixconfig():

View File

@ -780,7 +780,7 @@ class _TiramisuOptionDescription(_TiramisuOption, TiramisuConfig):
option_bag = OptionBag() option_bag = OptionBag()
option_bag.set_option(opt, option_bag.set_option(opt,
opt.impl_getpath(), opt.impl_getpath(),
None, None,
config_bag) config_bag)
if opt.impl_is_optiondescription(): if opt.impl_is_optiondescription():
config_bag.context.cfgimpl_get_settings().validate_properties(option_bag) config_bag.context.cfgimpl_get_settings().validate_properties(option_bag)
@ -1339,6 +1339,7 @@ class _TiramisuContextConfig(TiramisuConfig, _TiramisuContextConfigReset):
session_id=None, session_id=None,
persistent=False, persistent=False,
storage=None): storage=None):
"""Copy current config"""
return self._return_config(self._config_bag.context.duplicate(session_id, return self._return_config(self._config_bag.context.duplicate(session_id,
persistent=persistent, persistent=persistent,
storage=storage)) storage=storage))
@ -1348,6 +1349,7 @@ class _TiramisuContextConfig(TiramisuConfig, _TiramisuContextConfigReset):
persistent=False, persistent=False,
storage=None, storage=None,
metaconfig_prefix=None): metaconfig_prefix=None):
"""Copy current config with all parents"""
return self._return_config(self._config_bag.context.duplicate(session_id, return self._return_config(self._config_bag.context.duplicate(session_id,
persistent=persistent, persistent=persistent,
storage=storage, storage=storage,
@ -1355,9 +1357,19 @@ class _TiramisuContextConfig(TiramisuConfig, _TiramisuContextConfigReset):
deep=True)) deep=True))
def metaconfig(self): def metaconfig(self):
return self._return_config(self._config_bag.context.cfgimpl_get_meta()) """Get first meta configuration (obsolete please use parents)"""
try:
return next(self.parents())
except StopIteration:
return None
def parents(self):
"""Get all parents of current config"""
for parent in self._config_bag.context.get_parents():
yield self._return_config(parent)
def path(self): def path(self):
"""Get path from config (all parents name)"""
return self._config_bag.context.cfgimpl_get_config_path() return self._config_bag.context.cfgimpl_get_config_path()

View File

@ -599,7 +599,7 @@ class _CommonConfig(SubConfig):
'_impl_settings', '_impl_settings',
'_impl_properties_cache', '_impl_properties_cache',
'_impl_permissives_cache', '_impl_permissives_cache',
'_impl_meta', 'parents',
'impl_type') 'impl_type')
def _impl_build_all_caches(self): def _impl_build_all_caches(self):
@ -609,9 +609,9 @@ class _CommonConfig(SubConfig):
config_bag = ConfigBag(context=self) config_bag = ConfigBag(context=self)
descr.impl_build_force_store_values(config_bag) descr.impl_build_force_store_values(config_bag)
def cfgimpl_get_meta(self): def get_parents(self):
if self._impl_meta is not None: for parent in self.parents:
return self._impl_meta() yield parent()
# information # information
def impl_set_information(self, key, value): def impl_set_information(self, key, value):
@ -689,19 +689,27 @@ class _CommonConfig(SubConfig):
duplicated_config.cfgimpl_reset_cache(None, None) duplicated_config.cfgimpl_reset_cache(None, None)
if child is not None: if child is not None:
duplicated_config._impl_children.append(child) duplicated_config._impl_children.append(child)
child._impl_meta = weakref.ref(duplicated_config) child.parents.append(weakref.ref(duplicated_config))
if self._impl_meta: if self.parents:
if deep: if deep:
duplicated_config = self._impl_meta().duplicate(deep=deep, for parent in self.parents:
storage=storage, duplicated_config = parent().duplicate(deep=deep,
metaconfig_prefix=metaconfig_prefix, storage=storage,
child=duplicated_config, metaconfig_prefix=metaconfig_prefix,
persistent=persistent) child=duplicated_config,
persistent=persistent)
else: else:
duplicated_config._impl_meta = self._impl_meta duplicated_config.parents = self.parents
self._impl_meta()._impl_children.append(duplicated_config) for parent in self.parents:
parent()._impl_children.append(duplicated_config)
return duplicated_config return duplicated_config
def cfgimpl_get_config_path(self):
path = self._impl_name
for parent in self.parents:
path = parent().cfgimpl_get_config_path() + '.' + path
return path
# ____________________________________________________________ # ____________________________________________________________
class KernelConfig(_CommonConfig): class KernelConfig(_CommonConfig):
@ -733,7 +741,7 @@ class KernelConfig(_CommonConfig):
:param persistent: if persistent, don't delete storage when leaving :param persistent: if persistent, don't delete storage when leaving
:type persistent: `boolean` :type persistent: `boolean`
""" """
self._impl_meta = None self.parents = []
self._impl_symlink = [] self._impl_symlink = []
self._display_name = display_name self._display_name = display_name
if isinstance(descr, Leadership): if isinstance(descr, Leadership):
@ -774,11 +782,6 @@ class KernelConfig(_CommonConfig):
def impl_getname(self): def impl_getname(self):
return self._impl_name return self._impl_name
def cfgimpl_get_config_path(self):
if self._impl_meta is None or self._impl_meta() is None:
return self._impl_name
return self._impl_meta().cfgimpl_get_config_path() + '.' + self._impl_name
class KernelGroupConfig(_CommonConfig): class KernelGroupConfig(_CommonConfig):
__slots__ = ('__weakref__', __slots__ = ('__weakref__',
@ -804,7 +807,7 @@ class KernelGroupConfig(_CommonConfig):
raise ConflictError(_('config name must be uniq in ' raise ConflictError(_('config name must be uniq in '
'groupconfig for "{0}"').format(name)) 'groupconfig for "{0}"').format(name))
self._impl_children = children self._impl_children = children
self._impl_meta = None self.parents = []
session_id = gen_storage_id(session_id, self) session_id = gen_storage_id(session_id, self)
assert valid_name(session_id), _("invalid session ID: {0} for config").format(session_id) assert valid_name(session_id), _("invalid session ID: {0} for config").format(session_id)
super().__init__(_descr, super().__init__(_descr,
@ -973,11 +976,6 @@ class KernelGroupConfig(_CommonConfig):
return child return child
raise ConfigError(_('unknown config "{}"').format(name)) raise ConfigError(_('unknown config "{}"').format(name))
def cfgimpl_get_config_path(self):
if self._impl_meta is None or self._impl_meta() is None:
return self._impl_name
return self._impl_meta().cfgimpl_get_config_path() + '.' + self._impl_name
class KernelMixConfig(KernelGroupConfig): class KernelMixConfig(KernelGroupConfig):
__slots__ = ('_display_name', __slots__ = ('_display_name',
@ -998,9 +996,7 @@ class KernelMixConfig(KernelGroupConfig):
for child in children: for child in children:
if not isinstance(child, (KernelConfig, KernelMixConfig)): if not isinstance(child, (KernelConfig, KernelMixConfig)):
raise TypeError(_("child must be a Config, MixConfig or MetaConfig")) raise TypeError(_("child must be a Config, MixConfig or MetaConfig"))
if child.cfgimpl_get_meta() is not None: child.parents.append(weakref.ref(self))
raise ValueError(_("child has already a {}config's").format(self.impl_type))
child._impl_meta = weakref.ref(self)
properties, permissives, values, session_id = get_storages(self, properties, permissives, values, session_id = get_storages(self,
session_id, session_id,
persistent, persistent,
@ -1183,13 +1179,11 @@ class KernelMixConfig(KernelGroupConfig):
def add_config(self, def add_config(self,
apiconfig): apiconfig):
config = apiconfig._config_bag.context config = apiconfig._config_bag.context
if config._impl_meta is not None:
raise ConflictError(_('config is already in a metaconfig'))
if config.impl_getname() in [child.impl_getname() for child in self._impl_children]: if config.impl_getname() in [child.impl_getname() for child in self._impl_children]:
raise ConflictError(_('config name must be uniq in ' raise ConflictError(_('config name must be uniq in '
'groupconfig for {0}').format(config.impl_getname())) 'groupconfig for {0}').format(config.impl_getname()))
config._impl_meta = weakref.ref(self) config.parents.append(weakref.ref(self))
self._impl_children.append(config) self._impl_children.append(config)
config.cfgimpl_reset_cache(None, None) config.cfgimpl_reset_cache(None, None)
@ -1199,12 +1193,21 @@ class KernelMixConfig(KernelGroupConfig):
if session_id is not None: if session_id is not None:
for idx, child in enumerate(self._impl_children): for idx, child in enumerate(self._impl_children):
if session_id == child.impl_getname(): if session_id == child.impl_getname():
child._impl_meta = None
child.cfgimpl_reset_cache(None, None) child.cfgimpl_reset_cache(None, None)
return self._impl_children.pop(idx) self._impl_children.pop(idx)
raise ConfigError(_('cannot find the config {}').format(session_id)) break
else:
raise ConfigError(_('cannot find the config {}').format(session_id))
if config is not None: if config is not None:
self._impl_children.pop(config) self._impl_children.remove(config)
child = config
for index, parent in enumerate(child.parents):
if parent() == self:
child.parents.pop(index)
break
else:
raise ConfigError(_('cannot find the config {}').format(self.session_id))
return child
class KernelMetaConfig(KernelMixConfig): class KernelMetaConfig(KernelMixConfig):
@ -1285,7 +1288,7 @@ class KernelMetaConfig(KernelMixConfig):
config.cfgimpl_get_settings().rw_remove = self.cfgimpl_get_settings().rw_remove config.cfgimpl_get_settings().rw_remove = self.cfgimpl_get_settings().rw_remove
config.cfgimpl_get_settings().default_properties = self.cfgimpl_get_settings().default_properties config.cfgimpl_get_settings().default_properties = self.cfgimpl_get_settings().default_properties
config._impl_meta = weakref.ref(self) config.parents.append(weakref.ref(self))
self._impl_children.append(config) self._impl_children.append(config)
return config return config

View File

@ -140,13 +140,13 @@ class Values(object):
def getdefaultvalue(self, def getdefaultvalue(self,
option_bag): option_bag):
"""get default value: """get default value:
- get meta config value or - get parents config value or
- get calculated value or - get calculated value or
- get default value - get default value
""" """
moption_bag = self._get_meta(option_bag) moption_bag = self._get_modified_parent(option_bag)
if moption_bag: if moption_bag is not None:
# retrieved value from meta config # retrieved value from parent config
return moption_bag.config_bag.context.cfgimpl_get_values().get_cached_value(moption_bag) return moption_bag.config_bag.context.cfgimpl_get_values().get_cached_value(moption_bag)
if option_bag.option.impl_has_callback(): if option_bag.option.impl_has_callback():
@ -328,38 +328,56 @@ class Values(object):
option_bag.index, option_bag.index,
commit) commit)
def _get_meta(self, def _get_modified_parent(self,
option_bag): option_bag):
context = option_bag.config_bag.context """ Search in differents parents a Config with a modified value and return it
meta = context.cfgimpl_get_meta() If not found, return None
if meta is None: For follower option, return the Config where leader is modified
return None """
def build_option_bag(option_bag, parent):
doption_bag = option_bag.copy()
config_bag = option_bag.config_bag.copy()
config_bag.context = parent
config_bag.unrestraint()
doption_bag.config_bag = config_bag
return doption_bag
if option_bag.option.impl_is_follower(): if option_bag.option.impl_is_follower():
leader = option_bag.option.impl_get_leadership().get_leader() leader = option_bag.option.impl_get_leadership().get_leader()
leaderpath = leader.impl_getpath() leaderpath = leader.impl_getpath()
# follower could be a "meta" only if leader hasn't value
if self._p_.hasvalue(leaderpath, if self._p_.hasvalue(leaderpath,
index=None): index=None):
return None return None
doption_bag = option_bag.copy() config_bag = option_bag.config_bag
config_bag = option_bag.config_bag.copy() leader_option_bag = OptionBag()
config_bag.context = meta leader_option_bag.set_option(leader,
doption_bag.config_bag = config_bag leaderpath,
if 'force_metaconfig_on_freeze' in option_bag.properties: None,
# remove force_metaconfig_on_freeze only if option in metaconfig config_bag)
# hasn't force_metaconfig_on_freeze properties leader_option_bag = self._get_modified_parent(leader_option_bag)
ori_properties = doption_bag.properties if leader_option_bag is None:
del doption_bag.properties return None
if not self.force_to_metaconfig(doption_bag): new_config_bag = leader_option_bag.config_bag
doption_bag.properties = ori_properties - {'force_metaconfig_on_freeze'} if not new_config_bag.context.cfgimpl_get_values()._p_.hasvalue(option_bag.path,
else: index=option_bag.index):
doption_bag.properties = ori_properties return None
config_bag.unrestraint() return build_option_bag(option_bag, new_config_bag.context)
meta_option_bag = meta.cfgimpl_get_values().getowner(doption_bag, for parent in option_bag.config_bag.context.get_parents():
only_default=True) doption_bag = build_option_bag(option_bag, parent)
if meta_option_bag == owners.default: if 'force_metaconfig_on_freeze' in option_bag.properties:
return None # remove force_metaconfig_on_freeze only if option in metaconfig
return meta_option_bag # hasn't force_metaconfig_on_freeze properties
ori_properties = doption_bag.properties
del doption_bag.properties
if not self.force_to_metaconfig(doption_bag):
doption_bag.properties = ori_properties - {'force_metaconfig_on_freeze'}
else:
doption_bag.properties = ori_properties
parent_owner = parent.cfgimpl_get_values().getowner(doption_bag,
only_default=True)
if parent_owner != owners.default:
return doption_bag
return None
#______________________________________________________________________ #______________________________________________________________________
@ -408,8 +426,8 @@ class Values(object):
index=option_bag.index) index=option_bag.index)
if validate_meta is not False and (owner is owners.default or \ if validate_meta is not False and (owner is owners.default or \
'frozen' in option_bag.properties and 'force_metaconfig_on_freeze' in option_bag.properties): 'frozen' in option_bag.properties and 'force_metaconfig_on_freeze' in option_bag.properties):
moption_bag = self._get_meta(option_bag) moption_bag = self._get_modified_parent(option_bag)
if moption_bag: if moption_bag is not None:
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: elif 'force_metaconfig_on_freeze' in option_bag.properties: