diff --git a/tests/test_option_callback.py b/tests/test_option_callback.py index 5edc363..97f468b 100644 --- a/tests/test_option_callback.py +++ b/tests/test_option_callback.py @@ -10,7 +10,7 @@ from tiramisu.setting import groups, owners from tiramisu import ChoiceOption, BoolOption, IntOption, FloatOption, \ StrOption, OptionDescription, SymLinkOption, IPOption, NetmaskOption, Leadership, \ undefined, Calculation, Params, ParamOption, ParamValue, ParamIndex, calc_value, \ - valid_ip_netmask, ParamSelfOption, ParamInformation + valid_ip_netmask, ParamSelfOption, ParamInformation, ParamSelfInformation from tiramisu.error import PropertiesOptionError, ConflictError, LeadershipError, ConfigError from tiramisu.i18n import _ from tiramisu.storage import list_sessions @@ -378,6 +378,28 @@ async def test_callback_information(config_type): await cfg.information.set('information', 'new_value') assert await cfg.option('val1').value.get() == 'new_value' assert await cfg.option('val2').value.get() == 'new_value' + await cfg.information.set('information', 'new_value2') + assert await cfg.option('val1').value.get() == 'new_value2' + assert await cfg.option('val2').value.get() == 'new_value2' + assert not await list_sessions() + + +@pytest.mark.asyncio +async def test_callback_information2(config_type): + val1 = StrOption('val1', "", Calculation(return_value, Params(ParamSelfInformation('information', 'no_value')))) + val2 = StrOption('val2', "", Calculation(return_value, Params(ParamSelfInformation('information')))) + val2.impl_set_information('information', 'new_value') + val3 = StrOption('val3', "", Calculation(return_value, Params(ParamSelfInformation('information')))) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3]) + async with await Config(maconfig) as cfg: + await cfg.property.read_write() + cfg = await get_config(cfg, config_type) + assert await cfg.option('val1').value.get() == 'no_value' + assert await cfg.option('val2').value.get() == 'new_value' + with pytest.raises(ConfigError): + await cfg.option('val3').value.get() + await cfg.option('val2').information.set('information', 'new_value2') + assert await cfg.option('val2').value.get() == 'new_value2' assert not await list_sessions() diff --git a/tiramisu/__init__.py b/tiramisu/__init__.py index 7ca9e23..038fc95 100644 --- a/tiramisu/__init__.py +++ b/tiramisu/__init__.py @@ -18,7 +18,7 @@ from .function import calc_value, calc_value_property_help, valid_ip_netmask, \ valid_network_netmask, valid_in_network, valid_broadcast, \ valid_not_equal from .autolib import Calculation, Params, ParamOption, ParamDynOption, ParamSelfOption, \ - ParamValue, ParamIndex, ParamSuffix, ParamInformation + ParamValue, ParamIndex, ParamSuffix, ParamInformation, ParamSelfInformation from .option import * from .error import APIError from .api import Config, MetaConfig, GroupConfig, MixConfig @@ -37,6 +37,7 @@ allfuncs = ['Calculation', 'ParamIndex', 'ParamSuffix', 'ParamInformation', + 'ParamSelfInformation', 'MetaConfig', 'MixConfig', 'GroupConfig', diff --git a/tiramisu/api.py b/tiramisu/api.py index 064f5a3..c1dd4e8 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -464,24 +464,23 @@ class TiramisuOptionInformation(CommonTiramisuOption): @option_and_connection async def get(self, key, default=undefined): """Get information""" - path = self._option_bag.path values = self._option_bag.config_bag.context.cfgimpl_get_values() - try: - return await values.get_information(self._option_bag.config_bag.connection, - key, - path=path) - except ValueError: - return self._option_bag.option.impl_get_information(key, default) + return await values.get_information(self._option_bag.config_bag, + self._option_bag, + key, + default, + ) @option_and_connection async def set(self, key, value): """Set information""" path = self._option_bag.path values = self._option_bag.config_bag.context.cfgimpl_get_values() - await values.set_information(self._option_bag.config_bag.connection, + await values.set_information(self._option_bag.config_bag, + self._option_bag, key, value, - path=path) + ) @option_and_connection async def reset(self, @@ -890,10 +889,12 @@ class TiramisuContextInformation(TiramisuConfig): default=undefined, ): """Get an information""" - return await self._config_bag.context.impl_get_information(self._config_bag.connection, - name, - default, - ) + values = self._config_bag.context.cfgimpl_get_values() + return await values.get_information(self._config_bag, + None, + name, + default, + ) @connection async def set(self, diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index ffa9b5e..dad5934 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -128,6 +128,10 @@ class ParamInformation(Param): self.default_value = default_value +class ParamSelfInformation(ParamInformation): + __slots__ = tuple() + + class ParamIndex(Param): __slots__ = tuple() @@ -297,8 +301,17 @@ async def manager_callback(callbk: Param, return callbk.value if isinstance(callbk, ParamInformation): + if isinstance(callbk, ParamSelfInformation): + option_bag = OptionBag() + option_bag.set_option(option, + index, + config_bag, + ) + else: + option_bag = None try: - return await config_bag.context.impl_get_information(config_bag.connection, + return await config_bag.context.impl_get_information(config_bag, + option_bag, callbk.information_name, callbk.default_value, ) diff --git a/tiramisu/config.py b/tiramisu/config.py index fdf724d..cd793d9 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -572,7 +572,8 @@ class _CommonConfig(SubConfig): :param key: information's key (ex: "help", "doc" :param value: information's value (ex: "the help string") """ - await self._impl_values.set_information(config_bag.connection, + await self._impl_values.set_information(config_bag, + None, key, value) for option in config_bag.context.cfgimpl_get_description()._cache_dependencies_information.get(key, []): @@ -583,14 +584,17 @@ class _CommonConfig(SubConfig): await config_bag.context.cfgimpl_reset_cache(option_bag) async def impl_get_information(self, - connection, + config_bag, + option_bag, key, - default=undefined): + default, + ): """retrieves one information's item :param key: the item string (ex: "help") """ - return await self._impl_values.get_information(connection, + return await self._impl_values.get_information(config_bag, + option_bag, key, default) diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 4561c3d..a576e57 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -27,7 +27,7 @@ from itertools import chain from .baseoption import BaseOption, submulti, STATIC_TUPLE from ..i18n import _ from ..setting import undefined, OptionBag, Undefined -from ..autolib import Calculation, Params, ParamOption, ParamInformation +from ..autolib import Calculation, Params, ParamOption, ParamInformation, ParamSelfInformation from ..error import (ConfigError, ValueWarning, ValueErrorWarning, PropertiesOptionError, ValueOptionError, display_list) from .syndynoption import SynDynOption @@ -67,7 +67,7 @@ class Option(BaseOption): warnings_only: bool=False, extra: Optional[Dict]=None): _setattr = object.__setattr__ - _dependencies_information = [] + _dependencies_information = [[], []] if not multi and default_multi is not None: raise ValueError(_("default_multi is set whereas multi is False" " in option: {0}").format(name)) @@ -107,8 +107,10 @@ class Option(BaseOption): if isinstance(param, ParamOption): param.option._add_dependency(self) self._has_dependency = True + elif isinstance(param, ParamSelfInformation): + _dependencies_information[1].append(param.information_name) elif isinstance(param, ParamInformation): - _dependencies_information.append(param.information_name) + _dependencies_information[0].append(param.information_name) self._validators = tuple(validators) if extra is not None and extra != {}: @@ -165,7 +167,7 @@ class Option(BaseOption): if is_multi and isinstance(default, list): default = tuple(default) _setattr(self, '_default', default) - if _dependencies_information: + if _dependencies_information[0] or _dependencies_information[1]: self._dependencies_information = _dependencies_information def value_dependencies(self, @@ -188,8 +190,10 @@ class Option(BaseOption): for param in chain(value.params.args, value.params.kwargs.values()): if isinstance(param, ParamOption): param.option._add_dependency(self) + elif isinstance(param, ParamSelfInformation): + _dependencies_information[1].append(param.information_name) elif isinstance(param, ParamInformation): - _dependencies_information.append(param.information_name) + _dependencies_information[0].append(param.information_name) #__________________________________________________________________________ # option's information @@ -203,8 +207,14 @@ class Option(BaseOption): def impl_is_dynsymlinkoption(self) -> bool: return False - def get_dependencies_information(self) -> List[str]: - return getattr(self, '_dependencies_information', []) + def get_dependencies_information(self, + itself=False, + ) -> List[str]: + if itself: + idx = 1 + else: + idx = 0 + return getattr(self, '_dependencies_information', [[], []])[idx] def get_type(self) -> str: # _display_name for compatibility with older version than 3.0rc3 @@ -449,7 +459,7 @@ class Option(BaseOption): self._display_name, option_bag.ori_option, '{0}'.format(err), - err_index) + err_index) from err warnings.warn_explicit(ValueErrorWarning(val, self._display_name, option_bag.ori_option, diff --git a/tiramisu/storage/dictionary/value.py b/tiramisu/storage/dictionary/value.py index 027c78f..b3a7f80 100644 --- a/tiramisu/storage/dictionary/value.py +++ b/tiramisu/storage/dictionary/value.py @@ -257,16 +257,12 @@ class Values: connection, path, key, - default): + ): """retrieves one information's item :param key: the item string (ex: "help") """ - value = self._storage.get_informations().get(path, {}).get(key, default) - if value is undefined: - raise ValueError(_("information's item" - " not found: {0}").format(key)) - return value + return self._storage.get_informations().get(path, {})[key] async def del_information(self, connection, diff --git a/tiramisu/storage/postgres/value.py b/tiramisu/storage/postgres/value.py index 3d0b6a9..2f3f5c5 100644 --- a/tiramisu/storage/postgres/value.py +++ b/tiramisu/storage/postgres/value.py @@ -186,7 +186,7 @@ class Values: connection, path, key, - default): + ): """retrieves one information's item :param key: the item string (ex: "help") @@ -197,12 +197,8 @@ class Values: "session_id = $2 AND path = $3", key, self._storage.database_id, path) if value is None: - if default is undefined: - raise ValueError(_("information's item" - " not found: {0}").format(key)) - return default - else: - return loads(value) + raise KeyError() + return loads(value) async def del_information(self, connection, diff --git a/tiramisu/value.py b/tiramisu/value.py index 3d299d6..f5afe1a 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -583,33 +583,53 @@ class Values: # information async def set_information(self, - connection, + config_bag, + option_bag, key, value, - path=None): + ): """updates the information's attribute :param key: information's key (ex: "help", "doc" :param value: information's value (ex: "the help string") """ - await self._p_.set_information(connection, + if option_bag is None: + path = None + else: + path = option_bag.path + await self._p_.set_information(config_bag.connection, path, key, value) + if path is not None: + for option in option_bag.option.get_dependencies_information(itself=True): + await config_bag.context.cfgimpl_reset_cache(option_bag) async def get_information(self, - connection, + config_bag, + option_bag, key, - default=undefined, - path=None): + default, + ): """retrieves one information's item :param key: the item string (ex: "help") """ - return await self._p_.get_information(connection, - path, - key, - default) + if option_bag is None: + path = None + else: + path = option_bag.path + try: + return await self._p_.get_information(config_bag.connection, + path, + key, + ) + except KeyError as err: + if option_bag: + return option_bag.option.impl_get_information(key, default) + if default is not undefined: + return default + raise ValueError(_("information's item not found: {0}").format(key)) async def del_information(self, connection,