From 6b473e63f2c0094aa3bd6ce24b1da7ca6608c878 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 19 Nov 2023 13:40:55 +0100 Subject: [PATCH] feat: dynamic family can have sub family --- tests/test_dyn_optiondescription.py | 48 ++++++++++-- tiramisu/autolib.py | 27 +++---- tiramisu/config.py | 29 +++---- tiramisu/option/dynoptiondescription.py | 88 ++++++++++++++++------ tiramisu/option/optiondescription.py | 6 -- tiramisu/option/syndynoption.py | 1 - tiramisu/option/syndynoptiondescription.py | 17 +++-- tiramisu/value.py | 40 +++++----- 8 files changed, 153 insertions(+), 103 deletions(-) diff --git a/tests/test_dyn_optiondescription.py b/tests/test_dyn_optiondescription.py index 5c1f29c..b8c67f6 100644 --- a/tests/test_dyn_optiondescription.py +++ b/tests/test_dyn_optiondescription.py @@ -1434,7 +1434,7 @@ def test_leadership_dyndescription_param_follower(): # assert not list_sessions() -def test_leadership_default_multi_dyndescription(): +def test_leadership_default_multi_dyndescription_sub(): st1 = StrOption('st1', "", multi=True) st2 = StrOption('st2', "", multi=True, default_multi='no') stm = Leadership('st1', '', [st1, st2]) @@ -1628,12 +1628,46 @@ def test_invalid_conflict_dyndescription(): # assert not list_sessions() -def test_invalid_subod_dyndescription(): - st2 = StrOption('st2', '') - od1 = OptionDescription('od1', '', [st2]) - od1 - with pytest.raises(ConfigError): - DynOptionDescription('dod', '', [od1], suffixes=Calculation(return_list)) +#def test_invalid_subod_dyndescription(): +# st2 = StrOption('st2', '') +# od1 = OptionDescription('od1', '', [st2]) +# od1 +# with pytest.raises(ConfigError): +# DynOptionDescription('dod', '', [od1], suffixes=Calculation(return_list)) +## assert not list_sessions() +# +# +def test_leadership_default_multi_dyndescription(): + st1 = StrOption('st1', "", multi=True) + st2 = StrOption('st2', "", multi=True, default_multi='no') + stm = Leadership('st1', '', [st1, st2]) + od1 = OptionDescription('od1', '', [stm]) + st = DynOptionDescription('st', '', [od1], suffixes=Calculation(return_list)) + od = OptionDescription('od', '', [st]) + od1 = OptionDescription('od', '', [od]) + cfg = Config(od1) + owner = cfg.owner.get() + # + assert cfg.value.get() == {'od.stval1.od1.st1.st1': [], + 'od.stval2.od1.st1.st1': [], + } + assert cfg.option('od.stval1.od1.st1.st1').value.get() == [] + assert cfg.option('od.stval2.od1.st1.st1').value.get() == [] + assert cfg.option('od.stval1.od1.st1.st1').owner.isdefault() + assert cfg.option('od.stval2.od1.st1.st1').owner.isdefault() + # + cfg.option('od.stval1.od1.st1.st1').value.set(['yes']) + assert cfg.option('od.stval1.od1.st1.st1').value.get() == ['yes'] + assert cfg.option('od.stval1.od1.st1.st2', 0).value.get() == 'no' + assert cfg.option('od.stval2.od1.st1.st1').value.get() == [] + assert cfg.option('od.stval1.od1.st1.st1').owner.get() == owner + assert cfg.option('od.stval1.od1.st1.st2', 0).owner.isdefault() + assert cfg.option('od.stval2.od1.st1.st1').owner.isdefault() + assert cfg.value.get() == {'od.stval1.od1.st1.st1': [], + 'od.stval1.od1.st1.st1': [{'od.stval1.od1.st1.st1': 'yes', + 'od.stval1.od1.st1.st2': 'no'}], + 'od.stval2.od1.st1.st1': [], + } # assert not list_sessions() diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 91e9ece..41ae217 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -430,13 +430,11 @@ def manager_callback(callback: Callable, if callbk_option.issubdyn(): found = False if isinstance(param, ParamDynOption): - subdyn = param.dynoptiondescription - rootpath = subdyn.impl_getpath() + param.suffix - suffix = param.suffix - callbk_option = callbk_option.to_dynoption(rootpath, - suffix, - subdyn, - ) + soption_bag = param.dynoptiondescription.get_sub_child(callbk_option, + param.suffix, + config_bag, + ) + callbk_option = soption_bag.option found = True elif option.impl_is_dynsymlinkoption(): rootpath = option.rootpath @@ -454,16 +452,11 @@ def manager_callback(callback: Callable, found = True if not found: callbk_options = [] - dynopt = callbk_option.getsubdyn() - rootpath = dynopt.impl_getpath() - subpaths = [rootpath] + callbk_option.impl_getpath()[len(rootpath) + 1:].split('.')[:-1] - for suffix in dynopt.get_suffixes(config_bag): - path_suffix = dynopt.convert_suffix_to_path(suffix) - subpath = '.'.join([subp + path_suffix for subp in subpaths]) - doption = callbk_option.to_dynoption(subpath, - suffix, - dynopt) - callbk_options.append(doption) + for doption_bag in callbk_option.getsubdyn().get_sub_children(callbk_option, + config_bag, + None, + ): + callbk_options.append(doption_bag.option) if callbk_options is None: callbk_options = [callbk_option] values = None diff --git a/tiramisu/config.py b/tiramisu/config.py index 318a715..aa3dd47 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -161,26 +161,15 @@ class _SubConfig: resetted_opts, ): # reset cache for all chidren - path = option_bag.option.impl_getpath() - if '.' in path: - subpath = path.rsplit('.', 1)[0] - else: - subpath = '' - - for suffix in option_bag.option.get_suffixes(option_bag.config_bag): - doption = option_bag.option.to_dynoption(subpath, - suffix, - option_bag.option, - ) - doption_bag = OptionBag(doption, - option_bag.index, - option_bag.config_bag, - properties=None, - ) - for coption in doption.get_children_recursively(None, - None, - option_bag.config_bag, - ): + for doption_bag in option_bag.option.get_sub_children(option_bag.option, + option_bag.config_bag, + option_bag.index, + properties=None, + ): + for coption in doption_bag.option.get_children_recursively(None, + None, + option_bag.config_bag, + ): coption_bag = self.get_sub_option_bag(doption_bag, # pylint: disable=no-member coption.impl_getpath(), None, diff --git a/tiramisu/option/dynoptiondescription.py b/tiramisu/option/dynoptiondescription.py index 86c7a34..07aa683 100644 --- a/tiramisu/option/dynoptiondescription.py +++ b/tiramisu/option/dynoptiondescription.py @@ -22,7 +22,7 @@ """ import re import weakref -from typing import List, Any +from typing import List, Any, Optional from itertools import chain from ..autolib import ParamOption @@ -125,24 +125,70 @@ class DynOptionDescription(OptionDescription): index=None, properties=undefined, ): - if option == self: - rootpath = self.impl_getpath().rsplit('.', 1)[0] - else: - rootpath = self.impl_getpath() - subpath = option.impl_getpath()[len(rootpath):].rsplit('.', 1)[0] + rootpath = self.get_root_path(option) + subpath = self.get_sub_path(option) for suffix in self.get_suffixes(config_bag): - path_suffix = self.convert_suffix_to_path(suffix) - if option == self: - parent_path = rootpath - else: - parent_path = rootpath + path_suffix + subpath - doption_bag = OptionBag(option.to_dynoption(parent_path, - suffix, - self, - ), - index, - config_bag, - properties=properties, - ori_option=option - ) - yield doption_bag + yield self.get_sub_child(option, + suffix, + config_bag, + index=index, + rootpath=rootpath, + subpath=subpath, + properties=properties, + ) + + def get_root_path(self, option): + path = self.impl_getpath() + rootpath = path.rsplit('.', 1)[0] if '.' in path else '' + if option != self: + if rootpath: + rootpath += '.' + return rootpath + + def get_sub_path(self, option): + if option != self: + return option.impl_getpath()[len(self.impl_getpath()):].rsplit('.', 1)[0] + return None + + def get_sub_child(self, + option, + suffix, + config_bag, + *, + index: Optional[int]=None, + rootpath: Optional[str]=None, + subpath: Optional[str]=None, + properties: Optional[str]=None, + ): + if rootpath is None: + rootpath = self.get_root_path(option) + if option == self: + parent_path = rootpath + else: + if subpath is None: + subpath = self.get_sub_path(option) + parent_path = rootpath + self.impl_getname(suffix) + subpath + return OptionBag(option.to_dynoption(parent_path, + suffix, + self, + ), + index, + config_bag, + properties=properties, + ori_option=option + ) + + def _setsubdyn(self, + subdyn, + ) -> None: + raise ConfigError(_('cannot set a dynoptiondescription in a ' + 'dynoptiondescription')) + + def impl_getname(self, suffix=None) -> str: + """get name + """ + name = super().impl_getname() + if suffix is None: + return name + path_suffix = self.convert_suffix_to_path(suffix) + return name + path_suffix diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index 72c5e50..e042db9 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -320,12 +320,6 @@ class OptionDescription(OptionDescriptionWalk): # the group_type is useful for filtering OptionDescriptions in a config self._group_type = groups.default - def _setsubdyn(self, - subdyn, - ) -> None: - raise ConfigError(_('cannot set optiondescription in a ' - 'dynoptiondescription')) - def impl_is_optiondescription(self) -> bool: """the option is an option description """ diff --git a/tiramisu/option/syndynoption.py b/tiramisu/option/syndynoption.py index 71a72aa..a61b530 100644 --- a/tiramisu/option/syndynoption.py +++ b/tiramisu/option/syndynoption.py @@ -54,7 +54,6 @@ class SynDynOption: """get option name """ return self.opt.impl_getname() - #return self.opt.impl_getname() + self.dyn_parent.convert_suffix_to_path(self.suffix) def impl_get_display_name(self) -> str: """get option display name diff --git a/tiramisu/option/syndynoptiondescription.py b/tiramisu/option/syndynoptiondescription.py index 242f8ee..4e98a2e 100644 --- a/tiramisu/option/syndynoptiondescription.py +++ b/tiramisu/option/syndynoptiondescription.py @@ -81,9 +81,15 @@ class SynDynOptionDescription: def impl_getname(self) -> str: """get name """ - if isinstance(self, SynDynLeadership): - return self.opt.impl_getname() - return self.opt.impl_getname() + self.ori_dyn.convert_suffix_to_path(self._suffix) + if self.opt.impl_is_dynoptiondescription(): + return self.opt.impl_getname(self._suffix) + return self.opt.impl_getname() + + def impl_get_display_name(self) -> str: + """get display name + """ + if self.opt.impl_is_dynoptiondescription(): + return self.opt.impl_get_display_name() + str(self._suffix) def get_children(self, config_bag: ConfigBag, @@ -130,11 +136,6 @@ class SynDynOptionDescription: path = f'{self.rootpath}.{path}' return path - def impl_get_display_name(self) -> str: - """get display name - """ - return self.opt.impl_get_display_name() + str(self._suffix) - class SynDynLeadership(SynDynOptionDescription): """SynDynLeadership internal option, it's an instanciate synoptiondescription diff --git a/tiramisu/value.py b/tiramisu/value.py index 7f337c3..ee2682d 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -359,29 +359,23 @@ class Values: force_store_options.append(coption) if not force_store_options: continue - rootpath = option.impl_getpath() - for suffix in option.get_suffixes(option_bag.config_bag): - for coption in force_store_options: - parent_subpath = rootpath + suffix + coption.impl_getpath()[len(rootpath):].rsplit('.', 1)[0] - doption = coption.to_dynoption(parent_subpath, - suffix, - option, - ) - if coption.impl_is_follower(): - leader = coption.impl_get_leadership().get_leader() - loption_bag = OptionBag(leader, - None, - option_bag.config_bag, - properties=frozenset(), - ) - indexes = range(len(self.get_value(loption_bag)[0])) - else: - indexes = [None] - for index in indexes: - coption_bag = OptionBag(doption, - index, - option_bag.config_bag, - ) + for coption in force_store_options: + if coption.impl_is_follower(): + leader = coption.impl_get_leadership().get_leader() + loption_bag = OptionBag(leader, + None, + option_bag.config_bag, + properties=frozenset(), + ) + indexes = range(len(self.get_value(loption_bag)[0])) + else: + indexes = [None] + for index in indexes: + for coption_bag in option.get_sub_children(coption, + option_bag.config_bag, + index, + properties=frozenset(), + ): default_value = [self.get_value(coption_bag)[0], owners.forced] self._values.setdefault(coption_bag.path, {})[index] = default_value