requires could be apply to a slave and properties could be different
authorEmmanuel Garette <egarette@cadoles.com>
Sun, 29 Nov 2015 22:03:08 +0000 (23:03 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Sun, 29 Nov 2015 22:03:08 +0000 (23:03 +0100)
20 files changed:
ChangeLog
test/test_cache.py
test/test_config_api.py
test/test_option_setting.py
test/test_parsing_group.py
test/test_requires.py
test/test_slots.py
tiramisu/autolib.py
tiramisu/config.py
tiramisu/option/baseoption.py
tiramisu/option/masterslave.py
tiramisu/option/option.py
tiramisu/option/optiondescription.py
tiramisu/setting.py
tiramisu/storage/__init__.py
tiramisu/storage/dictionary/option.py
tiramisu/storage/dictionary/storage.py
tiramisu/storage/dictionary/value.py
tiramisu/storage/util.py
tiramisu/value.py

index 6c8f3ca..070a857 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,6 @@
+Sun Nov 29 23:01:28 2015 +0200 Emmanuel Garette <egarette@cadoles.com>
+       * requires could be apply to a slave and properties could be different
+
 Mon Oct 12 17:05:28 2015 +0200 Emmanuel Garette <egarette@cadoles.com>
        * domainname with only one character is now allowed
 
index 7a6f6dc..cb0cb03 100644 (file)
@@ -4,9 +4,10 @@ do_autopath()
 
 from tiramisu import setting
 setting.expires_time = 1
-from tiramisu.option import IntOption, OptionDescription
+from tiramisu.option import IntOption, StrOption, OptionDescription
 from tiramisu.config import Config
-from tiramisu.error import ConfigError, PropertiesOptionError
+from tiramisu.error import ConfigError
+from tiramisu.setting import groups
 
 
 from time import sleep, time
@@ -50,9 +51,9 @@ def test_get_cache():
     values = c.cfgimpl_get_values()
     settings = c.cfgimpl_get_settings()
     ntime = time() + 1
-    settings._p_.setcache('u1', set(['inject']), ntime)
+    settings._p_.setcache('u1', set(['inject']), ntime, None)
     assert 'inject' in settings[od1.u1]
-    values._p_.setcache('u1', 100, ntime)
+    values._p_.setcache('u1', 100, ntime, None)
     assert c.u1 == [100]
 
 
@@ -62,9 +63,9 @@ def test_get_cache_no_expire():
     c = Config(od1)
     values = c.cfgimpl_get_values()
     settings = c.cfgimpl_get_settings()
-    settings._p_.setcache('u1', set(['inject2']), None)
+    settings._p_.setcache('u1', set(['inject2']), None, None)
     assert 'inject2' in settings[od1.u1]
-    values._p_.setcache('u1', 200, None)
+    values._p_.setcache('u1', 200, None, None)
     assert c.u1 == [200]
 
 
@@ -279,13 +280,70 @@ def test_force_cache():
     c.cfgimpl_get_settings().remove('expire')
 
     c.cfgimpl_get_values().force_cache()
-    assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': ([], None), 'u3': ([], None), 'u2': (None, None), 'u4': (None, None)}
-    assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u4': (set(['disabled']), None), 'u1': (set([]), None), 'u3': (set([]), None), 'u2': (set([]), None)}
+    assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': {None: ([], None)},
+                                                        'u2': {None: (None, None)},
+                                                        'u3': {None: ([], None)},
+                                                        'u4': {None: (None, None)}}
+    assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u1': {None: (set([]), None)},
+                                                          'u2': {None: (set([]), None)},
+                                                          'u3': {None: (set([]), None)},
+                                                          'u4': {None: (set(['disabled']), None)}}
     c.read_only()
 
     c.cfgimpl_get_values().force_cache()
-    assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': ([], None), 'u3': ([], None), 'u2': (None, None)}
-    assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u4': (set(['disabled']), None), 'u1': (set([]), None), 'u3': (set([]), None), 'u2': (set([]), None)}
+    assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': {None: ([], None)},
+                                                        'u2': {None: (None, None)},
+                                                        'u3': {None: ([], None)}}
+    assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u1': {None: (set([]), None)},
+                                                          'u2': {None: (set([]), None)},
+                                                          'u3': {None: (set([]), None)},
+                                                          'u4': {None: (set(['disabled']), None)}}
 
     c.cfgimpl_get_settings().remove('cache')
     raises(ConfigError, "c.cfgimpl_get_values().force_cache()")
