* config herite from BaseInformation class
authorEmmanuel Garette <egarette@cadoles.com>
Wed, 8 May 2013 16:14:42 +0000 (18:14 +0200)
committerEmmanuel Garette <egarette@cadoles.com>
Wed, 8 May 2013 16:14:42 +0000 (18:14 +0200)
* _cfgimpl_ => _impl_
* optimpl_ => impl_
* properties/permissives are now set/frozenset
* validation raise ValueError if not valid, didn't return anything otherwise
* consistencies are now validate in setting and when deleting value
* ip/network with netmask consistency now works
* DomainnameOption now works
* if no validation, don't set cache for value
* symlinkoption: remove path (not used)

16 files changed:
test/test_config.py
test/test_config_domain.py [new file with mode: 0644]
test/test_config_ip.py [new file with mode: 0644]
test/test_metaconfig.py
test/test_option_calculation.py [new file with mode: 0644]
test/test_option_consistency.py
test/test_option_default.py
test/test_option_setting.py
test/test_option_type.py
test/test_option_with_special_name.py
test/test_parsing_group.py
test/test_slots.py [new file with mode: 0644]
tiramisu/config.py
tiramisu/option.py
tiramisu/setting.py
tiramisu/value.py

index 819c606..6c6f91d 100644 (file)
@@ -104,3 +104,11 @@ def test_cfgimpl_get_home_by_path():
     assert config.cfgimpl_get_home_by_path('dummy')[1] == 'dummy'
     #assert config.getpaths(include_groups=False) == ['gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
     #assert config.getpaths(include_groups=True) == ['gc', 'gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
+
+
+def test_information_config():
+    descr = make_description()
+    config = Config(descr)
+    string = 'some informations'
+    config.impl_set_information('info', string)
+    assert config.impl_get_information('info') == string
diff --git a/test/test_config_domain.py b/test/test_config_domain.py
new file mode 100644 (file)
index 0000000..ab6e00e
--- /dev/null
@@ -0,0 +1,37 @@
+import autopath
+from py.test import raises
+
+from tiramisu.config import Config
+from tiramisu.option import DomainnameOption, OptionDescription
+
+
+def test_domainname():
+    d = DomainnameOption('d', '')
+    od = OptionDescription('a', '', [d])
+    c = Config(od)
+    c.d = 'toto.com'
+    raises(ValueError, "c.d = 'toto'")
+    c.d = 'toto3.com'
+    c.d = 'toto3.3la'
+    raises(ValueError, "c.d = '3toto.com'")
+    c.d = 'toto.co3'
+    raises(ValueError, "c.d = 'toto_super.com'")
+    c.d = 'toto-.com'
+
+
+def test_domainname_netbios():
+    d = DomainnameOption('d', '', type_='netbios')
+    od = OptionDescription('a', '', [d])
+    c = Config(od)
+    raises(ValueError, "c.d = 'toto.com'")
+    c.d = 'toto'
+    raises(ValueError, "c.d = 'domainnametoolong'")
+
+
+def test_domainname_hostname():
+    d = DomainnameOption('d', '', type_='hostname')
+    od = OptionDescription('a', '', [d])
+    c = Config(od)
+    raises(ValueError, "c.d = 'toto.com'")
+    c.d = 'toto'
+    c.d = 'domainnametoolong'
diff --git a/test/test_config_ip.py b/test/test_config_ip.py
new file mode 100644 (file)
index 0000000..7512384
--- /dev/null
@@ -0,0 +1,45 @@
+import autopath
+from py.test import raises
+
+from tiramisu.setting import owners
+from tiramisu.config import Config
+from tiramisu.option import IPOption, NetworkOption, NetmaskOption, \
+    OptionDescription
+
+
+def test_ip():
+    a = IPOption('a', '')
+    b = IPOption('b', '', only_private=True)
+    od = OptionDescription('od', '', [a, b])
+    c = Config(od)
+    c.a = '192.168.1.1'
+    c.a = '192.168.1.0'
+    c.a = '88.88.88.88'
+    c.a = '0.0.0.0'
+    assert(ValueError, "c.a = '255.255.255.0'")
+    c.b = '192.168.1.1'
+    c.b = '192.168.1.0'
+    assert(ValueError, "c.b = '88.88.88.88'")
+    c.b = '0.0.0.0'
+    assert(ValueError, "c.b = '255.255.255.0'")
+
+
+def test_network():
+    a = NetworkOption('a', '')
+    od = OptionDescription('od', '', [a])
+    c = Config(od)
+    c.a = '192.168.1.1'
+    c.a = '192.168.1.0'
+    c.a = '88.88.88.88'
+    c.a = '0.0.0.0'
+    assert(ValueError, "c.a = '255.255.255.0'")
+
+def test_netmask():
+    a = NetmaskOption('a', '')
+    od = OptionDescription('od', '', [a])
+    c = Config(od)
+    assert(ValueError, "c.a = '192.168.1.1'")
+    assert(ValueError, "c.a = '192.168.1.0'")
+    assert(ValueError, "c.a = '88.88.88.88'")
+    c.a = '0.0.0.0'
+    c.a = '255.255.255.0'
index 3e2c924..f20f4dd 100644 (file)
@@ -29,7 +29,7 @@ def make_description():
 #FIXME ne pas mettre 2 OD differents dans un meta
 def test_none():
     meta = make_description()
-    conf1, conf2 = meta._cfgimpl_children
+    conf1, conf2 = meta._impl_children
     assert conf1.od1.i3 is conf2.od1.i3 is None
     assert conf1.getowner('od1.i3') is conf2.getowner('od1.i3') is owners.default
     meta.od1.i3 = 3
@@ -58,7 +58,7 @@ def test_none():
 
 def test_default():
     meta = make_description()
-    conf1, conf2 = meta._cfgimpl_children
+    conf1, conf2 = meta._impl_children
     assert conf1.od1.i2 == conf2.od1.i2 == 1
     assert conf1.getowner('od1.i2') is conf2.getowner('od1.i2') is owners.default
     meta.od1.i2 = 3
@@ -87,7 +87,7 @@ def test_default():
 
 def test_contexts():
     meta = make_description()
-    conf1, conf2 = meta._cfgimpl_children
+    conf1, conf2 = meta._impl_children
     assert conf1.od1.i2 == conf2.od1.i2 == 1
     assert conf1.getowner('od1.i2') is conf2.getowner('od1.i2') is owners.default
     meta.set_contexts('od1.i2', 6)
@@ -108,7 +108,7 @@ def test_meta_meta():
     meta1 = make_description()
     meta2 = MetaConfig([meta1])
     meta2.cfgimpl_get_settings().setowner(owners.meta)
-    conf1, conf2 = meta1._cfgimpl_children
+    conf1, conf2 = meta1._impl_children
     assert conf1.od1.i2 == conf2.od1.i2 == 1
     assert conf1.getowner('od1.i2') is conf2.getowner('od1.i2') is owners.default
     meta2.od1.i2 = 3
@@ -142,7 +142,7 @@ def test_meta_meta_set():
     meta1 = make_description()
     meta2 = MetaConfig([meta1])
     meta2.cfgimpl_get_settings().setowner(owners.meta)
-    conf1, conf2 = meta1._cfgimpl_children
+    conf1, conf2 = meta1._impl_children
     meta2.set_contexts('od1.i1', 7)
     assert conf1.od1.i1 == conf2.od1.i1 == 7
     assert conf1.getowner('od1.i1') is conf2.getowner('od1.i1') is owners.user
@@ -160,7 +160,7 @@ def test_not_meta():
     conf2 = Config(od2)
     meta = MetaConfig([conf1, conf2], False)
     raises(ConfigError, 'meta.od1.i1')
-    conf1, conf2 = meta._cfgimpl_children
+    conf1, conf2 = meta._impl_children
     meta.set_contexts('od1.i1', 7)
     assert conf1.od1.i1 == conf2.od1.i1 == 7
     assert conf1.getowner('od1.i1') is conf2.getowner('od1.i1') is owners.user
diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py
new file mode 100644 (file)
index 0000000..0f76c2f
--- /dev/null
@@ -0,0 +1,271 @@
+import autopath
+from py.test import raises
+
+from tiramisu.config import Config
+from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
+    StrOption, IPOption, OptionDescription
+from tiramisu.error import PropertiesOptionError, ConflictError
+
+
+def make_description():
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                  ('std', 'thunk'), 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    intoption2 = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                requires=((boolop, True, 'hidden'),))
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=((boolop, True, 'hidden'),))
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption, intoption2])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                              wantref_option, stroption,
+                              wantframework_option,
+                              intoption, boolop])
+    return descr
+
+
+def make_description_duplicates():
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
+    ## dummy 1
+    gcdummy = BoolOption('dummy', 'dummy', default=False)
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                  ('std', 'thunk'), 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                requires=((boolop, True, 'hidden'),))
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=((boolop, True, 'hidden'),))
+    # dummy2 (same path)
+    gcdummy2 = BoolOption('dummy', 'dummy2', default=True)
+    # dummy3 (same name)
+    gcdummy3 = BoolOption('dummy', 'dummy2', default=True)
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, gcdummy2, floatoption])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                              wantref_option, stroption,
+                              wantframework_option,
+                              intoption, boolop, gcdummy3])
+    return descr
+
+
+def test_identical_paths():
+    """If in the schema (the option description) there is something that
+    have the same name, an exection is raised
+    """
+    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=((boolop, True, 'hidden'),))
+    # second multi
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=((boolop, True, '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)
+
+    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",
+                          requires=((intoption, 1, 'hidden'),))
+
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                              stroption, intoption])
+    return descr
+
+
+def test_hidden_if_in():
+    descr = make_description_requires()
+    cfg = Config(descr)
+    setting = cfg.cfgimpl_get_settings()
+    cfg.read_write()
+    stroption = cfg.unwrap_from_path('str')
+    assert not 'hidden' in setting[stroption]
+    cfg.int = 1
+    raises(PropertiesOptionError, "cfg.str")
+    raises(PropertiesOptionError, 'cfg.str="uvw"')
+    assert 'hidden' in setting[stroption]
+
+
+def test_hidden_if_in_with_group():
+    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")
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption],
+                                requires=((intoption, 1, 'hidden'),))
+    descr = OptionDescription('constraints', '', [gcgroup, booloption,
+                              objspaceoption, stroption, intoption])
+    cfg = Config(descr)
+    setting = cfg.cfgimpl_get_settings()
+    cfg.read_write()
+    assert not 'hidden' in setting[stroption]
+    cfg.int = 1
+    raises(PropertiesOptionError, "cfg.gc.name")
+
+
+def test_disabled_with_group():
+    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")
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption],
+                                requires=((intoption, 1, 'disabled'),))
+    descr = OptionDescription('constraints', '', [gcgroup, booloption,
+                              objspaceoption, stroption, intoption])
+    cfg = Config(descr)
+    cfg.read_write()
+    assert cfg.gc.name
+    cfg.int = 1
+    raises(PropertiesOptionError, "cfg.gc.name")
+#____________________________________________________________
+
+
+def make_description_callback():
+    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
+    gcdummy = BoolOption('dummy', 'dummy')
+    objspaceoption = ChoiceOption('objspace', 'Object space',
+                                  ('std', 'thunk'), 'std')
+    booloption = BoolOption('bool', 'Test boolean option', default=True)
+    intoption = IntOption('int', 'Test int option', default=0)
+    floatoption = FloatOption('float', 'Test float option', default=2.3)
+    stroption = StrOption('str', 'Test string option', default="abc")
+    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
+    wantref_option = BoolOption('wantref', 'Test requires', default=False,
+                                requires=((boolop, True, 'hidden'),))
+    wantframework_option = BoolOption('wantframework', 'Test requires',
+                                      default=False,
+                                      requires=((boolop, True, 'hidden'),))
+    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
+    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
+                              wantref_option, stroption,
+                              wantframework_option,
+                              intoption, boolop])
+    return descr
+
+
+def test_has_callback():
+    descr = make_description_callback()
+    # here the owner is 'default'
+    config = Config(descr)
+    setting = config.cfgimpl_get_settings()
+    config.read_write()
+    config.bool = False
+    # because dummy has a callback
+    dummy = config.unwrap_from_path('gc.dummy')
+    setting.append('freeze')
+    setting[dummy].append('frozen')
+    raises(PropertiesOptionError, "config.gc.dummy = True")
+
+
+def test_freeze_and_has_callback():
+    descr = make_description_callback()
+    config = Config(descr)
+    setting = config.cfgimpl_get_settings()
+    config.read_write()
+    config.bool = False
+    setting = config.cfgimpl_get_settings()
+    setting.append('freeze')
+    dummy = config.unwrap_from_path('gc.dummy')
+    setting[dummy].append('frozen')
+    raises(PropertiesOptionError, "config.gc.dummy = True")
index 0d39d5b..9af6452 100644 (file)
 import autopath
 from py.test import raises
 
+from tiramisu.setting import owners
 from tiramisu.config import Config
-from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
-    StrOption, OptionDescription
-from tiramisu.error import PropertiesOptionError, ConflictError
-
-
-def make_description():
-    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                  ('std', 'thunk'), 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
-    wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                requires=((boolop, True, 'hidden'),))
-    wantframework_option = BoolOption('wantframework', 'Test requires',
-                                      default=False,
-                                      requires=((boolop, True, 'hidden'),))
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                              wantref_option, stroption,
-                              wantframework_option,
-                              intoption, boolop])
-    return descr
-
-
-def make_description_duplicates():
-    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
-    ## dummy 1
-    gcdummy = BoolOption('dummy', 'dummy', default=False)
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                  ('std', 'thunk'), 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
-    wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                requires=((boolop, True, 'hidden'),))
-    wantframework_option = BoolOption('wantframework', 'Test requires',
-                                      default=False,
-                                      requires=((boolop, True, 'hidden'),))
-    # dummy2 (same path)
-    gcdummy2 = BoolOption('dummy', 'dummy2', default=True)
-    # dummy3 (same name)
-    gcdummy3 = BoolOption('dummy', 'dummy2', default=True)
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, gcdummy2, floatoption])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                              wantref_option, stroption,
-                              wantframework_option,
-                              intoption, boolop, gcdummy3])
-    return descr
-
-
-def test_identical_paths():
-    """If in the schema (the option description) there is something that
-    have the same name, an exection is raised
-    """
-    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=((boolop, True, 'hidden'),))
-    # second multi
-    wantframework_option = BoolOption('wantframework', 'Test requires',
-                                      default=False,
-                                      requires=((boolop, True, '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)
-
-    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",
-                          requires=((intoption, 1, 'hidden'),))
-
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                              stroption, intoption])
-    return descr
-
-
-def test_hidden_if_in():
-    descr = make_description_requires()
-    cfg = Config(descr)
-    setting = cfg.cfgimpl_get_settings()
-    cfg.read_write()
-    stroption = cfg.unwrap_from_path('str')
-    assert not 'hidden' in setting[stroption]
-    cfg.int = 1
-    raises(PropertiesOptionError, "cfg.str")
-    raises(PropertiesOptionError, 'cfg.str="uvw"')
-    assert 'hidden' in setting[stroption]
-
-
-def test_hidden_if_in_with_group():
-    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")
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption],
-                                requires=((intoption, 1, 'hidden'),))
-    descr = OptionDescription('constraints', '', [gcgroup, booloption,
-                              objspaceoption, stroption, intoption])
-    cfg = Config(descr)
-    setting = cfg.cfgimpl_get_settings()
-    cfg.read_write()
-    assert not 'hidden' in setting[stroption]
-    cfg.int = 1
-    raises(PropertiesOptionError, "cfg.gc.name")
-
-
-def test_disabled_with_group():
-    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")
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption],
-                                requires=((intoption, 1, 'disabled'),))
-    descr = OptionDescription('constraints', '', [gcgroup, booloption,
-                              objspaceoption, stroption, intoption])
-    cfg = Config(descr)
-    cfg.read_write()
-    assert cfg.gc.name
-    cfg.int = 1
-    raises(PropertiesOptionError, "cfg.gc.name")
-#____________________________________________________________
-
-
-def make_description_callback():
-    gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
-    gcdummy = BoolOption('dummy', 'dummy')
-    objspaceoption = ChoiceOption('objspace', 'Object space',
-                                  ('std', 'thunk'), 'std')
-    booloption = BoolOption('bool', 'Test boolean option', default=True)
-    intoption = IntOption('int', 'Test int option', default=0)
-    floatoption = FloatOption('float', 'Test float option', default=2.3)
-    stroption = StrOption('str', 'Test string option', default="abc")
-    boolop = BoolOption('boolop', 'Test boolean option op', default=True)
-    wantref_option = BoolOption('wantref', 'Test requires', default=False,
-                                requires=((boolop, True, 'hidden'),))
-    wantframework_option = BoolOption('wantframework', 'Test requires',
-                                      default=False,
-                                      requires=((boolop, True, 'hidden'),))
-    gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
-    descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
-                              wantref_option, stroption,
-                              wantframework_option,
-                              intoption, boolop])
-    return descr
-
-
-def test_has_callback():
-    descr = make_description_callback()
-    # here the owner is 'default'
-    config = Config(descr)
-    setting = config.cfgimpl_get_settings()
-    config.read_write()
-    config.bool = False
-    # because dummy has a callback
-    dummy = config.unwrap_from_path('gc.dummy')
-    setting.append('freeze')
-    setting[dummy].append('frozen')
-    raises(PropertiesOptionError, "config.gc.dummy = True")
-
-
-def test_freeze_and_has_callback():
-    descr = make_description_callback()
-    config = Config(descr)
-    setting = config.cfgimpl_get_settings()
-    config.read_write()
-    config.bool = False
-    setting = config.cfgimpl_get_settings()
-    setting.append('freeze')
-    dummy = config.unwrap_from_path('gc.dummy')
-    setting[dummy].append('frozen')
-    raises(PropertiesOptionError, "config.gc.dummy = True")
+from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
+    OptionDescription
+
+
+def test_consistency_not_equal():
+    a = IntOption('a', '')
+    b = IntOption('b', '')
+    od = OptionDescription('od', '', [a, b])
+    a.impl_add_consistency('not_equal', (a, b))
+    c = Config(od)
+    assert c.a is None
+    assert c.b is None
+    c.a = 1
+    del(c.a)
+    c.a = 1
+    raises(ValueError, "c.b = 1")
+    c.b = 2
+
+
+def test_consistency_default():
+    a = IntOption('a', '', 1)
+    b = IntOption('b', '', 1)
+    raises(ValueError, "a.impl_add_consistency('not_equal', (a, b))")
+
+
+def test_consistency_default_diff():
+    a = IntOption('a', '', 3)
+    b = IntOption('b', '', 1)
+    od = OptionDescription('od', '', [a, b])
+    a.impl_add_consistency('not_equal', (a, b))
+    c = Config(od)
+    raises(ValueError, "c.a = 1")
+    c.a = 2
+    c.b = 3
+    assert c.getowner('a') is owners.user
+    raises(ValueError, "del(c.a)")
+    assert c.getowner('a') is owners.user
+
+
+def test_consistency_ip_netmask():
+    a = IPOption('a', '')
+    b = NetmaskOption('b', '')
+    od = OptionDescription('od', '', [a, b])
+    b.impl_add_consistency('ip_netmask', (b, a))
+    c = Config(od)
+    c.a = '192.168.1.1'
+    c.b = '255.255.255.0'
+    c.a = '192.168.1.2'
+    c.b = '255.255.255.255'
+    c.b = '255.255.255.0'
+    raises(ValueError, "c.a = '192.168.1.0'")
+
+
+def test_consistency_network_netmask():
+    a = NetworkOption('a', '')
+    b = NetmaskOption('b', '')
+    od = OptionDescription('od', '', [a, b])
+    b.impl_add_consistency('network_netmask', (b, a))
+    c = Config(od)
+    c.a = '192.168.1.1'
+    c.b = '255.255.255.255'
+    del(c.b)
+    c.a = '192.168.1.0'
+    c.b = '255.255.255.0'
+    raises(ValueError, "c.a = '192.168.1.1'")
index f155b8d..3f3cf8a 100644 (file)
@@ -50,7 +50,7 @@ def test_default_is_none():
 def test_set_defaut_value_from_option_object():
     """Options have an available default setting and can give it back"""
     b = BoolOption("boolean", "", default=False)
