properties validation not in setting and now launch when modify multi
authorEmmanuel Garette <egarette@cadoles.com>
Wed, 17 Apr 2013 19:33:34 +0000 (21:33 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Wed, 17 Apr 2013 19:33:34 +0000 (21:33 +0200)
test/test_freeze.py [new file with mode: 0644]
test/test_mandatory.py
test/test_option_consistency.py
test/test_option_type.py
tiramisu/config.py
tiramisu/error.py
tiramisu/setting.py
tiramisu/value.py

diff --git a/test/test_freeze.py b/test/test_freeze.py
new file mode 100644 (file)
index 0000000..d4f6d65
--- /dev/null
@@ -0,0 +1,138 @@
+# coding: utf-8
+"frozen and hidden values"
+import autopath
+
+from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
+    StrOption, OptionDescription
+from tiramisu.config import Config
+from tiramisu.error import PropertiesOptionError
+
+
+#____________________________________________________________
+#freeze
+def make_description_freeze():
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                  ('std', 'thunk'), 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=[True], multi=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                requires=(('boolop', True, 'hidden'),))
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=(('boolop', True, 'hidden'),))
+
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
+                              wantref_option, stroption,
+                              wantframework_option,
+                              intoption, boolop])
+    return descr
+
+
+def test_freeze_whole_config():
+    descr = make_description_freeze()
+    conf = Config(descr)
+    setting = conf.cfgimpl_get_settings()
+    setting.read_write()
+    setting.enable_property('everything_frozen')
+    assert conf.gc.dummy is False
+    prop = []
+    try:
+        conf.gc.dummy = True
+    except PropertiesOptionError, err:
+        prop = err.proptype
+    assert 'frozen' in prop
+    setting.disable_property('everything_frozen')
+    conf.gc.dummy = True
+    assert conf.gc.dummy is True
+
+
+def test_freeze_one_option():
+    "freeze an option "
+    descr = make_description_freeze()
+    conf = Config(descr)
+    setting = conf.cfgimpl_get_settings()
+    setting.read_write()
+    #freeze only one option
+    dummy = conf.unwrap_from_path('gc.dummy')
+    setting.add_property('frozen', dummy)
+    assert conf.gc.dummy is False
+    prop = []
+    try:
+        conf.gc.dummy = True
+    except PropertiesOptionError, err:
+        prop = err.proptype
+    assert 'frozen' in prop
+
+
+def test_frozen_value():
+    "setattr a frozen value at the config level"
+    s = StrOption("string", "", default="string")
+    descr = OptionDescription("options", "", [s])
+    config = Config(descr)
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    setting.enable_property('frozen')
+    setting.add_property('frozen', s)
+    prop = []
+    try:
+        config.string = "egg"
+    except PropertiesOptionError, err:
+        prop = err.proptype
+    assert 'frozen' in prop
+
+
+def test_freeze():
+    "freeze a whole configuration object"
+    descr = make_description_freeze()
+    conf = Config(descr)
+    setting = conf.cfgimpl_get_settings()
+    setting.read_write()
+    setting.enable_property('frozen')
+    name = conf.unwrap_from_path("gc.name")
+    setting.add_property('frozen', name)
+    prop = []
+    try:
+        conf.gc.name = 'framework'
+    except PropertiesOptionError, err:
+        prop = err.proptype
+    assert 'frozen' in prop
+
+
+def test_freeze_multi():
+    descr = make_description_freeze()
+    conf = Config(descr)
+    setting = conf.cfgimpl_get_settings()
+    setting.read_write()
+    setting.enable_property('frozen')
+    obj = conf.unwrap_from_path('boolop')
+    setting.add_property('frozen', obj)
+    prop = []
+    try:
+        conf.boolop = [True]
+    except PropertiesOptionError, err:
+        prop = err.proptype
+    assert 'frozen' in prop
+
+
+def test_freeze_get_multi():
+    descr = make_description_freeze()
+    conf = Config(descr)
+    setting = conf.cfgimpl_get_settings()
+    setting.read_write()
+    setting.enable_property('frozen')
+    valmulti = conf.boolop
+    valmulti.append(False)
+    obj = conf.unwrap_from_path('boolop')
+    setting.add_property('frozen', obj)
+    prop = []
+    try:
+        valmulti.append(False)
+    except PropertiesOptionError, err:
+        prop = err.proptype
+    assert 'frozen' in prop
index c598cd3..6172c2a 100644 (file)
@@ -150,6 +150,15 @@ def test_mandatory_multi_empty():
     assert 'mandatory' in prop
 
 
