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 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): def teardown_function(function):
assert list_sessions() == [], 'session list is not empty when leaving "{}"'.format(function.__name__) 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'] return ['val1', 'val2']
def return_list_dot(val=None, suffix=None):
return ['val.1', 'val.2']
def return_same_list(*args, **kwargs): def return_same_list(*args, **kwargs):
return ['val1', 'val1'] 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.stval1.st1val1.st2val1', 0).owner.isdefault()
assert api.option('od.stval2.st1val2.st1val2').owner.isdefault() assert api.option('od.stval2.st1val2.st1val2').owner.isdefault()
def test_leadership_dyndescription_param(): def test_leadership_dyndescription_param():
val1 = StrOption('val1', '', ['val1', 'val2'], multi=True) val1 = StrOption('val1', '', ['val1', 'val2'], multi=True)
odval = OptionDescription('odval1', '', [val1]) odval = OptionDescription('odval1', '', [val1])
@ -1259,11 +1270,11 @@ def test_leadership_callback_samegroup_dyndescription():
api = Config(od2) api = Config(od2)
owner = api.owner.get() owner = api.owner.get()
assert api.value.dict() == {'od.stval1.st1val1.st1val1': [], assert api.value.dict() == {'od.stval1.st1val1.st1val1': [],
'od.stval1.st1val1.st2val1': [], 'od.stval1.st1val1.st2val1': [],
'od.stval1.st1val1.st3val1': [], 'od.stval1.st1val1.st3val1': [],
'od.stval2.st1val2.st1val2': [], 'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [], 'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []} 'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').value.get() == [] assert api.option('od.stval1.st1val1.st1val1').value.get() == []
assert api.option('od.stval2.st1val2.st1val2').value.get() == [] assert api.option('od.stval2.st1val2.st1val2').value.get() == []
assert api.option('od.stval1.st1val1.st1val1').owner.isdefault() 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']) api.option('od.stval1.st1val1.st1val1').value.set(['yes'])
assert api.value.dict() == {'od.stval1.st1val1.st1val1': ['yes'], assert api.value.dict() == {'od.stval1.st1val1.st1val1': ['yes'],
'od.stval1.st1val1.st2val1': [None], 'od.stval1.st1val1.st2val1': [None],
'od.stval1.st1val1.st3val1': [None], 'od.stval1.st1val1.st3val1': [None],
'od.stval2.st1val2.st1val2': [], 'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [], 'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []} 'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner 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.st2val1', 0).owner.isdefault()
assert api.option('od.stval1.st1val1.st3val1', 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') api.option('od.stval1.st1val1.st2val1', 0).value.set('yes')
assert api.value.dict() == {'od.stval1.st1val1.st1val1': ['yes'], assert api.value.dict() == {'od.stval1.st1val1.st1val1': ['yes'],
'od.stval1.st1val1.st2val1': ['yes'], 'od.stval1.st1val1.st2val1': ['yes'],
'od.stval1.st1val1.st3val1': ['yes'], 'od.stval1.st1val1.st3val1': ['yes'],
'od.stval2.st1val2.st1val2': [], 'od.stval2.st1val2.st1val2': [],
'od.stval2.st1val2.st2val2': [], 'od.stval2.st1val2.st2val2': [],
'od.stval2.st1val2.st3val2': []} 'od.stval2.st1val2.st3val2': []}
assert api.option('od.stval1.st1val1.st1val1').owner.get() == owner 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.st2val1', 0).owner.get() == owner
assert api.option('od.stval1.st1val1.st3val1', 0).owner.isdefault() assert api.option('od.stval1.st1val1.st3val1', 0).owner.isdefault()
@ -1344,3 +1355,107 @@ def test_invalid_name_dyndescription():
od1 = OptionDescription('od', '', [dod]) od1 = OptionDescription('od', '', [dod])
cfg = Config(od1) cfg = Config(od1)
raises(ValueError, "cfg.value.dict()") 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 callbk_option = callbk.option
if callbk_option.issubdyn(): if callbk_option.issubdyn():
callbk_option = callbk_option.to_dynoption(option.rootpath, 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: if leadership_must_have_index and callbk_option.impl_get_leadership() and index is None:
raise Break() raise Break()
if config_bag is undefined: 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()): for woption in option_bag.option._get_dependencies(self.cfgimpl_get_description()):
option = woption() option = woption()
if option.impl_is_dynoptiondescription(): 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): for suffix in option.get_suffixes(option_bag.config_bag):
doption = option.to_dynoption(subpath, doption = option.to_dynoption(desc.impl_getpath(),
suffix) suffix,
option)
doption_path = doption.impl_getpath() doption_path = doption.impl_getpath()
doption_bag = OptionBag() doption_bag = OptionBag()
doption_bag.set_option(doption, doption_bag.set_option(doption,
@ -124,13 +125,16 @@ class SubConfig(object):
resetted_opts, resetted_opts,
doption_bag) doption_bag)
elif option.issubdyn(): elif option.issubdyn():
# it's an option in dynoptiondescription, remove cache for all generated option
dynopt = option.getsubdyn() dynopt = option.getsubdyn()
rootpath = dynopt.impl_getpath() rootpath = dynopt.impl_getpath()
subpaths = [rootpath] + option.impl_getpath()[len(rootpath) + 1:].split('.')[:-1] subpaths = [rootpath] + option.impl_getpath()[len(rootpath) + 1:].split('.')[:-1]
for suffix in dynopt.get_suffixes(option_bag.config_bag): 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, doption = option.to_dynoption(subpath,
suffix) suffix,
dynopt)
doption_path = doption.impl_getpath() doption_path = doption.impl_getpath()
doption_bag = OptionBag() doption_bag = OptionBag()
doption_bag.set_option(doption, doption_bag.set_option(doption,

View File

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

View File

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

View File

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

View File

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

View File

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