+
+
+def test_cache_master_slave():
+    ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
+    netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
+    interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
+    interface1.impl_set_group_type(groups.master)
+    maconfig = OptionDescription('toto', '', [interface1])
+    cfg = Config(maconfig)
+    cfg.read_write()
+    assert cfg.cfgimpl_get_values()._p_.get_cached(cfg) == {}
+    assert cfg.cfgimpl_get_settings()._p_.get_cached(cfg) == {}
+    #
+    cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.2')
+    cfg.ip_admin_eth0.ip_admin_eth0
+    cfg.ip_admin_eth0.netmask_admin_eth0
+    cache = cfg.cfgimpl_get_values()._p_.get_cached(cfg)
+    assert set(cache.keys()) == set(['ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
+    assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
+    assert cache['ip_admin_eth0.ip_admin_eth0'][None][0] == ['192.168.1.2']
+    assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([None, 0])
+    assert cache['ip_admin_eth0.netmask_admin_eth0'][None][0] == [None]
+    assert cache['ip_admin_eth0.netmask_admin_eth0'][0][0] is None
+    cache = cfg.cfgimpl_get_settings()._p_.get_cached(cfg)
+    assert set(cache.keys()) == set(['ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
+    assert set(cache['ip_admin_eth0'].keys()) == set([None])
+    assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
+    assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([None, 0])
+    #
+    cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.1')
+    cfg.ip_admin_eth0.ip_admin_eth0
+    cfg.ip_admin_eth0.netmask_admin_eth0
+    cache = cfg.cfgimpl_get_values()._p_.get_cached(cfg)
+    assert set(cache.keys()) == set(['ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
+    assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
+    assert cache['ip_admin_eth0.ip_admin_eth0'][None][0] == ['192.168.1.2', '192.168.1.1']
+    assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([None, 0, 1])
+    assert cache['ip_admin_eth0.netmask_admin_eth0'][None][0] == [None, None]
+    assert cache['ip_admin_eth0.netmask_admin_eth0'][0][0] is None
+    assert cache['ip_admin_eth0.netmask_admin_eth0'][1][0] is None
+    cache = cfg.cfgimpl_get_settings()._p_.get_cached(cfg)
+    assert set(cache.keys()) == set(['ip_admin_eth0', 'ip_admin_eth0.ip_admin_eth0', 'ip_admin_eth0.netmask_admin_eth0'])
+    assert set(cache['ip_admin_eth0'].keys()) == set([None])
+    assert set(cache['ip_admin_eth0.ip_admin_eth0'].keys()) == set([None])
+    assert set(cache['ip_admin_eth0.netmask_admin_eth0'].keys()) == set([None, 0, 1])
+    #DEL, insert, ...
index 433d887..4b7bb4d 100644 (file)
@@ -102,6 +102,24 @@ def test_make_dict_with_disabled():
         OptionDescription("s1", "", [
             BoolOption("a", "", default=False),
             BoolOption("b", "", default=False, properties=('disabled',))]),
+        OptionDescription("s2", "", [
+            BoolOption("a", "", default=False),
+            BoolOption("b", "", default=False)], properties=('disabled',)),
+        IntOption("int", "", default=42)])
+    config = Config(descr)
+    config.read_only()
+    d = config.make_dict()
+    assert d == {"s1.a": False, "int": 42}
+
+
+def test_make_dict_with_disabled_in_callback():
+    descr = OptionDescription("opt", "", [
+        OptionDescription("s1", "", [
+            BoolOption("a", "", default=False),
+            BoolOption("b", "", default=False, properties=('disabled',))]),
+        OptionDescription("s2", "", [
+            BoolOption("a", "", default=False),
+            BoolOption("b", "", default=False)], properties=('disabled',)),
         IntOption("int", "", default=42)])
     config = Config(descr)
     config.read_only()
index b1c1e85..83009e1 100644 (file)
@@ -4,7 +4,7 @@ do_autopath()
 
 from py.test import raises
 
-from tiramisu.setting import owners
+from tiramisu.setting import owners, groups
 from tiramisu.config import Config
 from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
     StrOption, OptionDescription
@@ -96,6 +96,7 @@ def test_default_with_multi():
 def test_idontexist():
     descr = make_description()
     cfg = Config(descr)
+    cfg
     raises(AttributeError, "cfg.idontexist")
 
 
@@ -222,9 +223,50 @@ def test_multi_with_requires_with_disabled_in_another_group():
 
 
 def test_multi_with_requires_that_is_multi():
-    s = StrOption("string", "", default=["string"], multi=True)
-    intoption = IntOption('int', 'Test int option', default=[0], multi=True)
-    raises(ValueError, "StrOption('str', 'Test string option', default=['abc'], requires=[{'option': intoption, 'expected': 1, 'action': 'hidden'}], multi=True)")
+    b = IntOption('int', 'Test int option', default=[0], multi=True)
+    c = StrOption('str', 'Test string option', default=['abc'], requires=[{'option': b, 'expected': 1, 'action': 'hidden'}], multi=True)
+    descr = OptionDescription("opt", "", [b, c])
+    descr
+    raises(ValueError, "Config(descr)")
+
+
+def test_multi_with_requires_that_is_masterslave():
+    b = IntOption('int', 'Test int option', default=[0], multi=True)
+    c = StrOption('str', 'Test string option', requires=[{'option': b, 'expected': 1, 'action': 'hidden'}], multi=True)
+    descr = OptionDescription("int", "", [b, c])
+    descr.impl_set_group_type(groups.master)
+    Config(descr)
+
+
+def test_multi_with_requires_that_is_masterslave_master():
+    b = IntOption('int', 'Test int option', multi=True)
+    c = StrOption('str', 'Test string option', requires=[{'option': b, 'expected': 1, 'action': 'hidden'}], multi=True)
+    descr = OptionDescription("str", "", [c, b])
+    descr.impl_set_group_type(groups.master)
+    raises(ValueError, "Config(descr)")
+
+
+def test_multi_with_requires_that_is_masterslave2():
+    b = IntOption('int', 'Test int option', default=[0], multi=True)
+    c = StrOption('str', 'Test string option', multi=True)
+    d = StrOption('str1', 'Test string option', requires=[{'option': c, 'expected': '1', 'action': 'hidden'}], multi=True)
+    descr = OptionDescription("int", "", [b, c, d])
+    descr.impl_set_group_type(groups.master)
+    Config(descr)
+
+
+def test_multi_with_requires_that_is_not_same_masterslave():
+    b = IntOption('int', 'Test int option', default=[0], multi=True)
+    c = StrOption('str', 'Test string option', requires=[{'option': b, 'expected': 1, 'action': 'hidden'}], multi=True)
+    descr1 = OptionDescription("int", "", [b, c])
+    descr1.impl_set_group_type(groups.master)
+    d = IntOption('int1', 'Test int option', default=[0], multi=True)
+    e = StrOption('str', 'Test string option', requires=[{'option': b, 'expected': 1, 'action': 'hidden'}], multi=True)
+    descr2 = OptionDescription("int1", "", [d, e])
+    descr2.impl_set_group_type(groups.master)
+    descr3 = OptionDescription('val', '', [descr1, descr2])
+    descr3
+    raises(ValueError, "Config(descr3)")
 
 
 def test_multi_with_bool():
@@ -241,6 +283,7 @@ def test_multi_with_bool_two():
     s = BoolOption("bool", "", default=[False], multi=True)
     descr = OptionDescription("options", "", [s])
     config = Config(descr)
+    config
     assert descr.bool.impl_is_multi() is True
     raises(ValueError, "config.bool = True")
 
@@ -383,5 +426,6 @@ def test_properties_cached():
     c.read_write()
     setting = c.cfgimpl_get_settings()
     option = c.cfgimpl_get_description().sub.b1
+    option
     c._setattr('sub.b1', True, force_permissive=True)
     assert str(setting[b1]) in ["['test']", "[u'test']"]
index ca65579..ba9a667 100644 (file)
@@ -242,6 +242,7 @@ def test_allowed_groups():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
+    interface1
     raises(ValueError, "interface1.impl_set_group_type('toto')")
 
 
@@ -266,6 +267,7 @@ def test_master_not_valid_name():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     invalid_group = OptionDescription('interface1', '', [ip_admin_eth0, netmask_admin_eth0])
+    invalid_group
     raises(ValueError, "invalid_group.impl_set_group_type(groups.master)")
 
 
@@ -274,6 +276,7 @@ def test_sub_group_in_master_group():
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     subgroup = OptionDescription("subgroup", '', [])
     invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0])
+    invalid_group
     raises(ValueError, "invalid_group.impl_set_group_type(groups.master)")
 
 
@@ -281,6 +284,7 @@ def test_group_always_has_multis():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
     group = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
+    group
     raises(ValueError, "group.impl_set_group_type(groups.master)")
 
 
index 02d1d36..3def709 100644 (file)
@@ -3,6 +3,7 @@ from autopath import do_autopath
 do_autopath()
 
 from copy import copy
+from tiramisu.setting import groups
 from tiramisu import setting
 setting.expires_time = 1
 from tiramisu.option import IPOption, OptionDescription, BoolOption, IntOption, StrOption
@@ -52,6 +53,7 @@ def test_requires_with_requires():
 
 def test_requires_invalid():
     a = BoolOption('activate_service', '', True)
+    a
     raises(ValueError, "IPOption('ip_address_service', '', requires='string')")
     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'unknown': True}])")
     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False}])")
