refactoring, the values are in an OptionValues object
authorgwen <gremond@cadoles.com>
Fri, 8 Feb 2013 10:50:22 +0000 (11:50 +0100)
committergwen <gremond@cadoles.com>
Fri, 8 Feb 2013 10:50:22 +0000 (11:50 +0100)
test/test_option_setting.py
tiramisu/config.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/value.py

index 3a5a4db..97a0705 100644 (file)
@@ -111,10 +111,10 @@ def test_access_with_multi_default():
     s = StrOption("string", "", default=["string"], multi=True)
     descr = OptionDescription("options", "", [s])
     config = Config(descr)
     s = StrOption("string", "", default=["string"], multi=True)
     descr = OptionDescription("options", "", [s])
     config = Config(descr)
-    assert config._cfgimpl_values.owners[s] == 'default'
+    assert config._cfgimpl_values.getowner(s) == 'default'
     config.string = ["foo", "bar"]
     assert config.string == ["foo", "bar"]
     config.string = ["foo", "bar"]
     assert config.string == ["foo", "bar"]
-    assert config._cfgimpl_values.owners[s] == 'user'
+    assert config._cfgimpl_values.getowner(s) == 'user'
 
 #def test_attribute_access_with_multi2():
 #    s = StrOption("string", "", default="string", multi=True)
 
 #def test_attribute_access_with_multi2():
 #    s = StrOption("string", "", default="string", multi=True)
index 514a985..47ee6dc 100644 (file)
@@ -25,9 +25,9 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
     AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound,
     MandatoryError, MethodCallError, NoValueReturned)
 from tiramisu.option import (OptionDescription, Option, SymLinkOption,
     AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound,
     MandatoryError, MethodCallError, NoValueReturned)
 from tiramisu.option import (OptionDescription, Option, SymLinkOption,
-    Multi, apply_requires)
+    apply_requires)
 from tiramisu.setting import groups, owners, Setting
 from tiramisu.setting import groups, owners, Setting
-from tiramisu.value import OptionValues
+from tiramisu.value import OptionValues, Multi
 
 # ____________________________________________________________
 class Config(object):
 
 # ____________________________________________________________
 class Config(object):
@@ -43,20 +43,23 @@ class Config(object):
         :param context: the current root config
         :type context: `Config`
         """
         :param context: the current root config
         :type context: `Config`
         """
+        # main option description
         self._cfgimpl_descr = descr
         self._cfgimpl_descr = descr
+        # sub option descriptions
+        self._cfgimpl_subconfigs = {}
         self._cfgimpl_parent = parent
         self._cfgimpl_parent = parent
+        if context is None:
+            self._cfgimpl_context = self
+        else:
+            self._cfgimpl_context = context
         if parent == None:
             self._cfgimpl_settings = Setting()
         if parent == None:
             self._cfgimpl_settings = Setting()
-            self._cfgimpl_values = OptionValues()
+            self._cfgimpl_values = OptionValues(self._cfgimpl_context)
         else:
             if context is None:
                 raise ConfigError("cannot find a value for this config")
             self._cfgimpl_settings = None
             self._cfgimpl_values = None
         else:
             if context is None:
                 raise ConfigError("cannot find a value for this config")
             self._cfgimpl_settings = None
             self._cfgimpl_values = None
-        if context is None:
-            self._cfgimpl_context = self
-        else:
-            self._cfgimpl_context = context
         "warnings are a great idea, let's make up a better use of it"
         self._cfgimpl_warnings = []
         self._cfgimpl_toplevel = self._cfgimpl_get_toplevel()
         "warnings are a great idea, let's make up a better use of it"
         self._cfgimpl_warnings = []
         self._cfgimpl_toplevel = self._cfgimpl_get_toplevel()
@@ -91,21 +94,9 @@ class Config(object):
         #max len for a master/slave group
         max_len_child = 0
         for child in self._cfgimpl_descr._children:
         #max len for a master/slave group
         max_len_child = 0
         for child in self._cfgimpl_descr._children:
-            if isinstance(child, Option):
-                if child.is_multi():
-                    childdef = Multi(copy(child.getdefault()), config=self,
-                                     opt=child)
-                    max_len_child = max(max_len_child, len(childdef))
-                    self._cfgimpl_context._cfgimpl_values[child] = childdef
-                    self._cfgimpl_context._cfgimpl_values.previous_values[child] = list(childdef)
-                else:
-                    childdef = child.getdefault()
-                    self._cfgimpl_context._cfgimpl_values[child] = childdef
-                    self._cfgimpl_context._cfgimpl_values.previous_values[child] = childdef
-                child.setowner(self, owners.default)
-            elif isinstance(child, OptionDescription):
+            if isinstance(child, OptionDescription):
                 self._validate_duplicates(child._children)
                 self._validate_duplicates(child._children)
-                self._cfgimpl_context._cfgimpl_values[child] = Config(child, parent=self,
+                self._cfgimpl_subconfigs[child] = Config(child, parent=self,
                                                 context=self._cfgimpl_context)
 
 #    def cfgimpl_update(self):
                                                 context=self._cfgimpl_context)
 
 #    def cfgimpl_update(self):
@@ -139,7 +130,7 @@ class Config(object):
         if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption:
             self._validate(name, getattr(self._cfgimpl_descr, name))
         self.setoption(name, value,
         if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption:
             self._validate(name, getattr(self._cfgimpl_descr, name))
         self.setoption(name, value,
-                            self._cfgimpl_context._cfgimpl_settings.get_owner())
+                            self._cfgimpl_context._cfgimpl_settings.getowner())
 
     def _validate(self, name, opt_or_descr, permissive=False):
         "validation for the setattr and the getattr"
 
     def _validate(self, name, opt_or_descr, permissive=False):
         "validation for the setattr and the getattr"
@@ -161,23 +152,6 @@ class Config(object):
                     " {1}".format(name, str(properties)),
                     properties)
 
                     " {1}".format(name, str(properties)),
                     properties)
 
-    def _is_empty(self, opt):
-        "convenience method to know if an option is empty"
-        if (not opt.is_multi() and self._cfgimpl_context._cfgimpl_values[opt] == None) or \
-            (opt.is_multi() and (self._cfgimpl_context._cfgimpl_values[opt] == [] or \
-                None in self._cfgimpl_context._cfgimpl_values[opt])):
-            return True
-        return False
-
-    def _test_mandatory(self, path, opt):
-        # mandatory options
-        mandatory = self._cfgimpl_context._cfgimpl_settings.mandatory
-        if opt.is_mandatory() and mandatory:
-            if self._is_empty(opt) and \
-                    opt.is_empty_by_default():
-                raise MandatoryError("option: {0} is mandatory "
-                                      "and shall have a value".format(path))
-
     def __getattr__(self, name):
         return self._getattr(name)
 
     def __getattr__(self, name):
         return self._getattr(name)
 
@@ -185,12 +159,11 @@ class Config(object):
         """fills a multi option with default and calculated values
         """
         # FIXME C'EST ENCORE DU N'IMPORTE QUOI
         """fills a multi option with default and calculated values
         """
         # FIXME C'EST ENCORE DU N'IMPORTE QUOI
-        value = self._cfgimpl_context._cfgimpl_values[opt]
         if not isinstance(result, list):
             _result = [result]
         else:
             _result = result
         if not isinstance(result, list):
             _result = [result]
         else:
             _result = result
-        return Multi(_result, value.config, opt=value.opt)
+        return Multi(_result, self._cfgimpl_context, opt)
 
     def _getattr(self, name, permissive=False):
         """
 
     def _getattr(self, name, permissive=False):
         """
@@ -211,56 +184,18 @@ class Config(object):
         if type(opt_or_descr) == SymLinkOption:
             rootconfig = self._cfgimpl_get_toplevel()
             return getattr(rootconfig, opt_or_descr.path)
         if type(opt_or_descr) == SymLinkOption:
             rootconfig = self._cfgimpl_get_toplevel()
             return getattr(rootconfig, opt_or_descr.path)
-        if opt_or_descr not in self._cfgimpl_context._cfgimpl_values:
-            raise AttributeError("%s object has no attribute %s" %
-                                 (self.__class__, name))
+
         self._validate(name, opt_or_descr, permissive)
         self._validate(name, opt_or_descr, permissive)
