optimisations and all is properties
authorEmmanuel Garette <egarette@cadoles.com>
Wed, 3 Apr 2013 10:20:26 +0000 (12:20 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Wed, 3 Apr 2013 10:20:26 +0000 (12:20 +0200)
14 files changed:
test/test_config.py
test/test_config_api.py
test/test_option_consistency.py
test/test_option_default.py
test/test_option_owner.py
test/test_option_setting.py
test/test_option_type.py
test/test_parsing_group.py
tiramisu/autolib.py
tiramisu/basetype.py [deleted file]
tiramisu/config.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/value.py

index beb75ad..6c13c32 100644 (file)
@@ -6,14 +6,14 @@ from tiramisu.config import *
 from tiramisu.option import *
 
 def make_description():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False)
     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")
+    stroption = StrOption('str', 'Test string option', default="abc", properties=('mandatory', ))
     boolop = BoolOption('boolop', 'Test boolean option op', default=True)
     wantref_option = BoolOption('wantref', 'Test requires', default=False)
     wantframework_option = BoolOption('wantframework', 'Test requires',
@@ -52,8 +52,8 @@ def test_base_config_and_groups():
     assert nm._name == 'name'
     gc = config.unwrap_from_path('gc')
     assert gc._name == 'gc'
-    nm = config.unwrap_from_name('name')
-    assert nm._name == 'name'
+    #nm = config.unwrap_from_name('name')
+    #assert nm._name == 'name'
 
 def test_base_config_in_a_tree():
     "how options are organized into a tree"
@@ -105,3 +105,16 @@ def test_cfgimpl_get_home_by_path():
     assert config.cfgimpl_get_home_by_path('dummy')[1] == 'dummy'
     assert config.getpaths(include_groups=False) == ['gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
     assert config.getpaths(include_groups=True) == ['gc', 'gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
+
+def test_mandatory_warnings():
+    descr = make_description()
+    config = Config(descr)
+    assert(MandatoryError, "config.str = ''")
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    assert list(mandatory_warnings(config)) == []
+    setting.disable_property('mandatory')
+    config.str = ''
+    assert list(mandatory_warnings(config)) == ['str']
+    setting.enable_property('mandatory')
+    assert list(mandatory_warnings(config)) == ['str']
index eac3117..4272389 100644 (file)
@@ -6,10 +6,10 @@ from tiramisu.config import *
 from tiramisu.option import *
 
 def make_description():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False)
     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)
@@ -63,7 +63,8 @@ def test_cfgimpl_get_value():
     "same as getattr."
     descr = make_description()
     conf = Config(descr)
-    assert conf.cfgimpl_get_value(('gc', 'dummy')) == False
+    #FIXME
+    #assert conf.cfgimpl_get_value(('gc', 'dummy')) == False
 
 #____________________________________________________________
 def test_getpaths():
@@ -84,9 +85,8 @@ def test_getpaths():
 
 def test_getpaths_with_hidden():
     objspaceoption = ChoiceOption('objspace', 'Object space',
-                                ['std', 'thunk'], 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    booloption.hide()
+                                ('std', 'thunk'), 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True, properties=('hidden',))
     intoption = IntOption('int', 'Test int option', default=0)
     stroption = StrOption('str', 'Test string option', default="abc")
     boolop = BoolOption('boolop', 'Test boolean option op', default=True)
@@ -100,7 +100,7 @@ def test_getpaths_with_hidden():
                                            intoption, boolop])
 
     config = Config(descr)
-    result = ['objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
+    result = ['bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
     assert config.getpaths() == result
     r2 = ['bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
     assert config.getpaths(allpaths=True) == r2
@@ -149,20 +149,20 @@ def test_find_in_config():
     "finds option in config"
     descr = make_description()
     conf = Config(descr)
-    assert conf.find(byname='dummy') == [conf.unwrap_from_name('dummy')]
-    assert conf.find_first(byname='dummy') == conf.unwrap_from_name('dummy')
-    assert conf.find(bytype=ChoiceOption) == [conf.unwrap_from_name('name'), conf.unwrap_from_name('objspace')]
-    assert conf.find_first(bytype=ChoiceOption) == conf.unwrap_from_name('name')
-    assert conf.find(byvalue='ref') == [conf.unwrap_from_name('name')]
-    assert conf.find_first(byvalue='ref') == conf.unwrap_from_name('name')
+    assert conf.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
+    assert conf.find_first(byname='dummy') == conf.unwrap_from_path('gc.dummy')
+    assert conf.find(bytype=ChoiceOption) == [conf.unwrap_from_path('gc.name'), conf.unwrap_from_path('objspace')]
+    assert conf.find_first(bytype=ChoiceOption) == conf.unwrap_from_path('gc.name')
+    assert conf.find(byvalue='ref') == [conf.unwrap_from_path('gc.name')]
+    assert conf.find_first(byvalue='ref') == conf.unwrap_from_path('gc.name')
     # combinaison of filters
-    assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_name('dummy')]
-    assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_name('dummy')
-    assert conf.find(byvalue=False, byname='dummy') == [conf.unwrap_from_name('dummy')]
-    assert conf.find_first(byvalue=False, byname='dummy') == conf.unwrap_from_name('dummy')
+    assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
+    assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_path('gc.dummy')
+    assert conf.find(byvalue=False, byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
+    assert conf.find_first(byvalue=False, byname='dummy') == conf.unwrap_from_path('gc.dummy')
     # byattrs
-    assert conf.find_first(byattrs= dict(default=2.3)) == conf.unwrap_from_name('float')
-    assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_name('dummy')
+    assert conf.find_first(byattrs= dict(default=2.3)) == conf.unwrap_from_path('gc.float')
+    assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_path('gc.dummy')
 
 def test_does_not_find_in_config():
     descr = make_description()
index 0b43775..8bc9b10 100644 (file)
@@ -5,20 +5,20 @@ from tiramisu.config import *
 from tiramisu.option import *
 
 def make_description():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False)
     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")
     boolop = BoolOption('boolop', 'Test boolean option op', default=True)
     wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=['boolop'])
+                                    requires=(('boolop', True, 'hidden'),))
     wantframework_option = BoolOption('wantframework', 'Test requires',
                                       default=False,
-                                      requires=['boolop'])
+                                      requires=(('boolop', True, 'hidden'),))
     gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
     descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
                                            wantref_option, stroption,
@@ -27,21 +27,21 @@ def make_description():
     return descr
 
 def make_description_duplicates():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     ## dummy 1
     gcdummy = BoolOption('dummy', 'dummy', default=False)
     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")
     boolop = BoolOption('boolop', 'Test boolean option op', default=True)
     wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=['boolop'])
+                                    requires=(('boolop', True, 'hidden'),))
     wantframework_option = BoolOption('wantframework', 'Test requires',
                                       default=False,
-                                      requires=['boolop'])
+                                      requires=(('boolop', True, 'hidden'),))
     # dummy2 (same path)
     gcdummy2  = BoolOption('dummy', 'dummy2', default=True)
     # dummy3 (same name)
@@ -57,8 +57,7 @@ def test_identical_paths():
     """If in the schema (the option description) there is something that
     have the same name, an exection is raised
     """
-    descr = make_description_duplicates()
-    raises(ConflictConfigError, "cfg = Config(descr)")
+    raises(ConflictConfigError, "make_description_duplicates()")
 
 def make_description2():
     gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
@@ -75,11 +74,11 @@ def make_description2():
     boolop = BoolOption('boolop', 'Test boolean option op', default=True)
     boolop.enable_multi()
     wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=['boolop'])
+                                    requires=(('boolop', True, 'hidden'),))
     # second multi
     wantframework_option = BoolOption('wantframework', 'Test requires',
                                       default=False,
-                                      requires=['boolop'])
+                                      requires=(('boolop', True, 'hidden'),))
     wantframework_option.enable_multi()
 
     gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
@@ -110,20 +109,20 @@ def make_description2():
 #    raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
 # ____________________________________________________________
 # adding dynamically new options description schema
-def test_newoption_add_in_descr():
-    descr = make_description()
-    newoption = BoolOption('newoption', 'dummy twoo', default=False)
-    descr.add_child(newoption)
-    config = Config(descr)
-    assert config.newoption == False
+#def test_newoption_add_in_descr():
+#    descr = make_description()
+#    newoption = BoolOption('newoption', 'dummy twoo', default=False)
+#    descr.add_child(newoption)
+#    config = Config(descr)
+#    assert config.newoption == False
 
-def test_newoption_add_in_subdescr():
-    descr = make_description()
-    newoption = BoolOption('newoption', 'dummy twoo', default=False)
-    descr.gc.add_child(newoption)
-    config = Config(descr)
-    config.bool = False
-    assert config.gc.newoption == False
+#def test_newoption_add_in_subdescr():
+#    descr = make_description()
+#    newoption = BoolOption('newoption', 'dummy twoo', default=False)
+#    descr.gc.add_child(newoption)
+#    config = Config(descr)
+#    config.bool = False
+#    assert config.gc.newoption == False
 
 #def test_newoption_add_in_config():
 #    descr = make_description()
@@ -135,17 +134,17 @@ def test_newoption_add_in_subdescr():
 #    assert config.newoption == False
 # ____________________________________________________________
 def make_description_requires():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False)
 
     floatoption = FloatOption('float', 'Test float option', default=2.3)
 
     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)
     stroption = StrOption('str', 'Test string option', default="abc",
-                          requires=[('int', 1, 'hide')])
+                          requires=(('int', 1, 'hidden'),))
 
     gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
     descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
@@ -155,72 +154,76 @@ def make_description_requires():
 def test_hidden_if_in():
     descr = make_description_requires()
     cfg = Config(descr)
+    setting = cfg.cfgimpl_get_settings()
+    setting.read_write()
     intoption = cfg.unwrap_from_path('int')
     stroption = cfg.unwrap_from_path('str')
-    assert not stroption._is_hidden()
+    assert not setting.has_property('hidden', stroption)
     cfg.int = 1
     raises(PropertiesOptionError, "cfg.str")
     raises(PropertiesOptionError, 'cfg.str= "uvw"')
-    assert stroption._is_hidden()
+    assert setting.has_property('hidden', stroption)
 
 def test_hidden_if_in_with_group():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False)
 
     floatoption = FloatOption('float', 'Test float option', default=2.3)
 
     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)
     stroption = StrOption('str', 'Test string option', default="abc")
     gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption],
-                                                  requires=[('int', 1, 'hide')])
+                                                  requires=(('int', 1, 'hidden'),))
     descr = OptionDescription('constraints', '', [gcgroup, booloption,
                                           objspaceoption, stroption, intoption])
     cfg = Config(descr)
-    assert not gcgroup._is_hidden()
+    setting = cfg.cfgimpl_get_settings()
+    setting.read_write()
+    assert not setting.has_property('hidden', stroption)
     cfg.int = 1
     raises(PropertiesOptionError, "cfg.gc.name")
-    assert gcgroup._is_hidden()
 
 def test_disabled_with_group():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False)
 
     floatoption = FloatOption('float', 'Test float option', default=2.3)
 
     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)
     stroption = StrOption('str', 'Test string option', default="abc")
     gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption],
-                                                  requires=[('int', 1, 'disable')])
+                                                  requires=(('int', 1, 'disabled'),))
     descr = OptionDescription('constraints', '', [gcgroup, booloption,
                                           objspaceoption, stroption, intoption])
     cfg = Config(descr)
-    assert not gcgroup._is_disabled()
+    setting = cfg.cfgimpl_get_settings()
+    setting.read_write()
+    assert cfg.gc.name
     cfg.int = 1
     raises(PropertiesOptionError, "cfg.gc.name")
-    assert gcgroup._is_disabled()
 #____________________________________________________________
 
 def make_description_callback():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', callback="toto")
     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")
     boolop = BoolOption('boolop', 'Test boolean option op', default=True)
     wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=['boolop'])
+                                    requires=(('boolop', True, 'hidden'),))
     wantframework_option = BoolOption('wantframework', 'Test requires',
                                       default=False,
-                                      requires=['boolop'])
+                                      requires=(('boolop', True, 'hidden'),))
     gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
     descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
                                            wantref_option, stroption,
@@ -232,21 +235,23 @@ def test_has_callback():
     descr = make_description_callback()
     # here the owner is 'default'
     config = Config(descr)
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
     config.bool = False
     # because dummy has a callback
     dummy = config.unwrap_from_path('gc.dummy')
-    settings = config.cfgimpl_get_settings()
-    settings.freeze()
-    dummy.freeze()
+    setting.enable_property('freeze')
+    setting.add_property('frozen', dummy)
     raises(TypeError, "config.gc.dummy = True")
 
 def test_freeze_and_has_callback_with_setoption():
     descr = make_description_callback()
     config = Config(descr)
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
     config.bool = False
-    settings = config.cfgimpl_get_settings()
-    settings.freeze()
+    config.cfgimpl_get_settings().enable_property('freeze')
     dummy = config.unwrap_from_path('gc.dummy')
-    dummy.freeze()
+    config.cfgimpl_get_settings().add_property('frozen', dummy)
     raises(TypeError, "config.gc.setoption('dummy', True, 'gen_config')")
 #____________________________________________________________
index 84652d0..ad1e1a8 100644 (file)
@@ -51,31 +51,36 @@ def test_set_defaut_value_from_option_object():
     assert b.getdefault() == False
 
 def test_mandatory():
-    dummy1 = BoolOption('dummy1', 'doc dummy', mandatory=True)
-    dummy2 = BoolOption('dummy2', 'doc dummy', mandatory=True)
+    dummy1 = BoolOption('dummy1', 'doc dummy', properties=('mandatory', ))
+    dummy2 = BoolOption('dummy2', 'doc dummy', properties=('mandatory', ))
     group = OptionDescription('group', '', [dummy1, dummy2])
     config = Config(group)
+    setting = config.cfgimpl_get_settings()
+    setting.read_only()
 #    config.setoption('dummy1', True)
     raises(MandatoryError, 'config.dummy1')
+    setting.read_write()
     config.dummy1 = True
+    setting.read_only()
     assert config.dummy1 == True
     raises(MandatoryError, 'config.dummy2 == None')
 #    raises(MandatoryError, "config.override({'dummy2':None})")
+    setting.read_write()
     config.set(dummy2=True)
     config.dummy2 = False
+    setting.read_only()
     assert config.dummy2 == False
 
 def test_force_default_on_freeze():
     "a frozen option wich is forced returns his default"