-    assert b.optimpl_getdefault() is False
+    assert b.impl_getdefault() is False
 
 
 def test_force_default_on_freeze():
index 56ba2cb..5f23796 100644 (file)
@@ -233,7 +233,7 @@ def test_multi_with_bool():
     s = BoolOption("bool", "", default=[False], multi=True)
     descr = OptionDescription("options", "", [s])
     config = Config(descr)
-    assert descr.bool.optimpl_is_multi() is True
+    assert descr.bool.impl_is_multi() is True
     config.bool = [True, False]
     assert config.cfgimpl_get_values()[s] == [True, False]
     assert config.bool == [True, False]
@@ -243,7 +243,7 @@ def test_multi_with_bool_two():
     s = BoolOption("bool", "", default=[False], multi=True)
     descr = OptionDescription("options", "", [s])
     config = Config(descr)
-    assert descr.bool.optimpl_is_multi() is True
+    assert descr.bool.impl_is_multi() is True
     raises(ValueError, "config.bool = True")
 
 
@@ -258,7 +258,7 @@ def test_choice_access_with_multi():
 #____________________________________________________________
 def test_symlink_option():
     boolopt = BoolOption("b", "", default=False)
-    linkopt = SymLinkOption("c", "s1.b", opt=boolopt)
+    linkopt = SymLinkOption("c", boolopt)
     descr = OptionDescription("opt", "",
                               [linkopt, OptionDescription("s1", "", [boolopt])])
     config = Config(descr)
@@ -280,10 +280,10 @@ def test_accepts_multiple_changes_from_option():
     descr = OptionDescription("options", "", [s])
     config = Config(descr)
     config.string = "egg"
-    assert s.optimpl_getdefault() == "string"
+    assert s.impl_getdefault() == "string"
     assert config.string == "egg"
     config.string = 'blah'
-    assert s.optimpl_getdefault() == "string"
+    assert s.impl_getdefault() == "string"
     assert config.string == "blah"
     config.string = 'bol'
     assert config.string == 'bol'
index b678ebb..e4a3656 100644 (file)
@@ -119,5 +119,5 @@ def test_with_many_subgroups():
     path = 'gc.subgroup.booltwo'
     homeconfig, name = config.cfgimpl_get_home_by_path(path)
     assert name == "booltwo"
-    getattr(homeconfig._cfgimpl_descr, name)
+    getattr(homeconfig.cfgimpl_get_description(), name)
     assert 'hidden' in setting[booltwo]
index 94b94e1..483aca0 100644 (file)
@@ -50,7 +50,7 @@ def test_optname_shall_not_start_with_numbers():
 def test_option_has_an_api_name():
     raises(ValueError, "BoolOption('cfgimpl_get_settings', 'dummy', default=False)")
     raises(ValueError, "BoolOption('unwrap_from_path', 'dummy', default=False)")
-    raises(ValueError, "BoolOption('optimpl_getdoc', 'dummy', default=False)")
+    raises(ValueError, "BoolOption('impl_getdoc', 'dummy', default=False)")
     raises(ValueError, "BoolOption('_unvalid', 'dummy', default=False)")
     raises(ValueError, "BoolOption('6unvalid', 'dummy', default=False)")
     BoolOption('unvalid6', 'dummy', default=False)
index 2c6eaad..18b1de1 100644 (file)
@@ -27,13 +27,13 @@ def make_description():
 
     master = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
     interface1 = OptionDescription('interface1', '', [master])
-    interface1.optimpl_set_group_type(groups.family)
+    interface1.impl_set_group_type(groups.family)
 
     general = OptionDescription('general', '', [numero_etab, nom_machine,
                                 nombre_interfaces, activer_proxy_client,
                                 mode_conteneur_actif, adresse_serveur_ntp,
                                 time_zone])
-    general.optimpl_set_group_type(groups.family)
+    general.impl_set_group_type(groups.family)
     creole = OptionDescription('creole', 'first tiramisu configuration', [general, interface1])
     descr = OptionDescription('baseconfig', 'baseconifgdescr', [creole])
     return descr
@@ -74,10 +74,10 @@ def test_get_group_type():
     descr = make_description()
     config = Config(descr)
     grp = config.unwrap_from_path('creole.general')
-    assert grp.optimpl_get_group_type() == groups.family
-    assert grp.optimpl_get_group_type() == 'family'
-    assert isinstance(grp.optimpl_get_group_type(), groups.GroupType)
-    raises(TypeError, 'grp.optimpl_set_group_type(groups.default)')
+    assert grp.impl_get_group_type() == groups.family
+    assert grp.impl_get_group_type() == 'family'
+    assert isinstance(grp.impl_get_group_type(), groups.GroupType)
+    raises(TypeError, 'grp.impl_set_group_type(groups.default)')
 
 
 def test_iter_on_groups():
@@ -103,31 +103,31 @@ def test_groups_with_master():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
-    interface1.optimpl_set_group_type(groups.master)
-    assert interface1.optimpl_get_group_type() == groups.master
+    interface1.impl_set_group_type(groups.master)
+    assert interface1.impl_get_group_type() == groups.master
 
 
 def test_groups_with_master_in_config():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
-    interface1.optimpl_set_group_type(groups.master)
+    interface1.impl_set_group_type(groups.master)
     Config(interface1)
-    assert interface1.optimpl_get_group_type() == groups.master
+    assert interface1.impl_get_group_type() == groups.master
 
 
 def test_allowed_groups():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
-    raises(ValueError, "interface1.optimpl_set_group_type('toto')")
+    raises(ValueError, "interface1.impl_set_group_type('toto')")
 
 
 def test_master_not_valid_name():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     invalid_group = OptionDescription('interface1', '', [ip_admin_eth0, netmask_admin_eth0])
-    raises(ValueError, "invalid_group.optimpl_set_group_type(groups.master)")
+    raises(ValueError, "invalid_group.impl_set_group_type(groups.master)")
 
 
 def test_sub_group_in_master_group():
@@ -135,14 +135,14 @@ def test_sub_group_in_master_group():
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     subgroup = OptionDescription("subgroup", '', [])
     invalid_group = OptionDescription('ip_admin_eth0', '', [subgroup, ip_admin_eth0, netmask_admin_eth0])
-    raises(ValueError, "invalid_group.optimpl_set_group_type(groups.master)")
+    raises(ValueError, "invalid_group.impl_set_group_type(groups.master)")
 
 
 def test_group_always_has_multis():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
     group = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
-    raises(ValueError, "group.optimpl_set_group_type(groups.master)")
+    raises(ValueError, "group.impl_set_group_type(groups.master)")
 
 
 #____________________________________________________________
@@ -150,11 +150,11 @@ def test_values_with_master_and_slaves():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
-    interface1.optimpl_set_group_type(groups.master)
+    interface1.impl_set_group_type(groups.master)
     maconfig = OptionDescription('toto', '', [interface1])
     cfg = Config(maconfig)
-    owner = cfg._cfgimpl_context._cfgimpl_settings.getowner()
-    assert interface1.optimpl_get_group_type() == groups.master
+    owner = cfg.cfgimpl_get_settings().getowner()
+    assert interface1.impl_get_group_type() == groups.master
     assert cfg.getowner("ip_admin_eth0.ip_admin_eth0") == owners.default
     assert cfg.getowner("ip_admin_eth0.netmask_admin_eth0") == owners.default
     assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
@@ -169,11 +169,11 @@ def test_reset_values_with_master_and_slaves():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
-    interface1.optimpl_set_group_type(groups.master)
+    interface1.impl_set_group_type(groups.master)
     maconfig = OptionDescription('toto', '', [interface1])
     cfg = Config(maconfig)
-    owner = cfg._cfgimpl_context._cfgimpl_settings.getowner()
-    assert interface1.optimpl_get_group_type() == groups.master
+    owner = cfg.cfgimpl_get_settings().getowner()
+    assert interface1.impl_get_group_type() == groups.master
     assert cfg.getowner("ip_admin_eth0.ip_admin_eth0") == owners.default
     assert cfg.getowner("ip_admin_eth0.netmask_admin_eth0") == owners.default
     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
@@ -190,7 +190,7 @@ def test_values_with_master_and_slaves_slave():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
-    interface1.optimpl_set_group_type(groups.master)
+    interface1.impl_set_group_type(groups.master)
     maconfig = OptionDescription('toto', '', [interface1])
     cfg = Config(maconfig)
     assert cfg.ip_admin_eth0.netmask_admin_eth0 == []
@@ -212,7 +212,7 @@ def test_values_with_master_and_slaves_master():
     ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
     netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True)
     interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
-    interface1.optimpl_set_group_type(groups.master)
+    interface1.impl_set_group_type(groups.master)
     maconfig = OptionDescription('toto', '', [interface1])
     cfg = Config(maconfig)
     cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