+        if isinstance(opt_or_descr, OptionDescription):
+            if opt_or_descr not in self._cfgimpl_subconfigs:
+                raise AttributeError("%s with name %s object has no attribute %s" %
+                                 (self.__class__, opt_or_descr._name, name))
+            return self._cfgimpl_subconfigs[opt_or_descr]
         # special attributes
         if name.startswith('_cfgimpl_'):
             # if it were in __dict__ it would have been found already
             return self.__dict__[name]
         # special attributes
         if name.startswith('_cfgimpl_'):
             # if it were in __dict__ it would have been found already
             return self.__dict__[name]
-            raise AttributeError("%s object has no attribute %s" %
-                                 (self.__class__, name))
-        if not isinstance(opt_or_descr, OptionDescription):
-            # options with callbacks
-            if opt_or_descr.has_callback():
-                value = self._cfgimpl_context._cfgimpl_values[opt_or_descr]
-                if (not opt_or_descr.is_frozen() or \
-                        not opt_or_descr.is_forced_on_freeze()) and \
-                        not opt_or_descr.is_default_owner(self):
-                    return value
-                try:
-                    result = opt_or_descr.getcallback_value(
-                            self._cfgimpl_get_toplevel())
-                except NoValueReturned, err:
-                    pass
-                else:
-                    if opt_or_descr.is_multi():
-                        _result = self.fill_multi(opt_or_descr, result)
-                    else:
-                        # this result **shall not** be a list
-                        if isinstance(result, list):
-                            raise ConfigError('invalid calculated value returned'
-                                ' for option {0} : shall not be a list'.format(name))
-                        _result = result
-                    if _result != None and not opt_or_descr.validate(_result,
-                                self._cfgimpl_context._cfgimpl_settings.validator):
-                        raise ConfigError('invalid calculated value returned'
-                            ' for option {0}'.format(name))
-                    self._cfgimpl_context._cfgimpl_values[opt_or_descr] = _result
-                    opt_or_descr.setowner(self, owners.default)
-            # frozen and force default
-            if not opt_or_descr.has_callback() and opt_or_descr.is_forced_on_freeze():
-                value = opt_or_descr.getdefault()
-                if opt_or_descr.is_multi():
-                    value = self.fill_multi(opt_or_descr, value,
-                                use_default_multi=True,
-                                default_multi=opt_or_descr.getdefault_multi())
-                self._cfgimpl_context._cfgimpl_values[opt_or_descr] = value
-                opt_or_descr.setowner(self, owners.default)
-            self._test_mandatory(name, opt_or_descr)
-        value = self._cfgimpl_context._cfgimpl_values[opt_or_descr]
-        return value
+        return self._cfgimpl_context._cfgimpl_values[opt_or_descr]
 
     def unwrap_from_name(self, name):
         """convenience method to extract and Option() object from the Config()
 
     def unwrap_from_name(self, name):
         """convenience method to extract and Option() object from the Config()
@@ -301,7 +236,7 @@ class Config(object):
                 if not isinstance(who, owners.DefaultOwner):
                     if type(value) != Multi:
                         if type(value) == list:
                 if not isinstance(who, owners.DefaultOwner):
                     if type(value) != Multi:
                         if type(value) == list:
-                            value = Multi(value, self, child)
+                            value = Multi(value, self._cfgimpl_context, child)
                         else:
                             raise ConfigError("invalid value for option:"
                                        " {0} that is set to multi".format(name))
                         else:
                             raise ConfigError("invalid value for option:"
                                        " {0} that is set to multi".format(name))
@@ -339,7 +274,7 @@ class Config(object):
                 except Exception, e:
                     raise e # HiddenOptionError or DisabledOptionError
                 homeconfig.setoption(name, value,
                 except Exception, e:
                     raise e # HiddenOptionError or DisabledOptionError
                 homeconfig.setoption(name, value,
-                            self._cfgimpl_context._cfgimpl_settings.get_owner())
+                            self._cfgimpl_context._cfgimpl_settings.getowner())
             elif len(candidates) > 1:
                 raise AmbigousOptionError(
                     'more than one option that ends with %s' % (key, ))
             elif len(candidates) > 1:
                 raise AmbigousOptionError(
                     'more than one option that ends with %s' % (key, ))
index 2dc6221..ca949af 100644 (file)
@@ -27,6 +27,7 @@ from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError,
     PropertiesOptionError)
 from tiramisu.autolib import carry_out_calculation
 from tiramisu.setting import groups, owners
     PropertiesOptionError)
 from tiramisu.autolib import carry_out_calculation
 from tiramisu.setting import groups, owners
