Merge branch 'master' into orm
authorEmmanuel Garette <egarette@cadoles.com>
Thu, 12 Dec 2013 16:50:42 +0000 (17:50 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Thu, 12 Dec 2013 16:50:42 +0000 (17:50 +0100)
Conflicts:
test/test_config.py
tiramisu/autolib.py
tiramisu/option.py
tiramisu/value.py

1  2 
test/test_config.py
test/test_option_calculation.py
test/test_option_consistency.py
test/test_requires.py
tiramisu/autolib.py
tiramisu/config.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/value.py

diff --combined test/test_config.py
@@@ -43,7 -43,7 +43,7 @@@ def test_base_config()
      cfg = Config(descr)
      assert cfg.dummy is False
      dm = cfg.unwrap_from_path('dummy')
 -    assert dm._name == 'dummy'
 +    assert dm.impl_getname() == 'dummy'
  
  
  def test_not_config():
@@@ -82,11 -82,11 +82,11 @@@ def test_base_config_and_groups()
      assert config.gc.name == 'ref'
      assert config.bool is False
      nm = config.unwrap_from_path('gc.name')
 -    assert nm._name == 'name'
 +    assert nm.impl_getname() == 'name'
      gc = config.unwrap_from_path('gc')
 -    assert gc._name == 'gc'
 -    #nm = config.unwrap_from_name('name')
 -    #assert nm._name == 'name'
 +    assert gc.impl_getname() == 'gc'
 +    #nm = config.unwrap_fromimpl_getname()('name')
 +    #assert nm.impl_getname() == 'name'
  
  
  def test_base_config_in_a_tree():
@@@ -131,8 -131,10 +131,10 @@@ def test_cfgimpl_get_home_by_path()
      config.bool = False
      assert config.cfgimpl_get_home_by_path('gc.dummy')[1] == 'dummy'
      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_not_valid_properties():
+     raises(TypeError, "stroption = StrOption('str', 'Test string option', default='abc', properties=['mandatory',])")
  
  
  def test_information_config():
      config.impl_set_information('info', string)
      assert config.impl_get_information('info') == string
      raises(ValueError, "config.impl_get_information('noinfo')")
+     assert config.impl_get_information('noinfo', 'default') == 'default'
  
  
 +#FIXME test impl_get_xxx pour OD ou ne pas cacher
  def test_config_impl_get_path_by_opt():
      descr = make_description()
      config = Config(descr)
      dummy = config.unwrap_from_path('gc.dummy')
      boo = config.unwrap_from_path('bool')
+     unknown = IntOption('test', '')
      assert config.cfgimpl_get_description().impl_get_path_by_opt(boo) == 'bool'
      assert config.cfgimpl_get_description().impl_get_path_by_opt(dummy) == 'gc.dummy'
+     raises(AttributeError, "config.cfgimpl_get_description().impl_get_path_by_opt(unknown)")
  
  
  def test_config_impl_get_opt_by_path():
      boo = config.unwrap_from_path('bool')
      assert config.cfgimpl_get_description().impl_get_opt_by_path('bool') == boo
      assert config.cfgimpl_get_description().impl_get_opt_by_path('gc.dummy') == dummy
+     raises(AttributeError, "config.cfgimpl_get_description().impl_get_opt_by_path('gc.unknown')")
  
  
  def test_information_display():
@@@ -226,16 -231,46 +232,49 @@@ def test_duplicated_option()
      g1 = IntOption('g1', '', 1)
      #in same OptionDescription
      raises(ConflictError, "d1 = OptionDescription('od', '', [g1, g1])")
 +
 +
 +def test_duplicated_option_diff_od():
 +    g1 = IntOption('g1', '', 1)
      d1 = OptionDescription('od1', '', [g1])
 -    d2 = OptionDescription('od2', '', [g1])
 -    root = OptionDescription('root', '', [d1, d2])
      #in different OptionDescription
 -    raises(ConflictError, "config = Config(root)")
 +    raises(ConflictError, "d2 = OptionDescription('od2', '', [g1])")
 +
  
  def test_cannot_assign_value_to_option_description():
      descr = make_description()
      cfg = Config(descr)
      raises(TypeError, "cfg.gc = 3")
+ def test_config_multi():
+     i1 = IntOption('test1', '', multi=True)
+     i2 = IntOption('test2', '', multi=True, default_multi=1)
+     i3 = IntOption('test3', '', default=[2], multi=True, default_multi=1)
+     od = OptionDescription('test', '', [i1, i2, i3])
+     config = Config(od)
+     assert config.test1 == []
+     assert config.test2 == []
+     config.test2.append()
+     assert config.test2 == [1]
+     assert config.test3 == [2]
+     config.test3.append()
+     assert config.test3 == [2, 1]
+ def test_no_validation():
+     i1 = IntOption('test1', '')
+     od = OptionDescription('test', '', [i1])
+     c = Config(od)
+     setting = c.cfgimpl_get_settings()
+     c.test1 = 1
+     raises(ValueError, 'c.test1 = "yes"')
+     assert c.test1 == 1
+     setting.remove('validator')
+     c.test1 = "yes"
+     assert c.test1 == "yes"
+     setting.append('validator')
+     raises(ValueError, 'c.test1')
+     del(c.test1)
+     assert c.test1 == None
@@@ -21,7 -21,13 +21,13 @@@ def return_list(value=None)
  
  
  def return_list2(*args):
-     return list(args)
+     l = []
+     for arg in args:
+         if isinstance(arg, list):
+             l.extend(arg)
+         else:
+             l.append(arg)
+     return l
  
  
  def return_value(value=None):
@@@ -34,6 -40,10 +40,10 @@@ def return_value2(*args, **kwargs)
      return value
  
  
+ def return_calc(i, j, k):
+     return i + j + k
  def make_description():
      gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
      gcdummy = BoolOption('dummy', 'dummy', default=False)
@@@ -93,83 -103,6 +103,6 @@@ def test_identical_paths()
      raises(ConflictError, "make_description_duplicates()")
  
  
- def make_description2():
-     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')
-     booloption = BoolOption('bool', 'Test boolean option', default=True)
-     intoption = IntOption('int', 'Test int option', default=0)
-     stroption = StrOption('str', 'Test string option', default="abc")
-     # first multi
-     boolop = BoolOption('boolop', 'Test boolean option op', default=True)
-     boolop.enable_multi()
-     wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                 requires=({'option': boolop, 'expected': True, 'action': 'hidden'},))
-     # second multi
-     wantframework_option = BoolOption('wantframework', 'Test requires',
-                                       default=False,
-                                       requires=({'option': boolop, 'expected': True, 'action': 'hidden'},))
-     wantframework_option.enable_multi()
-     gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
-     descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                               wantref_option, stroption,
-                               wantframework_option,
-                               intoption, boolop])
-     return descr
- # FIXME: il faudra tester les validations sur les multis
- #def test_multi_constraints():
- #    "a multi in a constraint has to have the same length"
- #    descr = make_description2()
- #    cfg = Config(descr)
- #    cfg.boolop = [True, True, False]
- #    cfg.wantframework = [False, False, True]
- #
- #def test_multi_raise():
- #    "a multi in a constraint has to have the same length"
- #    # FIXME fusionner les deux tests, MAIS PROBLEME :
- #    # il ne devrait pas etre necessaire de refaire une config
- #    # si la valeur est modifiee une deuxieme fois ->
- #    #raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
- #    #   ExceptionFailure: 'DID NOT RAISE'
- #    descr = make_description2()
- #    cfg = Config(descr)
- #    cfg.boolop = [True]
- #    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_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()
- #    config = Config(descr)
- #    config.bool = False
- #    newoption = BoolOption('newoption', 'dummy twoo', default=False)
- #    descr.add_child(newoption)
- #    config.cfgimpl_update()
- #    assert config.newoption == False
- # ____________________________________________________________
  def make_description_requires():
      gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
      gcdummy = BoolOption('dummy', 'dummy', default=False)
@@@ -310,6 -243,19 +243,19 @@@ def test_callback()
      assert cfg.val1 == 'val'
  
  
+ def test_callback_params_without_callback():
+     raises(ValueError, "StrOption('val2', '', callback_params={'': ('yes',)})")
+ def test_callback_invalid():
+     raises(ValueError, 'val1 = StrOption("val1", "", callback="string")')
+     raises(ValueError, 'val1 = StrOption("val1", "", callback=return_val, callback_params="string")')
+     val1 = StrOption('val1', "", 'val')
+     raises(ValueError, "StrOption('val2', '', callback=return_value, callback_params={'': 'string'})")
+     raises(ValueError, "StrOption('val4', '', callback=return_value, callback_params={'value': (('string', False),)})")
+     raises(ValueError, "StrOption('val4', '', callback=return_value, callback_params={'value': ((val1, 'string'),)})")
  def test_callback_value():
      val1 = StrOption('val1', "", 'val')
      val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)})
@@@ -372,7 -318,6 +318,7 @@@ def test_callback_symlink()
      cfg = Config(maconfig)
      cfg.read_write()
      assert cfg.val1 == 'val'
 +    assert cfg.val2 == 'val'
      assert cfg.val3 == 'val'
      cfg.val1 = 'new-val'
      assert cfg.val1 == 'new-val'
@@@ -422,7 -367,7 +368,7 @@@ def test_callback_multi_value()
      cfg.val1.append('new-val2')
      assert cfg.val1 == ['new-val', 'new-val2']
      assert cfg.val2 == ['new-val', 'new-val2']
-     assert cfg.val4 == ['new-val', 'yes', 'new-val2', 'yes']
+     assert cfg.val4 == ['new-val', 'new-val2', 'yes']
      del(cfg.val1)
      assert cfg.val1 == ['val']
      assert cfg.val2 == ['val']
@@@ -444,6 -389,14 +390,14 @@@ def test_callback_multi_list()
      assert cfg.val1 == ['val', 'val']
  
  
+ def test_callback_multi_list_extend():
+     val1 = StrOption('val1', "", callback=return_list2, callback_params={'': (['1', '2', '3'], ['4', '5'])}, multi=True)
+     maconfig = OptionDescription('rootconfig', '', [val1])
+     cfg = Config(maconfig)
+     cfg.read_write()
+     assert cfg.val1 == ['1', '2', '3', '4', '5']
  def test_callback_master_and_slaves_master():
      val1 = StrOption('val1', "", multi=True, callback=return_val)
      val2 = StrOption('val2', "", multi=True)
      cfg = Config(maconfig)
      cfg.read_write()
      assert cfg.val1.val1 == ['val']
-     cfg.val1.val1.append(None)
+     cfg.val1.val1.append()
      assert cfg.val1.val1 == ['val', 'val']
      assert cfg.val1.val2 == [None, None]
  
@@@ -468,7 -421,7 +422,7 @@@ def test_callback_master_and_slaves_mas
      cfg.read_write()
      assert cfg.val1.val1 == ['val', 'val']
      assert cfg.val1.val2 == [None, None]
-     cfg.val1.val1.append(None)
+     cfg.val1.val1.append()
      assert cfg.val1.val1 == ['val', 'val', None]
      assert cfg.val1.val2 == [None, None, None]
      del(cfg.val1.val1)
@@@ -536,7 -489,8 +490,8 @@@ def test_callback_master_and_slaves_val
      val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)})
      val4 = StrOption('val4', '', multi=True, default=['val10', 'val11'])
      val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)})
