From 9e80345dd1d58d4528949724af6b770af83eab6e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 9 Jan 2022 20:36:04 +0100 Subject: [PATCH] calc_value with join could crash --- tests/test_leadership.py | 13 +++++++++++++ tests/test_option_callback.py | 28 ++++++++++++++++++++++++++++ tests/test_option_setting.py | 13 +++++++++++++ tiramisu/function.py | 17 +++++++++-------- tiramisu/option/optiondescription.py | 10 ++++++++-- 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/tests/test_leadership.py b/tests/test_leadership.py index f2a8f9b..2bbdc3b 100644 --- a/tests/test_leadership.py +++ b/tests/test_leadership.py @@ -988,6 +988,19 @@ async def test_follower_not_multi(): assert not await list_sessions() +@pytest.mark.asyncio +async def test_follower_force_store_value_none(): + ip_admin_eth0 = IPOption('ip_admin_eth0', "ip réseau autorisé", multi=True, default=['1.1.1.1']) + netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "masque du sous-réseau", multi=True, properties=('force_store_value',)) + interface0 = Leadership('interface0', '', [ip_admin_eth0, netmask_admin_eth0]) + od1 = OptionDescription('od', '', [interface0]) + od2 = OptionDescription('toto', '', [od1]) + async with await Config(od2) as cfg: + await cfg.property.read_write() + assert await cfg.option('od.interface0.netmask_admin_eth0', 0).owner.isdefault() + assert not await list_sessions() + + @pytest.mark.asyncio async def test_follower_force_store_value(): ip_admin_eth0 = IPOption('ip_admin_eth0', "ip réseau autorisé", multi=True, default=['1.1.1.1']) diff --git a/tests/test_option_callback.py b/tests/test_option_callback.py index b31a50c..bed5273 100644 --- a/tests/test_option_callback.py +++ b/tests/test_option_callback.py @@ -1529,6 +1529,34 @@ async def test_calc_value_join(config_type): assert not await list_sessions() +@pytest.mark.asyncio +async def test_calc_value_join_multi(config_type): + val1 = StrOption('val1', "", multi=True) + val4 = StrOption('val4', "", Calculation(calc_value, Params((ParamOption(val1)), join=ParamValue('.'), multi=ParamValue(True))), multi=True) + od = OptionDescription('root', '', [val1, val4]) + async with await Config(od) as cfg: + cfg = await get_config(cfg, config_type) + assert await cfg.value.dict() == {'val1': [], 'val4': []} + await cfg.option('val1').value.set(['val1']) + assert await cfg.value.dict() == {'val1': ['val1'], 'val4': ['val1']} + assert not await list_sessions() + + +@pytest.mark.asyncio +async def test_calc_value_join_multi_value(config_type): + val1 = StrOption('val1', "", ['val1'], multi=True) + val2 = StrOption('val2', "", ['val2'], multi=True) + val3 = StrOption('val3', "", [None], multi=True) + val4 = StrOption('val4', "", Calculation(calc_value, Params((ParamOption(val1), ParamOption(val2), ParamOption(val3)), join=ParamValue('.'), multi=ParamValue(True))), multi=True) + od = OptionDescription('root', '', [val1, val2, val3, val4]) + async with await Config(od) as cfg: + cfg = await get_config(cfg, config_type) + assert await cfg.value.dict() == {'val1': ['val1'], 'val2': ['val2'], 'val3': [None], 'val4': []} + await cfg.option('val3').value.set(['val3']) + assert await cfg.value.dict() == {'val1': ['val1'], 'val2': ['val2'], 'val3': ['val3'], 'val4': ['val1.val2.val3']} + assert not await list_sessions() + + @pytest.mark.asyncio async def test_calc_value_min(): val1 = StrOption('val1', "", 'val1') diff --git a/tests/test_option_setting.py b/tests/test_option_setting.py index 197168a..dbcabcf 100644 --- a/tests/test_option_setting.py +++ b/tests/test_option_setting.py @@ -768,6 +768,19 @@ async def test_set_modified_value(): assert not await list_sessions() +@pytest.mark.asyncio +async def test_none_is_not_modified(): + gcdummy = StrOption('dummy', 'dummy', properties=('force_store_value',)) + gcdummy1 = StrOption('dummy1', 'dummy1', default="str", properties=('force_store_value',)) + gcgroup = OptionDescription('gc', '', [gcdummy, gcdummy1]) + descr = OptionDescription('tiramisu', '', [gcgroup]) + async with await Config(descr) as cfg: + assert await cfg.value.exportation() == [[], [], [], []] + await cfg.property.read_write() + assert await cfg.value.exportation() == [['gc.dummy1'], [None], ['str'], ['forced']] + assert not await list_sessions() + + @pytest.mark.asyncio async def test_pprint(): msg_error = _("cannot access to {0} \"{1}\" because has {2} {3}") diff --git a/tiramisu/function.py b/tiramisu/function.py index 447b186..8e2001e 100644 --- a/tiramisu/function.py +++ b/tiramisu/function.py @@ -347,14 +347,15 @@ class CalcValue: raise ValueError(_(f'unexpected value in calc_value with join attribute "{val}" with invalid length "{length_val}"')) length_val = lval new_value = [] - for idx in range(length_val): - idx_val = [] - for val in value: - if isinstance(val, list): - idx_val.append(val[idx]) - else: - idx_val.append(val) - new_value.append(join.join(idx_val)) + if length_val is not None: + for idx in range(length_val): + idx_val = [] + for val in value: + if isinstance(val, list): + idx_val.append(val[idx]) + else: + idx_val.append(val) + new_value.append(join.join(idx_val)) value = new_value else: value = [] diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index e9c90a6..3fd0806 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -132,9 +132,12 @@ class CacheOptionDescription(BaseOption): index, config_bag) option_bag.properties = frozenset() + value = await values.getvalue(option_bag) + if value is None: + continue await values._p_.setvalue(config_bag.connection, subpath, - await values.getvalue(option_bag), + value, owners.forced, index, False) @@ -163,9 +166,12 @@ class CacheOptionDescription(BaseOption): option_bags.append(option_bag) for option_bag in option_bags: option_bag.properties = frozenset() + value = await values.getvalue(option_bag) + if value is None: + continue await values._p_.setvalue(config_bag.connection, option_bag.path, - await values.getvalue(option_bag), + value, owners.forced, None, False,