@@ -568,6 +570,7 @@ def test_requires_requirement_append():
 
 def test_requires_different_inverse():
     a = BoolOption('activate_service', '', True)
+    a
     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': True, 'action': 'disabled', 'inverse': True}, {'option': a, 'expected': True, 'action': 'disabled', 'inverse': False}])")
 
 
@@ -605,5 +608,25 @@ def test_set_item():
 
 def test_properties_conflict():
     a = BoolOption('activate_service', '', True)
+    a
     raises(ValueError, "IPOption('ip_address_service', '', properties=('disabled',), requires=[{'option': a, 'expected': False, 'action': 'disabled'}])")
     raises(ValueError, "od1 = OptionDescription('service', '', [a], properties=('disabled',), requires=[{'option': a, 'expected': False, 'action': 'disabled'}])")
+
+
+def test_master_slave_requires():
+    ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
+    netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True,
+                                   requires=[{'option': ip_admin_eth0, 'expected': '192.168.1.1', 'action': 'disabled'}])
+    interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
+    interface1.impl_set_group_type(groups.master)
+    maconfig = OptionDescription('toto', '', [interface1])
+    cfg = Config(maconfig)
+    cfg.read_write()
+    assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
+    assert cfg.ip_admin_eth0.ip_admin_eth0 == []
+    cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.2')
+    assert cfg.ip_admin_eth0.netmask_admin_eth0 == [None]
+    assert cfg.ip_admin_eth0.ip_admin_eth0 == ['192.168.1.2']
+    cfg.ip_admin_eth0.ip_admin_eth0.append('192.168.1.1')
+    assert cfg.ip_admin_eth0.netmask_admin_eth0[0] is None
+    raises(PropertiesOptionError, "cfg.ip_admin_eth0.netmask_admin_eth0[1]")
index d3bfdd0..6412c81 100644 (file)
@@ -62,21 +62,21 @@ def test_slots_option_readonly():
     p = URLOption('p', '')
     q = FilenameOption('q', '')
     m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l, o, p, q])
-    a._requires = 'a'
-    b._requires = 'b'
-    c._requires = 'c'
-    d._requires = 'd'
-    e._requires = 'e'
-    g._requires = 'g'
-    h._requires = 'h'
-    i._requires = 'i'
-    j._requires = 'j'
-    k._requires = 'k'
-    l._requires = 'l'
-    m._requires = 'm'
-    o._requires = 'o'
-    p._requires = 'p'
-    q._requires = 'q'
+    a._requires = (((a,),),)
+    b._requires = (((a,),),)
+    c._requires = (((a,),),)
+    d._requires = (((a,),),)
+    e._requires = (((a,),),)
+    g._requires = (((a,),),)
+    h._requires = (((a,),),)
+    i._requires = (((a,),),)
+    j._requires = (((a,),),)
+    k._requires = (((a,),),)
+    l._requires = (((a,),),)
+    m._requires = (((a,),),)
+    o._requires = (((a,),),)
+    p._requires = (((a,),),)
+    q._requires = (((a,),),)
     Config(m)
     raises(AttributeError, "a._requires = 'a'")
     raises(AttributeError, "b._requires = 'b'")
index acb342b..7e59cce 100644 (file)
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
 "enables us to carry out a calculation and return an option's value"
-from tiramisu.error import PropertiesOptionError, ConfigError, ContextError, \
+from .error import PropertiesOptionError, ConfigError, ContextError, \
     SlaveError
-from tiramisu.i18n import _
-from tiramisu.setting import undefined
+from .i18n import _
+from .setting import undefined
 # ____________________________________________________________
 
 
