diff --git a/test/test_choice_option.py b/test/test_choice_option.py index 9b3d504..f636acc 100644 --- a/test/test_choice_option.py +++ b/test/test_choice_option.py @@ -5,6 +5,7 @@ do_autopath() from tiramisu.setting import owners from tiramisu.option import ChoiceOption, StrOption, OptionDescription from tiramisu.config import Config +from tiramisu.error import ConfigError from py.test import raises @@ -86,3 +87,18 @@ def test_choiceoption_calc_opt_multi_function(): assert cfg.getowner(ch) == owners.default # raises(ValueError, "cfg.ch2") + + +def test_choiceoption_calc_invalid(): + st = StrOption('st', '', ['val1'], multi=True) + st + raises(ValueError, "ch = ChoiceOption('ch', '', default_multi='val2', values=[1, 2, 3], values_params={'': ((st, False),)}, multi=True)") + + +def test_choiceoption_calc_not_list(): + st = StrOption('st', '', 'val1') + ch = ChoiceOption('ch', "", default_multi='val2', values=return_val, values_params={'': ((st, False),)}, multi=True) + od = OptionDescription('od', '', [st, ch]) + cfg = Config(od) + cfg.read_write() + raises(ConfigError, "cfg.ch = ['val1']") diff --git a/test/test_config.py b/test/test_config.py index d71c709..28e5ee9 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -49,6 +49,14 @@ def test_base_config(): assert dm.impl_getname() == 'dummy' +def test_base_config_name(): + gcdummy = BoolOption('dummy', 'dummy', default=False) + descr = OptionDescription('tiramisu', '', [gcdummy]) + cfg = Config(descr, name='cfg') + cfg.impl_getname() == 'cfg' + raises(ValueError, "Config(descr, name='unvalid name')") + + def test_not_config(): assert raises(TypeError, "Config('str')") @@ -324,6 +332,15 @@ def test_delete_config_with_subconfig(): raises(ConfigError, 'sub.make_dict()') +def test_subconfig(): + i = IntOption('i', '') + o = OptionDescription('val', '', [i]) + o2 = OptionDescription('val', '', [o]) + c = Config(o2) + raises(TypeError, "SubConfig(i, weakref.ref(c))") + pass + + def test_config_weakref(): o = OptionDescription('val', '', []) o2 = OptionDescription('val', '', [o]) diff --git a/test/test_config_domain.py b/test/test_config_domain.py index ed8dac8..3c0fb8f 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -85,7 +85,7 @@ def test_domainname_warning(): raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamean'") c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nd' c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnameto.olongthathavemorethanmaximumsizeforatruedomainnameanditsnoteas.ytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowie' - raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnameto.olongthathavemorethanmaximumsizeforatruedomainnameanditsnoteas.ytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowien'") + raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainname.nditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnamet.olongthathavemorethanmaximumsizeforatruedomainnameanditsnotea.ytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowie.xxxx'") c.f = 'd' c.f = 'd.t' # diff --git a/test/test_config_ip.py b/test/test_config_ip.py index b4359f4..c5c61b9 100644 --- a/test/test_config_ip.py +++ b/test/test_config_ip.py @@ -1,16 +1,20 @@ from autopath import do_autopath do_autopath() +import warnings from py.test import raises from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, \ PortOption, OptionDescription +from tiramisu.error import ValueWarning def test_ip(): a = IPOption('a', '') b = IPOption('b', '', private_only=True) - od = OptionDescription('od', '', [a, b]) + d = IPOption('d', '', warnings_only=True, private_only=True) + warnings.simplefilter("always", ValueWarning) + od = OptionDescription('od', '', [a, b, d]) c = Config(od) c.a = '192.168.1.1' c.a = '192.168.1.0' @@ -24,10 +28,9 @@ def test_ip(): raises(ValueError, "c.b = '255.255.255.0'") raises(ValueError, "IPOption('a', 'ip', default='192.000.023.01')") - d = IPOption('a', 'ip', default='192.0.23.1') - od = OptionDescription('od', '', [d]) - c = Config(od) - raises(ValueError, "c.a = '192.000.023.01'") + with warnings.catch_warnings(record=True) as w: + c.d = '88.88.88.88' + assert len(w) == 1 def test_ip_default(): @@ -40,21 +43,31 @@ def test_ip_default(): def test_ip_reserved(): a = IPOption('a', '') b = IPOption('b', '', allow_reserved=True) - od = OptionDescription('od', '', [a, b]) - c = Config(od) - raises(ValueError, "c.a = '226.94.1.1'") - c.b = '226.94.1.1' + c = IPOption('c', '', warnings_only=True) + od = OptionDescription('od', '', [a, b, c]) + warnings.simplefilter("always", ValueWarning) + cfg = Config(od) + raises(ValueError, "cfg.a = '226.94.1.1'") + cfg.b = '226.94.1.1' + with warnings.catch_warnings(record=True) as w: + cfg.c = '226.94.1.1' + assert len(w) == 1 def test_network(): a = NetworkOption('a', '') - od = OptionDescription('od', '', [a]) + b = NetworkOption('b', '', warnings_only=True) + od = OptionDescription('od', '', [a, b]) + warnings.simplefilter("always", ValueWarning) 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' raises(ValueError, "c.a = '255.255.255.0'") + with warnings.catch_warnings(record=True) as w: + c.b = '255.255.255.0' + assert len(w) == 1 def test_netmask(): diff --git a/test/test_dyn_optiondescription.py b/test/test_dyn_optiondescription.py index 381bfd6..b82a4e8 100644 --- a/test/test_dyn_optiondescription.py +++ b/test/test_dyn_optiondescription.py @@ -707,6 +707,88 @@ def test_consistency_dyndescription(): raises(ValueError, "cfg.od.dodval2.stval2 = 'yes'") +def test_consistency_dyndescription_default(): + st = StrOption('st', '', 'yes') + st2 = StrOption('st2', '') + dod = DynOptionDescription('dod', '', [st, st2], callback=return_list) + od = OptionDescription('od', '', [dod]) + st.impl_add_consistency('not_equal', st2) + od2 = OptionDescription('od', '', [od]) + cfg = Config(od2) + cfg + raises(ValueError, "cfg.od.dodval1.st2val1 = 'yes'") + raises(ValueError, "cfg.od.dodval2.st2val2 = 'yes'") + + +def test_consistency_dyndescription_multi(): + st = StrOption('st', '', multi=True) + st2 = StrOption('st2', '', multi=True) + dod = DynOptionDescription('dod', '', [st, st2], callback=return_list) + od = OptionDescription('od', '', [dod]) + st.impl_add_consistency('not_equal', st2) + od2 = OptionDescription('od', '', [od]) + cfg = Config(od2) + cfg.od.dodval1.stval1.append('yes') + raises(ValueError, "cfg.od.dodval1.st2val1.append('yes')") + cfg.od.dodval2.stval2.append('yes') + raises(ValueError, "cfg.od.dodval2.st2val2.append('yes')") + raises(ValueError, "cfg.od.dodval1.st2val1.append('yes')") + del(cfg.od.dodval2.stval2) + raises(ValueError, "cfg.od.dodval1.st2val1.append('yes')") + cfg.od.dodval2.st2val2.append('yes') + raises(ValueError, "cfg.od.dodval2.stval2.append('yes')") + + +def test_consistency_dyndescription_default_multi(): + st = StrOption('st', '', ['yes'], multi=True) + st2 = StrOption('st2', '', multi=True) + dod = DynOptionDescription('dod', '', [st, st2], callback=return_list) + od = OptionDescription('od', '', [dod]) + st.impl_add_consistency('not_equal', st2) + od2 = OptionDescription('od', '', [od]) + cfg = Config(od2) + raises(ValueError, "cfg.od.dodval1.st2val1.append('yes')") + raises(ValueError, "cfg.od.dodval1.st2val1.append('yes')") + cfg.od.dodval1.stval1.append('yes') + + +def test_consistency_dyndescription_default_multi2(): + st = StrOption('st', '', ['yes'], multi=True) + st2 = StrOption('st2', '', ['yes'], multi=True) + dod = DynOptionDescription('dod', '', [st, st2], callback=return_list) + dod + raises(ValueError, "st.impl_add_consistency('not_equal', st2)") + + +def test_consistency_only_one_dyndescription(): + st = StrOption('st', '') + st + st2 = StrOption('st2', '') + DynOptionDescription('dod', '', [st2], callback=return_list) + raises(ConfigError, "st.impl_add_consistency('not_equal', st2)") + raises(ConfigError, "st2.impl_add_consistency('not_equal', st)") + + +def test_consistency_became_dyndescription(): + st = StrOption('st', '') + st2 = StrOption('st2', '') + st2.impl_add_consistency('not_equal', st) + od = DynOptionDescription('dod', '', [st2], callback=return_list) + od2 = OptionDescription('od', '', [od, st]) + od2 + raises(ConfigError, "c = Config(od2)") + + +def test_consistency_became_dyndescription2(): + st = StrOption('st', '') + st2 = StrOption('st2', '') + st.impl_add_consistency('not_equal', st2) + od = DynOptionDescription('dod', '', [st2], callback=return_list) + od2 = OptionDescription('od', '', [od, st]) + od2 + raises(ConfigError, "c = Config(od2)") + + def test_consistency_external_dyndescription(): st = StrOption('st', '') st1 = StrOption('st1', '') diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index b9487fb..69c4087 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -24,7 +24,7 @@ def make_description(): conf2 = Config(od2, name='conf2') conf1.read_write() conf2.read_write() - meta = MetaConfig([conf1, conf2]) + meta = MetaConfig([conf1, conf2], name='meta') meta.cfgimpl_get_settings().setowner(owners.meta) return meta @@ -168,6 +168,9 @@ def test_meta_meta_set(): raises(AttributeError, "meta2.find_firsts(byname='i1', byvalue=10)") raises(AttributeError, "meta2.find_firsts(byname='not', byvalue=10)") raises(AttributeError, "meta2.find_firsts(byname='i6')") + raises(ValueError, "meta2.set_value('od1.i6', 7, only_config=True, force_default=True)") + raises(ValueError, "meta2.set_value('od1.i6', 7, only_config=True, force_default_if_same=True)") + raises(ValueError, "meta2.set_value('od1.i6', 7, only_config=True, force_dont_change_value=True)") def test_not_meta(): @@ -200,6 +203,21 @@ def test_group_find_firsts(): assert [conf1, conf2] == grp.find_firsts(byname='i1').cfgimpl_get_children() +def test_group_group(): + i1 = IntOption('i1', '') + od1 = OptionDescription('od1', '', [i1]) + od2 = OptionDescription('od2', '', [od1]) + conf1 = Config(od2, name='conf1') + conf2 = Config(od2, name='conf2') + grp = GroupConfig([conf1, conf2]) + raises(ValueError, "GroupConfig([grp])") + grp = GroupConfig([conf1, conf2], 'grp') + grp2 = GroupConfig([grp]) + grp2.set_value('od1.i1', 2) + assert grp2.grp.conf1.od1.i1 == 2 + assert grp2.grp.conf1.getowner(i1) == owners.user + + def test_meta_path(): meta = make_description() assert meta._impl_path is None diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index a269702..021f6e4 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -869,3 +869,10 @@ def test_masterslaves_callback_description(): assert cfg.od.st.st1.st1 == ['yes'] assert cfg.od.st.st1.st2 == ['yes'] assert cfg.getowner(st1) == owner + + +def test_re_set_callback(): + st1 = StrOption('st1', "", multi=True) + st2 = StrOption('st2', "", multi=True) + st2.impl_set_callback(return_value, {'': ((st1, False),)}) + raises(ConfigError, "st2.impl_set_callback(return_value, {'': ((st1, False),)})") diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index c6a0f09..323f6e4 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -6,7 +6,7 @@ from py.test import raises from tiramisu.setting import owners, groups from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ - BroadcastOption, SymLinkOption, OptionDescription + BroadcastOption, SymLinkOption, OptionDescription, submulti from tiramisu.error import ConfigError, ValueWarning, PropertiesOptionError import warnings @@ -14,7 +14,6 @@ import warnings def test_consistency(): a = IntOption('a', '') b = IntOption('b', '') - od = OptionDescription('od', '', [a, b]) a.impl_add_consistency('not_equal', b) #consistency to itself raises(ConfigError, "a.impl_add_consistency('not_equal', a)") @@ -25,17 +24,26 @@ def test_consistency(): def test_consistency_not_exists(): a = IntOption('a', '') b = IntOption('b', '') - od = OptionDescription('od', '', [a, b]) + a, b raises(ConfigError, "a.impl_add_consistency('not_exists', b)") def test_consistency_unknown_params(): a = IntOption('a', '') b = IntOption('b', '') - od = OptionDescription('od', '', [a, b]) + a, b raises(ValueError, "a.impl_add_consistency('not_equal', b, unknown=False)") +def test_consistency_warnings_only_default(): + a = IntOption('a', '', 1) + b = IntOption('b', '', 1) + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + a.impl_add_consistency('not_equal', b, warnings_only=True) + assert w != [] + + def test_consistency_warnings_only(): a = IntOption('a', '') b = IntOption('b', '') @@ -99,8 +107,8 @@ def test_consistency_not_in_config_1(): b = IntOption('b', '') a.impl_add_consistency('not_equal', b) od1 = OptionDescription('od1', '', [a]) - od2 = OptionDescription('od2', '', [b]) od = OptionDescription('root', '', [od1]) + od raises(ConfigError, "Config(od)") @@ -121,6 +129,7 @@ def test_consistency_not_in_config_3(): od1 = OptionDescription('od1', '', [a]) od2 = OptionDescription('od2', '', [b]) od = OptionDescription('root', '', [od1, od2]) + od #with subconfig raises(ConfigError, "Config(od.od1)") @@ -145,6 +154,34 @@ def test_consistency_not_equal_symlink(): assert set(od._cache_consistencies.keys()) == set([a, b]) +def test_consistency_not_equal_submulti(): + a = IntOption('a', '', multi=submulti) + b = IntOption('b', '', multi=submulti) + od = OptionDescription('od', '', [a, b]) + a.impl_add_consistency('not_equal', b) + c = Config(od) + assert c.a == [] + assert c.b == [] + c.a = [[1]] + del(c.a) + c.a = [[1]] + raises(ValueError, "c.b = [[1]]") + c.a = [[1, 2]] + c.b = [[3]] + c.b = [[3, 1]] + c.b = [[3]] + c.b[0].append(1) + c.b = [[3], [1]] + + +def test_consistency_not_equal_default_submulti(): + a = IntOption('a', '', [[1, 2]], multi=submulti) + b = IntOption('b', '', [[1]], multi=submulti) + od = OptionDescription('od', '', [a, b]) + od + raises(ValueError, "a.impl_add_consistency('not_equal', b)") + + def test_consistency_not_equal_multi(): a = IntOption('a', '', multi=True) b = IntOption('b', '', multi=True) @@ -160,9 +197,25 @@ def test_consistency_not_equal_multi(): c.b = [2] +def test_consistency_not_equal_multi_default(): + a = IntOption('a', '', multi=True) + b = IntOption('b', '', multi=True, default_multi=1) + od = OptionDescription('od', '', [a, b]) + a.impl_add_consistency('not_equal', b) + c = Config(od) + assert c.a == [] + assert c.b == [] + 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) + a, b raises(ValueError, "a.impl_add_consistency('not_equal', b)") @@ -170,6 +223,7 @@ def test_consistency_default_multi(): a = IntOption('a', '', [2, 1], multi=True) b = IntOption('b', '', [1, 1], multi=True) c = IntOption('c', '', [1, 2], multi=True) + b raises(ValueError, "a.impl_add_consistency('not_equal', b)") a.impl_add_consistency('not_equal', c) @@ -221,8 +275,11 @@ def test_consistency_ip_in_network(): a = NetworkOption('a', '') b = NetmaskOption('b', '') c = IPOption('c', '') - od = OptionDescription('od', '', [a, b, c]) + d = IPOption('d', '') + od = OptionDescription('od', '', [a, b, c, d]) c.impl_add_consistency('in_network', a, b) + d.impl_add_consistency('in_network', a, b, warnings_only=True) + warnings.simplefilter("always", ValueWarning) cfg = Config(od) cfg.a = '192.168.1.0' cfg.b = '255.255.255.0' @@ -230,6 +287,9 @@ def test_consistency_ip_in_network(): raises(ValueError, "cfg.c = '192.168.2.1'") raises(ValueError, "cfg.c = '192.168.1.0'") raises(ValueError, "cfg.c = '192.168.1.255'") + with warnings.catch_warnings(record=True) as w: + cfg.d = '192.168.2.1' + assert len(w) == 1 def test_consistency_ip_in_network_len_error(): @@ -239,6 +299,7 @@ def test_consistency_ip_in_network_len_error(): od = OptionDescription('od', '', [a, b, c]) c.impl_add_consistency('in_network', a) cfg = Config(od) + cfg raises(ConfigError, "cfg.a = '192.168.2.0'") @@ -257,7 +318,7 @@ def test_consistency_ip_netmask_network_error(): def test_consistency_ip_netmask_error_multi(): a = IPOption('a', '', multi=True) b = NetmaskOption('b', '') - od = OptionDescription('od', '', [a, b]) + OptionDescription('od', '', [a, b]) raises(ConfigError, "b.impl_add_consistency('ip_netmask', a)") @@ -449,17 +510,17 @@ def test_consistency_broadcast_default_1(): a = NetworkOption('a', '', '192.168.1.0') b = NetmaskOption('b', '', '255.255.255.128') c = BroadcastOption('c', '', '192.168.2.127') - d = BroadcastOption('d', '', '192.168.1.127') od = OptionDescription('a', '', [a, b, c]) + od raises(ValueError, "c.impl_add_consistency('broadcast', a, b)") def test_consistency_broadcast_default_2(): a = NetworkOption('a', '', '192.168.1.0') b = NetmaskOption('b', '', '255.255.255.128') - c = BroadcastOption('c', '', '192.168.2.127') d = BroadcastOption('d', '', '192.168.1.127') od2 = OptionDescription('a', '', [a, b, d]) + od2 d.impl_add_consistency('broadcast', a, b) diff --git a/test/test_submulti.py b/test/test_submulti.py index b97ec2a..d43035e 100644 --- a/test/test_submulti.py +++ b/test/test_submulti.py @@ -11,8 +11,11 @@ from tiramisu.error import SlaveError from py.test import raises -def return_val(): - return 'val' +def return_val(val=None): + if val is None: + return 'val' + else: + return val def return_list(value=None): @@ -319,6 +322,8 @@ def test_callback_submulti_list(): cfg.multi.append() assert cfg.getowner(multi) == owner assert cfg.multi == [['val', 'val'], ['val', 'val']] + cfg.multi.append() + assert cfg.multi == [['val', 'val'], ['val', 'val'], ['val', 'val']] del(cfg.multi) assert cfg.getowner(multi) == owners.default cfg.multi[0].append() @@ -342,6 +347,8 @@ def test_callback_submulti_list_list(): cfg.multi[0].append() assert cfg.getowner(multi) == owner assert cfg.multi == [['val', 'val', None]] + del(cfg.multi) + cfg.multi.append() #FIXME multi sur une master @@ -639,3 +646,20 @@ def test__master_is_submulti(): cfg.ip_admin_eth0.ip_admin_eth0[0].pop(0) assert cfg.ip_admin_eth0.ip_admin_eth0 == [["192.168.1.1"], ["192.168.230.147"]] raises(ValueError, 'cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.1.1", "192.168.1.1"]') + + +def test_callback_submulti(): + multi = StrOption('multi', '', multi=submulti) + multi2 = StrOption('multi2', '', multi=submulti, callback=return_val, callback_params={'': ((multi, False),)}) + od = OptionDescription('multi', '', [multi, multi2]) + cfg = Config(od) + cfg.read_write() + owner = cfg.cfgimpl_get_settings().getowner() + assert cfg.getowner(multi) == owners.default + assert cfg.multi == [] + assert cfg.multi2 == [] + cfg.multi.append(['val']) + assert cfg.getowner(multi) == owner + assert cfg.getowner(multi2) == owners.default + assert cfg.multi == [['val']] + assert cfg.multi2 == [['val']] diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 73a8503..acb342b 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -139,7 +139,6 @@ def carry_out_calculation(option, context, callback, callback_params, # if callback_params has a callback, launch several time calculate() master_slave = False # multi's option should have same value for all option - len_multi = None try: if option._is_subdyn(): tcparams[''] = [(option.impl_getsuffix(), False)] @@ -182,7 +181,6 @@ def carry_out_calculation(option, context, callback, callback_params, if opt.impl_is_master_slaves() and \ opt.impl_get_master_slaves().in_same_group(option): - len_multi = len(value) master_slave = True is_multi = True else: @@ -197,32 +195,20 @@ def carry_out_calculation(option, context, callback, callback_params, # if no index, return a list if master_slave: ret = [] - if index is not undefined: - # for example if append master and get a no default slave without - # getting master - range_ = [index] - else: - range_ = range(len_multi) - for incr in range_: - args = [] - kwargs = {} - for key, couples in tcparams.items(): - for couple in couples: - value, ismulti = couple - if ismulti: - val = value[incr] - else: - val = value - if key == '': - args.append(val) - else: - kwargs[key] = val - calc = calculate(callback, args, kwargs) - if index is not undefined: - ret = calc - else: - ret.append(calc) - return ret + args = [] + kwargs = {} + for key, couples in tcparams.items(): + for couple in couples: + value, ismulti = couple + if ismulti: + val = value[index] + else: + val = value + if key == '': + args.append(val) + else: + kwargs[key] = val + return calculate(callback, args, kwargs) else: # no value is multi # return a single value @@ -236,15 +222,9 @@ def carry_out_calculation(option, context, callback, callback_params, else: kwargs[key] = couple[0] ret = calculate(callback, args, kwargs) - if callback_params != {}: - if isinstance(ret, list) and index is not undefined: - if option.impl_is_master_slaves('slave'): - raise SlaveError(_("callback cannot return a list for a " - "slave option ({0})").format(path)) - if len(ret) < index + 1: - ret = None - else: - ret = ret[index] + if callback_params != {} and isinstance(ret, list) and index is not undefined: + raise SlaveError(_("callback cannot return a list for a " + "slave option ({0})").format(path)) return ret diff --git a/tiramisu/config.py b/tiramisu/config.py index d7dd69f..8e3eb47 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -669,9 +669,6 @@ class Config(_CommonConfig): def impl_getname(self): return self._impl_name - def impl_setname(self, name): - self._impl_name = name - class GroupConfig(_CommonConfig): __slots__ = ('__weakref__', '_impl_children', '_impl_name') @@ -684,12 +681,10 @@ class GroupConfig(_CommonConfig): for child in children: if not isinstance(child, _CommonConfig): raise ValueError(_("groupconfig's children must be Config, MetaConfig or GroupConfig")) - name = child._impl_name - if name is None: + name_ = child._impl_name + if name_ is None: raise ValueError(_('name must be set to config before creating groupconfig')) - #if name in names: - # raise ValueError(_('config name must be uniq in groupconfig')) - names.append(name) + names.append(name_) if len(names) != len(set(names)): for idx in xrange(1, len(names) + 1): name = names.pop(0) @@ -827,6 +822,9 @@ class MetaConfig(GroupConfig): def set_value(self, path, value, force_default=False, force_dont_change_value=False, force_default_if_same=False, only_config=False): + """only_config: could be set if you want modify value in all Config included in + this MetaConfig + """ if only_config: if force_default or force_default_if_same or force_dont_change_value: raise ValueError(_('force_default, force_default_if_same or ' diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 3f1867e..8629672 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -140,6 +140,7 @@ class Base(StorageBase): default = [] self.impl_validate(default) self._set_default_values(default, default_multi) + #callback is False in optiondescription if callback is not False: self.impl_set_callback(callback, callback_params) self.commit() @@ -184,7 +185,7 @@ class BaseOption(Base): :param load: `True` if we are at the init of the option description :type load: bool """ - if not load and self.impl_getrequires() is None: + if not load and self.impl_getrequires() == []: self._state_requires = None elif load and self._state_requires is None: del(self._state_requires) @@ -215,7 +216,7 @@ class BaseOption(Base): if self.__class__.__name__ == 'OptionDescription' or \ isinstance(self, SymLinkOption): return - if not load and self.impl_get_callback() is None: + if not load and self.impl_get_callback() == (None, {}): self._state_callback = None self._state_callback_params = {} elif load and self._state_callback is None: @@ -228,22 +229,19 @@ class BaseOption(Base): else: callback, callback_params = self.impl_get_callback() self._state_callback_params = {} - if callback_params is not None: - cllbck_prms = {} - for key, values in callback_params.items(): - vls = [] - for value in values: - if isinstance(value, tuple) and value[0] is not None: - if load: - value = (descr.impl_get_opt_by_path(value[0]), - value[1]) - else: - value = (descr.impl_get_path_by_opt(value[0]), - value[1]) - vls.append(value) - cllbck_prms[key] = tuple(vls) - else: - cllbck_prms = None + cllbck_prms = {} + for key, values in callback_params.items(): + vls = [] + for value in values: + if isinstance(value, tuple) and value[0] is not None: + if load: + value = (descr.impl_get_opt_by_path(value[0]), + value[1]) + else: + value = (descr.impl_get_path_by_opt(value[0]), + value[1]) + vls.append(value) + cllbck_prms[key] = tuple(vls) if load: del(self._state_callback) @@ -354,12 +352,7 @@ class BaseOption(Base): except (KeyError, AttributeError): pass elif name == '_opt': - try: - if self._impl_getopt() is not None: - #so _opt is already set - is_readonly = True - except (KeyError, AttributeError): - pass + pass elif name != '_readonly': is_readonly = self.impl_is_readonly() if is_readonly: # pragma: optional cover @@ -470,21 +463,10 @@ class Option(OnlyOption): try: all_cons_vals.append(opt_value[index]) except IndexError, err: - log.debug('indexerror in _launch_consistency: {0}'.format(err)) #value is not already set, could be higher index #so return if no value and not default_value - if context is not undefined: - if isinstance(opt, DynSymLinkOption): - path = opt.impl_getpath(context) - else: - path = descr.impl_get_path_by_opt(opt) - default_value = context.cfgimpl_get_values()._getvalue(opt, path, True, index=index) - else: - default_value = opt.impl_getdefault_multi() - if default_value is not None: - all_cons_vals.append(default_value) - else: - return + log.debug('indexerror in _launch_consistency: {0}'.format(err)) + return except PropertiesOptionError as err: log.debug('propertyerror in _launch_consistency: {0}'.format(err)) if transitive: @@ -563,16 +545,12 @@ class Option(OnlyOption): if self._is_warnings_only(): warning = error error = None - except ValueWarning as warning: - log.debug(_('do_validation for {0}: warning in value').format( - self.impl_getname()), exc_info=True) - pass if error is None and warning is None: try: # if context launch consistency validation - if context is not undefined: - descr._valid_consistency(current_opt, _value, context, - _index, submulti_index) + #if context is not undefined: + self._valid_consistency(current_opt, _value, context, + _index, submulti_index) except ValueError as error: log.debug(_('do_validation for {0}: error in consistency').format( self.impl_getname()), exc_info=True) @@ -584,7 +562,7 @@ class Option(OnlyOption): if warning: msg = _("warning on the value of the option {0}: {1}").format( self.impl_getname(), warning) - if context is None or 'warnings' in \ + if context is undefined or 'warnings' in \ context.cfgimpl_get_settings(): warnings.warn_explicit(ValueWarning(msg, self), ValueWarning, @@ -594,8 +572,8 @@ class Option(OnlyOption): self.impl_getname(), error)) # generic calculation - if context is not undefined: - descr = context.cfgimpl_get_description() + #if context is not undefined: + # descr = context.cfgimpl_get_description() if not self.impl_is_multi(): do_validation(value, None, None) @@ -656,33 +634,7 @@ class Option(OnlyOption): "accesses the Option's doc" return self.impl_get_information('doc') - #def impl_getkey(self, value): - # return value - - def impl_add_consistency(self, func, *other_opts, **params): - """Add consistency means that value will be validate with other_opts - option's values. - - :param func: function's name - :type func: `str` - :param other_opts: options used to validate value - :type other_opts: `list` of `tiramisu.option.Option` - :param params: extra params (warnings_only and transitive are allowed) - """ - if self.impl_is_readonly(): # pragma: optional cover - raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is" - " read-only").format( - self.__class__.__name__, - self.impl_getname())) - warnings_only = False - transitive = True - for key, value in params.items(): - if key == 'warnings_only': - warnings_only = value - elif key == 'transitive': - transitive = value - else: - raise ValueError(_('unknow parameter {0} in consistency').format(key)) + def _valid_consistencies(self, other_opts): if self._is_subdyn(): dynod = self._impl_getsubdyn() else: @@ -706,31 +658,75 @@ class Option(OnlyOption): if self.impl_is_multi() != opt.impl_is_multi(): # pragma: optional cover raise ConfigError(_('every options in consistency must be ' 'multi or none')) + + def impl_add_consistency(self, func, *other_opts, **params): + """Add consistency means that value will be validate with other_opts + option's values. + + :param func: function's name + :type func: `str` + :param other_opts: options used to validate value + :type other_opts: `list` of `tiramisu.option.Option` + :param params: extra params (warnings_only and transitive are allowed) + """ + if self.impl_is_readonly(): # pragma: optional cover + raise AttributeError(_("'{0}' ({1}) cannot add consistency, option is" + " read-only").format( + self.__class__.__name__, + self.impl_getname())) + self._valid_consistencies(other_opts) func = '_cons_{0}'.format(func) if func not in dir(self): raise ConfigError(_('consistency {0} not available for this option').format(func)) all_cons_opts = tuple([self] + list(other_opts)) - value = self.impl_getdefault() - if value is not None: - if self.impl_is_multi(): - for idx, val in enumerate(value): - if not self.impl_is_submulti(): - self._launch_consistency(func, self, val, undefined, idx, - None, all_cons_opts, - warnings_only, transitive) - else: - for slave_idx, val in enumerate(value): - self._launch_consistency(func, self, val, None, - idx, slave_idx, - all_cons_opts, - warnings_only, transitive) - else: - self._launch_consistency(func, self, value, undefined, None, - None, all_cons_opts, warnings_only, - transitive) + unknown_params = set(params.keys()) - set(['warnings_only', 'transitive']) + if unknown_params != set(): + raise ValueError(_('unknow parameter {0} in consistency').format(unknown_params)) self._add_consistency(func, all_cons_opts, params) - #re validate default value when add consistency - self.impl_validate(self.impl_getdefault()) + #validate default value when add consistency + try: + self.impl_validate(self.impl_getdefault()) + except ValueError, err: + self._del_consistency() + raise err + + def _valid_consistency(self, option, value, context, index, submulti_idx): + if context is not undefined: + descr = context.cfgimpl_get_description() + if descr._cache_consistencies is None: + return True + #consistencies is something like [('_cons_not_equal', (opt1, opt2))] + if isinstance(option, DynSymLinkOption): + consistencies = descr._cache_consistencies.get(option._impl_getopt()) + else: + consistencies = descr._cache_consistencies.get(option) + else: + consistencies = option._get_consistencies() + if consistencies is not None: + for func, all_cons_opts, params in consistencies: + warnings_only = params.get('warnings_only', False) + transitive = params.get('transitive', True) + #all_cons_opts[0] is the option where func is set + if isinstance(option, DynSymLinkOption): + subpath = '.'.join(option._dyn.split('.')[:-1]) + namelen = len(option._impl_getopt().impl_getname()) + suffix = option.impl_getname()[namelen:] + opts = [] + for opt in all_cons_opts: + name = opt.impl_getname() + suffix + path = subpath + '.' + name + opts.append(opt._impl_to_dyn(name, path)) + else: + opts = all_cons_opts + try: + opts[0]._launch_consistency(func, option, value, context, + index, submulti_idx, opts, + warnings_only, transitive) + except ValueError as err: + if warnings_only: + raise ValueWarning(err.message, option) + else: + raise err def _cons_not_equal(self, opts, vals, warnings_only): for idx_inf, val_inf in enumerate(vals): @@ -740,6 +736,7 @@ class Option(OnlyOption): msg = _("same value for {0} and {1}, should be different") else: msg = _("same value for {0} and {1}, must be different") + log.debug('_cons_not_equal: {0} and {1} are not different'.format(val_inf, val_sup)) raise ValueError(msg.format(opts[idx_inf].impl_getname(), opts[idx_inf + idx_sup + 1].impl_getname())) @@ -753,7 +750,7 @@ class Option(OnlyOption): :param load: `True` if we are at the init of the option description :type load: bool """ - if not load and self._get_consistencies() is None: + if not load and self._get_consistencies() == (): self._state_consistencies = None elif load and self._state_consistencies is None: del(self._state_consistencies) @@ -787,10 +784,7 @@ class Option(OnlyOption): def _validate_callback(self, callback, callback_params): if callback is None: return - try: - default_multi = self.impl_getdefault_multi() - except AttributeError: - default_multi = None + default_multi = self.impl_getdefault_multi() if (not self.impl_is_multi() and (self.impl_getdefault() is not None or default_multi is not None)) or \ (self.impl_is_multi() and (self.impl_getdefault() != [] or @@ -808,8 +802,6 @@ def validate_requires_arg(requires, name): know more about the description of the requires dictionary """ - if requires is None: - return None, None ret_requires = {} config_action = {} diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 7b430c8..623f668 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -173,13 +173,9 @@ class IPOption(Option): # sometimes an ip term starts with a zero # but this does not fit in some case, for example bind does not like it self._impl_valid_unicode(value) - try: - for val in value.split('.'): - if val.startswith("0") and len(val) > 1: - raise ValueError(_('invalid IP')) # pragma: optional cover - except AttributeError: # pragma: optional cover - #if integer for example - raise ValueError(_('invalid IP')) + for val in value.split('.'): + if val.startswith("0") and len(val) > 1: + raise ValueError(_('invalid IP')) # pragma: optional cover # 'standard' validation try: IP('{0}/32'.format(value)) @@ -432,6 +428,7 @@ class DomainnameOption(Option): def _validate(self, value, context=undefined): self._impl_valid_unicode(value) + def _valid_length(val): if len(val) < 1: raise ValueError(_("invalid domainname's length (min 1)")) diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index 8a3831e..05ef6ef 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -24,10 +24,9 @@ import re from tiramisu.i18n import _ from tiramisu.setting import groups, undefined # , log -from .baseoption import BaseOption, DynSymLinkOption, SymLinkOption, \ - allowed_character +from .baseoption import BaseOption, SymLinkOption, allowed_character from . import MasterSlaves -from tiramisu.error import ConfigError, ConflictError, ValueWarning +from tiramisu.error import ConfigError, ConflictError from tiramisu.storage import get_storages_option from tiramisu.autolib import carry_out_calculation @@ -103,6 +102,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): cache_option.append(option._get_id()) if not isinstance(option, OptionDescription): for func, all_cons_opts, params in option._get_consistencies(): + all_cons_opts[0]._valid_consistencies(all_cons_opts[1:]) for opt in all_cons_opts: _consistencies.setdefault(opt, []).append((func, @@ -162,40 +162,6 @@ class OptionDescription(BaseOption, StorageOptionDescription): raise ValueError(_('group_type: {0}' ' not allowed').format(group_type)) - def _valid_consistency(self, option, value, context, index, submulti_idx): - if self._cache_consistencies is None: - return True - #consistencies is something like [('_cons_not_equal', (opt1, opt2))] - if isinstance(option, DynSymLinkOption): - consistencies = self._cache_consistencies.get(option._impl_getopt()) - else: - consistencies = self._cache_consistencies.get(option) - if consistencies is not None: - for func, all_cons_opts, params in consistencies: - warnings_only = params.get('warnings_only', False) - transitive = params.get('transitive', True) - #all_cons_opts[0] is the option where func is set - if isinstance(option, DynSymLinkOption): - subpath = '.'.join(option._dyn.split('.')[:-1]) - namelen = len(option._impl_getopt().impl_getname()) - suffix = option.impl_getname()[namelen:] - opts = [] - for opt in all_cons_opts: - name = opt.impl_getname() + suffix - path = subpath + '.' + name - opts.append(opt._impl_to_dyn(name, path)) - else: - opts = all_cons_opts - try: - opts[0]._launch_consistency(func, option, value, context, - index, submulti_idx, opts, - warnings_only, transitive) - except ValueError as err: - if warnings_only: - raise ValueWarning(err.message, option) - else: - raise err - def _impl_getstate(self, descr=None): """enables us to export into a dict :param descr: parent :class:`tiramisu.option.OptionDescription` @@ -317,12 +283,15 @@ class DynOptionDescription(OptionDescription): for child in children: if isinstance(child, OptionDescription): if child.impl_get_group_type() != groups.master: - raise ConfigError(_('cannot set optiondescription in an ' + raise ConfigError(_('cannot set optiondescription in a ' 'dynoptiondescription')) for chld in child._impl_getchildren(): chld._impl_setsubdyn(self) if isinstance(child, SymLinkOption): - raise ConfigError(_('cannot set symlinkoption in an ' + raise ConfigError(_('cannot set symlinkoption in a ' + 'dynoptiondescription')) + if isinstance(child, SymLinkOption): + raise ConfigError(_('cannot set symlinkoption in a ' 'dynoptiondescription')) child._impl_setsubdyn(self) self.impl_set_callback(callback, callback_params) diff --git a/tiramisu/storage/dictionary/option.py b/tiramisu/storage/dictionary/option.py index b3c135d..f7d22b1 100644 --- a/tiramisu/storage/dictionary/option.py +++ b/tiramisu/storage/dictionary/option.py @@ -145,6 +145,9 @@ class StorageBase(object): except AttributeError: self._consistencies = [cons] + def _del_consistency(self): + self._consistencies.pop(-1) + def _get_consistencies(self): try: return self._consistencies diff --git a/tiramisu/value.py b/tiramisu/value.py index aa8b5cc..2e0deb3 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -57,7 +57,7 @@ class Values(object): def _get_multi(self, opt, path): return Multi([], self.context, opt, path) - def _getdefaultvalue(self, opt, path, with_meta, index): + def _getdefaultvalue(self, opt, path, with_meta, index, submulti_index): # if value has callback and is not set if opt.impl_has_callback(): callback, callback_params = opt.impl_get_callback() @@ -65,18 +65,14 @@ class Values(object): callback=callback, callback_params=callback_params, index=index) - try: - if isinstance(value, list) and index is not undefined: - #if submulti and return a list of list, just return list - if opt.impl_is_submulti(): - val = value[index] - if isinstance(val, list): - value = val - else: - value = value[index] + if isinstance(value, list) and index is not undefined: + #if return a list and index is set, return value only if + #it's a submulti without submulti_index and without list of list + if opt.impl_is_submulti() and submulti_index is undefined and \ + (len(value) == 0 or not isinstance(value[0], list)): + return value + else: return value - except IndexError: - pass if with_meta: meta = self._getcontext().cfgimpl_get_meta() if meta is not None: @@ -94,7 +90,7 @@ class Values(object): # now try to get default value value = opt.impl_getdefault() if opt.impl_is_multi() and index is not undefined: - if value is None: + if value == []: value = opt.impl_getdefault_multi() else: try: @@ -103,43 +99,20 @@ class Values(object): value = opt.impl_getdefault_multi() return value - def _getvalue(self, opt, path, is_default, index=undefined, - with_meta=True, self_properties=undefined, + def _getvalue(self, opt, path, is_default, self_properties, + index=undefined, submulti_index=undefined, with_meta=True, masterlen=undefined): """actually retrieves the value :param opt: the `option.Option()` object :returns: the option's value (or the default value if not set) """ - if opt.impl_is_optiondescription(): # pragma: optional cover - raise ValueError(_('optiondescription has no value')) - - if self_properties is undefined: - self_properties = self._getcontext().cfgimpl_get_settings()._getproperties( - opt, path, read_write=False) force_default = 'frozen' in self_properties and \ 'force_default_on_freeze' in self_properties + # not default value if not is_default and not force_default: if opt.impl_is_master_slaves('slave'): - #if masterlen is not undefined: - if index is undefined: - value = [] - vals = self._p_.getvalue(path) - length = max(masterlen, len(vals)) - for idx in xrange(0, length): - try: - if vals[idx] is undefined: - value.append(self._getdefaultvalue(opt, path, with_meta, idx)) - else: - value.append(vals[idx]) - except IndexError: - try: - value.append(self._getdefaultvalue(opt, path, with_meta, idx)) - except IndexError: - value.append(None) - else: - value = self._p_.getvalue(path, index) - return value + return self._p_.getvalue(path, index) else: value = self._p_.getvalue(path) if index is not undefined: @@ -151,7 +124,7 @@ class Values(object): pass else: return value - return self._getdefaultvalue(opt, path, with_meta, index) + return self._getdefaultvalue(opt, path, with_meta, index, submulti_index) def get_modified_values(self): context = self._getcontext() @@ -316,13 +289,9 @@ class Values(object): self_properties=self_properties, index=index) try: - if index is None: - gv_index = undefined - else: - gv_index = index - value = self._getvalue(opt, path, is_default, index=gv_index, + value = self._getvalue(opt, path, is_default, self_properties, + index=index, submulti_index=submulti_index, with_meta=with_meta, - self_properties=self_properties, masterlen=masterlen) config_error = None except ConfigError as err: @@ -344,9 +313,6 @@ class Values(object): else: force_index = index if opt.impl_is_multi(): - #for slave is a multi - if index is None and not isinstance(value, list): - value = [] if force_index is None: value = Multi(value, self.context, opt, path) elif opt.impl_is_submulti() and submulti_index is undefined: @@ -773,10 +739,7 @@ class Multi(list): " which is a slave").format(self.opt.impl_getname())) index = self.__len__() if value is undefined: - try: - value = self._get_validated_value(index) - except IndexError: - value = None + value = self._get_validated_value(index) context = self._getcontext() setting = context.cfgimpl_get_settings() setting_properties = setting._getproperties(read_write=False)