diff --git a/test/test_slots.py b/test/test_slots.py
new file mode 100644 (file)
index 0000000..0104e84
--- /dev/null
@@ -0,0 +1,54 @@
+# coding: utf-8
+import autopath
+from py.test import raises
+
+from tiramisu.config import Config, SubConfig
+from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
+    StrOption, OptionDescription, SymLinkOption, UnicodeOption
+
+
+def test_slots_option():
+    c = BoolOption('a', '')
+    raises(AttributeError, "c.x = 1")
+    c = IntOption('a', '')
+    raises(AttributeError, "c.x = 1")
+    c = FloatOption('a', '')
+    raises(AttributeError, "c.x = 1")
+    c = StrOption('a', '')
+    raises(AttributeError, "c.x = 1")
+    c = SymLinkOption('b', c)
+    raises(AttributeError, "c.x = 1")
+    c = UnicodeOption('a', '')
+    raises(AttributeError, "c.x = 1")
+    c = ChoiceOption('a', '', ('a',))
+    raises(AttributeError, "c.x = 1")
+    c = OptionDescription('a', '', [])
+    raises(AttributeError, "c.x = 1")
+
+
+def test_slots_config():
+    od1 = OptionDescription('a', '', [])
+    od2 = OptionDescription('a', '', [od1])
+    c = Config(od2)
+    raises(AttributeError, "c.x = 1")
+    raises(AttributeError, "c.cfgimpl_x = 1")
+    sc = c.a
+    assert isinstance(sc, SubConfig)
+    raises(AttributeError, "sc.x = 1")
+    raises(AttributeError, "sc.cfgimpl_x = 1")
+
+
+def test_slots_setting():
+    od1 = OptionDescription('a', '', [])
+    od2 = OptionDescription('a', '', [od1])
+    c = Config(od2)
+    s = c.cfgimpl_get_settings()
+    raises(AttributeError, "s.x = 1")
+
+
+def test_slots_value():
+    od1 = OptionDescription('a', '', [])
+    od2 = OptionDescription('a', '', [od1])
+    c = Config(od2)
+    v = c.cfgimpl_get_values()
+    raises(AttributeError, "v.x = 1")
index c98daa9..a8608ff 100644 (file)
 # ____________________________________________________________
 #from inspect import getmembers, ismethod
 from tiramisu.error import PropertiesOptionError, ConfigError
-from tiramisu.option import OptionDescription, Option, SymLinkOption
+from tiramisu.option import OptionDescription, Option, SymLinkOption, \
+    BaseInformation
 from tiramisu.setting import groups, Setting
 from tiramisu.value import Values
 from tiramisu.i18n import _
 
 
-class SubConfig(object):
+class SubConfig(BaseInformation):
     "sub configuration management entry"
-    __slots__ = ('_cfgimpl_context', '_cfgimpl_descr')
+    __slots__ = ('_impl_context', '_impl_descr')
 
     def __init__(self, descr, context):
         """ Configuration option management master class
@@ -44,11 +45,11 @@ class SubConfig(object):
         if not isinstance(descr, OptionDescription):
             raise ValueError(_('descr must be an optiondescription, not {0}'
                                '').format(type(descr)))
-        self._cfgimpl_descr = descr
+        self._impl_descr = descr
         # sub option descriptions
         if not isinstance(context, SubConfig):
             raise ValueError('context must be a SubConfig')
-        self._cfgimpl_context = context
+        self._impl_context = context
 
     def cfgimpl_reset_cache(self, only_expired=False, only=('values',
                                                             'settings')):
@@ -68,14 +69,14 @@ class SubConfig(object):
         return self, path[-1]
 
     def __hash__(self):
-        return hash(self.cfgimpl_get_description().optimpl_getkey(self))
+        return hash(self.cfgimpl_get_description().impl_getkey(self))
 
     def __eq__(self, other):
         "Config comparison"
         if not isinstance(other, Config):
             return False
-        return self.cfgimpl_get_description().optimpl_getkey(self) == \
-            other.cfgimpl_get_description().optimpl_getkey(other)
+        return self.cfgimpl_get_description().impl_getkey(self) == \
+            other.cfgimpl_get_description().impl_getkey(other)
 
     def __ne__(self, other):
         "Config comparison"
@@ -87,7 +88,7 @@ class SubConfig(object):
     def __iter__(self):
         """Pythonesque way of parsing group's ordered options.
         iteration only on Options (not OptionDescriptions)"""
-        for child in self.cfgimpl_get_description().optimpl_getchildren():
+        for child in self.cfgimpl_get_description().impl_getchildren():
             if not isinstance(child, OptionDescription):
                 try:
                     yield child._name, getattr(self, child._name)
@@ -99,7 +100,7 @@ class SubConfig(object):
     def iter_all(self):
         """A way of parsing options **and** groups.
         iteration on Options and OptionDescriptions."""
-        for child in self.cfgimpl_get_description().optimpl_getchildren():
+        for child in self.cfgimpl_get_description().impl_getchildren():
             try:
                 yield child._name, getattr(self, child._name)
             except GeneratorExit:
@@ -120,11 +121,11 @@ class SubConfig(object):
         if group_type is not None:
             if not isinstance(group_type, groups.GroupType):
                 raise TypeError(_("unknown group_type: {0}").format(group_type))
-        for child in self.cfgimpl_get_description().optimpl_getchildren():
+        for child in self.cfgimpl_get_description().impl_getchildren():
             if isinstance(child, OptionDescription):
                 try:
                     if group_type is None or (group_type is not None and
-                                              child.optimpl_get_group_type()
+                                              child.impl_get_group_type()
                                               == group_type):
                         yield child._name, getattr(self, child._name)
                 except GeneratorExit:
@@ -148,25 +149,25 @@ class SubConfig(object):
     __repr__ = __str__
 
     def cfgimpl_get_context(self):
-        return self._cfgimpl_context
+        return self._impl_context
 
     def cfgimpl_get_description(self):
-        if self._cfgimpl_descr is None:
+        if self._impl_descr is None:
             raise ConfigError(_('no optiondescription for this config (may be MetaConfig without meta)'))
         else:
-            return self._cfgimpl_descr
+            return self._impl_descr
 
     def cfgimpl_get_settings(self):
-        return self.cfgimpl_get_context()._cfgimpl_settings
+        return self.cfgimpl_get_context()._impl_settings
 
     def cfgimpl_get_values(self):
-        return self.cfgimpl_get_context()._cfgimpl_values
+        return self.cfgimpl_get_context()._impl_values
 
     # ____________________________________________________________
     # attribute methods
     def __setattr__(self, name, value):
         "attribute notation mechanism for the setting of the value of an option"
-        if name.startswith('_cfgimpl_'):
+        if name.startswith('_impl_'):
             #self.__dict__[name] = value
             object.__setattr__(self, name, value)
             return
@@ -182,7 +183,7 @@ class SubConfig(object):
                                               force_permissive=force_permissive)
         else:
             context = self.cfgimpl_get_context()
-            path = context.cfgimpl_get_description().optimpl_get_path_by_opt(child._opt)
+            path = context.cfgimpl_get_description().impl_get_path_by_opt(child._opt)
             context._setattr(path, value, force_permissive=force_permissive)
 
     def __delattr__(self, name):
@@ -210,14 +211,15 @@ class SubConfig(object):
                                        force_properties=force_properties,
                                        validate=validate)
         # special attributes
-        if name.startswith('_cfgimpl_') or name.startswith('cfgimpl_'):
+        if name.startswith('_impl_') or name.startswith('cfgimpl_') \
+                or name.startswith('impl_'):
             # if it were in __dict__ it would have been found already
             return object.__getattribute__(self, name)
         opt_or_descr = getattr(self.cfgimpl_get_description(), name)
         # symlink options
         if isinstance(opt_or_descr, SymLinkOption):
             context = self.cfgimpl_get_context()
-            path = context.cfgimpl_get_description().optimpl_get_path_by_opt(opt_or_descr._opt)
+            path = context.cfgimpl_get_description().impl_get_path_by_opt(opt_or_descr._opt)
             return context._getattr(path, validate=validate,
                                     force_properties=force_properties,
                                     force_permissive=force_permissive)
@@ -371,7 +373,7 @@ class SubConfig(object):
                                                          type_='path',
                                                          _subpath=mypath):
                 path = '.'.join(path.split('.')[:-1])
-                opt = self.cfgimpl_get_context().cfgimpl_get_description().optimpl_get_opt_by_path(path)
+                opt = self.cfgimpl_get_context().cfgimpl_get_description().impl_get_opt_by_path(path)
                 if mypath is not None:
                     if mypath == path:
                         withoption = None
@@ -387,7 +389,7 @@ class SubConfig(object):
                 self._make_sub_dict(opt, path, pathsvalues, _currpath, flatten)
         #withoption can be set to None below !
         if withoption is None:
-            for opt in self.cfgimpl_get_description().optimpl_getchildren():
+            for opt in self.cfgimpl_get_description().impl_getchildren():
                 path = opt._name
                 self._make_sub_dict(opt, path, pathsvalues, _currpath, flatten)
         if _currpath == []:
@@ -416,14 +418,14 @@ class SubConfig(object):
     def cfgimpl_get_path(self):
         descr = self.cfgimpl_get_description()
         context_descr = self.cfgimpl_get_context().cfgimpl_get_description()
-        return context_descr.optimpl_get_path_by_opt(descr)
+        return context_descr.impl_get_path_by_opt(descr)
 
 
 class ConfigCommon(SubConfig):
-    __slots__ = ('_cfgimpl_values', '_cfgimpl_settings', '_cfgimpl_meta')
+    __slots__ = ('_impl_values', '_impl_settings', '_impl_meta')
 
-    def _cfgimpl_build_all_paths(self):
-        self.cfgimpl_get_description().optimpl_build_cache()
+    def _impl_build_all_paths(self):
+        self.cfgimpl_get_description().impl_build_cache()
 
     def read_only(self):
         self.cfgimpl_get_settings().read_only()
@@ -432,7 +434,7 @@ class ConfigCommon(SubConfig):
         self.cfgimpl_get_settings().read_write()
 
     def getowner(self, path):
-        opt = self.cfgimpl_get_description().optimpl_get_opt_by_path(path)
+        opt = self.cfgimpl_get_description().impl_get_opt_by_path(path)
         return self.cfgimpl_get_values().getowner(opt)
 
     def unwrap_from_path(self, path):
@@ -451,7 +453,7 @@ class ConfigCommon(SubConfig):
         return None
 
     def cfgimpl_get_meta(self):
-        return self._cfgimpl_meta
+        return self._impl_meta
 
 
 # ____________________________________________________________
@@ -467,11 +469,12 @@ class Config(ConfigCommon):
         :param context: the current root config
         :type context: `Config`
         """
