when change len of calculated master, change len of slave too
authorEmmanuel Garette <egarette@cadoles.com>
Sun, 2 Feb 2014 17:20:01 +0000 (18:20 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Sun, 2 Feb 2014 17:20:01 +0000 (18:20 +0100)
test/test_option_calculation.py
test/test_state.py
tiramisu/autolib.py
tiramisu/value.py

index 91d61a4..89f9291 100644 (file)
@@ -373,6 +373,9 @@ def test_callback_multi_value():
     assert cfg.val2 == ['val']
     assert cfg.val3 == ['yes']
     assert cfg.val4 == ['val', 'yes']
+    cfg.val2.append('new')
+    assert cfg.val1 == ['val']
+    assert cfg.val2 == ['val', 'new']
 
 
 def test_callback_multi_list():
@@ -466,6 +469,66 @@ def test_callback_master_and_slaves_slave():
     assert cfg.val1.val2 == ['val2', 'val2', 'val']
 
 
+def test_callback_master_and_slaves_slave_cal():
+    val3 = StrOption('val3', "", multi=True)
+    val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val3, False),)})
+    val2 = StrOption('val2', "", multi=True, callback=return_val)
+    interface1 = OptionDescription('val1', '', [val1, val2])
+    interface1.impl_set_group_type(groups.master)
+    maconfig = OptionDescription('rootconfig', '', [interface1, val3])
+    cfg = Config(maconfig)
+    cfg.read_write()
+    assert cfg.val3 == []
+    assert cfg.val1.val1 == []
+    assert cfg.val1.val2 == []
+    cfg.val1.val1 = ['val1']
+    cfg.val3 = ['val1']
+    assert cfg.val1.val1 == ['val1']
+    assert cfg.val1.val2 == ['val']
+    assert cfg.val1.val1 == ['val1']
+    assert cfg.val1.val2 == ['val']
+    del(cfg.val1.val1)
+    cfg.val1.val2 = ['val']
+    cfg.val3 = ['val1', 'val2']
+    assert cfg.val1.val2 == ['val', 'val']
+    assert cfg.val1.val1 == ['val1', 'val2']
+    cfg.val1.val2 = ['val1', 'val2']
+    cfg.val3.pop(1)
+    # cannot remove slave's value because master is calculated
+    # so raise
+    raises(SlaveError, "cfg.val1.val1")
+    raises(SlaveError, "cfg.val1.val2")
+    cfg.val3 = ['val1', 'val2', 'val3']
+    assert cfg.val1.val2 == ['val1', 'val2', 'val']
+
+
+def test_callback_master_and_slaves_slave_cal2():
+    val3 = StrOption('val3', "", ['val', 'val'], multi=True)
+    val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val3, False),)})
+    val2 = StrOption('val2', "", ['val2', 'val2'], multi=True)
+    interface1 = OptionDescription('val1', '', [val1, val2])
+    interface1.impl_set_group_type(groups.master)
+    maconfig = OptionDescription('rootconfig', '', [interface1, val3])
+    cfg = Config(maconfig)
+    cfg.read_write()
+    assert cfg.val3 == ['val', 'val']
+    assert cfg.val1.val1 == ['val', 'val']
+    assert cfg.val1.val2 == ['val2', 'val2']
+    cfg.val3.pop(1)
+#    # cannot remove slave's value because master is calculated
+#    # so raise
+    raises(SlaveError, "cfg.val1.val1")
+    raises(SlaveError, "cfg.val1.val2")
+    cfg.val3 = ['val', 'val']
+    assert cfg.val3 == ['val', 'val']
+    assert cfg.val1.val1 == ['val', 'val']
+    assert cfg.val1.val2 == ['val2', 'val2']
+    raises(SlaveError, "cfg.val1.val1 = ['val']")
+    assert cfg.val3 == ['val', 'val']
+    assert cfg.val1.val1 == ['val', 'val']
+    assert cfg.val1.val2 == ['val2', 'val2']
+
+
 def test_callback_master_and_slaves_slave_list():
     val1 = StrOption('val1', "", multi=True)
     val2 = StrOption('val2', "", multi=True, callback=return_list)
index ef46ce2..7693cb6 100644 (file)
@@ -249,3 +249,46 @@ def test_state_values_owner():
         delete_session('29090931')
     except ConfigError:
         pass