-    dummy1 = BoolOption('dummy1', 'doc dummy', default=False)
+    dummy1 = BoolOption('dummy1', 'doc dummy', default=False, properties=('force_default_on_freeze',))
     dummy2 = BoolOption('dummy2', 'doc dummy', default=True)
     group = OptionDescription('group', '', [dummy1, dummy2])
     config = Config(group)
     config.dummy1 = True
     config.dummy2 = False
-    dummy1.freeze()
-    dummy1.force_default()
-    dummy2.freeze()
+    config.cfgimpl_get_settings().add_property('frozen', dummy1)
+    config.cfgimpl_get_settings().add_property('frozen', dummy2)
     assert config.dummy1 == False
     assert config.dummy2 == False
 
@@ -104,14 +109,14 @@ def test_overrides_changes_option_value():
 # test various option types
 def test_choice_with_no_default():
     descr = OptionDescription("test", "", [
-        ChoiceOption("backend", "", ["c", "cli"])])
+        ChoiceOption("backend", "", ("c", "cli"))])
     config = Config(descr)
     assert config.backend is None
     config.backend = "c"
 
 def test_choice_with_default():
     descr = OptionDescription("test", "", [
-        ChoiceOption("backend", "", ["c", "cli"], default="cli")])
+        ChoiceOption("backend", "", ("c", "cli"), default="cli")])
     config = Config(descr)
     assert config.backend == "cli"
 
index 2421389..5bdbf38 100644 (file)
@@ -32,9 +32,9 @@ def test_default_owner():
     cfg = Config(descr)
     assert cfg.dummy == False
     dm = cfg.unwrap_from_path('dummy')
-    assert dm.getowner(cfg) == 'default'
-    dm.setowner(cfg, owners.user)
-    assert dm.getowner(cfg) == owners.user
+    assert cfg.cfgimpl_get_values().getowner(dm) == 'default'
+    cfg.dummy = True
+    assert cfg.cfgimpl_get_values().getowner(dm) == owners.user
 
 def test_add_owner():
     gcdummy = BoolOption('dummy', 'dummy', default=False)
@@ -42,10 +42,11 @@ def test_add_owner():
     cfg = Config(descr)
     assert cfg.dummy == False
     dm = cfg.unwrap_from_path('dummy')
-    assert dm.getowner(cfg) == 'default'
+    assert cfg.cfgimpl_get_values().getowner(dm) == 'default'
     owners.add_owner("gen_config")
-    dm.setowner(cfg, owners.gen_config)
-    assert dm.getowner(cfg) == owners.gen_config
+    cfg.cfgimpl_get_settings().setowner(owners.gen_config)
+    cfg.dummy = True
+    assert cfg.cfgimpl_get_values().getowner(dm) == owners.gen_config
 
 def test_owner_is_not_a_string():
     gcdummy = BoolOption('dummy', 'dummy', default=False)
@@ -53,9 +54,8 @@ def test_owner_is_not_a_string():
     cfg = Config(descr)
     assert cfg.dummy == False
     dm = cfg.unwrap_from_path('dummy')
-    assert dm.getowner(cfg) == owners.default
-    assert dm.getowner(cfg) == 'default'
-    assert isinstance(dm.getowner(cfg), owners.Owner)
-    dm.setowner(cfg, owners.user)
-
-    assert dm.getowner(cfg) == 'user'
+    assert cfg.cfgimpl_get_values().getowner(dm) == owners.default
+    assert cfg.cfgimpl_get_values().getowner(dm) == 'default'
+    assert isinstance(cfg.cfgimpl_get_values().getowner(dm), owners.Owner)
+    cfg.dummy = True
+    assert cfg.cfgimpl_get_values().getowner(dm) == 'user'
index 97a0705..c030fdb 100644 (file)
@@ -5,12 +5,13 @@ from py.test import raises
 from tiramisu.config import *
 from tiramisu.option import *
 from tiramisu.error import *
+from tiramisu.setting import owners
 
 def make_description():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False)
     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)
@@ -40,9 +41,7 @@ def test_setitem():
     s = StrOption("string", "", default=["string", "sdfsdf"], default_multi="prout", multi=True)
     descr = OptionDescription("options", "", [s])
     config = Config(descr)
-    print config.string[1]
     config.string[1] = "titi"
-    print config.string[1]
 
 def test_reset():
     "if value is None, resets to default owner"
@@ -51,10 +50,10 @@ def test_reset():
     config = Config(descr)
     config.string = "foo"
     assert config.string == "foo"
-    assert config._cfgimpl_values.owners[s] == owners.user
+    assert config.cfgimpl_get_values().getowner(s) == owners.user
     config.unwrap_from_path("string").reset(config)
     assert config.string == 'string'
-    assert config._cfgimpl_values.owners[s] == owners.default
+    assert config.cfgimpl_get_values().getowner(s) == owners.default
 
 def test_reset_with_multi():
     s = StrOption("string", "", default=["string"], default_multi="string" , multi=True)
@@ -63,13 +62,13 @@ def test_reset_with_multi():
 #    config.string = []
     config.unwrap_from_path("string").reset(config)
     assert config.string == ["string"]
-    assert config._cfgimpl_values.owners[s] == 'default'
+    assert config.cfgimpl_get_values().getowner(s) == 'default'
     config.string = ["eggs", "spam", "foo"]
-    assert config._cfgimpl_values.owners[s] == 'user'
+    assert config.cfgimpl_get_values().getowner(s) == 'user'
     config.string = []
     config.unwrap_from_path("string").reset(config)
 #    assert config.string == ["string"]
-    assert config._cfgimpl_values.owners[s] == 'default'
+    assert config.cfgimpl_get_values().getowner(s) == 'default'
     raises(ConfigError, "config.string = None")
 
 def test_default_with_multi():
@@ -127,13 +126,15 @@ def test_multi_with_requires():
     s = StrOption("string", "", default=["string"], default_multi="string", multi=True)
     intoption = IntOption('int', 'Test int option', default=0)
     stroption = StrOption('str', 'Test string option', default=["abc"], default_multi = "abc",
-                          requires=[('int', 1, 'hide')], multi=True)
+                          requires=[('int', 1, 'hidden')], multi=True)
     descr = OptionDescription("options", "", [s, intoption, stroption])
     config = Config(descr)
-    assert stroption._is_hidden() == False
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
     config.int = 1
     raises(PropertiesOptionError, "config.str = ['a', 'b']")
-    assert stroption._is_hidden()
+    assert config.cfgimpl_get_settings().has_property('hidden', stroption)
 
 def test__requires_with_inverted():
     s = StrOption("string", "", default=["string"], multi=True)
@@ -142,40 +143,41 @@ def test__requires_with_inverted():
                           requires=[('int', 1, 'hide', 'inverted')], multi=True)
     descr = OptionDescription("options", "", [s, intoption, stroption])
     config = Config(descr)
-    assert stroption._is_hidden() == False
+    assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
     config.int = 1
-    assert stroption._is_hidden() == False
+    assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
 
 def test_multi_with_requires_in_another_group():
     s = StrOption("string", "", default=["string"], multi=True)
     intoption = IntOption('int', 'Test int option', default=0)
     descr = OptionDescription("options", "", [intoption])
     stroption = StrOption('str', 'Test string option', default=["abc"],
-                          requires=[('int', 1, 'hide')], multi=True)
+                          requires=[('int', 1, 'hidden')], multi=True)
     descr = OptionDescription("opt", "", [stroption])
     descr2 = OptionDescription("opt2", "", [intoption, s, descr])
     config = Config(descr2)
-    assert stroption._is_hidden() == False
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
     config.int = 1
     raises(PropertiesOptionError,  "config.opt.str = ['a', 'b']")
-    assert stroption._is_hidden()
+    assert config.cfgimpl_get_settings().has_property('hidden', stroption)
 
 def test_apply_requires_from_config():
     s = StrOption("string", "", default=["string"], multi=True)
     intoption = IntOption('int', 'Test int option', default=0)
     descr = OptionDescription("options", "", [intoption])
     stroption = StrOption('str', 'Test string option', default=["abc"],
-                          requires=[('int', 1, 'hide')], multi=True)
+                          requires=[('int', 1, 'hidden')], multi=True)
     descr = OptionDescription("opt", "", [stroption])
     descr2 = OptionDescription("opt2", "", [intoption, s, descr])
     config = Config(descr2)
-    assert stroption._is_hidden() == False
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
     config.int = 1
-    try:
-        config.opt.str
-    except:
-        pass
-    assert stroption._is_hidden()
+    raises(PropertiesOptionError, 'config.opt.str')
+    assert config.cfgimpl_get_settings().has_property('hidden', stroption)
 
 
 def test_apply_requires_with_disabled():
@@ -183,43 +185,46 @@ def test_apply_requires_with_disabled():
     intoption = IntOption('int', 'Test int option', default=0)
     descr = OptionDescription("options", "", [intoption])
     stroption = StrOption('str', 'Test string option', default=["abc"],
-                          requires=[('int', 1, 'disable')], multi=True)
+                          requires=[('int', 1, 'disabled')], multi=True)
     descr = OptionDescription("opt", "", [stroption])
     descr2 = OptionDescription("opt2", "", [intoption, s, descr])
     config = Config(descr2)
-    assert stroption._is_disabled() == False
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    assert not config.cfgimpl_get_settings().has_property('disabled', stroption)
     config.int = 1
-    try:
-        config.opt.str
-    except:
-        pass
-    assert stroption._is_disabled()
+    raises(PropertiesOptionError, 'config.opt.str')
+    assert config.cfgimpl_get_settings().has_property('disabled', stroption)
 
 def test_multi_with_requires_with_disabled_in_another_group():
     s = StrOption("string", "", default=["string"], multi=True)
     intoption = IntOption('int', 'Test int option', default=0)
     descr = OptionDescription("options", "", [intoption])
     stroption = StrOption('str', 'Test string option', default=["abc"],
-                          requires=[('int', 1, 'disable')], multi=True)
+                          requires=[('int', 1, 'disabled')], multi=True)
     descr = OptionDescription("opt", "", [stroption])
     descr2 = OptionDescription("opt2", "", [intoption, s, descr])
     config = Config(descr2)
-    assert stroption._is_disabled() == False
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    assert not config.cfgimpl_get_settings().has_property('disabled', stroption)
     config.int = 1
     raises(PropertiesOptionError,  "config.opt.str = ['a', 'b']")
-    assert stroption._is_disabled()
+    assert config.cfgimpl_get_settings().has_property('disabled', stroption)
 
 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)
     stroption = StrOption('str', 'Test string option', default=["abc"],
-                          requires=[('int', [1, 1], 'hide')], multi=True)
+                          requires=[('int', [1, 1], 'hidden')], multi=True)
     descr = OptionDescription("options", "", [s, intoption, stroption])
     config = Config(descr)
-    assert stroption._is_hidden() == False
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
     config.int = [1, 1]
     raises(PropertiesOptionError, "config.str = ['a', 'b']")
-    assert stroption._is_hidden()
+    assert config.cfgimpl_get_settings().has_property('hidden', stroption)
 
 def test_multi_with_bool():
     s = BoolOption("bool", "", default=[False], multi=True)
@@ -227,7 +232,7 @@ def test_multi_with_bool():
     config = Config(descr)
     assert descr.bool.multi == True
     config.bool = [True, False]
-    assert config._cfgimpl_context._cfgimpl_values[s] == [True, False]
+    assert config.cfgimpl_get_values()[s] == [True, False]
     assert config.bool == [True, False]
 
 def test_multi_with_bool_two():
@@ -238,7 +243,7 @@ def test_multi_with_bool_two():
     raises(ConfigError, "config.bool = True")
 
 def test_choice_access_with_multi():
-    ch = ChoiceOption("t1", "", ["a", "b"], default=["a"], multi=True)
+    ch = ChoiceOption("t1", "", ("a", "b"), default=["a"], multi=True)
     descr = OptionDescription("options", "", [ch])
     config = Config(descr)
     config.t1 = ["a", "b", "a", "b"]
@@ -257,7 +262,7 @@ def test_dwim_set():
     descr = OptionDescription("opt", "", [
         OptionDescription("sub", "", [
             BoolOption("b1", ""),
-            ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'),
+            ChoiceOption("c1", "", ('a', 'b', 'c'), 'a'),
             BoolOption("d1", ""),
         ]),
         BoolOption("b2", ""),
@@ -287,13 +292,14 @@ def test_more_set():
     assert config.int == 23
 
 def test_set_with_hidden_option():
-    boolopt = BoolOption("a", "", default=False)
-    boolopt.hide()
+    boolopt = BoolOption("a", "", default=False, properties=(('hidden'),))
     descr = OptionDescription("opt", "", [
         OptionDescription("s1", "", [boolopt]),
         IntOption("int", "", default=42)])
     d = {'s1.a': True, 'int': 23}
     config = Config(descr)
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
     raises(PropertiesOptionError, "config.set(**d)")
 
 def test_set_with_unknown_option():
@@ -380,16 +386,17 @@ def test_access_by_get():
     assert cfg.get('dummy') == False
 
 def test_access_by_get_whith_hide():
-    b1 = BoolOption("b1", "")
-    b1.hide()
+    b1 = BoolOption("b1", "", properties=(('hidden'),))
     descr = OptionDescription("opt", "", [
     OptionDescription("sub", "", [
         b1,
-        ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'),
+        ChoiceOption("c1", "", ('a', 'b', 'c'), 'a'),
         BoolOption("d1", ""),
     ]),
     BoolOption("b2", ""),
     BoolOption("d1", ""),
     ])
     c = Config(descr)
-    raises(PropertiesOptionError, "c.get('b1')")
+    setting = c.cfgimpl_get_settings()
+    setting.read_write()
+    raises(NotFoundError, "c.get('b1')")
index aab0ac7..4a83c4e 100644 (file)
@@ -7,22 +7,20 @@ from tiramisu.config import *
 from tiramisu.option import *
 
 def make_description():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-    # hidding dummy here
-    gcdummy.hide()
+    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')])
+                                    requires=(('gc.name', 'ref', 'hidden'),))
     wantframework_option = BoolOption('wantframework', 'Test requires',
                                       default=False,
-                                      requires=[('gc.name', 'framework')])
+                                      requires=(('gc.name', 'framework', 'hidden'),))
 
     # ____________________________________________________________
     booloptiontwo = BoolOption('booltwo', 'Test boolean option two', default=False)
