dynoptiondescription inside dynoptiondescription

This commit is contained in:
egarette@silique.fr 2023-12-04 17:46:46 +01:00
parent 6b473e63f2
commit 90564d4983
13 changed files with 937 additions and 824 deletions

View file

@ -38,8 +38,7 @@ def return_list2(suffix):
def return_list(val=None, suffix=None): def return_list(val=None, suffix=None):
if val: if val:
return val return val
else: return ['val1', 'val2']
return ['val1', 'val2']
def return_list_dot(val=None, suffix=None): def return_list_dot(val=None, suffix=None):
@ -335,7 +334,7 @@ def test_callback_dyndescription_outside1():
lst = StrOption('lst', '', ['val1', 'val2'], multi=True) lst = StrOption('lst', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '', Calculation(return_dynval)) st = StrOption('st', '', Calculation(return_dynval))
dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list, Params(ParamOption(lst)))) 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]) od = OptionDescription('od', '', [dod, out])
od2 = OptionDescription('od', '', [od, lst]) od2 = OptionDescription('od', '', [od, lst])
cfg = Config(od2) cfg = Config(od2)
@ -1628,15 +1627,6 @@ def test_invalid_conflict_dyndescription():
# assert not list_sessions() # 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(): def test_leadership_default_multi_dyndescription():
st1 = StrOption('st1', "", multi=True) st1 = StrOption('st1', "", multi=True)
st2 = StrOption('st2', "", multi=True, default_multi='no') st2 = StrOption('st2', "", multi=True, default_multi='no')
@ -1671,15 +1661,223 @@ def test_leadership_default_multi_dyndescription():
# assert not list_sessions() # assert not list_sessions()
def test_invalid_subdynod_dyndescription(): def test_subdynod_dyndescription_root():
st2 = StrOption('st2', '') st2 = StrOption('st2', '')
od1 = DynOptionDescription('od1', '', [st2], suffixes=Calculation(return_list)) dod1 = DynOptionDescription('dod1', '', [st2], suffixes=Calculation(return_list, Params(ParamValue(['a', 'b']))))
od1 dod = DynOptionDescription('dod', '', [dod1], suffixes=Calculation(return_list))
with pytest.raises(ConfigError): st3 = StrOption('st3', '', Calculation(return_dynval, Params(ParamDynOption(st2, 'dodval1.dod1a.st2', dod))))
DynOptionDescription('dod', '', [od1], suffixes=Calculation(return_list)) # 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() # 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(): def test_invalid_symlink_dyndescription():
st = StrOption('st', '') st = StrOption('st', '')
st2 = SymLinkOption('st2', st) st2 = SymLinkOption('st2', st)

View file