+def test_mandatory_multi_append():
+    descr = make_description()
+    config = Config(descr)
+    setting = config.cfgimpl_get_settings()
+    config.str3 = ['yes']
+    setting.read_write()
+    config.str3.append(None)
+
+
 def test_mandatory_disabled():
     descr = make_description()
     config = Config(descr)
@@ -212,3 +221,16 @@ def test_mandatory_warnings_disabled():
     assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3']
     setting.add_property('disabled', descr.str)
     assert list(mandatory_warnings(config)) == ['str1', 'str2', 'str3']
+
+
+def test_mandatory_warnings_frozen():
+    descr = make_description()
+    config = Config(descr)
+    config.str = ''
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    config.str
+    assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3']
+    setting.add_property('frozen', descr.str)
+    setting.read_only()
+    assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3']
index 4d3b8f5..735724f 100644 (file)
@@ -3,6 +3,7 @@ from py.test import raises
 
 from tiramisu.config import *
 from tiramisu.option import *
+from error import ConfigError
 
 def make_description():
     gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
@@ -242,7 +243,7 @@ def test_has_callback():
     dummy = config.unwrap_from_path('gc.dummy')
     setting.enable_property('freeze')
     setting.add_property('frozen', dummy)
-    raises(ConfigError, "config.gc.dummy = True")
+    raises(PropertiesOptionError, "config.gc.dummy = True")
 
 def test_freeze_and_has_callback_with_setoption():
     descr = make_description_callback()
@@ -253,5 +254,5 @@ def test_freeze_and_has_callback_with_setoption():
     config.cfgimpl_get_settings().enable_property('freeze')
     dummy = config.unwrap_from_path('gc.dummy')
     config.cfgimpl_get_settings().add_property('frozen', dummy)
-    raises(ConfigError, "config.gc.setoption('dummy', descr.gc.dummy, True)")
+    raises(PropertiesOptionError, "config.gc.setoption('dummy', descr.gc.dummy, True)")
 #____________________________________________________________
index 3e66e7c..12a777d 100644 (file)
@@ -3,21 +3,24 @@
 import autopath
 from py.test import raises
 
-from tiramisu.config import *
-from tiramisu.option import *
+from tiramisu.config import Config
+from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
+    StrOption, OptionDescription
+from tiramisu.error import PropertiesOptionError
+
 
 def make_description():
     gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False, properties=(('hidden'),))
     objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ('std', 'thunk'), 'std')
+                                  ('std', 'thunk'), 'std')
     booloption = BoolOption('bool', 'Test boolean option', default=True)
     intoption = IntOption('int', 'Test int option', default=0)
     floatoption = FloatOption('float', 'Test float option', default=2.3)
     stroption = StrOption('str', 'Test string option', default="abc")
 
     wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=(('gc.name', 'ref', 'hidden'),))
+                                requires=(('gc.name', 'ref', 'hidden'),))
     wantframework_option = BoolOption('wantframework', 'Test requires',
                                       default=False,
                                       requires=(('gc.name', 'framework', 'hidden'),))
@@ -29,75 +32,12 @@ def make_description():
 
     gcgroup = OptionDescription('gc', '', [subgroup, gcoption, gcdummy, floatoption])
     descr = OptionDescription('trs', '', [gcgroup, booloption, objspaceoption,
-                                           wantref_option, stroption,
-                                           wantframework_option,
-                                           intoption])
+                                          wantref_option, stroption,
+                                          wantframework_option,
+                                          intoption])
     return descr
-#____________________________________________________________
-#freeze
-def make_description_freeze():
-    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ('std', 'thunk'), 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
-    wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=(('boolop', True, 'hidden'),))
-    wantframework_option = BoolOption('wantframework', 'Test requires',
-                                      default=False,
-                                      requires=(('boolop', True, 'hidden'),))
 
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
-    descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
-                                           wantref_option, stroption,
-                                           wantframework_option,
-                                           intoption, boolop])
-    return descr
 