+
+
+def test_state_unkown_setting_owner():
+    """load an unknow _owner, should create it"""
+    assert not 'supernewuser' in owners.__dict__
+    loads("""ccopy_reg
+_reconstructor
+p0
+(ctiramisu.setting
+Settings
+p1
+c__builtin__
+object
+p2
+Ntp3
+Rp4
+(dp5
+S'_owner'
+p6
+S'supernewuser'
+p7
+sS'_p_'
+p8
+g0
+(ctiramisu.storage.dictionary.setting
+Settings
+p9
+g2
+Ntp10
+Rp11
+(dp12
+S'_cache'
+p13
+(dp14
+sS'_permissives'
+p15
+(dp16
+sS'_properties'
+p17
+(dp18
+sbsb.
+.""")
+    assert 'supernewuser' in owners.__dict__
index ffdbb0a..cb3552a 100644 (file)
@@ -152,7 +152,10 @@ def carry_out_calculation(option, config, callback, callback_params,
                     opt)
                 # get value
                 try:
-                    value = config._getattr(path, force_permissive=True)
+                    value = config._getattr(path, force_permissive=True, validate=False)
+                    # convert to list, not modifie this multi
+                    if value.__class__.__name__ == 'Multi':
+                        value = list(value)
                 except PropertiesOptionError as err:
                     if force_permissive:
                         continue
index a02196a..863f2a1 100644 (file)
@@ -73,7 +73,7 @@ class Values(object):
         else:
             return value
 
-    def _getvalue(self, opt, path, validate=True):
+    def _getvalue(self, opt, path):
         """actually retrieves the value
 
         :param opt: the `option.Option()` object
@@ -82,14 +82,9 @@ class Values(object):
         if not self._p_.hasvalue(path):
             # if there is no value
             value = self._getdefault(opt)
-            if opt.impl_is_multi():
-                value = Multi(value, self.context, opt, path, validate)
         else:
             # if there is a value
             value = self._p_.getvalue(path)
-            if opt.impl_is_multi() and not isinstance(value, Multi):
-                # load value so don't need to validate if is not a Multi
-                value = Multi(value, self.context, opt, path, validate=False)
         return value
 
     def get_modified_values(self):
@@ -198,7 +193,7 @@ class Values(object):
         # ConfigError if properties did not raise.
         config_error = None
         force_permissives = None
-        # if value is callback and is not set
+        # if value has callback and is not set
         # or frozen with force_default_on_freeze
         if opt.impl_has_callback() and (
                 self._is_default_owner(path) or
@@ -208,7 +203,7 @@ class Values(object):
             if (opt.impl_is_multi() and
                     opt.impl_get_multitype() == multitypes.slave):
                 masterp = self._get_opt_path(opt.impl_get_master_slaves())
-                mastervalue = getattr(context, masterp)
+                mastervalue = context._getattr(masterp, validate=validate)
                 lenmaster = len(mastervalue)
                 if lenmaster == 0:
                     value = []
@@ -240,7 +235,10 @@ class Values(object):
             if opt.impl_is_multi():
                 value = Multi(value, self.context, opt, path, validate)
         else:
-            value = self._getvalue(opt, path, validate)
+            value = self._getvalue(opt, path)
+            if opt.impl_is_multi():
+                # load value so don't need to validate if is not a Multi
+                value = Multi(value, self.context, opt, path, validate=validate)
         if config_error is None and validate:
             opt.impl_validate(value, context, 'validator' in setting)
         if config_error is None and self._is_default_owner(path) and \
@@ -266,10 +264,27 @@ class Values(object):
         context = self._getcontext()
         opt.impl_validate(value, context,
                           'validator' in context.cfgimpl_get_settings())
-        if opt.impl_is_multi() and not isinstance(value, Multi):
+        if opt.impl_is_multi():
             value = Multi(value, self.context, opt, path, setitem=True)
+            # Save old value
+            if opt.impl_get_multitype() == multitypes.master and \
+                    self._p_.hasvalue(path):
+                old_value = self._p_.getvalue(path)
+                old_owner = self._p_.getowner(path, None)
+            else:
+                old_value = undefined
+                old_owner = undefined
         self._setvalue(opt, path, value, force_permissive=force_permissive,
                        is_write=is_write)
+        if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master:
+            try:
+                value._valid_master()
+            except Exception, err:
+                if old_value is not undefined:
+                    self._p_.setvalue(path, old_value, old_owner)
+                else:
+                    self._p_.resetvalue(path)
+                raise err
 
     def _setvalue(self, opt, path, value, force_permissive=False,
                   force_properties=None,
@@ -283,6 +298,8 @@ class Values(object):
                                         force_permissive=force_permissive,
                                         force_properties=force_properties)
         owner = context.cfgimpl_get_settings().getowner()
+        if isinstance(value, Multi):
+            value = list(value)
         self._p_.setvalue(path, value, owner)
 
     def getowner(self, opt):
@@ -403,6 +420,8 @@ class Multi(list):
         :param opt: the option object that have this Multi value
         :param setitem: only if set a value
         """