-        self._cfgimpl_settings = Setting(self)
-        self._cfgimpl_values = Values(self)
-        super(Config, self).__init__(descr, self)  # , slots)
-        self._cfgimpl_build_all_paths()
-        self._cfgimpl_meta = None
+        self._impl_settings = Setting(self)
+        self._impl_values = Values(self)
+        super(Config, self).__init__(descr, self)
+        self._impl_build_all_paths()
+        self._impl_meta = None
+        self._impl_informations = {}
 
     def cfgimpl_reset_cache(self, only_expired=False, only=('values', 'settings')):
         if 'values' in only:
@@ -481,30 +484,30 @@ class Config(ConfigCommon):
 
 
 class MetaConfig(ConfigCommon):
-    __slots__ = ('_cfgimpl_children',)
+    __slots__ = ('_impl_children',)
 
     def __init__(self, children, meta=True):
         if not isinstance(children, list):
             raise ValueError(_("metaconfig's children must be a list"))
-        self._cfgimpl_descr = None
+        self._impl_descr = None
         if meta:
             for child in children:
                 if not isinstance(child, ConfigCommon):
                     raise ValueError(_("metaconfig's children must be Config, not {0}"
                                        "".format(type(child))))
-                if self._cfgimpl_descr is None:
-                    self._cfgimpl_descr = child.cfgimpl_get_description()
-                elif not self._cfgimpl_descr is child.cfgimpl_get_description():
+                if self._impl_descr is None:
+                    self._impl_descr = child.cfgimpl_get_description()
+                elif not self._impl_descr is child.cfgimpl_get_description():
                     raise ValueError(_('all config in MetaConfig must have same '
                                        'optiondescription'))
                 if child.cfgimpl_get_meta() is not None:
                     raise ValueError(_("child has already a metaconfig's"))
-                child._cfgimpl_meta = self
+                child._impl_meta = self
 
-        self._cfgimpl_children = children
-        self._cfgimpl_settings = Setting(self)
-        self._cfgimpl_values = Values(self)
-        self._cfgimpl_meta = None
+        self._impl_children = children
+        self._impl_settings = Setting(self)
+        self._impl_values = Values(self)
+        self._impl_meta = None
 
     def cfgimpl_get_context(self):
         return self
@@ -514,11 +517,11 @@ class MetaConfig(ConfigCommon):
             self.cfgimpl_get_values().reset_cache(only_expired=only_expired)
         if 'settings' in only:
             self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
-        for child in self._cfgimpl_children:
+        for child in self._impl_children:
             child.cfgimpl_reset_cache(only_expired=only_expired, only=only)
 
     def set_contexts(self, path, value):
-        for child in self._cfgimpl_children:
+        for child in self._impl_children:
             try:
                 if not isinstance(child, MetaConfig):
                     setattr(child, path, value)
@@ -537,7 +540,7 @@ class MetaConfig(ConfigCommon):
                                     check_properties=False)
         except ConfigError:
             pass
-        for child in self._cfgimpl_children:
+        for child in self._impl_children:
             try:
                 if not isinstance(child, MetaConfig):
                     if bypath is not None:
@@ -570,9 +573,9 @@ def mandatory_warnings(config):
     """
     #if value in cache, properties are not calculated
     config.cfgimpl_reset_cache(only=('values',))
-    for path in config.cfgimpl_get_description().optimpl_getpaths(include_groups=True):
+    for path in config.cfgimpl_get_description().impl_getpaths(include_groups=True):
         try:
-            config._getattr(path, force_properties=('mandatory',))
+            config._getattr(path, force_properties=frozenset(('mandatory',)))
         except PropertiesOptionError, err:
             if err.proptype == ['mandatory']:
                 yield path
index 038cee8..6fc9395 100644 (file)
@@ -31,9 +31,9 @@ from tiramisu.i18n import _
 from tiramisu.autolib import carry_out_calculation
 
 name_regexp = re.compile(r'^\d+')
-forbidden_names = ['iter_all', 'iter_group', 'find', 'find_first',
+forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
                    'make_dict', 'unwrap_from_path', 'read_only',
-                   'read_write', 'getowner', 'set_contexts']
+                   'read_write', 'getowner', 'set_contexts')
 
 
 def valid_name(name):
@@ -43,7 +43,7 @@ def valid_name(name):
         return False
     if re.match(name_regexp, name) is None and not name.startswith('_') \
             and name not in forbidden_names \
-            and not name.startswith('optimpl_') \
+            and not name.startswith('impl_') \
             and not name.startswith('cfgimpl_'):
         return True
     else:
@@ -53,24 +53,24 @@ def valid_name(name):
 
 
 class BaseInformation(object):
-    __slots__ = ('_informations',)
+    __slots__ = ('_impl_informations',)
 
-    def optimpl_set_information(self, key, value):
+    def impl_set_information(self, key, value):
         """updates the information's attribute
         (wich is a dictionnary)
 
         :param key: information's key (ex: "help", "doc"
         :param value: information's value (ex: "the help string")
         """
-        self._informations[key] = value
+        self._impl_informations[key] = value
 
-    def optimpl_get_information(self, key, default=None):
+    def impl_get_information(self, key, default=None):
         """retrieves one information's item
 
         :param key: the item string (ex: "help")
         """
-        if key in self._informations:
-            return self._informations[key]
+        if key in self._impl_informations:
+            return self._impl_informations[key]
         elif default is not None:
             return default
         else:
@@ -112,8 +112,8 @@ class Option(BaseInformation):
         if not valid_name(name):
             raise ValueError(_("invalid name: {0} for option").format(name))
         self._name = name
-        self._informations = {}
-        self.optimpl_set_information('doc', doc)
+        self._impl_informations = {}
+        self.impl_set_information('doc', doc)
         validate_requires_arg(requires, self._name)
         self._requires = requires
         self._multi = multi
@@ -150,37 +150,23 @@ class Option(BaseInformation):
         if self._multi:
             if default is None:
                 default = []
-            #if not isinstance(default, list):
-            #    raise ValidateError("invalid default value {0} "
-            #                        "for option {1} : not list type"
-            #                        "".format(str(default), name))
-            if not self.optimpl_validate(default):
-                raise ValueError(_("invalid default value {0} "
-                                 "for option {1}"
-                                 "").format(str(default), name))
             self._multitype = multitypes.default
             self._default_multi = default_multi
-        else:
-            if default is not None and not self.optimpl_validate(default):
-                raise ValueError(_("invalid default value {0} "
-                                 "for option {1}").format(str(default), name))
+        self.impl_validate(default)
         self._default = default
         if properties is None:
-            properties = ()
+            properties = tuple()
         if not isinstance(properties, tuple):
             raise TypeError(_('invalid properties type {0} for {1},'
                             ' must be a tuple').format(type(properties), self._name))
-        self._properties = properties  # 'hidden', 'disabled'...
+        self._properties = set(properties)  # 'hidden', 'disabled'...
 
     def __eq__(self, other):
         "Option comparison"
         if not isinstance(other, Option):
             return False
-        a = list(self.__slots__ + Option.__slots__ + BaseInformation.__slots__)
-        #a.remove('_default_multi')
-        a.remove('_master_slaves')
-        a.remove('_multitype')
-        for var in a:
+        slots = list(self.__slots__ + Option.__slots__ + BaseInformation.__slots__)
+        for var in slots:
             try:
                 val1 = getattr(self, var)
                 not_in1 = False
@@ -203,7 +189,10 @@ class Option(BaseInformation):
             return False
         return not self == other
 
-    def optimpl_validate(self, value, context=None, validate=True):
+    def _launch_consistency(self, func, opt, value, context, index, opts):
+        return getattr(self, func)(opt, value, context, index, opts)
+
+    def impl_validate(self, value, context=None, validate=True):
         """
         :param value: the option's value
         :param validate: if true enables ``self._validator`` validation
@@ -217,121 +206,114 @@ class Option(BaseInformation):
 
         def val_validator():
             #add current value has first argument
-            if self.optimpl_is_multi():
+            if self.impl_is_multi():
                 for val in value:
                     if not _val_validator(val):
                         return False
+                return True
             else:
-                return _val_validator(val)
-            return True
+                return _val_validator(value)
         # generic calculation
         if context is not None:
-            cons = context.cfgimpl_get_description()
-        else:
-            cons = None
+            descr = context.cfgimpl_get_description()
         if not self._multi:
-            # None allows the reset of the value
-            if value is not None:
-                # customizing the validator
-                if validate and self._validator is not None and \
-                        not self._validator[0](value, **self._validator[1]):
-                    return False
-                if not self._validate(value):
-                    return False
-                if cons is not None:
-                    return cons._valid_consistency(self, value, context, None)
+            if value is not None and ((validate and
+                                      self._validator is not None and
+                                      not val_validator()) or
+                                      not self._validate(value)):
+                raise ValueError(_("invalid value {0} for option {1}"
+                                   "").format(value, self._name))
+            if context is not None:
+                descr._valid_consistency(self, value, context, None)
         else:
             if not isinstance(value, list):
-                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._name))
             for index in range(0, len(value)):
                 val = value[index]
-                # None allows the reset of the value
-                if val is not None:
-                    # customizing the validator
-                    if validate and self._validator is not None and \
-                            not val_validator():
-                        return False
-                    if not self._validate(val):
-                        return False
-                    if cons is not None and not cons._valid_consistency(self,
-                                                                        val,
-                                                                        context,
-                                                                        index):
-                        return False
-        return True
-
-    def optimpl_getdefault(self, default_multi=False):
+                if val is not None and ((validate and
+                                         self._validator is not None and
+                                         not val_validator()) or
+                                        not self._validate(val)):
+                        raise ValueError(_("invalid value {0} for option {1}"
+                                           "").format(value, self._name))
+                if context is not None:
+                    descr._valid_consistency(self, val, context, index)
+
+    def impl_getdefault(self, default_multi=False):
         "accessing the default value"