@@ -222,9 +222,13 @@ def carry_out_calculation(option, context, callback, callback_params,
                 else:
                     kwargs[key] = couple[0]
         ret = calculate(callback, args, kwargs)
-        if callback_params != {} and isinstance(ret, list) and index is not undefined:
-            raise SlaveError(_("callback cannot return a list for a "
-                               "slave option ({0})").format(path))
+        try:
+            if callback_params != {} and isinstance(ret, list) and \
+                    option.impl_is_master_slaves('slave'):
+                raise SlaveError(_("callback cannot return a list for a "
+                                   "slave option ({0})").format(path))
+        except AttributeError:
+            pass
         return ret
 
 
index 8e3eb47..ac1b6f5 100644 (file)
 import weakref
 
 
-from tiramisu.error import PropertiesOptionError, ConfigError, ConflictError
-from tiramisu.option import OptionDescription, Option, SymLinkOption, \
+from .error import PropertiesOptionError, ConfigError, ConflictError
+from .option import OptionDescription, Option, SymLinkOption, \
     DynSymLinkOption
-from tiramisu.option.baseoption import valid_name
-from tiramisu.setting import groups, Settings, default_encoding, undefined
-from tiramisu.storage import get_storages, get_storage, set_storage, \
+from .option.baseoption import valid_name
+from .setting import groups, Settings, default_encoding, undefined
+from .storage import get_storages, get_storage, set_storage, \
     _impl_getstate_setting, get_storages_validation
-from tiramisu.value import Values, Multi
-from tiramisu.i18n import _
+from .value import Values, Multi
+from .i18n import _
 
 
 class SubConfig(object):
@@ -242,7 +242,7 @@ class SubConfig(object):
         return subpath
 
     def getattr(self, name, force_permissive=False, validate=True,
-                _setting_properties=undefined):
+                _setting_properties=undefined, index=None):
         """
         attribute notation mechanism for accessing the value of an option
         :param name: attribute name
@@ -256,23 +256,25 @@ class SubConfig(object):
                 name, force_permissive=force_permissive)
             return homeconfig.getattr(name, force_permissive=force_permissive,
                                       validate=validate,
-                                      _setting_properties=_setting_properties)
+                                      _setting_properties=_setting_properties,
+                                      index=index)
         context = self._cfgimpl_get_context()
         option = self.cfgimpl_get_description().__getattr__(name,
                                                             context=context)
         subpath = self._get_subpath(name)
         if isinstance(option, DynSymLinkOption):
-            return self.cfgimpl_get_values()._get_cached_item(
+            return self.cfgimpl_get_values()._get_cached_value(
                 option, path=subpath,
                 validate=validate,
                 force_permissive=force_permissive,
-                setting_properties=_setting_properties)
+                setting_properties=_setting_properties, index=index)
         elif isinstance(option, SymLinkOption):  # pragma: no dynoptiondescription cover
             path = context.cfgimpl_get_description().impl_get_path_by_opt(
                 option._impl_getopt())
             return context.getattr(path, validate=validate,
                                    force_permissive=force_permissive,
-                                   _setting_properties=_setting_properties)
+                                   _setting_properties=_setting_properties,
+                                   index=index)
         elif option.impl_is_optiondescription():
             self.cfgimpl_get_settings().validate_properties(
                 option, True, False, path=subpath,
@@ -280,11 +282,12 @@ class SubConfig(object):
                 setting_properties=_setting_properties)
             return SubConfig(option, self._impl_context, subpath)
         else:
-            return self.cfgimpl_get_values()._get_cached_item(
+            return self.cfgimpl_get_values()._get_cached_value(
                 option, path=subpath,
                 validate=validate,
                 force_permissive=force_permissive,
-                setting_properties=_setting_properties)
+                setting_properties=_setting_properties,
+                index=index)
 
     def find(self, bytype=None, byname=None, byvalue=undefined, type_='option',
              check_properties=True, force_permissive=False):
index 3373106..b33f5d6 100644 (file)
@@ -22,12 +22,12 @@ import re
 from types import FunctionType
 import warnings
 
-from tiramisu.i18n import _
-from tiramisu.setting import log, undefined
-from tiramisu.autolib import carry_out_calculation
-from tiramisu.error import ConfigError, ValueWarning, PropertiesOptionError,\
+from ..i18n import _
+from ..setting import log, undefined
+from ..autolib import carry_out_calculation
+from ..error import ConfigError, ValueWarning, PropertiesOptionError,\
     ContextError
-from tiramisu.storage import get_storages_option
+from ..storage import get_storages_option
 
 
 StorageBase = get_storages_option('base')
@@ -853,10 +853,6 @@ def validate_requires_arg(requires, name):
         if not isinstance(option, Option):  # pragma: optional cover
             raise ValueError(_('malformed requirements '
                                'must be an option in option {0}').format(name))
-        if option.impl_is_multi():  # pragma: optional cover
-            raise ValueError(_('malformed requirements option {0} '
-                               'must not be a multi for {1}').format(
-                                   option.impl_getname(), name))
         if expected is not None:
             try:
                 option._validate(expected)
index eaf8579..cc58366 100644 (file)
@@ -19,9 +19,9 @@
 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
-from tiramisu.i18n import _
-from tiramisu.setting import log, undefined
-from tiramisu.error import SlaveError, ConfigError
+from ..i18n import _
+from ..setting import log, undefined
+from ..error import SlaveError, ConfigError, PropertiesOptionError
 from .baseoption import DynSymLinkOption, SymLinkOption, Option
 
 
@@ -50,8 +50,8 @@ class MasterSlaves(object):
             else:
                 if child.impl_getdefault() != []:
                     raise ValueError(_("not allowed default value for option {0} "
-                                    "in group {1}").format(child.impl_getname(),
-                                                            name))
+                                       "in group {1}").format(child.impl_getname(),
+                                                              name))
                 slaves.append(child)
         if self.master is None:  # pragma: optional cover
             raise ValueError(_('master group with wrong'
@@ -112,50 +112,41 @@ class MasterSlaves(object):
         for slave in self.getslaves(opt):
             if not values.is_default_owner(slave, validate_properties=False,
                                            validate_meta=False, index=index):
-                values._get_cached_item(slave, validate=False,
-                                        validate_properties=False
-                                        ).pop(index, force=True)
+                values._get_cached_value(slave, validate=False,
+                                         validate_properties=False
+                                         ).pop(index, force=True)
         pass
 
     def getitem(self, values, opt, path, validate, force_permissive,
                 force_properties, validate_properties, slave_path=undefined,
-                slave_value=undefined, setting_properties=undefined, self_properties=undefined):
+                slave_value=undefined, setting_properties=undefined,
+                self_properties=undefined, index=None):
         if self.is_master(opt):
             return self._getmaster(values, opt, path, validate,
                                    force_permissive, force_properties,
                                    validate_properties, slave_path,
-                                   slave_value, self_properties)
+                                   slave_value, self_properties, index)
         else:
             return self._getslave(values, opt, path, validate,
                                   force_permissive, force_properties,
-                                  validate_properties, setting_properties, self_properties)
+                                  validate_properties, setting_properties,
+                                  self_properties, index)
 
     def _getmaster(self, values, opt, path, validate, force_permissive,
                    force_properties, validate_properties, c_slave_path,
-                   c_slave_value, self_properties):
+                   c_slave_value, self_properties, index):
         value = values._get_validated_value(opt, path, validate,
                                             force_permissive,
                                             force_properties,
                                             validate_properties,
-                                            self_properties=self_properties)
-        if validate is True:
+                                            self_properties=self_properties,
+                                            index=index)
+        if index is None and validate is True:
             masterlen = len(value)
             for slave in self.getslaves(opt):
                 try:
                     slave_path = slave.impl_getpath(values._getcontext())
                     slavelen = values._p_.get_max_length(slave_path)
-                    #if c_slave_path == slave_path:
-                    #    slave_value = c_slave_value
-                    #else:
-                    #    slave_value = values._get_validated_value(slave,
-                    #                                              slave_path,
-                    #                                              False,
-                    #                                              False,
-                    #                                              None, False,
-                    #                                              None,
-                    #                                              self_properties=self_properties,
-                    #                                              masterlen=masterlen)
-                    #slavelen = len(slave_value)
                     self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt)
                 except ConfigError:  # pragma: optional cover
                     pass
@@ -163,7 +154,7 @@ class MasterSlaves(object):
 
     def _getslave(self, values, opt, path, validate, force_permissive,
                   force_properties, validate_properties, setting_properties,
-                  self_properties):
+                  self_properties, index):
         """
         if master has length 0:
             return []
