diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index f14e4d1..78479c9 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -5,12 +5,16 @@ from py.test import raises from tiramisu.setting import groups, owners from tiramisu.config import Config, GroupConfig, MetaConfig -from tiramisu.option import IntOption, StrOption, OptionDescription +from tiramisu.option import IntOption, StrOption, NetworkOption, NetmaskOption, OptionDescription from tiramisu.error import ConfigError, ConflictError owners.addowner('meta') +def raise_exception(): + raise Exception('test') + + def make_description(): i1 = IntOption('i1', '') i2 = IntOption('i2', '', default=1) @@ -155,7 +159,6 @@ def test_meta_meta_set(): meta2.cfgimpl_get_settings().setowner(owners.meta) conf1, conf2 = meta1.cfgimpl_get_children() meta2.set_value('od1.i1', 7, only_config=True) - #PropertiesOptionError meta2.set_value('od1.i6', 7, only_config=True) assert conf1.od1.i1 == conf2.od1.i1 == 7 assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user @@ -500,3 +503,33 @@ def test_meta_force_default_and_dont_change(): meta.read_write() meta.cfgimpl_get_settings().setowner(owners.meta) raises(ValueError, "meta.set_value('ip_admin_eth0', ['192.168.1.4'], force_default=True, force_dont_change_value=True)") + + +def test_meta_properties_meta(): + ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1']) + netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", multi=True, properties=('disabled',)) + netmask_admin_eth0.impl_add_consistency('network_netmask', ip_admin_eth0) + interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + interface1.impl_set_group_type(groups.master) + conf1 = Config(interface1, name='conf1') + conf1.read_write() + conf2 = Config(interface1, name='conf2') + conf2.read_write() + meta = MetaConfig([conf1, conf2]) + meta.read_write() + assert conf1.make_dict() == {} + + +def test_meta_exception_meta(): + ip_admin_eth0 = NetworkOption('ip_admin_eth0', "ip", multi=True, default=['192.168.1.1']) + netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "mask", multi=True, callback=raise_exception) + netmask_admin_eth0.impl_add_consistency('network_netmask', ip_admin_eth0) + interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + interface1.impl_set_group_type(groups.master) + conf1 = Config(interface1, name='conf1') + conf1.read_write() + conf2 = Config(interface1, name='conf2') + conf2.read_write() + meta = MetaConfig([conf1, conf2]) + meta.read_write() + raises(Exception, "conf1.make_dict()") diff --git a/test/test_submulti.py b/test/test_submulti.py index 8793387..9477699 100644 --- a/test/test_submulti.py +++ b/test/test_submulti.py @@ -3,7 +3,7 @@ from autopath import do_autopath do_autopath() from tiramisu.setting import groups, owners -from tiramisu.config import Config +from tiramisu.config import Config, MetaConfig from tiramisu.option import StrOption, IntOption, OptionDescription, submulti from tiramisu.value import SubMulti, Multi from tiramisu.error import SlaveError @@ -679,3 +679,20 @@ def test_submulti_unique(): raises(ValueError, "c.int[0].extend([1, 2, 1, 3])") raises(ValueError, "c.int[0].extend([1, 2, 0, 3])") c.int[0].extend([4, 5, 6]) + + +def test_multi_submulti_meta(): + multi = StrOption('multi', '', multi=submulti) + od = OptionDescription('od', '', [multi]) + conf1 = Config(od, name='conf1') + conf1.read_write() + conf2 = Config(od, name='conf2') + conf2.read_write() + meta = MetaConfig([conf1, conf2]) + meta.read_write() + meta.multi = [['val']] + assert meta.multi == [['val']] + multi = conf1.multi[0] + multi.append() + assert conf1.multi == [['val', None]] + assert meta.multi == [['val']] diff --git a/tiramisu/config.py b/tiramisu/config.py index 3e398ec..05aee2b 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -300,10 +300,10 @@ class SubConfig(object): path = context.cfgimpl_get_description().impl_get_path_by_opt( option._impl_getopt()) cfg = context.getattr(path, validate=validate, - force_permissive=force_permissive, - _setting_properties=_setting_properties, - _self_properties=_self_properties, - index=index, returns_raise=True) + force_permissive=force_permissive, + _setting_properties=_setting_properties, + _self_properties=_self_properties, + index=index, returns_raise=True) elif option.impl_is_optiondescription(): props = self.cfgimpl_get_settings().validate_properties( option, True, False, path=subpath, @@ -854,13 +854,15 @@ class GroupConfig(_CommonConfig): return ret def getattr(self, name, force_permissive=False, validate=True, - _setting_properties=undefined, + _setting_properties=undefined, _self_properties=undefined, index=None, returns_raise=False): for child in self._impl_children: if name == child._impl_name: return child return super(GroupConfig, self).getattr(name, force_permissive, validate, + _self_properties=_self_properties, + index=index, _setting_properties=_setting_properties, returns_raise=returns_raise) diff --git a/tiramisu/value.py b/tiramisu/value.py index 2631b40..bc6ed70 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -50,7 +50,7 @@ class Values(object): old `SubConfig`, `Values`, `Multi` or `Settings`) """ context = self.context() - if context is None: # pragma: optional cover + if context is None: raise ConfigError(_('the context does not exist anymore')) return context @@ -80,14 +80,25 @@ class Values(object): )._get_cached_value(opt, path, index=index, submulti_index=submulti_index, from_masterslave=True) if isinstance(value, Exception): - if not isinstance(value, PropertiesOptionError): + if not isinstance(value, PropertiesOptionError): # pragma: no cover raise value else: if isinstance(value, Multi): if index is not None: value = value[index] + if isinstance(value, SubMulti): + if submulti_index is not undefined: + value = value[submulti_index] + else: + value = list(value) else: - value = list(value) + new_value = [] + for val in value: + if isinstance(val, SubMulti): + val = list(val) + new_value.append(val) + value = new_value + del new_value return value # now try to get default value value = opt.impl_getdefault() @@ -182,7 +193,7 @@ class Values(object): read_write=False, apply_requires=False): value = self._getdefaultvalue(opt, path, True, undefined, undefined, validate) - if isinstance(value, Exception): + if isinstance(value, Exception): # pragma: no cover raise value self._setvalue(opt, path, value, force_owner=owners.forced) else: @@ -328,20 +339,16 @@ class Values(object): # value is not set, for 'undefined' (cannot set None because of # mandatory property) value = undefined - else: + else: # pragma: no cover raise value else: value_error = False - if index is undefined: - force_index = None - else: - force_index = index if opt.impl_is_multi(): - if force_index is None: + if index is None: value = Multi(value, self.context, opt, path) elif opt.impl_is_submulti() and submulti_index is undefined: value = SubMulti(value, self.context, opt, path, - force_index) + index) if validate: if submulti_index is undefined: @@ -350,7 +357,7 @@ class Values(object): force_submulti_index = submulti_index err = opt.impl_validate(value, context, 'validator' in setting_properties, - force_index=force_index, + force_index=index, force_submulti_index=force_submulti_index, display_error=True, display_warnings=False) @@ -376,7 +383,7 @@ class Values(object): if not value_error and validate and display_warnings: opt.impl_validate(value, context, 'validator' in setting_properties, - force_index=force_index, + force_index=index, force_submulti_index=force_submulti_index, display_error=False, display_warnings=display_warnings) @@ -384,7 +391,7 @@ class Values(object): return config_error return value - def __setitem__(self, opt, value): # pragma: optional cover + def __setitem__(self, opt, value): raise ConfigError(_('you should only set value with config')) def setitem(self, opt, value, path, force_permissive=False, @@ -535,12 +542,12 @@ class Values(object): :param opt: the `option.Option` object :param owner: a valid owner, that is a `setting.owners.Owner` object """ - if not isinstance(owner, owners.Owner): # pragma: optional cover + if not isinstance(owner, owners.Owner): raise TypeError(_("invalid generic owner {0}").format(str(owner))) path = opt.impl_getpath(self._getcontext()) session = self._p_.getsession() - if not self._p_.hasvalue(path, session): # pragma: optional cover + if not self._p_.hasvalue(path, session): raise ConfigError(_('no value for {0} cannot change owner to {1}' '').format(path, owner)) props = self._getcontext().cfgimpl_get_settings().validate_properties(opt, @@ -682,7 +689,7 @@ class Values(object): for path in context.cfgimpl_get_description().impl_getpaths( include_groups=True): err = context.getattr(path, returns_raise=True) - if isinstance(err, Exception) and not isinstance(err, PropertiesOptionError): + if isinstance(err, Exception) and not isinstance(err, PropertiesOptionError): # pragma: no cover raise err def __getstate__(self): @@ -711,12 +718,12 @@ class Multi(list): """ if value is None: value = [] - if not opt.impl_is_submulti() and isinstance(value, Multi): # pragma: optional cover + if not opt.impl_is_submulti() and isinstance(value, Multi): raise ValueError(_('{0} is already a Multi ').format( opt.impl_getname())) self.opt = opt self.path = path - if not isinstance(context, weakref.ReferenceType): # pragma: optional cover + if not isinstance(context, weakref.ReferenceType): raise ValueError('context must be a Weakref') self.context = context if not isinstance(value, list): @@ -745,7 +752,7 @@ class Multi(list): old `SubConfig`, `Values`, `Multi` or `Settings`) """ context = self.context() - if context is None: # pragma: optional cover + if context is None: raise ConfigError(_('the context does not exist anymore')) return context @@ -791,7 +798,7 @@ class Multi(list): """the list value can be updated (appened) only if the option is a master """ - if not force and self.opt.impl_is_master_slaves('slave'): # pragma: optional cover + if not force and self.opt.impl_is_master_slaves('slave'): raise SlaveError(_("cannot append a value on a multi option {0}" " which is a slave").format(self.opt.impl_getname())) index = self.__len__() @@ -827,7 +834,7 @@ class Multi(list): if self.opt.impl_is_master_slaves(): raise SlaveError(_("cannot sort multi option {0} if master or slave" "").format(self.opt.impl_getname())) - if sys.version_info[0] >= 3: + if sys.version_info[0] >= 3: # pragma: no cover if cmp is not None: raise ValueError(_('cmp is not permitted in python v3 or ' 'greater')) @@ -901,7 +908,7 @@ class Multi(list): """ context = self._getcontext() if not force: - if self.opt.impl_is_master_slaves('slave'): # pragma: optional cover + if self.opt.impl_is_master_slaves('slave'): raise SlaveError(_("cannot pop a value on a multi option {0}" " which is a slave").format(self.opt.impl_getname())) if self.opt.impl_is_master_slaves('master'):