@ -270,7 +270,7 @@ class _TiramisuOptionOptionDescription:
"""Get dependencies from this option""" """Get dependencies from this option"""
option_bag = options_bag[-1] option_bag = options_bag[-1]
options = [] 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(), options.append(TiramisuOption(option().impl_getpath(),
None, None,
self._config_bag, self._config_bag,
@ -1039,8 +1039,8 @@ class TiramisuContextProperty(TiramisuConfig):
return self._config_bag.properties return self._config_bag.properties
def _set(self, def _set(self,
props, props,
): ):
"""Personalise config properties""" """Personalise config properties"""
if 'force_store_value' in props: if 'force_store_value' in props:
force_store_value = 'force_store_value' not in self._config_bag.properties force_store_value = 'force_store_value' not in self._config_bag.properties

View file

@ -82,10 +82,10 @@ class ParamOption(Param):
class ParamDynOption(ParamOption): class ParamDynOption(ParamOption):
__slots__ = ('suffix',) __slots__ = ('subpath',)
def __init__(self, def __init__(self,
option: 'Option', option: 'Option',
suffix: str, subpath: str,
dynoptiondescription: 'DynOptionDescription', dynoptiondescription: 'DynOptionDescription',
notraisepropertyerror: bool=False, notraisepropertyerror: bool=False,
raisepropertyerror: bool=False, raisepropertyerror: bool=False,
@ -95,7 +95,7 @@ class ParamDynOption(ParamOption):
notraisepropertyerror, notraisepropertyerror,
raisepropertyerror, raisepropertyerror,
) )
self.suffix = suffix self.subpath = subpath
self.dynoptiondescription = dynoptiondescription self.dynoptiondescription = dynoptiondescription
self.optional = optional self.optional = optional
@ -430,22 +430,65 @@ def manager_callback(callback: Callable,
if callbk_option.issubdyn(): if callbk_option.issubdyn():
found = False found = False
if isinstance(param, ParamDynOption): if isinstance(param, ParamDynOption):
soption_bag = param.dynoptiondescription.get_sub_child(callbk_option, od_path = param.dynoptiondescription.impl_getpath()
param.suffix, if "." in od_path:
config_bag, rootpath = od_path.rsplit('.', 1)[0] + '.'
) else:
callbk_option = soption_bag.option 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 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(): elif option.impl_is_dynsymlinkoption():
rootpath = option.rootpath rootpath = option.rootpath
call_path = callbk_option.impl_getpath() 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 # in same dynoption
if len(callbk_option.impl_getpath().split('.')) == len(rootpath.split('.')):
rootpath = rootpath.rsplit('.', 1)[0]
suffix = option.impl_getsuffix() suffix = option.impl_getsuffix()
subdyn = callbk_option.getsubdyn() 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, suffix,
subdyn, subdyn,
) )
@ -454,7 +497,7 @@ def manager_callback(callback: Callable,
callbk_options = [] callbk_options = []
for doption_bag in callbk_option.getsubdyn().get_sub_children(callbk_option, for doption_bag in callbk_option.getsubdyn().get_sub_children(callbk_option,
config_bag, config_bag,
None, index=None,
): ):
callbk_options.append(doption_bag.option) callbk_options.append(doption_bag.option)
if callbk_options is None: if callbk_options is None:
@ -476,6 +519,8 @@ def manager_callback(callback: Callable,
else: else:
index_ = None index_ = None
with_index = False with_index = False
if callbk_option.impl_getpath() == 'od.dodval1.st.boolean':
raise Exception('pfff')
value = get_value(config_bag, value = get_value(config_bag,
callbk_option, callbk_option,
param, param,

View file

@ -27,6 +27,7 @@ from typing import Optional, List, Any, Union
from .error import PropertiesOptionError, ConfigError, ConflictError, \ from .error import PropertiesOptionError, ConfigError, ConflictError, \
LeadershipError LeadershipError
from .option import DynOptionDescription, Leadership, Option from .option import DynOptionDescription, Leadership, Option
from .option.syndynoptiondescription import SubDynOptionDescription
from .setting import OptionBag, ConfigBag, Settings, undefined, groups from .setting import OptionBag, ConfigBag, Settings, undefined, groups
from .value import Values, owners from .value import Values, owners
from .i18n import _ from .i18n import _
@ -130,26 +131,74 @@ class _SubConfig:
if option_bag.path in resetted_opts: if option_bag.path in resetted_opts:
return return
resetted_opts.append(option_bag.path) 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() option = woption()
soption_bag = OptionBag(option, if woption in option_bag.option._get_suffixes_dependencies() and \
option_bag.index, option_bag.option.issubdyn() and \
option_bag.config_bag, option.impl_is_dynoptiondescription():
properties=None, paths = [subdyn().impl_getpath() for subdyn in option.get_sub_dyns()]
) for weak_subdyn in option_bag.option.get_sub_dyns():
if option.impl_is_dynoptiondescription(): subdyn = weak_subdyn()
self._reset_cache_dyn_optiondescription(soption_bag, if subdyn.impl_getpath() in paths:
resetted_opts, root_path = option_bag.option.impl_getpath()
) if '.' in root_path:
elif option.issubdyn(): root_path = root_path.rsplit('.', 1)[0]
# it's an option in dynoptiondescription, remove cache for all generated option nb_elt = root_path.count('.') + 1
self._reset_cache_dyn_option(soption_bag, else:
resetted_opts, 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: else:
self.reset_one_option_cache(resetted_opts, soption_bag = OptionBag(option,
soption_bag, 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 del option
option_bag.option.reset_cache(option_bag.path, option_bag.option.reset_cache(option_bag.path,
option_bag.config_bag, option_bag.config_bag,
@ -163,7 +212,7 @@ class _SubConfig:
# reset cache for all chidren # reset cache for all chidren
for doption_bag in option_bag.option.get_sub_children(option_bag.option, for doption_bag in option_bag.option.get_sub_children(option_bag.option,
option_bag.config_bag, option_bag.config_bag,
option_bag.index, index=option_bag.index,
properties=None, properties=None,
): ):
for coption in doption_bag.option.get_children_recursively(None, for coption in doption_bag.option.get_children_recursively(None,
@ -187,13 +236,13 @@ class _SubConfig:
resetted_opts, resetted_opts,
): ):
option = option_bag.option option = option_bag.option
if isinstance(option, DynOptionDescription): if isinstance(option, (DynOptionDescription, SubDynOptionDescription)):
dynoption = option dynoption = option
else: else:
dynoption = option.getsubdyn() dynoption = option.getsubdyn()
for doption_bag in dynoption.get_sub_children(option, for doption_bag in dynoption.get_sub_children(option,
option_bag.config_bag, option_bag.config_bag,
option_bag.index, index=option_bag.index,
properties=None properties=None
): ):
self.reset_one_option_cache(resetted_opts, self.reset_one_option_cache(resetted_opts,
@ -467,7 +516,7 @@ class _SubConfig:
dynopt = suboption.getsubdyn() dynopt = suboption.getsubdyn()
return list(dynopt.get_sub_children(suboption, return list(dynopt.get_sub_children(suboption,
option_bag.config_bag, option_bag.config_bag,
option_bag.index, index=option_bag.index,
)) ))
if suboption.impl_is_follower(): if suboption.impl_is_follower():
options_bag = self.get_sub_option_bag(option_bag.config_bag, # pylint: disable=no-member 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], index: Optional[int],
validate_properties: bool, validate_properties: bool,
leadership_length: int=None, leadership_length: int=None,
*,
properties=undefined, properties=undefined,
follower_not_apply_requires: bool=False, follower_not_apply_requires: bool=False,
allow_dynoption: bool=False,
) -> List[OptionBag]: ) -> List[OptionBag]:
"""Get the suboption for path and the name of the option """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 # pylint: disable=too-many-branches,too-many-locals,too-many-arguments
if isinstance(bag, ConfigBag): if isinstance(bag, ConfigBag):
option_bag = OptionBag(self.get_description(), option_bag = OptionBag(self.get_description(),
@ -710,6 +762,7 @@ class _CommonConfig(_SubConfig):
option = suboption.get_child(step, option = suboption.get_child(step,
option_bag.config_bag, option_bag.config_bag,
subpath, subpath,
allow_dynoption=allow_dynoption,
) )
if idx == last_idx: if idx == last_idx:
option_index = index option_index = index

View file

@ -551,7 +551,7 @@ msgstr "ne doit pas être une IP"
#: tiramisu/option/domainnameoption.py:139 #: tiramisu/option/domainnameoption.py:139
msgid "must have dot" msgid "must have dot"
msgstr "doit avec un point" msgstr "doit avoir un point"
#: tiramisu/option/domainnameoption.py:141 #: tiramisu/option/domainnameoption.py:141
msgid "invalid length (max 255)" msgid "invalid length (max 255)"

File diff suppressed because it is too large Load diff

View file

@ -53,7 +53,7 @@ class Base:
__slots__ = ('_name', __slots__ = ('_name',
'_path', '_path',
'_informations', '_informations',
'_subdyn', '_subdyns',
'_properties', '_properties',
'_has_dependency', '_has_dependency',
'_dependencies', '_dependencies',
@ -106,9 +106,9 @@ class Base:
return getattr(self, '_has_dependency', False) return getattr(self, '_has_dependency', False)
return hasattr(self, '_dependencies') return hasattr(self, '_dependencies')
def _get_dependencies(self, def get_dependencies(self,
context_od, context_od,
) -> Set[str]: ) -> Set[str]:
ret = set(getattr(self, '_dependencies', STATIC_TUPLE)) ret = set(getattr(self, '_dependencies', STATIC_TUPLE))
if context_od and hasattr(context_od, '_dependencies'): if context_od and hasattr(context_od, '_dependencies'):
# add options that have context is set in calculation # add options that have context is set in calculation
@ -123,7 +123,7 @@ class Base:
is_suffix: bool=False, is_suffix: bool=False,
) -> None: ) -> None:
woption = weakref.ref(option) woption = weakref.ref(option)
options = self._get_dependencies(None) options = self.get_dependencies(None)
options.add(woption) options.add(woption)
self._dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init self._dependencies = tuple(options) # pylint: disable=attribute-defined-outside-init
if is_suffix: if is_suffix:
@ -141,6 +141,9 @@ class Base:
""" """
return False return False
def impl_is_sub_dyn_optiondescription(self):
return False
def impl_getname(self) -> str: def impl_getname(self) -> str:
"""get name """get name
""" """
@ -175,17 +178,22 @@ class Base:
subdyn, subdyn,
) -> None: ) -> None:
# pylint: disable=attribute-defined-outside-init # 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: def issubdyn(self) -> bool:
"""is sub dynoption """is sub dynoption
""" """
return getattr(self, '_subdyn', None) is not None return getattr(self, '_subdyns', None) is not None
def getsubdyn(self): def getsubdyn(self):
"""get sub dynoption """get sub dynoption
""" """
return self._subdyn() return self._subdyns[0]()
def get_sub_dyns(self):
return self._subdyns
# ____________________________________________________________ # ____________________________________________________________
# information # information
@ -251,7 +259,8 @@ class BaseOption(Base):
def __setattr__(self, def __setattr__(self,
name: str, name: str,
value: Any) -> Any: value: Any,
) -> Any:
"""set once and only once some attributes in the option, """set once and only once some attributes in the option,
like `_name`. `_name` cannot be changed once the option is like `_name`. `_name` cannot be changed once the option is
pushed in the :class:`tiramisu.option.OptionDescription`. pushed in the :class:`tiramisu.option.OptionDescription`.

View file

@ -22,13 +22,14 @@
""" """
import re import re
import weakref import weakref
from typing import List, Any, Optional from typing import List, Any, Optional, Tuple
from itertools import chain from itertools import chain
from ..autolib import ParamOption from ..autolib import ParamOption
from ..i18n import _ from ..i18n import _
from .optiondescription import OptionDescription from .optiondescription import OptionDescription
from .syndynoptiondescription import SynDynLeadership
from .baseoption import BaseOption from .baseoption import BaseOption
from ..setting import OptionBag, ConfigBag, undefined from ..setting import OptionBag, ConfigBag, undefined
from ..error import ConfigError from ..error import ConfigError
@ -41,7 +42,9 @@ NAME_REGEXP = re.compile(r'^[a-zA-Z\d\-_]*$')
class DynOptionDescription(OptionDescription): class DynOptionDescription(OptionDescription):
"""dyn option description """dyn option description
""" """
__slots__ = ('_suffixes',) __slots__ = ('_suffixes',
'_subdyns',
)
def __init__(self, def __init__(self,
name: str, name: str,
@ -85,10 +88,15 @@ class DynOptionDescription(OptionDescription):
def get_suffixes(self, def get_suffixes(self,
config_bag: ConfigBag, config_bag: ConfigBag,
dynoption=None,
) -> List[str]: ) -> List[str]:
"""get dynamic suffixes """get dynamic suffixes
""" """
option_bag = OptionBag(self, if dynoption:
self_opt = dynoption
else:
self_opt = self
option_bag = OptionBag(self_opt,
None, None,
config_bag, config_bag,
properties=None, properties=None,
@ -119,70 +127,69 @@ class DynOptionDescription(OptionDescription):
def impl_is_dynoptiondescription(self) -> bool: def impl_is_dynoptiondescription(self) -> bool:
return True 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, def get_sub_children(self,
option, option,
config_bag, config_bag,
*,
index=None, index=None,
properties=undefined, properties=undefined,
dynoption=None,
): ):
rootpath = self.get_root_path(option) root_path, sub_path = self.split_path(dynoption,
subpath = self.get_sub_path(option) option,
for suffix in self.get_suffixes(config_bag): )
yield self.get_sub_child(option, for suffix in self.get_suffixes(config_bag,
suffix, dynoption=dynoption,
config_bag, ):
index=index, if self.option_is_self(option):
rootpath=rootpath, parent_path = root_path
subpath=subpath, elif root_path:
properties=properties, parent_path = root_path + self.impl_getname(suffix) + sub_path
) else:
parent_path = self.impl_getname(suffix) + sub_path
def get_root_path(self, option): yield OptionBag(option.to_dynoption(parent_path,
path = self.impl_getpath() suffix,
rootpath = path.rsplit('.', 1)[0] if '.' in path else '' self,
if option != self: ),
if rootpath: index,
rootpath += '.' config_bag,
return rootpath properties=properties,
ori_option=option
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: def impl_getname(self, suffix=None) -> str:
"""get name """get name

View file

@ -106,10 +106,9 @@ class Leadership(OptionDescription):
def _setsubdyn(self, def _setsubdyn(self,
subdyn, subdyn,
) -> None: ) -> None:
# pylint: disable=attribute-defined-outside-init,protected-access
for chld in self._children[1]: for chld in self._children[1]:
chld._setsubdyn(subdyn) chld._setsubdyn(subdyn)
self._subdyn = subdyn super()._setsubdyn(subdyn)
def is_leader(self, def is_leader(self,
opt: Option, opt: Option,

View file

@ -20,13 +20,14 @@
# ____________________________________________________________ # ____________________________________________________________
"""OptionDescription """OptionDescription
""" """
import weakref
from typing import Optional, Iterator, Union, List from typing import Optional, Iterator, Union, List
from ..i18n import _ from ..i18n import _
from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined from ..setting import ConfigBag, OptionBag, groups, undefined, owners, Undefined
from .baseoption import BaseOption from .baseoption import BaseOption
from .syndynoptiondescription import SynDynOptionDescription from .syndynoptiondescription import SubDynOptionDescription, SynDynOptionDescription
from ..error import ConfigError, ConflictError from ..error import ConfigError, ConflictError
@ -127,15 +128,14 @@ class CacheOptionDescription(BaseOption):
dynopt = option.getsubdyn() dynopt = option.getsubdyn()
yield from dynopt.get_sub_children(option, yield from dynopt.get_sub_children(option,
config_bag, config_bag,
None, index=None,
) )
else: else:
option_bag = OptionBag(option, yield OptionBag(option,
None, None,
config_bag, config_bag,
properties=None, properties=None,
) )
yield option_bag
if 'force_store_value' not in config_bag.properties: if 'force_store_value' not in config_bag.properties:
return return
values = config_bag.context.get_values() values = config_bag.context.get_values()
@ -194,6 +194,10 @@ class OptionDescriptionWalk(CacheOptionDescription):
name: str, name: str,
config_bag: ConfigBag, config_bag: ConfigBag,
subpath: str, subpath: str,
*,
dynoption=None,
option_suffix: Optional[str]=None,
allow_dynoption: bool=False,
) -> Union[BaseOption, SynDynOptionDescription]: ) -> Union[BaseOption, SynDynOptionDescription]:
"""get a child """get a child
""" """
@ -201,18 +205,32 @@ class OptionDescriptionWalk(CacheOptionDescription):
if name in self._children[0]: # pylint: disable=no-member if name in self._children[0]: # pylint: disable=no-member
option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member option = self._children[1][self._children[0].index(name)] # pylint: disable=no-member
if option.impl_is_dynoptiondescription(): if option.impl_is_dynoptiondescription():
raise AttributeError(_(f'unknown option "{name}" ' if allow_dynoption:
"in root optiondescription (it's a dynamic option)" 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 return option
# if dyn # if dyn
if dynoption:
self_opt = dynoption
else:
self_opt = self
for child in self._children[1]: # pylint: disable=no-member for child in self._children[1]: # pylint: disable=no-member
if not child.impl_is_dynoptiondescription(): if not child.impl_is_dynoptiondescription():
continue continue
cname = child.impl_getname() cname = child.impl_getname()
if not name.startswith(cname): if not name.startswith(cname):
continue 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): if name != cname + child.convert_suffix_to_path(suffix):
continue continue
return child.to_dynoption(subpath, return child.to_dynoption(subpath,
@ -229,22 +247,39 @@ class OptionDescriptionWalk(CacheOptionDescription):
def get_children(self, def get_children(self,
config_bag: Union[ConfigBag, Undefined], config_bag: Union[ConfigBag, Undefined],
*,
dyn: bool=True, dyn: bool=True,
#path: Optional[str]=None,
dynoption=None,
option_suffix: Optional[str]=None,
) -> Union[BaseOption, SynDynOptionDescription]: ) -> Union[BaseOption, SynDynOptionDescription]:
"""get children """get children
""" """
# if path:
# subpath = path
if dynoption:
self_opt = dynoption
else:
self_opt = self
if not dyn or config_bag is undefined or \ if not dyn or config_bag is undefined or \
config_bag.context.get_description() == self: config_bag.context.get_description() == self:
subpath = '' subpath = ''
else: else:
subpath = self.impl_getpath() subpath = self_opt.impl_getpath()
for child in self._children[1]: # pylint: disable=no-member for child in self._children[1]: # pylint: disable=no-member
if dyn and child.impl_is_dynoptiondescription(): 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, yield child.to_dynoption(subpath,
suffix, suffix,
child, child,
) )
elif dyn and child.issubdyn() or child.impl_is_dynsymlinkoption():
yield child.to_dynoption(subpath,
option_suffix,
child,
)
else: else:
yield child yield child
@ -320,6 +355,13 @@ class OptionDescription(OptionDescriptionWalk):
# the group_type is useful for filtering OptionDescriptions in a config # the group_type is useful for filtering OptionDescriptions in a config
self._group_type = groups.default 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: def impl_is_optiondescription(self) -> bool:
"""the option is an option description """the option is an option description
""" """
@ -367,6 +409,11 @@ class OptionDescription(OptionDescriptionWalk):
ori_dyn) -> SynDynOptionDescription: ori_dyn) -> SynDynOptionDescription:
"""get syn dyn option description """get syn dyn option description
""" """
if suffix is None:
return SubDynOptionDescription(self,
rootpath,
ori_dyn,
)
return SynDynOptionDescription(self, return SynDynOptionDescription(self,
rootpath, rootpath,
suffix, suffix,

View file

@ -58,7 +58,7 @@ class SynDynOption:
def impl_get_display_name(self) -> str: def impl_get_display_name(self) -> str:
"""get option display name """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 return self.opt._get_display_name(dyn_name=self.impl_getname(), # pylint: disable=protected-access
suffix=suffix, suffix=suffix,
) )

View file

@ -25,11 +25,75 @@ from typing import Optional, Iterator, Any, List
from ..i18n import _ from ..i18n import _
from ..setting import ConfigBag from ..setting import ConfigBag, undefined
from .baseoption import BaseOption from .baseoption import BaseOption
from .syndynoption import SynDynOption 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: class SynDynOptionDescription:
"""SynDynOptionDescription internal option, it's an instanciate synoptiondescription """SynDynOptionDescription internal option, it's an instanciate synoptiondescription
""" """
@ -57,27 +121,6 @@ class SynDynOptionDescription:
name, 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: def impl_getname(self) -> str:
"""get name """get name
""" """
@ -98,14 +141,40 @@ class SynDynOptionDescription:
# pylint: disable=unused-argument # pylint: disable=unused-argument
"""get children """get children
""" """
subpath = self.impl_getpath() yield from self.opt.get_children(config_bag,
children = [] dynoption=self,
for child in self.opt.get_children(config_bag): option_suffix=self._suffix,
children.append(child.to_dynoption(subpath, )
self._suffix,
self.ori_dyn, def get_child(self,
)) name: str,
return children 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: def impl_is_dynsymlinkoption(self) -> bool:
"""it's a dynsymlinkoption """it's a dynsymlinkoption
@ -136,6 +205,11 @@ class SynDynOptionDescription:
path = f'{self.rootpath}.{path}' path = f'{self.rootpath}.{path}'
return path return path
def impl_getsuffix(self) -> str:
"""get suffix
"""
return self._suffix
class SynDynLeadership(SynDynOptionDescription): class SynDynLeadership(SynDynOptionDescription):
"""SynDynLeadership internal option, it's an instanciate synoptiondescription """SynDynLeadership internal option, it's an instanciate synoptiondescription

View file

@ -373,7 +373,7 @@ class Values:
for index in indexes: for index in indexes:
for coption_bag in option.get_sub_children(coption, for coption_bag in option.get_sub_children(coption,
option_bag.config_bag, option_bag.config_bag,
index, index=index,
properties=frozenset(), properties=frozenset(),
): ):
default_value = [self.get_value(coption_bag)[0], owners.forced] default_value = [self.get_value(coption_bag)[0], owners.forced]