@@ -191,46 +182,43 @@ class MasterSlaves(object):
         masterlen = self.get_length(values, opt, validate, undefined,
                                     undefined, force_permissive,
                                     master=master)
-        master_is_meta = values._is_meta(opt, masterp)
-        #value = values._get_validated_value(opt, path, validate,
-        #                                    force_permissive,
-        #                                    force_properties,
-        #                                    validate_properties,
-        #                                    None,  # not undefined
-        #                                    with_meta=master_is_meta,
-        #                                    self_properties=self_properties)
-        #if slave, had values until master's one
-        #path = opt.impl_getpath(context)
-        #valuelen = len(value)
-        #if validate:
-        #    self.validate_slave_length(masterlen, valuelen,
-        #                               opt.impl_getname(), opt)
-        #if valuelen < masterlen:
-
-        #FIXME voir si pas de plus grande valeur !
-        value = values._get_multi(opt, path)
-        for index in range(0, masterlen):
-            #index = valuelen + num
-            value.append(values._get_validated_value(opt, path, validate,
+        master_is_meta = values._is_meta(master, masterp)
+        multi = values._get_multi(opt, path)
+        if masterlen == 0:
+            if validate_properties:
+                context.cfgimpl_get_settings().validate_properties(opt, False,
+                                                                   False,
+                                                                   value=multi,
+                                                                   path=path,
+                                                                   force_permissive=force_permissive,
+                                                                   force_properties=force_properties,
+                                                                   setting_properties=setting_properties)
+        else:
+            one_has_value = False
+            if index is None:
+                indexes = range(0, masterlen)
+            else:
+                indexes = [index]
+            for idx in indexes:
+                try:
+                    value = values._get_cached_value(opt, path, validate,
                                                      force_permissive, force_properties,
                                                      validate_properties,
                                                      with_meta=master_is_meta,
-                                                     index=index,
-                                                     self_properties=self_properties,
-                                                     masterlen=masterlen),
-                         setitem=False,
-                         force=True,
-                         validate=validate)
-        #FIXME hu?
-        if validate_properties:
-            context.cfgimpl_get_settings().validate_properties(opt, False,
-                                                               False,
-                                                               value=value,
-                                                               path=path,
-                                                               force_permissive=force_permissive,
-                                                               force_properties=force_properties,
-                                                               setting_properties=setting_properties)
-        return value
+                                                     index=idx,
+                                                     # not self_properties,
+                                                     # depends to index
+                                                     #self_properties=self_properties,
+                                                     masterlen=masterlen,
+                                                     from_masterslave=True)
+                    multi.append(value, setitem=False, force=True, validate=validate)
+                    one_has_value = True
+                except PropertiesOptionError, err:
+                    multi.append_properties_error(err)
+            if not one_has_value:
+                #raise last err
+                raise err
+        return multi
 
     def setitem(self, values, opt, value, path):
         if self.is_master(opt):
@@ -241,15 +229,6 @@ class MasterSlaves(object):
                 slave_path = base_path + slave.impl_getname()
                 slavelen = values._p_.get_max_length(slave_path)
                 self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt)
-                #slave_value = values._get_validated_value(slave,
-                #                                          slave_path,
-                #                                          False,
-                #                                          False,
-                #                                          None, False,
-                #                                          None,
-                #                                          masterlen=masterlen)  # not undefined
-                #slavelen = len(slave_value)
-                #self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt)
         else:
             self.validate_slave_length(self.get_length(values, opt,
                                                        slave_path=path), len(value),
index 7b669aa..3e59e20 100644 (file)
@@ -23,12 +23,12 @@ import re
 import sys
 from IPy import IP
 from types import FunctionType
-from tiramisu.setting import log, undefined
+from ..setting import log, undefined
 
-from tiramisu.error import ConfigError, ContextError
-from tiramisu.i18n import _
+from ..error import ConfigError, ContextError
+from ..i18n import _
 from .baseoption import Option, validate_callback
-from tiramisu.autolib import carry_out_calculation
+from ..autolib import carry_out_calculation
 
 
 class ChoiceOption(Option):
index 05ef6ef..46f1074 100644 (file)
@@ -22,13 +22,13 @@ from copy import copy
 import re
 
 
-from tiramisu.i18n import _
-from tiramisu.setting import groups, undefined  # , log
+from ..i18n import _
+from ..setting import groups, undefined  # , log
 from .baseoption import BaseOption, SymLinkOption, allowed_character
 from . import MasterSlaves
-from tiramisu.error import ConfigError, ConflictError
-from tiramisu.storage import get_storages_option
-from tiramisu.autolib import carry_out_calculation
+from ..error import ConfigError, ConflictError
+from ..storage import get_storages_option
+from ..autolib import carry_out_calculation
 
 
 StorageOptionDescription = get_storages_option('optiondescription')
@@ -135,6 +135,19 @@ class OptionDescription(BaseOption, StorageOptionDescription):
             option._set_readonly()
             if isinstance(option, OptionDescription):
                 option.impl_validate_options(cache_option)
+            if option.impl_getrequires() != []:
+                for requires in option.impl_getrequires():
+                    for require in requires:
+                        if require[0].impl_is_multi():
+                            if option.impl_is_master_slaves('slave') and require[0].impl_is_master_slaves():
+                                if option.impl_get_master_slaves() != require[0].impl_get_master_slaves():
+                                    raise ValueError(_('malformed requirements option {0} '
+                                                       'must be in same master/slaves for {1}').format(
+                                                           require[0].impl_getname(), option.impl_getname()))
+                            else:
+                                raise ValueError(_('malformed requirements option {0} '
+                                                   'must not be a multi for {1}').format(
+                                                       require[0].impl_getname(), option.impl_getname()))
         if init:
             if len(cache_option) != len(set(cache_option)):
                 for idx in xrange(1, len(cache_option) + 1):
index 8e26688..ebfab5a 100644 (file)
@@ -19,9 +19,9 @@ from time import time
 from copy import copy
 from logging import getLogger
 import weakref
-from tiramisu.error import (RequirementError, PropertiesOptionError,
-                            ConstError, ConfigError)
-from tiramisu.i18n import _
+from .error import (RequirementError, PropertiesOptionError,
+                    ConstError, ConfigError)
+from .i18n import _
 
 
 "Default encoding for display a Config if raise UnicodeEncodeError"
@@ -367,7 +367,7 @@ class Settings(object):
 
     def _getproperties(self, opt=None, path=None,
                        setting_properties=undefined, read_write=True,
-                       apply_requires=True):
+                       apply_requires=True, index=None):
         """
         """
         if opt is None:
@@ -384,17 +384,17 @@ class Settings(object):
                     ntime = int(time())
                 else:
                     ntime = None
-                if 'cache' in setting_properties and self._p_.hascache(path):
-                    is_cached, props = self._p_.getcache(path, ntime)
+                if 'cache' in setting_properties and self._p_.hascache(path, index):
+                    is_cached, props = self._p_.getcache(path, ntime, index)
             if not is_cached:
                 props = self._p_.getproperties(path, opt.impl_getproperties())
                 if apply_requires:
                     props = copy(props)
-                    props |= self.apply_requires(opt, path, setting_properties)
+                    props |= self.apply_requires(opt, path, setting_properties, index)
                     if 'cache' in setting_properties:
                         if 'expire' in setting_properties:
                             ntime = ntime + expires_time
-                        self._p_.setcache(path, props, ntime)
+                        self._p_.setcache(path, props, ntime, index)
         if read_write:
             props = copy(props)
         return props
@@ -458,7 +458,8 @@ class Settings(object):
             properties = copy(self_properties)
         else:
             properties = self._getproperties(opt_or_descr, path,
-                                             setting_properties=setting_properties)
+                                             setting_properties=setting_properties,
+                                             index=index)
         # remove opt permissive
         # permissive affect option's permission with or without permissive
         # global property
@@ -571,7 +572,7 @@ class Settings(object):
         else:
             self._p_.reset_all_cache()
 
-    def apply_requires(self, opt, path, setting_properties):
+    def apply_requires(self, opt, path, setting_properties, index):
         """carries out the jit (just in time) requirements between options
 
         a requirement is a tuple of this form that comes from the option's
@@ -633,7 +634,8 @@ class Settings(object):
                                              "'{1}'").format(path, reqpath))
                 try:
                     value = context.getattr(reqpath, force_permissive=True,
-                                            _setting_properties=setting_properties)
+                                            _setting_properties=setting_properties,
+                                            index=index)
                 except PropertiesOptionError as err:
                     if not transitive:
                         continue
index cd63676..8cabd6d 100644 (file)
@@ -29,8 +29,11 @@ configurator ``set_storage()``.
 from time import time
 from random import randint
 import os
-from tiramisu.error import ConfigError
-from tiramisu.i18n import _
+from ..error import ConfigError
+from ..i18n import _
+
+
+MODULE_PATH = os.path.split(os.path.split(os.path.split(__file__)[0])[0])[1]
 
 
 class StorageType(object):
@@ -53,7 +56,7 @@ class StorageType(object):
         if self.storage_type is None:
             self.storage_type = self.default_storage
         if self.mod is None:
-            modulepath = 'tiramisu.storage.{0}'.format(self.storage_type)
+            modulepath = '{0}.storage.{1}'.format(MODULE_PATH, self.storage_type)
             try:
                 mod = __import__(modulepath)
             except ImportError:
index f7d22b1..4863119 100644 (file)
@@ -17,9 +17,9 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
 # ____________________________________________________________
-from tiramisu.i18n import _
-from tiramisu.setting import undefined
-from tiramisu.error import ConfigError
+from ...i18n import _
+from ...setting import undefined
+from ...error import ConfigError
 
 
 #____________________________________________________________
index 252d29e..e9c257b 100644 (file)
@@ -14,8 +14,8 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # ____________________________________________________________
-from tiramisu.i18n import _
-from tiramisu.error import ConfigError
+from ...i18n import _
+from ...error import ConfigError
 from ..util import SerializeObject
 
 
index 7b663fd..6b50054 100644 (file)
@@ -16,7 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # ____________________________________________________________
 from ..util import Cache
-from tiramisu.setting import undefined
+from ...setting import undefined
 
 
 class Values(Cache):
index 170232f..b118913 100644 (file)
@@ -15,7 +15,7 @@
 # You should have received a copy of the GNU Lesser General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 # ____________________________________________________________
-from tiramisu.setting import owners
+from ..setting import owners
 
 
 class SerializeObject(object):
@@ -97,27 +97,30 @@ class Cache(object):
                 value = tuple(_value)
             setattr(self, key, value)
 
-    def setcache(self, path, val, time):
-        self._cache[path] = (val, time)
+    def setcache(self, path, val, time, index):
+        self._cache.setdefault(path, {})[index] = (val, time)
 
-    def getcache(self, path, exp):
-        value, created = self._cache[path]
+    def getcache(self, path, exp, index):
+        value, created = self._cache[path][index]
         if created is None or exp <= created:
             return True, value
         return False, None
 
-    def hascache(self, path):
+    def hascache(self, path, index):
         """ path is in the cache
 
         :param path: the path's option
         """
-        return path in self._cache
+        return path in self._cache and index in self._cache[path]
 
     def reset_expired_cache(self, exp):
-        for key in tuple(self._cache.keys()):
-            val, created = self._cache[key]
-            if created is not None and exp > created:
-                del(self._cache[key])
+        for key in self._cache.keys():
+            for index in self._cache[key].keys():
+                val, created = self._cache[key][index]
+                if created is not None and exp > created:
+                    del(self._cache[key][index])
+                    if self._cache[key] == {}:
+                        del(self._cache[key])
 
     def reset_all_cache(self):
         "empty the cache"
@@ -125,6 +128,6 @@ class Cache(object):
 
     def get_cached(self, context):
         """return all values in a dictionary