-def test_freeze_whole_config():
-    descr = make_description_freeze()
-    conf = Config(descr)
-    settings = conf.cfgimpl_get_settings()
-    settings.enable_property('everything_frozen')
-    assert conf.gc.dummy == False
-    raises(ConfigError, "conf.gc.dummy = True")
-    settings.disable_property('everything_frozen')
-    conf.gc.dummy = True
-    assert conf.gc.dummy == True
-
-def test_freeze_one_option():
-    "freeze an option "
-    descr = make_description_freeze()
-    conf = Config(descr)
-    setting = conf.cfgimpl_get_settings()
-    setting.read_write()
-    #freeze only one option
-    dummy = conf.unwrap_from_path('gc.dummy')
-    conf.gc.cfgimpl_get_settings().add_property('frozen', dummy)
-    assert conf.gc.dummy == False
-    raises(ConfigError, "conf.gc.dummy = True")
-
-def test_frozen_value():
-    "setattr a frozen value at the config level"
-    s = StrOption("string", "", default="string")
-    descr = OptionDescription("options", "", [s])
-    config = Config(descr)
-    settings = config.cfgimpl_get_settings().enable_property('frozen')
-    config.cfgimpl_get_settings().add_property('frozen', s)
-    raises(ConfigError, 'config.string = "egg"')
-
-def test_freeze():
-    "freeze a whole configuration object"
-    descr = make_description()
-    conf = Config(descr)
-    settings = conf.cfgimpl_get_settings().enable_property('frozen')
-    name = conf.unwrap_from_path("gc.name")
-    conf.cfgimpl_get_settings().add_property('frozen', name)
-    raises(ConfigError, "conf.gc.name = 'framework'")
 # ____________________________________________________________
 def test_is_hidden():
     descr = make_description()
@@ -110,9 +50,7 @@ def test_is_hidden():
     raises(PropertiesOptionError, "config.gc.dummy == False")
     # getattr
     raises(PropertiesOptionError, "config.gc.dummy")
-    # I want to access to this option anyway
-    opt = config.unwrap_from_path("gc.dummy")
-    assert config.cfgimpl_get_values()[opt] == False
+
 
 def test_group_is_hidden():
     descr = make_description()
@@ -120,7 +58,7 @@ def test_group_is_hidden():
     setting = config.cfgimpl_get_settings()
     setting.read_write()
     gc = config.unwrap_from_path('gc')
-    dummy = config.unwrap_from_path('gc.dummy')
+    config.unwrap_from_path('gc.dummy')
     config.cfgimpl_get_settings().add_property('hidden', gc)
     raises(PropertiesOptionError, "config.gc.dummy")
     assert config.cfgimpl_get_settings().has_property('hidden', gc)
@@ -132,6 +70,7 @@ def test_group_is_hidden():
     #dummy est en hide
     raises(PropertiesOptionError, "config.gc.dummy == False")
 
+
 def test_global_show():
     descr = make_description()
     config = Config(descr)
@@ -142,15 +81,16 @@ def test_global_show():
     assert config.cfgimpl_get_settings().has_property('hidden', dummy)
     raises(PropertiesOptionError, "config.gc.dummy == False")
 
+
 def test_with_many_subgroups():
     descr = make_description()
     config = Config(descr)
     booltwo = config.unwrap_from_path('gc.subgroup.booltwo')
     assert not config.cfgimpl_get_settings().has_property('hidden', booltwo)
-    assert config.gc.subgroup.booltwo == False
+    assert config.gc.subgroup.booltwo is False
     config.cfgimpl_get_settings().add_property('hidden', booltwo)
     path = 'gc.subgroup.booltwo'
     homeconfig, name = config.cfgimpl_get_home_by_path(path)
     assert name == "booltwo"
-    option = getattr(homeconfig._cfgimpl_descr, name)
+    getattr(homeconfig._cfgimpl_descr, name)
     assert config.cfgimpl_get_settings().has_property('hidden', booltwo)
index 717fc49..f29ac00 100644 (file)
@@ -21,8 +21,7 @@
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
 #from inspect import getmembers, ismethod
-from tiramisu.error import (PropertiesOptionError, ConfigError,
-                            AmbigousOptionError)
+from tiramisu.error import PropertiesOptionError, AmbigousOptionError
 from tiramisu.option import OptionDescription, Option, SymLinkOption
 from tiramisu.setting import groups, Setting, apply_requires
 from tiramisu.value import Values
@@ -79,49 +78,10 @@ class SubConfig(object):
             return homeconfig.__setattr__(name, value)
         child = getattr(self._cfgimpl_descr, name)
         if type(child) != SymLinkOption:
-            self._validate(name, getattr(self._cfgimpl_descr, name), value,
-                           force_permissive=force_permissive)
-            self.setoption(name, child, value)
+            self.setoption(name, child, value, force_permissive)
         else:
             child.setoption(self.cfgimpl_get_context(), value)
 
