propertyerror are transitive in consistency, now it's possible to set non-transitive...
authorEmmanuel Garette <egarette@cadoles.com>
Mon, 1 Dec 2014 21:58:53 +0000 (22:58 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Mon, 1 Dec 2014 21:58:53 +0000 (22:58 +0100)
ChangeLog
test/test_option_consistency.py
tiramisu/option/baseoption.py
tiramisu/option/optiondescription.py

index 85fdc0d..65824bc 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+Mon Dec  1 22:58:13 2014 +0200 Emmanuel Garette <egarette@cadoles.com>
+       * propertyerror are transitive in consistency, now it's possible to set
+       non-transitive consistency
+
 Sun Oct 26 08:50:38 2014 +0200 Emmanuel Garette <egarette@cadoles.com>
        * if option is frozen with force_default_on_freeze property, owner
        must be 'default' check property when tried to change owner
index 4a39d7e..cca1909 100644 (file)
@@ -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'
 
index 74b26ca..8d08c80 100644 (file)
@@ -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)
+            try:
+                #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)
                     else:
-                        path = descr.impl_get_path_by_opt(opt)
-                    opt_value = context.getattr(path, validate=False,
-                                                force_permissive=True)
+                        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:
-                    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
+                    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())
 
index e9898d3..d719137 100644 (file)
@@ -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)