-     interface1 = OptionDescription('val1', '', [val1, val2, val3, val5])
+     val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val5, False),)})
+     interface1 = OptionDescription('val1', '', [val1, val2, val3, val5, val6])
      interface1.impl_set_group_type(groups.master)
      maconfig = OptionDescription('rootconfig', '', [interface1, val4])
      cfg = Config(maconfig)
      assert cfg.val1.val2 == []
      assert cfg.val1.val3 == []
      assert cfg.val1.val5 == []
+     assert cfg.val1.val6 == []
      #
      cfg.val1.val1 = ['val1']
      assert cfg.val1.val1 == ['val1']
      assert cfg.val1.val2 == ['val1']
      assert cfg.val1.val3 == ['yes']
      assert cfg.val1.val5 == ['val10']
+     assert cfg.val1.val6 == ['val10']
      #
      cfg.val1.val1.append('val2')
      assert cfg.val1.val1 == ['val1', 'val2']
      assert cfg.val1.val2 == ['val1', 'val2']
      assert cfg.val1.val3 == ['yes', 'yes']
      assert cfg.val1.val5 == ['val10', 'val11']
+     assert cfg.val1.val6 == ['val10', 'val11']
      #
      cfg.val1.val1 = ['val1', 'val2', 'val3']
      assert cfg.val1.val1 == ['val1', 'val2', 'val3']
      assert cfg.val1.val2 == ['val1', 'val2', 'val3']
      assert cfg.val1.val3 == ['yes', 'yes', 'yes']
      assert cfg.val1.val5 == ['val10', 'val11', None]
+     assert cfg.val1.val6 == ['val10', 'val11', None]
      #
      cfg.val1.val1.pop(2)
      assert cfg.val1.val1 == ['val1', 'val2']
      assert cfg.val1.val2 == ['val1', 'val2']
      assert cfg.val1.val3 == ['yes', 'yes']
      assert cfg.val1.val5 == ['val10', 'val11']
+     assert cfg.val1.val6 == ['val10', 'val11']
      #
      cfg.val1.val2 = ['val2', 'val2']
      cfg.val1.val3 = ['val2', 'val2']
      assert cfg.val1.val2 == ['val2', 'val2']
      assert cfg.val1.val3 == ['val2', 'val2']
      assert cfg.val1.val5 == ['val2', 'val2']
+     assert cfg.val1.val6 == ['val2', 'val2']
      #
      cfg.val1.val1.append('val3')
      assert cfg.val1.val2 == ['val2', 'val2', 'val3']
      assert cfg.val1.val3 == ['val2', 'val2', 'yes']
      assert cfg.val1.val5 == ['val2', 'val2', None]
+     assert cfg.val1.val6 == ['val2', 'val2', None]
      cfg.cfgimpl_get_settings().remove('cache')
      cfg.val4 = ['val10', 'val11', 'val12']
      #if value is already set, not updated !
      cfg.val1.val1.append('val3')
      cfg.val1.val1 = ['val1', 'val2', 'val3']
      assert cfg.val1.val5 == ['val2', 'val2', 'val12']
+     assert cfg.val1.val6 == ['val2', 'val2', 'val12']
+ def test_callback_master():
+     val2 = StrOption('val2', "", multi=True, callback=return_value)
+     val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val2, False),)})
+     interface1 = OptionDescription('val1', '', [val1, val2])
+     raises(ValueError, "interface1.impl_set_group_type(groups.master)")
+ def test_callback_master_and_other_master_slave():
+     val1 = StrOption('val1', "", multi=True)
+     val2 = StrOption('val2', "", multi=True)
+     val3 = StrOption('val3', "", multi=True)
+     val4 = StrOption('val4', '', multi=True, default=['val10', 'val11'])
+     val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)})
+     val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val2, False),)})
+     interface1 = OptionDescription('val1', '', [val1, val2, val3])
+     interface1.impl_set_group_type(groups.master)
+     interface2 = OptionDescription('val4', '', [val4, val5, val6])
+     interface2.impl_set_group_type(groups.master)
+     maconfig = OptionDescription('rootconfig', '', [interface1, interface2])
+     cfg = Config(maconfig)
+     cfg.read_write()
+     assert cfg.val4.val4 == ['val10', 'val11']
+     assert cfg.val4.val5 == [None, None]
+     assert cfg.val4.val6 == [None, None]
+     cfg.val1.val1 = ['yes']
+     assert cfg.val4.val4 == ['val10', 'val11']
+     assert cfg.val4.val5 == ['yes', None]
+     assert cfg.val4.val6 == [None, None]
+     cfg.val1.val2 = ['no']
+     assert cfg.val4.val4 == ['val10', 'val11']
+     assert cfg.val4.val5 == ['yes', None]
+     assert cfg.val4.val6 == ['no', None]
+     cfg.val1.val1 = ['yes', 'yes', 'yes']
+     cfg.val1.val2 = ['no', 'no', 'no']
+     assert cfg.val4.val4 == ['val10', 'val11']
+     assert cfg.val4.val5 == ['yes', 'yes']
+     assert cfg.val4.val6 == ['no', 'no']
+ def test_callback_different_type():
+     val = IntOption('val', "", default=2)
+     val_ = IntOption('val_', "", default=3)
+     val1 = IntOption('val1', "", multi=True)
+     val2 = IntOption('val2', "", multi=True, callback=return_calc, callback_params={'': ((val, False), (val1, False)), 'k': ((val_, False),)})
+     interface1 = OptionDescription('val1', '', [val1, val2])
+     interface1.impl_set_group_type(groups.master)
+     maconfig = OptionDescription('rootconfig', '', [interface1, val, val_])
+     cfg = Config(maconfig)
+     cfg.read_write()
+     assert cfg.val1.val1 == []
+     assert cfg.val1.val2 == []
+     cfg.val1.val1 = [1]
+     assert cfg.val1.val1 == [1]
+     assert cfg.val1.val2 == [6]
+     cfg.val1.val1 = [1, 3]
+     assert cfg.val1.val1 == [1, 3]
+     assert cfg.val1.val2 == [6, 8]
+     cfg.val1.val1 = [1, 3, 5]
+     assert cfg.val1.val1 == [1, 3, 5]
+     assert cfg.val1.val2 == [6, 8, 10]
  
  
  def test_callback_hidden():
@@@ -654,7 -678,7 +679,7 @@@ def test_callback_multi_list_params()
      maconfig = OptionDescription('rootconfig', '', [val1, oval2])
      cfg = Config(maconfig)
      cfg.read_write()
-     assert cfg.val2.val2 == ['val', 'val', 'val', 'val']
+     assert cfg.val2.val2 == ['val', 'val']
  
  
  def test_callback_multi_list_params_key():
      maconfig = OptionDescription('rootconfig', '', [val1, oval2])
      cfg = Config(maconfig)
      cfg.read_write()
-     assert cfg.val2.val2 == ['val', 'val', 'val', 'val']
- def test_callback_multi_multi():
-     val1 = StrOption('val1', "", multi=True, default=['val1', 'val2', 'val3'])
-     val2 = StrOption('val2', "", multi=True, default=['val11', 'val12'])
-     val3 = StrOption('val3', "", default='val4')
-     val4 = StrOption('val4', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val2, False))})
-     val5 = StrOption('val5', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val3, False))})
-     val6 = StrOption('val6', "", multi=True, default=['val21', 'val22', 'val23'])
-     val7 = StrOption('val7', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val6, False))})
-     raises(ValueError, "StrOption('val8', '', multi=True, callback=return_list2, callback_params={'value': ((val1, False), (val6, False))})")
-     maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5, val6, val7])
-     cfg = Config(maconfig)
-     cfg.read_write()
-     raises(ConfigError, "cfg.val4")
-     assert cfg.val5 == ['val1', 'val4', 'val2', 'val4', 'val3', 'val4']
-     assert cfg.val7 == ['val1', 'val21', 'val2', 'val22', 'val3', 'val23']
- def test_multi_with_no_value():
-     #First option return [] (so without value)
-     val1 = StrOption('val1', "", ['val'], multi=True)
-     val2 = StrOption('val2', "", multi=True)
-     val3 = StrOption('val3', '', multi=True, callback=return_value, callback_params={'': ((val2, False),), 'value': ((val1, False),)})
-     od = OptionDescription('od', '', [val1, val2, val3])
-     c = Config(od)
-     raises(ConfigError, "c.val3")
+     assert cfg.val2.val2 == ['val', 'val']
@@@ -8,6 -8,17 +8,17 @@@ from tiramisu.option import IPOption, N
  from tiramisu.error import ConfigError
  
  
+ def test_consistency():
+     a = IntOption('a', '')
+     b = IntOption('b', '')
+     od = OptionDescription('od', '', [a, b])
+     a.impl_add_consistency('not_equal', b)
+     #consistency to itself
+     raises(ConfigError, "a.impl_add_consistency('not_equal', a)")
+     #consistency with string
+     raises(ConfigError, "a.impl_add_consistency('not_equal', 'a')")
  def test_consistency_not_equal():
      a = IntOption('a', '')
      b = IntOption('b', '')
@@@ -53,7 -64,7 +64,7 @@@ def test_consistency_not_equal_many_opt
      raises(ValueError, "c.e = 3")
  
  
 -def test_consistency_not_in_config():
 +def test_consistency_not_in_config_1():
      a = IntOption('a', '')
      b = IntOption('b', '')
      a.impl_add_consistency('not_equal', b)
      od2 = OptionDescription('od2', '', [b])
      od = OptionDescription('root', '', [od1])
      raises(ConfigError, "Config(od)")
 +
 +
 +def test_consistency_not_in_config_2():
 +    a = IntOption('a', '')
 +    b = IntOption('b', '')
 +    a.impl_add_consistency('not_equal', b)
 +    od1 = OptionDescription('od1', '', [a])
 +    od2 = OptionDescription('od2', '', [b])
      od = OptionDescription('root', '', [od1, od2])
      Config(od)
 +
 +
 +def test_consistency_not_in_config_3():
 +    a = IntOption('a', '')
 +    b = IntOption('b', '')
 +    a.impl_add_consistency('not_equal', b)
 +    od1 = OptionDescription('od1', '', [a])
 +    od2 = OptionDescription('od2', '', [b])
 +    od = OptionDescription('root', '', [od1, od2])
      #with subconfig
      raises(ConfigError, "Config(od.od1)")
  
  
 -def test_consistency_afer_config():
 +def test_consistency_after_config():
      a = IntOption('a', '')
      b = IntOption('b', '')
      od1 = OptionDescription('od1', '', [a])
      od2 = OptionDescription('od2', '', [b])
      od = OptionDescription('root', '', [od1, od2])
      Config(od)
 -    raises(AttributeError, "a.impl_add_consistency('not_equal', b)")
 +    #FIXME a cause du read_only
 +    #raises(AttributeError, "a.impl_add_consistency('not_equal', b)")
  
  
  def test_consistency_not_equal_symlink():