-        example: {'path1': ('value1', 'time1'), 'path2': ('value2', 'time2')}
+        example: {'path1': {'index1': ('value1', 'time1')}, 'path2': {'index2': ('value2', 'time2', )}}
         """
         return self._cache
index 2e0deb3..23d3fd4 100644 (file)
 from time import time
 import sys
 import weakref
-from tiramisu.error import ConfigError, SlaveError, PropertiesOptionError
-from tiramisu.setting import owners, expires_time, undefined
-from tiramisu.autolib import carry_out_calculation
-from tiramisu.i18n import _
-from tiramisu.option import SymLinkOption, DynSymLinkOption, Option
+from .error import ConfigError, SlaveError, PropertiesOptionError
+from .setting import owners, expires_time, undefined
+from .autolib import carry_out_calculation
+from .i18n import _
+from .option import SymLinkOption, DynSymLinkOption, Option
 
 
 class Values(object):
@@ -65,7 +65,7 @@ class Values(object):
                                           callback=callback,
                                           callback_params=callback_params,
                                           index=index)
-            if isinstance(value, list) and index is not undefined:
+            if isinstance(value, list) and index is not None:
                 #if return a list and index is set, return value only if
                 #it's a submulti without submulti_index and without list of list
                 if opt.impl_is_submulti() and submulti_index is undefined and \
@@ -78,9 +78,9 @@ class Values(object):
             if meta is not None:
                 try:
                     value = meta.cfgimpl_get_values(
-                    )._get_cached_item(opt, path)
+                    )._get_cached_value(opt, path, index=index, from_masterslave=True)
                     if isinstance(value, Multi):
-                        if index is not undefined:
+                        if index is not None:
                             value = value[index]
                         else:
                             value = list(value)
@@ -89,7 +89,7 @@ class Values(object):
                     pass
         # now try to get default value
         value = opt.impl_getdefault()
-        if opt.impl_is_multi() and index is not undefined:
+        if opt.impl_is_multi() and index is not None:
             if value == []:
                 value = opt.impl_getdefault_multi()
             else:
@@ -115,7 +115,7 @@ class Values(object):
                 return self._p_.getvalue(path, index)
             else:
                 value = self._p_.getvalue(path)
-                if index is not undefined:
+                if index is not None:
                     try:
                         return value[index]
                     except IndexError:
@@ -204,13 +204,15 @@ class Values(object):
     def getitem(self, opt, validate=True, force_permissive=False):
         """
         """
-        return self._get_cached_item(opt, validate=validate,
-                                     force_permissive=force_permissive)
-
-    def _get_cached_item(self, opt, path=None, validate=True,
-                         force_permissive=False, force_properties=None,
-                         validate_properties=True,
-                         setting_properties=undefined, self_properties=undefined):
+        return self._get_cached_value(opt, validate=validate,
+                                      force_permissive=force_permissive)
+
+    def _get_cached_value(self, opt, path=None, validate=True,
+                          force_permissive=False, force_properties=None,
+                          validate_properties=True,
+                          setting_properties=undefined, self_properties=undefined,
+                          index=None, from_masterslave=False, with_meta=True,
+                          masterlen=undefined):
         untrusted_cached_properties = force_properties is None
         context = self._getcontext()
         if path is None:
@@ -221,13 +223,14 @@ class Values(object):
             )._getproperties(read_write=False)
         if self_properties is undefined:
             self_properties = context.cfgimpl_get_settings()._getproperties(
-                opt, path, read_write=False, setting_properties=setting_properties)
-        if 'cache' in setting_properties and self._p_.hascache(path):
+                opt, path, read_write=False,
+                setting_properties=setting_properties, index=index)
+        if 'cache' in setting_properties and self._p_.hascache(path, index):
             if 'expire' in setting_properties:
                 ntime = int(time())
-            is_cached, value = self._p_.getcache(path, ntime)
+            is_cached, value = self._p_.getcache(path, ntime, index)
             if is_cached:
-                if opt.impl_is_multi() and not isinstance(value, Multi):
+                if opt.impl_is_multi() and not isinstance(value, Multi) and index is None:
                     value = Multi(value, self.context, opt, path)
                 if not untrusted_cached_properties:
                     # revalidate properties (because not default properties)
@@ -236,42 +239,41 @@ class Values(object):
                                                                        force_permissive=force_permissive,
                                                                        force_properties=force_properties,
                                                                        setting_properties=setting_properties,
-                                                                       self_properties=self_properties)
+                                                                       self_properties=self_properties,
+                                                                       index=index)
                 return value
-        val = self._getitem(opt, path, validate, force_permissive,
-                            force_properties, validate_properties,
-                            setting_properties, self_properties=self_properties)
-        if 'cache' in setting_properties and validate and validate_properties \
+        if not from_masterslave and opt.impl_is_master_slaves():
+            val = opt.impl_get_master_slaves().getitem(self, opt, path,
+                                                       validate,
+                                                       force_permissive,
+                                                       force_properties,
+                                                       validate_properties,
+                                                       setting_properties=setting_properties,
+                                                       index=index,
+                                                       self_properties=self_properties)
+        else:
+            val = self._get_validated_value(opt, path, validate,
+                                            force_permissive,
+                                            force_properties,
+                                            validate_properties,
+                                            setting_properties=setting_properties,
+                                            self_properties=self_properties,
+                                            with_meta=with_meta,
+                                            masterlen=masterlen,
+                                            index=index)
+        # cache doesn't work with SubMulti yet
+        if not isinstance(val, SubMulti) and 'cache' in setting_properties and validate and validate_properties \
                 and force_permissive is False and force_properties is None:
             if 'expire' in setting_properties:
                 if ntime is None:
                     ntime = int(time())
                 ntime = ntime + expires_time
-            self._p_.setcache(path, val, ntime)
+            self._p_.setcache(path, val, ntime, index)
         return val
 
-    def _getitem(self, opt, path, validate, force_permissive, force_properties,
-                 validate_properties, setting_properties=undefined,
-                 self_properties=undefined):
-        if opt.impl_is_master_slaves():
-            return opt.impl_get_master_slaves().getitem(self, opt, path,
-                                                        validate,
-                                                        force_permissive,
-                                                        force_properties,
-                                                        validate_properties,
-                                                        setting_properties=setting_properties,
-                                                        self_properties=self_properties)
-        else:
-            return self._get_validated_value(opt, path, validate,
-                                             force_permissive,
-                                             force_properties,
-                                             validate_properties,
-                                             setting_properties=setting_properties,
-                                             self_properties=self_properties)
-
     def _get_validated_value(self, opt, path, validate, force_permissive,
                              force_properties, validate_properties,
-                             index=undefined, submulti_index=undefined,
+                             index=None, submulti_index=undefined,
                              with_meta=True, setting_properties=undefined,
                              self_properties=undefined, masterlen=undefined):
         """same has getitem but don't touch the cache
