From 90564d4983e3efe58de9bc56d1eb4bbb7a5724e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 4 Dec 2023 17:46:46 +0100 Subject: [PATCH] dynoptiondescription inside dynoptiondescription --- tests/test_dyn_optiondescription.py | 232 ++++- tiramisu/api.py | 6 +- tiramisu/autolib.py | 71 +- tiramisu/config.py | 99 ++- tiramisu/locale/fr/LC_MESSAGES/tiramisu.po | 2 +- tiramisu/locale/tiramisu.pot | 979 +++++++-------------- tiramisu/option/baseoption.py | 27 +- tiramisu/option/dynoptiondescription.py | 129 +-- tiramisu/option/leadership.py | 3 +- tiramisu/option/optiondescription.py | 75 +- tiramisu/option/syndynoption.py | 2 +- tiramisu/option/syndynoptiondescription.py | 134 ++- tiramisu/value.py | 2 +- 13 files changed, 937 insertions(+), 824 deletions(-) diff --git a/tests/test_dyn_optiondescription.py b/tests/test_dyn_optiondescription.py index b8c67f6..90bb790 100644 --- a/tests/test_dyn_optiondescription.py +++ b/tests/test_dyn_optiondescription.py @@ -38,8 +38,7 @@ def return_list2(suffix): def return_list(val=None, suffix=None): if val: return val - else: - return ['val1', 'val2'] + return ['val1', 'val2'] def return_list_dot(val=None, suffix=None): @@ -335,7 +334,7 @@ def test_callback_dyndescription_outside1(): lst = StrOption('lst', '', ['val1', 'val2'], multi=True) st = StrOption('st', '', Calculation(return_dynval)) dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list, Params(ParamOption(lst)))) - out = StrOption('out', '', Calculation(return_dynval, Params(ParamDynOption(st, 'val1', dod)))) + out = StrOption('out', '', Calculation(return_dynval, Params(ParamDynOption(st, 'dodval1.st', dod)))) od = OptionDescription('od', '', [dod, out]) od2 = OptionDescription('od', '', [od, lst]) cfg = Config(od2) @@ -1628,15 +1627,6 @@ 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)) -## assert not list_sessions() -# -# def test_leadership_default_multi_dyndescription(): st1 = StrOption('st1', "", multi=True) st2 = StrOption('st2', "", multi=True, default_multi='no') @@ -1671,15 +1661,223 @@ def test_leadership_default_multi_dyndescription(): # assert not list_sessions() -def test_invalid_subdynod_dyndescription(): +def test_subdynod_dyndescription_root(): st2 = StrOption('st2', '') - od1 = DynOptionDescription('od1', '', [st2], suffixes=Calculation(return_list)) - od1 - with pytest.raises(ConfigError): - DynOptionDescription('dod', '', [od1], suffixes=Calculation(return_list)) + dod1 = DynOptionDescription('dod1', '', [st2], suffixes=Calculation(return_list, Params(ParamValue(['a', 'b'])))) + dod = DynOptionDescription('dod', '', [dod1], suffixes=Calculation(return_list)) + st3 = StrOption('st3', '', Calculation(return_dynval, Params(ParamDynOption(st2, 'dodval1.dod1a.st2', dod)))) + # FIXME st4 = StrOption('st4', '', Calculation(return_dynval, Params(ParamOption(st2))), multi=True) + od1 = OptionDescription('od', '', [dod, st3]) #, st4]) + cfg = Config(od1) + assert cfg.value.get() == {'dodval1.dod1a.st2': None, + 'dodval1.dod1b.st2': None, + 'dodval2.dod1a.st2': None, + 'dodval2.dod1b.st2': None, + 'st3': None, + } + assert cfg.option('dodval1.dod1a.st2').owner.isdefault() + assert cfg.option('dodval1.dod1a.st2').value.get() is None + assert cfg.option('dodval1.dod1b.st2').value.get() is None + assert cfg.option('dodval2.dod1a.st2').value.get() is None + assert cfg.option('dodval2.dod1b.st2').value.get() is None + assert cfg.option('dodval2.dod1b.st2').value.get() is None + assert cfg.option('st3').value.get() is None + # + cfg.option('dodval1.dod1a.st2').value.set('val') + assert cfg.value.get() == {'dodval1.dod1a.st2': 'val', + 'dodval1.dod1b.st2': None, + 'dodval2.dod1a.st2': None, + 'dodval2.dod1b.st2': None, + 'st3': 'val', + } + assert not cfg.option('dodval1.dod1a.st2').owner.isdefault() + assert cfg.option('dodval1.dod1a.st2').value.get() == 'val' + assert cfg.option('dodval1.dod1b.st2').value.get() is None + assert cfg.option('dodval2.dod1a.st2').value.get() is None + assert cfg.option('dodval2.dod1b.st2').value.get() is None + assert cfg.option('st3').value.get() == 'val' + # + cfg.option('dodval2.dod1a.st2').value.reset() + assert cfg.value.get() == {'dodval1.dod1a.st2': 'val', + 'dodval1.dod1b.st2': None, + 'dodval2.dod1a.st2': None, + 'dodval2.dod1b.st2': None, + 'st3': 'val', + } + assert not cfg.option('dodval1.dod1a.st2').owner.isdefault() + assert cfg.option('dodval1.dod1a.st2').value.get() == 'val' + assert cfg.option('dodval1.dod1b.st2').value.get() is None + assert cfg.option('dodval2.dod1a.st2').value.get() is None + assert cfg.option('dodval2.dod1b.st2').value.get() is None + assert cfg.option('st3').value.get() == 'val' + # + cfg.option('dodval1.dod1a.st2').value.reset() + assert cfg.value.get() == {'dodval1.dod1a.st2': None, + 'dodval1.dod1b.st2': None, + 'dodval2.dod1a.st2': None, + 'dodval2.dod1b.st2': None, + 'st3': None, + } + assert cfg.option('dodval1.dod1a.st2').owner.isdefault() + assert cfg.option('dodval1.dod1a.st2').value.get() is None + assert cfg.option('dodval1.dod1b.st2').value.get() is None + assert cfg.option('dodval2.dod1a.st2').value.get() is None + assert cfg.option('dodval2.dod1b.st2').value.get() is None + assert cfg.option('st3').value.get() is None # assert not list_sessions() +def test_subdynod_dyndescription(): + st2 = StrOption('st2', '') + dod1 = DynOptionDescription('dod1', '', [st2], suffixes=Calculation(return_list, Params(ParamValue(['a', 'b'])))) + dod = DynOptionDescription('dod', '', [dod1], suffixes=Calculation(return_list)) + od1 = OptionDescription('od', '', [dod]) + st3 = StrOption('st3', '', Calculation(return_dynval, Params(ParamDynOption(st2, 'dodval1.dod1a.st2', dod)))) + od = OptionDescription('od', '', [od1, st3]) #, st4]) + cfg = Config(od) + assert cfg.value.get() == {'od.dodval1.dod1a.st2': None, + 'od.dodval1.dod1b.st2': None, + 'od.dodval2.dod1a.st2': None, + 'od.dodval2.dod1b.st2': None, + 'st3': None, + } + assert cfg.option('od.dodval1.dod1a.st2').owner.isdefault() + assert cfg.option('od.dodval1.dod1a.st2').value.get() is None + assert cfg.option('od.dodval1.dod1b.st2').value.get() is None + assert cfg.option('od.dodval2.dod1a.st2').value.get() is None + assert cfg.option('od.dodval2.dod1b.st2').value.get() is None + assert cfg.option('od.dodval2.dod1b.st2').value.get() is None + assert cfg.option('st3').value.get() is None + # + cfg.option('od.dodval1.dod1a.st2').value.set('val') + assert cfg.value.get() == {'od.dodval1.dod1a.st2': 'val', + 'od.dodval1.dod1b.st2': None, + 'od.dodval2.dod1a.st2': None, + 'od.dodval2.dod1b.st2': None, + 'st3': 'val', + } + assert not cfg.option('od.dodval1.dod1a.st2').owner.isdefault() + assert cfg.option('od.dodval1.dod1a.st2').value.get() == 'val' + assert cfg.option('od.dodval1.dod1b.st2').value.get() is None + assert cfg.option('od.dodval2.dod1a.st2').value.get() is None + assert cfg.option('od.dodval2.dod1b.st2').value.get() is None + assert cfg.option('st3').value.get() == 'val' + # + cfg.option('od.dodval2.dod1a.st2').value.reset() + assert cfg.value.get() == {'od.dodval1.dod1a.st2': 'val', + 'od.dodval1.dod1b.st2': None, + 'od.dodval2.dod1a.st2': None, + 'od.dodval2.dod1b.st2': None, + 'st3': 'val', + } + assert not cfg.option('od.dodval1.dod1a.st2').owner.isdefault() + assert cfg.option('od.dodval1.dod1a.st2').value.get() == 'val' + assert cfg.option('od.dodval1.dod1b.st2').value.get() is None + assert cfg.option('od.dodval2.dod1a.st2').value.get() is None + assert cfg.option('od.dodval2.dod1b.st2').value.get() is None + assert cfg.option('st3').value.get() == 'val' + # + cfg.option('od.dodval1.dod1a.st2').value.reset() + assert cfg.value.get() == {'od.dodval1.dod1a.st2': None, + 'od.dodval1.dod1b.st2': None, + 'od.dodval2.dod1a.st2': None, + 'od.dodval2.dod1b.st2': None, + 'st3': None, + } + assert cfg.option('od.dodval1.dod1a.st2').owner.isdefault() + assert cfg.option('od.dodval1.dod1a.st2').value.get() is None + assert cfg.option('od.dodval1.dod1b.st2').value.get() is None + assert cfg.option('od.dodval2.dod1a.st2').value.get() is None + assert cfg.option('od.dodval2.dod1b.st2').value.get() is None + assert cfg.option('st3').value.get() is None +# assert not list_sessions() + +#FIXME une option dans une dyn qui est utilisé pour calculé dans une subdyn DOIT être dans le meme répertoire pour le moment ! +def test_subdynod_dyndescription_2(): + st2 = StrOption('st2', '') + st1 = StrOption('st1', '', default=['a', 'b'], multi=True) + dod1 = DynOptionDescription('dod1', '', [st2], suffixes=Calculation(return_list, Params(ParamOption(st1)))) + dod = DynOptionDescription('dod', '', [dod1, st1], suffixes=Calculation(return_list)) + od1 = OptionDescription('od', '', [dod]) + st3 = StrOption('st3', '', Calculation(return_dynval, Params(ParamDynOption(st2, 'dodval1.dod1a.st2', dod)))) + od = OptionDescription('od', '', [od1, st3]) #, st4]) + cfg = Config(od) + assert cfg.value.get() == {'od.dodval1.dod1a.st2': None, + 'od.dodval1.dod1b.st2': None, + 'od.dodval1.st1': ['a', 'b'], + 'od.dodval2.dod1a.st2': None, + 'od.dodval2.dod1b.st2': None, + 'od.dodval2.st1': ['a', 'b'], + 'st3': None, + } + cfg.cache.reset() + assert cfg.option('od.dodval1.dod1a.st2').value.get() is None + assert cfg.option('od.dodval1.dod1b.st2').value.get() is None + assert cfg.option('od.dodval1.st1').value.get() == ['a', 'b'] + assert cfg.option('od.dodval2.dod1a.st2').value.get() is None + assert cfg.option('od.dodval2.dod1b.st2').value.get() is None + assert cfg.option('od.dodval2.st1').value.get() == ['a', 'b'] + assert cfg.option('st3').value.get() is None + # + cfg.option('od.dodval1.st1').value.set(['a']) + cfg.option('od.dodval2.st1').value.set(['b', 'c']) + assert cfg.value.get() == {'od.dodval1.st1': ['a'], + 'od.dodval1.dod1a.st2': None, + 'od.dodval2.st1': ['b', 'c'], + 'od.dodval2.dod1b.st2': None, + 'od.dodval2.dod1c.st2': None, + 'st3': None, + } + + +def test_subdynod_dyndescription_leadership(): + st1 = StrOption('st1', '', multi=True) + st2 = StrOption('st2', '', multi=True) + stm = Leadership('stm', '', [st1, st2]) + dod1 = DynOptionDescription('dod1', '', [stm], suffixes=Calculation(return_list, Params(ParamValue(['a', 'b'])))) + dod = DynOptionDescription('dod', '', [dod1], suffixes=Calculation(return_list)) + od1 = OptionDescription('od', '', [dod]) + st3 = StrOption('st3', '', Calculation(return_dynval, Params(ParamDynOption(st1, 'dodval1.dod1a.stm.st1', dod))), multi=True) + # FIXME st4 = StrOption('st4', '', Calculation(return_dynval, Params(ParamOption(st2))), multi=True) + st5 = StrOption('st5', '', Calculation(return_dynval, Params(ParamDynOption(st2, 'dodval1.dod1a.stm.st2', dod))), multi=True) + #cfg = Config(od1) + #FIXME + od = OptionDescription('od', '', [od1, st3 , st5]) #, st4]) + cfg = Config(od) + assert cfg.value.get() == {'od.dodval1.dod1a.stm.st1': [], + 'od.dodval1.dod1b.stm.st1': [], + 'od.dodval2.dod1a.stm.st1': [], + 'od.dodval2.dod1b.stm.st1': [], + 'st3': [], + 'st5': [], + } + assert cfg.option('od.dodval1.dod1a.stm.st1').owner.isdefault() + assert cfg.option('od.dodval1.dod1a.stm.st1').value.get() == [] + assert cfg.option('od.dodval1.dod1b.stm.st1').value.get() == [] + assert cfg.option('od.dodval2.dod1a.stm.st1').value.get() == [] + assert cfg.option('od.dodval2.dod1b.stm.st1').value.get() == [] + assert cfg.option('od.dodval2.dod1b.stm.st1').value.get() == [] + assert cfg.option('st3').value.get() == [] + assert cfg.option('st5').value.get() == [] + # + cfg.option('od.dodval1.dod1a.stm.st1').value.set(['val']) + assert cfg.option('st3').value.get() == ['val'] + assert cfg.value.get() == {'od.dodval1.dod1a.stm.st1': [{'od.dodval1.dod1a.stm.st1': 'val', + 'od.dodval1.dod1a.stm.st2': None}], + 'od.dodval1.dod1b.stm.st1': [], + 'od.dodval2.dod1a.stm.st1': [], + 'od.dodval2.dod1b.stm.st1': [], + 'st3': ['val'], + 'st5': [], + } + assert not cfg.option('od.dodval1.dod1a.stm.st1').owner.isdefault() + assert cfg.option('od.dodval1.dod1a.stm.st1').value.get() == ['val'] + assert cfg.option('od.dodval1.dod1b.stm.st1').value.get() == [] + assert cfg.option('od.dodval2.dod1a.stm.st1').value.get() == [] + assert cfg.option('od.dodval2.dod1b.stm.st1').value.get() == [] + # + + def test_invalid_symlink_dyndescription(): st = StrOption('st', '') st2 = SymLinkOption('st2', st) diff --git a/tiramisu/api.py b/tiramisu/api.py index 46bce7f..c085be1 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -270,7 +270,7 @@ class _TiramisuOptionOptionDescription: """Get dependencies from this option""" option_bag = options_bag[-1] options = [] - for option in option_bag.option._get_dependencies(self._config_bag.context): + for option in option_bag.option.get_dependencies(self._config_bag.context): options.append(TiramisuOption(option().impl_getpath(), None, self._config_bag, @@ -1039,8 +1039,8 @@ class TiramisuContextProperty(TiramisuConfig): return self._config_bag.properties def _set(self, - props, - ): + props, + ): """Personalise config properties""" if 'force_store_value' in props: force_store_value = 'force_store_value' not in self._config_bag.properties diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 41ae217..923d5a9 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -82,10 +82,10 @@ class ParamOption(Param): class ParamDynOption(ParamOption): - __slots__ = ('suffix',) + __slots__ = ('subpath',) def __init__(self, option: 'Option', - suffix: str, + subpath: str, dynoptiondescription: 'DynOptionDescription', notraisepropertyerror: bool=False, raisepropertyerror: bool=False, @@ -95,7 +95,7 @@ class ParamDynOption(ParamOption): notraisepropertyerror, raisepropertyerror, ) - self.suffix = suffix + self.subpath = subpath self.dynoptiondescription = dynoptiondescription self.optional = optional @@ -430,22 +430,65 @@ def manager_callback(callback: Callable, if callbk_option.issubdyn(): found = False if isinstance(param, ParamDynOption): - soption_bag = param.dynoptiondescription.get_sub_child(callbk_option, - param.suffix, - config_bag, - ) - callbk_option = soption_bag.option + od_path = param.dynoptiondescription.impl_getpath() + if "." in od_path: + rootpath = od_path.rsplit('.', 1)[0] + '.' + else: + rootpath = '' + full_path = rootpath + param.subpath + root_option_bag = OptionBag(config_bag.context.get_description(), + None, + config_bag, + ) + try: + soptions_bag = config_bag.context.get_sub_option_bag(root_option_bag, + full_path, + #FIXME index? + index=None, + validate_properties=True, + properties=None, + ) + except AttributeError as err: + raise ConfigError(_(f'option "{option.impl_get_display_name()}" is not in a dynoptiondescription: {err}')) + callbk_option = soptions_bag[-1].option found = True + elif option.impl_is_sub_dyn_optiondescription(): + if option.getsubdyn() == callbk_option.getsubdyn(): + root_path = option.impl_getpath().rsplit('.', 1)[0] + len_path = root_path.count('.') + full_path = root_path + '.' + callbk_option.impl_getpath().split('.', len_path + 1)[-1] + root_option_bag = OptionBag(config_bag.context.get_description(), + None, + config_bag, + ) + try: + soptions_bag = config_bag.context.get_sub_option_bag(root_option_bag, + full_path, + #FIXME index? + index=None, + validate_properties=True, + properties=None, + ) + except AttributeError as err: + raise ConfigError(_(f'option "{option.impl_get_display_name()}" is not in a dynoptiondescription: {err}')) + callbk_option = soptions_bag[-1].option + found = True elif option.impl_is_dynsymlinkoption(): rootpath = option.rootpath call_path = callbk_option.impl_getpath() - if call_path.startswith(option.opt.impl_getpath().rsplit('.', 1)[0]): + if option.opt.issubdyn() and callbk_option.getsubdyn() == option.getsubdyn() or \ + not option.opt.issubdyn() and callbk_option.getsubdyn() == option.opt: # in same dynoption - if len(callbk_option.impl_getpath().split('.')) == len(rootpath.split('.')): - rootpath = rootpath.rsplit('.', 1)[0] suffix = option.impl_getsuffix() subdyn = callbk_option.getsubdyn() - callbk_option = callbk_option.to_dynoption(rootpath, + root_path, sub_path = subdyn.split_path(subdyn, + option, + ) + if root_path: + parent_path = root_path + subdyn.impl_getname(suffix) + sub_path + else: + parent_path = subdyn.impl_getname(suffix) + sub_path + callbk_option = callbk_option.to_dynoption(parent_path, suffix, subdyn, ) @@ -454,7 +497,7 @@ def manager_callback(callback: Callable, callbk_options = [] for doption_bag in callbk_option.getsubdyn().get_sub_children(callbk_option, config_bag, - None, + index=None, ): callbk_options.append(doption_bag.option) if callbk_options is None: @@ -476,6 +519,8 @@ def manager_callback(callback: Callable, else: index_ = None with_index = False + if callbk_option.impl_getpath() == 'od.dodval1.st.boolean': + raise Exception('pfff') value = get_value(config_bag, callbk_option, param, diff --git a/tiramisu/config.py b/tiramisu/config.py index aa3dd47..e827fd2 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -27,6 +27,7 @@ from typing import Optional, List, Any, Union from .error import PropertiesOptionError, ConfigError, ConflictError, \ LeadershipError from .option import DynOptionDescription, Leadership, Option +from .option.syndynoptiondescription import SubDynOptionDescription from .setting import OptionBag, ConfigBag, Settings, undefined, groups from .value import Values, owners from .i18n import _ @@ -130,26 +131,74 @@ class _SubConfig: if option_bag.path in resetted_opts: return resetted_opts.append(option_bag.path) - for woption in option_bag.option._get_dependencies(option_bag.option): # pylint: disable=protected-access + for woption in option_bag.option.get_dependencies(option_bag.option): option = woption() - soption_bag = OptionBag(option, - option_bag.index, - option_bag.config_bag, - properties=None, - ) - if option.impl_is_dynoptiondescription(): - self._reset_cache_dyn_optiondescription(soption_bag, - resetted_opts, - ) - elif option.issubdyn(): - # it's an option in dynoptiondescription, remove cache for all generated option - self._reset_cache_dyn_option(soption_bag, - resetted_opts, - ) + if woption in option_bag.option._get_suffixes_dependencies() and \ + option_bag.option.issubdyn() and \ + option.impl_is_dynoptiondescription(): + paths = [subdyn().impl_getpath() for subdyn in option.get_sub_dyns()] + for weak_subdyn in option_bag.option.get_sub_dyns(): + subdyn = weak_subdyn() + if subdyn.impl_getpath() in paths: + root_path = option_bag.option.impl_getpath() + if '.' in root_path: + root_path = root_path.rsplit('.', 1)[0] + nb_elt = root_path.count('.') + 1 + else: + root_path = '' + nb_elt = 1 + config_bag = option_bag.config_bag + root_option_bag = OptionBag(config_bag.context.get_description(), + None, + config_bag, + ) + full_path = root_path + '.' + option.impl_getpath().split('.', nb_elt)[-1] + try: + options_bag = config_bag.context.get_sub_option_bag(root_option_bag, + full_path, + #FIXME index? + None, + validate_properties=False, + properties=None, + allow_dynoption=True, + ) + except AttributeError as err: + raise ConfigError(_(f'option "{option.impl_get_display_name()}" is not in a dynoptiondescription: {err}')) + self._reset_cache_dyn_optiondescription(options_bag[-1], + resetted_opts, + ) + break else: - self.reset_one_option_cache(resetted_opts, - soption_bag, - ) + soption_bag = OptionBag(option, + option_bag.index, + option_bag.config_bag, + properties=None, + ) + if option.impl_is_dynoptiondescription(): + self._reset_cache_dyn_optiondescription(soption_bag, + resetted_opts, + ) + elif option.issubdyn(): + # it's an option in dynoptiondescription, remove cache for all generated option + config_bag = option_bag.config_bag + options = [soption_bag] + for wdynopt in reversed(option.get_sub_dyns()): + dynopt = wdynopt() + sub_options = [] + for sub_option in options: + sub_options.extend(dynopt.get_sub_children(sub_option.option, + config_bag, + properties=None, + )) + options = sub_options + for doption_bag in options: + self.reset_one_option_cache(resetted_opts, + doption_bag, + ) + else: + self.reset_one_option_cache(resetted_opts, + soption_bag, + ) del option option_bag.option.reset_cache(option_bag.path, option_bag.config_bag, @@ -163,7 +212,7 @@ class _SubConfig: # reset cache for all chidren for doption_bag in option_bag.option.get_sub_children(option_bag.option, option_bag.config_bag, - option_bag.index, + index=option_bag.index, properties=None, ): for coption in doption_bag.option.get_children_recursively(None, @@ -187,13 +236,13 @@ class _SubConfig: resetted_opts, ): option = option_bag.option - if isinstance(option, DynOptionDescription): + if isinstance(option, (DynOptionDescription, SubDynOptionDescription)): dynoption = option else: dynoption = option.getsubdyn() for doption_bag in dynoption.get_sub_children(option, option_bag.config_bag, - option_bag.index, + index=option_bag.index, properties=None ): self.reset_one_option_cache(resetted_opts, @@ -467,7 +516,7 @@ class _SubConfig: dynopt = suboption.getsubdyn() return list(dynopt.get_sub_children(suboption, option_bag.config_bag, - option_bag.index, + index=option_bag.index, )) if suboption.impl_is_follower(): options_bag = self.get_sub_option_bag(option_bag.config_bag, # pylint: disable=no-member @@ -679,11 +728,14 @@ class _CommonConfig(_SubConfig): index: Optional[int], validate_properties: bool, leadership_length: int=None, + *, properties=undefined, follower_not_apply_requires: bool=False, + allow_dynoption: bool=False, ) -> List[OptionBag]: """Get the suboption for path and the name of the option - :returns: tuple (config, name)""" + :returns: option_bag + """ # pylint: disable=too-many-branches,too-many-locals,too-many-arguments if isinstance(bag, ConfigBag): option_bag = OptionBag(self.get_description(), @@ -710,6 +762,7 @@ class _CommonConfig(_SubConfig): option = suboption.get_child(step, option_bag.config_bag, subpath, + allow_dynoption=allow_dynoption, ) if idx == last_idx: option_index = index diff --git a/tiramisu/locale/fr/LC_MESSAGES/tiramisu.po b/tiramisu/locale/fr/LC_MESSAGES/tiramisu.po index 0a70b4d..3c8a926 100644 --- a/tiramisu/locale/fr/LC_MESSAGES/tiramisu.po +++ b/tiramisu/locale/fr/LC_MESSAGES/tiramisu.po @@ -551,7 +551,7 @@ msgstr "ne doit pas être une IP" #: tiramisu/option/domainnameoption.py:139 msgid "must have dot" -msgstr "doit avec un point" +msgstr "doit avoir un point" #: tiramisu/option/domainnameoption.py:141 msgid "invalid length (max 255)" diff --git a/tiramisu/locale/tiramisu.pot b/tiramisu/locale/tiramisu.pot index e32fb30..0f35fbe 100644 --- a/tiramisu/locale/tiramisu.pot +++ b/tiramisu/locale/tiramisu.pot @@ -5,225 +5,249 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2019-08-28 15:53+CEST\n" +"POT-Creation-Date: 2023-11-19 21:26+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: ENCODING\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/api.py:59 +#: tiramisu/api.py:61 msgid "Settings:" msgstr "" -#: tiramisu/api.py:60 +#: tiramisu/api.py:63 msgid "Access to option without verifying permissive properties" msgstr "" -#: tiramisu/api.py:61 +#: tiramisu/api.py:67 msgid "Access to option without property restriction" msgstr "" -#: tiramisu/api.py:65 +#: tiramisu/api.py:70 +msgid "Do not warnings during validation" +msgstr "" + +#: tiramisu/api.py:75 msgid "Call: {}" msgstr "" -#: tiramisu/api.py:67 +#: tiramisu/api.py:77 msgid "Commands:" msgstr "" -#: tiramisu/api.py:104 -msgid "index \"{}\" is greater than the leadership length \"{}\" for option \"{}\"" -msgstr "" - -#: tiramisu/api.py:109 -msgid "option must not be an optiondescription" -msgstr "" - -#: tiramisu/api.py:137 -msgid "index must be set with the follower option \"{}\"" -msgstr "" - -#: tiramisu/api.py:140 -msgid "unknown method {} in {}" -msgstr "" - -#: tiramisu/api.py:398 -msgid "cannot add this property: \"{0}\"" -msgstr "" - -#: tiramisu/api.py:554 tiramisu/config.py:239 -msgid "can't delete a SymLinkOption" -msgstr "" - -#: tiramisu/api.py:714 tiramisu/api.py:1515 -msgid "please specify a valid sub function ({})" -msgstr "" - -#: tiramisu/api.py:734 -msgid "unknown config type {}" -msgstr "" - -#: tiramisu/api.py:795 tiramisu/api.py:1271 +#: tiramisu/api.py:185 msgid "unknown list type {}" msgstr "" -#: tiramisu/api.py:797 tiramisu/api.py:1273 +#: tiramisu/api.py:187 msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/api.py:1096 -msgid "properties must be a frozenset" +#: tiramisu/api.py:352 +msgid "only multi value has defaultmulti" msgstr "" -#: tiramisu/api.py:1102 tiramisu/api.py:1124 -msgid "unknown when {} (must be in append or remove)" +#: tiramisu/api.py:371 tiramisu/option/intoption.py:31 +msgid "integer" msgstr "" -#: tiramisu/api.py:1114 tiramisu/api.py:1136 tiramisu/config.py:1269 -msgid "unknown type {}" +#: tiramisu/api.py:374 tiramisu/option/domainnameoption.py:43 +msgid "domain name" msgstr "" -#: tiramisu/api.py:1440 -msgid "cannot set session_id and config together" +#: tiramisu/api.py:376 +msgid "ip" msgstr "" -#: tiramisu/autolib.py:96 -msgid "unable to carry out a calculation for \"{}\", {}" +#: tiramisu/api.py:376 +msgid "netmask" msgstr "" -#: tiramisu/autolib.py:257 -msgid "the \"{}\" function with positional arguments \"{}\" and keyword arguments \"{}\" must not return a list (\"{}\") for the follower option \"{}\"" +#: tiramisu/api.py:376 +msgid "network" msgstr "" -#: tiramisu/autolib.py:266 -msgid "the \"{}\" function must not return a list (\"{}\") for the follower option \"{}\"" +#: tiramisu/api.py:471 +msgid "cannot add this property: \"{0}\"" msgstr "" -#: tiramisu/autolib.py:295 -msgid "unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and \"{4}\" for option \"{2}\"" -msgstr "" - -#: tiramisu/autolib.py:302 -msgid "unexpected error \"{0}\" in function \"{1}\" for option \"{2}\"" -msgstr "" - -#: tiramisu/config.py:69 -msgid "\"{0}\" must be an optiondescription, not an {1}" -msgstr "" - -#: tiramisu/config.py:200 -msgid "unknown option {}" -msgstr "" - -#: tiramisu/config.py:208 -msgid "there is no option description for this config (may be GroupConfig)" -msgstr "" - -#: tiramisu/config.py:224 -msgid "can't assign to a SymLinkOption" -msgstr "" - -#: tiramisu/config.py:228 +#: tiramisu/api.py:621 msgid "cannot reduce length of the leader \"{}\"" msgstr "" -#: tiramisu/config.py:308 -msgid "the follower option \"{}\" has greater length ({}) than the leader length ({})" +#: tiramisu/api.py:1088 +msgid "properties must be a frozenset" msgstr "" -#: tiramisu/config.py:405 +#: tiramisu/api.py:1094 tiramisu/api.py:1118 +msgid "unknown when {} (must be in append or remove)" +msgstr "" + +#: tiramisu/api.py:1106 tiramisu/api.py:1127 tiramisu/config.py:1210 +msgid "unknown type {}" +msgstr "" + +#: tiramisu/api.py:1432 +msgid "do not use unrestraint, nowarnings or forcepermissive together" +msgstr "" + +#: tiramisu/autolib.py:44 +msgid "args in params must be a tuple" +msgstr "" + +#: tiramisu/autolib.py:47 tiramisu/autolib.py:52 +msgid "arg in params must be a Param" +msgstr "" + +#: tiramisu/autolib.py:49 +msgid "kwargs in params must be a dict" +msgstr "" + +#: tiramisu/autolib.py:72 +msgid "paramoption needs an option not {}" +msgstr "" + +#: tiramisu/autolib.py:77 +msgid "param must have a boolean not a {} for notraisepropertyerror" +msgstr "" + +#: tiramisu/autolib.py:78 +msgid "param must have a boolean not a {} for raisepropertyerror" +msgstr "" + +#: tiramisu/autolib.py:133 +msgid "option in ParamInformation cannot be a symlinkoption" +msgstr "" + +#: tiramisu/autolib.py:135 +msgid "option in ParamInformation cannot be a follower" +msgstr "" + +#: tiramisu/autolib.py:137 +msgid "option in ParamInformation cannot be a dynamic option" +msgstr "" + +#: tiramisu/autolib.py:166 +msgid "first argument ({0}) must be a function" +msgstr "" + +#: tiramisu/autolib.py:168 +msgid "help_function ({0}) must be a function" +msgstr "" + +#: tiramisu/autolib.py:316 tiramisu/autolib.py:362 +msgid "unable to carry out a calculation for \"{}\", {}" +msgstr "" + +#: tiramisu/autolib.py:319 tiramisu/autolib.py:365 +msgid "the option \"{0}\" is used in a calculation but is invalid ({1})" +msgstr "" + +#: tiramisu/autolib.py:402 +msgid "option \"{}\" cannot be calculated: {}" +msgstr "" + +#: tiramisu/autolib.py:411 +msgid "option \"{}\" is not in a dynoptiondescription" +msgstr "" + +#: tiramisu/autolib.py:562 +msgid "the \"{}\" function with positional arguments \"{}\" and keyword arguments \"{}\" must not return a list (\"{}\") for the follower option \"{}\"" +msgstr "" + +#: tiramisu/autolib.py:571 +msgid "the \"{}\" function must not return a list (\"{}\") for the follower option \"{}\"" +msgstr "" + +#: tiramisu/autolib.py:604 +msgid "unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and \"{4}\" for option \"{2}\"" +msgstr "" + +#: tiramisu/autolib.py:611 +msgid "unexpected error \"{0}\" in function \"{1}\" for option \"{2}\"" +msgstr "" + +#: tiramisu/config.py:82 +msgid "there is no option description for this config (may be GroupConfig)" +msgstr "" + +#: tiramisu/config.py:269 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:455 -msgid "make_dict can't filtering with value without option" +#: tiramisu/config.py:532 tiramisu/option/optiondescription.py:72 +msgid "option description seems to be part of an other config" msgstr "" -#: tiramisu/config.py:508 -msgid "unexpected path \"{0}\", should start with \"{1}\"" -msgstr "" - -#: tiramisu/config.py:660 -msgid "cannot duplicate {}" -msgstr "" - -#: tiramisu/config.py:716 -msgid "parent of {} not already exists" -msgstr "" - -#: tiramisu/config.py:755 +#: tiramisu/config.py:790 msgid "cannot set leadership object has root optiondescription" msgstr "" -#: tiramisu/config.py:757 +#: tiramisu/config.py:792 msgid "cannot set dynoptiondescription object has root optiondescription" msgstr "" -#: tiramisu/config.py:774 tiramisu/config.py:819 -msgid "invalid session ID: {0} for config" -msgstr "" - -#: tiramisu/config.py:803 -msgid "groupconfig's children must be a list" -msgstr "" - -#: tiramisu/config.py:807 -msgid "groupconfig's children must be Config, MetaConfig or GroupConfig" -msgstr "" - -#: tiramisu/config.py:814 +#: tiramisu/config.py:840 msgid "config name must be uniq in groupconfig for \"{0}\"" msgstr "" -#: tiramisu/config.py:984 +#: tiramisu/config.py:1024 msgid "unknown config \"{}\"" msgstr "" -#: tiramisu/config.py:1005 +#: tiramisu/config.py:1047 msgid "child must be a Config, MixConfig or MetaConfig" msgstr "" -#: tiramisu/config.py:1037 +#: tiramisu/config.py:1079 msgid "force_default, force_default_if_same or force_dont_change_value cannot be set with only_config" msgstr "" -#: tiramisu/config.py:1059 +#: tiramisu/config.py:1085 msgid "force_default and force_dont_change_value cannot be set together" msgstr "" -#: tiramisu/config.py:1190 tiramisu/config.py:1267 +#: tiramisu/config.py:1208 msgid "config name must be uniq in groupconfig for {0}" msgstr "" -#: tiramisu/config.py:1207 tiramisu/config.py:1216 +#: tiramisu/config.py:1246 +msgid "config added has no name, the name is mandatory" +msgstr "" + +#: tiramisu/config.py:1248 +msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\"" +msgstr "" + +#: tiramisu/config.py:1270 msgid "cannot find the config {}" msgstr "" -#: tiramisu/config.py:1238 +#: tiramisu/config.py:1294 msgid "MetaConfig with optiondescription must have string has child, not {}" msgstr "" -#: tiramisu/config.py:1250 +#: tiramisu/config.py:1303 msgid "child must be a Config or MetaConfig" msgstr "" -#: tiramisu/config.py:1254 +#: tiramisu/config.py:1307 msgid "all config in metaconfig must have the same optiondescription" msgstr "" -#: tiramisu/config.py:1305 +#: tiramisu/config.py:1319 msgid "metaconfig must have the same optiondescription" msgstr "" -#: tiramisu/error.py:24 +#: tiramisu/error.py:26 msgid "and" msgstr "" -#: tiramisu/error.py:26 +#: tiramisu/error.py:28 msgid "or" msgstr "" @@ -231,293 +255,164 @@ msgstr "" msgid " {} " msgstr "" -#: tiramisu/error.py:105 tiramisu/setting.py:601 +#: tiramisu/error.py:98 msgid "property" msgstr "" -#: tiramisu/error.py:107 tiramisu/setting.py:603 +#: tiramisu/error.py:100 msgid "properties" msgstr "" -#: tiramisu/error.py:109 -msgid "cannot access to {0} \"{1}\" because \"{2}\" has {3} {4}" -msgstr "" - -#: tiramisu/error.py:116 -msgid "cannot access to {0} \"{1}\" because has {2} {3}" -msgstr "" - -#: tiramisu/error.py:192 +#: tiramisu/error.py:187 msgid "invalid value" msgstr "" -#: tiramisu/error.py:197 +#: tiramisu/error.py:192 msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\"" msgstr "" -#: tiramisu/error.py:201 tiramisu/error.py:205 +#: tiramisu/error.py:208 tiramisu/error.py:212 msgid "\"{0}\" is an invalid {1} for \"{2}\"" msgstr "" -#: tiramisu/function.py:34 -msgid "args in params must be a tuple" +#: tiramisu/function.py:113 +msgid "this IP is not in network {network[\"value\"]} ({network[\"name\"]})" msgstr "" -#: tiramisu/function.py:37 tiramisu/function.py:42 -msgid "arg in params must be a Param" +#: tiramisu/function.py:115 +msgid "this IP is not in network {network[\"value\"]} ({network[\"name\"]}) with netmask {netmask[\"value\"]} ({netmask[\"name\"]})" msgstr "" -#: tiramisu/function.py:39 -msgid "kwargs in params must be a dict" +#: tiramisu/function.py:559 +msgid "the value of \"{0}\" is {1}" msgstr "" -#: tiramisu/function.py:58 -msgid "paramoption needs an option not {}" +#: tiramisu/function.py:561 +msgid "the value of \"{0}\" is not {1}" msgstr "" -#: tiramisu/function.py:64 -msgid "param must have a boolean not a {} for notraisepropertyerror" -msgstr "" - -#: tiramisu/function.py:271 -msgid "unexpected {} condition_operator in calc_value" -msgstr "" - -#: tiramisu/option/baseoption.py:75 tiramisu/option/symlinkoption.py:33 +#: tiramisu/option/baseoption.py:70 tiramisu/option/symlinkoption.py:40 msgid "\"{0}\" is an invalid name for an option" msgstr "" -#: tiramisu/option/baseoption.py:93 +#: tiramisu/option/baseoption.py:83 msgid "invalid properties type {0} for {1}, must be a frozenset" msgstr "" -#: tiramisu/option/baseoption.py:115 -msgid "conflict: properties already set in requirement {0} for {1}" +#: tiramisu/option/baseoption.py:89 +msgid "invalid property type {0} for {1}, must be a string or a Calculation" msgstr "" -#: tiramisu/option/baseoption.py:162 -msgid "{0} must be a function" -msgstr "" - -#: tiramisu/option/baseoption.py:164 -msgid "{0}_params must be a params" -msgstr "" - -#: tiramisu/option/baseoption.py:205 -msgid "cannot find those arguments \"{}\" in function \"{}\" for \"{}\"" -msgstr "" - -#: tiramisu/option/baseoption.py:224 -msgid "missing those arguments \"{}\" in function \"{}\" for \"{}\"" -msgstr "" - -#: tiramisu/option/baseoption.py:258 -msgid "params defined for a callback function but no callback defined yet for option \"{0}\"" -msgstr "" - -#: tiramisu/option/baseoption.py:350 tiramisu/storage/dictionary/value.py:256 -#: tiramisu/storage/sqlite3/value.py:201 -msgid "information's item not found: {0}" -msgstr "" - -#: tiramisu/option/baseoption.py:363 +#: tiramisu/option/baseoption.py:227 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option/baseoption.py:394 +#: tiramisu/option/baseoption.py:265 msgid "\"{}\" ({}) object attribute \"{}\" is read-only" msgstr "" -#: tiramisu/option/baseoption.py:404 -msgid "\"{}\" not part of any Config" -msgstr "" - -#: tiramisu/option/baseoption.py:459 -msgid "malformed requirements must be an option in option {0}" -msgstr "" - -#: tiramisu/option/baseoption.py:462 -msgid "malformed requirements multi option must not set as requires of non multi option {0}" -msgstr "" - -#: tiramisu/option/baseoption.py:501 -msgid "malformed requirements expected must have option and value for option {0}" -msgstr "" - -#: tiramisu/option/baseoption.py:508 tiramisu/option/baseoption.py:524 -msgid "malformed requirements expected value must be valid for option {0}: {1}" -msgstr "" - -#: tiramisu/option/baseoption.py:538 -msgid "malformed requirements for option: {0} action cannot be force_store_value" -msgstr "" - -#: tiramisu/option/baseoption.py:546 -msgid "malformed requirements for option: {0} inverse must be boolean" -msgstr "" - -#: tiramisu/option/baseoption.py:553 -msgid "malformed requirements for option: {0} transitive must be boolean" -msgstr "" - -#: tiramisu/option/baseoption.py:560 -msgid "malformed requirements for option: {0} same_action must be boolean" -msgstr "" - -#: tiramisu/option/baseoption.py:567 -msgid "malformed requirements for option: \"{0}\" operator must be \"or\" or \"and\"" -msgstr "" - -#: tiramisu/option/baseoption.py:580 -msgid "malformed requirements type for option: {0}, must be a dict" -msgstr "" - -#: tiramisu/option/baseoption.py:586 -msgid "malformed requirements for option: {0} unknown keys {1}, must only {2}" -msgstr "" - -#: tiramisu/option/baseoption.py:598 -msgid "malformed requirements for option: {0} require must have option, expected and action keys" -msgstr "" - -#: tiramisu/option/booloption.py:31 +#: tiramisu/option/booloption.py:32 msgid "boolean" msgstr "" -#: tiramisu/option/broadcastoption.py:32 +#: tiramisu/option/broadcastoption.py:33 msgid "broadcast address" msgstr "" -#: tiramisu/option/broadcastoption.py:39 tiramisu/option/dateoption.py:38 -#: tiramisu/option/domainnameoption.py:126 tiramisu/option/ipoption.py:83 -#: tiramisu/option/netmaskoption.py:42 tiramisu/option/networkoption.py:68 -#: tiramisu/option/passwordoption.py:39 tiramisu/option/portoption.py:107 -#: tiramisu/option/urloption.py:41 +#: tiramisu/option/broadcastoption.py:41 msgid "invalid string" msgstr "" -#: tiramisu/option/broadcastoption.py:57 -msgid "invalid broadcast consistency, a network and a netmask are needed" -msgstr "" - -#: tiramisu/option/broadcastoption.py:62 -msgid "broadcast \"{4}\" invalid with network {0}/{1} (\"{2}\"/\"{3}\")" -msgstr "" - -#: tiramisu/option/choiceoption.py:37 +#: tiramisu/option/choiceoption.py:38 msgid "choice" msgstr "" -#: tiramisu/option/choiceoption.py:66 -msgid "values is not a function, so values_params must be None" +#: tiramisu/option/choiceoption.py:51 +msgid "values must be a tuple or a calculation for {0}" msgstr "" -#: tiramisu/option/choiceoption.py:68 -msgid "values must be a tuple or a function for {0}" +#: tiramisu/option/choiceoption.py:67 +msgid "the calculated values \"{0}\" for \"{1}\" is not a list" msgstr "" -#: tiramisu/option/choiceoption.py:108 -msgid "calculated values for {0} is not a list" -msgstr "" - -#: tiramisu/option/choiceoption.py:123 +#: tiramisu/option/choiceoption.py:97 msgid "only \"{0}\" is allowed" msgstr "" -#: tiramisu/option/choiceoption.py:126 +#: tiramisu/option/choiceoption.py:99 msgid "only {0} are allowed" msgstr "" -#: tiramisu/option/dateoption.py:31 +#: tiramisu/option/dateoption.py:33 msgid "date" msgstr "" -#: tiramisu/option/domainnameoption.py:38 -msgid "domain name" -msgstr "" - -#: tiramisu/option/domainnameoption.py:59 -msgid "unknown type_ {0} for hostname" -msgstr "" - #: tiramisu/option/domainnameoption.py:62 +msgid "unknown type {0} for hostname" +msgstr "" + +#: tiramisu/option/domainnameoption.py:65 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option/domainnameoption.py:64 +#: tiramisu/option/domainnameoption.py:67 +msgid "allow_cidr_network must be a boolean" +msgstr "" + +#: tiramisu/option/domainnameoption.py:69 msgid "allow_without_dot must be a boolean" msgstr "" -#: tiramisu/option/domainnameoption.py:73 -msgid "only lowercase, number, \"-\" and \".\" are characters are allowed" +#: tiramisu/option/domainnameoption.py:71 +msgid "allow_startswith_dot must be a boolean" msgstr "" -#: tiramisu/option/domainnameoption.py:74 -msgid "only lowercase, number, \"-\" and \".\" are characters are recommanded" -msgstr "" - -#: tiramisu/option/domainnameoption.py:77 -msgid "only lowercase, number and - are characters are allowed" -msgstr "" - -#: tiramisu/option/domainnameoption.py:78 -msgid "only lowercase, number and \"-\" are characters are recommanded" -msgstr "" - -#: tiramisu/option/domainnameoption.py:80 #: tiramisu/option/domainnameoption.py:81 +msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are allowed" +msgstr "" + +#: tiramisu/option/domainnameoption.py:82 +msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are recommanded" +msgstr "" + +#: tiramisu/option/domainnameoption.py:84 +#: tiramisu/option/domainnameoption.py:85 msgid "could be a IP, otherwise {}" msgstr "" -#: tiramisu/option/domainnameoption.py:120 +#: tiramisu/option/domainnameoption.py:125 msgid "invalid length (min 1)" msgstr "" -#: tiramisu/option/domainnameoption.py:122 +#: tiramisu/option/domainnameoption.py:127 msgid "invalid length (max {0})" msgstr "" #: tiramisu/option/domainnameoption.py:133 -msgid "must not be an IP" -msgstr "" - -#: tiramisu/option/domainnameoption.py:139 msgid "must have dot" msgstr "" -#: tiramisu/option/domainnameoption.py:141 +#: tiramisu/option/domainnameoption.py:135 msgid "invalid length (max 255)" msgstr "" -#: tiramisu/option/domainnameoption.py:149 +#: tiramisu/option/domainnameoption.py:154 +msgid "must not be an IP" +msgstr "" + +#: tiramisu/option/domainnameoption.py:180 msgid "some characters are uppercase" msgstr "" -#: tiramisu/option/dynoptiondescription.py:56 -msgid "cannot set optiondescription in a dynoptiondescription" +#: tiramisu/option/dynoptiondescription.py:65 +msgid "suffixes in dynoptiondescription has to be a calculation" msgstr "" -#: tiramisu/option/dynoptiondescription.py:61 -msgid "cannot set symlinkoption in a dynoptiondescription" -msgstr "" - -#: tiramisu/option/dynoptiondescription.py:72 -msgid "callback is mandatory for the dynoptiondescription \"{}\"" -msgstr "" - -#: tiramisu/option/dynoptiondescription.py:86 -msgid "DynOptionDescription callback for option \"{}\", is not a list ({})" -msgstr "" - -#: tiramisu/option/dynoptiondescription.py:92 +#: tiramisu/option/dynoptiondescription.py:109 msgid "invalid suffix \"{}\" for option \"{}\"" msgstr "" -#: tiramisu/option/dynoptiondescription.py:101 -msgid "DynOptionDescription callback return a list with multiple value \"{}\"" -msgstr "" - -#: tiramisu/option/emailoption.py:32 +#: tiramisu/option/emailoption.py:34 msgid "email address" msgstr "" @@ -525,312 +420,143 @@ msgstr "" msgid "file name" msgstr "" -#: tiramisu/option/floatoption.py:31 +#: tiramisu/option/filenameoption.py:38 +msgid "must starts with \"/\"" +msgstr "" + +#: tiramisu/option/floatoption.py:32 msgid "float" msgstr "" -#: tiramisu/option/intoption.py:31 -msgid "integer" -msgstr "" - -#: tiramisu/option/intoption.py:55 -msgid "value must be greater than \"{0}\"" -msgstr "" - -#: tiramisu/option/intoption.py:58 -msgid "value must be less than \"{0}\"" -msgstr "" - -#: tiramisu/option/ipoption.py:36 +#: tiramisu/option/ipoption.py:33 msgid "IP" msgstr "" -#: tiramisu/option/ipoption.py:89 tiramisu/option/networkoption.py:74 -msgid "must use CIDR notation" +#: tiramisu/option/ipoption.py:57 +msgid "it's in fact a network address" msgstr "" -#: tiramisu/option/ipoption.py:111 +#: tiramisu/option/ipoption.py:59 +msgid "it's in fact a broacast address" +msgstr "" + +#: tiramisu/option/ipoption.py:72 +msgid "CIDR address must have a \"/\"" +msgstr "" + +#: tiramisu/option/ipoption.py:83 msgid "shouldn't be reserved IP" msgstr "" -#: tiramisu/option/ipoption.py:113 +#: tiramisu/option/ipoption.py:85 msgid "mustn't be reserved IP" msgstr "" -#: tiramisu/option/ipoption.py:117 +#: tiramisu/option/ipoption.py:89 msgid "should be private IP" msgstr "" -#: tiramisu/option/ipoption.py:119 +#: tiramisu/option/ipoption.py:91 msgid "must be private IP" msgstr "" -#: tiramisu/option/ipoption.py:147 -msgid "IP not in network \"{0}\" (\"{1}\")" -msgstr "" - -#: tiramisu/option/ipoption.py:162 -msgid "ip_network needs an IP, a network and a netmask" -msgstr "" - -#: tiramisu/option/ipoption.py:169 -msgid "IP not in network \"{2}\"/\"{4}\" (\"{3}\"/\"{5}\")" -msgstr "" - -#: tiramisu/option/ipoption.py:171 -msgid "the network doest not match with IP \"{0}\" (\"{1}\") and network \"{4}\" (\"{5}\")" -msgstr "" - -#: tiramisu/option/ipoption.py:173 -msgid "the netmask does not match with IP \"{0}\" (\"{1}\") and broadcast \"{2}\" (\"{3}\")" -msgstr "" - #: tiramisu/option/leadership.py:56 msgid "a leader and a follower are mandatories in leadership \"{}\"" msgstr "" -#: tiramisu/option/leadership.py:62 +#: tiramisu/option/leadership.py:73 +msgid "leader cannot have \"{}\" property" +msgstr "" + +#: tiramisu/option/leadership.py:77 msgid "leadership \"{0}\" shall not have a symlinkoption" msgstr "" -#: tiramisu/option/leadership.py:65 +#: tiramisu/option/leadership.py:80 msgid "leadership \"{0}\" shall not have a subgroup" msgstr "" -#: tiramisu/option/leadership.py:68 +#: tiramisu/option/leadership.py:83 msgid "only multi option allowed in leadership \"{0}\" but option \"{1}\" is not a multi" msgstr "" -#: tiramisu/option/leadership.py:73 -msgid "not allowed default value for follower option \"{0}\" in leadership \"{1}\"" +#: tiramisu/option/macoption.py:34 +msgid "mac address" msgstr "" -#: tiramisu/option/leadership.py:89 -msgid "callback of leader's option shall not refered to a follower's ones" -msgstr "" - -#: tiramisu/option/leadership.py:97 -msgid "leader {} have requirement, but Leadership {} too" -msgstr "" - -#: tiramisu/option/leadership.py:112 -msgid "malformed requirements option \"{0}\" must not be in follower for \"{1}\"" -msgstr "" - -#: tiramisu/option/netmaskoption.py:35 +#: tiramisu/option/netmaskoption.py:32 msgid "netmask address" msgstr "" -#: tiramisu/option/netmaskoption.py:60 -msgid "network_netmask needs a network and a netmask" -msgstr "" - -#: tiramisu/option/netmaskoption.py:69 -msgid "the netmask \"{0}\" (\"{1}\") does not match" -msgstr "" - -#: tiramisu/option/netmaskoption.py:72 -msgid "the network \"{0}\" (\"{1}\") does not match" -msgstr "" - -#: tiramisu/option/netmaskoption.py:83 -msgid "ip_netmask needs an IP and a netmask" -msgstr "" - -#: tiramisu/option/netmaskoption.py:91 -msgid "this is a network with netmask \"{0}\" (\"{1}\")" -msgstr "" - -#: tiramisu/option/netmaskoption.py:95 -msgid "this is a broadcast with netmask \"{0}\" (\"{1}\")" -msgstr "" - -#: tiramisu/option/netmaskoption.py:100 -msgid "IP \"{0}\" (\"{1}\") is the network" -msgstr "" - -#: tiramisu/option/netmaskoption.py:104 -msgid "IP \"{0}\" (\"{1}\") is the broadcast" -msgstr "" - #: tiramisu/option/networkoption.py:32 msgid "network address" msgstr "" -#: tiramisu/option/networkoption.py:91 +#: tiramisu/option/networkoption.py:51 +msgid "must use CIDR notation" +msgstr "" + +#: tiramisu/option/networkoption.py:68 msgid "shouldn't be reserved network" msgstr "" -#: tiramisu/option/networkoption.py:93 +#: tiramisu/option/networkoption.py:70 msgid "mustn't be reserved network" msgstr "" -#: tiramisu/option/option.py:78 +#: tiramisu/option/option.py:69 msgid "default_multi is set whereas multi is False in option: {0}" msgstr "" -#: tiramisu/option/option.py:95 -msgid "invalid multi type \"{}\"" +#: tiramisu/option/option.py:86 +msgid "invalid multi type \"{}\" for \"{}\"" msgstr "" -#: tiramisu/option/option.py:118 -msgid "unique must be a boolean, not \"{}\"" +#: tiramisu/option/option.py:102 +msgid "validators must be a Calculation for \"{}\"" msgstr "" -#: tiramisu/option/option.py:120 -msgid "unique must be set only with multi value" -msgstr "" - -#: tiramisu/option/option.py:131 -msgid "invalid default_multi value {0} for option {1}: {2}" +#: tiramisu/option/option.py:127 +msgid "invalid default_multi value \"{0}\" for option \"{1}\"" msgstr "" #: tiramisu/option/option.py:137 msgid "invalid default_multi value \"{0}\" for option \"{1}\", must be a list for a submulti" msgstr "" -#: tiramisu/option/option.py:259 -msgid "invalid value \"{}\", this value is already in \"{}\"" +#: tiramisu/option/option.py:291 +msgid "the value \"{}\" is not unique" msgstr "" -#: tiramisu/option/option.py:289 +#: tiramisu/option/option.py:331 msgid "which must not be a list" msgstr "" -#: tiramisu/option/option.py:323 tiramisu/option/option.py:332 +#: tiramisu/option/option.py:373 tiramisu/option/option.py:399 msgid "which must be a list" msgstr "" -#: tiramisu/option/option.py:337 +#: tiramisu/option/option.py:392 msgid "which \"{}\" must be a list of list" msgstr "" -#: tiramisu/option/option.py:379 -msgid "default value not allowed if option \"{0}\" is calculated" -msgstr "" - -#: tiramisu/option/option.py:427 -msgid "'{0}' ({1}) cannot add consistency, option is read-only" -msgstr "" - -#: tiramisu/option/option.py:435 -msgid "consistency {0} not available for this option" -msgstr "" - -#: tiramisu/option/option.py:442 -msgid "unknown parameter {0} in consistency" -msgstr "" - -#: tiramisu/option/option.py:554 tiramisu/option/option.py:559 -msgid "cannot add consistency with submulti option" -msgstr "" - -#: tiramisu/option/option.py:560 -msgid "consistency must be set with an option, not {}" -msgstr "" - -#: tiramisu/option/option.py:563 tiramisu/option/option.py:571 -msgid "almost one option in consistency is in a dynoptiondescription but not all" -msgstr "" - -#: tiramisu/option/option.py:567 -msgid "option in consistency must be in same dynoptiondescription" -msgstr "" - -#: tiramisu/option/option.py:574 -msgid "cannot add consistency with itself" -msgstr "" - -#: tiramisu/option/option.py:576 -msgid "every options in consistency must be multi or none" -msgstr "" - -#: tiramisu/option/option.py:616 -msgid "unexpected length of \"{}\" in constency \"{}\", should be \"{}\"" -msgstr "" - -#: tiramisu/option/option.py:715 -msgid "should be different from the value of {}" -msgstr "" - -#: tiramisu/option/option.py:717 -msgid "must be different from the value of {}" -msgstr "" - -#: tiramisu/option/option.py:720 -msgid "value for {} should be different" -msgstr "" - -#: tiramisu/option/option.py:722 -msgid "value for {} must be different" -msgstr "" - -#: tiramisu/option/optiondescription.py:63 -#: tiramisu/option/optiondescription.py:181 -msgid "option description seems to be part of an other config" -msgstr "" - -#: tiramisu/option/optiondescription.py:87 -msgid "the follower \"{0}\" cannot have \"force_store_value\" property" -msgstr "" - -#: tiramisu/option/optiondescription.py:91 -msgid "the dynoption \"{0}\" cannot have \"force_store_value\" property" -msgstr "" - -#: tiramisu/option/optiondescription.py:99 tiramisu/setting.py:689 -msgid "a leader ({0}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\"" -msgstr "" - -#: tiramisu/option/optiondescription.py:108 -msgid "malformed consistency option \"{0}\" must be in same leadership" -msgstr "" - -#: tiramisu/option/optiondescription.py:116 -msgid "malformed consistency option \"{0}\" must not be a multi for \"{1}\"" -msgstr "" - -#: tiramisu/option/optiondescription.py:120 -msgid "malformed consistency option \"{0}\" must be in same leadership as \"{1}\"" -msgstr "" - -#: tiramisu/option/optiondescription.py:151 -msgid "malformed requirements option \"{0}\" must be in same leadership for \"{1}\"" -msgstr "" - -#: tiramisu/option/optiondescription.py:155 -msgid "malformed requirements option \"{0}\" must not be a multi for \"{1}\"" -msgstr "" - -#: tiramisu/option/optiondescription.py:159 +#: tiramisu/option/optiondescription.py:109 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option/optiondescription.py:170 -msgid "consistency with option {0} which is not in Config" -msgstr "" - -#: tiramisu/option/optiondescription.py:225 -msgid "unknown option \"{0}\" in optiondescription \"{1}\"" -msgstr "" - -#: tiramisu/option/optiondescription.py:279 +#: tiramisu/option/optiondescription.py:306 msgid "children in optiondescription \"{}\" must be a list" msgstr "" -#: tiramisu/option/optiondescription.py:303 +#: tiramisu/option/optiondescription.py:329 msgid "duplicate option name: \"{0}\"" msgstr "" -#: tiramisu/option/optiondescription.py:308 -msgid "the option's name \"{}\" start as the dynoptiondescription's name \"{}\"" -msgstr "" - -#: tiramisu/option/optiondescription.py:333 +#: tiramisu/option/optiondescription.py:374 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option/optiondescription.py:337 +#: tiramisu/option/optiondescription.py:378 msgid "group_type: {0} not allowed" msgstr "" @@ -838,193 +564,148 @@ msgstr "" msgid "password" msgstr "" -#: tiramisu/option/portoption.py:44 +#: tiramisu/option/permissionsoption.py:38 +msgid "unix file permissions" +msgstr "" + +#: tiramisu/option/permissionsoption.py:52 +msgid "only 3 or 4 octal digits are allowed" +msgstr "" + +#: tiramisu/option/permissionsoption.py:65 +msgid "user" +msgstr "" + +#: tiramisu/option/permissionsoption.py:66 +#: tiramisu/option/permissionsoption.py:68 +msgid "group" +msgstr "" + +#: tiramisu/option/permissionsoption.py:69 +msgid "other" +msgstr "" + +#: tiramisu/option/permissionsoption.py:73 +msgid "too weak" +msgstr "" + +#: tiramisu/option/portoption.py:41 msgid "port" msgstr "" -#: tiramisu/option/portoption.py:81 +#: tiramisu/option/portoption.py:71 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option/portoption.py:86 +#: tiramisu/option/portoption.py:76 msgid "max value is empty" msgstr "" -#: tiramisu/option/portoption.py:111 +#: tiramisu/option/portoption.py:91 msgid "range must have two values only" msgstr "" -#: tiramisu/option/portoption.py:113 +#: tiramisu/option/portoption.py:93 msgid "first port in range must be smaller than the second one" msgstr "" -#: tiramisu/option/portoption.py:123 -msgid "must be an integer between {0} and {1}" -msgstr "" - #: tiramisu/option/stroption.py:33 msgid "string" msgstr "" -#: tiramisu/option/symlinkoption.py:37 +#: tiramisu/option/symlinkoption.py:44 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option/syndynoptiondescription.py:68 -msgid "unknown option \"{0}\" in syndynoptiondescription \"{1}\"" +#: tiramisu/option/symlinkoption.py:60 +msgid "cannot set symlinkoption in a dynoptiondescription" msgstr "" -#: tiramisu/option/urloption.py:34 +#: tiramisu/option/urloption.py:39 msgid "URL" msgstr "" -#: tiramisu/option/urloption.py:44 +#: tiramisu/option/urloption.py:91 msgid "must start with http:// or https://" msgstr "" -#: tiramisu/option/urloption.py:62 -msgid "port must be an between 0 and 65536" -msgstr "" - -#: tiramisu/option/urloption.py:71 +#: tiramisu/option/urloption.py:122 msgid "must ends with a valid resource name" msgstr "" -#: tiramisu/option/usernameoption.py:32 -msgid "username" +#: tiramisu/option/usernameoption.py:35 +msgid "unix username" msgstr "" -#: tiramisu/setting.py:262 +#: tiramisu/option/usernameoption.py:42 +msgid "unix groupname" +msgstr "" + +#: tiramisu/setting.py:302 msgid "can't rebind {0}" msgstr "" -#: tiramisu/setting.py:267 +#: tiramisu/setting.py:308 msgid "can't unbind {0}" msgstr "" -#: tiramisu/setting.py:541 -msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" +#: tiramisu/setting.py:515 +msgid "invalid property type {type(new_prop)} for {option_bag.option.impl_getname()} with {prop.function.__name__} function" msgstr "" -#: tiramisu/setting.py:604 -msgid "cannot access to option \"{0}\" because required option \"{1}\" has {2} {3}" +#: tiramisu/setting.py:521 +msgid "leader cannot have \"{new_prop}\" property" msgstr "" -#: tiramisu/setting.py:632 -msgid "the calculated value is {0}" +#: tiramisu/setting.py:591 +msgid "leader cannot have \"{list(not_allowed_properties)}\" property" msgstr "" -#: tiramisu/setting.py:634 -msgid "the calculated value is not {0}" +#: tiramisu/setting.py:595 +msgid "a leader ({opt.impl_get_display_name()}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\"" msgstr "" -#: tiramisu/setting.py:638 -msgid "the value of \"{0}\" is {1}" -msgstr "" - -#: tiramisu/setting.py:640 -msgid "the value of \"{0}\" is not {1}" -msgstr "" - -#: tiramisu/setting.py:679 -msgid "cannot set property {} for option \"{}\" this property is calculated" -msgstr "" - -#: tiramisu/setting.py:684 -msgid "can't assign property to the symlinkoption \"{}\"" -msgstr "" - -#: tiramisu/setting.py:716 +#: tiramisu/setting.py:626 msgid "permissive must be a frozenset" msgstr "" -#: tiramisu/setting.py:720 -msgid "can't assign permissive to the symlinkoption \"{}\"" -msgstr "" - -#: tiramisu/setting.py:727 +#: tiramisu/setting.py:635 msgid "cannot add those permissives: {0}" msgstr "" -#: tiramisu/setting.py:744 -msgid "can't reset properties to the symlinkoption \"{}\"" -msgstr "" - -#: tiramisu/setting.py:759 -msgid "can't reset permissives to the symlinkoption \"{}\"" -msgstr "" - -#: tiramisu/storage/__init__.py:61 -msgid "cannot import the storage {0}" -msgstr "" - -#: tiramisu/storage/__init__.py:73 -msgid "storage_type is already set, cannot rebind it" -msgstr "" - -#: tiramisu/storage/dictionary/storage.py:44 -#: tiramisu/storage/sqlite3/storage.py:129 -msgid "session \"{}\" already exists" -msgstr "" - -#: tiramisu/storage/dictionary/storage.py:46 -msgid "a dictionary cannot be persistent" -msgstr "" - -#: tiramisu/storage/dictionary/value.py:265 -#: tiramisu/storage/sqlite3/value.py:213 -msgid "information's item not found {0}" -msgstr "" - -#: tiramisu/storage/dictionary/value.py:284 -msgid "cannot delete none persistent session" -msgstr "" - -#: tiramisu/storage/sqlite3/storage.py:44 -msgid "cannot change setting when connexion is already opened" -msgstr "" - -#: tiramisu/todict.py:67 tiramisu/todict.py:563 -msgid "context is not supported from now for {}" -msgstr "" - -#: tiramisu/todict.py:345 +#: tiramisu/todict.py:352 msgid "option {} only works when remotable is not \"none\"" msgstr "" -#: tiramisu/todict.py:489 +#: tiramisu/todict.py:505 msgid "unable to transform tiramisu object to dict: {}" msgstr "" -#: tiramisu/todict.py:795 tiramisu/todict.py:927 +#: tiramisu/todict.py:816 tiramisu/todict.py:955 msgid "unknown form {}" msgstr "" -#: tiramisu/todict.py:840 +#: tiramisu/todict.py:862 msgid "not in current area" msgstr "" -#: tiramisu/todict.py:860 +#: tiramisu/todict.py:883 msgid "only multi option can have action \"add\", but \"{}\" is not a multi" msgstr "" -#: tiramisu/todict.py:862 -msgid "unknown action" +#: tiramisu/todict.py:885 +msgid "unknown action {}" msgstr "" -#: tiramisu/value.py:425 -msgid "can't set owner for the symlinkoption \"{}\"" -msgstr "" - -#: tiramisu/value.py:428 tiramisu/value.py:638 +#: tiramisu/value.py:506 tiramisu/value.py:722 msgid "set owner \"{0}\" is forbidden" msgstr "" -#: tiramisu/value.py:431 -msgid "no value for {0} cannot change owner to {1}" +#: tiramisu/value.py:636 +msgid "index {index} is greater than the length {length} for option \"{option_bag.option.impl_get_display_name()}\"" msgstr "" -#: tiramisu/value.py:509 -msgid "index {} is greater than the length {} for option \"{}\"" +#: tiramisu/value.py:695 +msgid "information's item not found: {0}" msgstr "" diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 0157d32..04dc405 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -53,7 +53,7 @@ class Base: __slots__ = ('_name', '_path', '_informations', - '_subdyn', + '_subdyns', '_properties', '_has_dependency', '_dependencies', @@ -106,9 +106,9 @@ class Base: return getattr(self, '_has_dependency', False) return hasattr(self, '_dependencies') - def _get_dependencies(self, - context_od, - ) -> Set[str]: + def get_dependencies(self, + context_od, + ) -> Set[str]: ret = set(getattr(self, '_dependencies', STATIC_TUPLE)) if context_od and hasattr(context_od, '_dependencies'): # add options that have context is set in calculation @@ -123,7 +123,7 @@ class Base: is_suffix: bool=False, ) -> None: woption = weakref.ref(option) - options = self._get_dependencies(None) + options = self.get_dependencies(None) options.add(woption) self._dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init if is_suffix: @@ -141,6 +141,9 @@ class Base: """ return False + def impl_is_sub_dyn_optiondescription(self): + return False + def impl_getname(self) -> str: """get name """ @@ -175,17 +178,22 @@ class Base: subdyn, ) -> None: # pylint: disable=attribute-defined-outside-init - self._subdyn = subdyn + if getattr(self, '_subdyns', None) is None: + self._subdyns = [] + self._subdyns.append(subdyn) def issubdyn(self) -> bool: """is sub dynoption """ - return getattr(self, '_subdyn', None) is not None + return getattr(self, '_subdyns', None) is not None def getsubdyn(self): """get sub dynoption """ - return self._subdyn() + return self._subdyns[0]() + + def get_sub_dyns(self): + return self._subdyns # ____________________________________________________________ # information @@ -251,7 +259,8 @@ class BaseOption(Base): def __setattr__(self, name: str, - value: Any) -> Any: + value: Any, + ) -> Any: """set once and only once some attributes in the option, like `_name`. `_name` cannot be changed once the option is pushed in the :class:`tiramisu.option.OptionDescription`. diff --git a/tiramisu/option/dynoptiondescription.py b/tiramisu/option/dynoptiondescription.py index 07aa683..5677fe1 100644 --- a/tiramisu/option/dynoptiondescription.py +++ b/tiramisu/option/dynoptiondescription.py @@ -22,13 +22,14 @@ """ import re import weakref -from typing import List, Any, Optional +from typing import List, Any, Optional, Tuple from itertools import chain from ..autolib import ParamOption from ..i18n import _ from .optiondescription import OptionDescription +from .syndynoptiondescription import SynDynLeadership from .baseoption import BaseOption from ..setting import OptionBag, ConfigBag, undefined from ..error import ConfigError @@ -41,7 +42,9 @@ NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$') class DynOptionDescription(OptionDescription): """dyn option description """ - __slots__ = ('_suffixes',) + __slots__ = ('_suffixes', + '_subdyns', + ) def __init__(self, name: str, @@ -85,10 +88,15 @@ class DynOptionDescription(OptionDescription): def get_suffixes(self, config_bag: ConfigBag, + dynoption=None, ) -> List[str]: """get dynamic suffixes """ - option_bag = OptionBag(self, + if dynoption: + self_opt = dynoption + else: + self_opt = self + option_bag = OptionBag(self_opt, None, config_bag, properties=None, @@ -119,70 +127,69 @@ class DynOptionDescription(OptionDescription): def impl_is_dynoptiondescription(self) -> bool: return True + def option_is_self(self, + option, + ) -> bool: + return option == self or \ + (option.impl_is_sub_dyn_optiondescription() and option.opt == self) + + def split_path(self, + dynoption, + option, + ) -> Tuple[str, str]: + """self.impl_getpath() is something like root.xxx.dynoption_path + option.impl_getpath() is something like root.xxx.dynoption_path.sub.path + must return ('root.xxx.', '.sub') + """ + if dynoption is None: + self_path = self.impl_getpath() + else: + self_path = dynoption.impl_getpath() + root_path = self_path.rsplit('.', 1)[0] if '.' in self_path else None + # + if self.option_is_self(option): + sub_path = '' + else: + option_path = option.impl_getpath() + if root_path: + if isinstance(option, SynDynLeadership): + count_root_path = option_path.count('.') - root_path.count('.') + root_path = option_path.rsplit('.', count_root_path)[0] + root_path += '.' + self_number_child = self_path.count('.') + 1 + option_sub_path = option_path.split('.', self_number_child)[-1] + sub_path = '.' + option_sub_path.rsplit('.', 1)[0] if '.' in option_sub_path else '' + return root_path, sub_path + def get_sub_children(self, option, config_bag, + *, index=None, properties=undefined, + dynoption=None, ): - rootpath = self.get_root_path(option) - subpath = self.get_sub_path(option) - for suffix in self.get_suffixes(config_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')) + root_path, sub_path = self.split_path(dynoption, + option, + ) + for suffix in self.get_suffixes(config_bag, + dynoption=dynoption, + ): + if self.option_is_self(option): + parent_path = root_path + elif root_path: + parent_path = root_path + self.impl_getname(suffix) + sub_path + else: + parent_path = self.impl_getname(suffix) + sub_path + yield OptionBag(option.to_dynoption(parent_path, + suffix, + self, + ), + index, + config_bag, + properties=properties, + ori_option=option + ) def impl_getname(self, suffix=None) -> str: """get name diff --git a/tiramisu/option/leadership.py b/tiramisu/option/leadership.py index ff677e2..dc88e75 100644 --- a/tiramisu/option/leadership.py +++ b/tiramisu/option/leadership.py @@ -106,10 +106,9 @@ class Leadership(OptionDescription): def _setsubdyn(self, subdyn, ) -> None: - # pylint: disable=attribute-defined-outside-init,protected-access for chld in self._children[1]: chld._setsubdyn(subdyn) - self._subdyn = subdyn + super()._setsubdyn(subdyn) def is_leader(self, opt: Option, diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index e042db9..e3617cb 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -20,13 +20,14 @@ # ____________________________________________________________ """OptionDescription """ +import weakref from typing import Optional, Iterator, Union, List from ..i18n import _ from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined from .baseoption import BaseOption -from .syndynoptiondescription import SynDynOptionDescription +from .syndynoptiondescription import SubDynOptionDescription, SynDynOptionDescription from ..error import ConfigError, ConflictError @@ -127,15 +128,14 @@ class CacheOptionDescription(BaseOption): dynopt = option.getsubdyn() yield from dynopt.get_sub_children(option, config_bag, - None, + index=None, ) else: - option_bag = OptionBag(option, - None, - config_bag, - properties=None, - ) - yield option_bag + yield OptionBag(option, + None, + config_bag, + properties=None, + ) if 'force_store_value' not in config_bag.properties: return values = config_bag.context.get_values() @@ -194,6 +194,10 @@ class OptionDescriptionWalk(CacheOptionDescription): name: str, config_bag: ConfigBag, subpath: str, + *, + dynoption=None, + option_suffix: Optional[str]=None, + allow_dynoption: bool=False, ) -> Union[BaseOption, SynDynOptionDescription]: """get a child """ @@ -201,18 +205,32 @@ class OptionDescriptionWalk(CacheOptionDescription): if name in self._children[0]: # pylint: disable=no-member option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member if option.impl_is_dynoptiondescription(): - raise AttributeError(_(f'unknown option "{name}" ' - "in root optiondescription (it's a dynamic option)" - )) + if allow_dynoption: + option_suffix = None + else: + raise AttributeError(_(f'unknown option "{name}" ' + "in root optiondescription (it's a dynamic option)" + )) + if option.issubdyn(): + return option.to_dynoption(subpath, + option_suffix, + option, + ) return option # if dyn + if dynoption: + self_opt = dynoption + else: + self_opt = self for child in self._children[1]: # pylint: disable=no-member if not child.impl_is_dynoptiondescription(): continue cname = child.impl_getname() if not name.startswith(cname): continue - for suffix in child.get_suffixes(config_bag): + for suffix in child.get_suffixes(config_bag, + dynoption, + ): if name != cname + child.convert_suffix_to_path(suffix): continue return child.to_dynoption(subpath, @@ -229,22 +247,39 @@ class OptionDescriptionWalk(CacheOptionDescription): def get_children(self, config_bag: Union[ConfigBag, Undefined], + *, dyn: bool=True, + #path: Optional[str]=None, + dynoption=None, + option_suffix: Optional[str]=None, ) -> Union[BaseOption, SynDynOptionDescription]: """get children """ +# if path: +# subpath = path + if dynoption: + self_opt = dynoption + else: + self_opt = self if not dyn or config_bag is undefined or \ config_bag.context.get_description() == self: subpath = '' else: - subpath = self.impl_getpath() + subpath = self_opt.impl_getpath() for child in self._children[1]: # pylint: disable=no-member if dyn and child.impl_is_dynoptiondescription(): - for suffix in child.get_suffixes(config_bag): + for suffix in child.get_suffixes(config_bag, + dynoption, + ): yield child.to_dynoption(subpath, suffix, child, ) + elif dyn and child.issubdyn() or child.impl_is_dynsymlinkoption(): + yield child.to_dynoption(subpath, + option_suffix, + child, + ) else: yield child @@ -320,6 +355,13 @@ class OptionDescription(OptionDescriptionWalk): # the group_type is useful for filtering OptionDescriptions in a config self._group_type = groups.default + def _setsubdyn(self, + subdyn, + ) -> None: + for child in self._children[1]: + child._setsubdyn(subdyn) + super()._setsubdyn(subdyn) + def impl_is_optiondescription(self) -> bool: """the option is an option description """ @@ -367,6 +409,11 @@ class OptionDescription(OptionDescriptionWalk): ori_dyn) -> SynDynOptionDescription: """get syn dyn option description """ + if suffix is None: + return SubDynOptionDescription(self, + rootpath, + ori_dyn, + ) return SynDynOptionDescription(self, rootpath, suffix, diff --git a/tiramisu/option/syndynoption.py b/tiramisu/option/syndynoption.py index a61b530..407cc76 100644 --- a/tiramisu/option/syndynoption.py +++ b/tiramisu/option/syndynoption.py @@ -58,7 +58,7 @@ class SynDynOption: def impl_get_display_name(self) -> str: """get option display name """ - suffix = self.dyn_parent.convert_suffix_to_path(self.suffix) + suffix = self.dyn_parent.getsubdyn().convert_suffix_to_path(self.suffix) return self.opt._get_display_name(dyn_name=self.impl_getname(), # pylint: disable=protected-access suffix=suffix, ) diff --git a/tiramisu/option/syndynoptiondescription.py b/tiramisu/option/syndynoptiondescription.py index 4e98a2e..fab5841 100644 --- a/tiramisu/option/syndynoptiondescription.py +++ b/tiramisu/option/syndynoptiondescription.py @@ -25,11 +25,75 @@ from typing import Optional, Iterator, Any, List from ..i18n import _ -from ..setting import ConfigBag +from ..setting import ConfigBag, undefined from .baseoption import BaseOption from .syndynoption import SynDynOption +class SubDynOptionDescription: + __slots__ = ('rootpath', + 'opt', + 'dyn_parent', + '__weakref__', + ) + + def __init__(self, + opt: BaseOption, + rootpath: str, + dyn_parent, + ) -> None: + self.opt = opt + self.rootpath = rootpath + self.dyn_parent = dyn_parent + + def impl_getpath(self) -> str: + """get path + """ + path = self.opt.impl_getname() + if self.rootpath: + path = f'{self.rootpath}.{path}' + return path + + def get_sub_children(self, + option, + config_bag, + *, + index=None, + properties=undefined, + ): + return self.opt.get_sub_children(option, + config_bag, + index=index, + properties=properties, + dynoption=self, + ) + + def getsubdyn(self): + return self.opt.getsubdyn() + + def impl_is_optiondescription(self): + return True + + def impl_is_dynsymlinkoption(self): + return True + + def impl_is_sub_dyn_optiondescription(self): + return True + + def impl_get_display_name(self) -> str: + return self.opt.impl_get_display_name() + + def impl_is_dynoptiondescription(self) -> bool: + return True + + def to_dynoption(self, + rootpath: str, + suffix: str, + ori_dyn, + ): + return self.opt.to_dynoption(rootpath, suffix, ori_dyn) + + class SynDynOptionDescription: """SynDynOptionDescription internal option, it's an instanciate synoptiondescription """ @@ -57,27 +121,6 @@ class SynDynOptionDescription: name, ) - def get_child(self, - name: str, - config_bag: ConfigBag, - subpath: str, - ) -> BaseOption: - """get child by name - """ - # pylint: disable=unused-argument - try: - child = self._children[1][self._children[0].index(name)] - except ValueError: - # when name not in self._children - pass - else: - return child.to_dynoption(subpath, - self._suffix, - self.ori_dyn) - raise AttributeError(_('unknown option "{0}" ' - 'in dynamic optiondescription "{1}"' - '').format(name, self.impl_get_display_name())) - def impl_getname(self) -> str: """get name """ @@ -98,14 +141,40 @@ class SynDynOptionDescription: # pylint: disable=unused-argument """get children """ - subpath = self.impl_getpath() - children = [] - for child in self.opt.get_children(config_bag): - children.append(child.to_dynoption(subpath, - self._suffix, - self.ori_dyn, - )) - return children + yield from self.opt.get_children(config_bag, + dynoption=self, + option_suffix=self._suffix, + ) + + def get_child(self, + name: str, + config_bag: ConfigBag, + subpath: str, + allow_dynoption: bool=False, + ): + """get children + """ + return self.opt.get_child(name, + config_bag, + subpath, + dynoption=self, + option_suffix=self._suffix, + allow_dynoption=allow_dynoption, + ) + + def get_sub_children(self, + option, + config_bag, + *, + index=None, + properties=undefined, + ): + return self.opt.get_sub_children(option, + config_bag, + index=index, + properties=properties, + dynoption=self, + ) def impl_is_dynsymlinkoption(self) -> bool: """it's a dynsymlinkoption @@ -136,6 +205,11 @@ class SynDynOptionDescription: path = f'{self.rootpath}.{path}' return path + def impl_getsuffix(self) -> str: + """get suffix + """ + return self._suffix + class SynDynLeadership(SynDynOptionDescription): """SynDynLeadership internal option, it's an instanciate synoptiondescription diff --git a/tiramisu/value.py b/tiramisu/value.py index ee2682d..b452e60 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -373,7 +373,7 @@ class Values: for index in indexes: for coption_bag in option.get_sub_children(coption, option_bag.config_bag, - index, + index=index, properties=frozenset(), ): default_value = [self.get_value(coption_bag)[0], owners.forced]