@@ -38,20 +36,20 @@ def make_description():
 #____________________________________________________________
 #freeze
 def make_description_freeze():
-    gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
     gcdummy = BoolOption('dummy', 'dummy', default=False)
     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")
     boolop = BoolOption('boolop', 'Test boolean option op', default=True)
     wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                    requires=['boolop'])
+                                    requires=(('boolop', True, 'hidden'),))
     wantframework_option = BoolOption('wantframework', 'Test requires',
                                       default=False,
-                                      requires=['boolop'])
+                                      requires=(('boolop', True, 'hidden'),))
 
     gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
     descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
@@ -64,10 +62,10 @@ def test_freeze_whole_config():
     descr = make_description_freeze()
     conf = Config(descr)
     settings = conf.cfgimpl_get_settings()
-    settings.freeze_everything()
+    settings.enable_property('everything_frozen')
     assert conf.gc.dummy == False
     raises(TypeError, "conf.gc.dummy = True")
-    settings.un_freeze_everything()
+    settings.disable_property('everything_frozen')
     conf.gc.dummy = True
     assert conf.gc.dummy == True
 
@@ -75,8 +73,11 @@ 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
-    conf.gc._cfgimpl_descr.dummy.freeze()
+    dummy = conf.unwrap_from_path('gc.dummy')
+    conf.gc.cfgimpl_get_settings().add_property('frozen', dummy)
     assert conf.gc.dummy == False
     raises(TypeError, "conf.gc.dummy = True")
 
@@ -85,45 +86,48 @@ def test_frozen_value():
     s = StrOption("string", "", default="string")
     descr = OptionDescription("options", "", [s])
     config = Config(descr)
-    settings = config.cfgimpl_get_settings()
-    settings.freeze()
-    s.freeze()
+    settings = config.cfgimpl_get_settings().enable_property('frozen')
+    config.cfgimpl_get_settings().add_property('frozen', s)
     raises(TypeError, 'config.string = "egg"')
 
 def test_freeze():
     "freeze a whole configuration object"
     descr = make_description()
     conf = Config(descr)
-    settings = conf.cfgimpl_get_settings()
-    settings.freeze()
+    settings = conf.cfgimpl_get_settings().enable_property('frozen')
     name = conf.unwrap_from_path("gc.name")
-    name.freeze()
+    conf.cfgimpl_get_settings().add_property('frozen', name)
     raises(TypeError, "conf.gc.name = 'framework'")
 # ____________________________________________________________
 def test_is_hidden():
     descr = make_description()
     config = Config(descr)
-    assert config.gc._cfgimpl_descr.dummy._is_hidden() == True
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    dummy = config.unwrap_from_path('gc.dummy')
+    assert not config.cfgimpl_get_settings().has_property('frozen', dummy)
     # setattr
     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_context._cfgimpl_values[opt] == False
+    assert config.cfgimpl_get_values()[opt] == False
 
 def test_group_is_hidden():
     descr = make_description()
     config = Config(descr)
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
     gc = config.unwrap_from_path('gc')
     dummy = config.unwrap_from_path('gc.dummy')
-    gc.hide()
+    config.cfgimpl_get_settings().add_property('hidden', gc)
     raises(PropertiesOptionError, "config.gc.dummy")
-    assert gc._is_hidden()
+    assert config.cfgimpl_get_settings().has_property('hidden', gc)
     raises(PropertiesOptionError, "config.gc.float")
     # manually set the subconfigs to "show"
-    gc.show()
-    assert gc._is_hidden() == False
+    config.cfgimpl_get_settings().del_property('hidden', gc)
+    assert not config.cfgimpl_get_settings().has_property('hidden', gc)
     assert config.gc.float == 2.3
     #dummy est en hide
     raises(PropertiesOptionError, "config.gc.dummy == False")
@@ -131,17 +135,22 @@ def test_group_is_hidden():
 def test_global_show():
     descr = make_description()
     config = Config(descr)
-    assert config.gc._cfgimpl_descr.dummy._is_hidden() == True
+    setting = config.cfgimpl_get_settings()
+    setting.read_write()
+    dummy = config.unwrap_from_path('gc.dummy')
+    config.cfgimpl_get_settings().add_property('hidden', dummy)
+    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)
-    assert config.gc.subgroup._cfgimpl_descr.booltwo._is_hidden() == False
+    booltwo = config.unwrap_from_path('gc.subgroup.booltwo')
+    assert not config.cfgimpl_get_settings().has_property('hidden', booltwo)
     assert config.gc.subgroup.booltwo == False
-    config.gc.subgroup._cfgimpl_descr.booltwo.hide()
+    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)
-    assert option._is_hidden()
+    assert config.cfgimpl_get_settings().has_property('hidden', booltwo)
index 9148194..09797c1 100644 (file)
@@ -17,7 +17,7 @@ def make_description():
                                       default=False)
     adresse_serveur_ntp = StrOption('serveur_ntp', "adresse serveur ntp", multi=True)
     time_zone = ChoiceOption('time_zone', 'fuseau horaire du serveur',
-                                ['Paris', 'Londres'], 'Paris')
+                                ('Paris', 'Londres'), 'Paris')
 
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
@@ -134,10 +134,10 @@ def test_values_with_master_and_slaves():
     opt = cfg.unwrap_from_path("ip_admin_eth0.ip_admin_eth0")
     owner = cfg._cfgimpl_context._cfgimpl_settings.getowner()
     assert interface1.get_group_type() == groups.master
-    assert opt.getowner(cfg) == owners.default
+    assert cfg.cfgimpl_get_values().getowner(opt) == owners.default
     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
     assert  cfg.ip_admin_eth0.ip_admin_eth0 == ["192.168.230.145"]
-    assert opt.getowner(cfg) == owner
+    assert cfg.cfgimpl_get_values().getowner(opt) == owner
 
 def test_reset_values_with_master_and_slaves():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
@@ -149,9 +149,9 @@ def test_reset_values_with_master_and_slaves():
     opt = cfg.unwrap_from_path("ip_admin_eth0.ip_admin_eth0")
     owner = cfg._cfgimpl_context._cfgimpl_settings.getowner()
     assert interface1.get_group_type() == groups.master
-    assert opt.getowner(cfg) == owners.default
+    assert cfg.cfgimpl_get_values().getowner(opt) == owners.default
     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
-    assert opt.getowner(cfg) == owner
-    cfg._cfgimpl_context._cfgimpl_values.reset(opt)
-    assert opt.getowner(cfg) == owners.default
+    assert cfg.cfgimpl_get_values().getowner(opt) == owner
+    cfg.cfgimpl_get_values().reset(opt)
+    assert cfg.cfgimpl_get_values().getowner(opt) == owners.default
     assert  cfg.ip_admin_eth0.ip_admin_eth0 == []
index 44b8934..cf5d40c 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors)
+# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -27,14 +27,15 @@ from tiramisu.error import PropertiesOptionError, ConflictConfigError
 #    # in case of an 'auto' and a 'fill' without a value,
 #    # we have to carry out a calculation
 #    return calc_factory(name, callback, callback_params, config)
-def carry_out_calculation(name, option, config):
+
+
+def carry_out_calculation(name, config, callback, callback_params):
     # FIXME we have to know the exact status of the config
     # not to disrupt it
     # config.freeze()
-    callback=option.getcallback()
-    callback_params=option.getcallback_params()
-    if callback_params is None:
-        callback_params = {}
+    #callback, callback_params = option.getcallback()
+    #if callback_params is None:
+    #    callback_params = {}
     tcparams = {}
     one_is_multi = False
     len_multi = 0
@@ -44,7 +45,7 @@ def carry_out_calculation(name, option, config):
             if type(value) == tuple:
                 path, check_disabled = value
                 try:
-                    opt_value = config._getattr(path, permissive=True)
+                    opt_value = config._getattr(path, force_permissive=True)
                     opt = config.unwrap_from_path(path)
                 except PropertiesOptionError, err:
                     if check_disabled:
@@ -52,12 +53,14 @@ def carry_out_calculation(name, option, config):
                     raise PropertiesOptionError(err, err.proptype)
                 is_multi = opt.is_multi()
                 if is_multi:
-                    if opt_value != None:
+                    if opt_value is not None:
                         len_value = len(opt_value)
                         if len_multi != 0 and len_multi != len_value:
-                            raise ConflictConfigError('unable to carry out a calculation, '
-                            'option values with multi types must have same length for: '
-                             + name)
+                            raise ConflictConfigError('unable to carry out '
+                                                      'a calculation, option '
+                                                      'values with multi types'
+                                                      ' must have same length '
+                                                      'for: ' + name)
                         len_multi = len_value
                     one_is_multi = True
                 tcparams.setdefault(key, []).append((opt_value, is_multi))
@@ -72,7 +75,7 @@ def carry_out_calculation(name, option, config):
             for key, couples in tcparams.items():
                 for couple in couples:
                     value, ismulti = couple
-                    if ismulti and value != None:
+                    if ismulti and value is not None:
                         if key == '':
                             params.append(value[incr])
                         else:
@@ -104,6 +107,7 @@ def carry_out_calculation(name, option, config):
                     tcp[key] = couple[0]
         return calculate(name, callback, params, tcp)
 
+
 def calculate(name, callback, params, tcparams):
     try:
         # XXX not only creole...
@@ -113,5 +117,5 @@ def calculate(name, callback, params, tcparams):
         import traceback
         traceback.print_exc()
         raise ConflictConfigError("callback: {0} return error {1} for "
-                       "option: {2}".format(callback, str(err), name))
-
+                                  "option: {2}".format(callback, str(err),
+                                                       name))
diff --git a/tiramisu/basetype.py b/tiramisu/basetype.py
deleted file mode 100644 (file)
index 1a8ab13..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- coding: utf-8 -*-
-"base 'interface' types for option types"
-# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
-# The original `Config` design model is unproudly borrowed from
-# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
-# the whole pypy projet is under MIT licence
-# ____________________________________________________________
-
-class CommonType(object):
-    def has_properties(self):
-        return bool(len(self.properties))
-    def has_property(self, propname):
-        return propname in self.properties
-    def get_properties(self):
-        return self.properties
-    def add_property(self, propname):
-        if not propname in self.properties:
-            self.properties.append(propname)
-    def del_property(self, propname):
-        if self.has_property(propname):
-            self.properties.remove(propname)
-
-#class HiddenBaseType(BaseType):
-#    def hide(self):
-#        self.add_property('hidden')
-#    def show(self):
-#        self.del_property('hidden')
-#    def _is_hidden(self):
-#        # dangerous method: how an Option() can determine its status by itself ?
-#        return self.has_property('hidden')
-
-#class DisabledBaseType(BaseType):
-#    def disable(self):
-#        self.add_property('disabled')
-#    def enable(self):
-#        self.del_property('disabled')
-#    def _is_disabled(self):
-#        return self.has_property('disabled')
-
-# commonly used properties. Option and OptionDescription will have these methods
-common_properties = {'hidden': ('hide', 'show', '_is_hidden'),
-              'disabled': ('disable', 'enable', '_is_disabled')
-             }
-
-basetype_methods = dict()
-
-def build_properties(prop, add_prop, del_prop, is_prop):
-    def add_property(self):
-        self.add_property(prop)
-    def del_property(self):
-        self.del_property(prop)
-    def is_property(self):
-        return self.has_property(prop)
-    return {add_prop:add_property, del_prop:del_property,
-                is_prop:is_property}
-
-for propname, meth_names in common_properties.items():
-    basetype_methods.update(build_properties(propname, meth_names[0], meth_names[1],
-                            meth_names[2]))
-
-BaseType = type('BaseType', (CommonType,), basetype_methods)
-
index 469e198..caf808d 100644 (file)
 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
-from copy import copy
-from inspect import getmembers, ismethod
-from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
-    AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound,
-    MandatoryError, MethodCallError, NoValueReturned)
+#from inspect import getmembers, ismethod
+from tiramisu.error import (PropertiesOptionError, NotFoundError,
+                            AmbigousOptionError, NoMatchingOptionFound, MandatoryError)
 from tiramisu.option import (OptionDescription, Option, SymLinkOption,
-    apply_requires)
-from tiramisu.setting import groups, owners, Setting
+                             apply_requires)
+from tiramisu.setting import groups, Setting
 from tiramisu.value import Values
 
-# ____________________________________________________________
-class Config(object):
-    "main configuration management entry"
-    __slots__ = ('_cfgimpl_descr', '_cfgimpl_subconfigs',
-            '_cfgimpl_parent', '_cfgimpl_warnings', '_cfgimpl_permissive',
-            '_cfgimpl_context', '_cfgimpl_settings', '_cfgimpl_values',
-            '_cfgimpl_slots', '_cfgimpl_build_all_paths')
 
-    def __init__(self, descr, parent=None, context=None, valid_opt_names=True):
+class SubConfig(object):
+    "sub configuration management entry"
+    __slots__ = ('_cfgimpl_descr', '_cfgimpl_subconfigs', '_cfgimpl_parent',
+                 '_cfgimpl_context')
+
+    def __init__(self, descr, parent, context):  # FIXME , slots):
         """ Configuration option management master class
 
         :param descr: describes the configuration schema
         :type descr: an instance of ``option.OptionDescription``
-        :param parent: is None if the ``Config`` is root parent Config otherwise
-        :type parent: ``Config``
+        :param parent: parent's `Config`
+        :type parent: `Config`
         :param context: the current root config
         :type context: `Config`
         """
         # main option description
         self._cfgimpl_descr = descr
         # sub option descriptions
-        self._cfgimpl_subconfigs = {}
+        self._cfgimpl_subconfigs = None
         self._cfgimpl_parent = parent
-        self._cfgimpl_permissive = []
-        if context is None:
-            self._cfgimpl_context = self
-        else:
-            self._cfgimpl_context = context
-        if parent is None:
-            self._cfgimpl_settings = Setting()
-            self._cfgimpl_settings.valid_opt_names = valid_opt_names
-            self._cfgimpl_values = Values(self._cfgimpl_context)
-            #self._cfgimpl_all_paths = {}
-        else:
-            if context is None:
-                raise ConfigError("cannot find a value for this config")
-            valid_opt_names = context._cfgimpl_settings.valid_opt_names
-        "warnings are a great idea, let's make up a better use of it"
-        self._cfgimpl_warnings = []
-        if valid_opt_names:
-            # some api members shall not be used as option's names !
-            methods = getmembers(self, ismethod)
-            self._cfgimpl_slots = [key for key, value in methods
-                                  if not key.startswith("_")]
-        else:
-            self._cfgimpl_slots = []
-        self._cfgimpl_build()
-        if parent is None:
-            self._cfgimpl_build_all_paths()
+        self._cfgimpl_context = context
+        #self._cfgimpl_build(slots)
 
