Works callback/multi/master-slave
authorEmmanuel Garette <egarette@cadoles.com>
Wed, 12 Jun 2013 08:22:50 +0000 (10:22 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Wed, 12 Jun 2013 08:22:50 +0000 (10:22 +0200)
- in multi, never modify _default value
- _reset slave when _reset master
- if append None in master with callback return not a list, replace None by the value
- list support in slave

test/test_option_calculation.py
test/test_parsing_group.py
tiramisu/autolib.py
tiramisu/value.py

index 1585a80..9196b7b 100644 (file)
@@ -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']
+
+
index 0365f52..934d3ef 100644 (file)
@@ -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():
index 69dd0c9..2e45d38 100644 (file)
@@ -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:
index e90e59c..0f07759 100644 (file)
@@ -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):