From 302be618cee4ccbbe9620114cdcbcd2eb0a1e0d0 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 24 Jan 2023 22:31:32 +0100 Subject: [PATCH] follower in symlink --- tests/test_symlink.py | 14 ++++++++++++++ tiramisu/api.py | 34 +++++++++++++++++++++++++++++++++- tiramisu/value.py | 4 ++-- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/tests/test_symlink.py b/tests/test_symlink.py index fe8a1de..10000da 100644 --- a/tests/test_symlink.py +++ b/tests/test_symlink.py @@ -283,10 +283,17 @@ async def test_symlink_with_follower(config_type): await cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2']) assert await cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': ['val1', 'val2'], 'ip_admin_eth0.netmask_admin_eth0': [None, None], 'follower': [None, None]} # + assert await cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).owner.get() == 'default' + assert await cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).owner.get() == 'default' + assert await cfg.option('follower', 0).owner.get() == 'default' + assert await cfg.option('follower', 1).owner.get() == 'default' + assert await cfg.option('follower').owner.get() == ['default', 'default'] + # assert await cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).value.get() == None assert await cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == None assert await cfg.option('follower', 0).value.get() == None assert await cfg.option('follower', 1).value.get() == None + assert await cfg.option('follower').value.get() == [None, None] # await cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val3') assert await cfg.value.dict() == {'ip_admin_eth0.ip_admin_eth0': ['val1', 'val2'], 'ip_admin_eth0.netmask_admin_eth0': [None, 'val3'], 'follower': [None, 'val3']} @@ -295,6 +302,13 @@ async def test_symlink_with_follower(config_type): assert await cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).value.get() == 'val3' assert await cfg.option('follower', 0).value.get() == None assert await cfg.option('follower', 1).value.get() == 'val3' + assert await cfg.option('follower').value.get() == [None, 'val3'] + # + assert await cfg.option('ip_admin_eth0.netmask_admin_eth0', 0).owner.get() == 'default' + assert await cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).owner.get() == 'user' + assert await cfg.option('follower', 0).owner.get() == 'default' + assert await cfg.option('follower', 1).owner.get() == 'user' + assert await cfg.option('follower').owner.get() == ['default', 'user'] assert not await list_sessions() diff --git a/tiramisu/api.py b/tiramisu/api.py index 7a1ecda..e57b1ce 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -145,6 +145,8 @@ class CommonTiramisuOption(CommonTiramisu): self._subconfig = None def __getattr__(self, name): + if name == '__name__': + return self.__class__.__name__ raise APIError(_('unknown method "{}" in "{}"').format(name, self.__class__.__name__)) @@ -451,6 +453,19 @@ class TiramisuOptionOwner(CommonTiramisuOption): @option_and_connection async def get(self): """Get owner for a specified option""" + if self._option_bag.option.impl_is_follower() and self._option_bag.index is None: + if self._option_bag.option.impl_is_symlinkoption(): + length = await self._subconfig.cfgimpl_get_length_leadership(self._option_bag) + value = [] + for index in range(length): + soption_bag = self._option_bag.copy() + soption_bag.index = index + value.append(await self._values.getowner(soption_bag, + self._name, + ) + ) + return value + raise APIError('index must be set with a follower option') return await self._values.getowner(self._option_bag) @option_and_connection @@ -663,7 +678,10 @@ def option_type(typ): del config_bag.connection raise APIError(_('please specify a valid sub function ({})').format(func.__name__)) ret = await func(*args, **kwargs) - del config_bag.connection + try: + del config_bag.connection + except AttributeError: + pass return ret wrapped.func = func return wrapped @@ -698,6 +716,17 @@ class TiramisuOptionValue(CommonTiramisuOption): async def get(self): """Get option's value""" if self._option_bag.option.impl_is_follower() and self._option_bag.index is None: + # if it's a follower included in a symlinkoption, this should consider as a list + if self._option_bag.option.impl_is_symlinkoption(): + value = [] + for index in range(await self._len()): + soption_bag = self._option_bag.copy() + soption_bag.index = index + value.append(await self._subconfig.getattr(self._name, + soption_bag, + ) + ) + return value raise APIError('index must be set with a follower option') return await self._subconfig.getattr(self._name, self._option_bag, @@ -784,6 +813,9 @@ class TiramisuOptionValue(CommonTiramisuOption): @option_type('follower') async def len(self): + return await self._len() + + async def _len(self): """Length of follower option""" # for example if index is None if '_length' not in vars(self): diff --git a/tiramisu/value.py b/tiramisu/value.py index 0b577e3..21e8ccf 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -307,8 +307,8 @@ class Values: "convenience method to know if an option is empty" empty = opt._empty if index in [None, undefined] and opt.impl_is_multi(): - isempty = value is None or (not force_allow_empty_list and value == []) or \ - None in value or empty in value + isempty = value is None or (isinstance(value, list) and not force_allow_empty_list and value == []) or \ + (isinstance(value, list) and None in value) or empty in value else: isempty = value is None or value == empty or (opt.impl_is_submulti() and value == []) return isempty