-    def _cfgimpl_build_all_paths(self):
-        self._cfgimpl_descr.build_cache()
+    def cfgimpl_get_context(self):
+        return self._cfgimpl_context
 
     def cfgimpl_get_settings(self):
         return self._cfgimpl_context._cfgimpl_settings
 
-    def cfgimpl_set_settings(self, settings):
-        if not isinstance(settings, Setting):
-            raise ConfigError("setting not allowed")
-        self._cfgimpl_context._cfgimpl_settings = settings
+    def cfgimpl_get_values(self):
+        return self._cfgimpl_context._cfgimpl_values
 
     def cfgimpl_get_description(self):
         return self._cfgimpl_descr
 
-    def cfgimpl_get_value(self, path):
-        """same as multiple getattrs
-
-        :param path: list or tuple of path
-
-        example : ``path = (sub_conf, opt)``  makes a getattr of sub_conf, then
-        a getattr of opt, and then returns the opt's value.
-
-        :returns: subconf or option's value
-        """
-        subpaths = list(path)
-        subconf_or_opt = self
-        for subpath in subpaths:
-            subconf_or_opt = getattr(subconf_or_opt, subpath)
-        return subconf_or_opt
-
-    def _validate_duplicates(self, children):
-        """duplicates Option names in the schema
-        :type children: list of `Option` or `OptionDescription`
-        """
-        duplicates = []
-        for dup in children:
-            if dup._name not in duplicates:
-                duplicates.append(dup._name)
-            else:
-                raise ConflictConfigError('duplicate option name: '
-                    '{0}'.format(dup._name))
-
-    def _cfgimpl_build(self):
-        """
-        - builds the config object from the schema
-        - settles various default values for options
-        """
-        self._validate_duplicates(self._cfgimpl_descr._children)
-        if self._cfgimpl_descr.group_type == groups.master:
-            mastername = self._cfgimpl_descr._name
-            masteropt = getattr(self._cfgimpl_descr, mastername)
-            self._cfgimpl_context._cfgimpl_values.masters[masteropt] = []
-
-        for child in self._cfgimpl_descr._children:
-            if isinstance(child, OptionDescription):
-                self._validate_duplicates(child._children)
-                self._cfgimpl_subconfigs[child] = Config(child, parent=self,
-                                                context=self._cfgimpl_context)
-            else:
-                if child._name in self._cfgimpl_slots:
-                    raise NameError("invalid name for the option:"
-                                    " {0}".format(child._name))
-
-            if (self._cfgimpl_descr.group_type == groups.master and
-                    child != masteropt):
-                self._cfgimpl_context._cfgimpl_values.slaves[child] = masteropt
-                self._cfgimpl_context._cfgimpl_values.masters[masteropt].append(child)
-
     # ____________________________________________________________
     # attribute methods
     def __setattr__(self, name, value):
@@ -156,53 +72,44 @@ class Config(object):
             #self.__dict__[name] = value
             object.__setattr__(self, name, value)
             return
+        self._setattr(name, value)
+
+    def _setattr(self, name, value, force_permissive=False):
         if '.' in name:
             homeconfig, name = self.cfgimpl_get_home_by_path(name)
-            return setattr(homeconfig, name, value)
+            return homeconfig.__setattr__(name, value)
         if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption:
-            self._validate(name, getattr(self._cfgimpl_descr, name))
-        if name in self._cfgimpl_slots:
-            raise NameError("invalid name for the option:"
-                            " {0}".format(name))
+            self._validate(name, getattr(self._cfgimpl_descr, name), force_permissive=force_permissive)
         self.setoption(name, value)
 
-    def _validate(self, name, opt_or_descr, permissive=False):
+    def _validate(self, name, opt_or_descr, force_permissive=False):
         "validation for the setattr and the getattr"
-        apply_requires(opt_or_descr, self, permissive=permissive)
+        apply_requires(opt_or_descr, self)
         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(copy(opt_or_descr.properties))
-        properties = properties & set(self._cfgimpl_context._cfgimpl_settings.get_properties())
-        if permissive:
-            properties = properties - set(self._cfgimpl_context._cfgimpl_settings.get_permissive())
-        properties = properties - set(self._cfgimpl_permissive)
+        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'):
+            properties = properties - set(self.cfgimpl_get_settings().get_permissive())
+        properties = properties - set(self.cfgimpl_get_settings().get_permissive(self.cfgimpl_get_description()))
         properties = list(properties)
         if properties != []:
             raise PropertiesOptionError("trying to access"
-                    " to an option named: {0} with properties"
-                    " {1}".format(name, str(properties)),
-                    properties)
+                                        " to an option named: {0} with properties"
+                                        " {1}".format(name, str(properties)),
+                                        properties)
 
     def __getattr__(self, name):
         return self._getattr(name)
 
-#    def fill_multi(self, opt, result, use_default_multi=False, default_multi=None):
-#        """fills a multi option with default and calculated values
-#        """
-#        # FIXME C'EST ENCORE DU N'IMPORTE QUOI
-#        if not isinstance(result, list):
-#            _result = [result]
-#        else:
-#            _result = result
-#        return Multi(_result, self._cfgimpl_context, opt)
-
-    def _getattr(self, name, permissive=False):
+    def _getattr(self, name, force_permissive=False, force_properties=None):
         """
         attribute notation mechanism for accessing the value of an option
         :param name: attribute name
-        :param permissive: permissive doesn't raise some property error
-                          (see ``permissive``)
         :return: option's value if name is an option name, OptionDescription
                  otherwise
         """
@@ -210,50 +117,29 @@ class Config(object):
         # for instance getattr(self, "creole.general.family.adresse_ip_eth0")
         if '.' in name:
             homeconfig, name = self.cfgimpl_get_home_by_path(name)
-            return homeconfig._getattr(name, permissive)
+            return homeconfig._getattr(name)
         opt_or_descr = getattr(self._cfgimpl_descr, name)
         # symlink options
         if type(opt_or_descr) == SymLinkOption:
-            rootconfig = self._cfgimpl_get_toplevel()
-            return getattr(rootconfig, opt_or_descr.path)
+            rootconfig = self.cfgimpl_get_context()
+            path = rootconfig.cfgimpl_get_description().get_path_by_opt(opt_or_descr.opt)
+            return getattr(rootconfig, path)
 
-        self._validate(name, opt_or_descr, permissive)
+        self._validate(name, opt_or_descr, force_permissive=force_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]
+            children = self.cfgimpl_get_description()._children
+            if opt_or_descr not in children[1]:
+                raise AttributeError("{0} with name {1} object has "
+                                     "no attribute {2}".format(self.__class__,
+                                                               opt_or_descr._name,
+                                                               name))
+            return SubConfig(opt_or_descr, self, self._cfgimpl_context)
         # special attributes
         if name.startswith('_cfgimpl_'):
             # if it were in __dict__ it would have been found already
-            return self.__dict__[name]
-        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()
-        **and it is slow**: it recursively searches into the namespaces
-
-        :returns: Option()
-        """
-        paths = self.getpaths(allpaths=True)
-        opts = dict([(path, self.unwrap_from_path(path)) for path in paths])
-        all_paths = [p.split(".") for p in self.getpaths()]
-        for pth in all_paths:
-            if name in pth:
-                return opts[".".join(pth)]
-        raise NotFoundError("name: {0} not found".format(name))
-
-    def unwrap_from_path(self, path):
-        """convenience method to extract and Option() object from the Config()
-        and it is **fast**: finds the option directly in the appropriate
-        namespace
-
-        :returns: Option()
-        """
-        if '.' in path:
-            homeconfig, path = self.cfgimpl_get_home_by_path(path)
-            return getattr(homeconfig._cfgimpl_descr, path)
-        return getattr(self._cfgimpl_descr, path)
+            object.__getattr__(self, name)
+        return self.cfgimpl_get_values()._getitem(opt_or_descr,
+                                                  force_properties=force_properties)
 
     def setoption(self, name, value, who=None):
         """effectively modifies the value of an Option()
@@ -262,60 +148,6 @@ class Config(object):
         child = getattr(self._cfgimpl_descr, name)
         child.setoption(self, value)
 
-    def set(self, **kwargs):
-        """
-        do what I mean"-interface to option setting. Searches all paths
-        starting from that config for matches of the optional arguments
-        and sets the found option if the match is not ambiguous.
-
-        :param kwargs: dict of name strings to values.
-        """
-        all_paths = [p.split(".") for p in self.getpaths(allpaths=True)]
-        for key, value in kwargs.iteritems():
-            key_p = key.split('.')
-            candidates = [p for p in all_paths if p[-len(key_p):] == key_p]
-            if len(candidates) == 1:
-                name = '.'.join(candidates[0])
-                homeconfig, name = self.cfgimpl_get_home_by_path(name)
-                try:
-                    getattr(homeconfig, name)
-                except MandatoryError:
-                    pass
-                except Exception, e:
-                    raise e  # HiddenOptionError or DisabledOptionError
-                homeconfig.setoption(name, value)
-            elif len(candidates) > 1:
-                raise AmbigousOptionError(
-                    'more than one option that ends with %s' % (key, ))
-            else:
-                raise NoMatchingOptionFound(
-                    'there is no option that matches %s'
-                    ' or the option is hidden or disabled' % (key, ))
-
-    def get(self, name):
-        """
-        same as a `find_first()` method in a config that has identical names:
-        it returns the first item of an option named `name`
-
-        much like the attribute access way, except that
-        the search for the option is performed recursively in the whole
-        configuration tree.
-        **carefull**: very slow !
-
-        :returns: option value.
-        """
-        paths = self.getpaths(allpaths=True)
-        pathsvalues = []
-        for path in paths:
-            pathname = path.split('.')[-1]
-            if pathname == name:
-                try:
-                    value = getattr(self, path)
-                    return value
-                except Exception, e:
-                    raise e
-        raise NotFoundError("option {0} not found in config".format(name))
-
     def cfgimpl_get_home_by_path(self, path):
         """:returns: tuple (config, name)"""
         path = path.split('.')
@@ -323,40 +155,16 @@ class Config(object):
             self = getattr(self, step)
         return self, path[-1]
 
-    def _cfgimpl_get_toplevel(self):
-        ":returns: root config"
-        while self._cfgimpl_parent is not None:
-            self = self._cfgimpl_parent
-        return self
-
     def _cfgimpl_get_path(self):
         "the path in the attribute access meaning."
+        #FIXME optimisation
         subpath = []
         obj = self
         while obj._cfgimpl_parent is not None:
             subpath.insert(0, obj._cfgimpl_descr._name)
             obj = obj._cfgimpl_parent
         return ".".join(subpath)
-    # ______________________________________________________________________
-#    def cfgimpl_previous_value(self, path):
-#        "stores the previous value"
-#        home, name = self.cfgimpl_get_home_by_path(path)
-#        # FIXME  fucking name
-#        return home._cfgimpl_context._cfgimpl_values.previous_values[name]
-
-#    def get_previous_value(self, name):
-#        "for the time being, only the previous Option's value is accessible"
-#        return self._cfgimpl_context._cfgimpl_values.previous_values[name]
-    # ______________________________________________________________________
-    def add_warning(self, warning):
-        "Config implements its own warning pile. Could be useful"
-        self._cfgimpl_get_toplevel()._cfgimpl_warnings.append(warning)
 
-    def get_warnings(self):
-        "Config implements its own warning pile"
-        return self._cfgimpl_get_toplevel()._cfgimpl_warnings
-
-    # ____________________________________________________________
     def getkey(self):
         return self._cfgimpl_descr.getkey(self)
 
@@ -377,7 +185,7 @@ class Config(object):
     def __iter__(self):
         """Pythonesque way of parsing group's ordered options.
         iteration only on Options (not OptionDescriptions)"""
-        for child in self._cfgimpl_descr._children:
+        for child in self._cfgimpl_descr._children[1]:
             if not isinstance(child, OptionDescription):
                 try:
                     yield child._name, getattr(self, child._name)
@@ -389,7 +197,7 @@ class Config(object):
     def iter_all(self):
         """A way of parsing options **and** groups.
         iteration on Options and OptionDescriptions."""
-        for child in self._cfgimpl_descr._children:
+        for child in self._cfgimpl_descr._children[1]:
             try:
                 yield child._name, getattr(self, child._name)
             except GeneratorExit:
@@ -410,7 +218,7 @@ class Config(object):
         if group_type is not None:
             if not isinstance(group_type, groups.GroupType):
                 raise TypeError("Unknown group_type: {0}".format(group_type))
-        for child in self._cfgimpl_descr._children:
+        for child in self._cfgimpl_descr._children[1]:
             if isinstance(child, OptionDescription):
                 try:
                     if group_type is not None:
@@ -423,10 +231,12 @@ class Config(object):
                 except:
                     pass
     # ______________________________________________________________________
+
     def cfgimpl_set_permissive(self, permissive):
         if not isinstance(permissive, list):
             raise TypeError('permissive must be a list')
-        self._cfgimpl_permissive = permissive
+        self.cfgimpl_get_settings().set_permissive(permissive, self.cfgimpl_get_description())
+
     # ______________________________________________________________________
     def __str__(self):
         "Config's string representation"
@@ -457,8 +267,7 @@ class Config(object):
                 paths.append(path)
             else:
                 try:
-                    value = getattr(self, path)
-
+                    getattr(self, path)
                 except MandatoryError:
                     if mandatory:
                         paths.append(path)
@@ -468,33 +277,135 @@ class Config(object):
                     paths.append(path)
         return paths
 
