From 6de65859b4828d5f26634ca984eda81d1ab28502 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 8 May 2013 18:14:42 +0200 Subject: [PATCH] * config herite from BaseInformation class * _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) --- test/test_config.py | 8 + test/test_config_domain.py | 37 +++ test/test_config_ip.py | 45 ++++ test/test_metaconfig.py | 12 +- test/test_option_calculation.py | 271 ++++++++++++++++++++ test/test_option_consistency.py | 312 +++++------------------ test/test_option_default.py | 2 +- test/test_option_setting.py | 10 +- test/test_option_type.py | 2 +- test/test_option_with_special_name.py | 2 +- test/test_parsing_group.py | 44 ++-- test/test_slots.py | 54 ++++ tiramisu/config.py | 101 ++++---- tiramisu/option.py | 350 +++++++++++++------------- tiramisu/setting.py | 42 ++-- tiramisu/value.py | 60 ++--- 16 files changed, 781 insertions(+), 571 deletions(-) create mode 100644 test/test_config_domain.py create mode 100644 test/test_config_ip.py create mode 100644 test/test_option_calculation.py create mode 100644 test/test_slots.py diff --git a/test/test_config.py b/test/test_config.py index 819c606..6c6f91d 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -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 index 0000000..ab6e00e --- /dev/null +++ b/test/test_config_domain.py @@ -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 index 0000000..7512384 --- /dev/null +++ b/test/test_config_ip.py @@ -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' diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index 3e2c924..f20f4dd 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -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 index 0000000..0f76c2f --- /dev/null +++ b/test/test_option_calculation.py @@ -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") diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 0d39d5b..9af6452 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -1,270 +1,70 @@ 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 +from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ + OptionDescription -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 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 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_consistency_default(): + a = IntOption('a', '', 1) + b = IntOption('b', '', 1) + raises(ValueError, "a.impl_add_consistency('not_equal', (a, b))") -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 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 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 +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'") -# 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") +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'") diff --git a/test/test_option_default.py b/test/test_option_default.py index f155b8d..3f3cf8a 100644 --- a/test/test_option_default.py +++ b/test/test_option_default.py @@ -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(): diff --git a/test/test_option_setting.py b/test/test_option_setting.py index 56ba2cb..5f23796 100644 --- a/test/test_option_setting.py +++ b/test/test_option_setting.py @@ -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' diff --git a/test/test_option_type.py b/test/test_option_type.py index b678ebb..e4a3656 100644 --- a/test/test_option_type.py +++ b/test/test_option_type.py @@ -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] diff --git a/test/test_option_with_special_name.py b/test/test_option_with_special_name.py index 94b94e1..483aca0 100644 --- a/test/test_option_with_special_name.py +++ b/test/test_option_with_special_name.py @@ -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) diff --git a/test/test_parsing_group.py b/test/test_parsing_group.py index 2c6eaad..18b1de1 100644 --- a/test/test_parsing_group.py +++ b/test/test_parsing_group.py @@ -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 index 0000000..0104e84 --- /dev/null +++ b/test/test_slots.py @@ -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") diff --git a/tiramisu/config.py b/tiramisu/config.py index c98daa9..a8608ff 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -22,15 +22,16 @@ # ____________________________________________________________ #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 diff --git a/tiramisu/option.py b/tiramisu/option.py index 038cee8..6fc9395 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -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 + 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 optimpl_getdefault(self, default_multi=False): + 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]): diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 533fd46..9c3c313 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -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}' " diff --git a/tiramisu/value.py b/tiramisu/value.py index c69b588..47046a0 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -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)