diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 1585a80..9196b7b 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -5,13 +5,21 @@ from tiramisu.setting import groups from tiramisu.config import Config from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ StrOption, OptionDescription -from tiramisu.error import PropertiesOptionError, ConflictError +from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError -def return_value(): +def return_val(): return 'val' +def return_list(): + return ['val', 'val'] + + +def return_value(value): + return value + + def make_description(): gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False) @@ -277,7 +285,7 @@ def test_freeze_and_has_callback(): def test_callback(): - val1 = StrOption('val1', "", callback=return_value) + val1 = StrOption('val1', "", callback=return_val) maconfig = OptionDescription('rootconfig', '', [val1]) cfg = Config(maconfig) cfg.read_write() @@ -288,9 +296,115 @@ def test_callback(): assert cfg.val1 == 'val' -def test_callback_master_and_slaves(): +def test_callback_value(): + val1 = StrOption('val1', "", 'val') + val2 = StrOption('val2', "", callback=return_value, callback_params={'': (('val1', False),)}) + maconfig = OptionDescription('rootconfig', '', [val1, val2]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == 'val' + assert cfg.val2 == 'val' + cfg.val1 = 'new-val' + assert cfg.val1 == 'new-val' + assert cfg.val2 == 'new-val' + del(cfg.val1) + assert cfg.val1 == 'val' + assert cfg.val2 == 'val' + + +def test_callback_list(): + val1 = StrOption('val1', "", callback=return_list) + maconfig = OptionDescription('rootconfig', '', [val1]) + cfg = Config(maconfig) + cfg.read_write() + raises(ValueError, "cfg.val1") + + +def test_callback_multi(): + val1 = StrOption('val1', "", callback=return_val, multi=True) + maconfig = OptionDescription('rootconfig', '', [val1]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == ['val'] + cfg.val1 = ['new-val'] + assert cfg.val1 == ['new-val'] + cfg.val1.append('new-val2') + assert cfg.val1 == ['new-val', 'new-val2'] + del(cfg.val1) + assert cfg.val1 == ['val'] + + +def test_callback_multi_value(): + val1 = StrOption('val1', "", ['val'], multi=True) + val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': (('val1', False),)}) + maconfig = OptionDescription('rootconfig', '', [val1, val2]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == ['val'] + assert cfg.val2 == ['val'] + cfg.val1 = ['new-val'] + assert cfg.val1 == ['new-val'] + assert cfg.val2 == ['new-val'] + cfg.val1.append('new-val2') + assert cfg.val1 == ['new-val', 'new-val2'] + assert cfg.val2 == ['new-val', 'new-val2'] + del(cfg.val1) + assert cfg.val1 == ['val'] + assert cfg.val2 == ['val'] + + +def test_callback_multi_list(): + val1 = StrOption('val1', "", callback=return_list, multi=True) + maconfig = OptionDescription('rootconfig', '', [val1]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == ['val', 'val'] + cfg.val1 = ['new-val'] + assert cfg.val1 == ['new-val'] + cfg.val1.append('new-val2') + assert cfg.val1 == ['new-val', 'new-val2'] + del(cfg.val1) + assert cfg.val1 == ['val', 'val'] + + +def test_callback_master_and_slaves_master(): + val1 = StrOption('val1', "", multi=True, callback=return_val) + val2 = StrOption('val2', "", multi=True) + interface1 = OptionDescription('val1', '', [val1, val2]) + interface1.impl_set_group_type(groups.master) + maconfig = OptionDescription('rootconfig', '', [interface1]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1.val1 == ['val'] + cfg.val1.val1.append(None) + assert cfg.val1.val1 == ['val', 'val'] + assert cfg.val1.val2 == [None, None] + + +def test_callback_master_and_slaves_master_list(): + val1 = StrOption('val1', "", multi=True, callback=return_list) + val2 = StrOption('val2', "", multi=True) + interface1 = OptionDescription('val1', '', [val1, val2]) + interface1.impl_set_group_type(groups.master) + maconfig = OptionDescription('rootconfig', '', [interface1]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1.val1 == ['val', 'val'] + assert cfg.val1.val2 == [None, None] + cfg.val1.val1.append(None) + assert cfg.val1.val1 == ['val', 'val', None] + assert cfg.val1.val2 == [None, None, None] + del(cfg.val1.val1) + assert cfg.val1.val1 == ['val', 'val'] + assert cfg.val1.val2 == [None, None] + assert cfg.val1.val1.pop(1) + assert cfg.val1.val1 == ['val'] + assert cfg.val1.val2 == [None] + + +def test_callback_master_and_slaves_slave(): val1 = StrOption('val1', "", multi=True) - val2 = StrOption('val2', "", multi=True, callback=return_value) + val2 = StrOption('val2', "", multi=True, callback=return_val) interface1 = OptionDescription('val1', '', [val1, val2]) interface1.impl_set_group_type(groups.master) maconfig = OptionDescription('rootconfig', '', [interface1]) @@ -320,3 +434,56 @@ def test_callback_master_and_slaves(): # cfg.val1.val1.append('val3') assert cfg.val1.val2 == ['val2', 'val2', 'val'] + + +def test_callback_master_and_slaves_slave_list(): + val1 = StrOption('val1', "", multi=True) + val2 = StrOption('val2', "", multi=True, callback=return_list) + interface1 = OptionDescription('val1', '', [val1, val2]) + interface1.impl_set_group_type(groups.master) + maconfig = OptionDescription('rootconfig', '', [interface1]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1.val2 == [] + cfg.val1.val1 = ['val1', 'val2'] + assert cfg.val1.val1 == ['val1', 'val2'] + assert cfg.val1.val2 == ['val', 'val'] + cfg.val1.val1 = ['val1'] + #wrong len + raises(SlaveError, 'cfg.val1.val2') + + +def test_callback_master_and_slaves_value(): + val1 = StrOption('val1', "", multi=True) + val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': (('val1.val1', False),)}) + interface1 = OptionDescription('val1', '', [val1, val2]) + interface1.impl_set_group_type(groups.master) + maconfig = OptionDescription('rootconfig', '', [interface1]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1.val1 == [] + assert cfg.val1.val2 == [] + # + cfg.val1.val1 = ['val1'] + assert cfg.val1.val1 == ['val1'] + assert cfg.val1.val2 == ['val1'] + # + cfg.val1.val1.append('val2') + assert cfg.val1.val1 == ['val1', 'val2'] + assert cfg.val1.val2 == ['val1', 'val2'] + # + cfg.val1.val1 = ['val1', 'val2', 'val3'] + assert cfg.val1.val1 == ['val1', 'val2', 'val3'] + assert cfg.val1.val2 == ['val1', 'val2', 'val3'] + # + cfg.val1.val1.pop(2) + assert cfg.val1.val1 == ['val1', 'val2'] + assert cfg.val1.val2 == ['val1', 'val2'] + # + cfg.val1.val2 = ['val2', 'val2'] + assert cfg.val1.val2 == ['val2', 'val2'] + # + cfg.val1.val1.append('val3') + assert cfg.val1.val2 == ['val2', 'val2', 'val3'] + + diff --git a/test/test_parsing_group.py b/test/test_parsing_group.py index 0365f52..934d3ef 100644 --- a/test/test_parsing_group.py +++ b/test/test_parsing_group.py @@ -229,8 +229,13 @@ def test_values_with_master_and_slaves_master(): cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.145"] cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0'] raises(SlaveError, 'cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145"]') + assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['255.255.255.0', '255.255.255.0'] cfg.ip_admin_eth0.ip_admin_eth0.pop(1) assert cfg.ip_admin_eth0.ip_admin_eth0 == ["192.168.230.145"] + assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['255.255.255.0'] + del(cfg.ip_admin_eth0.ip_admin_eth0) + assert cfg.ip_admin_eth0.ip_admin_eth0 == [] + assert cfg.ip_admin_eth0.netmask_admin_eth0 == [] def test_values_with_master_owner(): diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 69dd0c9..2e45d38 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -30,7 +30,7 @@ from tiramisu.i18n import _ # return calc_factory(name, callback, callback_params, config) -def carry_out_calculation(name, config, callback, callback_params): +def carry_out_calculation(name, config, callback, callback_params, index=None): "a function that carries out a calculation for an option's value" #callback, callback_params = option.getcallback() #if callback_params is None: @@ -71,7 +71,11 @@ def carry_out_calculation(name, config, callback, callback_params): if one_is_multi: ret = [] - for incr in range(len_multi): + if index: + range_ = [index] + else: + range_ = range(len_multi) + for incr in range_: tcp = {} params = [] for key, couples in tcparams.items(): @@ -91,10 +95,13 @@ def carry_out_calculation(name, config, callback, callback_params): else: tcp[key] = value calc = calculate(name, callback, params, tcp) - if isinstance(calc, list): - ret.extend(calc) + if index: + ret = calc else: - ret.append(calc) + if isinstance(calc, list): + ret.extend(calc) + else: + ret.append(calc) return ret else: diff --git a/tiramisu/value.py b/tiramisu/value.py index e90e59c..0f07759 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -18,6 +18,7 @@ # # ____________________________________________________________ from time import time +from copy import copy from tiramisu.error import ConfigError, SlaveError from tiramisu.setting import owners, multitypes, expires_time from tiramisu.autolib import carry_out_calculation @@ -47,9 +48,13 @@ class Values(object): def _get_default(self, opt): meta = self.context.cfgimpl_get_meta() if meta is not None: - return meta.cfgimpl_get_values()[opt] + value = meta.cfgimpl_get_values()[opt] else: - return opt.impl_getdefault() + value = opt.impl_getdefault() + if opt.impl_is_multi(): + return copy(value) + else: + return value def _get_value(self, opt, validate=True): "return value or default value if not set" @@ -72,6 +77,9 @@ class Values(object): opt.impl_validate(opt.impl_getdefault(), self.context, 'validator' in setting) self.context.cfgimpl_reset_cache() + if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master: + for slave in opt.impl_get_master_slaves(): + self._reset(slave) del(self._values[opt]) def _is_empty(self, opt, value): @@ -83,13 +91,14 @@ class Values(object): return True return False - def _getcallback_value(self, opt): + def _getcallback_value(self, opt, index=None): callback, callback_params = opt._callback if callback_params is None: callback_params = {} return carry_out_calculation(opt._name, config=self.context, callback=callback, - callback_params=callback_params) + callback_params=callback_params, + index=index) def __getitem__(self, opt): return self.getitem(opt) @@ -133,11 +142,8 @@ class Values(object): if not no_value_slave: value = self._getcallback_value(opt) if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave: - if isinstance(value, list): - raise ValueError('callback must not return list ' - 'in slave {0}: {1}'.format(opt._name, - value)) - value = [value for i in range(lenmaster)] + if not isinstance(value, list): + value = [value for i in range(lenmaster)] if opt.impl_is_multi(): value = Multi(value, self.context, opt, validate) #suppress value if already set @@ -297,20 +303,27 @@ class Multi(list): raise SlaveError(_("cannot append a value on a multi option {0}" " which is a slave").format(self.opt._name)) elif self.opt.impl_get_multitype() == multitypes.master: - for slave in self.opt.impl_get_master_slaves(): - values = self.context.cfgimpl_get_values() - if not values.is_default_owner(slave): - if slave.impl_has_callback(): - dvalue = values._getcallback_value(slave) - else: - dvalue = slave.impl_getdefault_multi() - #get multi without valid properties - values.getitem(slave, validate_properties=False).append( - dvalue, force=True) + values = self.context.cfgimpl_get_values() + if value is None and self.opt.impl_has_callback(): + value = values._getcallback_value(self.opt) + #Force None il return a list + if isinstance(value, list): + value = None self._validate(value) #set value without valid properties self.context.cfgimpl_get_values()._setvalue(self.opt, self, validate_properties=not force) super(Multi, self).append(value) + if not force and self.opt.impl_get_multitype() == multitypes.master: + for slave in self.opt.impl_get_master_slaves(): + if not values.is_default_owner(slave): + if slave.impl_has_callback(): + index = self.__len__() - 1 + dvalue = values._getcallback_value(slave, index=index) + else: + dvalue = slave.impl_getdefault_multi() + #get multi without valid properties + values.getitem(slave, validate_properties=False).append( + dvalue, force=True) def _validate(self, value): if value is not None and not self.opt._validate(value):