-    def _find(self, bytype, byname, byvalue, byattrs, first):
+    def getpath(self):
+        descr = self.cfgimpl_get_description()
+        context_descr = self.cfgimpl_get_context().cfgimpl_get_description()
+        return context_descr.get_path_by_opt(descr)
+
+    def get(self, name):
+        path = self.getpath()
+        return self.cfgimpl_get_context().get(name, _subpath=path)
+
+    def find(self, bytype=None, byname=None, byvalue=None, byattrs=None):
+        path = self.getpath()
+        return self.cfgimpl_get_context().find(bytype=bytype, byname=byname,
+                                               byvalue=byvalue,
+                                               byattrs=byattrs,
+                                               _subpath=path)
+
+    def find_first(self, bytype=None, byname=None, byvalue=None, byattrs=None):
+        path = self.getpath()
+        return self.cfgimpl_get_context().find_first(bytype=bytype,
+                                                     byname=byname,
+                                                     byvalue=byvalue,
+                                                     byattrs=byattrs,
+                                                     _subpath=path)
+
+
+# ____________________________________________________________
+class Config(SubConfig):
+    "main configuration management entry"
+    __slots__ = ('_cfgimpl_settings', '_cfgimpl_values')
+
+    def __init__(self, descr, valid_opt_names=True):
+        """ Configuration option management master class
+
+        :param descr: describes the configuration schema
+        :type descr: an instance of ``option.OptionDescription``
+        :param parent: is None if the ``Config`` is root parent Config otherwise
+        :type parent: ``Config``
+        :param context: the current root config
+        :type context: `Config`
+        """
+        self._cfgimpl_settings = Setting()
+        self._cfgimpl_values = Values(self)
+        #if valid_opt_names:
+        #    # some api members shall not be used as option's names !
+        #    #FIXME fait une boucle infini ...
+        #    #methods = getmembers(self, ismethod)
+        #    #slots = tuple([key for key, value in methods
+        #    #               if not key.startswith("_")])
+        #    slots = []
+        #else:
+        #    slots = []
+        super(Config, self).__init__(descr, None, self)  # , slots)
+        self._cfgimpl_build_all_paths()
+
+    def _cfgimpl_build_all_paths(self):
+        self._cfgimpl_descr.build_cache()
+
+    def unwrap_from_path(self, path):
+        """convenience method to extract and Option() object from the Config()
+        and it is **fast**: finds the option directly in the appropriate
+        namespace
+
+        :returns: Option()
+        """
+        if '.' in path:
+            homeconfig, path = self.cfgimpl_get_home_by_path(path)
+            return getattr(homeconfig._cfgimpl_descr, path)
+        return getattr(self._cfgimpl_descr, path)
+
+    def set(self, **kwargs):
+        """
+        do what I mean"-interface to option setting. Searches all paths
+        starting from that config for matches of the optional arguments
+        and sets the found option if the match is not ambiguous.
+
+        :param kwargs: dict of name strings to values.
+        """
+        all_paths = [p.split(".") for p in self.getpaths(allpaths=True)]
+        for key, value in kwargs.iteritems():
+            key_p = key.split('.')
+            candidates = [p for p in all_paths if p[-len(key_p):] == key_p]
+            if len(candidates) == 1:
+                name = '.'.join(candidates[0])
+                homeconfig, name = self.cfgimpl_get_home_by_path(name)
+                try:
+                    getattr(homeconfig, name)
+                except MandatoryError:
+                    pass
+                except Exception, e:
+                    raise e  # HiddenOptionError or DisabledOptionError
+                homeconfig.setoption(name, value)
+            elif len(candidates) > 1:
+                raise AmbigousOptionError(
+                    'more than one option that ends with %s' % (key, ))
+            else:
+                raise NoMatchingOptionFound(
+                    'there is no option that matches %s'
+                    ' or the option is hidden or disabled' % (key, ))
+
+    def get(self, name, _subpath=None):
+        """
+        same as a `find_first()` method in a config that has identical names:
+        it returns the first item of an option named `name`
+
+        much like the attribute access way, except that
+        the search for the option is performed recursively in the whole
+        configuration tree.
+
+        :returns: option value.
+        """
+        return self._find(byname=name, bytype=None, byvalue=None, byattrs=None,
+                          first=True, getvalue=True, _subpath=_subpath)
+
+    def _find(self, bytype, byname, byvalue, byattrs, first, getvalue=False,
+              _subpath=None):
         """
         convenience method for finding an option that lives only in the subtree
 
         :param first: return only one option if True, a list otherwise
         :return: find list or an exception if nothing has been found
         """
-        def _filter_by_attrs():
-            if byattrs is None:
-                return True
-            for key, value in byattrs.items():
-                if not hasattr(option, key):
-                    return False
-                else:
-                    if getattr(option, key) != value:
-                        return False
-                    else:
-                        continue
-            return True
         def _filter_by_name():
             if byname is None:
                 return True
-            pathname = path.split('.')[-1]
-            if pathname == byname:
+            if path == byname or path.endswith('.' + byname):
                 return True
             else:
                 return False
+
         def _filter_by_value():
             if byvalue is None:
                 return True
@@ -505,6 +416,7 @@ class Config(object):
             except:  # a property restricts the access of the value
                 pass
             return False
+
         def _filter_by_type():
             if bytype is None:
                 return True
@@ -512,12 +424,27 @@ class Config(object):
                 return True
             return False
 
+        def _filter_by_attrs():
+            if byattrs is None:
+                return True
+            for key, value in byattrs.items():
+                if not hasattr(option, key):
+                    return False
+                else:
+                    if getattr(option, key) != value:
+                        return False
+                    else:
+                        continue
+            return True
+
         find_results = []
-        paths = self.getpaths(allpaths=True)
-        for path in paths:
-            try:
-                option = self.unwrap_from_path(path)
-            except PropertiesOptionError, err:
+        opts, paths = self.cfgimpl_get_description()._cache_paths
+        for index in range(0, len(paths)):
+            path = paths[index]
+            option = opts[index]
+            if isinstance(option, OptionDescription):
+                continue
+            if _subpath is not None and not path.startswith(_subpath + '.'):
                 continue
             if not _filter_by_name():
                 continue
@@ -527,17 +454,27 @@ class Config(object):
                 continue
             if not _filter_by_attrs():
                 continue
+            #remove option with propertyerror, ...
+            try:
+                value = getattr(self, path)
+            except:  # a property restricts the access of the value
+                continue
             if first:
-                return option
+                if getvalue:
+                    return value
+                else:
+                    return option
             else:
-                find_results.append(option)
-
+                if getvalue:
+                    find_results.append(value)
+                else:
+                    find_results.append(option)
         if find_results == []:
             raise NotFoundError("no option found in config with these criteria")
         else:
             return find_results
 
-    def find(self, bytype=None, byname=None, byvalue=None, byattrs=None):
+    def find(self, bytype=None, byname=None, byvalue=None, byattrs=None, _subpath=None):
         """
             finds a list of options recursively in the config
 
@@ -547,9 +484,9 @@ class Config(object):
             :param byattrs: dict of option attributes (default, callback...)
             :returns: list of matching Option objects
         """
-        return self._find(bytype, byname, byvalue, byattrs, first=False)
+        return self._find(bytype, byname, byvalue, byattrs, first=False, _subpath=_subpath)
 
-    def find_first(self, bytype=None, byname=None, byvalue=None, byattrs=None):
+    def find_first(self, bytype=None, byname=None, byvalue=None, byattrs=None, _subpath=None):
         """
             finds an option recursively in the config
 
@@ -559,7 +496,7 @@ class Config(object):
             :param byattrs: dict of option attributes (default, callback...)
             :returns: list of matching Option objects
         """
-        return self._find(bytype, byname, byvalue, byattrs, first=True)
+        return self._find(bytype, byname, byvalue, byattrs, first=True, _subpath=_subpath)
 
 
 def make_dict(config, flatten=False):
@@ -586,15 +523,11 @@ def mandatory_warnings(config):
     where no value has been set
 
     :returns: generator of mandatory Option's path
-    FIXME : CAREFULL : not multi-user
     """
-    mandatory = config._cfgimpl_context._cfgimpl_settings.mandatory
-    config._cfgimpl_context._cfgimpl_settings.mandatory = True
-    for path in config._cfgimpl_descr.getpaths(include_groups=True):
+    for path in config.cfgimpl_get_description().getpaths(include_groups=True):
         try:
-            value = config._getattr(path, permissive=True)
+            config._getattr(path, force_properties=('mandatory',))
         except MandatoryError:
             yield path
         except PropertiesOptionError:
             pass
-    config._cfgimpl_context._cfgimpl_settings.mandatory = mandatory
index 9ed8aed..3710d6d 100644 (file)
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
 import re
+from copy import copy
 from types import FunctionType
-from tiramisu.basetype import BaseType
-from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError,
-    RequiresError, RequirementRecursionError, MandatoryError,
-    PropertiesOptionError)
+from tiramisu.error import (ConfigError, NotFoundError, ConflictConfigError,
+                            RequiresError, RequirementRecursionError,
+                            PropertiesOptionError)
 from tiramisu.autolib import carry_out_calculation
-from tiramisu.setting import groups, owners
+from tiramisu.setting import groups, multitypes
 
-requires_actions = [('hide', 'show'), ('enable', 'disable'),
-                    ('freeze', 'unfreeze')]
-
-available_actions = []
-reverse_actions = {}
-for act1, act2 in requires_actions:
-    available_actions.extend([act1, act2])
-    reverse_actions[act1] = act2
-    reverse_actions[act2] = act1
-# ____________________________________________________________
 name_regexp = re.compile(r'^\d+')
 
+
 def valid_name(name):
     try:
         name = str(name)
@@ -53,7 +44,9 @@ def valid_name(name):
 #____________________________________________________________
 #
 
-class BaseInformation:
+
+class BaseInformation(object):
+    __slots__ = ('informations')
 
     def set_information(self, key, value):
         """updates the information's attribute
@@ -64,29 +57,33 @@ class BaseInformation:
         """
         self.informations[key] = value
 
-    def get_information(self, key):
+    def get_information(self, key, default=None):
         """retrieves one information's item
 
         :param key: the item string (ex: "help")
         """
         if key in self.informations:
             return self.informations[key]
+        elif default is not None:
+            return default
         else:
             raise ValueError("Information's item not found: {0}".format(key))
 
-class Option(BaseType, BaseInformation):
+
+class Option(BaseInformation):
     """
     Abstract base class for configuration option's.
 
     Reminder: an Option object is **not** a container for the value
     """
-    #freeze means: cannot modify the value of an Option once set
-    _frozen = False
-    #if an Option has been frozen, shall return the default value
-    _force_default_on_freeze = False
+    __slots__ = ('_name', '_requires', 'multi', '_validator', 'default_multi',
+                 'default', '_properties', 'callback', 'multitype',
+                 'master_slaves')
+
     def __init__(self, name, doc, default=None, default_multi=None,
-                 requires=None, mandatory=False, multi=False, callback=None,
-                 callback_params=None, validator=None, validator_args={}):
+                 requires=None, multi=False, callback=None,
+                 callback_params=None, validator=None, validator_args=None,
+                 properties=None):
         """
         :param name: the option's name
         :param doc: the option's description
@@ -107,51 +104,58 @@ class Option(BaseType, BaseInformation):
         if not valid_name(name):
             raise NameError("invalid name: {0} for option".format(name))
         self._name = name
-        self.doc = doc
+        self.informations = {}
+        self.set_information('doc', doc)
+        validate_requires_arg(requires, self._name)
         self._requires = requires
-        self._mandatory = mandatory
         self.multi = multi
-        self._validator = None
-        self._validator_args = None
+        #self._validator_args = None
         if validator is not None:
             if type(validator) != FunctionType:
                 raise TypeError("validator must be a function")
-            self._validator = validator
-            if validator_args is not None:
-                self._validator_args = validator_args
+            self._validator = (validator, validator_args)
+        else:
+            self._validator = None
         if not self.multi and default_multi is not None:
             raise ConfigError("a default_multi is set whereas multi is False"
-                  " in option: {0}".format(name))
+                              " in option: {0}".format(name))
         if default_multi is not None and not self._validate(default_multi):
             raise ConfigError("invalid default_multi value {0} "
-                "for option {1}".format(str(default_multi), name))
-        self.default_multi = default_multi
-        #if self.multi and default_multi is None:
-        #    _cfgimpl_warnings[name] = DefaultMultiWarning
+                              "for option {1}".format(str(default_multi), name))
         if callback is not None and (default is not None or default_multi is not None):
             raise ConfigError("defaut values not allowed if option: {0} "
-                "is calculated".format(name))
-        self.callback = callback
-        if self.callback is None and callback_params is not None:
-            raise ConfigError("params defined for a callback function but"
-            " no callback defined yet for option {0}".format(name))
-        self.callback_params = callback_params
-        if self.multi == True:
-            if default == None:
+                              "is calculated".format(name))
+        if callback is None and callback_params is not None:
+            raise ConfigError("params defined for a callback function but "
+                              "no callback defined yet for option {0}".format(name))
+        if callback is not None:
+            self.callback = (callback, callback_params)
+        else:
+            self.callback = None
+        if self.multi:
+            if default is None:
                 default = []
             if not isinstance(default, list):
                 raise ConfigError("invalid default value {0} "
-                "for option {1} : not list type".format(str(default), name))
+                                  "for option {1} : not list type"
+                                  "".format(str(default), name))
             if not self.validate(default, False):
                 raise ConfigError("invalid default value {0} "
-                "for option {1}".format(str(default), name))
+                                  "for option {1}"
+                                  "".format(str(default), name))
+            self.multitype = multitypes.default
+            self.default_multi = default_multi
         else:
-            if default != None and not self.validate(default, False):
+            if default is not None and not self.validate(default, False):
                 raise ConfigError("invalid default value {0} "
-                                         "for option {1}".format(str(default), name))
+                                  "for option {1}".format(str(default), name))
         self.default = default
-        self.properties = [] # 'hidden', 'disabled'...
-        self.informations = {}
+        if properties is None:
+            properties = ()
+        if not isinstance(properties, tuple):
+            raise ConfigError('invalid properties type {0} for {1},'
+                              ' must be a tuple'.format(type(properties), self._name))
+        self._properties = properties  # 'hidden', 'disabled'...
 
     def validate(self, value, validate=True):
         """
@@ -159,25 +163,25 @@ class Option(BaseType, BaseInformation):
         :param validate: if true enables ``self._validator`` validation
         """
         # generic calculation
-        if self.multi == False:
+        if not self.multi:
             # None allows the reset of the value
-            if value != None:
+            if value is not None:
                 # customizing the validator
                 if validate and self._validator is not None and \
-                        not self._validator(value, **self._validator_args):
+                        not self._validator[0](value, **self._validator[1]):
                     return False
                 return self._validate(value)
         else:
             if not isinstance(value, list):
                 raise ConfigError("invalid value {0} "
-                        "for option {1} which must be a list".format(value,
-                        self._name))
+                                  "for option {1} which must be a list"
+                                  "".format(value, self._name))
             for val in value:
                 # None allows the reset of the value