+from tiramisu.value import Multi
 
 requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')]
 
 
 requires_actions = [('hide', 'show'), ('enable', 'disable'), ('freeze', 'unfreeze')]
 
@@ -37,62 +38,6 @@ for act1, act2 in requires_actions:
     reverse_actions[act1] = act2
     reverse_actions[act2] = act1
 # ____________________________________________________________
     reverse_actions[act1] = act2
     reverse_actions[act2] = act1
 # ____________________________________________________________
-# multi types
-
-class Multi(list):
-    """multi options values container
-    that support item notation for the values of multi options"""
-    def __init__(self, lst, config, opt):
-        """
-        :param lst: the Multi wraps a list value
-        :param config: the parent config
-        :param opt: the option object that have this Multi value
-        """
-        self.config = config
-        self.opt = opt
-        super(Multi, self).__init__(lst)
-
-    def __setitem__(self, key, value):
-        self._setvalue(value, key,
-                who=self.config._cfgimpl_context._cfgimpl_settings.get_owner())
-
-    def append(self, value):
-        """the list value can be updated (appened)
-        only if the option is a master
-        """
-        self._setvalue(value,
-                who=self.config._cfgimpl_context._cfgimpl_settings.get_owner())
-
-    def _setvalue(self, value, key=None, who=None):
-        if value != None:
-            if not self.opt._validate(value):
-                raise ConfigError("invalid value {0} "
-                    "for option {1}".format(str(value), self.opt._name))
-        oldvalue = list(self)
-        if key is None:
-            super(Multi, self).append(value)
-        else:
-            super(Multi, self).__setitem__(key, value)
-        if who != None:
-            if not isinstance(who, owners.Owner):
-                raise TypeError("invalid owner {0} for the value {1}".format(
-                                str(who), str(value)))
-            self.opt.setowner(self.config, getattr(owners, who))
-        self.config._cfgimpl_context._cfgimpl_values.previous_values[self.opt] = oldvalue
-
-    def pop(self, key):
-        """the list value can be updated (poped)
-        only if the option is a master
-
-        :param key: index of the element to pop
-        :return: the requested element
-
-        """
-        self.opt.setowner(self.config,
-                    self.config._cfgimpl_context._cfgimpl_settings.get_owner())
-        self.config._cfgimpl_context._cfgimpl_values.previous_values[self.opt] = list(self)
-        return super(Multi, self).pop(key)
-# ____________________________________________________________
 #
 class Option(HiddenBaseType, DisabledBaseType):
     """
 #
 class Option(HiddenBaseType, DisabledBaseType):
     """
@@ -267,7 +212,7 @@ class Option(HiddenBaseType, DisabledBaseType):
 
     def getowner(self, config):
         "config *must* be only the **parent** config (not the toplevel config)"
 
     def getowner(self, config):
         "config *must* be only the **parent** config (not the toplevel config)"
-        return config._cfgimpl_context._cfgimpl_values.owners[self]
+        return config._cfgimpl_context._cfgimpl_values.getowner(self)
 
     def reset(self, config):
         """resets the default value and owner
 
     def reset(self, config):
         """resets the default value and owner
@@ -297,13 +242,14 @@ class Option(HiddenBaseType, DisabledBaseType):
             if not self.is_multi() and value == '':
                 value = None
             if self.is_multi() and '' in value:
             if not self.is_multi() and value == '':
                 value = None
             if self.is_multi() and '' in value:
-                value = Multi([{'': None}.get(i, i) for i in value], config, self)
+                value = Multi([{'': None}.get(i, i) for i in value],
+                                config._cfgimpl_context, self)
             if config._cfgimpl_context._cfgimpl_settings.is_mandatory() \
                 and ((self.is_multi() and value == []) or \
                 (not self.is_multi() and value is None)):
                 raise MandatoryError('cannot change the value to %s for '
               'option %s' % (value, name))
             if config._cfgimpl_context._cfgimpl_settings.is_mandatory() \
                 and ((self.is_multi() and value == []) or \
                 (not self.is_multi() and value is None)):
                 raise MandatoryError('cannot change the value to %s for '
               'option %s' % (value, name))
