From c2e3bf86f13c627017d91c99e3e27fec46f55ca2 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 6 Jul 2024 14:31:15 +0200 Subject: [PATCH] add uncalculated and only_self option in api --- tests/auto/test_auto.py | 2 +- tests/test_choice_option.py | 1 + tests/test_dyn_optiondescription.py | 32 ++++++++++++ tests/test_option_callback.py | 1 + tiramisu/api.py | 75 +++++++++++++++++++---------- tiramisu/config.py | 4 +- tiramisu/option/choiceoption.py | 3 ++ 7 files changed, 91 insertions(+), 27 deletions(-) diff --git a/tests/auto/test_auto.py b/tests/auto/test_auto.py index 74d97ee..5e6ac35 100644 --- a/tests/auto/test_auto.py +++ b/tests/auto/test_auto.py @@ -864,7 +864,7 @@ def _test_value_normal(name, cfg, option, unrestraint, without_index): assert option.value.valid() is True # list if 'choice' in name: - assert option.value.list() == ('val1', 'val2') + assert option.value.list() == ['val1', 'val2'] else: with pytest.raises(ConfigError): option.value.list() diff --git a/tests/test_choice_option.py b/tests/test_choice_option.py index 753b781..a5ac271 100644 --- a/tests/test_choice_option.py +++ b/tests/test_choice_option.py @@ -72,6 +72,7 @@ def test_choiceoption_function(config_type): assert cfg.option('choice').owner.isdefault() # assert value_list(cfg.option('choice').value.list()) == ('val1', 'val2') + assert isinstance(cfg.option('choice').value.list(uncalculated=True), Calculation) # assert not list_sessions() diff --git a/tests/test_dyn_optiondescription.py b/tests/test_dyn_optiondescription.py index fdb543d..6f62b6b 100644 --- a/tests/test_dyn_optiondescription.py +++ b/tests/test_dyn_optiondescription.py @@ -77,6 +77,22 @@ def test_build_dyndescription(): assert parse_od_get(cfg.value.get()) == {'dodval1.st': None, 'dodval2.st': None} assert cfg.option('dodval1').isdynamic() assert cfg.option('dodval1.st').isdynamic() + assert cfg.option('dodval1').isdynamic(only_self=True) + assert not cfg.option('dodval1.st').isdynamic(only_self=True) +# assert not list_sessions() + + +def test_dyndescription_suffixes(): + st1 = StrOption('st', '') + dod = DynOptionDescription('dod', '', [st1], suffixes=Calculation(return_list)) + od1 = OptionDescription('od', '', [dod]) + cfg = Config(od1) + assert parse_od_get(cfg.value.get()) == {'dodval1.st': None, 'dodval2.st': None} + assert cfg.option('dodval1').suffixes() == ['val1'] + assert cfg.option('dodval1.st').suffixes() == ['val1'] + assert cfg.option('dodval1').suffixes(only_self=True) == ['val1', 'val2'] + with pytest.raises(ConfigError): + cfg.option('dodval1.st').suffixes(only_self=True) # assert not list_sessions() @@ -164,8 +180,10 @@ def test_getdoc_dyndescription(): cfg = Config(od2) assert cfg.option('od.dodval1.st').name() == 'st' assert cfg.option('od.dodval2.st').name() == 'st' + assert cfg.option('od.dodval1.st').name(uncalculated=True) == cfg.option('od.dodval2.st').name(uncalculated=True) == 'st' assert cfg.option('od.dodval1').name() == 'dodval1' assert cfg.option('od.dodval2').name() == 'dodval2' + assert cfg.option('od.dodval1').name(uncalculated=True) == cfg.option('od.dodval2').name(uncalculated=True) == 'dod' assert cfg.option('od.dodval1.st').doc() == 'doc1' assert cfg.option('od.dodval2.st').doc() == 'doc1' assert cfg.option('od.dodval1').doc() == 'doc2' @@ -173,6 +191,20 @@ def test_getdoc_dyndescription(): # assert not list_sessions() +def test_dyndescription_path(): + st1 = StrOption('st', 'doc1') + dod = DynOptionDescription('dod', 'doc2', [st1], suffixes=Calculation(return_list)) + od1 = OptionDescription('od', '', [dod]) + od2 = OptionDescription('od', '', [od1]) + cfg = Config(od2) + assert cfg.option('od.dodval1.st').path() == 'od.dodval1.st' + assert cfg.option('od.dodval2.st').path() == 'od.dodval2.st' + assert cfg.option('od.dodval1.st').path(uncalculated=True) == cfg.option('od.dodval2.st').path(uncalculated=True) == 'od.dod.st' + assert cfg.option('od.dodval1').path() == 'od.dodval1' + assert cfg.option('od.dodval2').path() == 'od.dodval2' + assert cfg.option('od.dodval1').path(uncalculated=True) == cfg.option('od.dodval2').path(uncalculated=True) == 'od.dod' + + def test_mod_dyndescription(): st = StrOption('st', '') dod = DynOptionDescription('dod', '', [st], suffixes=Calculation(return_list)) diff --git a/tests/test_option_callback.py b/tests/test_option_callback.py index 36f9392..3fecf0f 100644 --- a/tests/test_option_callback.py +++ b/tests/test_option_callback.py @@ -289,6 +289,7 @@ def test_callback(config_type): cfg = Config(od1) cfg.property.read_write() cfg = get_config(cfg, config_type) + assert isinstance(cfg.option('val1').value.get(uncalculated=True), Calculation) assert cfg.option('val1').value.get() == 'val' cfg.option('val1').value.set('new-val') assert cfg.option('val1').value.get() == 'new-val' diff --git a/tiramisu/api.py b/tiramisu/api.py index 377eeda..1fc58d9 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -199,9 +199,11 @@ class _TiramisuOptionWalk: def _list(self, subconfig: SubConfig, validate_properties: bool, + *, + uncalculated: bool=False, ): options = [] - for sub_subconfig in subconfig.get_children(validate_properties=validate_properties): + for sub_subconfig in subconfig.get_children(validate_properties, uncalculated=uncalculated): options.append(TiramisuOption(sub_subconfig.path, sub_subconfig.index, self._config_bag, @@ -243,15 +245,23 @@ class _TiramisuOptionOptionDescription: ) @option_type(['optiondescription', 'option', 'symlink', 'with_or_without_index']) - def name(self) -> str: + def name(self, + *, + uncalculated: bool=False, + ) -> str: """Get option name""" - #FIXME impl_getname ? + if uncalculated: + return self._subconfig.option.impl_getname() return self._subconfig.true_path.rsplit('.', 1)[-1] @option_type(['optiondescription', 'option', 'with_or_without_index', 'symlink']) def path(self, + *, + uncalculated: bool=False, ) -> str: """Get option path""" + if uncalculated: + return self._subconfig.option.impl_getpath() return self._subconfig.true_path @option_type(['optiondescription', 'option', 'symlink', 'with_or_without_index']) @@ -281,9 +291,14 @@ class _TiramisuOptionOptionDescription: return option.get_type() @option_type(['option', 'optiondescription', 'symlink', 'with_or_without_index']) - def isdynamic(self): + def isdynamic(self, + *, + only_self: bool=False): """Test if option is a dynamic optiondescription""" - return self._subconfig.is_dynamic + if not only_self: + return self._subconfig.is_dynamic + return self._subconfig.option.impl_is_optiondescription() and \ + self._subconfig.option.impl_is_dynoptiondescription() @option_type(['option', 'leadership']) def leader(self): @@ -304,9 +319,16 @@ class _TiramisuOptionOptionDescription: ) @option_type(['dynamic', 'with_or_without_index']) - def suffixes(self): + def suffixes(self, + only_self: bool=False, + ): """Get suffixes for dynamic option""" - return self._subconfig.suffixes + if not only_self: + return self._subconfig.suffixes + if not self._subconfig.option.impl_is_optiondescription() or \ + not self._subconfig.option.impl_is_dynoptiondescription(): + raise ConfigError(_(f'the option {self._subconfig.path} is not a dynamic option, cannot get suffixes with only_self parameter to True')) + return self._subconfig.option.get_suffixes(self._subconfig.parent) class _TiramisuOptionOption(_TiramisuOptionOptionDescription): @@ -447,6 +469,7 @@ class TiramisuOptionProperty(CommonTiramisuOption): @option_type(['option', 'optiondescription', 'with_index', 'symlink']) def get(self, + *, only_raises: bool=False, apply_requires: bool=True, uncalculated: bool=False, @@ -591,7 +614,6 @@ class _TiramisuODGet(): """exports the whole config into a `dict` :returns: dict of Option's name (or path) and values """ - #for self._subconfig, value in self._config_bag.context.walk(root_self._subconfig): def parse_od_get(values): ret_ = {} for subconfig, value in values.items(): @@ -604,19 +626,6 @@ class _TiramisuODGet(): value = parse_od_get(value) ret_[option] = value return ret_ -# else: -# leader_ret = [] -# leader = data['leader'] -# for idx, value in enumerate(self.get_value(leader, -# need_help=False, -# )): -# leader_dict = {leader: value} -# for follower in data.get(idx, []): -# leader_dict[follower] = self.get_value(follower, -# need_help=False, -# ) -# leader_ret.append(leader_dict) -# ret[leader] = leader_ret return parse_od_get(self._config_bag.context.walk(root_subconfig)) @@ -625,11 +634,18 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet): _validate_properties = True @option_type(['option', 'symlink', 'with_index', 'optiondescription']) - def get(self): + def get(self, + *, + uncalculated: bool=False, + ): """Get value for an option or option and sub option with values with optiondescription""" if self._subconfig.option.impl_is_optiondescription(): + if uncalculated: + raise ConfigError('uncalculated is not allowed for optiondescription') return self._od_get(self._subconfig) - return self._get() + if uncalculated: + return self._subconfig.option.impl_getdefault() + return self._get(uncalculated) def _get(self, need_help: bool=True, @@ -688,9 +704,14 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet): return True @option_type(['choice', 'with_index']) - def list(self): + def list(self, + *, + uncalculated: bool=False, + ): """All values available for a ChoiceOption""" - return self._subconfig.option.impl_get_values(self._subconfig) + return self._subconfig.option.impl_get_values(self._subconfig, + uncalculated, + ) @option_type('leader') def pop(self, @@ -811,11 +832,14 @@ class TiramisuOption(CommonTiramisu, @option_type('optiondescription') def list(self, + *, validate_properties: bool=True, + uncalculated: bool=False, ): """List options inside an option description (by default list only option)""" return self._list(self._subconfig, validate_properties, + uncalculated=uncalculated, ) def _load_dict(self, @@ -1062,6 +1086,7 @@ class TiramisuContextProperty(TiramisuConfig, PropertyPermissive): self._set(frozenset(props)) def get(self, + *, only_raises: bool=False, apply_requires: bool=True, uncalculated: bool=False, diff --git a/tiramisu/config.py b/tiramisu/config.py index 11ce32d..3b20daa 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -296,12 +296,14 @@ class SubConfig: def get_children(self, validate_properties, + *, + uncalculated: bool=False, ): if self.option.impl_is_leadership(): yield from self.get_leadership_children(validate_properties) else: for child in self.option.get_children(): - if child.impl_is_dynoptiondescription(): + if child.impl_is_dynoptiondescription() and not uncalculated: yield from self.dyn_to_subconfig(child, validate_properties, ) diff --git a/tiramisu/option/choiceoption.py b/tiramisu/option/choiceoption.py index 96d1f36..40a053f 100644 --- a/tiramisu/option/choiceoption.py +++ b/tiramisu/option/choiceoption.py @@ -58,12 +58,15 @@ class ChoiceOption(Option): def impl_get_values(self, subconfig: "SubConfig", + uncalculated: bool=False, ): """get values allowed by option """ choices = self._choice_values if isinstance(choices, tuple): choices = list(choices) + if uncalculated: + return choices values = get_calculated_value(subconfig, choices, )[0]