validator's function can have 1 arg, 2 args or 3 args
authorEmmanuel Garette <egarette@cadoles.com>
Thu, 12 Jan 2017 20:58:53 +0000 (21:58 +0100)
committerEmmanuel Garette <egarette@cadoles.com>
Thu, 12 Jan 2017 20:58:53 +0000 (21:58 +0100)
ChangeLog
test/test_option_validator.py
tiramisu/autolib.py
tiramisu/option/baseoption.py
tiramisu/setting.py

index 065b37d..f4862b1 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 Thu Jan 12 19:49:41 2017 +0200 Emmanuel Garette <egarette@cadoles.com>
        * can mix inversed and non inversed requires
+       * validator's function can have 1 arg, 2 args or 3 args without
+       valid_params specify by user. First arg will receive the value, second
+       arg will receive all values (useful for multi) and the third one will
+       receive index (useful for multi).
+       This functionaly only works for now if user not specify valid_params.
 
 Wed Jan 11 22:56:30 2017 +0200 Emmanuel Garette <egarette@cadoles.com>
        * copy the context in carry_out_calculation
index 27d060f..5e58fb7 100644 (file)
@@ -40,6 +40,19 @@ def is_context(value, context):
         raise ValueError('not context')
 
 
+def value_values(value, values):
+    if not (value == 'val' and values == ['val'] or
+            value == 'val1' and values == ['val1'] or
+            value == 'val2' and values == ['val1', 'val2']):
+        raise ValueError('error')
+
+
+def value_values_index(value, values, index):
+    value_values(value, values)
+    if not (index == 0 or (value == 'val2' and index == 1)):
+        raise ValueError('error 2')
+
+
 def test_validator():
     opt1 = StrOption('opt1', '', validator=return_true, default='val')
     raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')")
@@ -60,6 +73,24 @@ def test_validator_params():
     raises(ValueError, "cfg.opt2 = 'val'")
 
 
+def test_validator_params_value_values():
+    opt1 = StrOption('opt1', '', validator=value_values, default=['val'], multi=True)
+    root = OptionDescription('root', '', [opt1])
+    cfg = Config(root)
+    assert cfg.opt1 == ['val']
+    cfg.opt1[0] = 'val1'
+    cfg.opt1.append('val2')
+
+
+def test_validator_params_value_values_index():
+    opt1 = StrOption('opt1', '', validator=value_values_index, default=['val'], multi=True)
+    root = OptionDescription('root', '', [opt1])
+    cfg = Config(root)
+    assert cfg.opt1 == ['val']
+    cfg.opt1[0] = 'val1'
+    cfg.opt1.append('val2')
+
+
 def test_validator_params_context():
     opt1 = StrOption('opt1', '', validator=is_context, validator_params={'': ((None,),)}, default='val')
     root = OptionDescription('root', '', [opt1])