-        if self not in config._cfgimpl_context._cfgimpl_values:
+        if self not in config._cfgimpl_descr._children:
             raise AttributeError('unknown option %s' % (name))
 
         if config._cfgimpl_context._cfgimpl_settings.is_frozen_for_everything():
             raise AttributeError('unknown option %s' % (name))
 
         if config._cfgimpl_context._cfgimpl_settings.is_frozen_for_everything():
@@ -315,10 +261,10 @@ class Option(HiddenBaseType, DisabledBaseType):
             raise TypeError('cannot change the value to %s for '
                'option %s this option is frozen' % (str(value), name))
         apply_requires(self, config)
             raise TypeError('cannot change the value to %s for '
                'option %s this option is frozen' % (str(value), name))
         apply_requires(self, config)
-        if type(config._cfgimpl_context._cfgimpl_values[self]) == Multi:
-            config._cfgimpl_context._cfgimpl_values.previous_values[self] = list(config._cfgimpl_context._cfgimpl_values[self])
-        else:
-            config._cfgimpl_context._cfgimpl_values.previous_values[self] = config._cfgimpl_context._cfgimpl_values[self]
+#        if type(config._cfgimpl_context._cfgimpl_values[self]) == Multi:
+#            config._cfgimpl_context._cfgimpl_values.previous_values[self] = list(config._cfgimpl_context._cfgimpl_values[self])
+#        else:
+#            config._cfgimpl_context._cfgimpl_values.previous_values[self] = config._cfgimpl_context._cfgimpl_values[self]
         config._cfgimpl_context._cfgimpl_values[self] = value
 
     def getkey(self, value):
         config._cfgimpl_context._cfgimpl_values[self] = value
 
     def getkey(self, value):
index 7690dfe..ae9fe94 100644 (file)
@@ -197,11 +197,11 @@ class Setting():
         "freeze flag at Config level"
         return self.frozen
 
         "freeze flag at Config level"
         return self.frozen
 
-    def set_owner(self, owner):
+    def setowner(self, owner):
         ":param owner: sets the default value for owner at the Config level"
         if not isinstance(owner, owners.Owner):
             raise TypeError("invalid generic owner {0}".format(str(owner)))
         self.owner = owner
 
         ":param owner: sets the default value for owner at the Config level"
         if not isinstance(owner, owners.Owner):
             raise TypeError("invalid generic owner {0}".format(str(owner)))
         self.owner = owner
 
-    def get_owner(self):
+    def getowner(self):
         return self.owner
         return self.owner
index 3ce8d1b..3abdccd 100644 (file)
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
-"takes care of the option's values"
+"takes care of the option's values and multi values"
 # Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors)
 #
 # This program is free software; you can redistribute it and/or modify
 # Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors)
 #
 # This program is free software; you can redistribute it and/or modify
 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
+from tiramisu.error import NoValueReturned, MandatoryError
+from tiramisu.setting import owners
 
 class OptionValues(object):
 
 class OptionValues(object):
-    def __init__(self):
+    def __init__(self, context):
         self.owners = {}
         "Config's root indeed is in charge of the `Option()`'s values"
         self.values = {}
         self.previous_values = {}
         self.owners = {}
         "Config's root indeed is in charge of the `Option()`'s values"
         self.values = {}
         self.previous_values = {}
