validator's function can have 1 arg, 2 args or 3 args

This commit is contained in:
Emmanuel Garette 2017-01-12 21:58:53 +01:00
parent 01b7fc873e
commit 89fd367b20
5 changed files with 70 additions and 19 deletions

View file

@ -1,5 +1,10 @@
Thu Jan 12 19:49:41 2017 +0200 Emmanuel Garette <egarette@cadoles.com> Thu Jan 12 19:49:41 2017 +0200 Emmanuel Garette <egarette@cadoles.com>
* can mix inversed and non inversed requires * 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> Wed Jan 11 22:56:30 2017 +0200 Emmanuel Garette <egarette@cadoles.com>
* copy the context in carry_out_calculation * copy the context in carry_out_calculation

View file

@ -40,6 +40,19 @@ def is_context(value, context):
raise ValueError('not 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(): def test_validator():
opt1 = StrOption('opt1', '', validator=return_true, default='val') opt1 = StrOption('opt1', '', validator=return_true, default='val')
raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')") raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')")
@ -60,6 +73,24 @@ def test_validator_params():
raises(ValueError, "cfg.opt2 = 'val'") 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(): def test_validator_params_context():
opt1 = StrOption('opt1', '', validator=is_context, validator_params={'': ((None,),)}, default='val') opt1 = StrOption('opt1', '', validator=is_context, validator_params={'': ((None,),)}, default='val')
root = OptionDescription('root', '', [opt1]) root = OptionDescription('root', '', [opt1])

View file

@ -25,7 +25,7 @@ from .setting import undefined
def carry_out_calculation(option, context, callback, callback_params, 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 """a function that carries out a calculation for an option's value
:param option: the option :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 if callbk[0] is None: # pragma: optional cover
#Not an option, set full context #Not an option, set full context
tcparams.setdefault(key, []).append((context.duplicate(), False)) tcparams.setdefault(key, []).append((context.duplicate(), False))
elif callbk[0] == 'index':
tcparams.setdefault(key, []).append((value_index, False))
else: else:
# callbk is something link (opt, True|False) # callbk is something link (opt, True|False)
opt, force_permissive = callbk opt, force_permissive = callbk

View file

@ -22,6 +22,7 @@ import re
from types import FunctionType from types import FunctionType
import warnings import warnings
import sys import sys
from inspect import getargspec
from ..i18n import _ from ..i18n import _
from ..setting import log, undefined, debug from ..setting import log, undefined, debug
@ -58,7 +59,7 @@ def valid_name(name):
def validate_callback(callback, callback_params, type_): 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_)) raise ValueError(_('{0} must be a function').format(type_))
if callback_params is not None: if callback_params is not None:
if not isinstance(callback_params, dict): # pragma: optional cover if not isinstance(callback_params, dict): # pragma: optional cover
@ -74,7 +75,7 @@ def validate_callback(callback, callback_params, type_):
for callbk in callbacks: for callbk in callbacks:
if isinstance(callbk, tuple): if isinstance(callbk, tuple):
if len(callbk) == 1: 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 ' raise ValueError(_('{0}_params with length of '
'tuple as 1 must only have ' 'tuple as 1 must only have '
'None as first value')) 'None as first value'))
@ -85,12 +86,12 @@ def validate_callback(callback, callback_params, type_):
option, force_permissive = callbk option, force_permissive = callbk
if not isinstance(option, Option) and not \ if not isinstance(option, Option) and not \
isinstance(option, SymLinkOption): # pragma: optional cover isinstance(option, SymLinkOption): # pragma: optional cover
raise ValueError(_('{0}_params must have an option' raise ValueError(_('{}_params must have an option'
' not a {0} for first argument' ' not a {} for first argument'
).format(type_, type(option))) ).format(type_, type(option)))
if force_permissive not in [True, False]: # pragma: optional cover if force_permissive not in [True, False]: # pragma: optional cover
raise ValueError(_('{0}_params must have a boolean' raise ValueError(_('{}_params must have a boolean'
' not a {0} for second argument' ' not a {} for second argument'
).format(type_, type( ).format(type_, type(
force_permissive))) force_permissive)))
#____________________________________________________________ #____________________________________________________________
@ -123,7 +124,7 @@ class Base(StorageBase):
raise ValueError(_('invalid multi value')) raise ValueError(_('invalid multi value'))
if unique != undefined and not isinstance(unique, bool): if unique != undefined and not isinstance(unique, bool):
raise ValueError(_('unique must be a boolean')) 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')) raise ValueError(_('unique must be set only with multi value'))
if requires is not None: if requires is not None:
calc_properties, requires = validate_requires_arg(is_multi, calc_properties, requires = validate_requires_arg(is_multi,
@ -139,6 +140,17 @@ class Base(StorageBase):
type(properties), type(properties),
name)) name))
if validator is not None: 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') validate_callback(validator, validator_params, 'validator')
self._set_validator(validator, validator_params) self._set_validator(validator, validator_params)
self._set_has_dependency() self._set_has_dependency()
@ -513,7 +525,7 @@ class Option(OnlyOption):
return ValueError(_('invalid value "{}", this value is already in "{}"').format( return ValueError(_('invalid value "{}", this value is already in "{}"').format(
val, self.impl_get_display_name())) val, self.impl_get_display_name()))
def calculation_validator(val): def calculation_validator(val, _index):
validator, validator_params = self.impl_get_validator() validator, validator_params = self.impl_get_validator()
if validator is not None: if validator is not None:
if validator_params != {}: if validator_params != {}:
@ -532,7 +544,8 @@ class Option(OnlyOption):
# Raise ValueError if not valid # Raise ValueError if not valid
value = carry_out_calculation(current_opt, context=context, value = carry_out_calculation(current_opt, context=context,
callback=validator, callback=validator,
callback_params=validator_params_) callback_params=validator_params_,
value_index=_index)
if isinstance(value, Exception): if isinstance(value, Exception):
return value return value
@ -562,7 +575,7 @@ class Option(OnlyOption):
error = None error = None
if ((display_error and not self._is_warnings_only()) or if ((display_error and not self._is_warnings_only()) or
(display_warnings and self._is_warnings_only())): (display_warnings and self._is_warnings_only())):
error = calculation_validator(_value) error = calculation_validator(_value, _index)
if not error: if not error:
error = self._second_level_validation(_value, self._is_warnings_only()) error = self._second_level_validation(_value, self._is_warnings_only())
if error: if error:

View file

@ -617,7 +617,7 @@ class Settings(object):
exception :exc:`~error.RequirementError` is raised. exception :exc:`~error.RequirementError` is raised.
And at last, if no target option matches the expected values, the 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 :param opt: the option on wich the requirement occurs
:type opt: `option.Option()` :type opt: `option.Option()`