-                if val != None:
+                if val is not None:
                     # customizing the validator
                     if validate and self._validator is not None and \
-                            not self._validator(val, **self._validator_args):
+                            not self._validator[0](val, **self._validator[1]):
                         return False
                     if not self._validate(val):
                         return False
@@ -185,7 +189,7 @@ class Option(BaseType, BaseInformation):
 
     def getdefault(self, default_multi=False):
         "accessing the default value"
-        if default_multi == False or not self.is_multi():
+        if not default_multi or not self.is_multi():
             return self.default
         else:
             return self.getdefault_multi()
@@ -196,148 +200,88 @@ class Option(BaseType, BaseInformation):
 
     def is_empty_by_default(self):
         "no default value has been set yet"
-        if ((not self.is_multi() and self.default == None) or
+        if ((not self.is_multi() and self.default is None) or
                 (self.is_multi() and (self.default == [] or None in self.default))):
             return True
         return False
 
-    def force_default(self):
-        "if an Option has been frozen, shall return the default value"
-        self._force_default_on_freeze = True
-
-    def hascallback_and_isfrozen():
-        return self._frozen and self.has_callback()
-
-    def is_forced_on_freeze(self):
-        "if an Option has been frozen, shall return the default value"
-        return self._frozen and self._force_default_on_freeze
-
     def getdoc(self):
         "accesses the Option's doc"
-        return self.doc
-
-    def getcallback(self):
-        "a callback is only a link, the name of an external hook"
-        return self.callback
+        return self.get_information('doc')
 
     def has_callback(self):
         "to know if a callback has been defined or not"
-        if self.callback == None:
+        if self.callback is None:
             return False
         else:
             return True
 
     def getcallback_value(self, config):
-        return carry_out_calculation(self._name,
-                option=self, config=config)
-
-    def getcallback_params(self):
-        "if a callback has been defined, returns his arity"
-        return self.callback_params
-
-    def setowner(self, config, owner):
-        """
-        :param config: *must* be only the **parent** config
-                       (not the toplevel config)
-        :param owner: is a **real** owner, that is an object
-                      that lives in setting.owners
-        """
-        name = self._name
-        if not isinstance(owner, owners.Owner):
-            raise ConfigError("invalid type owner for option: {0}".format(
-                    str(name)))
-        config._cfgimpl_context._cfgimpl_values.owners[self] = owner
-
-    def getowner(self, config):
-        "config *must* be only the **parent** config (not the toplevel config)"
-        return config._cfgimpl_context._cfgimpl_values.getowner(self)
-
-    def get_previous_value(self, config):
-        return config._cfgimpl_context._cfgimpl_values.get_previous_value(self)
+        callback, callback_params = self.callback
+        if callback_params is None:
+            callback_params = {}
+        return carry_out_calculation(self._name, config=config,
+                                     callback=callback,
+                                     callback_params=callback_params)
 
     def reset(self, config):
         """resets the default value and owner
         """
         config._cfgimpl_context._cfgimpl_values.reset(self)
 
-    def is_default_owner(self, config):
-        """
-        :param config: *must* be only the **parent** config
-                       (not the toplevel config)
-        :return: boolean
-        """
-        return self.getowner(config) == owners.default
-
     def setoption(self, config, value):
         """changes the option's value with the value_owner's who
         :param config: the parent config is necessary here to store the value
         """
         name = self._name
-        rootconfig = config._cfgimpl_get_toplevel()
-        if not self.validate(value,
-                        config._cfgimpl_context._cfgimpl_settings.validator):
+        setting = config.cfgimpl_get_settings()
+        if not self.validate(value, setting.has_property('validator')):
             raise ConfigError('invalid value %s for option %s' % (value, name))
-        if self.is_mandatory():
-            # value shall not be '' for a mandatory option
-            # so '' is considered as being None
-            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._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 self not in config._cfgimpl_descr._children:
+        if self not in config._cfgimpl_descr._children[1]:
             raise AttributeError('unknown option %s' % (name))
 
-        if config._cfgimpl_context._cfgimpl_settings.is_frozen_for_everything():
+        if setting.has_property('everything_frozen'):
             raise TypeError("cannot set a value to the option {} if the whole "
-            "config has been frozen".format(name))
+                            "config has been frozen".format(name))
 
-        if config._cfgimpl_context._cfgimpl_settings.is_frozen() \
-                                                        and self.is_frozen():
+        if setting.has_property('frozen') and setting.has_property('frozen',
+                                                                   self):
             raise TypeError('cannot change the value to %s for '
-               'option %s this option is frozen' % (str(value), name))
+                            'option %s this option is frozen' % (str(value), name))
         apply_requires(self, config)
-        config._cfgimpl_context._cfgimpl_values[self] = value
+        config.cfgimpl_get_values()[self] = value
 
     def getkey(self, value):
         return value
-    # ____________________________________________________________
-    "freeze utility"
-    def freeze(self):
-        self._frozen = True
-        return True
-    def unfreeze(self):
-        self._frozen = False
-    def is_frozen(self):
-        return self._frozen
-    # ____________________________________________________________
+
     def is_multi(self):
         return self.multi
-    def is_mandatory(self):
-        return self._mandatory
+
 
 class ChoiceOption(Option):
+    __slots__ = ('values', 'open_values', 'opt_type')
     opt_type = 'string'
 
     def __init__(self, name, doc, values, default=None, default_multi=None,
-                 requires=None, mandatory=False, multi=False, callback=None,
+                 requires=None, multi=False, callback=None,
                  callback_params=None, open_values=False, validator=None,
-                 validator_args={}):
+                 validator_args=None, properties=()):
+        if not isinstance(values, tuple):
+            raise ConfigError('values must be a tuple for {0}'.format(name))
         self.values = values
-        if open_values not in [True, False]:
+        if open_values not in (True, False):
             raise ConfigError('Open_values must be a boolean for '
                               '{0}'.format(name))
         self.open_values = open_values
         super(ChoiceOption, self).__init__(name, doc, default=default,
-                        default_multi=default_multi, callback=callback,
-                        callback_params=callback_params, requires=requires,
-                        multi=multi, mandatory=mandatory, validator=validator,
-                        validator_args=validator_args)
+                                           default_multi=default_multi,
+                                           callback=callback,
+                                           callback_params=callback_params,
+                                           requires=requires,
+                                           multi=multi,
+                                           validator=validator,
+                                           validator_args=validator_args,
+                                           properties=properties)
 
     def _validate(self, value):
         if not self.open_values:
@@ -345,72 +289,91 @@ class ChoiceOption(Option):
         else:
             return True
 
+
 class BoolOption(Option):
+    __slots__ = ('opt_type')
     opt_type = 'bool'
 
     def _validate(self, value):
         return isinstance(value, bool)
 
+
 class IntOption(Option):
+    __slots__ = ('opt_type')
     opt_type = 'int'
 
     def _validate(self, value):
         return isinstance(value, int)
 
+
 class FloatOption(Option):
+    __slots__ = ('opt_type')
     opt_type = 'float'
 
     def _validate(self, value):
         return isinstance(value, float)
 
+
 class StrOption(Option):
+    __slots__ = ('opt_type')
     opt_type = 'string'
 
     def _validate(self, value):
         return isinstance(value, str)
 
+
 class UnicodeOption(Option):
+    __slots__ = ('opt_type')
     opt_type = 'unicode'
 
     def _validate(self, value):
         return isinstance(value, unicode)
 
+
 class SymLinkOption(object):
+    __slots__ = ('_name', 'opt')
     opt_type = 'symlink'
 
     def __init__(self, name, path, opt):
         self._name = name
-        self.path = path
         self.opt = opt
 
     def setoption(self, config, value):
-        setattr(config._cfgimpl_get_toplevel(), self.path, value)
+        context = config.cfgimpl_get_context()
+        path = context.cfgimpl_get_description().get_path_by_opt(self.opt)
+        setattr(context, path, value)
 
     def __getattr__(self, name):
-        if name in ('_name', 'path', 'opt', 'setoption'):
-            return self.__dict__[name]
+        if name in ('_name', 'opt', 'setoption'):
+            return object.__gettattr__(self, name)
         else:
             return getattr(self.opt, name)
 
+
 class IPOption(Option):
+    __slots__ = ('opt_type')
     opt_type = 'ip'
 
     def _validate(self, value):
         # by now the validation is nothing but a string, use IPy instead
         return isinstance(value, str)
 
+
 class NetmaskOption(Option):
+    __slots__ = ('opt_type')
     opt_type = 'netmask'
 
     def _validate(self, value):
         # by now the validation is nothing but a string, use IPy instead
         return isinstance(value, str)
 
-class OptionDescription(BaseType, BaseInformation):
+
+class OptionDescription(BaseInformation):
     """Config's schema (organisation, group) and container of Options"""
-    # the group_type is useful for filtering OptionDescriptions in a config
-    group_type = groups.default
-    def __init__(self, name, doc, children, requires=None):
+    __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
+                 '_properties', '_children')
+
+    def __init__(self, name, doc, children, requires=None, properties=()):
         """
         :param children: is a list of option descriptions (including
         ``OptionDescription`` instances for nested namespaces).
@@ -418,49 +381,55 @@ class OptionDescription(BaseType, BaseInformation):
         if not valid_name(name):
             raise NameError("invalid name: {0} for option descr".format(name))
         self._name = name
-        self.doc = doc
-        self._children = children
-        self._requires = requires
-        self._build()
-        self.properties = [] # 'hidden', 'disabled'...
         self.informations = {}
-        self._cache_paths = {}
+        self.set_information('doc', doc)
+        child_names = [child._name for child in children]
+        #better performance like this
+        valid_child = copy(child_names)
+        valid_child.sort()
+        old = None
+        for child in valid_child:
+            if child == old:
+                raise ConflictConfigError('duplicate option name: '
+                                          '{0}'.format(child))
+            old = child
+        self._children = (tuple(child_names), tuple(children))
+        validate_requires_arg(requires, self._name)
+        self._requires = requires
+        self._cache_paths = None
+        if not isinstance(properties, tuple):
+            raise ConfigError('invalid properties type {0} for {1},'
+                              ' must be a tuple'.format(type(properties), self._name))
+        self._properties = properties  # 'hidden', 'disabled'...
+        # the group_type is useful for filtering OptionDescriptions in a config
+        self._group_type = groups.default
 
     def getdoc(self):
-        return self.doc
-
-    def _build(self):
-        for child in self._children:
-            setattr(self, child._name, child)
-
-    def add_child(self, child):
-        "dynamically adds a configuration option"
-        #Nothing is static. Even the Mona Lisa is falling apart.
-        for ch in self._children:
-            if isinstance(ch, Option):
-                if child._name == ch._name:
-                    raise ConflictConfigError("existing option : {0}".format(
-                                                                   child._name))
-        self._children.append(child)
-        setattr(self, child._name, child)
-
-    def update_child(self, child):
-        "modification of an existing option"
-        # XXX : corresponds to the `redefine`, is it usefull
-        pass
+        return self.get_information('doc')
+
+    def __getattr__(self, name):
+        if name in self._children[0]:
+            return self._children[1][self._children[0].index(name)]
+        else:
+            try:
+                object.__getattr__(self, name)
+            except AttributeError:
+                raise AttributeError('unknown Option {} in OptionDescription {}'
+                                     ''.format(name, self._name))
 
     def getkey(self, config):
         return tuple([child.getkey(getattr(config, child._name))
-                      for child in self._children])
+                      for child in self._children[1]])
 
     def getpaths(self, include_groups=False, currpath=None):
         """returns a list of all paths in self, recursively
            currpath should not be provided (helps with recursion)
         """
+        #FIXME : cache
         if currpath is None:
             currpath = []
         paths = []
-        for option in self._children:
+        for option in self._children[1]:
             attr = option._name
             if attr.startswith('_cfgimpl'):
                 continue
@@ -469,29 +438,57 @@ class OptionDescription(BaseType, BaseInformation):
                     paths.append('.'.join(currpath + [attr]))
                 currpath.append(attr)
                 paths += option.getpaths(include_groups=include_groups,
-                                        currpath=currpath)
+                                         currpath=currpath)
                 currpath.pop()
             else:
                 paths.append('.'.join(currpath + [attr]))
         return paths
 
-    def build_cache(self, cache=None, currpath=None):
-        if currpath is None and self._cache_paths != {}:
+    def build_cache(self, cache_path=None, cache_option=None, currpath=None):
+        if currpath is None and self._cache_paths is not None:
             return
         if currpath is None:
+            save = True
             currpath = []
-        if cache is None:
-            cache = self._cache_paths
-        for option in self._children:
+        else:
+            save = False
+        if cache_path is None:
+            cache_path = []
+            cache_option = []
+        for option in self._children[1]:
             attr = option._name
             if attr.startswith('_cfgimpl'):
                 continue
+            cache_option.append(option)
+            cache_path.append(str('.'.join(currpath + [attr])))
             if isinstance(option, OptionDescription):
                 currpath.append(attr)
-                option.build_cache(cache, currpath)
+                option.build_cache(cache_path, cache_option, currpath)
                 currpath.pop()
-            else:
-                cache[option] = str('.'.join(currpath + [attr]))
+        if save:
+            #valid no duplicated option
+            valid_child = copy(cache_option)
+            valid_child.sort()
+            old = None
+            for child in valid_child:
+                if child == old:
+                    raise ConflictConfigError('duplicate option: '
+                                              '{0}'.format(child))
+                old = child
+            self._cache_paths = (tuple(cache_option), tuple(cache_path))
+
+    def get_opt_by_path(self, path):
+        try:
+            return self._cache_paths[0][self._cache_paths[1].index(path)]
+        except ValueError:
+            raise NotFoundError('no option for path {}'.format(path))
+
+    def get_path_by_opt(self, opt):
+        try:
+            return self._cache_paths[1][self._cache_paths[0].index(opt)]
+        except ValueError:
+            raise NotFoundError('no option {} found'.format(opt))
+
     # ____________________________________________________________
     def set_group_type(self, group_type):
         """sets a given group object to an OptionDescription
