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>
* 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

View 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])

View 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

View 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:

View 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()`