-        if not default_multi or not self.optimpl_is_multi():
+        if not default_multi or not self.impl_is_multi():
             return self._default
         else:
             return self.getdefault_multi()
 
-    def optimpl_getdefault_multi(self):
+    def impl_getdefault_multi(self):
         "accessing the default value for a multi"
         return self._default_multi
 
-    def optimpl_get_multitype(self):
+    def impl_get_multitype(self):
         return self._multitype
 
-    def optimpl_get_master_slaves(self):
+    def impl_get_master_slaves(self):
         return self._master_slaves
 
-    def optimpl_is_empty_by_default(self):
+    def impl_is_empty_by_default(self):
         "no default value has been set yet"
-        if ((not self.optimpl_is_multi() and self._default is None) or
-                (self.optimpl_is_multi() and (self._default == [] or None in self._default))):
+        if ((not self.impl_is_multi() and self._default is None) or
+                (self.impl_is_multi() and (self._default == [] or None in self._default))):
             return True
         return False
 
-    def optimpl_getdoc(self):
+    def impl_getdoc(self):
         "accesses the Option's doc"
-        return self.optimpl_get_information('doc')
+        return self.impl_get_information('doc')
 
-    def optimpl_has_callback(self):
+    def impl_has_callback(self):
         "to know if a callback has been defined or not"
         if self._callback is None:
             return False
         else:
             return True
 
-    def optimpl_getkey(self, value):
+    def impl_getkey(self, value):
         return value
 
-    def optimpl_is_multi(self):
+    def impl_is_multi(self):
         return self._multi
 
-    def optimpl_add_consistency(self, func, opts):
-        pass
+    def impl_add_consistency(self, func, opts):
         if self._consistencies is None:
             self._consistencies = []
         if self not in opts:
             opts = list(opts)
             opts.append(self)
             opts = tuple(opts)
-        self._consistencies.append(('_cons_{}'.format(func), opts))
+        func = '_cons_{}'.format(func)
+        for opt in opts:
+            if opt != self:
+                self._launch_consistency(func, opt, self.impl_getdefault(), None, None, opts)
+        self._consistencies.append((func, opts))
+        self.impl_validate(self.impl_getdefault(), None)
 
     def _cons_not_equal(self, opt, value, context, index, opts):
         values = [value]
-        descr = context.cfgimpl_get_description()
+        if context is not None:
+            descr = context.cfgimpl_get_description()
         for opt_ in opts:
             if opt_ is not opt:
-                path = descr.optimpl_get_path_by_opt(opt_)
-                val = context._getattr(path, validate=False)
+                if context is not None:
+                    path = descr.impl_get_path_by_opt(opt_)
+                    val = context._getattr(path, validate=False)
+                else:
+                    val = opt.impl_getdefault()
                 if val is not None:
                     if val in values:
-                        return False
+                        raise ValueError(_("invalid value {0} for option {1} "
+                                           "must be different as {2} option"
+                                           "").format(val, self._name,
+                                                      opt_._name))
                     values.append(val)
-        return True
-
-    def _cons_lower(self, value):
-        try:
-            return value.islower()
-        except AttributeError:
-            #no "islower" attribute
-            return False
 
 
 class ChoiceOption(Option):
@@ -359,10 +341,10 @@ class ChoiceOption(Option):
                                            validator_args=validator_args,
                                            properties=properties)
 
-    def optimpl_get_values(self):
+    def impl_get_values(self):
         return self._values
 
-    def optimpl_is_openvalues(self):
+    def impl_is_openvalues(self):
         return self._open_values
 
     def _validate(self, value):
@@ -418,8 +400,11 @@ class SymLinkOption(object):
     _opt_type = 'symlink'
     _consistencies = None
 
-    def __init__(self, name, path, opt):
+    def __init__(self, name, opt):
         self._name = name
+        if not isinstance(opt, Option):
+            raise ValueError(_('malformed symlinkoption '
+                               'must be an option for symlink {0}').format(name))
         self._opt = opt
 
     def __getattr__(self, name):
@@ -474,31 +459,6 @@ class NetmaskOption(Option):
     __slots__ = ('_opt_type',)
     _opt_type = 'netmask'
 
-    def __init__(self, name, doc, default=None, default_multi=None,
-                 requires=None, multi=False, callback=None,
-                 callback_params=None, validator=None, validator_args=None,
-                 properties=None, opt_ip=None):
-        if opt_ip is not None and not isinstance(opt_ip, IPOption) and \
-                not isinstance(opt_ip, NetworkOption):
-            raise TypeError(_('opt_ip must be a IPOption not {}').format(type(opt_ip)))
-        super(NetmaskOption, self).__init__(name, doc, default=default,
-                                            default_multi=default_multi,
-                                            callback=callback,
-                                            callback_params=callback_params,
-                                            requires=requires,
-                                            multi=multi,
-                                            validator=validator,
-                                            validator_args=validator_args,
-                                            properties=properties)
-        if opt_ip is None:
-            pass
-        elif isinstance(opt_ip, IPOption):
-            self._consistencies = [('cons_ip_netmask', (self, opt_ip))]
-        elif isinstance(opt_ip, NetworkOption):
-            self._consistencies = [('cons_network_netmask', (self, opt_ip))]
-        else:
-            raise TypeError(_('unknown type for opt_ip'))
-
     def _validate(self, value):
         try:
             IP('0.0.0.0/{}'.format(value))
@@ -508,36 +468,61 @@ class NetmaskOption(Option):
 
     def _cons_network_netmask(self, opt, value, context, index, opts):
         #opts must be (netmask, network) options
-        return self._cons_netmask(opt, value, context, index, opts, False)
+        self.__cons_netmask(opt, value, context, index, opts, False)
 
     def _cons_ip_netmask(self, opt, value, context, index, opts):
         #opts must be (netmask, ip) options
-        return self._cons_netmask(opt, value, context, index, opts, True)
+        self.__cons_netmask(opt, value, context, index, opts, True)
 
     def __cons_netmask(self, opt, value, context, index, opts, make_net):
         opt_netmask, opt_ipnetwork = opts
-        descr = context.cfgimpl_get_description()
+        if context is not None:
+            descr = context.cfgimpl_get_description()
         if opt is opt_ipnetwork:
             val_ipnetwork = value
-            path = descr.optimpl_get_path_by_opt(opt_netmask)
-            val_netmask = context._getattr(path, validate=False)
-            if opt_netmask.optimpl_is_multi():
+            if context is not None:
+                path = descr.impl_get_path_by_opt(opt_netmask)
+                val_netmask = context._getattr(path, validate=False)
+            else:
+                val_netmask = opt_netmask.impl_getdefault()
+            if opt_netmask.impl_is_multi():
                 val_netmask = val_netmask[index]
-            if val_netmask is None:
-                return True
         else:
             val_netmask = value
-            path = descr.optimpl_get_path_by_opt(opt_ipnetwork)
-            val_ipnetwork = getattr(context, path)
-            if opt_ipnetwork.optimpl_is_multi():
+            if context is not None:
+                path = descr.impl_get_path_by_opt(opt_ipnetwork)
+                val_ipnetwork = context._getattr(path, validate=False)
+            else:
+                val_ipnetwork = opt_ipnetwork.impl_getdefault()
+            if opt_ipnetwork.impl_is_multi():
                 val_ipnetwork = val_ipnetwork[index]
-            if val_ipnetwork is None:
-                return True
-        try:
-            IP('{}/{}'.format(val_ipnetwork, val_netmask, make_net=make_net))
-            return True
-        except ValueError:
-            return False
+        if None not in (val_ipnetwork, val_netmask):
+            msg = None
+            try:
+                ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
+                        make_net=make_net)
+                #if cidr == 32, ip same has network
+                if ip.prefixlen() != 32:
+                    try:
+                        IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
+                           make_net=not make_net)
+                    except ValueError:
+                        if not make_net:
+                            msg = _("invalid network {0} ({1}) with netmask {2} ({3}),"
+                                    " this network is an ip")
+                    else:
+                        if make_net:
+                            msg = _("invalid ip {0} ({1}) with netmask {2} ({3}),"
+                                    " this ip is a network")
+
+            except ValueError:
+                if make_net:
+                    msg = _("invalid ip {0} ({1}) with netmask {2} ({3})")
+                else:
+                    msg = _("invalid network {0} ({1}) with netmask {2} ({3})")
+            if msg is not None:
+                raise ValueError(msg.format(val_ipnetwork, opt_ipnetwork._name,
+                                            val_netmask, opt_netmask._name))
 
 
 class DomainnameOption(Option):
@@ -553,15 +538,15 @@ class DomainnameOption(Option):
         #hostname: to identify the device
         #domainname:
         #fqdn: with tld, not supported yet
-        super(NetmaskOption, self).__init__(name, doc, default=default,
-                                            default_multi=default_multi,
-                                            callback=callback,
-                                            callback_params=callback_params,
-                                            requires=requires,
-                                            multi=multi,
-                                            validator=validator,
-                                            validator_args=validator_args,
-                                            properties=properties)
+        super(DomainnameOption, self).__init__(name, doc, default=default,
+                                               default_multi=default_multi,
+                                               callback=callback,
+                                               callback_params=callback_params,
+                                               requires=requires,
+                                               multi=multi,
+                                               validator=validator,
+                                               validator_args=validator_args,
+                                               properties=properties)
         if type_ not in ['netbios', 'hostname', 'domainname']:
             raise ValueError(_('unknown type_ {0} for hostname').format(type_))
         self._type = type_
@@ -583,8 +568,13 @@ class DomainnameOption(Option):
         elif self._type == 'domainname':
             length = 255
             extrachar = '\.'
