can convert suffix to build path for dynoptiondescription

This commit is contained in:
Emmanuel Garette 2019-12-21 18:29:21 +01:00
parent 0cb69f7a85
commit 76e7fd93b2
9 changed files with 195 additions and 43 deletions

View File

@ -14,6 +14,12 @@ from tiramisu.error import PropertiesOptionError, ConfigError, ConflictError
from tiramisu.storage import list_sessions
class ConvertDynOptionDescription(DynOptionDescription):
def convert_suffix_to_path(self, suffix):
# remove dot with is illegal
return suffix.replace('.', '')
def teardown_function(function):
assert list_sessions() == [], 'session list is not empty when leaving "{}"'.format(function.__name__)
@ -39,6 +45,10 @@ def return_list(val=None, suffix=None):
return ['val1', 'val2']
def return_list_dot(val=None, suffix=None):
return ['val.1', 'val.2']
def return_same_list(*args, **kwargs):
return ['val1', 'val1']
@ -911,6 +921,7 @@ def test_leadership_default_multi_dyndescription():
assert api.option('od.stval1.st1val1.st2val1', 0).owner.isdefault()
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
def test_leadership_dyndescription_param():
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
odval = OptionDescription('odval1', '', [val1])
@ -1259,11 +1270,11 @@ def test_leadership_callback_samegroup_dyndescription():
api = Config(od2)
owner = api.owner.get()
assert api.value.dict() == {'od.stval1.st1val1.st1val1': [],
'od.stval1.st1val1.st2val1': [],
'od.stval1.st1val1.st3val1': [],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
'od.stval1.st1val1.st2val1': [],
'od.stval1.st1val1.st3val1': [],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').value.get() == []
assert api.option('od.stval2.st1val2.st1val2').value.get() == []
assert api.option('od.stval1.st1val1.st1val1').owner.isdefault()
@ -1271,11 +1282,11 @@ def test_leadership_callback_samegroup_dyndescription():
#
api.option('od.stval1.st1val1.st1val1').value.set(['yes'])
assert api.value.dict() == {'od.stval1.st1val1.st1val1': ['yes'],
'od.stval1.st1val1.st2val1': [None],
'od.stval1.st1val1.st3val1': [None],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
'od.stval1.st1val1.st2val1': [None],
'od.stval1.st1val1.st3val1': [None],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval1.st1val1.st2val1', 0).owner.isdefault()
assert api.option('od.stval1.st1val1.st3val1', 0).owner.isdefault()
@ -1283,11 +1294,11 @@ def test_leadership_callback_samegroup_dyndescription():
#
api.option('od.stval1.st1val1.st2val1', 0).value.set('yes')
assert api.value.dict() == {'od.stval1.st1val1.st1val1': ['yes'],
'od.stval1.st1val1.st2val1': ['yes'],
'od.stval1.st1val1.st3val1': ['yes'],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
'od.stval1.st1val1.st2val1': ['yes'],
'od.stval1.st1val1.st3val1': ['yes'],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval1.st1val1.st2val1', 0).owner.get() == owner
assert api.option('od.stval1.st1val1.st3val1', 0).owner.isdefault()
@ -1344,3 +1355,107 @@ def test_invalid_name_dyndescription():
od1 = OptionDescription('od', '', [dod])
cfg = Config(od1)
raises(ValueError, "cfg.value.dict()")
def test_leadership_dyndescription_convert():
st1 = StrOption('st1', "", multi=True)
st2 = StrOption('st2', "", multi=True)
stm = Leadership('st1', '', [st1, st2])
st = ConvertDynOptionDescription('st', '', [stm], suffixes=Calculation(return_list_dot))
od = OptionDescription('od', '', [st])
od2 = OptionDescription('od', '', [od])
api = Config(od2)
owner = api.owner.get()
#
assert api.value.dict() == {'od.stval1.st1val1.st2val1': [], 'od.stval2.st1val2.st2val2': [], 'od.stval2.st1val2.st1val2': [], 'od.stval1.st1val1.st1val1': []}
assert api.option('od.stval1.st1val1.st1val1').value.get() == []
assert api.option('od.stval2.st1val2.st1val2').value.get() == []
assert api.option('od.stval1.st1val1.st1val1').owner.isdefault()
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
#
api.option('od.stval1.st1val1.st1val1').value.set(['yes'])
assert api.value.dict() == {'od.stval1.st1val1.st2val1': [None], 'od.stval2.st1val2.st2val2': [], 'od.stval2.st1val2.st1val2': [], 'od.stval1.st1val1.st1val1': ['yes']}
assert api.option('od.stval1.st1val1.st1val1').value.get() == ['yes']
assert api.option('od.stval1.st1val1.st2val1', 0).value.get() == None
assert api.option('od.stval2.st1val2.st1val2').value.get() == []
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval1.st1val1.st2val1', 0).owner.isdefault()
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
#
api.option('od.stval1.st1val1.st2val1', 0).value.set('no')
assert api.option('od.stval1.st1val1.st1val1').value.get() == ['yes']
assert api.option('od.stval1.st1val1.st2val1', 0).value.get() == 'no'
assert api.option('od.stval2.st1val2.st1val2').value.get() == []
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval1.st1val1.st2val1', 0).owner.get() == owner
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
#
api.option('od.stval1.st1val1.st1val1').value.pop(0)
assert api.option('od.stval1.st1val1.st1val1').value.get() == []
assert api.option('od.stval2.st1val2.st1val2').value.get() == []
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
#
api.option('od.stval1.st1val1.st1val1').value.set(['yes'])
api.option('od.stval1.st1val1.st2val1', 0).value.set('yes')
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval1.st1val1.st2val1', 0).owner.get() == owner
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
api.option('od.stval1.st1val1.st2val1', 0).value.reset()
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval1.st1val1.st2val1', 0).owner.isdefault()
#
api.option('od.stval1.st1val1.st1val1').value.set(['yes'])
api.option('od.stval1.st1val1.st2val1', 0).value.set('yes')
api.option('od.stval1.st1val1.st1val1').value.reset()
assert api.option('od.stval1.st1val1.st1val1').value.get() == []
assert api.option('od.stval2.st1val2.st1val2').value.get() == []
assert api.option('od.stval1.st1val1.st1val1').owner.isdefault()
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
def test_leadership_callback_samegroup_dyndescription_convert():
st1 = StrOption('st1', "", multi=True)
st2 = StrOption('st2', "", multi=True)
st3 = StrOption('st3', "", Calculation(return_dynval, Params(ParamOption(st2))), multi=True)
stm = Leadership('st1', '', [st1, st2, st3])
stt = ConvertDynOptionDescription('st', '', [stm], suffixes=Calculation(return_list_dot))
od1 = OptionDescription('od', '', [stt])
od2 = OptionDescription('od', '', [od1])
api = Config(od2)
owner = api.owner.get()
assert api.value.dict() == {'od.stval1.st1val1.st1val1': [],
'od.stval1.st1val1.st2val1': [],
'od.stval1.st1val1.st3val1': [],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').value.get() == []
assert api.option('od.stval2.st1val2.st1val2').value.get() == []
assert api.option('od.stval1.st1val1.st1val1').owner.isdefault()
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
#
api.option('od.stval1.st1val1.st1val1').value.set(['yes'])
assert api.value.dict() == {'od.stval1.st1val1.st1val1': ['yes'],
'od.stval1.st1val1.st2val1': [None],
'od.stval1.st1val1.st3val1': [None],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval1.st1val1.st2val1', 0).owner.isdefault()
assert api.option('od.stval1.st1val1.st3val1', 0).owner.isdefault()
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
#
api.option('od.stval1.st1val1.st2val1', 0).value.set('yes')
assert api.value.dict() == {'od.stval1.st1val1.st1val1': ['yes'],
'od.stval1.st1val1.st2val1': ['yes'],
'od.stval1.st1val1.st3val1': ['yes'],
'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner
assert api.option('od.stval1.st1val1.st2val1', 0).owner.get() == owner
assert api.option('od.stval1.st1val1.st3val1', 0).owner.isdefault()
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()

View File

@ -279,7 +279,8 @@ def manager_callback(callbk: Union[ParamOption, ParamValue],
callbk_option = callbk.option
if callbk_option.issubdyn():
callbk_option = callbk_option.to_dynoption(option.rootpath,
option.impl_getsuffix())
option.impl_getsuffix(),
callbk_option.getsubdyn())
if leadership_must_have_index and callbk_option.impl_get_leadership() and index is None:
raise Break()
if config_bag is undefined:

View File

@ -110,10 +110,11 @@ class SubConfig(object):
for woption in option_bag.option._get_dependencies(self.cfgimpl_get_description()):
option = woption()
if option.impl_is_dynoptiondescription():
subpath = option.impl_getpath().rsplit('.', 1)[0]
# it's a dynoptiondescription remove cache for all generated optiondescription
for suffix in option.get_suffixes(option_bag.config_bag):
doption = option.to_dynoption(subpath,
suffix)
doption = option.to_dynoption(desc.impl_getpath(),
suffix,
option)
doption_path = doption.impl_getpath()
doption_bag = OptionBag()
doption_bag.set_option(doption,
@ -124,13 +125,16 @@ class SubConfig(object):
resetted_opts,
doption_bag)
elif option.issubdyn():
# it's an option in dynoptiondescription, remove cache for all generated option
dynopt = option.getsubdyn()
rootpath = dynopt.impl_getpath()
subpaths = [rootpath] + option.impl_getpath()[len(rootpath) + 1:].split('.')[:-1]
for suffix in dynopt.get_suffixes(option_bag.config_bag):
subpath = '.'.join([subp + suffix for subp in subpaths])
path_suffix = dynopt.convert_suffix_to_path(suffix)
subpath = '.'.join([subp + path_suffix for subp in subpaths])
doption = option.to_dynoption(subpath,
suffix)
suffix,
dynopt)
doption_path = doption.impl_getpath()
doption_bag = OptionBag()
doption_bag.set_option(doption,

View File

@ -63,6 +63,10 @@ class DynOptionDescription(OptionDescription):
if __debug__ and isinstance(suffixes, Calculation):
self._suffixes = suffixes
def convert_suffix_to_path(self,
suffix):
return suffix
def get_suffixes(self,
config_bag: ConfigBag) -> List[str]:
@ -78,6 +82,7 @@ class DynOptionDescription(OptionDescription):
'').format(self.impl_get_display_name(), values))
values_ = []
for val in values:
val = self.convert_suffix_to_path(val)
if not isinstance(val, str) or re.match(NAME_REGEXP, val) is None:
if val is not None:
raise ValueError(_('invalid suffix "{}" for option "{}"'

View File

@ -219,7 +219,9 @@ class Leadership(OptionDescription):
def to_dynoption(self,
rootpath: str,
suffix: str) -> SynDynLeadership:
suffix: str,
ori_dyn) -> SynDynLeadership:
return SynDynLeadership(self,
rootpath,
suffix)
suffix,
ori_dyn)

View File

@ -429,7 +429,9 @@ class Option(BaseOption):
def to_dynoption(self,
rootpath: str,
suffix: str) -> SynDynOption:
suffix: str,
ori_dyn) -> SynDynOption:
return SynDynOption(self,
rootpath,
suffix)
suffix,
ori_dyn)

View File

@ -175,9 +175,10 @@ class OptionDescriptionWalk(CacheOptionDescription):
cname = child.impl_getname()
if name.startswith(cname):
for suffix in child.get_suffixes(config_bag):
if name == cname + suffix:
if name == cname + child.convert_suffix_to_path(suffix):
return child.to_dynoption(subpath,
suffix)
suffix,
child)
if self.impl_get_group_type() == groups.root:
raise AttributeError(_('unknown option "{0}" '
'in root optiondescription'
@ -199,7 +200,8 @@ class OptionDescriptionWalk(CacheOptionDescription):
if dyn and child.impl_is_dynoptiondescription():
for suffix in child.get_suffixes(config_bag):
yield child.to_dynoption(subpath,
suffix)
suffix,
child)
else:
yield child
@ -305,7 +307,9 @@ class OptionDescription(OptionDescriptionWalk):
def to_dynoption(self,
rootpath: str,
suffix: str) -> SynDynOptionDescription:
suffix: str,
ori_dyn) -> SynDynOptionDescription:
return SynDynOptionDescription(self,
rootpath,
suffix)
suffix,
ori_dyn)