+        if isinstance(value, Multi):
+            raise ValueError(_('{0} is already a Multi ').format(opt._name))
         self.opt = opt
         self.path = path
         if not isinstance(context, weakref.ReferenceType):
@@ -412,8 +431,9 @@ class Multi(list):
             value = [value]
         if validate and self.opt.impl_get_multitype() == multitypes.slave:
             value = self._valid_slave(value, setitem)
-        elif validate and self.opt.impl_get_multitype() == multitypes.master:
-            self._valid_master(value)
+        elif not setitem and validate and \
+                self.opt.impl_get_multitype() == multitypes.master:
+            self._valid_master()
         super(Multi, self).__init__(value)
 
     def _getcontext(self):
@@ -433,12 +453,10 @@ class Multi(list):
         values = context.cfgimpl_get_values()
         masterp = context.cfgimpl_get_description().impl_get_path_by_opt(
             self.opt.impl_get_master_slaves())
-        mastervalue = getattr(context, masterp)
+        mastervalue = context._getattr(masterp, validate=False)
         masterlen = len(mastervalue)
         valuelen = len(value)
-        is_default_owner = not values._is_default_owner(self.path) or setitem
-        if valuelen > masterlen or (valuelen < masterlen and
-                                    is_default_owner):
+        if valuelen > masterlen or (valuelen < masterlen and setitem):
             raise SlaveError(_("invalid len for the slave: {0}"
                                " which has {1} as master").format(
                                    self.opt._name, masterp))
@@ -455,30 +473,12 @@ class Multi(list):
         #else: same len so do nothing
         return value
 
-    def _valid_master(self, value):
-        masterlen = len(value)
+    def _valid_master(self):
+        #masterlen = len(value)
         values = self._getcontext().cfgimpl_get_values()
         for slave in self.opt._master_slaves:
             path = values._get_opt_path(slave)
-            if not values._is_default_owner(path):
-                value_slave = values._getvalue(slave, path)
-                if len(value_slave) > masterlen:
-                    raise SlaveError(_("invalid len for the master: {0}"
-                                       " which has {1} as slave with"
-                                       " greater len").format(
-                                           self.opt._name, slave._name))
-                elif len(value_slave) < masterlen:
-                    for num in range(0, masterlen - len(value_slave)):
-                        if slave.impl_has_callback():
-                            # if callback add a value, but this value will not
-                            # change anymore automaticly (because this value
-                            # has owner)
-                            index = value_slave.__len__()
-                            value_slave.append(
-                                values._getcallback_value(slave, index=index),
-                                force=True)
-                        else:
-                            value_slave.append(undefined, force=True)
+            Multi(values._getvalue(slave, path), self.context, slave, path)
 
     def __setitem__(self, index, value):
         self._validate(value, index)
@@ -518,16 +518,15 @@ class Multi(list):
                         dvalue = values._getcallback_value(slave, index=index)
                     else:
                         dvalue = slave.impl_getdefault_multi()
-                    old_value = values.getitem(slave, path,
+                    old_value = values.getitem(slave, path, validate=False,
                                                validate_properties=False)
-                    if len(old_value) < self.__len__():
-                        values.getitem(slave, path,
-                                       validate_properties=False).append(
-                                           dvalue, force=True)
-                    else:
-                        values.getitem(slave, path,
-                                       validate_properties=False)[
-                                           index] = dvalue
+                    if len(old_value) + 1 != self.__len__():
+                        raise SlaveError(_("invalid len for the slave: {0}"
+                                           " which has {1} as master").format(
+                                               self.opt._name, self.__len__()))
+                    values.getitem(slave, path, validate=False,
+                                   validate_properties=False).append(
+                                       dvalue, force=True)
 
     def sort(self, cmp=None, key=None, reverse=False):
         if self.opt.impl_get_multitype() in [multitypes.slave,
@@ -592,12 +591,12 @@ class Multi(list):
             if self.opt.impl_get_multitype() == multitypes.slave:
                 raise SlaveError(_("cannot pop a value on a multi option {0}"
                                    " which is a slave").format(self.opt._name))
-            elif self.opt.impl_get_multitype() == multitypes.master:
+            if self.opt.impl_get_multitype() == multitypes.master:
                 for slave in self.opt.impl_get_master_slaves():
                     values = context.cfgimpl_get_values()
                     if not values.is_default_owner(slave):
                         #get multi without valid properties
-                        values.getitem(slave,
+                        values.getitem(slave, validate=False,
                                        validate_properties=False
                                        ).pop(index, force=True)
         #set value without valid properties