diff --git a/ChangeLog b/ChangeLog index 065b37d..f4862b1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ Thu Jan 12 19:49:41 2017 +0200 Emmanuel Garette * 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 * copy the context in carry_out_calculation diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 27d060f..5e58fb7 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -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]) diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 0ddf1f9..d5a37af 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -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 diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 14d2668..1509120 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -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: diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 26d5af2..2d55fbc 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -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()`