@@ -282,7 +284,7 @@ class Values(object):
         if setting_properties is undefined:
             setting_properties = setting._getproperties(read_write=False)
         if self_properties is undefined:
-            self_properties = setting._getproperties(opt, path, read_write=False)
+            self_properties = setting._getproperties(opt, path, read_write=False, index=index)
         is_default = self._is_default_owner(opt, path,
                                             validate_properties=False,
                                             validate_meta=False,
@@ -458,8 +460,8 @@ class Values(object):
         if 'frozen' in self_properties and 'force_default_on_freeze' in self_properties:
             return owners.default
         if validate_properties:
-            self._getitem(opt, path, True, force_permissive, None, True,
-                          self_properties=self_properties)
+            self._get_cached_value(opt, path, True, force_permissive, None, True,
+                                   self_properties=self_properties)
         owner = self._p_.getowner(path, owners.default, only_default=only_default, index=index)
         if validate_meta is undefined:
             if opt.impl_is_master_slaves('slave'):
@@ -601,12 +603,12 @@ class Values(object):
                                                               setting_properties=setting_properties)
                     if 'mandatory' in self_properties:
                         try:
-                            self._get_cached_item(true_opt, path=true_path,
-                                                  force_properties=frozenset(('mandatory',)),
-                                                  force_permissive=force_permissive,
-                                                  setting_properties=setting_properties,
-                                                  self_properties=self_properties,
-                                                  validate=validate)
+                            self._get_cached_value(true_opt, path=true_path,
+                                                   force_properties=frozenset(('mandatory',)),
+                                                   force_permissive=force_permissive,
+                                                   setting_properties=setting_properties,
+                                                   self_properties=self_properties,
+                                                   validate=validate)
                         except PropertiesOptionError as err:
                             if err.proptype == ['mandatory']:
                                 yield path
@@ -710,7 +712,7 @@ class Multi(list):
         setting_properties = setting._getproperties(read_write=False)
         if 'validator' in setting_properties and validate:
             fake_context = context._gen_fake_values()
-            fake_multi = fake_context.cfgimpl_get_values()._get_cached_item(
+            fake_multi = fake_context.cfgimpl_get_values()._get_cached_value(
                 self.opt, path=self.path, validate=False)
             fake_multi._setitem(index, value, validate=False)
             self._validate(value, fake_context, index, True)
@@ -721,8 +723,11 @@ class Multi(list):
     #def __repr__(self, *args, **kwargs):
     #    return super(Multi, self).__repr__(*args, **kwargs)
 
-    #def __getitem__(self, y):
-    #    return super(Multi, self).__getitem__(y)
+    def __getitem__(self, index):
+        value = super(Multi, self).__getitem__(index)
+        if isinstance(value, PropertiesOptionError):
+            raise value
+        return value
 
     def _get_validated_value(self, index):
         values = self._getcontext().cfgimpl_get_values()
@@ -740,15 +745,16 @@ class Multi(list):
         index = self.__len__()
         if value is undefined:
             value = self._get_validated_value(index)
-        context = self._getcontext()
-        setting = context.cfgimpl_get_settings()
-        setting_properties = setting._getproperties(read_write=False)
-        if 'validator' in setting_properties and validate and value not in [None, undefined]:
-            fake_context = context._gen_fake_values()
-            fake_multi = fake_context.cfgimpl_get_values()._get_cached_item(
-                self.opt, path=self.path, validate=False)
-            fake_multi.append(value, validate=False, force=True)
-            self._validate(value, fake_context, index, True)
+        if validate and value not in [None, undefined]:
+            context = self._getcontext()
+            setting = context.cfgimpl_get_settings()
+            setting_properties = setting._getproperties(read_write=False)
+            if 'validator' in setting_properties:
+                fake_context = context._gen_fake_values()
+                fake_multi = fake_context.cfgimpl_get_values()._get_cached_value(
+                    self.opt, path=self.path, validate=False)
+                fake_multi.append(value, validate=False, force=True)
+                self._validate(value, fake_context, index, True)
         if not '_index' in self.__slots__ and self.opt.impl_is_submulti():
             if not isinstance(value, SubMulti):
                 value = SubMulti(value, self.context, self.opt, self.path, index)
@@ -757,6 +763,9 @@ class Multi(list):
         if setitem:
             self._store(force=force)
 
+    def append_properties_error(self, err):
+        super(Multi, self).append(err)
+
     def sort(self, cmp=None, key=None, reverse=False):
         if self.opt.impl_is_master_slaves():
             raise SlaveError(_("cannot sort multi option {0} if master or slave"
@@ -786,7 +795,7 @@ class Multi(list):
         setting_properties = setting._getproperties(read_write=False)
         if 'validator' in setting_properties and validate and value is not None:
             fake_context = context._gen_fake_values()
-            fake_multi = fake_context.cfgimpl_get_values()._get_cached_item(
+            fake_multi = fake_context.cfgimpl_get_values()._get_cached_value(
                 self.opt, path=self.path, validate=False)
             fake_multi.insert(index, value, validate=False)
             self._validate(value, fake_context, index, True)
@@ -806,7 +815,7 @@ class Multi(list):
         setting_properties = setting._getproperties(read_write=False)
         if 'validator' in setting_properties and validate:
             fake_context = context._gen_fake_values()
-            fake_multi = fake_context.cfgimpl_get_values()._get_cached_item(
+            fake_multi = fake_context.cfgimpl_get_values()._get_cached_value(
                 self.opt, path=self.path, validate=False)
             fake_multi.extend(iterable, validate=False)
             self._validate(iterable, fake_context, index)