@@ -499,115 +496,123 @@ class OptionDescription(BaseType, BaseInformation):
         :param group_type: an instance of `GroupType` or `MasterGroupType`
                               that lives in `setting.groups`
         """
+        if self._group_type != groups.default:
+            ConfigError('cannot change group_type if already set '
+                        '(old {}, new {})'.format(self._group_type, group_type))
         if isinstance(group_type, groups.GroupType):
-            self.group_type = group_type
+            self._group_type = group_type
             if isinstance(group_type, groups.MasterGroupType):
+                #if master (same name has group) is set
                 identical_master_child_name = False
-                for child in self._children:
+                #for collect all slaves
+                slaves = []
+                master = None
+                for child in self._children[1]:
                     if isinstance(child, OptionDescription):
                         raise ConfigError("master group {} shall not have "
-                            "a subgroup".format(self._name))
+                                          "a subgroup".format(self._name))
                     if not child.multi:
                         raise ConfigError("not allowed option {0} in group {1}"
-                            ": this option is not a multi".format(child._name,
-                            self._name))
+                                          ": this option is not a multi"
+                                          "".format(child._name, self._name))
                     if child._name == self._name:
                         identical_master_child_name = True
+                        child.multitype = multitypes.master
+                        master = child
+                    else:
+                        slaves.append(child)
+                if master is None:
+                    raise ConfigError('master group with wrong master name for {}'
+                                      ''.format(self._name))
+                master.master_slaves = tuple(slaves)
+                for child in self._children[1]:
+                    if child != master:
+                        child.master_slaves = master
+                        child.multitype = multitypes.slave
                 if not identical_master_child_name:
                     raise ConfigError("the master group: {} has not any "
-                     "master child".format(self._name))
+                                      "master child".format(self._name))
         else:
             raise ConfigError('not allowed group_type : {0}'.format(group_type))
 
     def get_group_type(self):
-        return self.group_type
-    # ____________________________________________________________
-    "actions API"
-    def hide(self):
-        super(OptionDescription, self).hide()
-        for child in self._children:
-            if isinstance(child, OptionDescription):
-                child.hide()
-    def show(self):
-        super(OptionDescription, self).show()
-        for child in self._children:
-            if isinstance(child, OptionDescription):
-                child.show()
-
-    def disable(self):
-        super(OptionDescription, self).disable()
-        for child in self._children:
-            if isinstance(child, OptionDescription):
-                child.disable()
-    def enable(self):
-        super(OptionDescription, self).enable()
-        for child in self._children:
-            if isinstance(child, OptionDescription):
-                child.enable()
-# ____________________________________________________________
+        return self._group_type
+
 
 def validate_requires_arg(requires, name):
-    "malformed requirements"
-    config_action = []
-    for req in requires:
-        if not type(req) == tuple and len(req) != 3:
-            raise RequiresError("malformed requirements for option:"
-                                           " {0}".format(name))
-        action = req[2]
-        if action not in available_actions:
-            raise RequiresError("malformed requirements for option: {0}"
-                                " unknown action: {1}".format(name, action))
-        if reverse_actions[action] in config_action:
-            raise RequiresError("inconsistency in action types for option: {0}"
-                                " action: {1} in contradiction with {2}\n"
-                                " ({3})".format(name, action,
-                                    reverse_actions[action], requires))
-        config_action.append(action)
-
-def build_actions(requires):
-    "action are hide, show, enable, disable..."
-    trigger_actions = {}
-    for require in requires:
-        action = require[2]
-        trigger_actions.setdefault(action, []).append(require)
-    return trigger_actions
-
-def apply_requires(opt, config, permissive=False):
+    "check malformed requirements"
+    if requires is not None:
+        config_action = {}
+        for req in requires:
+            if not type(req) == tuple:
+                raise RequiresError("malformed requirements type for option:"
+                                    " {0}, must be a tuple".format(name))
+            if len(req) == 3:
+                action = req[2]
+                inverse = False
+            elif len(req) == 4:
+                action = req[2]
+                inverse = req[3]
+            else:
+                raise RequiresError("malformed requirements for option: {0}"
+                                    " invalid len".format(name))
+            if action in config_action:
+                if inverse != config_action[action]:
+                    raise RequiresError("inconsistency in action types for option: {0}"
+                                        " action: {1}".format(name, action))
+            else:
+                config_action[action] = inverse
+
+
+def apply_requires(opt, config):
     "carries out the jit (just in time requirements between options"
+    def build_actions(requires):
+        "action are hide, show, enable, disable..."
+        trigger_actions = {}
+        for require in requires:
+            action = require[2]
+            trigger_actions.setdefault(action, []).append(require)
+        return trigger_actions
+    #for symlink
     if hasattr(opt, '_requires') and opt._requires is not None:
-        rootconfig = config._cfgimpl_get_toplevel()
-        validate_requires_arg(opt._requires, opt._name)
         # filters the callbacks
+        setting = config.cfgimpl_get_settings()
         trigger_actions = build_actions(opt._requires)
+        if isinstance(opt, OptionDescription):
+            optpath = config._cfgimpl_get_path() + '.' + opt._name
+        else:
+            optpath = config.cfgimpl_get_context().cfgimpl_get_description().get_path_by_opt(opt)
         for requires in trigger_actions.values():
             matches = False
             for require in requires:
-                name, expected, action = require
-                path = config._cfgimpl_get_path() + '.' + opt._name
-                if name.startswith(path):
+                if len(require) == 3:
+                    path, expected, action = require
+                    inverse = False
+                elif len(require) == 4:
+                    path, expected, action, inverse = require
+                if path.startswith(optpath):
                     raise RequirementRecursionError("malformed requirements "
-                          "imbrication detected for option: '{0}' "
-                          "with requirement on: '{1}'".format(path, name))
-                homeconfig, shortname = rootconfig.cfgimpl_get_home_by_path(name)
+                                                    "imbrication detected for option: '{0}' "
+                                                    "with requirement on: '{1}'".format(optpath, path))
                 try:
-                    value = homeconfig._getattr(shortname, permissive=True)
+                    value = config.cfgimpl_get_context()._getattr(path, force_permissive=True)
                 except PropertiesOptionError, err:
                     properties = err.proptype
-                    if permissive:
-                        for perm in \
-                                config._cfgimpl_context._cfgimpl_settings.permissive:
-                            if perm in properties:
-                                properties.remove(perm)
-                    if properties != []:
-                        raise NotFoundError("option '{0}' has requirement's property error: "
-                                     "{1} {2}".format(opt._name, name, properties))
+                    raise NotFoundError("option '{0}' has requirement's property error: "
+                                        "{1} {2}".format(opt._name, path, properties))
                 except Exception, err:
                     raise NotFoundError("required option not found: "
-                                                             "{0}".format(name))
+                                        "{0}".format(path))
                 if value == expected:
-                    getattr(opt, action)() #.hide() or show() or...
-                    # FIXME generic programming opt.property_launch(action, False)
+                    if inverse:
+                        setting.del_property(action, opt)
+                    else:
+                        setting.add_property(action, opt)
                     matches = True
-            # no callback has been triggered, then just reverse the action
+                    #FIXME optimisation : fait un double break non ? voire un return
+            # no requirement has been triggered, then just reverse the action
             if not matches:
-                getattr(opt, reverse_actions[action])()
+                if inverse:
+                    setting.add_property(action, opt)
+                else:
+                    setting.del_property(action, opt)
index 7477fd9..1c1e8db 100644 (file)
 # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
+
+
 class _const:
     """convenient class that emulates a module
     and builds constants (that is, unique names)"""
-    class ConstError(TypeError): pass
+    class ConstError(TypeError):
+        pass
 
     def __setattr__(self, name, value):
-        if self.__dict__.has_key(name):
-            raise self.ConstError, "Can't rebind group (%s)"%name
+        if name in self.__dict__:
+            raise self.ConstError, "Can't rebind group ({})".format(name)
         self.__dict__[name] = value
 
     def __delattr__(self, name):
-        if self.__dict__.has_key(name):
-            raise self.ConstError, "Can't unbind group (%s)"%name
-        raise NameError, name
+        if name in self.__dict__:
+            raise self.ConstError, "Can't unbind group ({})".format(name)
+        raise NameError(name)
+
+
 # ____________________________________________________________
 class GroupModule(_const):
     "emulates a module to manage unique group (OptionDescription) names"
@@ -42,6 +47,7 @@ class GroupModule(_const):
         *normal* means : groups that are not master
         """
         pass
+
     class DefaultGroupType(GroupType):
         """groups that are default (typically 'default')"""
         pass
@@ -54,6 +60,7 @@ class GroupModule(_const):
 # setting.groups (emulates a module)
 groups = GroupModule()
 
+
 def populate_groups():
     "populates the available groups in the appropriate namespaces"
     groups.master = groups.MasterGroupType('master')
@@ -62,6 +69,8 @@ def populate_groups():
 
 # names are in the module now
 populate_groups()
+
+
 # ____________________________________________________________
 class OwnerModule(_const):
     """emulates a module to manage unique owner names.
@@ -72,12 +81,14 @@ class OwnerModule(_const):
         """allowed owner names
         """
         pass
+
     class DefaultOwner(Owner):
         """groups that are default (typically 'default')"""
         pass
 # setting.owners (emulates a module)
 owners = OwnerModule()
 
+
 def populate_owners():
     """populates the available owners in the appropriate namespaces
 
@@ -85,7 +96,8 @@ def populate_owners():
     - 'default' is the config owner after init time
     """
     setattr(owners, 'default', owners.DefaultOwner('default'))
-    setattr(owners,'user', owners.Owner('user'))
+    setattr(owners, 'user', owners.Owner('user'))
+
     def add_owner(name):
         """
         :param name: the name of the new owner
@@ -96,18 +108,23 @@ def populate_owners():
 # names are in the module now
 populate_owners()
 
+
 class MultiTypeModule(_const):
     class MultiType(str):
         pass
+
     class DefaultMultiType(MultiType):
         pass
+
     class MasterMultiType(MultiType):
         pass
+
     class SlaveMultiType(MultiType):
         pass
 
 multitypes = MultiTypeModule()
 
+
 def populate_multitypes():
     setattr(multitypes, 'default', multitypes.DefaultMultiType('default'))
     setattr(multitypes, 'master', multitypes.MasterMultiType('master'))
@@ -115,123 +132,114 @@ def populate_multitypes():
 
 populate_multitypes()
 
+
 #____________________________________________________________
-class Setting():
+class Setting(object):
     "``Config()``'s configuration options"
-    # properties attribute: the name of a property enables this property
-    properties = ['hidden', 'disabled']
-    # overrides the validations in the acces of the option values
-    permissive = []
-    # a mandatory option must have a value that is not None
-    mandatory = True
-    frozen = True
-    # enables validation function for options if set
-    validator = False
-    # generic owner
-    owner = owners.user
-    # in order to freeze everything, not **only** the frozen options
-    everything_frozen = False
-    # enables at build time to raise an exception if the option's name
-    # has the name of a config's method
-    valid_opt_names = True
+    __slots__ = ('properties', 'permissives', 'owner')
+
+    def __init__(self):
+        # properties attribute: the name of a property enables this property
+        # key is None for global properties
+        self.properties = {None: []}  # ['hidden', 'disabled', 'mandatory', 'frozen', 'validator']}
+        # permissive properties
+        self.permissives = {}
+        # generic owner
+        self.owner = owners.user
+
     #____________________________________________________________
     # properties methods
-    def has_properties(self):
+    def has_properties(self, opt=None):
         "has properties means the Config's properties attribute is not empty"
-        return bool(len(self.properties))
-
+        return bool(len(self.get_properties(opt)))
 
-    def get_properties(self):
-        return self.properties
+    def get_properties(self, opt=None):
+        if opt is None:
+            default = []
+        else:
+            default = list(opt._properties)
+        return self.properties.get(opt, default)
 
-    def has_property(self, propname):
+    def has_property(self, propname, opt=None):
         """has property propname in the Config's properties attribute
         :param property: string wich is the name of the property"""
-        return propname in self.properties
+        return propname in self.get_properties(opt)
 
     def enable_property(self, propname):
         "puts property propname in the Config's properties attribute"
-        if propname not in self.properties:
-            self.properties.append(propname)
+        props = self.get_properties()
+        if propname not in props:
+            props.append(propname)
+        self.set_properties(props)
 
     def disable_property(self, propname):
         "deletes property propname in the Config's properties attribute"
-        if self.has_property(propname):
-            self.properties.remove(propname)
+        props = self.get_properties()
+        if propname in props:
+            props.remove(propname)
+        self.set_properties(props)
+
+    def set_properties(self, properties, opt=None):
+        """save properties for specified opt
+        (never save properties if same has option properties)
+        """
+        if opt is None:
+            self.properties[opt] = properties
+        else:
+            if opt._properties == properties:
+                if opt in self.properties:
+                    del(self.properties[opt])
+            else:
+                self.properties[opt] = properties
+
+    def add_property(self, propname, opt):
+        properties = self.get_properties(opt)
+        if not propname in properties:
+            properties.append(propname)
+            self.set_properties(properties, opt)
+
+    def del_property(self, propname, opt):
+        properties = self.get_properties(opt)
+        if propname in properties:
+            properties.remove(propname)
+            self.set_properties(properties, opt)
+
     #____________________________________________________________
-    def get_permissive(self):
-        return self.permissive
+    def get_permissive(self, config=None):
+        return self.permissives.get(config, [])
 
-    def set_permissive(self, permissive):
+    def set_permissive(self, permissive, config=None):
         if not isinstance(permissive, list):
             raise TypeError('permissive must be a list')
-        self.permissive = permissive
+        self.permissives[config] = permissive
+
     #____________________________________________________________
-    # complete freeze methods
-    def freeze_everything(self):
-        """everything is frozen, not only the option that are tagged "frozen"
-        """
-        self.everything_frozen = True
+    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
 
-    def un_freeze_everything(self):
-        """everything is frozen, not only the option that are tagged "frozen"
-        """
-        self.everything_frozen = False
+    def getowner(self):
+        return self.owner
 
-    def is_frozen_for_everything(self):
-        """frozen for a whole config (not only the options
-        that have been set to frozen)"""
-        return self.everything_frozen
     #____________________________________________________________
     def read_only(self):
         "convenience method to freeze, hidde and disable"
-        self.freeze_everything()
-        self.freeze() # can be usefull...
+        self.enable_property('everything_frozen')
+        self.enable_property('frozen')  # can be usefull...
         self.disable_property('hidden')
         self.enable_property('disabled')