-        regexp = r'^[a-zA-Z]([a-zA-Z\d-{0}]{{,{1}}})*[a-zA-Z\d]$'.format(
-            extrachar, length - 2)
+            if '.' not in value:
+                raise ValueError(_("invalid value for {0}, must have dot"
+                                   "").format(self._name))
+        if len(value) > length:
+            raise ValueError(_("invalid value's length for {0} (max {1})"
+                               "").format(self._name, length))
+        regexp = r'^[a-z]([a-z\d{0}-])*[a-z\d]$'.format(extrachar)
         return re.match(regexp, value) is not None
 
 
@@ -593,7 +583,7 @@ class OptionDescription(BaseInformation):
     __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
                  '_properties', '_children', '_consistencies')
 
-    def __init__(self, name, doc, children, requires=None, properties=()):
+    def __init__(self, name, doc, children, requires=None, properties=None):
         """
         :param children: is a list of option descriptions (including
         ``OptionDescription`` instances for nested namespaces).
@@ -601,15 +591,15 @@ class OptionDescription(BaseInformation):
         if not valid_name(name):
             raise ValueError(_("invalid name: {0} for option descr").format(name))
         self._name = name
-        self._informations = {}
-        self.optimpl_set_information('doc', doc)
+        self._impl_informations = {}
+        self.impl_set_information('doc', doc)
         child_names = [child._name for child in children]
         #better performance like this
         valid_child = copy(child_names)
         valid_child.sort()
         old = None
         for child in valid_child:
-            if child == old:
+            if id(child) == id(old):
                 raise ConflictError(_('duplicate option name: '
                                       '{0}').format(child))
             old = child
@@ -618,15 +608,17 @@ class OptionDescription(BaseInformation):
         self._requires = requires
         self._cache_paths = None
         self._consistencies = None
+        if properties is None:
+            properties = tuple()
         if not isinstance(properties, tuple):
             raise TypeError(_('invalid properties type {0} for {1},'
                               ' must be a tuple').format(type(properties), self._name))
-        self._properties = properties  # 'hidden', 'disabled'...
+        self._properties = set(properties)  # 'hidden', 'disabled'...
         # the group_type is useful for filtering OptionDescriptions in a config
         self._group_type = groups.default
 
-    def optimpl_getdoc(self):
-        return self.optimpl_get_information('doc')
+    def impl_getdoc(self):
+        return self.impl_get_information('doc')
 
     def __getattr__(self, name):
         try:
@@ -635,32 +627,32 @@ class OptionDescription(BaseInformation):
             raise AttributeError(_('unknown Option {} in OptionDescription {}'
                                  '').format(name, self._name))
 
-    def optimpl_getkey(self, config):
-        return tuple([child.optimpl_getkey(getattr(config, child._name))
-                      for child in self.optimpl_getchildren()])
+    def impl_getkey(self, config):
+        return tuple([child.impl_getkey(getattr(config, child._name))
+                      for child in self.impl_getchildren()])
 
-    def optimpl_getpaths(self, include_groups=False, _currpath=None):
+    def impl_getpaths(self, include_groups=False, _currpath=None):
         """returns a list of all paths in self, recursively
            _currpath should not be provided (helps with recursion)
         """
         if _currpath is None:
             _currpath = []
         paths = []
-        for option in self.optimpl_getchildren():
+        for option in self.impl_getchildren():
             attr = option._name
             if isinstance(option, OptionDescription):
                 if include_groups:
                     paths.append('.'.join(_currpath + [attr]))
-                paths += option.optimpl_getpaths(include_groups=include_groups,
-                                                 _currpath=_currpath + [attr])
+                paths += option.impl_getpaths(include_groups=include_groups,
+                                              _currpath=_currpath + [attr])
             else:
                 paths.append('.'.join(_currpath + [attr]))
         return paths
 
-    def optimpl_getchildren(self):
+    def impl_getchildren(self):
         return self._children[1]
 
-    def optimpl_build_cache(self, cache_path=None, cache_option=None, _currpath=None, _consistencies=None):
+    def impl_build_cache(self, cache_path=None, cache_option=None, _currpath=None, _consistencies=None):
         if _currpath is None and self._cache_paths is not None:
             return
         if _currpath is None:
@@ -672,7 +664,7 @@ class OptionDescription(BaseInformation):
         if cache_path is None:
             cache_path = [self._name]
             cache_option = [self]
-        for option in self.optimpl_getchildren():
+        for option in self.impl_getchildren():
             attr = option._name
             if attr.startswith('_cfgimpl'):
                 continue
@@ -686,7 +678,7 @@ class OptionDescription(BaseInformation):
                             _consistencies.setdefault(opt, []).append((func, opts))
             else:
                 _currpath.append(attr)
-                option.optimpl_build_cache(cache_path, cache_option, _currpath, _consistencies)
+                option.impl_build_cache(cache_path, cache_option, _currpath, _consistencies)
                 _currpath.pop()
         if save:
             #valid no duplicated option
@@ -694,27 +686,27 @@ class OptionDescription(BaseInformation):
             valid_child.sort()
             old = None
             for child in valid_child:
-                if child == old:
+                if id(child) == id(old):
                     raise ConflictError(_('duplicate option: '
                                           '{0}').format(child))
                 old = child
             self._cache_paths = (tuple(cache_option), tuple(cache_path))
             self._consistencies = _consistencies
 
-    def optimpl_get_opt_by_path(self, path):
+    def impl_get_opt_by_path(self, path):
         try:
             return self._cache_paths[0][self._cache_paths[1].index(path)]
         except ValueError:
             raise AttributeError(_('no option for path {}').format(path))
 
-    def optimpl_get_path_by_opt(self, opt):
+    def impl_get_path_by_opt(self, opt):
         try:
             return self._cache_paths[1][self._cache_paths[0].index(opt)]
         except ValueError:
             raise AttributeError(_('no option {} found').format(opt))
 
     # ____________________________________________________________
-    def optimpl_set_group_type(self, group_type):
+    def impl_set_group_type(self, group_type):
         """sets a given group object to an OptionDescription
 
         :param group_type: an instance of `GroupType` or `MasterGroupType`
@@ -731,11 +723,11 @@ class OptionDescription(BaseInformation):
                 #for collect all slaves
                 slaves = []
                 master = None
-                for child in self.optimpl_getchildren():
+                for child in self.impl_getchildren():
                     if isinstance(child, OptionDescription):
                         raise ValueError(_("master group {} shall not have "
                                          "a subgroup").format(self._name))
-                    if not child.optimpl_is_multi():
+                    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))
@@ -749,7 +741,7 @@ class OptionDescription(BaseInformation):
                     raise ValueError(_('master group with wrong master name for {}'
                                      '').format(self._name))
                 master._master_slaves = tuple(slaves)
-                for child in self.optimpl_getchildren():
+                for child in self.impl_getchildren():
                     if child != master:
                         child._master_slaves = master
                         child._multitype = multitypes.slave
@@ -759,15 +751,17 @@ class OptionDescription(BaseInformation):
         else:
             raise ValueError(_('not allowed group_type : {0}').format(group_type))
 
-    def optimpl_get_group_type(self):
+    def impl_get_group_type(self):
         return self._group_type
 
-    def _valid_consistency(self, opt, value, context, index):
+    def _valid_consistency(self, opt, value, context=None, index=None):
         consistencies = self._consistencies.get(opt)
         if consistencies is not None:
             for consistency in consistencies:
                 func, opts = consistency
-                ret = getattr(opts[0], func)(opt, value, context, index, opts)
+                #ret = getattr(opts[0], func)(opt, value, context, index, opts)
+                ret = opts[0]._launch_consistency(func, opt, value, context,
+                                                  index, opts)
                 if ret is False:
                     return False
         return True
@@ -784,7 +778,7 @@ def validate_requires_arg(requires, name):
             if not isinstance(req[0], Option):
                 raise ValueError(_('malformed requirements first argument '
                                    'must be an option in option {0}').format(name))
-            if req[0].optimpl_is_multi():
+            if req[0].impl_is_multi():
                 raise ValueError(_('malformed requirements option {0} '
                                    'should not be a multi').format(name))
             if not req[0]._validate(req[1]):
index 533fd46..9c3c313 100644 (file)
@@ -21,6 +21,7 @@
 # the whole pypy projet is under MIT licence
 # ____________________________________________________________
 from time import time
+from copy import copy
 from tiramisu.error import RequirementRecursionError, PropertiesOptionError
 from tiramisu.i18n import _
 
@@ -152,22 +153,21 @@ class Property(object):
         self._properties = prop
 
     def append(self, propname):
-        if not propname in self._properties:
-            self._properties.append(propname)
-            self._setting._set_properties(self._properties, self._opt)
+        self._properties.add(propname)
+        self._setting._set_properties(self._properties, self._opt)
         self._setting.context.cfgimpl_reset_cache()
 
     def remove(self, propname):
         if propname in self._properties:
             self._properties.remove(propname)
             self._setting._set_properties(self._properties, self._opt)
-        self._setting.context.cfgimpl_reset_cache()
+            self._setting.context.cfgimpl_reset_cache()
 
     def __contains__(self, propname):
         return propname in self._properties
 
     def __repr__(self):
-        return str(self._properties)
+        return str(list(self._properties))
 
 
 #____________________________________________________________
@@ -178,7 +178,7 @@ class Setting(object):
     def __init__(self, context):
         # properties attribute: the name of a property enables this property
         # key is None for global properties
-        self._properties = {None: ['expire']}
+        self._properties = {None: set(('expire',))}
         # permissive properties
         self._permissives = {}
         # generic owner
@@ -192,7 +192,7 @@ class Setting(object):
         return propname in self._get_properties()
 
     def __repr__(self):
-        return str(self._get_properties())
+        return str(list(self._get_properties()))
 
     def __getitem__(self, opt):
         return Property(self, self._get_properties(opt), opt)
@@ -202,7 +202,7 @@ class Setting(object):
 
     def _get_properties(self, opt=None, is_apply_req=True):
         if opt is None:
-            props = self._properties.get(opt, [])
+            props = self._properties.get(opt, set())
         else:
             exp = None
             if opt in self._cache:
@@ -212,8 +212,7 @@ class Setting(object):
                     return props
             if is_apply_req:
                 apply_requires(opt, self.context)
-            default = list(opt._properties)
-            props = self._properties.get(opt, default)
+            props = self._properties.get(opt, opt._properties)
             self._set_cache(opt, props, exp)
         return props
 
@@ -243,24 +242,23 @@ class Setting(object):
                             value=None, force_permissive=False,
                             force_properties=None):
         #opt properties
-        properties = set(self._get_properties(opt_or_descr))
+        properties = copy(self._get_properties(opt_or_descr))
         #remove opt permissive
-        properties -= frozenset(self._get_permissive(opt_or_descr))
+        properties -= self._get_permissive(opt_or_descr)
         #remove global permissive if need
-        self_properties = self._get_properties()
+        self_properties = copy(self._get_properties())
         if force_permissive is True or 'permissive' in self_properties:
-            properties -= frozenset(self._get_permissive())
+            properties -= self._get_permissive()
 
         #global properties
-        set_properties = set(self_properties)
         if force_properties is not None:
-            set_properties.update(frozenset(force_properties))
+            self_properties.update(force_properties)
 
         #calc properties
-        properties &= set_properties
+        properties &= self_properties
         #mandatory and frozen are special properties
         if is_descr:
-            properties -= frozenset(['mandatory', 'frozen'])
+            properties -= frozenset(('mandatory', 'frozen'))
         else:
             if 'mandatory' in properties and \
                     not self.context.cfgimpl_get_values()._is_empty(opt_or_descr,
@@ -281,12 +279,12 @@ class Setting(object):
                                         list(properties))
 
     def _get_permissive(self, opt=None):
-        return self._permissives.get(opt, [])
+        return self._permissives.get(opt, frozenset())
 
     def set_permissive(self, permissive, opt=None):
         if not isinstance(permissive, tuple):
             raise TypeError(_('permissive must be a tuple'))
-        self._permissives[opt] = permissive
+        self._permissives[opt] = frozenset(permissive)
 
     #____________________________________________________________
     def setowner(self, owner):
@@ -347,7 +345,7 @@ def apply_requires(opt, config):
         setting = Property(settings, settings._get_properties(opt, False), opt)
         trigger_actions = build_actions(opt._requires)
         descr = config.cfgimpl_get_context().cfgimpl_get_description()
-        optpath = descr.optimpl_get_path_by_opt(opt)
+        optpath = descr.impl_get_path_by_opt(opt)
         for requires in trigger_actions.values():
             matches = False
             for require in requires:
@@ -356,7 +354,7 @@ def apply_requires(opt, config):
                     inverse = False
                 elif len(require) == 4:
                     option, expected, action, inverse = require
-                path = descr.optimpl_get_path_by_opt(option)
+                path = descr.impl_get_path_by_opt(option)
                 if path == optpath or path.startswith(optpath + '.'):
                     raise RequirementRecursionError(_("malformed requirements "
                                                     "imbrication detected for option: '{0}' "
index c69b588..47046a0 100644 (file)
@@ -44,14 +44,14 @@ class Values(object):
         if meta is not None:
             return meta.cfgimpl_get_values()[opt]
         else:
-            return opt.optimpl_getdefault()
+            return opt.impl_getdefault()
 
     def _get_value(self, opt):
         "return value or default value if not set"
         #if no value
         if opt not in self._values:
             value = self._get_default(opt)
-            if opt.optimpl_is_multi():
+            if opt.impl_is_multi():
                 value = Multi(value, self.context, opt)
         else:
             #if value
@@ -63,15 +63,17 @@ class Values(object):
 
     def _reset(self, opt):
         if opt in self._values:
+            setting = self.context.cfgimpl_get_settings()
+            opt.impl_validate(opt.impl_getdefault(), self.context, 'validator' in setting)
             self.context.cfgimpl_reset_cache()
             del(self._values[opt])
 
     def _is_empty(self, opt, value):
         "convenience method to know if an option is empty"
         empty = opt._empty
-        if (not opt.optimpl_is_multi() and (value is None or value == empty)) or \
-           (opt.optimpl_is_multi() and (value == [] or
-                                        None in value or empty in value)):
+        if (not opt.impl_is_multi() and (value is None or value == empty)) or \
+           (opt.impl_is_multi() and (value == [] or
+                                     None in value or empty in value)):
             return True
         return False
 
@@ -97,7 +99,8 @@ class Values(object):
             if exp < created:
                 return value
         val = self._getitem(opt, validate, force_permissive, force_properties)
-        self._set_cache(opt, val)
+        if validate:
+            self._set_cache(opt, val)
         return val
 
     def _getitem(self, opt, validate, force_permissive, force_properties):
@@ -105,7 +108,7 @@ class Values(object):
         setting = self.context.cfgimpl_get_settings()
         value = self._get_value(opt)
         is_frozen = 'frozen' in setting[opt]
-        if opt.optimpl_has_callback():
+        if opt.impl_has_callback():
             #if value is set and :
             # - not frozen
             # - frozen and not force_default_on_freeze
@@ -115,18 +118,17 @@ class Values(object):
                 pass
             else:
                 value = self._getcallback_value(opt)
-                if opt.optimpl_is_multi():
+                if opt.impl_is_multi():
                     value = Multi(value, self.context, opt)
                 #suppress value if already set
                 self._reset(opt)
         # frozen and force default
         elif is_frozen and 'force_default_on_freeze' in setting[opt]:
             value = self._get_default(opt)
-            if opt.optimpl_is_multi():
+            if opt.impl_is_multi():
                 value = Multi(value, self.context, opt)
-        if validate and not opt.optimpl_validate(value, self.context, 'validator' in setting):
-            raise ValueError(_('invalid calculated value returned'
-                             ' for option {0}: {1}').format(opt._name, value))
+        if validate:
+            opt.impl_validate(value, self.context, 'validator' in setting)
         if self.is_default_owner(opt) and \
                 'force_store_value' in setting[opt]:
             self.setitem(opt, value, is_write=False)
@@ -142,11 +144,9 @@ class Values(object):
         #is_write is, for example, used with "force_store_value"
         #user didn't change value, so not write
         #valid opt
-        if not opt.optimpl_validate(value, self.context,
-                                    'validator' in self.context.cfgimpl_get_settings()):
-            raise ValueError(_('invalid value {}'
-                             ' for option {}').format(value, opt._name))
-        if opt.optimpl_is_multi() and not isinstance(value, Multi):
+        opt.impl_validate(value, self.context,
+                          'validator' in self.context.cfgimpl_get_settings())
+        if opt.impl_is_multi() and not isinstance(value, Multi):
             value = Multi(value, self.context, opt)
         self._setvalue(opt, value, force_permissive=force_permissive,
                        is_write=is_write)
@@ -219,16 +219,16 @@ class Multi(list):
         self.context = context
         if not isinstance(value, list):
             value = [value]
-        if self.opt.optimpl_get_multitype() == multitypes.slave:
+        if self.opt.impl_get_multitype() == multitypes.slave:
             value = self._valid_slave(value)
-        elif self.opt.optimpl_get_multitype() == multitypes.master:
+        elif self.opt.impl_get_multitype() == multitypes.master:
             self._valid_master(value)
         super(Multi, self).__init__(value)
 
     def _valid_slave(self, value):
         #if slave, had values until master's one
-        masterp = self.context.cfgimpl_get_description().optimpl_get_path_by_opt(
-            self.opt.optimpl_get_master_slaves())
+        masterp = self.context.cfgimpl_get_description().impl_get_path_by_opt(
+            self.opt.impl_get_master_slaves())
         mastervalue = getattr(self.context, masterp)
         masterlen = len(mastervalue)
         if len(value) > masterlen or (len(value) < masterlen and
@@ -238,7 +238,7 @@ class Multi(list):
                                    self.opt._name, masterp))
         elif len(value) < masterlen:
             for num in range(0, masterlen - len(value)):
-                value.append(self.opt.optimpl_getdefault_multi())
+                value.append(self.opt.impl_getdefault_multi())
         #else: same len so do nothing
         return value
 
@@ -255,7 +255,7 @@ class Multi(list):
                                            self.opt._name, slave._name))
                 elif len(value_slave) < masterlen:
                     for num in range(0, masterlen - len(value_slave)):
-                        value_slave.append(slave.optimpl_getdefault_multi(), force=True)
+                        value_slave.append(slave.impl_getdefault_multi(), force=True)
 
     def __setitem__(self, key, value):
         self._validate(value)
@@ -268,14 +268,14 @@ class Multi(list):
         only if the option is a master
         """
         if not force:
-            if self.opt.optimpl_get_multitype() == multitypes.slave:
+            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))
-            elif self.opt.optimpl_get_multitype() == multitypes.master:
-                for slave in self.opt.optimpl_get_master_slaves():
+            elif self.opt.impl_get_multitype() == multitypes.master:
+                for slave in self.opt.impl_get_master_slaves():
                     values = self.context.cfgimpl_get_values()
                     if not values.is_default_owner(slave):
-                        values[slave].append(slave.optimpl_getdefault_multi(),
+                        values[slave].append(slave.impl_getdefault_multi(),
                                              force=True)
         self._validate(value)
         #assume not checking mandatory property
@@ -296,11 +296,11 @@ class Multi(list):
         :return: the requested element
         """
         if not force:
-            if self.opt.optimpl_get_multitype() == multitypes.slave:
+            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))
-            elif self.opt.optimpl_get_multitype() == multitypes.master:
-                for slave in self.opt.optimpl_get_master_slaves():
+            elif self.opt.impl_get_multitype() == multitypes.master:
+                for slave in self.opt.impl_get_master_slaves():
                     self.context.cfgimpl_get_values()[slave].pop(key, force=True)
         self.context.cfgimpl_get_values()._setvalue(self.opt, self)
         return super(Multi, self).pop(key)