From 683e40fbb5143190932edbf0fe03a7ee3464b341 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 2 Feb 2014 18:20:01 +0100 Subject: [PATCH] when change len of calculated master, change len of slave too --- test/test_option_calculation.py | 63 ++++++++++++++++++++++ test/test_state.py | 43 +++++++++++++++ tiramisu/autolib.py | 5 +- tiramisu/value.py | 95 ++++++++++++++++----------------- 4 files changed, 157 insertions(+), 49 deletions(-) diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 91d61a4..89f9291 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -373,6 +373,9 @@ def test_callback_multi_value(): assert cfg.val2 == ['val'] assert cfg.val3 == ['yes'] assert cfg.val4 == ['val', 'yes'] + cfg.val2.append('new') + assert cfg.val1 == ['val'] + assert cfg.val2 == ['val', 'new'] def test_callback_multi_list(): @@ -466,6 +469,66 @@ def test_callback_master_and_slaves_slave(): assert cfg.val1.val2 == ['val2', 'val2', 'val'] +def test_callback_master_and_slaves_slave_cal(): + val3 = StrOption('val3', "", multi=True) + val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val3, False),)}) + val2 = StrOption('val2', "", multi=True, callback=return_val) + 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 == [] + assert cfg.val1.val1 == [] + assert cfg.val1.val2 == [] + cfg.val1.val1 = ['val1'] + cfg.val3 = ['val1'] + assert cfg.val1.val1 == ['val1'] + assert cfg.val1.val2 == ['val'] + assert cfg.val1.val1 == ['val1'] + assert cfg.val1.val2 == ['val'] + del(cfg.val1.val1) + cfg.val1.val2 = ['val'] + cfg.val3 = ['val1', 'val2'] + assert cfg.val1.val2 == ['val', 'val'] + assert cfg.val1.val1 == ['val1', 'val2'] + cfg.val1.val2 = ['val1', '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 = ['val1', 'val2', 'val3'] + 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_slave_list(): val1 = StrOption('val1', "", multi=True) val2 = StrOption('val2', "", multi=True, callback=return_list) diff --git a/test/test_state.py b/test/test_state.py index ef46ce2..7693cb6 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -249,3 +249,46 @@ def test_state_values_owner(): delete_session('29090931') except ConfigError: pass + + +def test_state_unkown_setting_owner(): + """load an unknow _owner, should create it""" + assert not 'supernewuser' in owners.__dict__ + loads("""ccopy_reg +_reconstructor +p0 +(ctiramisu.setting +Settings +p1 +c__builtin__ +object +p2 +Ntp3 +Rp4 +(dp5 +S'_owner' +p6 +S'supernewuser' +p7 +sS'_p_' +p8 +g0 +(ctiramisu.storage.dictionary.setting +Settings +p9 +g2 +Ntp10 +Rp11 +(dp12 +S'_cache' +p13 +(dp14 +sS'_permissives' +p15 +(dp16 +sS'_properties' +p17 +(dp18 +sbsb. +.""") + assert 'supernewuser' in owners.__dict__ diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index ffdbb0a..cb3552a 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -152,7 +152,10 @@ def carry_out_calculation(option, config, callback, callback_params, opt) # get value try: - value = config._getattr(path, force_permissive=True) + value = config._getattr(path, force_permissive=True, validate=False) + # convert to list, not modifie this multi + if value.__class__.__name__ == 'Multi': + value = list(value) except PropertiesOptionError as err: if force_permissive: continue diff --git a/tiramisu/value.py b/tiramisu/value.py index a02196a..863f2a1 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -73,7 +73,7 @@ class Values(object): else: return value - def _getvalue(self, opt, path, validate=True): + def _getvalue(self, opt, path): """actually retrieves the value :param opt: the `option.Option()` object @@ -82,14 +82,9 @@ class Values(object): if not self._p_.hasvalue(path): # if there is no value value = self._getdefault(opt) - if opt.impl_is_multi(): - value = Multi(value, self.context, opt, path, validate) else: # if there is a value value = self._p_.getvalue(path) - if opt.impl_is_multi() and not isinstance(value, Multi): - # load value so don't need to validate if is not a Multi - value = Multi(value, self.context, opt, path, validate=False) return value def get_modified_values(self): @@ -198,7 +193,7 @@ class Values(object): # ConfigError if properties did not raise. config_error = None force_permissives = None - # if value is callback and is not set + # if value has callback and is not set # or frozen with force_default_on_freeze if opt.impl_has_callback() and ( self._is_default_owner(path) or @@ -208,7 +203,7 @@ class Values(object): if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave): masterp = self._get_opt_path(opt.impl_get_master_slaves()) - mastervalue = getattr(context, masterp) + mastervalue = context._getattr(masterp, validate=validate) lenmaster = len(mastervalue) if lenmaster == 0: value = [] @@ -240,7 +235,10 @@ class Values(object): if opt.impl_is_multi(): value = Multi(value, self.context, opt, path, validate) else: - value = self._getvalue(opt, path, validate) + value = self._getvalue(opt, path) + if opt.impl_is_multi(): + # load value so don't need to validate if is not a Multi + value = Multi(value, self.context, opt, path, validate=validate) if config_error is None and validate: opt.impl_validate(value, context, 'validator' in setting) if config_error is None and self._is_default_owner(path) and \ @@ -266,10 +264,27 @@ class Values(object): context = self._getcontext() opt.impl_validate(value, context, 'validator' in context.cfgimpl_get_settings()) - if opt.impl_is_multi() and not isinstance(value, Multi): + if opt.impl_is_multi(): value = Multi(value, self.context, opt, path, setitem=True) + # Save old value + if opt.impl_get_multitype() == multitypes.master and \ + self._p_.hasvalue(path): + old_value = self._p_.getvalue(path) + old_owner = self._p_.getowner(path, None) + else: + old_value = undefined + old_owner = undefined self._setvalue(opt, path, value, force_permissive=force_permissive, is_write=is_write) + if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master: + try: + value._valid_master() + except Exception, err: + if old_value is not undefined: + self._p_.setvalue(path, old_value, old_owner) + else: + self._p_.resetvalue(path) + raise err def _setvalue(self, opt, path, value, force_permissive=False, force_properties=None, @@ -283,6 +298,8 @@ class Values(object): force_permissive=force_permissive, force_properties=force_properties) owner = context.cfgimpl_get_settings().getowner() + if isinstance(value, Multi): + value = list(value) self._p_.setvalue(path, value, owner) def getowner(self, opt): @@ -403,6 +420,8 @@ class Multi(list): :param opt: the option object that have this Multi value :param setitem: only if set a value """ + if isinstance(value, Multi): + raise ValueError(_('{0} is already a Multi ').format(opt._name)) self.opt = opt self.path = path if not isinstance(context, weakref.ReferenceType): @@ -412,8 +431,9 @@ class Multi(list): value = [value] if validate and self.opt.impl_get_multitype() == multitypes.slave: value = self._valid_slave(value, setitem) - elif validate and self.opt.impl_get_multitype() == multitypes.master: - self._valid_master(value) + elif not setitem and validate and \ + self.opt.impl_get_multitype() == multitypes.master: + self._valid_master() super(Multi, self).__init__(value) def _getcontext(self): @@ -433,12 +453,10 @@ class Multi(list): values = context.cfgimpl_get_values() masterp = context.cfgimpl_get_description().impl_get_path_by_opt( self.opt.impl_get_master_slaves()) - mastervalue = getattr(context, masterp) + mastervalue = context._getattr(masterp, validate=False) masterlen = len(mastervalue) valuelen = len(value) - is_default_owner = not values._is_default_owner(self.path) or setitem - if valuelen > masterlen or (valuelen < masterlen and - is_default_owner): + if valuelen > masterlen or (valuelen < masterlen and setitem): raise SlaveError(_("invalid len for the slave: {0}" " which has {1} as master").format( self.opt._name, masterp)) @@ -455,30 +473,12 @@ class Multi(list): #else: same len so do nothing return value - def _valid_master(self, value): - masterlen = len(value) + def _valid_master(self): + #masterlen = len(value) values = self._getcontext().cfgimpl_get_values() for slave in self.opt._master_slaves: path = values._get_opt_path(slave) - if not values._is_default_owner(path): - value_slave = values._getvalue(slave, path) - if len(value_slave) > masterlen: - raise SlaveError(_("invalid len for the master: {0}" - " which has {1} as slave with" - " greater len").format( - self.opt._name, slave._name)) - elif len(value_slave) < masterlen: - for num in range(0, masterlen - len(value_slave)): - if slave.impl_has_callback(): - # if callback add a value, but this value will not - # change anymore automaticly (because this value - # has owner) - index = value_slave.__len__() - value_slave.append( - values._getcallback_value(slave, index=index), - force=True) - else: - value_slave.append(undefined, force=True) + Multi(values._getvalue(slave, path), self.context, slave, path) def __setitem__(self, index, value): self._validate(value, index) @@ -518,16 +518,15 @@ class Multi(list): dvalue = values._getcallback_value(slave, index=index) else: dvalue = slave.impl_getdefault_multi() - old_value = values.getitem(slave, path, + old_value = values.getitem(slave, path, validate=False, validate_properties=False) - if len(old_value) < self.__len__(): - values.getitem(slave, path, - validate_properties=False).append( - dvalue, force=True) - else: - values.getitem(slave, path, - validate_properties=False)[ - index] = dvalue + if len(old_value) + 1 != self.__len__(): + raise SlaveError(_("invalid len for the slave: {0}" + " which has {1} as master").format( + self.opt._name, self.__len__())) + values.getitem(slave, path, validate=False, + validate_properties=False).append( + dvalue, force=True) def sort(self, cmp=None, key=None, reverse=False): if self.opt.impl_get_multitype() in [multitypes.slave, @@ -592,12 +591,12 @@ class Multi(list): if self.opt.impl_get_multitype() == multitypes.slave: raise SlaveError(_("cannot pop a value on a multi option {0}" " which is a slave").format(self.opt._name)) - elif self.opt.impl_get_multitype() == multitypes.master: + if self.opt.impl_get_multitype() == multitypes.master: for slave in self.opt.impl_get_master_slaves(): values = context.cfgimpl_get_values() if not values.is_default_owner(slave): #get multi without valid properties - values.getitem(slave, + values.getitem(slave, validate=False, validate_properties=False ).pop(index, force=True) #set value without valid properties