index 0ddf1f9..d5a37af 100644 (file)
@@ -25,7 +25,7 @@ from .setting import undefined
 
 
 def carry_out_calculation(option, context, callback, callback_params,
-                          index=undefined):
+                          index=undefined, value_index=None):
     """a function that carries out a calculation for an option's value
 
     :param option: the option
@@ -149,6 +149,8 @@ def carry_out_calculation(option, context, callback, callback_params,
                 if callbk[0] is None:  # pragma: optional cover
                     #Not an option, set full context
                     tcparams.setdefault(key, []).append((context.duplicate(), False))
+                elif callbk[0] == 'index':
+                    tcparams.setdefault(key, []).append((value_index, False))
                 else:
                     # callbk is something link (opt, True|False)
                     opt, force_permissive = callbk
index 14d2668..1509120 100644 (file)
@@ -22,6 +22,7 @@ import re
 from types import FunctionType
 import warnings
 import sys
+from inspect import getargspec
 
 from ..i18n import _
 from ..setting import log, undefined, debug
@@ -58,7 +59,7 @@ def valid_name(name):
 
 
 def validate_callback(callback, callback_params, type_):
-    if type(callback) != FunctionType:  # pragma: optional cover
+    if not isinstance(callback, FunctionType):  # pragma: optional cover
         raise ValueError(_('{0} must be a function').format(type_))
     if callback_params is not None:
         if not isinstance(callback_params, dict):  # pragma: optional cover
@@ -70,11 +71,11 @@ def validate_callback(callback, callback_params, type_):
                                                                    key))
             if not isinstance(callbacks, tuple):  # pragma: optional cover
                 raise ValueError(_('{0}_params must be tuple for key "{1}"'
-                                   ).format(type_, key))
+                                  ).format(type_, key))
             for callbk in callbacks:
                 if isinstance(callbk, tuple):
                     if len(callbk) == 1:
-                        if callbk != (None,):  # pragma: optional cover
+                        if callbk not in  ((None,), ('index',)):  # pragma: optional cover
                             raise ValueError(_('{0}_params with length of '
                                                'tuple as 1 must only have '
                                                'None as first value'))
@@ -85,14 +86,14 @@ def validate_callback(callback, callback_params, type_):
                         option, force_permissive = callbk
                         if not isinstance(option, Option) and not \
                                 isinstance(option, SymLinkOption):  # pragma: optional cover
-                            raise ValueError(_('{0}_params must have an option'
-                                               ' not a {0} for first argument'
-                                               ).format(type_, type(option)))
+                            raise ValueError(_('{}_params must have an option'
+                                               ' not a {} for first argument'
+                                              ).format(type_, type(option)))
                         if force_permissive not in [True, False]:  # pragma: optional cover
-                            raise ValueError(_('{0}_params must have a boolean'
-                                               ' not a {0} for second argument'
-                                               ).format(type_, type(
-                                                   force_permissive)))
+                            raise ValueError(_('{}_params must have a boolean'
+                                               ' not a {} for second argument'
+                                              ).format(type_, type(
+                                                  force_permissive)))
 #____________________________________________________________
 #
 
@@ -123,7 +124,7 @@ class Base(StorageBase):
             raise ValueError(_('invalid multi value'))
         if unique != undefined and not isinstance(unique, bool):
             raise ValueError(_('unique must be a boolean'))
-        if not is_multi and unique == True:
+        if not is_multi and unique is True:
             raise ValueError(_('unique must be set only with multi value'))
         if requires is not None:
             calc_properties, requires = validate_requires_arg(is_multi,
@@ -135,10 +136,21 @@ class Base(StorageBase):
             properties = tuple()
         if not isinstance(properties, tuple):  # pragma: optional cover
             raise TypeError(_('invalid properties type {0} for {1},'
-                            ' must be a tuple').format(
-                                type(properties),
-                                name))
+                              ' must be a tuple').format(
+                                  type(properties),
+                                  name))
         if validator is not None:
+            if validator_params is None:
+                func_args = getargspec(validator)
+                defaults = func_args.defaults
+                if defaults is None:
+                    defaults = []
+                args = func_args.args[0:len(func_args.args)-len(defaults)]
+                if len(args) == 2:
+                    validator_params = {'': ((self, False),)}
+                elif len(args) == 3:
+                    validator_params = {'': ((self, False), ('index',))}
+
             validate_callback(validator, validator_params, 'validator')
             self._set_validator(validator, validator_params)
             self._set_has_dependency()
@@ -513,7 +525,7 @@ class Option(OnlyOption):
                         return ValueError(_('invalid value "{}", this value is already in "{}"').format(
                                             val, self.impl_get_display_name()))
 
-        def calculation_validator(val):
+        def calculation_validator(val, _index):
             validator, validator_params = self.impl_get_validator()
             if validator is not None:
                 if validator_params != {}:
@@ -532,7 +544,8 @@ class Option(OnlyOption):
                 # Raise ValueError if not valid
                 value = carry_out_calculation(current_opt, context=context,
                                               callback=validator,
-                                              callback_params=validator_params_)
+                                              callback_params=validator_params_,
+                                              value_index=_index)
                 if isinstance(value, Exception):
                     return value
 
@@ -562,7 +575,7 @@ class Option(OnlyOption):
                 error = None
                 if ((display_error and not self._is_warnings_only()) or
                         (display_warnings and self._is_warnings_only())):
-                    error = calculation_validator(_value)
+                    error = calculation_validator(_value, _index)
                     if not error:
                         error = self._second_level_validation(_value, self._is_warnings_only())
                     if error:
index 26d5af2..2d55fbc 100644 (file)
@@ -617,7 +617,7 @@ class Settings(object):
           exception :exc:`~error.RequirementError` is raised.
 
         And at last, if no target option matches the expected values, the
-        action must be removed from the option's properties list.
+        action will not add to the option's properties list.
 
         :param opt: the option on wich the requirement occurs
         :type opt: `option.Option()`