Merge remote-tracking branch 'official/master' into develop
This commit is contained in:
commit
75a073455e
|
@ -260,9 +260,31 @@ async def test_prop_dyndescription_force_store_value():
|
||||||
dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list))
|
dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list))
|
||||||
od = OptionDescription('od', '', [dod])
|
od = OptionDescription('od', '', [dod])
|
||||||
od2 = OptionDescription('od', '', [od])
|
od2 = OptionDescription('od', '', [od])
|
||||||
with pytest.raises(ConfigError):
|
async with await Config(od2) as cfg:
|
||||||
await Config(od2, session_id='error')
|
await cfg.property.read_write()
|
||||||
await delete_session('error')
|
assert await cfg.value.dict() == {'od.dodval1.stval1': None, 'od.dodval2.stval2': None}
|
||||||
|
assert not await list_sessions()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_prop_dyndescription_force_store_value_calculation_prefix():
|
||||||
|
lst = StrOption('lst', '', ['val1', 'val2'], multi=True)
|
||||||
|
st = StrOption('st', '', Calculation(return_list, Params(ParamSuffix())) , properties=('force_store_value',))
|
||||||
|
dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list, Params(ParamOption(lst))))
|
||||||
|
od = OptionDescription('od', '', [dod, lst])
|
||||||
|
od2 = OptionDescription('od', '', [od])
|
||||||
|
async with await Config(od2) as cfg:
|
||||||
|
await cfg.property.read_write()
|
||||||
|
assert await cfg.option('od.dodval1.stval1').owner.isdefault() == False
|
||||||
|
assert await cfg.option('od.dodval2.stval2').owner.isdefault() == False
|
||||||
|
assert await cfg.value.dict() == {'od.lst': ['val1', 'val2'], 'od.dodval1.stval1': 'val1', 'od.dodval2.stval2': 'val2'}
|
||||||
|
#
|
||||||
|
await cfg.option('od.lst').value.set(['val1', 'val2', 'val3'])
|
||||||
|
assert await cfg.option('od.dodval3.stval3').owner.isdefault() == False
|
||||||
|
assert await cfg.option('od.dodval1.stval1').owner.isdefault() == False
|
||||||
|
assert await cfg.option('od.dodval2.stval2').owner.isdefault() == False
|
||||||
|
assert await cfg.value.dict() == {'od.lst': ['val1', 'val2', 'val3'], 'od.dodval1.stval1': 'val1', 'od.dodval2.stval2': 'val2', 'od.dodval3.stval3': 'val3'}
|
||||||
|
|
||||||
assert not await list_sessions()
|
assert not await list_sessions()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1130,6 +1130,39 @@ async def test_meta_properties_meta_deepcopy():
|
||||||
await delete_sessions([meta, meta2])
|
await delete_sessions([meta, meta2])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_meta_properties_meta_deepcopy_multi_parent():
|
||||||
|
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip")
|
||||||
|
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask")
|
||||||
|
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
|
||||||
|
conf1 = await Config(interface1, session_id='conf1')
|
||||||
|
conf2 = await Config(interface1, session_id='conf2')
|
||||||
|
await conf1.property.read_write()
|
||||||
|
await conf2.property.read_write()
|
||||||
|
meta1 = await MetaConfig([conf1, conf2], session_id='meta1')
|
||||||
|
await meta1.permissive.add('hidden')
|
||||||
|
await meta1.property.read_write()
|
||||||
|
|
||||||
|
meta2 = await MetaConfig(['name1', 'name2'], optiondescription=interface1, session_id='meta2')
|
||||||
|
await meta2.config.add(conf1)
|
||||||
|
|
||||||
|
await meta1.option('ip_admin_eth0').value.set('192.168.1.1')
|
||||||
|
await meta2.option('netmask_admin_eth0').value.set('255.255.255.0')
|
||||||
|
|
||||||
|
assert await meta1.value.dict() == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': None}
|
||||||
|
assert await meta2.value.dict() == {'ip_admin_eth0': None, 'netmask_admin_eth0': '255.255.255.0'}
|
||||||
|
assert await conf1.value.dict() == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': '255.255.255.0'}
|
||||||
|
assert await conf2.value.dict() == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': None}
|
||||||
|
|
||||||
|
copy_meta2 = await conf1.config.deepcopy(session_id='copy_conf1', metaconfig_prefix='copy_')
|
||||||
|
assert await copy_meta2.config.path() == 'copy_meta2'
|
||||||
|
copy_meta1 = await copy_meta2.config('copy_meta1')
|
||||||
|
copy_conf1 = await copy_meta1.config('copy_conf1')
|
||||||
|
assert await copy_meta2.value.dict() == {'ip_admin_eth0': None, 'netmask_admin_eth0': '255.255.255.0'}
|
||||||
|
assert await copy_conf1.value.dict() == {'ip_admin_eth0': '192.168.1.1', 'netmask_admin_eth0': '255.255.255.0'}
|
||||||
|
await delete_sessions([conf1, conf2, meta1, meta2, copy_conf1, copy_meta1, copy_meta2])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_meta_properties_submeta_deepcopy():
|
async def test_meta_properties_submeta_deepcopy():
|
||||||
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1'])
|
ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1'])
|
||||||
|
|
|
@ -173,7 +173,8 @@ class SubConfig:
|
||||||
|
|
||||||
async def cfgimpl_reset_cache(self,
|
async def cfgimpl_reset_cache(self,
|
||||||
option_bag,
|
option_bag,
|
||||||
resetted_opts=None):
|
resetted_opts=None,
|
||||||
|
):
|
||||||
"""reset all settings in cache
|
"""reset all settings in cache
|
||||||
"""
|
"""
|
||||||
if resetted_opts is None:
|
if resetted_opts is None:
|
||||||
|
|
|
@ -55,6 +55,7 @@ class Base:
|
||||||
'_properties',
|
'_properties',
|
||||||
'_has_dependency',
|
'_has_dependency',
|
||||||
'_dependencies',
|
'_dependencies',
|
||||||
|
'_suffixes_dependencies',
|
||||||
'__weakref__'
|
'__weakref__'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -99,18 +100,29 @@ class Base:
|
||||||
return hasattr(self, '_dependencies')
|
return hasattr(self, '_dependencies')
|
||||||
|
|
||||||
def _get_dependencies(self,
|
def _get_dependencies(self,
|
||||||
context_od) -> Set[str]:
|
context_od,
|
||||||
|
) -> 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
|
||||||
return set(context_od._dependencies) | ret
|
return set(context_od._dependencies) | ret
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def _get_suffixes_dependencies(self) -> Set[str]:
|
||||||
|
return getattr(self, '_suffixes_dependencies', STATIC_TUPLE)
|
||||||
|
|
||||||
def _add_dependency(self,
|
def _add_dependency(self,
|
||||||
option) -> None:
|
option,
|
||||||
|
is_suffix: bool=False,
|
||||||
|
) -> None:
|
||||||
|
woption = weakref.ref(option)
|
||||||
options = self._get_dependencies(None)
|
options = self._get_dependencies(None)
|
||||||
options.add(weakref.ref(option))
|
options.add(weakref.ref(option))
|
||||||
self._dependencies = tuple(options)
|
self._dependencies = tuple(options)
|
||||||
|
if is_suffix:
|
||||||
|
options = list(self._get_suffixes_dependencies())
|
||||||
|
options.append(weakref.ref(option))
|
||||||
|
self._suffixes_dependencies = tuple(options)
|
||||||
|
|
||||||
def _impl_set_callback(self,
|
def _impl_set_callback(self,
|
||||||
callback: Callable,
|
callback: Callable,
|
||||||
|
|
|
@ -66,7 +66,9 @@ class DynOptionDescription(OptionDescription):
|
||||||
raise ConfigError(_('suffixes in dynoptiondescription has to be a calculation'))
|
raise ConfigError(_('suffixes in dynoptiondescription has to be a calculation'))
|
||||||
for param in chain(suffixes.params.args, suffixes.params.kwargs.values()):
|
for param in chain(suffixes.params.args, suffixes.params.kwargs.values()):
|
||||||
if isinstance(param, ParamOption):
|
if isinstance(param, ParamOption):
|
||||||
param.option._add_dependency(self)
|
param.option._add_dependency(self,
|
||||||
|
is_suffix=True,
|
||||||
|
)
|
||||||
self._suffixes = suffixes
|
self._suffixes = suffixes
|
||||||
|
|
||||||
def convert_suffix_to_path(self,
|
def convert_suffix_to_path(self,
|
||||||
|
|
|
@ -87,11 +87,6 @@ class CacheOptionDescription(BaseOption):
|
||||||
if not option.impl_is_symlinkoption():
|
if not option.impl_is_symlinkoption():
|
||||||
properties = option.impl_getproperties()
|
properties = option.impl_getproperties()
|
||||||
if 'force_store_value' in properties:
|
if 'force_store_value' in properties:
|
||||||
if __debug__:
|
|
||||||
if option.issubdyn():
|
|
||||||
raise ConfigError(_('the dynoption "{0}" cannot have '
|
|
||||||
'"force_store_value" property').format(
|
|
||||||
option.impl_get_display_name()))
|
|
||||||
force_store_values.append((subpath, option))
|
force_store_values.append((subpath, option))
|
||||||
if __debug__ and ('force_default_on_freeze' in properties or \
|
if __debug__ and ('force_default_on_freeze' in properties or \
|
||||||
'force_metaconfig_on_freeze' in properties) and \
|
'force_metaconfig_on_freeze' in properties) and \
|
||||||
|
@ -115,7 +110,8 @@ class CacheOptionDescription(BaseOption):
|
||||||
self._set_readonly()
|
self._set_readonly()
|
||||||
|
|
||||||
async def impl_build_force_store_values(self,
|
async def impl_build_force_store_values(self,
|
||||||
config_bag: ConfigBag) -> None:
|
config_bag: ConfigBag,
|
||||||
|
) -> None:
|
||||||
if 'force_store_value' not in config_bag.properties:
|
if 'force_store_value' not in config_bag.properties:
|
||||||
return
|
return
|
||||||
values = config_bag.context.cfgimpl_get_values()
|
values = config_bag.context.cfgimpl_get_values()
|
||||||
|
@ -142,18 +138,38 @@ class CacheOptionDescription(BaseOption):
|
||||||
owners.forced,
|
owners.forced,
|
||||||
index,
|
index,
|
||||||
False)
|
False)
|
||||||
|
else:
|
||||||
|
option_bags = []
|
||||||
|
if option.issubdyn():
|
||||||
|
dynopt = option.getsubdyn()
|
||||||
|
rootpath = dynopt.impl_getpath()
|
||||||
|
subpaths = [rootpath] + option.impl_getpath()[len(rootpath) + 1:].split('.')[1:]
|
||||||
|
for suffix in await dynopt.get_suffixes(config_bag):
|
||||||
|
path_suffix = dynopt.convert_suffix_to_path(suffix)
|
||||||
|
subpath = '.'.join([subp + path_suffix for subp in subpaths])
|
||||||
|
doption = option.to_dynoption(subpath,
|
||||||
|
suffix,
|
||||||
|
option)
|
||||||
|
doption_bag = OptionBag()
|
||||||
|
doption_bag.set_option(doption,
|
||||||
|
None,
|
||||||
|
config_bag)
|
||||||
|
option_bags.append(doption_bag)
|
||||||
else:
|
else:
|
||||||
option_bag = OptionBag()
|
option_bag = OptionBag()
|
||||||
option_bag.set_option(option,
|
option_bag.set_option(option,
|
||||||
None,
|
None,
|
||||||
config_bag)
|
config_bag)
|
||||||
|
option_bags.append(option_bag)
|
||||||
|
for option_bag in option_bags:
|
||||||
option_bag.properties = frozenset()
|
option_bag.properties = frozenset()
|
||||||
await values._p_.setvalue(config_bag.connection,
|
await values._p_.setvalue(config_bag.connection,
|
||||||
subpath,
|
option_bag.path,
|
||||||
await values.getvalue(option_bag),
|
await values.getvalue(option_bag),
|
||||||
owners.forced,
|
owners.forced,
|
||||||
None,
|
None,
|
||||||
False)
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OptionDescriptionWalk(CacheOptionDescription):
|
class OptionDescriptionWalk(CacheOptionDescription):
|
||||||
|
|
|
@ -242,6 +242,8 @@ class Values:
|
||||||
return
|
return
|
||||||
# calculated value is a new value, so reset cache
|
# calculated value is a new value, so reset cache
|
||||||
await option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
|
await option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
|
||||||
|
# and manage force_store_value
|
||||||
|
await self._set_force_value_suffix(option_bag)
|
||||||
|
|
||||||
async def calculate_value(self,
|
async def calculate_value(self,
|
||||||
option_bag: OptionBag,
|
option_bag: OptionBag,
|
||||||
|
@ -361,15 +363,58 @@ class Values:
|
||||||
check_error=False)
|
check_error=False)
|
||||||
|
|
||||||
async def _setvalue(self,
|
async def _setvalue(self,
|
||||||
option_bag,
|
option_bag: OptionBag,
|
||||||
value,
|
value: Any,
|
||||||
owner):
|
owner: str,
|
||||||
|
) -> None:
|
||||||
await option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
|
await option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
|
||||||
await self._p_.setvalue(option_bag.config_bag.connection,
|
await self._p_.setvalue(option_bag.config_bag.connection,
|
||||||
option_bag.path,
|
option_bag.path,
|
||||||
value,
|
value,
|
||||||
owner,
|
owner,
|
||||||
option_bag.index)
|
option_bag.index)
|
||||||
|
await self._set_force_value_suffix(option_bag)
|
||||||
|
|
||||||
|
async def _set_force_value_suffix(self,
|
||||||
|
option_bag: OptionBag,
|
||||||
|
) -> None:
|
||||||
|
if 'force_store_value' not in option_bag.config_bag.properties:
|
||||||
|
return
|
||||||
|
for woption in option_bag.option._get_suffixes_dependencies():
|
||||||
|
option = woption()
|
||||||
|
force_store_options = []
|
||||||
|
async for coption in option.get_children_recursively(None,
|
||||||
|
None,
|
||||||
|
option_bag.config_bag,
|
||||||
|
):
|
||||||
|
if 'force_store_value' in coption.impl_getproperties():
|
||||||
|
force_store_options.append(coption)
|
||||||
|
if not force_store_options:
|
||||||
|
continue
|
||||||
|
rootpath = option.impl_getpath()
|
||||||
|
settings = option_bag.config_bag.context.cfgimpl_get_settings()
|
||||||
|
for suffix in await option.get_suffixes(option_bag.config_bag):
|
||||||
|
for coption in force_store_options:
|
||||||
|
subpaths = [rootpath] + coption.impl_getpath()[len(rootpath) + 1:].split('.')[:-1]
|
||||||
|
path_suffix = option.convert_suffix_to_path(suffix)
|
||||||
|
subpath = '.'.join([subp + path_suffix for subp in subpaths])
|
||||||
|
doption = coption.to_dynoption(subpath,
|
||||||
|
suffix,
|
||||||
|
coption,
|
||||||
|
)
|
||||||
|
coption_bag = OptionBag()
|
||||||
|
coption_bag.set_option(doption,
|
||||||
|
None,
|
||||||
|
option_bag.config_bag,
|
||||||
|
)
|
||||||
|
coption_bag.properties = await settings.getproperties(coption_bag)
|
||||||
|
await self._p_.setvalue(coption_bag.config_bag.connection,
|
||||||
|
coption_bag.path,
|
||||||
|
await self.getvalue(coption_bag),
|
||||||
|
owners.forced,
|
||||||
|
None,
|
||||||
|
False,
|
||||||
|
)
|
||||||
|
|
||||||
async def _get_modified_parent(self,
|
async def _get_modified_parent(self,
|
||||||
option_bag: OptionBag) -> Optional[OptionBag]:
|
option_bag: OptionBag) -> Optional[OptionBag]:
|
||||||
|
|
Loading…
Reference in New Issue