From 54ca54e5053ffdb2ed696ac6b681b7dfd7389cef Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 19 Nov 2015 22:25:00 +0100 Subject: [PATCH] separate value in slave --- ChangeLog | 20 +-- test/test_dyn_optiondescription.py | 61 ++----- test/test_metaconfig.py | 6 +- test/test_multi.py | 1 + test/test_option_calculation.py | 232 +++----------------------- test/test_parsing_group.py | 6 +- test/test_submulti.py | 4 +- tiramisu/autolib.py | 6 +- tiramisu/config.py | 8 +- tiramisu/option/baseoption.py | 8 +- tiramisu/option/masterslave.py | 116 +++++++------ tiramisu/setting.py | 5 +- tiramisu/storage/dictionary/option.py | 2 +- tiramisu/storage/dictionary/value.py | 143 ++++++++++++++-- tiramisu/storage/util.py | 52 ++++-- tiramisu/value.py | 155 ++++++++++------- 16 files changed, 409 insertions(+), 416 deletions(-) diff --git a/ChangeLog b/ChangeLog index b8f6f5b..6c8f3ca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,7 +10,7 @@ Sun Jul 26 19:09:29 2015 +0200 Emmanuel Garette Fri Jul 24 18:03:59 2015 +0200 Emmanuel Garette * add duplicate option to Config, to generate new Config with same - value, properties, Option. Option are not duplication. + value, properties, Option. Option are not duplicated. Mon Apr 20 14:44:15 2015 +0200 Emmanuel Garette * if option is multi, the properties disallow [None] for a multi but @@ -44,7 +44,7 @@ Sun Dec 7 14:37:32 2014 +0200 Emmanuel Garette Mon Dec 1 22:58:13 2014 +0200 Emmanuel Garette * propertyerror are transitive in consistency, now it's possible to set non-transitive consistency - * if consistency with multiple option return if transitive + * support transitive in consistency with multiple option return * can reset slave value in all case when deleting master value * in_network's consistency now verify that IP is not network or broadcast's IP + ip_netmask's consistency now verify that IP is not @@ -52,11 +52,11 @@ Mon Dec 1 22:58:13 2014 +0200 Emmanuel Garette Sun Oct 26 08:50:38 2014 +0200 Emmanuel Garette * if option is frozen with force_default_on_freeze property, owner - must be 'default' check property when tried to change owner + must be 'default', check property when tried to change owner * bad characters in DomainnameOption could be in warning level * frozen with force_default_on_freeze can change owner * add force_permissive to config __iter__ - * pass force_permissive to slave for a master or to master for a slave + * add force_permissive to slave for a master or to master for a slave * remove mandatory_warnings in config.py * add force_permissive in mandatory_warnings @@ -78,13 +78,13 @@ Thu Jun 19 23:20:29 2014 +0200 Emmanuel Garette Sun Apr 27 10:32:40 2014 +0200 Emmanuel Garette * behavior change in ChoiceOption: - remove open_values, that no sens (no type validation is possible) if + remove open_values, that no sens (we cannot validate type) if you want something like open_values, please use a typed option and add impl_(s|g)et_information to add proposed values and use it in your code * add dynamic ChoiceOption: we can have dynamic ChoiceOption. Parameter values can be a function - and as callback, we can add values_params + and, as callback, we can add values_params Fri Apr 25 22:57:08 2014 +0200 Emmanuel Garette @@ -99,8 +99,8 @@ Sat Apr 12 11:37:27 2014 +0200 Emmanuel Garette * tiramisu/config.py (in cfgimpl_get_home_by_path and getattr) and tiramisu/value.py (in getitem): arity change, remove force_properties * tiramisu/option.py: split into tiramisu/option directory - * tiramisu/option/masterslave.py: master/slaves have no a special - object MasterSlaves for all code related to master/slaves options - * tiramisu/option/masterslave.py: master and slaves values (length, - consistency, ...) are now check every time + * tiramisu/option/masterslave.py: add special object MasterSlaves for + all code related to master/slaves options + * tiramisu/option/masterslave.py: check every time master and slaves + values (length, consistency, ...) * change None to undefined when needed diff --git a/test/test_dyn_optiondescription.py b/test/test_dyn_optiondescription.py index 9dab50b..381bfd6 100644 --- a/test/test_dyn_optiondescription.py +++ b/test/test_dyn_optiondescription.py @@ -904,8 +904,8 @@ def test_masterslaves_dyndescription(): assert cfg.od.stval2.st1val2.st2val2 == [] assert cfg.getowner(st1val1) == owner assert cfg.getowner(st1val2) == owners.default - assert cfg.getowner(st2val1) == owner - assert cfg.getowner(st2val2) == owners.default + assert cfg.getowner(st2val1, 0) == owner +# assert cfg.getowner(st2val2) == owners.default # cfg.od.stval1.st1val1.st1val1.pop(0) assert cfg.od.stval1.st1val1.st1val1 == [] @@ -914,18 +914,18 @@ def test_masterslaves_dyndescription(): assert cfg.od.stval2.st1val2.st2val2 == [] assert cfg.getowner(st1val1) == owner assert cfg.getowner(st1val2) == owners.default - assert cfg.getowner(st2val1) == owner - assert cfg.getowner(st2val2) == owners.default +# assert cfg.getowner(st2val1) == owner +# assert cfg.getowner(st2val2) == owners.default # cfg.od.stval1.st1val1.st1val1 = ['yes'] cfg.od.stval1.st1val1.st2val1 = ['yes'] assert cfg.getowner(st1val1) == owner - assert cfg.getowner(st2val1) == owner + assert cfg.getowner(st2val1, 0) == owner del(cfg.od.stval1.st1val1.st2val1) assert cfg.getowner(st1val1) == owner assert cfg.getowner(st1val2) == owners.default - assert cfg.getowner(st2val1) == owners.default - assert cfg.getowner(st2val2) == owners.default +# assert cfg.getowner(st2val1) == owners.default +# assert cfg.getowner(st2val2) == owners.default # cfg.od.stval1.st1val1.st1val1 = ['yes'] cfg.od.stval1.st1val1.st2val1 = ['yes'] @@ -1012,8 +1012,8 @@ def test_masterslaves_submulti_dyndescription(): assert cfg.od.stval2.st1val2.st2val2 == [] assert cfg.getowner(st1val1) == owner assert cfg.getowner(st1val2) == owners.default - assert cfg.getowner(st2val1) == owner - assert cfg.getowner(st2val2) == owners.default + assert cfg.getowner(st2val1, 0) == owner +# assert cfg.getowner(st2val2) == owners.default def test_masterslaves_consistency_ip_dyndescription(): @@ -1085,8 +1085,8 @@ def test_masterslaves_callback_dyndescription(): assert cfg.od.stval2.st1val2.st2val2 == [] assert cfg.getowner(st1val1) == owner assert cfg.getowner(st1val2) == owners.default - assert cfg.getowner(st2val1) == owner - assert cfg.getowner(st2val2) == owners.default + assert cfg.getowner(st2val1, 0) == owner +# assert cfg.getowner(st2val2) == owners.default # cfg.od.stval1.st1val1.st1val1.pop(0) assert cfg.od.stval1.st1val1.st1val1 == [] @@ -1095,13 +1095,13 @@ def test_masterslaves_callback_dyndescription(): assert cfg.od.stval2.st1val2.st2val2 == [] assert cfg.getowner(st1val1) == owner assert cfg.getowner(st1val2) == owners.default - assert cfg.getowner(st2val1) == owner - assert cfg.getowner(st2val2) == owners.default +# assert cfg.getowner(st2val1) == owner +# assert cfg.getowner(st2val2) == owners.default # cfg.od.stval1.st1val1.st1val1 = ['yes'] cfg.od.stval1.st1val1.st2val1 = ['yes'] assert cfg.getowner(st1val1) == owner - assert cfg.getowner(st2val1) == owner + assert cfg.getowner(st2val1, 0) == owner del(cfg.od.stval1.st1val1.st2val1) assert cfg.getowner(st1val1) == owner assert cfg.getowner(st1val2) == owners.default @@ -1159,31 +1159,6 @@ def test_masterslaves_callback_nomulti_dyndescription(): assert cfg.od.stval1.st1val1.st2val1 == ['val'] -def test_masterslaves_callback_multi_dyndescription(): - v1 = StrOption('v1', '', multi=True) - st1 = StrOption('st1', "", multi=True) - st2 = StrOption('st2', "", multi=True, callback=return_dynval, callback_params={'': ((v1, False),)}) - stm = OptionDescription('st1', '', [st1, st2]) - stm.impl_set_group_type(groups.master) - st = DynOptionDescription('st', '', [stm], callback=return_list) - od = OptionDescription('od', '', [st]) - od2 = OptionDescription('od', '', [od, v1]) - cfg = Config(od2) - assert cfg.od.stval1.st1val1.st1val1 == [] - assert cfg.od.stval1.st1val1.st2val1 == [] - cfg.od.stval1.st1val1.st1val1.append('yes') - assert cfg.od.stval1.st1val1.st1val1 == ['yes'] - assert cfg.od.stval1.st1val1.st2val1 == [None] - cfg.od.stval1.st1val1.st2val1 = ['no'] - cfg.v1 = ['no', 'no', 'no'] - cfg.od.stval1.st1val1.st1val1.append('yes') - assert cfg.od.stval1.st1val1.st1val1 == ['yes', 'yes'] - assert cfg.od.stval1.st1val1.st2val1 == ['no', 'no'] - cfg.od.stval1.st1val1.st1val1.pop(1) - assert cfg.od.stval1.st1val1.st1val1 == ['yes'] - assert cfg.od.stval1.st1val1.st2val1 == ['no'] - - def test_masterslaves_callback_samegroup_dyndescription(): st1 = StrOption('st1', "", multi=True) st2 = StrOption('st2', "", multi=True) @@ -1242,11 +1217,11 @@ def test_masterslaves_callback_samegroup_dyndescription(): 'od.stval2.st1val2.st2val2': [], 'od.stval2.st1val2.st3val2': []} assert cfg.getowner(st1val1) == owner + assert cfg.getowner(st2val1, 0) == owner + assert cfg.getowner(st3val1, 0) == owners.default assert cfg.getowner(st1val2) == owners.default - assert cfg.getowner(st2val1) == owner - assert cfg.getowner(st2val2) == owners.default - assert cfg.getowner(st3val1) == owners.default - assert cfg.getowner(st3val2) == owners.default + #assert cfg.getowner(st2val2) == owners.default + #assert cfg.getowner(st3val2) == owners.default def test_state_config(): diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index b395fcb..b9487fb 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -295,13 +295,13 @@ def test_meta_master_slaves_owners(): assert meta.conf1.getowner(netmask_admin_eth0) == owners.default meta.netmask_admin_eth0 = ['255.255.255.0'] assert meta.conf1.getowner(ip_admin_eth0) == owners.meta - assert meta.conf1.getowner(netmask_admin_eth0) == owners.meta + assert meta.conf1.getowner(netmask_admin_eth0, 0) == owners.meta meta.netmask_admin_eth0 = ['255.255.0.0'] assert meta.conf1.getowner(ip_admin_eth0) == owners.meta - assert meta.conf1.getowner(netmask_admin_eth0) == owners.meta + assert meta.conf1.getowner(netmask_admin_eth0, 0) == owners.meta meta.conf1.ip_admin_eth0 = ['192.168.1.1'] assert meta.conf1.getowner(ip_admin_eth0) == owners.user - assert meta.conf1.getowner(netmask_admin_eth0) == owners.default + assert meta.conf1.getowner(netmask_admin_eth0, 0) == owners.default def test_meta_force_default(): diff --git a/test/test_multi.py b/test/test_multi.py index 29c9ef1..5f51942 100644 --- a/test/test_multi.py +++ b/test/test_multi.py @@ -6,6 +6,7 @@ from tiramisu.value import Multi from tiramisu.option import IntOption, StrOption, OptionDescription from tiramisu.config import Config from tiramisu.error import ConfigError, PropertiesOptionError +from tiramisu.setting import groups import weakref from py.test import raises diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 6e6bb5f..a269702 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -548,33 +548,6 @@ def test_callback_master_and_slaves_slave_cal(): assert cfg.val1.val2 == ['val1', 'val2', 'val'] -def test_callback_master_and_slaves_slave_cal2(): - val3 = StrOption('val3', "", ['val', 'val'], multi=True) - val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val3, False),)}) - val2 = StrOption('val2', "", ['val2', 'val2'], multi=True) - interface1 = OptionDescription('val1', '', [val1, val2]) - interface1.impl_set_group_type(groups.master) - maconfig = OptionDescription('rootconfig', '', [interface1, val3]) - cfg = Config(maconfig) - cfg.read_write() - assert cfg.val3 == ['val', 'val'] - assert cfg.val1.val1 == ['val', 'val'] - assert cfg.val1.val2 == ['val2', 'val2'] - cfg.val3.pop(1) - # cannot remove slave's value because master is calculated - # so raise - raises(SlaveError, "cfg.val1.val1") - raises(SlaveError, "cfg.val1.val2") - cfg.val3 = ['val', 'val'] - assert cfg.val3 == ['val', 'val'] - assert cfg.val1.val1 == ['val', 'val'] - assert cfg.val1.val2 == ['val2', 'val2'] - raises(SlaveError, "cfg.val1.val1 = ['val']") - assert cfg.val3 == ['val', 'val'] - assert cfg.val1.val1 == ['val', 'val'] - assert cfg.val1.val2 == ['val2', 'val2'] - - def test_callback_master_and_slaves_master_disabled(): #properties must be transitive val1 = StrOption('val1', "", multi=True, properties=('disabled',)) @@ -643,35 +616,16 @@ def test_callback_master_and_slaves_slave_callback_disabled(): cfg = Config(maconfig) cfg.read_write() assert cfg.val1.val1 == [] - raises(ConfigError, "cfg.val1.val2") + assert cfg.val1.val2 == [] cfg.val1.val1.append('yes') assert cfg.val1.val1 == ['yes'] cfg.cfgimpl_get_settings().remove('disabled') - assert cfg.val1.val2 == [None] + raises(SlaveError, "cfg.val1.val2") cfg.val1.val2 = ['no'] cfg.val1.val1.append('yes1') - cfg.val1.val2[1] = 'no1' - cfg.cfgimpl_get_settings().append('disabled') - cfg.val1.val1.pop(0) - assert cfg.val1.val1 == ['yes1'] - assert cfg.val1.val2 == ['no1'] - - -def test_callback_master_and_slaves_slave_list(): - val1 = StrOption('val1', "", multi=True) - val2 = StrOption('val2', "", multi=True, callback=return_list) - interface1 = OptionDescription('val1', '', [val1, val2]) - interface1.impl_set_group_type(groups.master) - maconfig = OptionDescription('rootconfig', '', [interface1]) - cfg = Config(maconfig) - cfg.read_write() - #len is equal to 2 for slave and 0 for master raises(SlaveError, "cfg.val1.val2") - cfg.val1.val1 = ['val1', 'val2'] - assert cfg.val1.val1 == ['val1', 'val2'] - assert cfg.val1.val2 == ['val', 'val'] - #wrong len - raises(SlaveError, "cfg.val1.val1 = ['val1']") + cfg.cfgimpl_get_settings().append('disabled') + raises(ConfigError, "cfg.val1.val1.pop(0)") def test_callback_master_and_slaves_value(): @@ -687,35 +641,40 @@ def test_callback_master_and_slaves_value(): cfg = Config(maconfig) cfg.read_write() cfg.val4 == ['val10', 'val11'] - raises(SlaveError, "cfg.val1.val1") - raises(SlaveError, "cfg.val1.val2") - raises(SlaveError, "cfg.val1.val3") - raises(SlaveError, "cfg.val1.val5") - raises(SlaveError, "cfg.val1.val6") + assert cfg.val1.val1 == [] + assert cfg.val1.val2 == [] + assert cfg.val1.val3 == [] + assert cfg.val1.val5 == [] + assert cfg.val1.val6 == [] + #raises(SlaveError, "cfg.val1.val1") + #raises(SlaveError, "cfg.val1.val2") + #raises(SlaveError, "cfg.val1.val3") + #raises(SlaveError, "cfg.val1.val5") + #raises(SlaveError, "cfg.val1.val6") # #default calculation has greater length - raises(SlaveError, "cfg.val1.val1 = ['val1']") + #raises(SlaveError, "cfg.val1.val1 = ['val1']") # cfg.val1.val1 = ['val1', 'val2'] assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2'] assert cfg.val1.val3 == ['yes', 'yes'] - assert cfg.val1.val5 == ['val10', 'val11'] - assert cfg.val1.val6 == ['val10', 'val11'] + raises(SlaveError, "cfg.val1.val5") + raises(SlaveError, "cfg.val1.val6") # cfg.val1.val1.append('val3') assert cfg.val1.val1 == ['val1', 'val2', 'val3'] assert cfg.val1.val2 == ['val1', 'val2', 'val3'] assert cfg.val1.val3 == ['yes', 'yes', 'yes'] - assert cfg.val1.val5 == ['val10', 'val11', None] - assert cfg.val1.val6 == ['val10', 'val11', None] + raises(SlaveError, "cfg.val1.val5") + raises(SlaveError, "cfg.val1.val6") # cfg.val1.val1.pop(2) assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2'] assert cfg.val1.val3 == ['yes', 'yes'] - assert cfg.val1.val5 == ['val10', 'val11'] - assert cfg.val1.val6 == ['val10', 'val11'] + raises(SlaveError, "cfg.val1.val5") + raises(SlaveError, "cfg.val1.val6") # cfg.val1.val2 = ['val2', 'val2'] cfg.val1.val3 = ['val2', 'val2'] @@ -728,121 +687,16 @@ def test_callback_master_and_slaves_value(): cfg.val1.val1.append('val3') assert cfg.val1.val2 == ['val2', 'val2', 'val3'] assert cfg.val1.val3 == ['val2', 'val2', 'yes'] - assert cfg.val1.val5 == ['val2', 'val2', None] - assert cfg.val1.val6 == ['val2', 'val2', None] + raises(SlaveError, "cfg.val1.val5") + raises(SlaveError, "cfg.val1.val6") cfg.cfgimpl_get_settings().remove('cache') cfg.val4 = ['val10', 'val11', 'val12'] #if value is already set, not updated ! cfg.val1.val1.pop(2) cfg.val1.val1.append('val3') cfg.val1.val1 = ['val1', 'val2', 'val3'] - assert cfg.val1.val5 == ['val2', 'val2', 'val12'] - assert cfg.val1.val6 == ['val2', 'val2', 'val12'] - - -def test_callback_master_and_slaves_disabled(): - val = BoolOption('val', '', multi=True) - val1 = StrOption('val1', "", multi=True) - val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val, False),)}) - interface1 = OptionDescription('val1', '', [val1, val2]) - interface1.impl_set_group_type(groups.master) - maconfig = OptionDescription('rootconfig', '', [val, interface1]) - cfg = Config(maconfig) - cfg.read_write() - # - assert cfg.val == [] - assert cfg.val1.val1 == [] - assert cfg.val1.val2 == [] - cfg.val1.val1.append('val1') - assert cfg.val == [] - assert cfg.val1.val1 == ['val1'] - assert cfg.val1.val2 == [None] - # - cfg.val.append(True) - cfg.cfgimpl_get_settings()[cfg.cfgimpl_get_description().val1.val2].append('disabled') - assert cfg.val == [True] - assert cfg.val1.val1 == ['val1'] - raises(PropertiesOptionError, 'cfg.val1.val2') - # - cfg.val1.val1.append('val1_1') - assert cfg.val == [True] - assert cfg.val1.val1 == ['val1', 'val1_1'] - raises(PropertiesOptionError, 'cfg.val1.val2') - # - cfg.cfgimpl_get_settings()[cfg.cfgimpl_get_description().val1.val2].remove('disabled') - assert cfg.val == [True] - assert cfg.val1.val1 == ['val1', 'val1_1'] - raises(ValueError, 'cfg.val1.val2') - # - cfg.val = [] - assert cfg.val1.val1 == ['val1', 'val1_1'] - assert cfg.val1.val2 == [None, None] - # - cfg.val1.val2 = [None, None] - cfg.val = [True, True] - cfg.cfgimpl_get_settings()[cfg.cfgimpl_get_description().val1.val2].append('disabled') - cfg.val1.val1.pop(1) - assert cfg.val == [True, True] - assert cfg.val1.val1 == ['val1'] - raises(PropertiesOptionError, 'cfg.val1.val2') - # - cfg.val1.val1.append('val1_2') - assert cfg.val == [True, True] - assert cfg.val1.val1 == ['val1', 'val1_2'] - raises(PropertiesOptionError, 'cfg.val1.val2') - # - cfg.cfgimpl_get_settings()[cfg.cfgimpl_get_description().val1.val2].remove('disabled') - assert cfg.val == [True, True] - assert cfg.val1.val1 == ['val1', 'val1_2'] - #[None, None] + pop() + append() => [None, True] - raises(ValueError, 'assert cfg.val1.val2') - - -def test_callback_master_and_slaves_requires(): - val = StrOption('val', '', 'val') - valreq = StrOption('valreq', '', 'val') - val1 = StrOption('val1', "", multi=True) - val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)}) - val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)}) - val4 = StrOption('val4', '', multi=True, default=[]) - val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)}) - val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val, False),)}, - requires=({'option': valreq, 'expected': 'val_disabled', 'action': 'disabled'},)) - interface1 = OptionDescription('val1', '', [val1, val2, val3, val5, val6]) - interface1.impl_set_group_type(groups.master) - maconfig = OptionDescription('rootconfig', '', [val, valreq, interface1, val4]) - cfg = Config(maconfig) - cfg.read_write() - assert cfg.val1.val1 == [] - assert cfg.val1.val2 == [] - assert cfg.val1.val3 == [] - assert cfg.val1.val5 == [] - assert cfg.val1.val6 == [] - # - cfg.val1.val1 = ['val1'] - assert cfg.val1.val1 == ['val1'] - assert cfg.val1.val2 == ['val1'] - assert cfg.val1.val3 == ['yes'] - assert cfg.val1.val5 == [None] - assert cfg.val1.val6 == ['val'] - cfg.val4 = ['val10'] - assert cfg.val1.val5 == ['val10'] - # - cfg.valreq = 'val_disabled' - assert cfg.val1.val1 == ['val1'] - assert cfg.val1.val2 == ['val1'] - assert cfg.val1.val3 == ['yes'] - assert cfg.val1.val5 == ['val10'] - raises(PropertiesOptionError, 'cfg.val1.val6') - assert cfg.make_dict() == {'val1.val2': ['val1'], 'val1.val3': ['yes'], 'val1.val1': ['val1'], 'val1.val5': ['val10'], 'val': 'val', 'valreq': 'val_disabled', 'val4': ['val10']} - # - cfg.cfgimpl_get_settings()[cfg.cfgimpl_get_description().val].append('disabled') - cfg.cfgimpl_get_settings()[cfg.cfgimpl_get_description().val1.val3].append('disabled') - raises(PropertiesOptionError, 'cfg.val1.val6') - assert cfg.make_dict() == {'val1.val2': ['val1'], 'val1.val1': ['val1'], 'val1.val5': ['val10'], 'val4': ['val10'], 'valreq': 'val_disabled'} - # - cfg.valreq = 'val' - raises(ConfigError, 'cfg.val1.val6') + raises(SlaveError, "cfg.val1.val5") + raises(SlaveError, "cfg.val1.val6") def test_callback_master(): @@ -853,42 +707,6 @@ def test_callback_master(): raises(ValueError, "interface1.impl_set_group_type(groups.master)") -def test_callback_master_and_other_master_slave(): - val1 = StrOption('val1', "", multi=True) - val2 = StrOption('val2', "", multi=True) - val3 = StrOption('val3', "", multi=True) - val4 = StrOption('val4', '', multi=True, default=['val10', 'val11']) - val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)}) - val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val2, False),)}) - interface1 = OptionDescription('val1', '', [val1, val2, val3]) - interface1.impl_set_group_type(groups.master) - interface2 = OptionDescription('val4', '', [val4, val5, val6]) - interface2.impl_set_group_type(groups.master) - maconfig = OptionDescription('rootconfig', '', [interface1, interface2]) - cfg = Config(maconfig) - cfg.read_write() - assert cfg.val4.val4 == ['val10', 'val11'] - assert cfg.val4.val5 == [None, None] - assert cfg.val4.val6 == [None, None] - cfg.val1.val1 = ['yes'] - assert cfg.val4.val4 == ['val10', 'val11'] - assert cfg.val4.val5 == ['yes', None] - assert cfg.val4.val6 == [None, None] - cfg.val1.val2 = ['no'] - assert cfg.val4.val4 == ['val10', 'val11'] - assert cfg.val4.val5 == ['yes', None] - assert cfg.val4.val6 == ['no', None] - cfg.val1.val1 = ['yes', 'yes', 'yes'] - cfg.val1.val2 = ['no', 'no', 'no'] - raises(SlaveError, "cfg.val4.val4") - raises(SlaveError, "cfg.val4.val5") - raises(SlaveError, "cfg.val4.val6") - cfg.val4.getattr('val4', validate=False).append('val12') - assert cfg.val4.val4 == ['val10', 'val11', 'val12'] - assert cfg.val4.val5 == ['yes', 'yes', 'yes'] - assert cfg.val4.val6 == ['no', 'no', 'no'] - - #FIXME: slave est un symlink diff --git a/test/test_parsing_group.py b/test/test_parsing_group.py index 36d06d4..ca65579 100644 --- a/test/test_parsing_group.py +++ b/test/test_parsing_group.py @@ -361,16 +361,16 @@ def test_reset_values_with_master_and_slaves_default(): cfg.ip_admin_eth0.ip_admin_eth0[0] = "192.168.230.146" cfg.ip_admin_eth0.netmask_admin_eth0[0] = "255.255.255.0" assert cfg.getowner(ip_admin_eth0) == owner - assert cfg.getowner(netmask_admin_eth0) == owner + assert cfg.getowner(netmask_admin_eth0, 0) == owner del(cfg.ip_admin_eth0.ip_admin_eth0) assert cfg.getowner(ip_admin_eth0) == owners.default - assert cfg.getowner(netmask_admin_eth0) == owners.default + assert cfg.getowner(netmask_admin_eth0, 0) == owners.default assert cfg.ip_admin_eth0.ip_admin_eth0 == ['192.168.230.145'] assert cfg.ip_admin_eth0.netmask_admin_eth0 == [None] cfg.ip_admin_eth0.netmask_admin_eth0[0] = "255.255.255.0" assert cfg.getowner(ip_admin_eth0) == owners.default - assert cfg.getowner(netmask_admin_eth0) == owner + assert cfg.getowner(netmask_admin_eth0, 0) == owner del(cfg.ip_admin_eth0.ip_admin_eth0) assert cfg.getowner(ip_admin_eth0) == owners.default assert cfg.getowner(netmask_admin_eth0) == owners.default diff --git a/test/test_submulti.py b/test/test_submulti.py index ab9c03b..b97ec2a 100644 --- a/test/test_submulti.py +++ b/test/test_submulti.py @@ -417,10 +417,10 @@ def test_reset_values_with_master_and_slaves_submulti(): cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145") cfg.ip_admin_eth0.netmask_admin_eth0[0].append('255.255.255.0') assert cfg.getowner(ip_admin_eth0) == owner - assert cfg.getowner(netmask_admin_eth0) == owner + assert cfg.getowner(netmask_admin_eth0, 0) == owner del(cfg.ip_admin_eth0.ip_admin_eth0) assert cfg.getowner(ip_admin_eth0) == owners.default - assert cfg.getowner(netmask_admin_eth0) == owners.default +# assert cfg.getowner(netmask_admin_eth0) == owners.default assert cfg.ip_admin_eth0.ip_admin_eth0 == [] assert cfg.ip_admin_eth0.netmask_admin_eth0 == [] diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 9096f8b..73a8503 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -18,7 +18,8 @@ # the whole pypy projet is under MIT licence # ____________________________________________________________ "enables us to carry out a calculation and return an option's value" -from tiramisu.error import PropertiesOptionError, ConfigError, ContextError +from tiramisu.error import PropertiesOptionError, ConfigError, ContextError, \ + SlaveError from tiramisu.i18n import _ from tiramisu.setting import undefined # ____________________________________________________________ @@ -237,6 +238,9 @@ def carry_out_calculation(option, context, callback, callback_params, 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: diff --git a/tiramisu/config.py b/tiramisu/config.py index ead89db..d7dd69f 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -518,7 +518,7 @@ class _CommonConfig(SubConfig): "read write is a global config's setting, see `settings.py`" self.cfgimpl_get_settings().read_write() - def getowner(self, opt, force_permissive=False): + def getowner(self, opt, index=None, force_permissive=False): """convenience method to retrieve an option's owner from the config itself """ @@ -527,7 +527,7 @@ class _CommonConfig(SubConfig): not isinstance(opt, DynSymLinkOption): # pragma: optional cover raise TypeError(_('opt in getowner must be an option not {0}' '').format(type(opt))) - return self.cfgimpl_get_values().getowner(opt, + return self.cfgimpl_get_values().getowner(opt, index=index, force_permissive=force_permissive) def unwrap_from_path(self, path, force_permissive=False): @@ -608,12 +608,12 @@ class _CommonConfig(SubConfig): fake_config = Config(self._impl_descr, persistent=False, force_values=get_storages_validation(), force_settings=self.cfgimpl_get_settings()) - fake_config.cfgimpl_get_values()._p_._values = self.cfgimpl_get_values()._p_.get_modified_values() + fake_config.cfgimpl_get_values()._p_._values = self.cfgimpl_get_values()._p_._values return fake_config def duplicate(self): config = Config(self._impl_descr) - config.cfgimpl_get_values()._p_._values = self.cfgimpl_get_values()._p_.get_modified_values() + config.cfgimpl_get_values()._p_._values = self.cfgimpl_get_values()._p_._values config.cfgimpl_get_settings()._p_._properties = self.cfgimpl_get_settings()._p_.get_modified_properties() config.cfgimpl_get_settings()._p_._permissives = self.cfgimpl_get_settings()._p_.get_modified_permissives() return config diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 155bc31..3f1867e 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -98,7 +98,8 @@ class Base(StorageBase): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None, warnings_only=False, extra=None, allow_empty_list=undefined): + properties=None, warnings_only=False, extra=None, + allow_empty_list=undefined): if not valid_name(name): # pragma: optional cover raise ValueError(_("invalid name: {0} for option").format(name)) if requires is not None: @@ -108,7 +109,7 @@ class Base(StorageBase): calc_properties = frozenset() requires = undefined if not multi and default_multi is not None: # pragma: optional cover - raise ValueError(_("a default_multi is set whereas multi is False" + raise ValueError(_("default_multi is set whereas multi is False" " in option: {0}").format(name)) if multi is True: _multi = 0 @@ -133,7 +134,8 @@ class Base(StorageBase): 'requirement {0}'.format( list(set_forbidden_properties))) StorageBase.__init__(self, name, _multi, warnings_only, doc, extra, - calc_properties, requires, properties, allow_empty_list) + calc_properties, requires, properties, + allow_empty_list) if multi is not False and default is None: default = [] self.impl_validate(default) diff --git a/tiramisu/option/masterslave.py b/tiramisu/option/masterslave.py index 19a756a..eaf8579 100644 --- a/tiramisu/option/masterslave.py +++ b/tiramisu/option/masterslave.py @@ -48,6 +48,10 @@ class MasterSlaves(object): if child.impl_getname() == name: self.master = child else: + if child.impl_getdefault() != []: + raise ValueError(_("not allowed default value for option {0} " + "in group {1}").format(child.impl_getname(), + name)) slaves.append(child) if self.master is None: # pragma: optional cover raise ValueError(_('master group with wrong' @@ -107,7 +111,7 @@ class MasterSlaves(object): def pop(self, opt, values, index): for slave in self.getslaves(opt): if not values.is_default_owner(slave, validate_properties=False, - validate_meta=False): + validate_meta=False, index=index): values._get_cached_item(slave, validate=False, validate_properties=False ).pop(index, force=True) @@ -139,17 +143,19 @@ class MasterSlaves(object): for slave in self.getslaves(opt): try: slave_path = slave.impl_getpath(values._getcontext()) - if c_slave_path == slave_path: - slave_value = c_slave_value - else: - slave_value = values._get_validated_value(slave, - slave_path, - False, - False, - None, False, - None, - self_properties=self_properties) - slavelen = len(slave_value) + slavelen = values._p_.get_max_length(slave_path) + #if c_slave_path == slave_path: + # slave_value = c_slave_value + #else: + # slave_value = values._get_validated_value(slave, + # slave_path, + # False, + # False, + # None, False, + # None, + # self_properties=self_properties, + # masterlen=masterlen) + #slavelen = len(slave_value) self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt) except ConfigError: # pragma: optional cover pass @@ -186,39 +192,44 @@ class MasterSlaves(object): undefined, force_permissive, master=master) master_is_meta = values._is_meta(opt, masterp) - value = values._get_validated_value(opt, path, validate, - force_permissive, - force_properties, - validate_properties, - None, # not undefined - with_meta=master_is_meta, - self_properties=self_properties) + #value = values._get_validated_value(opt, path, validate, + # force_permissive, + # force_properties, + # validate_properties, + # None, # not undefined + # with_meta=master_is_meta, + # self_properties=self_properties) #if slave, had values until master's one - path = opt.impl_getpath(context) - valuelen = len(value) - if validate: - self.validate_slave_length(masterlen, valuelen, - opt.impl_getname(), opt) - if valuelen < masterlen: - for num in range(0, masterlen - valuelen): - index = valuelen + num - value.append(values._get_validated_value(opt, path, True, - False, None, - validate_properties=False, - with_meta=master_is_meta, - index=index, - self_properties=self_properties), - setitem=False, - force=True, - validate=validate) - if validate_properties: - context.cfgimpl_get_settings().validate_properties(opt, False, - False, - value=value, - path=path, - force_permissive=force_permissive, - force_properties=force_properties, - setting_properties=setting_properties) + #path = opt.impl_getpath(context) + #valuelen = len(value) + #if validate: + # self.validate_slave_length(masterlen, valuelen, + # opt.impl_getname(), opt) + #if valuelen < masterlen: + + #FIXME voir si pas de plus grande valeur ! + value = values._get_multi(opt, path) + for index in range(0, masterlen): + #index = valuelen + num + value.append(values._get_validated_value(opt, path, validate, + force_permissive, force_properties, + validate_properties, + with_meta=master_is_meta, + index=index, + self_properties=self_properties, + masterlen=masterlen), + setitem=False, + force=True, + validate=validate) + #FIXME hu? + if validate_properties: + context.cfgimpl_get_settings().validate_properties(opt, False, + False, + value=value, + path=path, + force_permissive=force_permissive, + force_properties=force_properties, + setting_properties=setting_properties) return value def setitem(self, values, opt, value, path): @@ -228,14 +239,17 @@ class MasterSlaves(object): base_path = '.'.join(path.split('.')[:-1]) + '.' for slave in self.getslaves(opt): slave_path = base_path + slave.impl_getname() - slave_value = values._get_validated_value(slave, - slave_path, - False, - False, - None, False, - None) # not undefined - slavelen = len(slave_value) + slavelen = values._p_.get_max_length(slave_path) self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt) + #slave_value = values._get_validated_value(slave, + # slave_path, + # False, + # False, + # None, False, + # None, + # masterlen=masterlen) # not undefined + #slavelen = len(slave_value) + #self.validate_slave_length(masterlen, slavelen, slave.impl_getname(), opt) else: self.validate_slave_length(self.get_length(values, opt, slave_path=path), len(value), diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 746608c..8e26688 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -433,7 +433,8 @@ class Settings(object): value=None, force_permissive=False, force_properties=None, setting_properties=undefined, - self_properties=undefined): + self_properties=undefined, + index=None): """ validation upon the properties related to `opt_or_descr` @@ -480,7 +481,7 @@ class Settings(object): else: if 'mandatory' in properties and \ not self._getcontext().cfgimpl_get_values()._isempty( - opt_or_descr, value): + opt_or_descr, value, index=index): properties.remove('mandatory') elif opt_or_descr.impl_is_multi() and \ not is_write and 'empty' in forced_properties and \ diff --git a/tiramisu/storage/dictionary/option.py b/tiramisu/storage/dictionary/option.py index ec24567..b3c135d 100644 --- a/tiramisu/storage/dictionary/option.py +++ b/tiramisu/storage/dictionary/option.py @@ -47,7 +47,7 @@ class StorageBase(object): '_master_slaves', '_choice_values', '_choice_values_params', - #autre + #other '_state_master_slaves', '_state_callback', '_state_callback_params', diff --git a/tiramisu/storage/dictionary/value.py b/tiramisu/storage/dictionary/value.py index 15b91fd..7b663fd 100644 --- a/tiramisu/storage/dictionary/value.py +++ b/tiramisu/storage/dictionary/value.py @@ -15,8 +15,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # ____________________________________________________________ -from copy import copy from ..util import Cache +from tiramisu.setting import undefined class Values(Cache): @@ -25,52 +25,167 @@ class Values(Cache): def __init__(self, storage): """init plugin means create values storage """ - self._values = {} + self._values = (tuple(), tuple(), tuple(), tuple()) self._informations = {} # should init cache too super(Values, self).__init__(storage) # value - def setvalue(self, path, value, owner): + def setvalue(self, path, value, owner, index): """set value for a path a specified value must be associated to an owner """ - self._values[path] = (owner, value) + values = [] + vidx = None - def getvalue(self, path): + def _setvalue_info(nb, idx, value, vidx): + lst = list(self._values[nb]) + if idx is None: + if index is None or nb == 0: + lst.append(value) + else: + lst.append([value]) + else: + if index is None or nb == 0: + lst[idx] = value + else: + if nb == 1: + try: + vidx = lst[idx].index(index) + except ValueError: + vidx = None + if vidx is None: + lst[idx].append(value) + elif nb != 1: + lst[idx][vidx] = value + values.append(tuple(lst)) + return vidx + try: + idx = self._values[0].index(path) + except ValueError: + idx = None + vidx = _setvalue_info(0, idx, path, vidx) + vidx = _setvalue_info(1, idx, index, vidx) + vidx = _setvalue_info(2, idx, value, vidx) + _setvalue_info(3, idx, owner, vidx) + self._values = tuple(values) + + def getvalue(self, path, index=None): """get value for a path return: only value, not the owner """ - return self._values[path][1] + return self._getvalue(path, 2, index) - def hasvalue(self, path): + def hasvalue(self, path, index=None): """if path has a value return: boolean """ - return path in self._values + return path in self._values[0] def resetvalue(self, path): """remove value means delete value in storage """ - del(self._values[path]) + def _resetvalue(nb): + lst = list(self._values[nb]) + lst.pop(idx) + values.append(tuple(lst)) + values = [] + idx = self._values[0].index(path) + _resetvalue(0) + _resetvalue(1) + _resetvalue(2) + _resetvalue(3) + self._values = tuple(values) def get_modified_values(self): """return all values in a dictionary example: {'path1': (owner, 'value1'), 'path2': (owner, 'value2')} """ - return copy(self._values) + values = {} + for idx, path in enumerate(self._values[0]): + values[path] = (self._values[3][idx], self._values[2][idx]) + return values # owner - def setowner(self, path, owner): + def setowner(self, path, owner, index=None): """change owner for a path """ - self._values[path] = (owner, self._values[path][1]) + idx = self._values[0].index(path) + if isinstance(self._values[3][idx], list): + if index is None: + raise ValueError('list but no index') + owner = list(self._values[3][idx])[index] = owner + elif index is not None: + raise ValueError('index set but not a list') + lst = list(self._values[3]) + lst[idx] = owner + values = list(self._values) + values[3] = tuple(lst) + self._values = tuple(values) - def getowner(self, path, default): + def get_max_length(self, path): + try: + idx = self._values[0].index(path) + except ValueError: + return 0 + return max(self._values[1][idx]) + 1 + + def getowner(self, path, default, index=None, only_default=False): """get owner for a path return: owner object """ - return self._values.get(path, (default, None))[0] + if index is None: + if only_default: + if path in self._values[0]: + return None + else: + return default + val = self._getvalue(path, 3, index) + if val is None: + return default + return val + else: + value = self._getvalue(path, 3, index) + if value is None: + return default + else: + return value + + def _getvalue(self, path, nb, index): + """ + _values == ((path1, path2), ((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2), ((idx1_1, idx1_2), None)) + """ + try: + idx = self._values[0].index(path) + except ValueError: + value = None + else: + if isinstance(self._values[1][idx], list): + if index is None: + raise ValueError('list but no index') + elif index is not None: + raise ValueError('index set but not a list') + + if self._values[1][idx] is None: + if index is None: + value = self._values[nb][idx] + else: + value = self._values[nb][idx][index] + else: + if index is not None: + try: + subidx = self._values[1][idx].index(index) + value = self._values[nb][idx][subidx] + except ValueError: + value = None + else: + value = [] + for i in xrange(0, max(self._values[1][idx])): + if i in self._values[1][idx]: + value.append(self._values[nb][idx][self._values[1][idx].index(i)]) + else: + value.append(undefined) + return value def set_information(self, key, value): """updates the information's attribute diff --git a/tiramisu/storage/util.py b/tiramisu/storage/util.py index 1657f18..170232f 100644 --- a/tiramisu/storage/util.py +++ b/tiramisu/storage/util.py @@ -47,11 +47,20 @@ class Cache(object): value = getattr(self, slot) #value has owners object, need 'str()' it if slot == '_values': - _value = {} - for key, values in value.items(): - vals = list(values) - vals[0] = str(vals[0]) - _value[key] = tuple(vals) + _value = [] + _value.append(value[0]) + _value.append(value[1]) + str_owner = [] + _value.append(value[2]) + for owner in value[3]: + if isinstance(owner, list): + str_owners = [] + for subowner in owner: + str_owners.append(str(subowner)) + str_owner.append(str_owners) + else: + str_owner.append(str(owner)) + _value.append(str_owner) states[slot] = _value else: states[slot] = value @@ -60,19 +69,32 @@ class Cache(object): return states def __setstate__(self, states): + def convert_owner(owner): + try: + owner = getattr(owners, owner) + except AttributeError: + owners.addowner(owner) + owner = getattr(owners, owner) + return owner + for key, value in states.items(): #value has owners object, need to reconstruct it if key == '_values': - _value = {} - for key_, values_ in value.items(): - vals = list(values_) - try: - vals[0] = getattr(owners, vals[0]) - except AttributeError: - owners.addowner(vals[0]) - vals[0] = getattr(owners, vals[0]) - _value[key_] = tuple(vals) - value = _value + _value = [] + _value.append(value[0]) + _value.append(value[1]) + _value.append(value[2]) + obj_owner = [] + for owner in value[3]: + if isinstance(owner, list): + obj_owners = [] + for subowner in owner: + obj_owners.append(convert_owner(subowner)) + obj_owner.append(tuple(obj_owners)) + else: + obj_owner.append(convert_owner(owner)) + _value.append(tuple(obj_owner)) + value = tuple(_value) setattr(self, key, value) def setcache(self, path, val, time): diff --git a/tiramisu/value.py b/tiramisu/value.py index 18b3afa..aa8b5cc 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -54,33 +54,10 @@ class Values(object): raise ConfigError(_('the context does not exist anymore')) return context - def _getvalue(self, opt, path, is_default, index=undefined, - with_meta=True, self_properties=undefined): - """actually retrieves the value + def _get_multi(self, opt, path): + return Multi([], self.context, opt, path) - :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 - if not is_default and not force_default: - value = self._p_.getvalue(path) - if index is not undefined: - try: - return value[index] - except IndexError: - #value is smaller than expected - #so return default value - pass - else: - return value - #so default value + def _getdefaultvalue(self, opt, path, with_meta, index): # if value has callback and is not set if opt.impl_has_callback(): callback, callback_params = opt.impl_get_callback() @@ -126,6 +103,56 @@ 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, + 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 + 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 + else: + value = self._p_.getvalue(path) + if index is not undefined: + try: + return value[index] + except IndexError: + #value is smaller than expected + #so return default value + pass + else: + return value + return self._getdefaultvalue(opt, path, with_meta, index) + def get_modified_values(self): context = self._getcontext() if context._impl_descr is not None: @@ -175,13 +202,13 @@ class Values(object): if hasvalue: self._p_.resetvalue(path) - def _isempty(self, opt, value, force_allow_empty_list=False): + def _isempty(self, opt, value, force_allow_empty_list=False, index=None): "convenience method to know if an option is empty" if value is undefined: return False else: empty = opt._empty - if opt.impl_is_multi(): + if index in [None, undefined] and opt.impl_is_multi(): if force_allow_empty_list: allow_empty_list = True else: @@ -273,7 +300,7 @@ class Values(object): force_properties, validate_properties, index=undefined, submulti_index=undefined, with_meta=True, setting_properties=undefined, - self_properties=undefined): + self_properties=undefined, masterlen=undefined): """same has getitem but don't touch the cache index is None for slave value, if value returned is not a list, just return [] """ @@ -286,7 +313,8 @@ class Values(object): is_default = self._is_default_owner(opt, path, validate_properties=False, validate_meta=False, - self_properties=self_properties) + self_properties=self_properties, + index=index) try: if index is None: gv_index = undefined @@ -294,7 +322,8 @@ class Values(object): gv_index = index value = self._getvalue(opt, path, is_default, index=gv_index, with_meta=with_meta, - self_properties=self_properties) + self_properties=self_properties, + masterlen=masterlen) config_error = None except ConfigError as err: # For calculating properties, we need value (ie for mandatory @@ -309,8 +338,7 @@ class Values(object): # value is not set, for 'undefined' (cannot set None because of # mandatory property) value = undefined - - if config_error is None: + else: if index is undefined: force_index = None else: @@ -358,7 +386,8 @@ class Values(object): force_permissive=force_permissive, force_properties=force_properties, setting_properties=setting_properties, - self_properties=self_properties) + self_properties=self_properties, + index=index) if config_error is not None: raise config_error return value @@ -394,7 +423,7 @@ class Values(object): def _setvalue(self, opt, path, value, force_permissive=False, is_write=True, validate_properties=True, - setting_properties=undefined): + setting_properties=undefined, index=None): context = self._getcontext() context.cfgimpl_reset_cache() if validate_properties: @@ -410,7 +439,15 @@ class Values(object): if isinstance(val, SubMulti): value[idx] = list(val) owner = context.cfgimpl_get_settings().getowner() - self._p_.setvalue(path, value, owner) + if opt.impl_is_master_slaves('slave') and index is None: + try: + self._p_.resetvalue(path) + except ValueError: + pass + for idx, val in enumerate(value): + self._p_.setvalue(path, val, owner, idx) + else: + self._p_.setvalue(path, value, owner, index) def _is_meta(self, opt, path): context = self._getcontext() @@ -418,13 +455,13 @@ class Values(object): self_properties = setting._getproperties(opt, path, read_write=False) if 'frozen' in self_properties and 'force_default_on_freeze' in self_properties: return False - if self._p_.getowner(path, owners.default) is not owners.default: + if self._p_.getowner(path, owners.default, only_default=True) is not owners.default: return False if context.cfgimpl_get_meta() is not None: return True return False - def getowner(self, opt, force_permissive=False): + def getowner(self, opt, index=None, force_permissive=False): """ retrieves the option's owner @@ -437,11 +474,12 @@ class Values(object): not isinstance(opt, DynSymLinkOption): opt = opt._impl_getopt() path = opt.impl_getpath(self._getcontext()) - return self._getowner(opt, path, force_permissive=force_permissive) + return self._getowner(opt, path, index=index, force_permissive=force_permissive) def _getowner(self, opt, path, validate_properties=True, force_permissive=False, validate_meta=undefined, - self_properties=undefined): + self_properties=undefined, only_default=False, + index=None): """get owner of an option """ if not isinstance(opt, Option) and not isinstance(opt, @@ -456,7 +494,7 @@ class Values(object): if validate_properties: self._getitem(opt, path, True, force_permissive, None, True, self_properties=self_properties) - owner = self._p_.getowner(path, owners.default) + owner = self._p_.getowner(path, owners.default, only_default=only_default, index=index) if validate_meta is undefined: if opt.impl_is_master_slaves('slave'): master = opt.impl_get_master_slaves().getmaster(opt) @@ -467,7 +505,11 @@ class Values(object): if validate_meta: meta = context.cfgimpl_get_meta() if owner is owners.default and meta is not None: - owner = meta.cfgimpl_get_values()._getowner(opt, path) + owner = meta.cfgimpl_get_values()._getowner(opt, path, + validate_properties=validate_properties, + force_permissive=force_permissive, + self_properties=self_properties, + only_default=only_default, index=index) return owner def setowner(self, opt, owner): @@ -481,9 +523,6 @@ class Values(object): raise TypeError(_("invalid generic owner {0}").format(str(owner))) path = opt.impl_getpath(self._getcontext()) - self._setowner(opt, path, owner) - - def _setowner(self, opt, path, owner): if not self._p_.hasvalue(path): # pragma: optional cover raise ConfigError(_('no value for {0} cannot change owner to {1}' '').format(path, owner)) @@ -495,7 +534,7 @@ class Values(object): self._p_.setowner(path, owner) def is_default_owner(self, opt, validate_properties=True, - validate_meta=True): + validate_meta=True, index=None): """ :param config: *must* be only the **parent** config (not the toplevel config) @@ -504,14 +543,18 @@ class Values(object): path = opt.impl_getpath(self._getcontext()) return self._is_default_owner(opt, path, validate_properties=validate_properties, - validate_meta=validate_meta) + validate_meta=validate_meta, index=index) def _is_default_owner(self, opt, path, validate_properties=True, - validate_meta=True, self_properties=undefined): - return self._getowner(opt, path, validate_properties, - validate_meta=validate_meta, - self_properties=self_properties) == \ - owners.default + validate_meta=True, self_properties=undefined, + index=None): + if not opt.impl_is_master_slaves('slave'): + index = None + d = self._getowner(opt, path, validate_properties, + validate_meta=validate_meta, + self_properties=self_properties, only_default=True, + index=index) + return d == owners.default def reset_cache(self, only_expired): """ @@ -640,7 +683,6 @@ class Values(object): # ____________________________________________________________ # multi types - class Multi(list): """multi options values container that support item notation for the values of multi options""" @@ -708,7 +750,7 @@ class Multi(list): self._validate(value, fake_context, index, True) #assume not checking mandatory property super(Multi, self).__setitem__(index, value) - context.cfgimpl_get_values()._setvalue(self.opt, self.path, self, setting_properties=setting_properties) + self._store() #def __repr__(self, *args, **kwargs): # return super(Multi, self).__repr__(*args, **kwargs) @@ -750,7 +792,7 @@ class Multi(list): value.submulti = weakref.ref(self) super(Multi, self).append(value) if setitem: - self._store(force) + self._store(force=force) def sort(self, cmp=None, key=None, reverse=False): if self.opt.impl_is_master_slaves(): @@ -773,7 +815,6 @@ class Multi(list): self._store() def insert(self, index, value, validate=True): - #FIXME value should be undefined if self.opt.impl_is_master_slaves(): raise SlaveError(_("cannot insert multi option {0} if master or " "slave").format(self.opt.impl_getname())) @@ -833,7 +874,7 @@ class Multi(list): context.cfgimpl_get_values(), index) #set value without valid properties ret = super(Multi, self).pop(index) - self._store(force) + self._store(force=force) return ret def _store(self, force=False):