@@@ -267,20 -260,13 +278,20 @@@ def test_consistency_broadcast()
      c.c[1] = '192.168.2.255'
  
  
 -def test_consistency_broadcast_default():
 +def test_consistency_broadcast_default_1():
      a = NetworkOption('a', '', '192.168.1.0')
      b = NetmaskOption('b', '', '255.255.255.128')
      c = BroadcastOption('c', '', '192.168.2.127')
      d = BroadcastOption('d', '', '192.168.1.127')
      od = OptionDescription('a', '', [a, b, c])
      raises(ValueError, "c.impl_add_consistency('broadcast', a, b)")
 +
 +
 +def test_consistency_broadcast_default_2():
 +    a = NetworkOption('a', '', '192.168.1.0')
 +    b = NetmaskOption('b', '', '255.255.255.128')
 +    c = BroadcastOption('c', '', '192.168.2.127')
 +    d = BroadcastOption('d', '', '192.168.1.127')
      od2 = OptionDescription('a', '', [a, b, d])
      d.impl_add_consistency('broadcast', a, b)
  
diff --combined test/test_requires.py
@@@ -26,6 -26,20 +26,20 @@@ def test_requires()
      assert props == ['disabled']
  
  
+ def test_requires_invalid():
+     a = BoolOption('activate_service', '', True)
+     raises(ValueError, "IPOption('ip_address_service', '', requires='string')")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'unknown': True}])")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False}])")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'action': 'disabled'}])")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'expected': False, 'action': 'disabled'}])")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'inverse': 'string'}])")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'transitive': 'string'}])")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'same_action': 'string'}])")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': 'string', 'expected': False, 'action': 'disabled'}])")
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': 'string', 'action': 'disabled'}])")
  def test_requires_same_action():
      a = BoolOption('activate_service', '', True)
      b = BoolOption('activate_service_web', '', True,
@@@ -489,21 -503,26 +503,26 @@@ def test_requires_multi_disabled_invers
          assert props == ['disabled']
  
  
 -def test_requires_requirement_append():
 -    a = BoolOption('activate_service', '', True)
 -    b = IPOption('ip_address_service', '',
 -                 requires=[{'option': a, 'expected': False, 'action': 'disabled'}])
 -    od = OptionDescription('service', '', [a, b])
 -    c = Config(od)
 -    c.read_write()
 -    str(c.cfgimpl_get_settings())
 -    str(c.cfgimpl_get_settings()[b])
 -    raises(ValueError, 'c.cfgimpl_get_settings()[b].append("disabled")')
 -    c.activate_service = False
 -    # disabled is now set, test to remove disabled before store in storage
 -    c.cfgimpl_get_settings()[b].append("test")
 +#def test_requires_requirement_append():
 +#    a = BoolOption('activate_service', '', True)
 +#    b = IPOption('ip_address_service', '',
 +#                 requires=[{'option': a, 'expected': False, 'action': 'disabled'}])
 +#    od = OptionDescription('service', '', [a, b])
 +#    c = Config(od)
 +#    c.read_write()
 +#    str(c.cfgimpl_get_settings())
 +#    str(c.cfgimpl_get_settings()[b])
 +#    raises(ValueError, 'c.cfgimpl_get_settings()[b].append("disabled")')
 +#    c.activate_service = False
 +#    # disabled is now set, test to remove disabled before store in storage
 +#    c.cfgimpl_get_settings()[b].append("test")
  
  
+ def test_requires_different_inverse():
+     a = BoolOption('activate_service', '', True)
+     raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': True, 'action': 'disabled', 'inverse': True}, {'option': a, 'expected': True, 'action': 'disabled', 'inverse': False}])")
  def test_requires_recursive_path():
      a = BoolOption('activate_service', '', True)
      b = IPOption('ip_address_service', '',
diff --combined tiramisu/autolib.py
  # ____________________________________________________________
  "enables us to carry out a calculation and return an option's value"
  from tiramisu.error import PropertiesOptionError, ConfigError
+ from tiramisu.setting import multitypes
  from tiramisu.i18n import _
  # ____________________________________________________________
  
  
- def carry_out_calculation(name, config, callback, callback_params,
+ def carry_out_calculation(option, config, callback, callback_params,
                            index=None, max_len=None):
      """a function that carries out a calculation for an option's value
  
 +    :param name: the option name (`opt.impl_getname()`)
+     :param name: the option
      :param config: the context config in order to have
                     the whole options available
      :param callback: the name of the callback function
      Values could have multiple values only when key is ''.
  
      * if no callback_params:
-       => calculate()
+       => calculate(<function func at 0x2092320>, [], {})
  
      * if callback_params={'': ('yes',)}
-       => calculate('yes')
+       => calculate(<function func at 0x2092320>, ['yes'], {})
  
      * if callback_params={'value': ('yes',)}
-       => calculate(value='yes')
+       => calculate(<function func at 0x165b320>, [], {'value': 'yes'})
  
      * if callback_params={'': ('yes', 'no')}
        => calculate('yes', 'no')
      * if callback_params={'value': ('yes', 'no')}
        => ValueError()
  
+     * if callback_params={'': (['yes', 'no'],)}
+       => calculate(<function func at 0x176b320>, ['yes', 'no'], {})
+     * if callback_params={'value': ('yes', 'no')}
+       => raises ValueError()
      * if callback_params={'': ((opt1, False),)}
  
         - a simple option:
           opt1 == 11
-          => calculate(11)
+          => calculate(<function func at 0x1cea320>, [11], {})
  
-        - a multi option:
+        - a multi option and not master/slave:
           opt1 == [1, 2, 3]
-          => calculate(1)
-          => calculate(2)
-          => calculate(3)
+          => calculate(<function func at 0x223c320>, [[1, 2, 3]], {})
+        - option is master or slave of opt1:
+          opt1 == [1, 2, 3]
+          => calculate(<function func at 0x223c320>, [1], {})
+          => calculate(<function func at 0x223c320>, [2], {})
+          => calculate(<function func at 0x223c320>, [3], {})
+       - opt is a master or slave but not related to option:
+         opt1 == [1, 2, 3]
+         => calculate(<function func at 0x11b0320>, [[1, 2, 3]], {})
  
      * if callback_params={'value': ((opt1, False),)}
  
         - a simple option:
           opt1 == 11
-          => calculate(value=11)
+          => calculate(<function func at 0x17ff320>, [], {'value': 11})
  
         - a multi option:
           opt1 == [1, 2, 3]
-          => calculate(value=1)
-          => calculate(value=2)
-          => calculate(value=3)
+          => calculate(<function func at 0x1262320>, [], {'value': [1, 2, 3]})
  
      * if callback_params={'': ((opt1, False), (opt2, False))}
  
+       - two single options
+           opt1 = 11
+           opt2 = 12
+           => calculate(<function func at 0x217a320>, [11, 12], {})
        - a multi option with a simple option
            opt1 == [1, 2, 3]
-           opt2 == 11
-           => calculate(1, 11)
-           => calculate(2, 11)
-           => calculate(3, 11)
+           opt2 == 12
+           => calculate(<function func at 0x2153320>, [[1, 2, 3], 12], {})
  
        - a multi option with an other multi option but with same length
            opt1 == [1, 2, 3]
            opt2 == [11, 12, 13]
-           => calculate(1, 11)
-           => calculate(2, 12)
-           => calculate(3, 13)
+           => calculate(<function func at 0x1981320>, [[1, 2, 3], [11, 12, 13]], {})
  
        - a multi option with an other multi option but with different length
            opt1 == [1, 2, 3]
            opt2 == [11, 12]
-           => ConfigError()
+           => calculate(<function func at 0x2384320>, [[1, 2, 3], [11, 12]], {})
  
        - a multi option without value with a simple option
            opt1 == []
            opt2 == 11
-           => []
+           => calculate(<function func at 0xb65320>, [[], 12], {})
  
      * if callback_params={'value': ((opt1, False), (opt2, False))}
-       => ConfigError()
+       => raises ValueError()
  
      If index is not None, return a value, otherwise return:
  
      # multi's option should have same value for all option
      len_multi = None
  
-     if callback_params != []:
-         for callbacks in callback_params:
-             key = callbacks.name
-             for callbk in callbacks.params:
-                 if callbk.option is not None:
-                     # callbk is something link (opt, True|False)
-                     option = callbk.get_option(config)
-                     force_permissive = callbk.force_permissive
-                     path = config.cfgimpl_get_description().impl_get_path_by_opt(
-                         option)
-                     # get value
-                     try:
-                         value = config._getattr(path, force_permissive=True)
-                     except PropertiesOptionError as err:
-                         if force_permissive:
-                             continue
-                         raise ConfigError(_('unable to carry out a calculation, '
-                                             'option {0} has properties: {1} for: '
-                                             '{2}').format(option.impl_getname(),
-                                                         err.proptype,
-                                                         name))
-                     is_multi = option.impl_is_multi()
-                     if is_multi:
-                         len_value = len(value)
-                         if len_multi is not None and len_multi != len_value:
-                             raise ConfigError(_('unable to carry out a '
-                                                 'calculation, option value with'
-                                                 ' multi types must have same '
-                                                 'length for: {0}').format(name))
-                         len_multi = len_value
-                         one_is_multi = True
-                     tcparams.setdefault(key, []).append((value, is_multi))
-                 else:
-                     # callbk is a value and not a multi
-                     tcparams.setdefault(key, []).append((callbk.value, False))
+     for key, callbacks in callback_params.items():
+         for callbk in callbacks:
+             if isinstance(callbk, tuple):
+                 # callbk is something link (opt, True|False)
+                 opt, force_permissive = callbk
+                 path = config.cfgimpl_get_description().impl_get_path_by_opt(
+                     opt)
+                 # get value
+                 try:
+                     value = config._getattr(path, force_permissive=True)
+                 except PropertiesOptionError as err:
+                     if force_permissive:
+                         continue
+                     raise ConfigError(_('unable to carry out a calculation, '
+                                         'option {0} has properties: {1} for: '
+                                         '{2}').format(opt._name,
+                                                       err.proptype,
+                                                       option._name))
+                 is_multi = False
+                 if opt.impl_is_multi():
+                     #opt is master, search if option is a slave
+                     if opt.impl_get_multitype() == multitypes.master:
+                         if option in opt.impl_get_master_slaves():
+                             is_multi = True
+                     #opt is slave, search if option is an other slaves
+                     elif opt.impl_get_multitype() == multitypes.slave:
+                         if option in opt.impl_get_master_slaves().impl_get_master_slaves():
+                             is_multi = True
+                 if is_multi:
+                     len_multi = len(value)
+                     one_is_multi = True
+                 tcparams.setdefault(key, []).append((value, is_multi))
+             else:
+                 # callbk is a value and not a multi
+                 tcparams.setdefault(key, []).append((callbk, False))
  
      # if one value is a multi, launch several time calculate
      # if index is set, return a value
      if one_is_multi:
          ret = []
          if index:
-             if index < len_multi:
-                 range_ = [index]
-             else:
-                 range_ = []
-                 ret = None
+             range_ = [index]
          else:
-             if max_len and max_len < len_multi:
-                 range_ = range(max_len)
-             else:
-                 range_ = range(len_multi)
+             range_ = range(len_multi)
          for incr in range_:
              args = []
              kwargs = {}
              if index:
                  ret = calc
              else:
-                 if isinstance(calc, list):
-                     ret.extend(calc)
-                 else:
-                     ret.append(calc)
+                 ret.append(calc)
          return ret
      else:
          # no value is multi
                      args.append(couple[0])
                  else:
                      kwargs[key] = couple[0]
-         return calculate(callback, args, kwargs)
+         ret = calculate(callback, args, kwargs)
+         if callback_params != {}:
+             if isinstance(ret, list) and max_len:
+                 ret = ret[:max_len]
+                 if len(ret) < max_len:
+                     ret = ret + [None] * (max_len - len(ret))
+             if isinstance(ret, list) and index:
+                 if len(ret) < index + 1:
+                     ret = None
+                 else:
+                     ret = ret[index]
+         return ret
  
  
  def calculate(callback, args, kwargs):
diff --combined tiramisu/config.py
@@@ -73,24 -73,21 +73,24 @@@ class SubConfig(object)
                                   force_properties=force_properties)
          return self, path[-1]
  
 -    def __hash__(self):
 -        return hash(self.cfgimpl_get_description().impl_getkey(self))
 -
 -    def __eq__(self, other):
 -        "Config's comparison"
 -        if not isinstance(other, Config):
 -            return False
 -        return self.cfgimpl_get_description().impl_getkey(self) == \
 -            other.cfgimpl_get_description().impl_getkey(other)
 -
 -    def __ne__(self, other):
 -        "Config's comparison"
 -        if not isinstance(other, Config):
 -            return True
 -        return not self == other
 +    #def __hash__(self):
 +    #FIXME
 +    #    return hash(self.cfgimpl_get_description().impl_getkey(self))
 +
 +    #def __eq__(self, other):
 +    #FIXME
 +    #    "Config's comparison"
 +    #    if not isinstance(other, Config):
 +    #        return False
 +    #    return self.cfgimpl_get_description().impl_getkey(self) == \
 +    #        other.cfgimpl_get_description().impl_getkey(other)
 +
 +    #def __ne__(self, other):
 +    #FIXME
 +    #    "Config's comparison"
 +    #    if not isinstance(other, Config):
 +    #        return True
 +    #    return not self == other
  
      # ______________________________________________________________________
      def __iter__(self):
@@@ -99,7 -96,7 +99,7 @@@
          for child in self.cfgimpl_get_description().impl_getchildren():
              if not isinstance(child, OptionDescription):
                  try:
 -                    yield child._name, getattr(self, child._name)
 +                    yield child.impl_getname(), getattr(self, child.impl_getname())
                  except GeneratorExit:
                      raise StopIteration
                  except PropertiesOptionError:
          iteration on Options and OptionDescriptions."""
          for child in self.cfgimpl_get_description().impl_getchildren():
              try:
 -                yield child._name, getattr(self, child._name)
 +                yield child.impl_getname(), getattr(self, child.impl_getname())
              except GeneratorExit:
                  raise StopIteration
              except PropertiesOptionError:
                      if group_type is None or (group_type is not None and
                                                child.impl_get_group_type()
                                                == group_type):
 -                        yield child._name, getattr(self, child._name)
 +                        yield child.impl_getname(), getattr(self, child.impl_getname())
                  except GeneratorExit:
                      raise StopIteration
                  except PropertiesOptionError:
                  force_properties=force_properties,
                  force_permissive=force_permissive)
  
-     def find(self, bytype=None, byname=None, byvalue=None, type_='option'):
+     def find(self, bytype=None, byname=None, byvalue=None, type_='option',
+              check_properties=True):
          """
              finds a list of options recursively in the config
  
              :param bytype: Option class (BoolOption, StrOption, ...)
 -            :param byname: filter by Option._name
 +            :param byname: filter by Option.impl_getname()
              :param byvalue: filter by the option's value
              :returns: list of matching Option objects
          """
          return self._cfgimpl_get_context()._find(bytype, byname, byvalue,
                                                   first=False,
                                                   type_=type_,
-                                                  _subpath=self.cfgimpl_get_path()
-                                                  )
+                                                  _subpath=self.cfgimpl_get_path(),
+                                                  check_properties=check_properties)
  
      def find_first(self, bytype=None, byname=None, byvalue=None,
-                    type_='option', display_error=True):
+                    type_='option', display_error=True, check_properties=True):
          """
              finds an option recursively in the config
  
              :param bytype: Option class (BoolOption, StrOption, ...)
 -            :param byname: filter by Option._name
 +            :param byname: filter by Option.impl_getname()
              :param byvalue: filter by the option's value
              :returns: list of matching Option objects
          """
          return self._cfgimpl_get_context()._find(
              bytype, byname, byvalue, first=True, type_=type_,
-             _subpath=self.cfgimpl_get_path(), display_error=display_error)
+             _subpath=self.cfgimpl_get_path(), display_error=display_error,
+             check_properties=check_properties)
  
      def _find(self, bytype, byname, byvalue, first, type_='option',
                _subpath=None, check_properties=True, display_error=True):
              raise ValueError(_('unknown type_ type {0}'
                                 'for _find').format(type_))
          find_results = []
 -        opts, paths = self.cfgimpl_get_description()._cache_paths
 -        for index in range(0, len(paths)):
 -            option = opts[index]
 +        paths = self.cfgimpl_get_description()._cache_paths[1]
 +        for path in paths:
 +            option = self.cfgimpl_get_description().impl_get_opt_by_path(path)
              if isinstance(option, OptionDescription):
                  continue
 -            path = paths[index]
              if _subpath is not None and not path.startswith(_subpath + '.'):
                  continue
              if not _filter_by_name():
          #withoption can be set to None below !
          if withoption is None:
              for opt in self.cfgimpl_get_description().impl_getchildren():
 -                path = opt._name
 +                path = opt.impl_getname()
                  self._make_sub_dict(opt, path, pathsvalues, _currpath, flatten)
          if _currpath == []:
              options = dict(pathsvalues)
                  pass  # this just a hidden or disabled option
          else:
              try:
 -                value = self._getattr(opt._name)
 +                value = self._getattr(opt.impl_getname())
                  if flatten:
 -                    name = opt._name
 +                    name = opt.impl_getname()
                  else:
 -                    name = '.'.join(_currpath + [opt._name])
 +                    name = '.'.join(_currpath + [opt.impl_getname()])
                  pathsvalues.append((name, value))
              except PropertiesOptionError:
                  pass  # this just a hidden or disabled option
