diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 17f685c..8ca8687 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -4,7 +4,7 @@ from py.test import raises from tiramisu.setting import groups from tiramisu.config import Config from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ - StrOption, OptionDescription + StrOption, OptionDescription, SymLinkOption from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError, ConfigError @@ -12,11 +12,19 @@ def return_val(): return 'val' -def return_list(): +def return_concat(*args): + return '.'.join(list(args)) + + +def return_list(value=None): return ['val', 'val'] -def return_value(value): +def return_list2(*args): + return list(args) + + +def return_value(value=None): return value @@ -298,18 +306,73 @@ def test_callback(): def test_callback_value(): val1 = StrOption('val1', "", 'val') - val2 = StrOption('val2', "", callback=return_value, callback_params={'': (('val1', False),)}) - maconfig = OptionDescription('rootconfig', '', [val1, val2]) + val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)}) + val3 = StrOption('val3', "", callback=return_value, callback_params={'': ('yes',)}) + val4 = StrOption('val4', "", callback=return_value, callback_params={'value': ((val1, False),)}) + val5 = StrOption('val5', "", callback=return_value, callback_params={'value': ('yes',)}) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5]) cfg = Config(maconfig) cfg.read_write() assert cfg.val1 == 'val' assert cfg.val2 == 'val' + assert cfg.val4 == 'val' cfg.val1 = 'new-val' assert cfg.val1 == 'new-val' assert cfg.val2 == 'new-val' + assert cfg.val4 == 'new-val' del(cfg.val1) assert cfg.val1 == 'val' assert cfg.val2 == 'val' + assert cfg.val3 == 'yes' + assert cfg.val4 == 'val' + assert cfg.val5 == 'yes' + + +def test_callback_value_tuple(): + val1 = StrOption('val1', "", 'val1') + val2 = StrOption('val2', "", 'val2') + val3 = StrOption('val3', "", callback=return_concat, callback_params={'': ((val1, False), (val2, False))}) + val4 = StrOption('val4', "", callback=return_concat, callback_params={'': ('yes', 'no')}) + raises(ValueError, "StrOption('val4', '', callback=return_concat, callback_params={'value': ('yes', 'no')})") + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == 'val1' + assert cfg.val2 == 'val2' + assert cfg.val3 == 'val1.val2' + assert cfg.val4 == 'yes.no' + cfg.val1 = 'new-val' + assert cfg.val3 == 'new-val.val2' + del(cfg.val1) + assert cfg.val3 == 'val1.val2' + + +def test_callback_value_force_permissive(): + val1 = StrOption('val1', "", 'val', properties=('disabled',)) + val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)}) + val3 = StrOption('val3', "", callback=return_value, callback_params={'': ((val1, True),)}) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3]) + cfg = Config(maconfig) + cfg.read_only() + raises(ConfigError, "cfg.val2") + assert cfg.val3 is None + + +def test_callback_symlink(): + val1 = StrOption('val1', "", 'val') + val2 = SymLinkOption('val2', val1) + val3 = StrOption('val3', "", callback=return_value, callback_params={'': ((val2, False),)}) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == 'val' + assert cfg.val3 == 'val' + cfg.val1 = 'new-val' + assert cfg.val1 == 'new-val' + assert cfg.val3 == 'new-val' + del(cfg.val1) + assert cfg.val1 == 'val' + assert cfg.val3 == 'val' def test_callback_list(): @@ -336,21 +399,28 @@ def test_callback_multi(): def test_callback_multi_value(): val1 = StrOption('val1', "", ['val'], multi=True) - val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': (('val1', False),)}) - maconfig = OptionDescription('rootconfig', '', [val1, val2]) + val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)}) + val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)}) + val4 = StrOption('val4', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), 'yes')}) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4]) cfg = Config(maconfig) cfg.read_write() assert cfg.val1 == ['val'] assert cfg.val2 == ['val'] + assert cfg.val4 == ['val', 'yes'] cfg.val1 = ['new-val'] assert cfg.val1 == ['new-val'] assert cfg.val2 == ['new-val'] + assert cfg.val4 == ['new-val', 'yes'] cfg.val1.append('new-val2') assert cfg.val1 == ['new-val', 'new-val2'] assert cfg.val2 == ['new-val', 'new-val2'] + assert cfg.val4 == ['new-val', 'yes', 'new-val2', 'yes'] del(cfg.val1) assert cfg.val1 == ['val'] assert cfg.val2 == ['val'] + assert cfg.val3 == ['yes'] + assert cfg.val4 == ['val', 'yes'] def test_callback_multi_list(): @@ -455,41 +525,67 @@ def test_callback_master_and_slaves_slave_list(): def test_callback_master_and_slaves_value(): val1 = StrOption('val1', "", multi=True) - val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': (('val1.val1', False),)}) - interface1 = OptionDescription('val1', '', [val1, val2]) + val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)}) + val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)}) + val4 = StrOption('val4', '', multi=True, default=['val10', 'val11']) + val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)}) + interface1 = OptionDescription('val1', '', [val1, val2, val3, val5]) interface1.impl_set_group_type(groups.master) - maconfig = OptionDescription('rootconfig', '', [interface1]) + maconfig = OptionDescription('rootconfig', '', [interface1, val4]) cfg = Config(maconfig) cfg.read_write() assert cfg.val1.val1 == [] assert cfg.val1.val2 == [] + assert cfg.val1.val3 == [] + assert cfg.val1.val5 == [] # cfg.val1.val1 = ['val1'] assert cfg.val1.val1 == ['val1'] assert cfg.val1.val2 == ['val1'] + assert cfg.val1.val3 == ['yes'] + assert cfg.val1.val5 == ['val10'] # cfg.val1.val1.append('val2') assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2'] + assert cfg.val1.val3 == ['yes', 'yes'] + assert cfg.val1.val5 == ['val10', 'val11'] # cfg.val1.val1 = ['val1', 'val2', 'val3'] assert cfg.val1.val1 == ['val1', 'val2', 'val3'] assert cfg.val1.val2 == ['val1', 'val2', 'val3'] + assert cfg.val1.val3 == ['yes', 'yes', 'yes'] + assert cfg.val1.val5 == ['val10', 'val11', None] # cfg.val1.val1.pop(2) assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2'] + assert cfg.val1.val3 == ['yes', 'yes'] + assert cfg.val1.val5 == ['val10', 'val11'] # cfg.val1.val2 = ['val2', 'val2'] + cfg.val1.val3 = ['val2', 'val2'] + cfg.val1.val5 = ['val2', 'val2'] assert cfg.val1.val2 == ['val2', 'val2'] + assert cfg.val1.val3 == ['val2', 'val2'] + assert cfg.val1.val5 == ['val2', 'val2'] # cfg.val1.val1.append('val3') assert cfg.val1.val2 == ['val2', 'val2', 'val3'] + assert cfg.val1.val3 == ['val2', 'val2', 'yes'] + assert cfg.val1.val5 == ['val2', 'val2', None] + cfg.cfgimpl_get_settings().remove('cache') + cfg.val4 = ['val10', 'val11', 'val12'] + #if value is already set, not updated ! + cfg.val1.val1.pop(2) + cfg.val1.val1.append('val3') + cfg.val1.val1 = ['val1', 'val2', 'val3'] + assert cfg.val1.val5 == ['val2', 'val2', 'val12'] def test_callback_hidden(): opt1 = BoolOption('opt1', '') - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}) od1 = OptionDescription('od1', '', [opt1], properties=('hidden',)) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) @@ -502,7 +598,7 @@ def test_callback_hidden(): def test_callback_two_disabled(): opt1 = BoolOption('opt1', '', properties=('disabled',)) - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('disabled',)) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('disabled',)) od1 = OptionDescription('od1', '', [opt1]) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) @@ -513,7 +609,7 @@ def test_callback_two_disabled(): def test_callback_calculating_disabled(): opt1 = BoolOption('opt1', '', properties=('disabled',)) - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}) od1 = OptionDescription('od1', '', [opt1]) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) @@ -524,7 +620,7 @@ def test_callback_calculating_disabled(): def test_callback_calculating_mandatory(): opt1 = BoolOption('opt1', '', properties=('disabled',)) - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('mandatory',)) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('mandatory',)) od1 = OptionDescription('od1', '', [opt1]) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) @@ -535,10 +631,47 @@ def test_callback_calculating_mandatory(): def test_callback_two_disabled_multi(): opt1 = BoolOption('opt1', '', properties=('disabled',)) - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('disabled',), multi=True) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('disabled',), multi=True) od1 = OptionDescription('od1', '', [opt1]) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) cfg = Config(maconfig) cfg.read_write() raises(PropertiesOptionError, 'cfg.od2.opt2') + + +def test_callback_multi_list_params(): + val1 = StrOption('val1', "", multi=True, default=['val1', 'val2']) + val2 = StrOption('val2', "", multi=True, callback=return_list, callback_params={'': ((val1, False),)}) + oval2 = OptionDescription('val2', '', [val2]) + maconfig = OptionDescription('rootconfig', '', [val1, oval2]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val2.val2 == ['val', 'val', 'val', 'val'] + + +def test_callback_multi_list_params_key(): + val1 = StrOption('val1', "", multi=True, default=['val1', 'val2']) + val2 = StrOption('val2', "", multi=True, callback=return_list, callback_params={'value': ((val1, False),)}) + oval2 = OptionDescription('val2', '', [val2]) + maconfig = OptionDescription('rootconfig', '', [val1, oval2]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val2.val2 == ['val', 'val', 'val', 'val'] + + +def test_callback_multi_multi(): + val1 = StrOption('val1', "", multi=True, default=['val1', 'val2', 'val3']) + val2 = StrOption('val2', "", multi=True, default=['val11', 'val12']) + val3 = StrOption('val3', "", default='val4') + val4 = StrOption('val4', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val2, False))}) + val5 = StrOption('val5', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val3, False))}) + val6 = StrOption('val6', "", multi=True, default=['val21', 'val22', 'val23']) + val7 = StrOption('val7', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val6, False))}) + raises(ValueError, "StrOption('val8', '', multi=True, callback=return_list2, callback_params={'value': ((val1, False), (val6, False))})") + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5, val6, val7]) + cfg = Config(maconfig) + cfg.read_write() + raises(ConfigError, "cfg.val4") + assert cfg.val5 == ['val1', 'val4', 'val2', 'val4', 'val3', 'val4'] + assert cfg.val7 == ['val1', 'val21', 'val2', 'val22', 'val3', 'val23'] diff --git a/test/test_option_validator.py b/test/test_option_validator.py new file mode 100644 index 0000000..8e00916 --- /dev/null +++ b/test/test_option_validator.py @@ -0,0 +1,59 @@ +import autopath +from py.test import raises + +from tiramisu.config import Config +from tiramisu.option import StrOption, OptionDescription +from tiramisu.error import ConfigError + + +def return_true(value, param=None): + if value == 'val' and param in [None, 'yes']: + return True + + +def return_false(value, param=None): + if value == 'val' and param in [None, 'yes']: + return False + + +def return_val(value, param=None): + return 'val' + + +def test_validator(): + opt1 = StrOption('opt1', '', validator=return_true, default='val') + raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')") + raises(ConfigError, "StrOption('opt3', '', validator=return_val, default='val')") + opt2 = StrOption('opt2', '', validator=return_false) + opt3 = StrOption('opt3', '', validator=return_val) + root = OptionDescription('root', '', [opt1, opt2, opt3]) + cfg = Config(root) + assert cfg.opt1 == 'val' + raises(ValueError, "cfg.opt2 = 'val'") + raises(ConfigError, "cfg.opt3 = 'val'") + + +def test_validator_params(): + opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ('yes',)}, default='val') + raises(ValueError, "StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}, default='val')") + raises(ConfigError, "StrOption('opt3', '', validator=return_val, validator_params={'': ('yes',)}, default='val')") + opt2 = StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}) + opt3 = StrOption('opt3', '', validator=return_val, validator_params={'': ('yes',)}) + root = OptionDescription('root', '', [opt1, opt2, opt3]) + cfg = Config(root) + assert cfg.opt1 == 'val' + raises(ValueError, "cfg.opt2 = 'val'") + raises(ConfigError, "cfg.opt3 = 'val'") + + +def test_validator_params_key(): + opt1 = StrOption('opt1', '', validator=return_true, validator_params={'param': ('yes',)}, default='val') + raises(TypeError, "StrOption('opt2', '', validator=return_true, validator_params={'param_unknown': ('yes',)}, default='val')") + root = OptionDescription('root', '', [opt1]) + cfg = Config(root) + assert cfg.opt1 == 'val' + + +def test_validator_params_option(): + opt0 = StrOption('opt0', '', default='val') + raises(ValueError, "opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ((opt0, False),)}, default='val')") diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 2cf41ca..de8a8c5 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -23,11 +23,9 @@ from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.i18n import _ # ____________________________________________________________ -def carry_out_calculation(name, - config, - callback, - callback_params, - index=None): + +def carry_out_calculation(name, config, callback, callback_params, + index=None, max_len=None): """a function that carries out a calculation for an option's value :param name: the option name (`opt._name`) @@ -40,36 +38,104 @@ def carry_out_calculation(name, :type callback_params: dict :param index: if an option is multi, only calculates the nth value :type index: int + :param max_len: max length for a multi + :type max_len: int + + * if no callback_params: + => calculate() + + * if callback_params={'': ('yes',)} + => calculate('yes') + + * if callback_params={'value': ('yes',)} + => calculate(value='yes') + + * if callback_params={'': ('yes', 'no')} + => calculate('yes', 'no') + + * if callback_params={'value': ('yes', 'no')} + => ValueError() + + * if callback_params={'': ((opt1, False),)} + + - a simple option: + opt1 == 11 + => calculate(11) + + - a multi option: + opt1 == [1, 2, 3] + => calculate(1) + => calculate(2) + => calculate(3) + + * if callback_params={'value': ((opt1, False),)} + + - a simple option: + opt1 == 11 + => calculate(value=11) + + - a multi option: + opt1 == [1, 2, 3] + => calculate(value=1) + => calculate(value=2) + => calculate(value=3) + + * if callback_params={'': ((opt1, False), (opt2, False))} + + - a multi option with a simple option + opt1 == [1, 2, 3] + opt2 == 11 + => calculate(1, 11) + => calculate(2, 11) + => calculate(3, 11) + + - a multi option with an other multi option but with same length + opt1 == [1, 2, 3] + opt2 == [11, 12, 13] + callback_params={'': ((opt1, False), (opt2, False))} + => calculate(1, 11) + => calculate(2, 12) + => calculate(3, 13) + + - a multi option with an other multi option but with different length + opt1 == [1, 2, 3] + opt2 == [11, 12] + callback_params={'': ((opt1, False), (opt2, False))} + => ConfigError() + + * if callback_params={'value': ((opt1, False), (opt2, False))} + => ConfigError() + + If index is not None, return a value, otherwise return: + + * a list if one parameters have multi option + * a value otherwise + + If calculate return list, this list is extend to return value. """ - #callback, callback_params = option.getcallback() - #if callback_params is None: - # callback_params = {} tcparams = {} one_is_multi = False len_multi = 0 - for key, values in callback_params.items(): - for value in values: - if type(value) == tuple: - path, check_disabled = value - if config is None: - if check_disabled: - continue - raise ConfigError(_('no config specified but needed')) + for key, callbacks in callback_params.items(): + for callbk in callbacks: + if isinstance(callbk, tuple): + option, force_permissive = callbk + # get value try: - opt_value = config._getattr(path, force_permissive=True) - opt = config.unwrap_from_path(path, force_permissive=True) + path = config.cfgimpl_get_description().impl_get_path_by_opt(option) + value = config._getattr(path, force_permissive=True) except PropertiesOptionError as err: - if check_disabled: + if force_permissive: continue raise ConfigError(_('unable to carry out a calculation, ' 'option {0} has properties: {1} for: ' - '{2}').format(path, err.proptype, + '{2}').format(option._name, err.proptype, name)) - is_multi = opt.impl_is_multi() + is_multi = option.impl_is_multi() if is_multi: - if opt_value is not None: - len_value = len(opt_value) + if value is not None: + len_value = len(value) if len_multi != 0 and len_multi != len_value: raise ConfigError(_('unable to carry out a ' 'calculation, option value with' @@ -77,16 +143,23 @@ def carry_out_calculation(name, 'length for: {0}').format(name)) len_multi = len_value one_is_multi = True - tcparams.setdefault(key, []).append((opt_value, is_multi)) + tcparams.setdefault(key, []).append((value, is_multi)) else: - tcparams.setdefault(key, []).append((value, False)) + tcparams.setdefault(key, []).append((callbk, False)) if one_is_multi: ret = [] if index: - range_ = [index] + if index < len_multi: + range_ = [index] + else: + range_ = [] + ret = None else: - range_ = range(len_multi) + if max_len and max_len < len_multi: + range_ = range(max_len) + else: + range_ = range(len_multi) for incr in range_: tcp = {} params = [] @@ -97,15 +170,9 @@ def carry_out_calculation(name, if key == '': params.append(value[incr]) else: - if len(value) > incr: - tcp[key] = value[incr] - else: - tcp[key] = '' + tcp[key] = value[incr] else: - if key == '': - params.append(value) - else: - tcp[key] = value + params.append(value) calc = calculate(name, callback, params, tcp) if index: ret = calc @@ -114,7 +181,6 @@ def carry_out_calculation(name, ret.extend(calc) else: ret.append(calc) - return ret else: tcp = {} @@ -130,7 +196,6 @@ def carry_out_calculation(name, def calculate(name, callback, params, tcparams): - # FIXME we don't need the option's name down there. """wrapper that launches the 'callback' :param callback: callback name diff --git a/tiramisu/option.py b/tiramisu/option.py index 0058866..1948f39 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -26,7 +26,7 @@ from copy import copy, deepcopy from types import FunctionType from IPy import IP -from tiramisu.error import ConflictError +from tiramisu.error import ConflictError, ConfigError from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -97,8 +97,8 @@ class BaseOption(object): "frozen" (which has noting to do with the high level "freeze" propertie or "read_only" property) """ - if not name.startswith('_state') and \ - name not in ('_cache_paths', '_consistencies'): + if not name.startswith('_state') and name not in ('_cache_paths', + '_consistencies'): is_readonly = False # never change _name if name == '_name': @@ -327,7 +327,7 @@ class Option(BaseOption): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_args=None, + callback_params=None, validator=None, validator_params=None, properties=None): """ :param name: the option's name @@ -344,18 +344,15 @@ class Option(BaseOption): :param callback_params: the callback's parameter :param validator: the name of a function which stands for a custom validation of the value - :param validator_args: the validator's parameters + :param validator_params: the validator's parameters :param properties: tuple of default properties """ super(Option, self).__init__(name, doc, requires, properties) self._multi = multi if validator is not None: - if type(validator) != FunctionType: - raise TypeError(_("validator must be a function")) - if validator_args is None: - validator_args = {} - self._validator = (validator, validator_args) + validate_callback(validator, validator_params, 'validator') + self._validator = (validator, validator_params) else: self._validator = None if not self._multi and default_multi is not None: @@ -377,11 +374,7 @@ class Option(BaseOption): "no callback defined" " yet for option {0}").format(name)) if callback is not None: - if type(callback) != FunctionType: - raise ValueError('callback must be a function') - if callback_params is not None and \ - not isinstance(callback_params, dict): - raise ValueError('callback_params must be a dict') + validate_callback(callback, callback_params, 'callback') self._callback = (callback, callback_params) else: self._callback = None @@ -448,11 +441,23 @@ class Option(BaseOption): def val_validator(val): if self._validator is not None: - callback_params = deepcopy(self._validator[1]) - callback_params.setdefault('', []).insert(0, val) - return carry_out_calculation(self._name, config=context, - callback=self._validator[0], - callback_params=callback_params) + if self._validator[1] is not None: + validator_params = deepcopy(self._validator[1]) + if '' in validator_params: + lst = list(validator_params['']) + lst.insert(0, val) + validator_params[''] = tuple(lst) + else: + validator_params[''] = (val,) + else: + validator_params = {'': (val,)} + ret = carry_out_calculation(self._name, config=context, + callback=self._validator[0], + callback_params=validator_params) + if ret not in [False, True]: + raise ConfigError(_('validator should return a boolean, ' + 'not {0}').format(ret)) + return ret else: return True @@ -566,7 +571,7 @@ class ChoiceOption(Option): def __init__(self, name, doc, values, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, open_values=False, validator=None, - validator_args=None, properties=()): + validator_params=None, properties=()): """ :param values: is a list of values the option can possibly take """ @@ -584,7 +589,7 @@ class ChoiceOption(Option): requires=requires, multi=multi, validator=validator, - validator_args=validator_args, + validator_params=validator_params, properties=properties) def impl_get_values(self): @@ -702,7 +707,7 @@ class IPOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_args=None, + callback_params=None, validator=None, validator_params=None, properties=None, only_private=False): self._only_private = only_private super(IPOption, self).__init__(name, doc, default=default, @@ -712,7 +717,7 @@ class IPOption(Option): requires=requires, multi=multi, validator=validator, - validator_args=validator_args, + validator_params=validator_params, properties=properties) def _validate(self, value): @@ -738,7 +743,7 @@ class PortOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_args=None, + callback_params=None, validator=None, validator_params=None, properties=None, allow_range=False, allow_zero=False, allow_wellknown=True, allow_registred=True, allow_private=False): @@ -772,7 +777,7 @@ class PortOption(Option): requires=requires, multi=multi, validator=validator, - validator_args=validator_args, + validator_params=validator_params, properties=properties) def _validate(self, value): @@ -857,7 +862,7 @@ class DomainnameOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_args=None, + callback_params=None, validator=None, validator_params=None, properties=None, allow_ip=False, type_='domainname'): #netbios: for MS domain #hostname: to identify the device @@ -876,7 +881,7 @@ class DomainnameOption(Option): requires=requires, multi=multi, validator=validator, - validator_args=validator_args, + validator_params=validator_params, properties=properties) def _validate(self, value): @@ -1252,3 +1257,33 @@ def validate_requires_arg(requires, name): require[3], require[4], require[5])) ret.append(tuple(ret_action)) return frozenset(config_action.keys()), tuple(ret) + + +def validate_callback(callback, callback_params, type_): + if type(callback) != FunctionType: + raise ValueError(_('{0} should be a function').format(type_)) + if callback_params is not None: + if not isinstance(callback_params, dict): + raise ValueError(_('{0}_params should be a dict').format(type_)) + for key, callbacks in callback_params.items(): + if key != '' and len(callbacks) != 1: + raise ValueError(_('{0}_params with key {1} should not have ' + 'length different to 1').format(type_, + key)) + if not isinstance(callbacks, tuple): + raise ValueError(_('{0}_params should be tuple for key "{1}"' + ).format(type_, key)) + for callbk in callbacks: + if isinstance(callbk, tuple): + option, force_permissive = callbk + if type_ == 'validator' and not force_permissive: + raise ValueError(_('validator not support tuple')) + if not isinstance(option, Option) and not \ + isinstance(option, SymLinkOption): + raise ValueError(_('{0}_params should have an option ' + 'not a {0} for first argument' + ).format(type_, type(option))) + if force_permissive not in [True, False]: + raise ValueError(_('{0}_params should have a boolean' + 'not a {0} for second argument' + ).format(type_, type(force_permissive))) diff --git a/tiramisu/value.py b/tiramisu/value.py index 578c4ee..b600eb8 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -124,7 +124,7 @@ class Values(object): return True return False - def _getcallback_value(self, opt, index=None): + def _getcallback_value(self, opt, index=None, max_len=None): """ retrieves a value for the options that have a callback @@ -139,7 +139,7 @@ class Values(object): return carry_out_calculation(opt._name, config=self.context(), callback=callback, callback_params=callback_params, - index=index) + index=index, max_len=max_len) def __getitem__(self, opt): "enables us to use the pythonic dictionary-like access to values" @@ -190,6 +190,7 @@ class Values(object): if opt.impl_has_callback() and ( self._is_default_owner(path) or (is_frozen and 'force_default_on_freeze' in setting[opt])): + lenmaster = None no_value_slave = False if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave): @@ -202,7 +203,7 @@ class Values(object): if not no_value_slave: try: - value = self._getcallback_value(opt) + value = self._getcallback_value(opt, max_len=lenmaster) except ConfigError as config_error: value = None # should not raise PropertiesOptionError if option is @@ -389,20 +390,27 @@ class Multi(list): def _valid_slave(self, value): #if slave, had values until master's one + values = self.context().cfgimpl_get_values() masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt( self.opt.impl_get_master_slaves()) mastervalue = getattr(self.context(), masterp) masterlen = len(mastervalue) valuelen = len(value) if valuelen > masterlen or (valuelen < masterlen and - not self.context().cfgimpl_get_values( - )._is_default_owner(self.path)): + not values._is_default_owner(self.path)): raise SlaveError(_("invalid len for the slave: {0}" " which has {1} as master").format( self.opt._name, masterp)) elif valuelen < masterlen: for num in range(0, masterlen - valuelen): - value.append(self.opt.impl_getdefault_multi()) + if self.opt.impl_has_callback(): + # if callback add a value, but this value will not change + # anymore automaticly (because this value has owner) + index = value.__len__() + value.append(values._getcallback_value(self.opt, + index=index)) + else: + value.append(self.opt.impl_getdefault_multi()) #else: same len so do nothing return value @@ -420,8 +428,17 @@ class Multi(list): self.opt._name, slave._name)) elif len(value_slave) < masterlen: for num in range(0, masterlen - len(value_slave)): - value_slave.append(slave.impl_getdefault_multi(), - force=True) + if slave.impl_has_callback(): + # if callback add a value, but this value will not + # change anymore automaticly (because this value + # has owner) + index = value_slave.__len__() + value_slave.append( + values._getcallback_value(slave, index=index), + force=True) + else: + value_slave.append(slave.impl_getdefault_multi(), + force=True) def __setitem__(self, key, value): self._validate(value)