View File

@ -29,15 +29,18 @@ class SynDynOption:
__slots__ = ('rootpath',
'opt',
'suffix',
'ori_dyn',
'__weakref__')
def __init__(self,
opt: BaseOption,
rootpath: str,
suffix: str) -> None:
suffix: str,
ori_dyn) -> None:
self.opt = opt
self.rootpath = rootpath
self.suffix = suffix
self.ori_dyn = ori_dyn
def __getattr__(self,
name: str) -> Any:
@ -71,4 +74,5 @@ class SynDynOption:
leadership = self.opt.impl_get_leadership()
if leadership:
return leadership.to_dynoption(self.rootpath,
self.suffix)
self.suffix,
self.ori_dyn)

View File

@ -18,7 +18,7 @@
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
from typing import Optional, Iterator, Union, Any, List
from typing import Optional, Iterator, Any, List
from ..i18n import _
@ -31,18 +31,24 @@ from .syndynoption import SynDynOption
class SynDynOptionDescription:
__slots__ = ('_opt',
'_subpath',
'_suffix')
'_suffix',
'ori_dyn')
def __init__(self,
opt: BaseOption,
subpath: str,
suffix: str) -> None:
suffix: str,
ori_dyn) -> None:
if opt.__class__.__name__.startswith('L') and ori_dyn is None:
raise Exception()
self._opt = opt
if subpath is None:
subpath = ''
assert isinstance(subpath, str), 'subpath must be a string, not {}'.format(type(subpath))
self._subpath = subpath
self._suffix = suffix
# For a Leadership inside a DynOptionDescription
self.ori_dyn = ori_dyn
def __getattr__(self,
name: str) -> Any:
@ -57,7 +63,6 @@ class SynDynOptionDescription:
name: str,
config_bag: ConfigBag,
subpath: str) -> BaseOption:
#FIXME -> Union[BaseOption, SynDynOptionDescription]:
if name.endswith(self._suffix):
oname = name[:-len(self._suffix)]
try:
@ -67,12 +72,20 @@ class SynDynOptionDescription:
pass
else:
return child.to_dynoption(subpath,
self._suffix)
self._suffix,
self._opt)
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,
for_path=False) -> str:
if for_path == 'toto':
if self.ori_dyn:
opt = self.ori_dyn
else:
opt = self._opt
return self._opt.impl_getname() + opt.convert_suffix_to_path(self._suffix)
return self._opt.impl_getname() + self._suffix
def impl_is_dynoptiondescription(self) -> bool:
@ -84,14 +97,14 @@ class SynDynOptionDescription:
subpath = self.impl_getpath()
for child in self._opt.get_children(config_bag):
yield child.to_dynoption(subpath,
self._suffix)
self._suffix,
self._opt)
def get_children_recursively(self,
bytype: Optional[BaseOption],
byname: Optional[str],
config_bag: ConfigBag,
self_opt: BaseOption=None) -> BaseOption:
# FIXME -> Iterator[Union[BaseOption, SynDynOptionDescription]]:
return self._opt.get_children_recursively(bytype,
byname,
config_bag,
@ -101,7 +114,7 @@ class SynDynOptionDescription:
subpath = self._subpath
if subpath != '':
subpath += '.'
return subpath + self.impl_getname()
return subpath + self.impl_getname(for_path=True)
def impl_get_display_name(self) -> str:
return self._opt.impl_get_display_name() + self._suffix
@ -110,13 +123,15 @@ class SynDynOptionDescription:
class SynDynLeadership(SynDynOptionDescription):
def get_leader(self) -> SynDynOption:
return self._opt.get_leader().to_dynoption(self.impl_getpath(),
self._suffix)
self._suffix,
self.ori_dyn)
def get_followers(self) -> Iterator[SynDynOption]:
subpath = self.impl_getpath()
for follower in self._opt.get_followers():
yield follower.to_dynoption(subpath,
self._suffix)
self._suffix,
self.ori_dyn)
def reset_cache(self,
path: str,