diff --combined tiramisu/option.py
  # ____________________________________________________________
  import re
  import sys
 -from copy import copy, deepcopy
 +from copy import copy
  from types import FunctionType
  from IPy import IP
  import warnings
 +#from pickle import loads, dumps
 +
 +from sqlalchemy.ext.declarative import declarative_base
 +from sqlalchemy import create_engine, Column, Integer, String, Boolean, \
 +    PickleType, ForeignKey, Table
 +from sqlalchemy.orm import relationship, backref
 +from sqlalchemy.orm import sessionmaker
  
  from tiramisu.error import ConfigError, ConflictError, ValueWarning
  from tiramisu.setting import groups, multitypes
@@@ -47,9 -40,7 +47,7 @@@ forbidden_names = ('iter_all', 'iter_gr
  
  def valid_name(name):
      "an option's name is a str and does not start with 'impl' or 'cfgimpl'"
-     try:
-         name = str(name)
-     except:
+     if not isinstance(name, str):
          return False
      if re.match(name_regexp, name) is None and not name.startswith('_') \
              and name not in forbidden_names \
  #____________________________________________________________
  #
  
 +engine = create_engine('sqlite:///:memory:')
 +Base = declarative_base()
 +
 +
 +class _RequireExpected(Base):
 +    __tablename__ = 'expected'
 +    id = Column(Integer, primary_key=True)
 +    expected = Column(PickleType)
 +    require = Column(Integer, ForeignKey('require.id'))
 +
 +    def __init__(self, expected):
 +        self.expected = expected
 +
 +
 +class _RequireOption(Base):
 +    __tablename__ = 'require'
 +    id = Column(Integer, primary_key=True)
 +    option = Column(Integer, ForeignKey('baseoption.id'))
 +    r_opt = Column(Integer)
 +    expected = relationship("_RequireExpected")
 +    action = Column(String, nullable=False)
 +    inverse = Column(Boolean, default=False)
 +    transitive = Column(Boolean, default=True)
 +    same_action = Column(Boolean, default=True)
 +
 +    def __init__(self, option, expected, action, inverse, transitive,
 +                 same_action):
 +        self.r_opt = option.id
 +        for expect in expected:
 +            self.expected.append(_RequireExpected(expect))
 +        self.action = action
 +        self.inverse = inverse
 +        self.transitive = transitive
 +        self.same_action = same_action
 +
 +    def get_expected(self):
 +        for expected in self.expected:
 +            yield(expected.expected)
 +
 +    def get_option(self, config):
 +        return config.cfgimpl_get_description().impl_get_opt_by_id(self.r_opt)
 +
 +
 +property_table = Table('property', Base.metadata,
 +                       Column('left_id', Integer, ForeignKey('propertyoption.name')),
 +                       Column('right_id', Integer, ForeignKey('baseoption.id'))
 +                       )
 +
 +
 +class _PropertyOption(Base):
 +    __tablename__ = 'propertyoption'
 +    name = Column(String, primary_key=True)
 +
 +    def __init__(self, name):
 +        self.name = name
 +
  
 -class BaseOption(object):
 +class _Information(Base):
 +    __tablename__ = 'information'
 +    id = Column(Integer, primary_key=True)
 +    option = Column(Integer, ForeignKey('baseoption.id'))
 +    key = Column(String)
 +    value = Column(PickleType)
 +
 +    def __init__(self, key, value):
 +        self.key = key
 +        self.value = value
 +
 +
 +class _CallbackParamOption(Base):
 +    __tablename__ = 'callback_param_option'
 +    id = Column(Integer, primary_key=True)
 +    callback_param = Column(Integer, ForeignKey('callback_param.id'))
 +    option = Column(Integer)
 +    force_permissive = Column(Boolean)
 +    value = Column(PickleType)
 +
 +    def __init__(self, option=None, force_permissive=None,  value=None):
 +        if value is not None:
 +            self.value = value
 +        else:
 +            if isinstance(option, SymLinkOption):
 +                option = option._opt
 +            self.option = option.id
 +            self.force_permissive = force_permissive
 +
 +    def get_option(self, config):
 +        return config.cfgimpl_get_description().impl_get_opt_by_id(self.option)
 +
 +
 +class _CallbackParam(Base):
 +    __tablename__ = 'callback_param'
 +    id = Column(Integer, primary_key=True)
 +    callback = Column(Integer, ForeignKey('baseoption.id'))
 +    name = Column(String)
 +    params = relationship('_CallbackParamOption')
 +
 +    def __init__(self, name, params):
 +        self.name = name
 +        for param in params:
 +            if isinstance(param, tuple):
 +                self.params.append(_CallbackParamOption(option=param[0],
 +                                                        force_permissive=param[1]))
 +            else:
 +                self.params.append(_CallbackParamOption(value=param))
 +
 +
 +consistency_table = Table('consistencyopt', Base.metadata,
 +                          Column('left_id', Integer, ForeignKey('consistency.id')),
 +                          Column('right_id', Integer, ForeignKey('baseoption.id'))
 +                          )
 +
 +
 +class _Consistency(Base):
 +    __tablename__ = 'consistency'
 +    id = Column(Integer, primary_key=True)
 +    func = Column(PickleType)
 +
 +    def __init__(self, func, all_cons_opts):
 +        self.func = func
 +        for option in all_cons_opts:
 +            option._consistencies.append(self)
 +
 +
 +class BaseOption(Base):
      """This abstract base class stands for attribute access
      in options that have to be set only once, it is of course done in the
      __setattr__ method
      """
 -    __slots__ = ('_name', '_requires', '_properties', '_readonly',
 -                 '_calc_properties', '_impl_informations',
 -                 '_state_readonly', '_state_requires', '_stated')
 +    __tablename__ = 'baseoption'
 +    id = Column(Integer, primary_key=True)
 +    _name = Column(String)
 +    _type = Column(PickleType)
 +    _informations = relationship('_Information')
 +    _default = Column(PickleType)
 +    _default_multi = Column(PickleType)
 +    _requires = relationship('_RequireOption')
 +    _multi = Column(Boolean)
 +    _multitype = Column(String)
 +    _callback = Column(PickleType)
 +    _callback_params = relationship('_CallbackParam')
 +    _validator = Column(PickleType)
 +    _validator_params = relationship('_CallbackParam')
 +    _parent = Column(Integer, ForeignKey('baseoption.id'))
 +    _children = relationship('BaseOption', enable_typechecks=False)
 +    _properties = relationship('_PropertyOption', secondary=property_table,
 +                               backref=backref('options', enable_typechecks=False))
 +    _warnings_only = Column(Boolean)
 +    _readonly = Column(Boolean, default=False)
 +    _consistencies = relationship('_Consistency', secondary=consistency_table,
 +                                  backref=backref('options', enable_typechecks=False))
 +    _choice_values = Column(PickleType)
 +    _choice_open_values = Column(Boolean)
 +    #FIXME devrait etre une table
 +    _optiondescription_group_type = Column(String)
 +    #__slots__ = ('_name', '_requires', '_properties', '_readonly',
 +    #             '_calc_properties', '_impl_informations',
 +    #             '_state_readonly', '_state_requires', '_stated')
  
 -    def __init__(self, name, doc, requires, properties):
 +    def __init__(self, name, doc, default=None, default_multi=None,
 +                 requires=None, multi=False, callback=None,
 +                 callback_params=None, validator=None, validator_params=None,
 +                 properties=None, warnings_only=False, choice_values=None,
 +                 choice_open_values=None):
          if not valid_name(name):
              raise ValueError(_("invalid name: {0} for option").format(name))
          self._name = name
 -        self._impl_informations = {}
 +        self._type = self.__class__
          self.impl_set_information('doc', doc)
 -        self._calc_properties, self._requires = validate_requires_arg(
 -            requires, self._name)
 +        requires = validate_requires_arg(requires, self._name)
 +        if requires is not None:
 +            for values in requires.values():
 +                for require in values.values():
 +                    self._requires.append(_RequireOption(*require))
 +        if not multi and default_multi is not None:
 +            raise ValueError(_("a default_multi is set whereas multi is False"
 +                             " in option: {0}").format(name))
 +        if default_multi is not None:
 +            try:
 +                self._validate(default_multi)
 +            except ValueError as err:
 +                raise ValueError(_("invalid default_multi value {0} "
 +                                   "for option {1}: {2}").format(
 +                                       str(default_multi), name, err))
 +        self._multi = multi
 +        if self._multi:
 +            if default is None:
 +                default = []
 +            self._multitype = multitypes.default
 +            self._default_multi = default_multi
 +        if callback is not None and ((not multi and (default is not None or
 +                                                     default_multi is not None))
 +                                     or (multi and (default != [] or
 +                                                    default_multi is not None))
 +                                     ):
 +            raise ValueError(_("default value not allowed if option: {0} "
 +                             "is calculated").format(name))
          if properties is None:
              properties = tuple()
          if not isinstance(properties, tuple):
                              ' must be a tuple').format(
                                  type(properties),
                                  self._name))
 -        if self._calc_properties is not None and properties is not tuple():
 -            set_forbidden_properties = set(properties) & self._calc_properties
 +        if validator is not None:
 +            validate_callback(validator, validator_params, 'validator')
 +            self._validator = validator
 +            if validator_params is not None:
 +                for key, values in validator_params.items():
 +                    self._validator_params.append(_CallbackParam(key, values))
 +        if callback is None and callback_params is not None:
 +            raise ValueError(_("params defined for a callback function but "
 +                             "no callback defined"
 +                             " yet for option {0}").format(name))
 +        if callback is not None:
 +            validate_callback(callback, callback_params, 'callback')
 +            self._callback = callback
 +            if callback_params is not None:
 +                for key, values in callback_params.items():
 +                    self._callback_params.append(_CallbackParam(key, values))
 +        if requires is not None and properties is not tuple():
 +            set_forbidden_properties = set(properties) & set(requires.keys())
              if set_forbidden_properties != frozenset():
                  raise ValueError('conflict: properties already set in '
                                   'requirement {0}'.format(
                                       list(set_forbidden_properties)))
 -        self._properties = properties  # 'hidden', 'disabled'...
 -
 -    def __setattr__(self, name, value):
 -        """set once and only once some attributes in the option,
 -        like `_name`. `_name` cannot be changed one the option and
 -        pushed in the :class:`tiramisu.option.OptionDescription`.
 -
 -        if the attribute `_readonly` is set to `True`, the option is
 -        "frozen" (which has noting to do with the high level "freeze"
 -        propertie or "read_only" property)
 -        """
 -        if not name.startswith('_state') and not name.startswith('_cache'):
 -            is_readonly = False
 -            # never change _name
 -            if name == '_name':
 -                try:
 -                    self._name
 -                    #so _name is already set
 -                    is_readonly = True
 -                except:
 -                    pass
 -            elif name != '_readonly':
 -                try:
 -                    if self._readonly is True:
 -                        is_readonly = True
 -                except AttributeError:
 -                    self._readonly = False
 -            if is_readonly:
 -                raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
 -                                       " read-only").format(
 -                                           self.__class__.__name__,
 -                                           self._name,
 -                                           name))
 -        object.__setattr__(self, name, value)
 +        if choice_values is not None:
 +            self._choice_values = choice_values
 +        if choice_open_values is not None:
 +            self._choice_open_values = choice_open_values
 +        self.impl_validate(default)
 +        if multi and default is None:
 +            self._default = []
 +        else:
 +            self._default = default
 +        for prop in properties:
 +            prop_obj = session.query(_PropertyOption).filter(_PropertyOption.name == prop).first()
 +            if prop_obj is None:
 +                prop_obj = _PropertyOption(prop)
 +            self._properties.append(prop_obj)
 +        self._warnings_only = warnings_only
  
 +    # ____________________________________________________________
      # information
      def impl_set_information(self, key, value):
          """updates the information's attribute
          :param key: information's key (ex: "help", "doc"
          :param value: information's value (ex: "the help string")
          """
 -        self._impl_informations[key] = value
 +        #FIXME pas append ! remplacer !
 +        info = session.query(_Information).filter_by(option=self.id, key=key).first()
 +        if info is None:
 +            self._informations.append(_Information(key, value))
 +        else:
 +            info.value = value
  
      def impl_get_information(self, key, default=None):
          """retrieves one information's item
  
          :param key: the item string (ex: "help")
          """
 -        if key in self._impl_informations:
 -            return self._impl_informations[key]
 +        info = session.query(_Information).filter_by(option=self.id, key=key).first()
 +        if info is not None:
 +            return info.value
          elif default is not None:
              return default
          else:
              raise ValueError(_("information's item not found: {0}").format(
                  key))
  
 +    # ____________________________________________________________
 +    # serialize object
      def _impl_convert_requires(self, descr, load=False):
          """export of the requires during the serialization process
  
          for key, value in state.items():
              setattr(self, key, value)
  
 +    def impl_getname(self):
 +        return self._name
 +
  
  class Option(BaseOption):
      """
  
      Reminder: an Option object is **not** a container for the value.
      """
 -    __slots__ = ('_multi', '_validator', '_default_multi', '_default',
 -                 '_state_callback', '_callback', '_multitype',
 -                 '_consistencies', '_warnings_only', '_master_slaves',
 -                 '_state_consistencies', '__weakref__')
 +#    __slots__ = ('_multi', '_validator', '_default_multi', '_default',
 +#                 '_state_callback', '_callback', '_multitype',
 +#                 '_consistencies', '_warnings_only', '_master_slaves',
 +#                 '_state_consistencies', '__weakref__')
      _empty = ''
  
      def __init__(self, name, doc, default=None, default_multi=None,
                   requires=None, multi=False, callback=None,
                   callback_params=None, validator=None, validator_params=None,
 -                 properties=None, warnings_only=False):
 +                 properties=None, warnings_only=False, choice_values=None,
 +                 choice_open_values=None):
          """
          :param name: the option's name
          :param doc: the option's description
                               Values()._warning contain message
  
          """
 -        super(Option, self).__init__(name, doc, requires, properties)
 -        self._multi = multi
 -        if validator is not None:
 -            validate_callback(validator, validator_params, 'validator')
 -            self._validator = (validator, validator_params)
 -        else:
 -            self._validator = None
 -        if not self._multi and default_multi is not None:
 -            raise ValueError(_("a default_multi is set whereas multi is False"
 -                             " in option: {0}").format(name))
 -        if default_multi is not None:
 -            try:
 -                self._validate(default_multi)
 -            except ValueError as err:
 -                raise ValueError(_("invalid default_multi value {0} "
 -                                   "for option {1}: {2}").format(
 -                                       str(default_multi), name, err))
 -        if callback is not None and (default is not None or
 -                                     default_multi is not None):
 -            raise ValueError(_("default value not allowed if option: {0} "
 -                             "is calculated").format(name))
 -        if callback is None and callback_params is not None:
 -            raise ValueError(_("params defined for a callback function but "
 -                             "no callback defined"
 -                             " yet for option {0}").format(name))
 -        if callback is not None:
 -            validate_callback(callback, callback_params, 'callback')
 -            self._callback = (callback, callback_params)
 -        else:
 -            self._callback = None
 -        if self._multi:
 -            if default is None:
 -                default = []
 -            self._multitype = multitypes.default
 -            self._default_multi = default_multi
 -        self._warnings_only = warnings_only
 -        self.impl_validate(default)
 -        self._default = default
 -        self._consistencies = None
 +        super(Option, self).__init__(name, doc, default, default_multi,
 +                                     requires, multi, callback,
 +                                     callback_params, validator,
 +                                     validator_params, properties,
 +                                     warnings_only, choice_values,
 +                                     choice_open_values)
 +        session.add(self)
 +        session.commit()
 +
 +    #def __setattr__(self, name, value):
 +    #    """set once and only once some attributes in the option,
 +    #    like `_name`. `_name` cannot be changed one the option and
 +    #    pushed in the :class:`tiramisu.option.OptionDescription`.
 +
 +    #    if the attribute `_readonly` is set to `True`, the option is
 +    #    "frozen" (which has noting to do with the high level "freeze"
 +    #    propertie or "read_only" property)
 +    #    """
 +    #    #FIXME ne devrait pas pouvoir redefinir _option
 +    #    if not name == '_option':
 +    #        is_readonly = False
 +    #        # never change _name
 +    #        if name == '_name':
 +    #            try:
 +    #                self._name
 +    #                #so _name is already set
 +    #                is_readonly = True
 +    #            except:
 +    #                pass
 +    #        elif name != '_readonly':
 +    #            try:
 +    #                if self._readonly is True:
 +    #                    is_readonly = True
 +    #            except AttributeError:
 +    #                self._readonly = False
 +    #        if is_readonly:
 +    #            raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
 +    #                                   " read-only").format(
 +    #                                       self.__class__.__name__,
 +    #                                       self._name,
 +    #                                       name))
 +    #    object.__setattr__(self, name, value)
 +
 +    def impl_getproperties(self):
 +        for prop in self._properties:
 +            yield(prop.name)
 +
 +    def impl_getrequires(self):
 +        return self._requires
  
      def _launch_consistency(self, func, option, value, context, index,
                              all_cons_opts):
  
          def val_validator(val):
              if self._validator is not None:
 -                if self._validator[1] is not None:
 -                    validator_params = deepcopy(self._validator[1])
 -                    if '' in validator_params:
 -                        lst = list(validator_params[''])
 -                        lst.insert(0, val)
 -                        validator_params[''] = tuple(lst)
 -                    else:
 -                        validator_params[''] = (val,)
 +                class FakeCallbackParamOption(object):
 +                    def __init__(self, option=None,
 +                                 force_permissive=None,
 +                                 value=None):
 +                        self.option = option
 +                        self.force_permissive = force_permissive
 +                        self.value = value
 +
 +                class FakeCallbackParam(object):
 +                    def __init__(self, key, params):
 +                        self.name = key
 +                        self.params = params
 +                if self._validator_params != []:
 +                    validator_params = []
 +                    validator_params_option = [FakeCallbackParamOption(value=val)]
 +                    #if '' in self._validator_params:
 +                    #    for param in self._validator_params['']:
 +                    #        if isinstance(param, tuple):
 +                    #            validator_params_option.append(FakeCallbackParamOption(option=param[0], force_permissive=param[1]))
 +                    #        else:
 +                    #            validator_params_option.append(FakeCallbackParamOption(value=param))
 +                    validator_params.append(FakeCallbackParam('', validator_params_option))
 +                    for key in self._validator_params:
 +                        if key.name != '':
 +                            validator_params.append(key)
                  else:
 -                    validator_params = {'': (val,)}
 +                    validator_params = (FakeCallbackParam('', (FakeCallbackParamOption(value=val),)),)
                  # Raise ValueError if not valid