-    def _validate_descr(self, name, opt_or_descr, force_permissive=False, is_raise=True):
-        if not isinstance(opt_or_descr, Option) and \
-                not isinstance(opt_or_descr, OptionDescription):
-            raise TypeError(_('unexpected object: {0}').format(repr(opt_or_descr)))
-        properties = set(self.cfgimpl_get_settings().get_properties(opt_or_descr))
-        #remove this properties, those properties are validate in value/setting
-        properties = properties - set(['mandatory', 'frozen'])
-        set_properties = set(self.cfgimpl_get_settings().get_properties())
-        properties = properties & set_properties
-        if force_permissive is True or self.cfgimpl_get_settings().has_property('permissive', is_apply_req=False):
-            properties = properties - set(self.cfgimpl_get_settings().get_permissive())
-        properties = properties - set(self.cfgimpl_get_settings().get_permissive(opt_or_descr))
-        properties = list(properties)
-        if is_raise:
-            if properties != []:
-                raise PropertiesOptionError(_("trying to access"
-                                            " to an option named: {0} with properties"
-                                            " {1}").format(name, str(properties)),
-                                            properties)
-        else:
-            return properties
-
-    def _validate(self, name, opt_or_descr, value, force_permissive=False,
-                  force_properties=None):
-        "validation for the setattr and the getattr"
-        properties = self._validate_descr(name, opt_or_descr,
-                                          force_permissive=force_permissive,
-                                          is_raise=False)
-        if self.cfgimpl_get_context().cfgimpl_get_values().is_mandatory_err(
-                opt_or_descr, value, force_properties=force_properties):
-            properties.append('mandatory')
-        if properties != []:
-            raise PropertiesOptionError(_("trying to access"
-                                        " to an option named: {0} with properties"
-                                        " {1}").format(name, str(properties)),
-                                        properties)
-
     def __getattr__(self, name):
         return self._getattr(name)
 
@@ -142,14 +102,23 @@ class SubConfig(object):
             return homeconfig._getattr(name, force_permissive=force_permissive,
                                        force_properties=force_properties,
                                        validate=validate)
+        # special attributes
+        if name.startswith('_cfgimpl_'):
+            # if it were in __dict__ it would have been found already
+            object.__getattr__(self, name)
         opt_or_descr = getattr(self._cfgimpl_descr, name)
         # symlink options
-        if type(opt_or_descr) == SymLinkOption:
+        if isinstance(opt_or_descr, SymLinkOption):
             rootconfig = self.cfgimpl_get_context()
             path = rootconfig.cfgimpl_get_description().get_path_by_opt(opt_or_descr.opt)
-            return rootconfig._getattr(path, validate=validate)
-        if isinstance(opt_or_descr, OptionDescription):
-            self._validate_descr(name, opt_or_descr, force_permissive=force_permissive)
+            return rootconfig._getattr(path, validate=validate,
+                                       force_properties=force_properties,
+                                       force_permissive=force_permissive)
+        elif isinstance(opt_or_descr, OptionDescription):
+            self.cfgimpl_get_settings().validate_properties(opt_or_descr,
+                                                            True, False,
+                                                            force_permissive=force_permissive,
+                                                            force_properties=force_properties)
             children = self.cfgimpl_get_description()._children
             if opt_or_descr not in children[1]:
                 raise AttributeError(_("{0} with name {1} object has "
@@ -157,36 +126,22 @@ class SubConfig(object):
                                                                 opt_or_descr._name,
                                                                 name))
             return SubConfig(opt_or_descr, self._cfgimpl_context)
-        # special attributes
-        if name.startswith('_cfgimpl_'):
-            # if it were in __dict__ it would have been found already
-            object.__getattr__(self, name)
-        value = self.cfgimpl_get_values()._getitem(opt_or_descr,
-                                                   validate=validate)
-        self._validate(name, opt_or_descr, value,
-                       force_permissive=force_permissive,
-                       force_properties=force_properties)
-        return value
-
-    def setoption(self, name, child, value):
+        else:
+            value = self.cfgimpl_get_values()._getitem(opt_or_descr,
+                                                       validate=validate,
+                                                       force_properties=force_properties,
+                                                       force_permissive=force_permissive)
+            return value
+
+    def setoption(self, name, child, value, force_permissive=False):
         """effectively modifies the value of an Option()
         (typically called by the __setattr__)
         """
-        setting = self.cfgimpl_get_settings()
         #needed ?
         apply_requires(child, self)
-        #needed to ?
         if child not in self._cfgimpl_descr._children[1]:
             raise AttributeError(_('unknown option {0}').format(name))
 
-        if setting.has_property('everything_frozen'):
-            raise ConfigError(_("cannot set a value to the option {0} if the whole "
-                              "config has been frozen").format(name))
-
-        if setting.has_property('frozen') and setting.has_property('frozen',
-                                                                   child, is_apply_req=False):
-            raise ConfigError(_('cannot change the value to {0} for '
-                              'option {1} this option is frozen').format(str(value), name))
         self.cfgimpl_get_values()[child] = value
 
     def cfgimpl_get_home_by_path(self, path, force_permissive=False, force_properties=None):
