diff --git a/ChangeLog b/ChangeLog index 85fdc0d..65824bc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Mon Dec 1 22:58:13 2014 +0200 Emmanuel Garette + * propertyerror are transitive in consistency, now it's possible to set + non-transitive consistency + 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 diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 4a39d7e..cca1909 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -5,7 +5,7 @@ from tiramisu.setting import owners, groups from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ BroadcastOption, SymLinkOption, OptionDescription -from tiramisu.error import ConfigError, ValueWarning +from tiramisu.error import ConfigError, ValueWarning, PropertiesOptionError import warnings @@ -27,6 +27,13 @@ def test_consistency_not_exists(): 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]) + raises(ValueError, "a.impl_add_consistency('not_equal', b, unknown=False)") + + def test_consistency_warnings_only(): a = IntOption('a', '') b = IntOption('b', '') @@ -392,6 +399,26 @@ def test_consistency_permissive(): c.a = 1 +def test_consistency_disabled(): + a = IntOption('a', '') + b = IntOption('b', '', properties=('disabled',)) + od = OptionDescription('od', '', [a, b]) + a.impl_add_consistency('not_equal', b) + c = Config(od) + c.read_write() + raises(PropertiesOptionError, "c.a = 1") + + +def test_consistency_disabled_transitive(): + a = IntOption('a', '') + b = IntOption('b', '', properties=('disabled',)) + od = OptionDescription('od', '', [a, b]) + a.impl_add_consistency('not_equal', b, transitive=False) + c = Config(od) + c.read_write() + c.a = 1 + + def return_val(*args, **kwargs): return '192.168.1.1' diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 74b26ca..8d08c80 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -25,7 +25,7 @@ import warnings from tiramisu.i18n import _ from tiramisu.setting import log, undefined from tiramisu.autolib import carry_out_calculation -from tiramisu.error import ConfigError, ValueWarning +from tiramisu.error import ConfigError, ValueWarning, PropertiesOptionError from tiramisu.storage import get_storages_option @@ -403,7 +403,8 @@ class Option(OnlyOption): _empty = '' def _launch_consistency(self, func, option, value, context, index, - submulti_index, all_cons_opts, warnings_only): + submulti_index, all_cons_opts, warnings_only, + transitive): """Launch consistency now :param func: function name, this name should start with _cons_ @@ -420,47 +421,55 @@ class Option(OnlyOption): :type all_cons_opts: `list` of `tiramisu.option.Option` :param warnings_only: specific raise error for warning :type warnings_only: `boolean` + :param transitive: propertyerror is transitive + :type transitive: `boolean` """ if context is not undefined: descr = context.cfgimpl_get_description() all_cons_vals = [] for opt in all_cons_opts: - #get value - if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \ - option == opt: - opt_value = value - else: - #if context, calculate value, otherwise get 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) - opt_value = context.getattr(path, validate=False, - force_permissive=True) + try: + #get value + if (isinstance(opt, DynSymLinkOption) and option._dyn == opt._dyn) or \ + option == opt: + opt_value = value else: - opt_value = opt.impl_getdefault() + #if context, calculate value, otherwise get 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) + opt_value = context.getattr(path, validate=False, + force_permissive=True) + else: + opt_value = opt.impl_getdefault() - #append value - if not self.impl_is_multi() or (isinstance(opt, DynSymLinkOption) - and option._dyn == opt._dyn) or \ - option == opt: - all_cons_vals.append(opt_value) - elif self.impl_is_submulti(): - try: - all_cons_vals.append(opt_value[index][submulti_index]) - except IndexError: - #value is not already set, could be higher index - #so return if no value - return - else: - try: - all_cons_vals.append(opt_value[index]) - except IndexError: - #value is not already set, could be higher index - #so return if no value - return + #append value + if not self.impl_is_multi() or (isinstance(opt, DynSymLinkOption) + and option._dyn == opt._dyn) or \ + option == opt: + all_cons_vals.append(opt_value) + elif self.impl_is_submulti(): + try: + all_cons_vals.append(opt_value[index][submulti_index]) + except IndexError: + #value is not already set, could be higher index + #so return if no value + return + else: + try: + all_cons_vals.append(opt_value[index]) + except IndexError: + #value is not already set, could be higher index + #so return if no value + return + except PropertiesOptionError as err: + if transitive: + raise err + else: + pass getattr(self, func)(all_cons_opts, all_cons_vals, warnings_only) def impl_validate(self, value, context=undefined, validate=True, @@ -636,14 +645,22 @@ class Option(OnlyOption): :type func: `str` :param other_opts: options used to validate value :type other_opts: `list` of `tiramisu.option.Option` - :param params: extra params (only warnings_only are allowed) + :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 = params.get('warnings_only', False) + 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)) if self._is_subdyn(): dynod = self._impl_getsubdyn() else: @@ -678,16 +695,17 @@ class Option(OnlyOption): if not self.impl_is_submulti(): self._launch_consistency(func, self, val, undefined, idx, None, all_cons_opts, - warnings_only) + 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) + warnings_only, transitive) else: self._launch_consistency(func, self, value, undefined, None, - None, all_cons_opts, warnings_only) + None, all_cons_opts, warnings_only, + transitive) self._add_consistency(func, all_cons_opts, params) self.impl_validate(self.impl_getdefault()) diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index e9898d3..d719137 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -175,6 +175,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): 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]) @@ -190,7 +191,7 @@ class OptionDescription(BaseOption, StorageOptionDescription): try: opts[0]._launch_consistency(func, option, value, context, index, submulti_idx, opts, - warnings_only) + warnings_only, transitive) except ValueError as err: if warnings_only: raise ValueWarning(err.message, option)