From 1736170e43df048f51996529f5496e9ba76dfb3f Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 27 Dec 2017 15:48:49 +0100 Subject: [PATCH] add validator tests --- test/new_api/test_dyn_optiondescription.py | 2 +- test/new_api/test_option_calculation.py | 2 +- test/new_api/test_option_username.py | 26 ++ test/new_api/test_option_validator.py | 483 +++++++++++++++++++++ tiramisu/api.py | 142 ++++-- tiramisu/autolib.py | 43 +- tiramisu/config.py | 42 +- tiramisu/option/baseoption.py | 134 +++--- tiramisu/option/option.py | 12 +- tiramisu/value.py | 2 +- 10 files changed, 734 insertions(+), 154 deletions(-) create mode 100644 test/new_api/test_option_username.py create mode 100644 test/new_api/test_option_validator.py diff --git a/test/new_api/test_dyn_optiondescription.py b/test/new_api/test_dyn_optiondescription.py index 8855fe0..541e830 100644 --- a/test/new_api/test_dyn_optiondescription.py +++ b/test/new_api/test_dyn_optiondescription.py @@ -26,7 +26,7 @@ def return_dynval(value='val', suffix=None): return value -def return_list2(suffix): +def return_list2(suffix=None): return [str(suffix), 'val2'] diff --git a/test/new_api/test_option_calculation.py b/test/new_api/test_option_calculation.py index 1676f21..453e37d 100644 --- a/test/new_api/test_option_calculation.py +++ b/test/new_api/test_option_calculation.py @@ -861,7 +861,7 @@ def test_callback_master_and_slaves_slave_callback_disabled(): api.option('val1.val1').value.set(['yes', 'yes1']) assert api.option('val1.val2', 0).value.get() == 'no' api.property.add('disabled') - raises(ValueError, "api.option('val1.val1').value.set(['yes'])") + api.option('val1.val1').value.pop(1) def test_callback_master_and_slaves_value(): diff --git a/test/new_api/test_option_username.py b/test/new_api/test_option_username.py new file mode 100644 index 0000000..d328385 --- /dev/null +++ b/test/new_api/test_option_username.py @@ -0,0 +1,26 @@ +"configuration objects global API" +from .autopath import do_autopath +do_autopath() + +from py.test import raises + +from tiramisu.option import UsernameOption + +def test_username(): + UsernameOption('a', '', 'string') + UsernameOption('a', '', '_string') + UsernameOption('a', '', 's_tring') + UsernameOption('a', '', 'string_') + UsernameOption('a', '', 'string$') + UsernameOption('a', '', '_string$') + raises(ValueError, "UsernameOption('a', '', 'strin$g')") + UsernameOption('a', '', 's-tring') + raises(ValueError, "UsernameOption('a', '', '-string')") + UsernameOption('a', '', 's9tring') + raises(ValueError, "UsernameOption('a', '', '9string')") + raises(ValueError, "UsernameOption('a', '', '')") + UsernameOption('a', '', 's') + UsernameOption('a', '', 's2345678901234567890123456789012') + raises(ValueError, "UsernameOption('a', '', 's23456789012345678901234567890123')") + UsernameOption('a', '', 's234567890123456789012345678901$') + raises(ValueError, "UsernameOption('a', '', 's2345678901234567890123456789012$')") diff --git a/test/new_api/test_option_validator.py b/test/new_api/test_option_validator.py new file mode 100644 index 0000000..a1187b6 --- /dev/null +++ b/test/new_api/test_option_validator.py @@ -0,0 +1,483 @@ +from .autopath import do_autopath +do_autopath() + +import warnings +from py.test import raises + +from tiramisu import BoolOption, StrOption, OptionDescription, MasterSlaves, Config, getapi +from tiramisu.setting import groups +from tiramisu.error import ValueWarning, ConfigError +from tiramisu.i18n import _ + + +msg_err = _('attention, "{0}" could be an invalid {1} for "{2}", {3}') + + +def return_true(value, param=None): + if value == 'val' and param in [None, 'yes']: + return True + raise ValueError('test error') + + +def return_false(value, param=None): + if value == 'val' and param in [None, 'yes']: + raise ValueError('test error') + + +def return_val(value, param=None): + return 'val' + + +def return_if_val(value): + if value != 'val': + raise ValueError('test error') + + +def is_context(value, context): + api = getapi(context) + api.property.pop('validator') + if not isinstance(context, Config): + raise ValueError('not context') + + +def value_values(value, values): + if not (value == 'val' and values == ['val'] or + value == 'val1' and values == ['val1'] or + value == 'val1' and values == ['val1', 'val2'] or + value == 'val2' and values == ['val1', 'val2'] or + value == 'val1' and values == ['val1', None]): + 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 value_values_auto(value, values, auto=False): + if auto != False: + raise ValueError('auto should be False') + if not (value == 'val' and values == ['val'] or + value == 'val1' and values == ['val1'] or + value == 'val2' and values == ['val1', 'val2'] or + value == 'val1' and values == ['val1', None]): + raise ValueError('error') + + +def value_values_auto2(value, values, auto=False): + if auto != False: + raise ValueError('auto should be False') + if not (value == 'val1' and values == 'val' or + value == 'val2' and values == 'val'): + raise ValueError('error') + + + +def value_values_index2(value, values, index, auto=False): + if auto != False: + raise ValueError('auto should be False') + if not (value == 'val1' and values == ['val1'] and index == 'val' or + value == 'val1' and values == ['val1', None] and index == 'val' or + value == 'val2' and values == ['val1', 'val2'] and index == 'val'): + raise ValueError('error') + + +def value_empty(value, empty, values): + if not value == 'val' or empty is not False and not values == ['val']: + raise ValueError('error') + + +def valid_from_config(value, config): + api = getapi(config) + if api.option('opt1').value.get() != u'yes': + raise ValueError("c'est une erreur") + + +def test_validator(): + opt1 = StrOption('opt1', '', validator=return_true, default='val') + raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')") + opt2 = StrOption('opt2', '', validator=return_false) + root = OptionDescription('root', '', [opt1, opt2]) + api = getapi(Config(root)) + assert api.option('opt1').value.get() == 'val' + raises(ValueError, "api.option('opt2').value.set('val')") + try: + api.option('opt2').value.set('val') + except ValueError as err: + msg = _('"{0}" is an invalid {1} for "{2}", {3}').format('val', _('string'), 'opt2', 'test error') + assert str(err) == msg + + +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')") + opt2 = StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}) + root = OptionDescription('root', '', [opt1, opt2]) + api = getapi(Config(root)) + assert api.option('opt1').value.get() == 'val' + raises(ValueError, "api.option('opt2').value.set('val')") + + +def test_validator_params_value_values(): + opt1 = StrOption('opt1', '', validator=value_values, default=['val'], multi=True) + root = OptionDescription('root', '', [opt1]) + api = getapi(Config(root)) + assert api.option('opt1').value.get() == ['val'] + api.option('opt1').value.set(['val1', 'val2']) + + +def test_validator_params_value_values_index(): + opt1 = StrOption('opt1', '', validator=value_values_index, default=['val'], multi=True) + root = OptionDescription('root', '', [opt1]) + api = getapi(Config(root)) + assert api.option('opt1').value.get() == ['val'] + api.option('opt1').value.set(['val1', 'val2']) + + +def test_validator_params_value_values_master(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=value_values) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2']) + + +def test_validator_params_value_values_index_master(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=value_values_index) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val2']) + + +def test_validator_params_value_values_slave(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=value_values) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1') + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val2') + + +def test_validator_params_value_values_index_slave(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=value_values_index) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1') + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val2') + + +def test_validator_params_value_values_notmulti(): + raises(ConfigError, "opt1 = StrOption('opt1', '', validator=value_values, default='val')") + + +def test_validator_params_value_values_kwargs_empty(): + v = BoolOption('v', '', default=False) + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, default=["ip"]) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', + "masque du sous-reseau", + multi=True, + validator=value_empty, + validator_params={'': ((v, False),)}) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [v, interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['ip'] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['ip', 'val']) + #cfg.ip_admin_eth0.ip_admin_eth0.append('val') + #cfg.ip_admin_eth0.netmask_admin_eth0[1] = 'val2' + + +def test_validator_params_value_values_kwargs(): + v = BoolOption('v', '', default=False) + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, default=["ip"]) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', + "masque du sous-reseau", + multi=True, + validator=value_values_auto, + validator_params={'auto': ((v, False),)}) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [v, interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == ['ip'] + api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1') + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['ip', 'val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val2') + + +def test_validator_params_value_values_kwargs_values(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', + "masque du sous-reseau", + multi=True, + validator=value_values_auto2, + validator_params={'values': ((ip_admin_eth0, False),)}) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1') + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val2') + + +def test_validator_params_value_values_kwargs2(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', + "masque du sous-reseau", + multi=True, + validator=value_values_index2, + validator_params={'': (['val1'],), 'index': ((ip_admin_eth0, False),)}) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1') + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val']) + + +def test_validator_params_value_values_kwargs_index(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', + "masque du sous-reseau", + multi=True, + validator=value_values_index2, + validator_params={'index': ((ip_admin_eth0, False),)}) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1') + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val']) + api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val2') + + +def test_validator_params_context(): + opt1 = StrOption('opt1', '', validator=is_context, validator_params={'': ((None,),)}, default='val') + root = OptionDescription('root', '', [opt1]) + api = getapi(Config(root)) + assert 'validator' in api.property.get() + assert api.option('opt1').value.get() == 'val' + assert 'validator' in api.property.get() + + +def test_validator_params_context_value(): + opt1 = StrOption('opt1', '', 'yes') + opt2 = StrOption('opt2', '', validator=valid_from_config, validator_params={'': ((None,),)}, default='val') + root = OptionDescription('root', '', [opt1, opt2]) + api = getapi(Config(root)) + assert api.option('opt1').value.get() == 'yes' + assert api.option('opt2').value.get() == 'val' + api.option('opt1').value.set('no') + raises(ValueError, "assert api.option('opt2').value.get()") + + +def test_validator_params_key(): + opt1 = StrOption('opt1', '', validator=return_true, validator_params={'param': ('yes',)}, default='val') + raises(ConfigError, "StrOption('opt2', '', validator=return_true, validator_params={'param_unknown': ('yes',)}, default='val')") + root = OptionDescription('root', '', [opt1]) + api = getapi(Config(root)) + assert api.option('opt1').value.get() == 'val' + + +def test_validator_params_option(): + opt0 = StrOption('opt0', '', default='yes') + opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ((opt0, False),)}, default='val') + r = OptionDescription('root', '', [opt0, opt1]) + api = getapi(Config(r)) + assert api.option('opt1').value.get() == 'val' + api.option('opt0').value.set('val') + raises(ValueError, "api.option('opt1').value.get()") + + +def test_validator_multi(): + opt1 = StrOption('opt1', '', validator=return_if_val, multi=True) + root = OptionDescription('root', '', [opt1]) + api = getapi(Config(root)) + assert api.option('opt1').value.get() == [] + api.option('opt1').value.set(['val']) + assert api.option('opt1').value.get() == ['val'] + raises(ValueError, "api.option('opt1').value.set(['val', 'val1'])") + + +def test_validator_warning(): + opt1 = StrOption('opt1', '', validator=return_true, default='val', warnings_only=True) + opt2 = StrOption('opt2', '', validator=return_false, warnings_only=True) + opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, warnings_only=True) + root = OptionDescription('root', '', [opt1, opt2, opt3]) + api = getapi(Config(root)) + assert api.option('opt1').value.get() == 'val' + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + api.option('opt1').value.set('val') + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + api.option('opt2').value.set('val') + assert len(w) == 1 + assert w[0].message.opt == opt2 + assert str(w[0].message) == msg_err.format('val', opt2._display_name, 'opt2', 'test error') + # + with warnings.catch_warnings(record=True) as w: + api.option('opt3').value.set(['val']) + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + api.option('opt3').value.set(['val', 'val1']) + assert len(w) == 1 + assert w[0].message.opt == opt3 + assert str(w[0].message) == msg_err.format('val1', opt3._display_name, 'opt3', 'test error') + raises(ValueError, "api.option('opt2').value.set(1)") + # + with warnings.catch_warnings(record=True) as w: + api.option('opt2').value.set('val') + api.option('opt3').value.set(['val', 'val1', 'val']) + assert len(w) == 2 + assert w[0].message.opt == opt2 + assert str(w[0].message) == msg_err.format('val', opt2._display_name, 'opt2', 'test error') + assert w[1].message.opt == opt3 + assert str(w[0].message) == msg_err.format('val', opt2._display_name, 'opt2', 'test error') + + +def test_validator_warning_disabled(): + opt1 = StrOption('opt1', '', validator=return_true, default='val', warnings_only=True) + opt2 = StrOption('opt2', '', validator=return_false, warnings_only=True) + opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, warnings_only=True) + root = OptionDescription('root', '', [opt1, opt2, opt3]) + api = getapi(Config(root)) + api.property.pop('warnings') + assert api.option('opt1').value.get() == 'val' + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + api.option('opt1').value.set('val') + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + api.option('opt2').value.set('val') + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + api.option('opt3').value.set(['val']) + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + api.option('opt3').value.set(['val', 'val1']) + assert w == [] + raises(ValueError, "api.option('opt2').value.set(1)") + # + with warnings.catch_warnings(record=True) as w: + api.option('opt2').value.set('val') + api.option('opt3').value.set(['val', 'val1', 'val']) + assert w == [] + + +def test_validator_warning_master_slave(): + display_name_ip = "ip reseau autorise" + display_name_netmask = "masque du sous-reseau" + ip_admin_eth0 = StrOption('ip_admin_eth0', display_name_ip, multi=True, validator=return_false, warnings_only=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', display_name_netmask, multi=True, validator=return_if_val, warnings_only=True) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + assert interface1.impl_get_group_type() == groups.master + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + api.option('ip_admin_eth0.ip_admin_eth0').value.set([None]) + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val1') + assert len(w) == 1 + assert w[0].message.opt == netmask_admin_eth0 + assert str(w[0].message) == msg_err.format('val1', netmask_admin_eth0._display_name, display_name_netmask, 'test error') + # + with warnings.catch_warnings(record=True) as w: + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val']) + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == msg_err.format('val', ip_admin_eth0._display_name, display_name_ip, 'test error') + # + with warnings.catch_warnings(record=True) as w: + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val', 'val1', 'val1']) + #FIXME + #assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == msg_err.format('val', ip_admin_eth0._display_name, display_name_ip, 'test error') + # + with warnings.catch_warnings(record=True) as w: + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val', 'val1']) + #FIXME + #assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == msg_err.format('val', ip_admin_eth0._display_name, display_name_ip, 'test error') + # + warnings.resetwarnings() + with warnings.catch_warnings(record=True) as w: + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['val1', 'val1', 'val']) + #FIXME + #assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == msg_err.format('val', ip_admin_eth0._display_name, display_name_ip, 'test error') + + +def test_validator_slave_param(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', + "masque du sous-reseau", + multi=True, + validator=return_true, + validator_params={'param': ((ip_admin_eth0, False),)}) + interface1 = MasterSlaves('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + #interface1.impl_set_group_type(groups.master) + root = OptionDescription('root', '', [interface1]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0.ip_admin_eth0').value.get() == [] + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['yes']) + api.option('ip_admin_eth0.netmask_admin_eth0', 0).value.set('val') + api.option('ip_admin_eth0.ip_admin_eth0').value.set(['yes', 'yes']) + api.option('ip_admin_eth0.netmask_admin_eth0', 1).value.set('val') + + +def test_validator_dependencies(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise") + netmask_admin_eth0 = StrOption('netmask_admin_eth0', + "masque du sous-reseau", + validator=return_true, + validator_params={'param': ((ip_admin_eth0, False),)}) + opt2 = StrOption('opt2', '', validator=return_false) + root = OptionDescription('root', '', [ip_admin_eth0, netmask_admin_eth0, opt2]) + api = getapi(Config(root)) + assert api.option('ip_admin_eth0').option.has_dependency() is False + assert api.option('netmask_admin_eth0').option.has_dependency() is True + assert api.option('opt2').option.has_dependency() is False + # + assert api.option('ip_admin_eth0').option.has_dependency(False) is True + assert api.option('netmask_admin_eth0').option.has_dependency(False) is False + assert api.option('opt2').option.has_dependency(False) is False diff --git a/tiramisu/api.py b/tiramisu/api.py index 2862a2e..8045fe9 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -88,23 +88,25 @@ class CommonTiramisuOption(object): slave_need_index = True def __init__(self, + name, path, index, + subconfig, config_bag): - opt = config_bag.option - if not self.allow_optiondescription and opt.impl_is_optiondescription(): - raise APIError(_('option must not be an optiondescription')) self.path = path self.index = index self.config_bag = config_bag + self.name = name + self.subconfig = subconfig if self.slave_need_index: self._test_slave_index() if not self.allow_unrestraint: self._unrestraint_not_allowed(self.config_bag.force_unrestraint) def _test_slave_index(self): - if not self.config_bag.option.impl_is_optiondescription() and self.index is None and \ - self.config_bag.option.impl_is_master_slaves('slave'): + option = self.get_option() + if not option.impl_is_optiondescription() and self.index is None and \ + option.impl_is_master_slaves('slave'): raise APIError('index must be set with a slave option') def _unrestraint_not_allowed(self, force_unrestraint): @@ -121,6 +123,17 @@ class CommonTiramisuOption(object): else: super(CommonTiramisuOption, self).__getattribute__(name) + def get_option(self): + option = self.config_bag.option + if option is None: + option = self.subconfig.cfgimpl_get_description().impl_getchild(self.name, + self.config_bag, + self.subconfig) + self.config_bag.option = option + if not self.allow_optiondescription and option.impl_is_optiondescription(): + raise APIError(_('option must not be an optiondescription')) + return option + def _help(self): txt = [] for func_name in dir(self): @@ -140,65 +153,80 @@ class TiramisuOptionOption(CommonTiramisuOption): @count def ismulti(self): """test if option could have multi value""" - return self._opt.impl_is_multi() + option = self.get_option() + return option.impl_is_multi() @count def issubmulti(self): """test if option could have submulti value""" - return self._opt.impl_is_submulti() + option = self.get_option() + return option.impl_is_submulti() @count def ismasterslaves(self): """test if option is a master or a slave""" - return self._opt.impl_is_master_slaves() + option = self.get_option() + return option.impl_is_master_slaves() @count def ismaster(self): """test if option is a master""" - return self._opt.impl_is_master_slaves('master') + option = self.get_option() + return option.impl_is_master_slaves('master') @count def isslave(self): """test if option is a slave""" - return self._opt.impl_is_master_slaves('slave') + option = self.get_option() + return option.impl_is_master_slaves('slave') @count def getname(self): - return self._opt.impl_getname() + option = self.get_option() + return option.impl_getname() @count def getdoc(self): - return self._opt.impl_get_display_name() + option = self.get_option() + return option.impl_get_display_name() @count def default(self): - return self.config_bag.option.impl_getdefault() + option = self.get_option() + return option.impl_getdefault() @count def defaultmulti(self): - return self.config_bag.option.impl_getdefault_multi() + option = self.get_option() + return option.impl_getdefault_multi() @count def has_dependency(self, self_is_dep=True): - return self.config_bag.option.impl_has_dependency(self_is_dep) + option = self.get_option() + return option.impl_has_dependency(self_is_dep) class TiramisuOptionOwner(CommonTiramisuOption): """manager option's owner""" def __init__(self, + name, path, index, + subconfig, config_bag): - super(TiramisuOptionOwner, self).__init__(path, + super(TiramisuOptionOwner, self).__init__(name, + path, index, + subconfig, config_bag) self.values = self.config_bag.config.cfgimpl_get_values() @count def get(self): """get owner for a specified option""" + self.get_option() return self.values.getowner(self.path, self.index, self.config_bag) @@ -206,6 +234,7 @@ class TiramisuOptionOwner(CommonTiramisuOption): @count def isdefault(self): """is option has defaut value""" + self.get_option() return self.values.is_default_owner(self.path, self.index, self.config_bag) @@ -213,6 +242,7 @@ class TiramisuOptionOwner(CommonTiramisuOption): @count def set(self, owner): """get owner for a specified option""" + self.get_option() if TIRAMISU_VERSION == 2: if owner in ['default', 'forced', 'meta']: raise ConfigError() @@ -235,16 +265,21 @@ class TiramisuOptionProperty(CommonTiramisuOption): slave_need_index = False def __init__(self, + name, path, index, + subconfig, config_bag): - super(TiramisuOptionProperty, self).__init__(path, + super(TiramisuOptionProperty, self).__init__(name, + path, index, + subconfig, config_bag) self.settings = config_bag.config.cfgimpl_get_settings() @count def get(self): + self.get_option() self._test_slave_index() properties = self.settings.getproperties(self.path, self.index, @@ -256,6 +291,7 @@ class TiramisuOptionProperty(CommonTiramisuOption): @count def set(self, properties): """set properties for a specified option""" + self.get_option() properties = frozenset(properties) self.settings.setproperties(path=self.path, properties=properties, @@ -263,6 +299,7 @@ class TiramisuOptionProperty(CommonTiramisuOption): @count def add(self, prop): + self.get_option() self.settings.addproperty(self.path, prop, self.config_bag) @@ -277,6 +314,7 @@ class TiramisuOptionProperty(CommonTiramisuOption): def reset(self): """reset all personalised properties """ + self.get_option() self.settings.reset(opt=self.config_bag.option, path=self.path) @@ -300,9 +338,9 @@ class TiramisuOptionPermissive(CommonTiramisuOption): def get(self): """get permissive value for a specified path""" if TIRAMISU_VERSION == 2: - args = [self.setting_properties, self._path] + args = [self.setting_properties, self.path] else: - args = [self._opt, self._path] + args = [self._opt, self.path] return self.settings.getpermissive(*args) @count @@ -310,7 +348,7 @@ class TiramisuOptionPermissive(CommonTiramisuOption): if TIRAMISU_VERSION == 2: permissive = tuple(permissive) self.settings.setpermissive(opt=self._opt, - path=self._path, + path=self.path, permissive=permissive) @count @@ -325,7 +363,8 @@ class TiramisuOptionInformation(CommonTiramisuOption): @count def get(self, name, default=undefined): - return self.config_bag.option.impl_get_information(name, default) + option = self.get_option() + return option.impl_get_information(name, default) class TiramisuOptionValue(CommonTiramisuOption): @@ -334,11 +373,12 @@ class TiramisuOptionValue(CommonTiramisuOption): @count def get(self): + self.get_option() self._test_slave_index() settings = self.config_bag.config.cfgimpl_get_settings() - value = self.config_bag.config.getattr(self.path, - self.index, - self.config_bag) + value = self.subconfig.getattr(self.name, + self.index, + self.config_bag) if isinstance(value, Multi): value = list(value) return value @@ -346,6 +386,7 @@ class TiramisuOptionValue(CommonTiramisuOption): @count def set(self, value): """set a value for a specified option""" + self.get_option() self._test_slave_index() values = self.config_bag.config.cfgimpl_get_values() if isinstance(value, list): @@ -359,15 +400,16 @@ class TiramisuOptionValue(CommonTiramisuOption): value = values.getdefaultvalue(self.path, self.index, self.config_bag) - self.config_bag.config.setattr(self.path, - self.index, - value, - self.config_bag) + self.subconfig.setattr(self.name, + self.index, + value, + self.config_bag) @count def pop(self, index): """pop value for a specified master values """ + self.get_option() self._test_slave_index() #FIXME only for master self.config_bag.config.delattr(self.path, @@ -377,6 +419,7 @@ class TiramisuOptionValue(CommonTiramisuOption): @count def reset(self): """reset value for a value""" + self.get_option() self._test_slave_index() self.config_bag.config.delattr(self.path, self.index, @@ -385,7 +428,8 @@ class TiramisuOptionValue(CommonTiramisuOption): @count def len(self): #FIXME only for slave - subconfig_path = self._path.rsplit('.', 1)[0] + self.get_option() + subconfig_path = self.path.rsplit('.', 1)[0] subconfig = self.config.getattr(subconfig_path, None, self.config_bag) @@ -393,6 +437,7 @@ class TiramisuOptionValue(CommonTiramisuOption): def __getattr__(self, name): if name == 'list': + self.get_option() if isinstance(self.config_bag.option, ChoiceOption): return self._list raise APIError(_('{} allowed only for choiceoption').format(name)) @@ -416,11 +461,15 @@ class TiramisuOption(object): tmpl_help = ' {} {}: {}' def __init__(self, + name, path, index, + subconfig, config_bag): - self._path = path + self.name = name + self.subconfig = subconfig + self.path = path self.index = index self.config_bag = config_bag self.registers = {} @@ -436,8 +485,10 @@ class TiramisuOption(object): def __getattr__(self, subfunc): if subfunc in self.registers: - return self.registers[subfunc](self._path, + return self.registers[subfunc](self.name, + self.path, self.index, + self.subconfig, self.config_bag) elif subfunc == 'help': return self._help() @@ -450,7 +501,7 @@ class TiramisuOption(object): withvalue=undefined, withoption=None, fullpath=False): - return self.config_bag.config.getattr(self._path, + return self.config_bag.config.getattr(self.path, None, self.config_bag).make_dict(config_bag=self.config_bag, flatten=flatten, @@ -609,23 +660,18 @@ class TiramisuDispatcherOption(TiramisuContextOption): config_bag.validate_properties = validate if not validate: config_bag.setting_properties = None - opt = config_bag.config.unwrap_from_path(path, - index, - config_bag) - config_bag.option = opt - if index is not None and not opt.impl_is_master_slaves('slave'): - raise APIError('index must be set only with a slave option') - #if opt.impl_is_symlinkoption(): - # config_bag.ori_option = config_bag.option - # config_bag.option = opt.impl_getopt() - # true_path = config_bag.option.impl_getpath(self.config_bag.config) - # config_bag.config.unwrap_from_path(true_path, - # index, - # config_bag) - #else: - # true_path = None - return TiramisuOption(path, + subconfig, name = config_bag.config.cfgimpl_get_home_by_path(path, + config_bag) + #opt = config_bag.config.unwrap_from_path(path, + # index, + # config_bag) + #config_bag.option = opt + #if index is not None and not opt.impl_is_master_slaves('slave'): + # raise APIError('index must be set only with a slave option') + return TiramisuOption(name, + path, index, + subconfig, config_bag) diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index c3d4261..6136dec 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -32,6 +32,7 @@ def carry_out_calculation(option, callback_params, index, config_bag, + orig_value=undefined, is_validator=False): """a function that carries out a calculation for an option's value @@ -177,31 +178,43 @@ def carry_out_calculation(option, sconfig_bag = config_bag.copy('nooption') sconfig_bag.option = opt sconfig_bag.force_permissive = True - if opt == option: - sconfig_bag.validate = False if index is not None and opt.impl_is_master_slaves() and \ opt.impl_get_master_slaves().in_same_group(option): - if opt.impl_is_master_slaves('slave'): + if opt == option: + index_ = None + with_index = False + iter_slave = True + elif opt.impl_is_master_slaves('slave'): index_ = index with_index = False + iter_slave = False else: index_ = None with_index = True + iter_slave = False else: index_ = None with_index = False - try: - # get value - value = context.getattr(path, - index_, - sconfig_bag) - if with_index: - value = value[index] - except PropertiesOptionError as err: - if force_permissive: - continue - raise ConfigError(_('unable to carry out a calculation for "{}"' - ', {}').format(option.impl_get_display_name(), err)) + iter_slave = False + if opt == option and orig_value is not undefined and \ + (not opt.impl_is_master_slaves('slave') or index is None): + value = orig_value + else: + if opt == option: + sconfig_bag.validate = False + try: + # get value + value = context.getattr(path, + index_, + sconfig_bag, + iter_slave=iter_slave) + if with_index: + value = value[index] + except PropertiesOptionError as err: + if force_permissive: + continue + raise ConfigError(_('unable to carry out a calculation for "{}"' + ', {}').format(option.impl_get_display_name(), err)) if key == '': args.append(value) diff --git a/tiramisu/config.py b/tiramisu/config.py index 4c0c921..eda5a53 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -282,29 +282,32 @@ class SubConfig(object): value) context = self._cfgimpl_get_context() if '.' in name: # pragma: optional cover - self, name = self.cfgimpl_get_home_by_path(name, - config_bag) - child = self.cfgimpl_get_description().impl_getchild(name, - config_bag, - self) - if child.impl_is_optiondescription() or isinstance(child, SynDynOptionDescription): + raise Exception('ah non ...') + #self, name = self.cfgimpl_get_home_by_path(name, + # config_bag) + if config_bag.option is None: + config_bag.option = self.cfgimpl_get_description().impl_getchild(name, + config_bag, + self) + if config_bag.option.impl_is_optiondescription() or \ + isinstance(config_bag.option, SynDynOptionDescription): raise TypeError(_("can't assign to an OptionDescription")) # pragma: optional cover - elif child.impl_is_symlinkoption(): + elif config_bag.option.impl_is_symlinkoption(): raise TypeError(_("can't assign to a SymLinkOption")) else: - subpath = self._get_subpath(name) + path = self._get_subpath(name) if config_bag.setting_properties: - self.cfgimpl_get_settings().validate_properties(subpath, - index, - config_bag) - self.cfgimpl_get_description().impl_validate_value(child, - value, - self) - return self.cfgimpl_get_values().setvalue(subpath, - index, - value, - config_bag, - _commit) + context.cfgimpl_get_settings().validate_properties(path, + index, + config_bag) + context.cfgimpl_get_description().impl_validate_value(config_bag.option, + value, + self) + return context.cfgimpl_get_values().setvalue(path, + index, + value, + config_bag, + _commit) def delattr(self, name, @@ -363,6 +366,7 @@ class SubConfig(object): otherwise """ if '.' in name: + # raise Exception('je suis desole ...') self, name = self.cfgimpl_get_home_by_path(name, config_bag) diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index 0cfa672..0f55823 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -197,13 +197,16 @@ class Base(object): kwargs = set() if add_value: args.add('value') - for param in calculator_params.keys(): - if param == '': - for idx, _ in enumerate(calculator_params['']): - # construct an appropriate name - args.add('param{}'.format(idx)) - else: - kwargs.add(param) + if self.impl_is_dynoptiondescription(): + kwargs.add('suffix') + if calculator_params is not None: + for param in calculator_params.keys(): + if param == '': + for idx, _ in enumerate(calculator_params['']): + # construct an appropriate name + args.add('param{}'.format(idx)) + else: + kwargs.add(param) return args, kwargs def _build_calculator_params(self, @@ -211,65 +214,70 @@ class Base(object): calculator_params, add_value=False): - if calculator_params is not None: - func_args, func_kwargs, func_positional, func_keyword = self._get_function_args(calculator) - calculator_args, calculator_kwargs = self._get_parameters_args(calculator_params, add_value) - # remove knowned kwargs - common_kwargs = func_kwargs & calculator_kwargs - func_kwargs -= common_kwargs - calculator_kwargs -= common_kwargs - # remove knowned calculator's kwargs in func's args - common = func_args & calculator_kwargs - func_args -= common - calculator_kwargs -= common - # remove unknown calculator's args in func's args - for idx in range(min(len(calculator_args), len(func_args))): - func_args.pop() - calculator_args.pop() - # remove unknown calculator's args in func's kwargs + is_multi = self.impl_is_optiondescription() or self.impl_is_multi() + if calculator_params is None: + calculator_params = {} + func_args, func_kwargs, func_positional, func_keyword = self._get_function_args(calculator) + calculator_args, calculator_kwargs = self._get_parameters_args(calculator_params, add_value) + # remove knowned kwargs + common_kwargs = func_kwargs & calculator_kwargs + func_kwargs -= common_kwargs + calculator_kwargs -= common_kwargs + # remove knowned calculator's kwargs in func's args + common = func_args & calculator_kwargs + func_args -= common + calculator_kwargs -= common + # remove unknown calculator's args in func's args + for idx in range(min(len(calculator_args), len(func_args))): + func_args.pop() + calculator_args.pop() + # remove unknown calculator's args in func's kwargs + if is_multi: func_kwargs_left = func_kwargs - {'index', 'self'} - func_kwargs_pop = set() - for idx in range(min(len(calculator_args), len(func_kwargs_left))): - func_kwargs_pop.add(func_kwargs_left.pop()) - calculator_args.pop() - func_kwargs -= func_kwargs_pop - if func_positional: - calculator_args = set() - if func_keyword: - calculator_kwargs = set() - if calculator_args or calculator_kwargs: - # there is more args/kwargs than expected! - raise ConfigError(_('cannot find those arguments "{}" in function "{}" for "{}"' - '').format(list(calculator_args | calculator_kwargs), + else: + func_kwargs_left = func_kwargs + func_kwargs_pop = set() + for idx in range(min(len(calculator_args), len(func_kwargs_left))): + func_kwargs_pop.add(func_kwargs_left.pop()) + calculator_args.pop() + func_kwargs -= func_kwargs_pop + if func_positional: + calculator_args = set() + if func_keyword: + calculator_kwargs = set() + if calculator_args or calculator_kwargs: + # there is more args/kwargs than expected! + raise ConfigError(_('cannot find those arguments "{}" in function "{}" for "{}"' + '').format(list(calculator_args | calculator_kwargs), + calculator.__name__, + self.impl_get_display_name())) + has_self = False + has_index = False + if is_multi and func_args: + # there is extra args/kwargs + if not self.impl_is_optiondescription() and is_multi: + params = list(calculator_params.get('', tuple())) + if add_value: + # only for validator + has_self = True + params.append((self, False)) + func_args.pop() + if func_args: + has_index = True + params.append(('index',)) + func_args.pop() + if func_args: + raise ConfigError(_('missing those arguements "{}" in function "{}" for "{}"' + '').format(list(func_args), calculator.__name__, self.impl_get_display_name())) - has_self = False - has_index = False - if func_args: - # there is extra args/kwargs - if not self.impl_is_optiondescription() and self.impl_is_multi(): - params = list(calculator_params['']) - if add_value: - # only for validator - has_self = True - params.append((self, False)) - func_args.pop() - if func_args: - has_index = True - params.append(('index',)) - func_args.pop() - if func_args: - raise ConfigError(_('missing those arguements "{}" in function "{}" for "{}"' - '').format(list(func_args), - calculator.__name__, - self.impl_get_display_name())) - calculator_params[''] = tuple(params) - if not self.impl_is_optiondescription() and self.impl_is_multi(): - if add_value and not has_self and 'self' in func_kwargs: - # only for validator - calculator_params['self'] = (self, False) - if not has_index and 'index' in func_kwargs: - calculator_params['index'] = (('index',),) + calculator_params[''] = tuple(params) + if not self.impl_is_optiondescription() and self.impl_is_multi(): + if add_value and not has_self and 'self' in func_kwargs: + # only for validator + calculator_params['self'] = (self, False) + if not has_index and 'index' in func_kwargs: + calculator_params['index'] = (('index',),) return calculator_params def impl_has_dependency(self, diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 039c0a1..fca028a 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -97,6 +97,11 @@ class Option(OnlyOption): _setattr(self, '_multi', _multi) if multi is not False and default is None: default = [] + super(Option, self).__init__(name, + doc, + requires=requires, + properties=properties, + is_multi=is_multi) if validator is not None: validate_calculator(validator, validator_params, @@ -120,12 +125,6 @@ class Option(OnlyOption): _setattr(self, '_warnings_only', warnings_only) if allow_empty_list is not undefined: _setattr(self, '_allow_empty_list', allow_empty_list) - - super(Option, self).__init__(name, - doc, - requires=requires, - properties=properties, - is_multi=is_multi) if is_multi and default_multi is not None: def test_multi_value(value): try: @@ -241,6 +240,7 @@ class Option(OnlyOption): callback=validator, callback_params=validator_params_, index=_index, + orig_value=value, config_bag=config_bag, is_validator=True) diff --git a/tiramisu/value.py b/tiramisu/value.py index fa47d4d..1236176 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -327,7 +327,7 @@ class Values(object): context = self._getcontext() owner = context.cfgimpl_get_settings().getowner() if 'validator' in config_bag.setting_properties and config_bag.validate: - if config_bag.option._has_consistencies(context): + if index is not None or config_bag.option._has_consistencies(context): # set value to a fake config when option has dependency # validation will be complet in this case (consistency, ...) tested_context = context._gen_fake_values()