index 43083c9..fc31491 100644 (file)
@@ -33,8 +33,7 @@ class AmbigousOptionError(StandardError):
 
 
 class ConfigError(StandardError):
-    """if modify frozen config
-    or try to change owner for an option without value
+    """try to change owner for an option without value
     or if error in calculation"""
     pass
 
index dffc4ef..5af26f9 100644 (file)
@@ -211,6 +211,41 @@ class Setting(object):
             self.set_properties(properties, opt)
 
     #____________________________________________________________
+    def validate_properties(self, opt_or_descr, is_descr, is_write,
+                            value=None, force_permissive=False,
+                            force_properties=None):
+        properties = set(self.get_properties(opt_or_descr))
+        #remove this properties, those properties are validate in after
+        properties = properties - set(['mandatory', 'frozen'])
+        set_properties = self.get_properties()
+        if force_properties is not None:
+            set_properties.extend(force_properties)
+        set_properties = set(set_properties)
+        properties = properties & set_properties
+        if force_permissive is True or self.has_property('permissive', is_apply_req=False):
+            properties = properties - set(self.get_permissive())
+        properties = properties - set(self.get_permissive(opt_or_descr))
+        properties = list(properties)
+        raise_text = _("trying to access"
+                       " to an option named: {0} with properties"
+                       " {1}")
+        if not is_descr:
+            if self.context.cfgimpl_get_values().is_mandatory_err(opt_or_descr,
+                                                                  value,
+                                                                  force_properties=force_properties):
+                properties.append('mandatory')
+            if is_write and (self.has_property('everything_frozen') or (
+                    self.has_property('frozen') and
+                    self.has_property('frozen', opt_or_descr,
+                                      is_apply_req=False))):
+                properties.append('frozen')
+                raise_text = _('cannot change the value to {0} for '
+                               'option {1} this option is frozen')
+        if properties != []:
+            raise PropertiesOptionError(raise_text.format(opt_or_descr._name,
+                                                          str(properties)),
+                                        properties)
+
     def get_permissive(self, opt=None):
         return self.permissives.get(opt, [])
 
index fb3ddba..c1d8477 100644 (file)
@@ -107,10 +107,11 @@ class Values(object):
     def __getitem__(self, opt):
         return self._getitem(opt)
 
-    def _getitem(self, opt, validate=True):
+    def _getitem(self, opt, validate=True, force_permissive=False,
+                 force_properties=None):
         # options with callbacks
-        value = self._get_value(opt)
         setting = self.context.cfgimpl_get_settings()
+        value = self._get_value(opt)
         is_frozen = setting.has_property('frozen', opt, False)
         if opt.has_callback():
             #if value is set and :
@@ -137,11 +138,23 @@ class Values(object):
         if self.is_default_owner(opt) and \
                 setting.has_property('force_store_value', opt, False):
             self.setitem(opt, value, validate=validate)
+        setting.validate_properties(opt, False, False, value=value,
+                                    force_permissive=force_permissive,
+                                    force_properties=force_properties)
         return value
 
     def __setitem__(self, opt, value):
+        #valid config
+        #FIXME:
+        force_permissive = False
+        force_properties = None
+        setting = self.context.cfgimpl_get_settings()
+        setting.validate_properties(opt, False, True,
+                                    value=value, force_permissive=force_permissive,
+                                    force_properties=force_properties)
+        #valid opt
         if not opt.validate(value, self.context,
-                            self.context.cfgimpl_get_settings().has_property('validator')):
+                            setting.has_property('validator')):
             raise ValueError(_('invalid value {}'
                              ' for option {}').format(value, opt._name))
         if opt.is_multi():
@@ -227,7 +240,11 @@ class Multi(list):
                                      " which is a slave").format(self.opt._name))
             elif self.opt.get_multitype() == multitypes.master:
                 for slave in self.opt.get_master_slaves():
-                    self.context.cfgimpl_get_values()[slave].append(slave.getdefault_multi(), force=True)
+                    self.context.cfgimpl_get_values()[slave].append(
+                        slave.getdefault_multi(), force=True)
+        self.context.cfgimpl_get_settings().validate_properties(self.opt,
+                                                                False, True,
+                                                                value=value)
         self._validate(value)
         self.context.cfgimpl_get_values().setitem(self.opt, self)
         super(Multi, self).append(value)