-        self.mandatory = True
-        self.validator = True
+        self.enable_property('mandatory')
+        self.enable_property('validator')
+        self.disable_property('permissive')
 
     def read_write(self):
         "convenience method to freeze, hidde and disable"
-        self.un_freeze_everything()
-        self.freeze()
+        self.disable_property('everything_frozen')
+        self.enable_property('frozen')  # can be usefull...
         self.enable_property('hidden')
         self.enable_property('disabled')
-        self.mandatory = False
-        self.validator = False
-
-    def non_mandatory(self):
-        """mandatory at the Config level means that the Config raises an error
-        if a mandatory option is found"""
-        self.mandatory = False
-
-    def mandatory(self):
-        """mandatory at the Config level means that the Config raises an error
-        if a mandatory option is found"""
-        self.mandatory = True
-
-    def is_mandatory(self):
-        "all mandatory Options shall have a value"
-        return self.mandatory
-
-    def freeze(self):
-        "cannot modify the frozen `Option`'s"
-        self.frozen = True
-
-    def unfreeze(self):
-        "can modify the Options that are frozen"
-        self.frozen = False
-
-    def is_frozen(self):
-        "freeze flag at Config level"
-        return self.frozen
-
-    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
-
-    def getowner(self):
-        return self.owner
+        self.disable_property('mandatory')
+        self.disable_property('validator')
+        self.enable_property('permissive')
index d1ddbc8..24b268d 100644 (file)
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
-# The original `Config` design model is unproudly borrowed from
-# 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, MultiTypeError, \
-    ConfigError
+    ConfigError  # , OptionValueError
 from tiramisu.setting import owners, multitypes
 
+
 class Values(object):
+    __slots__ = ('values', 'context')
+
     def __init__(self, context):
         """
         Initializes the values's dict.
 
-        :param context: the context is the home config's values and properties
+        :param context: the context is the home config's values
         """
-        self.owners = {}
         "Config's root indeed is in charge of the `Option()`'s values"
         self.values = {}
-        self.previous_values = {}
-        self.masters = {}
-        self.slaves = {}
         self.context = context
 
-    def _get_multitype(self, opt):
-        if opt in self.slaves:
-            # slave
-            multitype = multitypes.slave
-        elif opt in self.masters:
-            # master
-            multitype = multitypes.master
-        # FIXME : default value for a multi, we shall work on groups
-        else:
-            multitype = multitypes.default
-        return multitype
-
     def _get_value(self, opt):
         "special case for the multis: they never return None"
         if opt not in self.values:
             if opt.is_multi():
-                multitype = self._get_multitype(opt)
-                value = Multi(opt.getdefault(), self.context, opt, multitype)
+                value = Multi(opt.getdefault(), self.context, opt)
+                if opt.multitype == multitypes.slave:
+                    masterpath = self.context._cfgimpl_descr.get_path_by_opt(opt.master_slaves)
+                    mastervalue = getattr(self.context, masterpath)
+                    masterlen = len(mastervalue)
+                    if len(value) < masterlen:
+                        for num in range(0, masterlen - len(value)):
+                            value.append(None, force=True)
             else:
                 value = opt.getdefault()
 
-            if opt in self.slaves:
-                masterpath = self.context._cfgimpl_descr._cache_paths[self.slaves[opt]]
-                mastervalue = getattr(self.context, masterpath)
-                masterlen = len(mastervalue)
-                if len(value) < masterlen:
-                    for num in range(0, masterlen - len(value)):
-                        value.append(None, force=True)
             return value
-        return self.values[opt]
+        return self.values[opt][1]
 
     def reset(self, opt):
         if opt in self.values:
-            self.set_previous_value(opt)
             del(self.values[opt])
-        self.setowner(opt, owners.default)
-
-    def set_previous_value(self, opt):
-        if opt in self.values:
-            old_value = self.values[opt]
-        elif opt.is_multi():
-            old_value = []
-        else:
-            old_value = None
-        if type(old_value) == Multi:
-           self.previous_values[opt] = list(old_value)
-        else:
-           self.previous_values[opt] = old_value
-
-    def get_previous_value(self, opt):
-        if opt in self.previous_values:
-            prec_value = self.previous_values[opt]
-        elif opt.is_multi():
-            prec_value = []
-        else:
-            prec_value = None
-        return prec_value
 
     def _is_empty(self, opt, value=None):
         "convenience method to know if an option is empty"
-        if value is not None:
-            return False
-        if (not opt.is_multi() and value == None) or \
-            (opt.is_multi() and (value == [] or \
-                None in self._get_value(opt))):
+        #FIXME: buggy ?
+        #if value is not None:
+        #    return False
+        if (not opt.is_multi() and value is None) or \
+           (opt.is_multi() and (value == [] or
+                                None in self._get_value(opt))):
             return True
         return False
 
     def is_empty(self, opt):
+        #FIXME that not empty ... just no value!
         if opt not in self.values:
             return True
-        value = self.values[opt]
+        value = self.values[opt][1]
         if not opt.is_multi():
-            if self._get_value(opt) == None:
+            if self._get_value(opt) is None:
                 return True
             return False
         else:
             value = list(value)
             for val in value:
-                if val != None:
+                if val is not None:
                     return False
             return True
 
-    def _test_mandatory(self, opt, value=None):
-        # mandatory options
-        mandatory = self.context._cfgimpl_settings.mandatory
-        if opt.is_mandatory() and mandatory:
+    def _test_mandatory(self, opt, value, force_properties=None):
+        setting = self.context.cfgimpl_get_settings()
+        if force_properties is None:
+            set_mandatory = setting.has_property('mandatory')
+        else:
+            set_mandatory = ('mandatory' in force_properties or
+                             setting.has_property('mandatory'))
+        if setting.has_property('mandatory', opt) and set_mandatory:
             if self._is_empty(opt, value) and \
                     opt.is_empty_by_default():
                 raise MandatoryError("option: {0} is mandatory "
-                                      "and shall have a value".format(opt._name))
+                                     "and shall have a value".format(opt._name))
+            #empty value
+            if opt.is_multi():
+                for val in value:
+                    if val == '':
+                        raise MandatoryError("option: {0} is mandatory "
+                                             "and shall have not empty value".format(opt._name))
+            else:
+                if value == '':
+                    raise MandatoryError("option: {0} is mandatory "
+                                         "and shall have not empty value".format(opt._name))
 
     def fill_multi(self, opt, result):
         """fills a multi option with default and calculated values
         """
-        value = self._get_value(opt)
         if not isinstance(result, list):
             _result = [result]
         else:
             _result = result
-        multitype = self._get_multitype(opt)
-        return Multi(_result, self.context, opt, multitype)
+        #multitype = self._get_multitype(opt)
+        return Multi(_result, self.context, opt)  # , multitype)
 
     def __getitem__(self, opt):
+        return self._getitem(opt)
+
+    def _getitem(self, opt, force_properties=None):
         # options with callbacks
         value = self._get_value(opt)
         if opt.has_callback():
-            if (not opt.is_frozen() or \
-                    not opt.is_forced_on_freeze()) and \
-                    not opt.is_default_owner(self.context):
+            setting = self.context.cfgimpl_get_settings()
+            if (not setting.has_property('frozen', opt) or
+                (setting.has_property('frozen', opt) and
+                 not setting.has_property('force_default_on_freeze', opt)
+                 )) and not self.context.cfgimpl_get_values().is_default_owner(opt):
                 return self._get_value(opt)
             try:
-                result = opt.getcallback_value(
-                        self.context)
-            except NoValueReturned, err:
+                result = opt.getcallback_value(self.context)
+            except NoValueReturned:
                 pass
             else:
                 if opt.is_multi():
@@ -162,85 +141,94 @@ class Values(object):
                     # 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(opt._name))
+                                          'for option {0} : shall not be a list'
+                                          ''.format(opt._name))
                     value = result
-                if value != None and not opt.validate(value,
-                            self.context._cfgimpl_settings.validator):
+                if value is not None and \
+                        not opt.validate(value, setting.has_property('validator')):
                     raise ConfigError('invalid calculated value returned'
-                        ' for option {0}'.format(opt._name))
+                                      ' for option {0}'.format(opt._name))
         # frozen and force default
-        if not opt.has_callback() and opt.is_forced_on_freeze():
+        if not opt.has_callback() and self.context.cfgimpl_get_settings().has_property('force_default_on_freeze', opt):
             value = opt.getdefault()
             if opt.is_multi():
                 value = self.fill_multi(opt, value)
-        self._test_mandatory(opt, value)
+        self._test_mandatory(opt, value, force_properties)
         return value
 
     def __setitem__(self, opt, value):
-        if opt in self.masters:
-            masterlen = len(value)
-            for slave in self.masters[opt]:
-                value_slave = self._get_value(slave)
-                if len(value_slave) > masterlen:
-                    raise MultiTypeError("invalid len for the slave: {0}"
-                    " which has {1} as master".format(slave._name,
-                                                      opt._name))
-                elif len(value_slave) < masterlen:
-                    for num in range(0, masterlen - len(value_slave)):
-                        value_slave.append(None, force=True)
+        if opt.is_multi():
+            if opt.multitype == multitypes.master:
+                masterlen = len(value)
+                for slave in self.opt.master_slaves:
+                    value_slave = self._get_value(slave)
+                    if len(value_slave) > masterlen:
+                        raise MultiTypeError("invalid len for the slave: {0}"
+                                             " which has {1} as master".format(
+                                                 slave._name, opt._name))
+                    elif len(value_slave) < masterlen:
+                        for num in range(0, masterlen - len(value_slave)):
+                            value_slave.append(None, force=True)
 
-        elif opt in self.slaves:
-            if len(self._get_value(self.slaves[opt])) != len(value):
-                   raise MultiTypeError("invalid len for the slave: {0}"
-                    " which has {1} as master".format(opt._name,
-                                                      self.slaves[opt]._name))
-        if opt.is_multi() and not isinstance(value, Multi):
-                value = Multi(value, self.context, opt, multitypes.default)
+            elif opt.multitype == multitypes.slave:
+                if len(self._get_value(self.opt.master_slaves)) != len(value):
+                    raise MultiTypeError("invalid len for the slave: {0}"
+                                         " which has {1} as master".format(
+                                             opt._name, self.opt.master_slaves._name))
+            if not isinstance(value, Multi):
+                value = Multi(value, self.context, opt)
         self.setitem(opt, value)
 
     def setitem(self, opt, value):
-        self.set_previous_value(opt)
         if type(value) == list:
             raise MultiTypeError("the type of the value {0} which is multi shall "
                                  "be Multi and not list".format(str(value)))
-        self.values[opt] = value
-        self.setowner(opt, self.context._cfgimpl_settings.getowner())
+        self._test_mandatory(opt, value)
+        self.values[opt] = (self.context.cfgimpl_get_settings().getowner(), value)
 
     def __contains__(self, opt):
         return opt in self.values
-    #____________________________________________________________
-    def setowner(self, opt, owner):
-        if isinstance(owner, owners.Owner):
-            self.owners[opt] = owner
-        else:
-            raise OptionValueError("Bad owner: " + str(owner))
 
     def getowner(self, opt):
-        return self.owners.get(opt, owners.default)
+        return self.values.get(opt, (owners.default, None))[0]
+
+    def setowner(self, opt, owner):
+        if opt not in self.values:
+            raise ConfigError('no value for {} cannot change owner to {}'.format(opt))
+        if not isinstance(owner, owners.Owner):
+            raise TypeError("invalid generic owner {0}".format(str(owner)))
+        self.values[opt] = (owner, self.values[opt][1])
+
+    def is_default_owner(self, opt):
+        """
+        :param config: *must* be only the **parent** config
+                       (not the toplevel config)
+        :return: boolean
+        """
+        return self.getowner(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, multitype):
+    __slots__ = ('opt', 'context')
+
+    def __init__(self, lst, context, opt):
         """
         :param lst: the Multi wraps a list value
-        :param context: the home config that has the settings and the values
+        :param context: the home config that has 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
-        self.multitype = multitype
+        self.context = context
         super(Multi, self).__init__(lst)
-        if multitype == multitypes.master:
-            self.slaves = context._cfgimpl_values.masters[opt]
-        else:
-            self.slaves = None
+
     def __setitem__(self, key, value):
         self._validate(value)
-        self.values[self.opt] = self
+        self.context.cfgimpl_get_values()[self.opt] = self
         super(Multi, self).__setitem__(key, value)
 
     def append(self, value, force=False):
@@ -248,20 +236,21 @@ class Multi(list):
         only if the option is a master
         """
         if not force:
-            if self.multitype == multitypes.slave:
+            if self.opt.multitype == multitypes.slave:
                 raise MultiTypeError("cannot append a value on a multi option {0}"
-                        " which is a slave".format(self.opt._name))
-            elif self.multitype == multitypes.master:
-                for slave in self.slaves:
-                    self.values[slave].append(None, force=True)
+                                     " which is a slave".format(self.opt._name))
+            elif self.opt.multitype == multitypes.master:
+                for slave in self.opt.master_slaves:
+                    self.context.cfgimpl_get_values()[slave].append(None, force=True)
         self._validate(value)
-        self.values.setitem(self.opt, self)
+        self.context.cfgimpl_get_values().setitem(self.opt, self)
         super(Multi, self).append(value)
 
     def _validate(self, value):
-        if value != None and not self.opt._validate(value):
+        if value is not None and not self.opt._validate(value):
             raise ConfigError("invalid value {0} "
-                    "for option {1}".format(str(value), self.opt._name))
+                              "for option {1}".format(str(value),
+                                                      self.opt._name))
 
     def pop(self, key, force=False):
         """the list value can be updated (poped)
@@ -271,11 +260,11 @@ class Multi(list):
         :return: the requested element
         """
         if not force:
-            if self.multitype == multitypes.slave:
+            if self.opt.multitype == multitypes.slave:
                 raise MultiTypeError("cannot append a value on a multi option {0}"
-                        " which is a slave".format(self.opt._name))
-            elif self.multitype == multitypes.master:
-                for slave in self.slaves:
-                    self.values[slave].pop(key, force=True)
-        self.values.setitem(self.opt, self)
+                                     " which is a slave".format(self.opt._name))
+            elif self.opt.multitype == multitypes.master:
+                for slave in self.opt.master_slaves:
+                    self.context.cfgimpl_get_values()[slave].pop(key, force=True)
+        self.context.cfgimpl_get_values().setitem(self.opt, self)
         return super(Multi, self).pop(key)