-                 carry_out_calculation(self.impl_getname(), config=context,
+                 carry_out_calculation(self, config=context,
 -                                      callback=self._validator[0],
 +                                      callback=self._validator,
                                        callback_params=validator_params)
  
          def do_validation(_value, _index=None):
                  self._validate(_value)
              except ValueError as err:
                  raise ValueError(_('invalid value for option {0}: {1}'
 -                                   '').format(self._name, err))
 +                                   '').format(self.impl_getname(), err))
              try:
                  # valid with self._validator
                  val_validator(_value)
                  self._second_level_validation(_value)
              except ValueError as err:
                  msg = _("invalid value for option {0}: {1}").format(
 -                    self._name, err)
 +                    self.impl_getname(), err)
                  if self._warnings_only:
                      warnings.warn_explicit(ValueWarning(msg, self),
                                             ValueWarning,
              do_validation(value, force_index)
          else:
              if not isinstance(value, list):
-                 raise ValueError(_("which must be a list").format(value,
-                                                                   self.impl_getname()))
 -                raise ValueError(_("invalid value {0} for option {1} which must be a list").format(value, self._name))
++                raise ValueError(_("invalid value {0} for option {1} which must be a list").format(value, self.impl_getname()))
              for index, val in enumerate(value):
                  do_validation(val, index)
  
-     def impl_getdefault(self, default_multi=False):
+     def impl_getdefault(self):
          "accessing the default value"
-         if not default_multi or not self.impl_is_multi():
-             return self._default
-         else:
-             return self.getdefault_multi()
+         return self._default
  
      def impl_getdefault_multi(self):
          "accessing the default value for a multi"
          else:
              return True
  
 -    def impl_getkey(self, value):
 -        return value
 +    def impl_get_callback(self):
 +        return self._callback, self._callback_params
 +
 +    #def impl_getkey(self, value):
 +    #    return value
  
      def impl_is_multi(self):
          return self._multi
          :param other_opts: options used to validate value
          :type other_opts: `list` of `tiramisu.option.Option`
          """
 -        if self._consistencies is None:
 -            self._consistencies = []
          for opt in other_opts:
              if not isinstance(opt, Option):
                  raise ConfigError(_('consistency should be set with an option'))
              else:
                  self._launch_consistency(func, self, value, None,
                                           None, all_cons_opts)
 -        self._consistencies.append((func, all_cons_opts))
 +        _Consistency(func, all_cons_opts)
          self.impl_validate(self.impl_getdefault())
  
      def _cons_not_equal(self, opts, vals):
              for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
                  if val_inf == val_sup is not None:
                      raise ValueError(_("same value for {0} and {1}").format(
 -                        opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name))
 +                        opts[idx_inf].impl_getname(), opts[idx_inf + idx_sup + 1].impl_getname()))
  
      def _impl_convert_callbacks(self, descr, load=False):
 +        #FIXME
          if not load and self._callback is None:
              self._state_callback = None
          elif load and self._state_callback is None:
@@@ -858,7 -627,7 +852,7 @@@ class ChoiceOption(Option)
      The option can also have the value ``None``
      """
  
 -    __slots__ = ('_values', '_open_values')
 +    #__slots__ = ('_values', '_open_values')
      _opt_type = 'string'
  
      def __init__(self, name, doc, values, default=None, default_multi=None,
          """
          if not isinstance(values, tuple):
              raise TypeError(_('values must be a tuple for {0}').format(name))
 -        self._values = values
          if open_values not in (True, False):
              raise TypeError(_('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,
                                             validator=validator,
                                             validator_params=validator_params,
                                             properties=properties,
 -                                           warnings_only=warnings_only)
 +                                           warnings_only=warnings_only,
 +                                           choice_values=values,
 +                                           choice_open_values=open_values)
  
      def impl_get_values(self):
 -        return self._values
 +        return self._choice_values
  
      def impl_is_openvalues(self):
 -        return self._open_values
 +        return self._choice_open_values
  
      def _validate(self, value):
 -        if not self._open_values and not value in self._values:
 +        if not self._choice_open_values and not value in self._choice_values:
              raise ValueError(_('value {0} is not permitted, '
                                 'only {1} is allowed'
 -                               '').format(value, self._values))
 +                               '').format(value, self._choice_values))
  
  
  class BoolOption(Option):
@@@ -940,7 -709,7 +934,7 @@@ class StrOption(Option)
  
  
  if sys.version_info[0] >= 3:
-     #UnicodeOption is same has StrOption in python 3+
+     #UnicodeOption is same as StrOption in python 3+
      class UnicodeOption(StrOption):
          __slots__ = tuple()
          pass
@@@ -957,10 -726,10 +951,10 @@@ else
  
  
  class SymLinkOption(BaseOption):
 -    __slots__ = ('_name', '_opt', '_state_opt')
 +    #__slots__ = ('_name', '_opt', '_state_opt', '_readonly', '_parent')
      _opt_type = 'symlink'
      #not return _opt consistencies
 -    _consistencies = None
 +    #_consistencies = None
  
      def __init__(self, name, opt):
          self._name = name
                                 'for symlink {0}').format(name))
          self._opt = opt
          self._readonly = True
 +        self._parent = None
 +        self._type = self.__class__
 +        session.add(self)
 +        session.commit()
  
      def __getattr__(self, name):
 -        if name in ('_name', '_opt', '_opt_type', '_readonly'):
 +        if name in ('_opt', '_opt_type', '_readonly', 'impl_getname'):
              return object.__getattr__(self, name)
          else:
              return getattr(self._opt, name)
          del(self._state_opt)
          super(SymLinkOption, self)._impl_setstate(descr)
  
 +    def impl_getname(self):
 +        return self._name
 +
 +    def impl_get_information(self, key, default=None):
 +        #FIXME ne devrait pas etre util si ?
 +        return self._opt.impl_get_information(key, default)
 +
  
  class IPOption(Option):
      "represents the choice of an ip"
                                         warnings_only=warnings_only)
  
      def _validate(self, value):
+         # sometimes an ip term starts with a zero
+         # but this does not fit in some case, for example bind does not like it
+         for val in value.split('.'):
+             if val.startswith("0") and len(val) > 1:
+                 raise ValueError(_('invalid IP'))
+         # 'standard' validation
          try:
              IP('{0}/32'.format(value))
          except ValueError:
@@@ -1174,7 -938,7 +1174,7 @@@ class NetmaskOption(Option)
              else:
                  msg = _('invalid network {0} ({1}) with netmask {2}')
          if msg is not None:
 -            raise ValueError(msg.format(val_ipnetwork, opts[1]._name,
 +            raise ValueError(msg.format(val_ipnetwork, opts[1].impl_getname(),
                                          val_netmask))
  
  
@@@ -1197,8 -961,8 +1197,8 @@@ class BroadcastOption(Option)
          if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast):
              raise ValueError(_('invalid broadcast {0} ({1}) with network {2} '
                                 '({3}) and netmask {4} ({5})').format(
 -                                   broadcast, opts[0]._name, network,
 -                                   opts[1]._name, netmask, opts[2]._name))
 +                                   broadcast, opts[0].impl_getname(), network,
 +                                   opts[1].impl_getname(), netmask, opts[2].impl_getname()))
  
  
  class DomainnameOption(Option):
      domainname:
      fqdn: with tld, not supported yet
      """
 -    __slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re')
 +    __slots__ = ('_dom_type', '_allow_ip', '_allow_without_dot', '_domain_re')
      _opt_type = 'domainname'
  
      def __init__(self, name, doc, default=None, default_multi=None,
                   warnings_only=False, allow_without_dot=False):
          if type_ not in ['netbios', 'hostname', 'domainname']:
              raise ValueError(_('unknown type_ {0} for hostname').format(type_))
 -        self._type = type_
 +        self._dom_type = type_
          if allow_ip not in [True, False]:
              raise ValueError(_('allow_ip must be a boolean'))
          if allow_without_dot not in [True, False]:
          end = ''
          extrachar = ''
          extrachar_mandatory = ''
 -        if self._type == 'netbios':
 +        if self._dom_type == 'netbios':
              length = 14
 -        elif self._type == 'hostname':
 +        elif self._dom_type == 'hostname':
              length = 62
 -        elif self._type == 'domainname':
 +        elif self._dom_type == 'domainname':
              length = 62
              if allow_without_dot is False:
                  extrachar_mandatory = '\.'
                  return
              except ValueError:
                  pass
 -        if self._type == 'domainname' and not self._allow_without_dot and \
 +        if self._dom_type == 'domainname' and not self._allow_without_dot and \
                  '.' not in value:
              raise ValueError(_("invalid domainname, must have dot"))
              if len(value) > 255:
@@@ -1339,11 -1103,11 +1339,11 @@@ class OptionDescription(BaseOption)
      """Config's schema (organisation, group) and container of Options
      The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
      """
 -    __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
 -                 '_state_group_type', '_properties', '_children',
 -                 '_cache_consistencies', '_calc_properties', '__weakref__',
 -                 '_readonly', '_impl_informations', '_state_requires',
 -                 '_stated', '_state_readonly')
 +    #_slots = ('_name', '_requires', '_cache_paths', '_group_type',
 +    #          '_state_group_type', '_properties', '_children',
 +    #          '_cache_consistencies', '_calc_properties', '__weakref__',
 +    #          '_readonly', '_impl_informations', '_state_requires',
 +    #          '_stated', '_state_readonly')
      _opt_type = 'optiondescription'
  
      def __init__(self, name, doc, children, requires=None, properties=None):
          :param children: a list of options (including optiondescriptions)
  
          """
 -        super(OptionDescription, self).__init__(name, doc, requires, properties)
 -        child_names = [child._name for child in children]
 +        super(OptionDescription, self).__init__(name, doc=doc, requires=requires, properties=properties)
 +        session.add(self)
 +        session.commit()
 +        child_names = [child.impl_getname() for child in children]
          #better performance like this
          valid_child = copy(child_names)
          valid_child.sort()
                  raise ConflictError(_('duplicate option name: '
                                        '{0}').format(child))
              old = child
 -        self._children = (tuple(child_names), tuple(children))
 +        for child in children:
 +            if child._parent is not None:
 +                raise ConflictError(_('duplicate option: '
 +                                      '{0}').format(child))
 +            self._children.append(child)  # = (tuple(child_names), tuple(children))
          self._cache_paths = None
          self._cache_consistencies = None
          # the group_type is useful for filtering OptionDescriptions in a config
 -        self._group_type = groups.default
 +        self._optiondescription_group_type = groups.default
 +
 +    def impl_getproperties(self):
 +        #FIXME
 +        for prop in self._properties:
 +            yield(prop.name)
 +
 +    def impl_getrequires(self):
 +        #FIXME
 +        return self._requires
  
      def impl_getdoc(self):
          return self.impl_get_information('doc')
  
 +    def impl_validate(self, *args):
 +        #FIXME a voir ...
 +        pass
 +
      def __getattr__(self, name):
 -        if name in self.__slots__:
 -            return object.__getattribute__(self, name)
          try:
 -            return self._children[1][self._children[0].index(name)]
 +            if name.startswith('_') or name.startswith('impl_'):
 +                return object.__getattribute__(self, name)
 +            else:
 +                #FIXME regression ...
 +                for child in self._children:
 +                    if child.impl_getname() == name:
 +                        #convert to object
 +                        return session.query(child._type).filter_by(id=child.id).first()
 +                #return pouet#self._children[1][self._children[0].index(name)]
          except ValueError:
 -            raise AttributeError(_('unknown Option {0} '
 -                                   'in OptionDescription {1}'
 -                                   '').format(name, self._name))
 +            pass
 +        raise AttributeError(_('unknown Option {0} '
 +                               'in OptionDescription {1}'
 +                               '').format(name, self.impl_getname()))
  
 -    def impl_getkey(self, config):
 -        return tuple([child.impl_getkey(getattr(config, child._name))
 -                      for child in self.impl_getchildren()])
 +    #def impl_getkey(self, config):
 +    #    return tuple([child.impl_getkey(getattr(config, child.impl_getname()))
 +    #                  for child in self.impl_getchildren()])
  
      def impl_getpaths(self, include_groups=False, _currpath=None):
          """returns a list of all paths in self, recursively
              _currpath = []
          paths = []
          for option in self.impl_getchildren():
 -            attr = option._name
 +            attr = option.impl_getname()
              if isinstance(option, OptionDescription):
                  if include_groups:
                      paths.append('.'.join(_currpath + [attr]))
          return paths
  
      def impl_getchildren(self):
 -        return self._children[1]
 +        for child in self._children:
 +            yield(session.query(child._type).filter_by(id=child.id).first())
  
      def impl_build_cache(self,
                           cache_path=None,
 +                         cache_type=None,
                           cache_option=None,
                           _currpath=None,
                           _consistencies=None,
              save = False
          if cache_path is None:
              cache_path = []
 +            cache_type = []
              cache_option = []
          for option in self.impl_getchildren():
 -            attr = option._name
 -            if option in cache_option:
 +            attr = option.impl_getname()
 +            if option.id is None:
 +                raise SystemError(_("an option's id should not be None "
 +                                    "for {0}").format(option.impl_getname()))
 +            if option.id in cache_option:
                  raise ConflictError(_('duplicate option: {0}').format(option))
 -
 -            cache_option.append(option)
 +            cache_option.append(option.id)
              if not force_no_consistencies:
                  option._readonly = True
              cache_path.append(str('.'.join(_currpath + [attr])))
 +            cache_type.append(option._type)
              if not isinstance(option, OptionDescription):
                  if not force_no_consistencies and \
 -                        option._consistencies is not None:
 +                        option._consistencies is not []:
                      for consistency in option._consistencies:
 -                        func, all_cons_opts = consistency
 +                        func = consistency.func
 +                        all_cons_opts = consistency.options
                          for opt in all_cons_opts:
                              _consistencies.setdefault(opt,
                                                        []).append((func,
                                                                    all_cons_opts))
              else:
                  _currpath.append(attr)
 -                option.impl_build_cache(cache_path,
 +                option.impl_build_cache(cache_path, cache_type,
                                          cache_option,
                                          _currpath,
                                          _consistencies,
                                          force_no_consistencies)
                  _currpath.pop()
          if save:
 -            self._cache_paths = (tuple(cache_option), tuple(cache_path))
 +            self._cache_paths = (tuple(cache_option), tuple(cache_path), tuple(cache_type))
              if not force_no_consistencies:
                  if _consistencies != {}:
                      self._cache_consistencies = {}
                      for opt, cons in _consistencies.items():
 -                        if opt not in cache_option:
 -                            raise ConfigError(_('consistency with option {0} which is not in Config').format(opt._name))
 +                        if opt.id not in cache_option:
 +                            raise ConfigError(_('consistency with option {0} which is not in Config').format(opt.impl_getname()))
                          self._cache_consistencies[opt] = tuple(cons)
                  self._readonly = True
  
      def impl_get_opt_by_path(self, path):
          try:
 -            return self._cache_paths[0][self._cache_paths[1].index(path)]
 +            idx = self._cache_paths[1].index(path)
 +            opt_id = self._cache_paths[0][idx]
 +            opt_type = self._cache_paths[2][idx]
 +            return session.query(opt_type).filter_by(id=opt_id).first()
          except ValueError:
              raise AttributeError(_('no option for path {0}').format(path))
  
 +    def impl_get_opt_by_id(self, opt_id):
 +        try:
 +            idx = self._cache_paths[0].index(opt_id)
 +            opt_type = self._cache_paths[2][idx]
 +            return session.query(opt_type).filter_by(id=opt_id).first()
 +        except ValueError:
 +            raise AttributeError(_('no id {0} found').format(opt_id))
 +
      def impl_get_path_by_opt(self, opt):
          try:
 -            return self._cache_paths[1][self._cache_paths[0].index(opt)]
 +            return self._cache_paths[1][self._cache_paths[0].index(opt.id)]
          except ValueError:
              raise AttributeError(_('no option {0} found').format(opt))
  
          :param group_type: an instance of `GroupType` or `MasterGroupType`
                                that lives in `setting.groups`
          """
 -        if self._group_type != groups.default:
 +        if self._optiondescription_group_type != groups.default:
              raise TypeError(_('cannot change group_type if already set '
 -                            '(old {0}, new {1})').format(self._group_type,
 +                            '(old {0}, new {1})').format(self._optiondescription_group_type,
                                                           group_type))
          if isinstance(group_type, groups.GroupType):
 -            self._group_type = group_type
 +            self._optiondescription_group_type = group_type
              if isinstance(group_type, groups.MasterGroupType):
                  #if master (same name has group) is set
-                 identical_master_child_name = False
                  #for collect all slaves
                  slaves = []
                  master = None
                  for child in self.impl_getchildren():
                      if isinstance(child, OptionDescription):
                          raise ValueError(_("master group {0} shall not have "
 -                                         "a subgroup").format(self._name))
 +                                         "a subgroup").format(self.impl_getname()))
                      if isinstance(child, SymLinkOption):
                          raise ValueError(_("master group {0} shall not have "
 -                                         "a symlinkoption").format(self._name))
 +                                         "a symlinkoption").format(self.impl_getname()))
                      if not child.impl_is_multi():
                          raise ValueError(_("not allowed option {0} "
                                           "in group {1}"
                                           ": this option is not a multi"
 -                                         "").format(child._name, self._name))
 -                    if child._name == self._name:
 +                                         "").format(child.impl_getname(), self.impl_getname()))
-                     if child.impl_getname() == self.impl_getname():
-                         identical_master_child_name = True
++                    if child._name == self.impl_getname():
                          child._multitype = multitypes.master
                          master = child
                      else:
                  if master is None:
                      raise ValueError(_('master group with wrong'
                                         ' master name for {0}'
 -                                       ).format(self._name))
 +                                       ).format(self.impl_getname()))
+                 if master._callback is not None and master._callback[1] is not None:
+                     for key, callbacks in master._callback[1].items():
+                         for callbk in callbacks:
+                             if isinstance(callbk, tuple):
+                                 if callbk[0] in slaves:
+                                     raise ValueError(_("callback of master's option shall "
+                                                        "not refered a slave's ones"))
                  master._master_slaves = tuple(slaves)
                  for child in self.impl_getchildren():
                      if child != master:
                          child._master_slaves = master
                          child._multitype = multitypes.slave
-                 if not identical_master_child_name:
-                     raise ValueError(_("no child has same nom has master group"
-                                        " for: {0}").format(self.impl_getname()))
          else:
              raise ValueError(_('group_type: {0}'
                                 ' not allowed').format(group_type))
  
      def impl_get_group_type(self):
 -        return self._group_type
 +        return getattr(groups, self._optiondescription_group_type)
  
      def _valid_consistency(self, option, value, context, index):
          if self._cache_consistencies is None:
                      return False
          return True
  
 +    # ____________________________________________________________
 +    # serialize object
 +
      def _impl_getstate(self, descr=None):
          """enables us to export into a dict
          :param descr: parent :class:`tiramisu.option.OptionDescription`
              self.impl_build_cache()
              descr = self
          super(OptionDescription, self)._impl_getstate(descr)
 -        self._state_group_type = str(self._group_type)
 +        self._state_group_type = str(self._optiondescription_group_type)
          for option in self.impl_getchildren():
              option._impl_getstate(descr)
  
              self._cache_consistencies = None
              self.impl_build_cache(force_no_consistencies=True)
              descr = self
 -        self._group_type = getattr(groups, self._state_group_type)
 +        self._optiondescription_group_type = getattr(groups, self._state_group_type)
          del(self._state_group_type)
          super(OptionDescription, self)._impl_setstate(descr)
          for option in self.impl_getchildren():
@@@ -1655,7 -1374,7 +1657,7 @@@ def validate_requires_arg(requires, nam
                       the description of the requires dictionary
      """
      if requires is None:
 -        return None, None
 +        return None
      ret_requires = {}
      config_action = {}
  
                                              inverse, transitive, same_action)
          else:
              ret_requires[action][option][1].append(expected)
 -    # transform dict to tuple
 -    ret = []
 -    for opt_requires in ret_requires.values():
 -        ret_action = []
 -        for require in opt_requires.values():
 -            ret_action.append((require[0], tuple(require[1]), require[2],
 -                               require[3], require[4], require[5]))
 -        ret.append(tuple(ret_action))
 -    return frozenset(config_action.keys()), tuple(ret)
 +    ## transform dict to tuple
 +    #ret = []
 +    #for opt_requires in ret_requires.values():
 +    #    ret_action = []
 +    #    for require in opt_requires.values():
 +    #        ret_action.append((require[0], tuple(require[1]), require[2],
 +    #                           require[3], require[4], require[5]))
 +    #    ret.append(tuple(ret_action))
 +    return ret_requires
  
  
  def validate_callback(callback, callback_params, type_):
                                             ' not a {0} for second argument'
                                             ).format(type_, type(
                                                 force_permissive)))
 +
 +#FIXME
 +Base.metadata.create_all(engine)
 +Session = sessionmaker(bind=engine)
 +session = Session()
diff --combined tiramisu/setting.py
@@@ -243,6 -243,12 +243,12 @@@ populate_multitypes(
  
  
  # ____________________________________________________________
+ class Undefined():
+     pass
+ undefined = Undefined()
+ # ____________________________________________________________
  class Property(object):
      "a property is responsible of the option's value access rules"
      __slots__ = ('_setting', '_properties', '_opt', '_path')
          self._properties = prop
  
      def append(self, propname):
 -        if self._opt is not None and self._opt._calc_properties is not None \
 -                and propname in self._opt._calc_properties:
 +        if self._opt is not None and self._opt.impl_getrequires() is not None \
 +                and propname in self._opt.impl_getrequires():
              raise ValueError(_('cannot append {0} property for option {1}: '
                                 'this property is calculated').format(
 -                                   propname, self._opt._name))
 +                                   propname, self._opt.impl_getname()))
          self._properties.add(propname)
          self._setting._setproperties(self._properties, self._opt, self._path)
  
@@@ -343,8 -349,7 +349,8 @@@ class Settings(object)
                  is_cached, props = self._p_.getcache(path, ntime)
                  if is_cached:
                      return props
 -            props = self._p_.getproperties(path, opt._properties)
 +            #FIXME
 +            props = self._p_.getproperties(path, opt.impl_getproperties())
              if is_apply_req:
                  props |= self.apply_requires(opt, path)
              if 'cache' in self:
          if opt is None:
              self._p_.setproperties(None, properties)
          else:
 -            if opt._calc_properties is not None:
 -                properties -= opt._calc_properties
 -            if set(opt._properties) == properties:
 -                self._p_.reset_properties(path)
 -            else:
 -                self._p_.setproperties(path, properties)
 +            #if opt._calc_properties is not None:
 +            #    properties -= opt._calc_properties
 +            #FIXME a revoir ---------
 +            #if set(opt._properties) == properties:
 +            #    self._p_.reset_properties(path)
 +            #else:
 +            #    self._p_.setproperties(path, properties)
 +            self._p_.setproperties(path, properties)
 +            #FIXME fin revoir -----
          self.context().cfgimpl_reset_cache()
  
      #____________________________________________________________
          """
          # opt properties
          properties = copy(self._getproperties(opt_or_descr, path))
+         self_properties = copy(self._getproperties())
          # remove opt permissive
-         properties -= self._p_.getpermissive(path)
+         if force_permissive is True or 'permissive' in self_properties:
+             properties -= self._p_.getpermissive(path)
          # remove global permissive if need
-         self_properties = copy(self._getproperties())
          if force_permissive is True or 'permissive' in self_properties:
              properties -= self._p_.getpermissive()
          if force_permissives is not None:
                  raise PropertiesOptionError(_('cannot change the value for '
                                                'option {0} this option is'
                                                ' frozen').format(
 -                                                  opt_or_descr._name),
 +                                                  opt_or_descr.impl_getname()),
                                              props)
              else:
                  raise PropertiesOptionError(_("trying to access to an option "
                                                "named: {0} with properties {1}"
 -                                              "").format(opt_or_descr._name,
 +                                              "").format(opt_or_descr.impl_getname(),
                                                           str(props)), props)
  
      def setpermissive(self, permissive, opt=None, path=None):
          :param path: the option's path in the config
          :type path: str
          """
 -        if opt._requires is None:
 +        if opt.impl_getrequires() is None:
              return frozenset()
  
          # filters the callbacks
          calc_properties = set()
 -        for requires in opt._requires:
 -            for require in requires:
 -                option, expected, action, inverse, \
 -                    transitive, same_action = require
 -                reqpath = self._get_path_by_opt(option)
 -                if reqpath == path or reqpath.startswith(path + '.'):
 -                    raise RequirementError(_("malformed requirements "
 -                                             "imbrication detected for option:"
 -                                             " '{0}' with requirement on: "
 -                                             "'{1}'").format(path, reqpath))
 -                try:
 -                    value = self.context()._getattr(reqpath,
 -                                                    force_permissive=True)
 -                except PropertiesOptionError as err:
 -                    if not transitive:
 -                        continue
 -                    properties = err.proptype
 -                    if same_action and action not in properties:
 -                        raise RequirementError(_("option '{0}' has "
 -                                                 "requirement's property "
 -                                                 "error: "
 -                                                 "{1} {2}").format(opt._name,
 -                                                                   reqpath,
 -                                                                   properties))
 -                    # transitive action, force expected
 -                    value = expected[0]
 -                    inverse = False
 -                if (not inverse and
 -                        value in expected or
 -                        inverse and value not in expected):
 -                    calc_properties.add(action)
 -                    # the calculation cannot be carried out
 -                    break
 +        for require in opt.impl_getrequires():
 +            expected = tuple(require.get_expected())
 +            inverse = require.inverse
 +            option = require.get_option(self.context())
 +            reqpath = self._get_path_by_opt(option)
 +            if reqpath == path or reqpath.startswith(path + '.'):
 +                raise RequirementError(_("malformed requirements "
 +                                         "imbrication detected for option:"
 +                                         " '{0}' with requirement on: "
 +                                         "'{1}'").format(path, reqpath))
 +            try:
 +                value = self.context()._getattr(reqpath,
 +                                                force_permissive=True)
 +            except PropertiesOptionError as err:
 +                if not require.transitive:
 +                    continue
 +                properties = err.proptype
 +                if require.same_action and require.action not in properties:
 +                    raise RequirementError(_("option '{0}' has "
 +                                             "requirement's property "
 +                                             "error: "
 +                                             "{1} {2}").format(opt.impl_getname(),
 +                                                               reqpath,
 +                                                               properties))
 +                # transitive action, force expected
 +                value = expected[0]
 +                inverse = False
 +            if not inverse and value in expected or \
 +                    inverse and value not in expected:
 +                calc_properties.add(require.action)
 +                # the calculation cannot be carried out
 +                #break
          return calc_properties
  
      def _get_path_by_opt(self, opt):
diff --combined tiramisu/value.py
@@@ -22,7 -22,7 +22,7 @@@ from copy import cop
  import sys
  import weakref
  from tiramisu.error import ConfigError, SlaveError
- from tiramisu.setting import owners, multitypes, expires_time
+ from tiramisu.setting import owners, multitypes, expires_time, undefined
  from tiramisu.autolib import carry_out_calculation
  from tiramisu.i18n import _
  from tiramisu.option import SymLinkOption
@@@ -134,10 -134,10 +134,10 @@@ class Values(object)
          :type index: int
          :returns: a calculated value
          """
 -        callback, callback_params = opt._callback
 +        callback, callback_params = opt.impl_get_callback()
          if callback_params is None:
              callback_params = {}
-         return carry_out_calculation(opt.impl_getname(), config=self.context(),
+         return carry_out_calculation(opt, config=self.context(),
                                       callback=callback,
                                       callback_params=callback_params,
                                       index=index, max_len=max_len)
@@@ -415,7 -415,7 +415,7 @@@ class Multi(list)
                                      is_default_owner):
              raise SlaveError(_("invalid len for the slave: {0}"
                                 " which has {1} as master").format(
 -                                   self.opt._name, masterp))
 +                                   self.opt.impl_getname(), masterp))
          elif valuelen < masterlen:
              for num in range(0, masterlen - valuelen):
                  if self.opt.impl_has_callback():
                      raise SlaveError(_("invalid len for the master: {0}"
                                         " which has {1} as slave with"
                                         " greater len").format(
 -                                           self.opt._name, slave._name))
 +                                           self.opt.impl_getname(), slave.impl_getname()))
                  elif len(value_slave) < masterlen:
                      for num in range(0, masterlen - len(value_slave)):
                          if slave.impl_has_callback():
                                  values._getcallback_value(slave, index=index),
                                  force=True)
                          else:
-                             value_slave.append(slave.impl_getdefault_multi(),
-                                                force=True)
+                             value_slave.append(undefined, force=True)
  
      def __setitem__(self, index, value):
          self._validate(value, index)
          super(Multi, self).__setitem__(index, value)
          self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
  
-     def append(self, value, force=False):
+     def append(self, value=undefined, force=False):
          """the list value can be updated (appened)
          only if the option is a master
          """
          if not force:
              if self.opt.impl_get_multitype() == multitypes.slave:
                  raise SlaveError(_("cannot append a value on a multi option {0}"
 -                                   " which is a slave").format(self.opt._name))
 +                                   " which is a slave").format(self.opt.impl_getname()))
              elif self.opt.impl_get_multitype() == multitypes.master:
                  values = self.context().cfgimpl_get_values()
-                 if value is None and self.opt.impl_has_callback():
+                 if value is undefined and self.opt.impl_has_callback():
                      value = values._getcallback_value(self.opt)
                      #Force None il return a list
                      if isinstance(value, list):
                          value = None
          index = self.__len__()
+         if value is undefined:
+             value = self.opt.impl_getdefault_multi()
          self._validate(value, index)
          super(Multi, self).append(value)
          self.context().cfgimpl_get_values()._setvalue(self.opt, self.path,
          if self.opt.impl_get_multitype() in [multitypes.slave,
                                               multitypes.master]:
              raise SlaveError(_("cannot sort multi option {0} if master or slave"
 -                               "").format(self.opt._name))
 +                               "").format(self.opt.impl_getname()))
          if sys.version_info[0] >= 3:
              if cmp is not None:
                  raise ValueError(_('cmp is not permitted in python v3 or greater'))
          if self.opt.impl_get_multitype() in [multitypes.slave,
                                               multitypes.master]:
              raise SlaveError(_("cannot reverse multi option {0} if master or "
 -                               "slave").format(self.opt._name))
 +                               "slave").format(self.opt.impl_getname()))
          super(Multi, self).reverse()
          self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
  
          if self.opt.impl_get_multitype() in [multitypes.slave,
                                               multitypes.master]:
              raise SlaveError(_("cannot insert multi option {0} if master or "
 -                               "slave").format(self.opt._name))
 +                               "slave").format(self.opt.impl_getname()))
          super(Multi, self).insert(index, obj)
          self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
  
          if self.opt.impl_get_multitype() in [multitypes.slave,
                                               multitypes.master]:
              raise SlaveError(_("cannot extend multi option {0} if master or "
 -                               "slave").format(self.opt._name))
 +                               "slave").format(self.opt.impl_getname()))
          super(Multi, self).extend(iterable)
          self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
  
                  raise ValueError(_("invalid value {0} "
                                     "for option {1}: {2}"
                                     "").format(str(value),
 -                                              self.opt._name, err))
 +                                              self.opt.impl_getname(), err))
  
      def pop(self, index, force=False):
          """the list value can be updated (poped)
          if not force:
              if self.opt.impl_get_multitype() == multitypes.slave:
                  raise SlaveError(_("cannot pop a value on a multi option {0}"
 -                                   " which is a slave").format(self.opt._name))
 +                                   " which is a slave").format(self.opt.impl_getname()))
              elif self.opt.impl_get_multitype() == multitypes.master:
                  for slave in self.opt.impl_get_master_slaves():
                      values = self.context().cfgimpl_get_values()