+        self.context = context
 
 
-    def __getitem__(self, opt_or_descr):
-        return self.values[opt_or_descr]
+    def _get_value(self, opt):
+        "special case for the multis: they never return None"
+        if opt.is_multi():
+            if opt not in self.values:
+                # FIXME : default value for a multi, we shall work on groups
+                return Multi(opt.getdefault(), self.context, opt)
+        else:
+            if opt not in self.values:
+                return opt.getdefault()
+        return self.values[opt]
 
 
-    def __setitem__(self, opt_or_descr, value):
-        self.values[opt_or_descr] = value
+    def _is_empty(self, opt):
+        "convenience method to know if an option is empty"
+        if (not opt.is_multi() and self._get_value(opt) == None) or \
+            (opt.is_multi() and (self._get_value(opt) == [] or \
+                None in self._get_value(opt))):
+            return True
+        return False
 
 
-    def __contains__(self, opt_or_descr):
-        return opt_or_descr in self.values
+    def _test_mandatory(self, opt):
+        # mandatory options
+        mandatory = self.context._cfgimpl_settings.mandatory
+        if opt.is_mandatory() and mandatory:
+            if self._is_empty(opt) and \
+                    opt.is_empty_by_default():
+                raise MandatoryError("option: {0} is mandatory "
+                                      "and shall have a value".format(opt._name))
+
+    def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
+        """fills a multi option with default and calculated values
+        """
+        value = self._get_value(opt)
+        if not isinstance(result, list):
+            _result = [result]
+        else:
+            _result = result
+        return Multi(_result, self.context, opt)
+
+    def __getitem__(self, opt):
+        # options with callbacks
+        if opt.has_callback():
+            if (not opt.is_frozen() or \
+                    not opt.is_forced_on_freeze()) and \
+                    not opt.is_default_owner(self):
+                return self._get_value(opt)
+            try:
+                result = opt.getcallback_value(
+                        self.context)
+            except NoValueReturned, err:
+                pass
+            else:
+                if opt.is_multi():
+                    #FIXME revoir les multis
+                    _result = fill_multi(opt, result)
+                else:
+                    # this result **shall not** be a list
+                    if isinstance(result, list):
+                        raise ConfigError('invalid calculated value returned'
+                            ' for option {0} : shall not be a list'.format(name))
+                    _result = result
+                if _result != None and not opt.validate(_result,
+                            self.context._cfgimpl_settings.validator):
+                    raise ConfigError('invalid calculated value returned'
+                        ' for option {0}'.format(name))
+                self.values[opt] = _result
+                self.owners[opt] = owners.default
+        # frozen and force default
+        if not opt.has_callback() and opt.is_forced_on_freeze():
+            value = opt.getdefault()
+            if opt.is_multi():
+                #FIXME le fill_multi
+                value = self.fill_multi(opt, value,
+                            use_default_multi=True,
+                            default_multi=opt.getdefault_multi())
+            self.values[opt] = value
+            self.owners[opt] = owners.default
+        self._test_mandatory(opt)
+        value = self._get_value(opt)
+        return value
+
+    def __setitem__(self, opt, value):
+        if opt in self.values:
+            old_value = self.values[opt]
+        else:
+            old_value = None
+        if type(old_value) == Multi:
+            self.previous_values[opt] = list(value)
+        else:
+            self.previous_values[opt] = value
+        self.values[opt] = value
+
+    def __contains__(self, opt):
+        return opt in self.values
+
+    #____________________________________________________________
+    def setowner(self, opt, owner):
+        pass
+
+    def getowner(self, opt):
+        return self.owners.get(opt, owners.default)
+# ____________________________________________________________
+# multi types
+class Multi(list):
+    """multi options values container
+    that support item notation for the values of multi options"""
+    def __init__(self, lst, context, opt):
+        """
+        :param lst: the Multi wraps a list value
+        :param context: the context has the settings and the values
+        :param opt: the option object that have this Multi value
+        """
+        self.settings = context._cfgimpl_settings
+        self.opt = opt
+        self.values = context._cfgimpl_values
+        super(Multi, self).__init__(lst)
+
+    def __setitem__(self, key, value):
+        self._setvalue(value, key, who=self.settings.getowner())
+
+    def append(self, value):
+        """the list value can be updated (appened)
+        only if the option is a master
+        """
+        self._setvalue(value, who=self.settings.getowner(self.opt))
+
+    def _setvalue(self, value, key=None, who=None):
+        if value != None:
+            if not self.opt._validate(value):
+                raise ConfigError("invalid value {0} "
+                    "for option {1}".format(str(value), self.opt._name))
+        oldvalue = list(self)
+        if key is None:
+            super(Multi, self).append(value)
+        else:
+            super(Multi, self).__setitem__(key, value)
+        if who != None:
+            if not isinstance(who, owners.Owner):
+                raise TypeError("invalid owner {0} for the value {1}".format(
+                                str(who), str(value)))
+            self.values.setowner(self.opt, getattr(owners, who))
+        self.values.previous_values[self.opt] = oldvalue
+
+    def pop(self, key):
+        """the list value can be updated (poped)
+        only if the option is a master
+
+        :param key: index of the element to pop
+        :return: the requested element
+        """
+        self.values.setowner(opt, self.settings.get_owner())
+        self.values.previous_values[self.opt] = list(self)
+        return super(Multi, self).pop(key)