diff --git a/doc/api/tiramisu.storage.txt b/doc/api/tiramisu.storage.txt new file mode 100644 index 0000000..86cb062 --- /dev/null +++ b/doc/api/tiramisu.storage.txt @@ -0,0 +1,6 @@ +tiramisu.storage +================ + +.. automodule:: tiramisu.storage + :members: + :noindex: \ No newline at end of file diff --git a/test/test_config.py b/test/test_config.py index 17e863a..e091294 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -230,3 +230,9 @@ def test_duplicated_option(): root = OptionDescription('root', '', [d1, d2]) #in different OptionDescription raises(ConflictError, "config = Config(root)") + +def test_cannot_assign_value_to_option_description(): + descr = make_description() + cfg = Config(descr) + raises(TypeError, "cfg.gc = 3") + diff --git a/test/test_config_ip.py b/test/test_config_ip.py index 2bdba53..b7d3010 100644 --- a/test/test_config_ip.py +++ b/test/test_config_ip.py @@ -7,7 +7,7 @@ from tiramisu.option import IPOption, NetworkOption, NetmaskOption, \ def test_ip(): a = IPOption('a', '') - b = IPOption('b', '', only_private=True) + b = IPOption('b', '', private_only=True) od = OptionDescription('od', '', [a, b]) c = Config(od) c.a = '192.168.1.1' diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 8ca8687..117de9d 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -28,6 +28,12 @@ def return_value(value=None): return value +def return_value2(*args, **kwargs): + value = list(args) + value.extend(kwargs.values()) + return value + + def make_description(): gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False) @@ -675,3 +681,13 @@ def test_callback_multi_multi(): raises(ConfigError, "cfg.val4") assert cfg.val5 == ['val1', 'val4', 'val2', 'val4', 'val3', 'val4'] assert cfg.val7 == ['val1', 'val21', 'val2', 'val22', 'val3', 'val23'] + + +def test_multi_with_no_value(): + #First option return [] (so without value) + val1 = StrOption('val1', "", ['val'], multi=True) + val2 = StrOption('val2', "", multi=True) + val3 = StrOption('val3', '', multi=True, callback=return_value, callback_params={'': ((val2, False),), 'value': ((val1, False),)}) + od = OptionDescription('od', '', [val1, val2, val3]) + c = Config(od) + raises(ConfigError, "c.val3") diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 5cf53cd..d5226db 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -4,7 +4,8 @@ from py.test import raises from tiramisu.setting import owners, groups from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ - SymLinkOption, OptionDescription + BroadcastOption, SymLinkOption, OptionDescription +from tiramisu.error import ConfigError def test_consistency_not_equal(): @@ -22,6 +23,60 @@ def test_consistency_not_equal(): c.b = 2 +def test_consistency_not_equal_many_opts(): + a = IntOption('a', '') + b = IntOption('b', '') + c = IntOption('c', '') + d = IntOption('d', '') + e = IntOption('e', '') + f = IntOption('f', '') + od = OptionDescription('od', '', [a, b, c, d, e, f]) + a.impl_add_consistency('not_equal', b, c, d, e, f) + c = Config(od) + assert c.a is None + assert c.b is None + # + c.a = 1 + del(c.a) + # + c.a = 1 + raises(ValueError, "c.b = 1") + # + c.b = 2 + raises(ValueError, "c.f = 2") + raises(ValueError, "c.f = 1") + # + c.d = 3 + raises(ValueError, "c.f = 3") + raises(ValueError, "c.a = 3") + raises(ValueError, "c.c = 3") + raises(ValueError, "c.e = 3") + + +def test_consistency_not_in_config(): + a = IntOption('a', '') + b = IntOption('b', '') + a.impl_add_consistency('not_equal', b) + od1 = OptionDescription('od1', '', [a]) + od2 = OptionDescription('od2', '', [b]) + od = OptionDescription('root', '', [od1]) + raises(ConfigError, "Config(od)") + od = OptionDescription('root', '', [od1, od2]) + Config(od) + #with subconfig + raises(ConfigError, "Config(od.od1)") + + +def test_consistency_afer_config(): + a = IntOption('a', '') + b = IntOption('b', '') + od1 = OptionDescription('od1', '', [a]) + od2 = OptionDescription('od2', '', [b]) + od = OptionDescription('root', '', [od1, od2]) + Config(od) + raises(AttributeError, "a.impl_add_consistency('not_equal', b)") + + def test_consistency_not_equal_symlink(): a = IntOption('a', '') b = IntOption('b', '') @@ -29,7 +84,7 @@ def test_consistency_not_equal_symlink(): od = OptionDescription('od', '', [a, b, c]) a.impl_add_consistency('not_equal', b) c = Config(od) - assert set(od._consistencies.keys()) == set([a, b]) + assert set(od._cache_consistencies.keys()) == set([a, b]) def test_consistency_not_equal_multi(): @@ -53,6 +108,14 @@ def test_consistency_default(): raises(ValueError, "a.impl_add_consistency('not_equal', b)") +def test_consistency_default_multi(): + a = IntOption('a', '', [2, 1], multi=True) + b = IntOption('b', '', [1, 1], multi=True) + c = IntOption('c', '', [1, 2], multi=True) + raises(ValueError, "a.impl_add_consistency('not_equal', b)") + a.impl_add_consistency('not_equal', c) + + def test_consistency_default_diff(): a = IntOption('a', '', 3) b = IntOption('b', '', 1) @@ -99,7 +162,7 @@ def test_consistency_ip_netmask_error_multi(): a = IPOption('a', '', multi=True) b = NetmaskOption('b', '') od = OptionDescription('od', '', [a, b]) - raises(ValueError, "b.impl_add_consistency('ip_netmask', a)") + raises(ConfigError, "b.impl_add_consistency('ip_netmask', a)") def test_consistency_ip_netmask_multi(): @@ -159,3 +222,53 @@ def test_consistency_network_netmask_multi_master(): c.a = ['192.168.1.0'] c.b = ['255.255.255.0'] raises(ValueError, "c.a = ['192.168.1.1']") + + +def test_consistency_broadcast(): + a = NetworkOption('a', '', multi=True) + b = NetmaskOption('b', '', multi=True) + c = BroadcastOption('c', '', multi=True) + od = OptionDescription('a', '', [a, b, c]) + od.impl_set_group_type(groups.master) + b.impl_add_consistency('network_netmask', a) + c.impl_add_consistency('broadcast', a, b) + c = Config(od) + #first, test network_netmask + c.a = ['192.168.1.128'] + raises(ValueError, "c.b = ['255.255.255.0']") + # + c.a = ['192.168.1.0'] + c.b = ['255.255.255.0'] + c.c = ['192.168.1.255'] + raises(ValueError, "c.a = ['192.168.1.1']") + # + c.a = ['192.168.1.0', '192.168.2.128'] + c.b = ['255.255.255.0', '255.255.255.128'] + c.c = ['192.168.1.255', '192.168.2.255'] + raises(ValueError, "c.c[1] = '192.168.2.128'") + c.c[1] = '192.168.2.255' + + +def test_consistency_broadcast_default(): + a = NetworkOption('a', '', '192.168.1.0') + b = NetmaskOption('b', '', '255.255.255.128') + c = BroadcastOption('c', '', '192.168.2.127') + d = BroadcastOption('d', '', '192.168.1.127') + od = OptionDescription('a', '', [a, b, c]) + raises(ValueError, "c.impl_add_consistency('broadcast', a, b)") + od2 = OptionDescription('a', '', [a, b, d]) + d.impl_add_consistency('broadcast', a, b) + + +def test_consistency_not_all(): + #_cache_consistencies is not None by not options has consistencies + a = NetworkOption('a', '', multi=True) + b = NetmaskOption('b', '', multi=True) + c = BroadcastOption('c', '', multi=True) + od = OptionDescription('a', '', [a, b, c]) + od.impl_set_group_type(groups.master) + b.impl_add_consistency('network_netmask', a) + c = Config(od) + c.a = ['192.168.1.0'] + c.b = ['255.255.255.0'] + c.c = ['192.168.1.255'] diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 8e00916..001f9f7 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -1,9 +1,11 @@ import autopath +import warnings from py.test import raises from tiramisu.config import Config from tiramisu.option import StrOption, OptionDescription -from tiramisu.error import ConfigError +from tiramisu.setting import groups +from tiramisu.error import ValueWarning def return_true(value, param=None): @@ -13,37 +15,36 @@ def return_true(value, param=None): def return_false(value, param=None): if value == 'val' and param in [None, 'yes']: - return False + raise ValueError('error') def return_val(value, param=None): return 'val' +def return_if_val(value): + if value != 'val': + raise ValueError('error') + + 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]) + root = OptionDescription('root', '', [opt1, opt2]) 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]) + root = OptionDescription('root', '', [opt1, opt2]) cfg = Config(root) assert cfg.opt1 == 'val' raises(ValueError, "cfg.opt2 = 'val'") - raises(ConfigError, "cfg.opt3 = 'val'") def test_validator_params_key(): @@ -57,3 +58,98 @@ def test_validator_params_key(): def test_validator_params_option(): opt0 = StrOption('opt0', '', default='val') raises(ValueError, "opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ((opt0, False),)}, default='val')") + + +def test_validator_multi(): + opt1 = StrOption('opt1', '', validator=return_if_val, multi=True) + root = OptionDescription('root', '', [opt1]) + cfg = Config(root) + assert cfg.opt1 == [] + cfg.opt1.append('val') + assert cfg.opt1 == ['val'] + raises(ValueError, "cfg.opt1.append('val1')") + raises(ValueError, "cfg.opt1 = ['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]) + cfg = Config(root) + assert cfg.opt1 == 'val' + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + cfg.opt1 = 'val' + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + cfg.opt2 = 'val' + assert len(w) == 1 + assert w[0].message.opt == opt2 + assert str(w[0].message) == 'invalid value val for option opt2: error' + # + with warnings.catch_warnings(record=True) as w: + cfg.opt3.append('val') + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + cfg.opt3.append('val1') + assert len(w) == 1 + assert w[0].message.opt == opt3 + assert str(w[0].message) == 'invalid value val1 for option opt3: error' + raises(ValueError, "cfg.opt2 = 1") + # + with warnings.catch_warnings(record=True) as w: + cfg.opt2 = 'val' + cfg.opt3.append('val') + assert len(w) == 2 + assert w[0].message.opt == opt2 + assert str(w[0].message) == 'invalid value val for option opt2: error' + assert w[1].message.opt == opt3 + assert str(w[1].message) == 'invalid value val1 for option opt3: error' + + +def test_validator_warning_master_slave(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=return_false, warnings_only=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=return_if_val, warnings_only=True) + interface1 = OptionDescription('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]) + cfg = Config(root) + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0.append(None) + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] + assert len(w) == 1 + assert w[0].message.opt == netmask_admin_eth0 + assert str(w[0].message) == 'invalid value val1 for option netmask_admin_eth0: error' + # + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' + # + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' + # + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' + # + warnings.resetwarnings() + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' diff --git a/test/test_state.py b/test/test_state.py index 8587b4a..ef46ce2 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -40,7 +40,7 @@ def _diff_opt(opt1, opt2): if diff2 != set(): raise Exception('more attribute in opt2 {0}'.format(list(diff2))) for attr in attr1: - if attr in ['_cache_paths']: + if attr in ['_cache_paths', '_cache_consistencies']: continue err1 = False err2 = False @@ -72,7 +72,8 @@ def _diff_opt(opt1, opt2): if isinstance(val1, list): for index, consistency in enumerate(val1): assert consistency[0] == val2[index][0] - assert consistency[1]._name == val2[index][1]._name + for idx, opt in enumerate(consistency[1]): + assert opt._name == val2[index][1][idx]._name elif attr == '_callback': assert val1[0] == val2[0] if val1[1] is not None: diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 20d693c..eb5c6ef 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -40,6 +40,13 @@ def carry_out_calculation(name, config, callback, callback_params, :param max_len: max length for a multi :type max_len: int + The callback_params is a dict. Key is used to build args (if key is '') + and kwargs (otherwise). Values are tuple of: + - values + - tuple with option and boolean's force_permissive (True when don't raise + if PropertiesOptionError) + Values could have multiple values only when key is ''. + * if no callback_params: => calculate() @@ -91,7 +98,6 @@ def carry_out_calculation(name, config, callback, callback_params, - 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) @@ -99,9 +105,13 @@ def carry_out_calculation(name, config, callback, callback_params, - 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() + - a multi option without value with a simple option + opt1 == [] + opt2 == 11 + => [] + * if callback_params={'value': ((opt1, False), (opt2, False))} => ConfigError() @@ -113,39 +123,47 @@ def carry_out_calculation(name, config, callback, callback_params, If calculate return list, this list is extend to return value. """ tcparams = {} + # if callback_params has a callback, launch several time calculate() one_is_multi = False - len_multi = 0 + # multi's option should have same value for all option + len_multi = None for key, callbacks in callback_params.items(): for callbk in callbacks: if isinstance(callbk, tuple): + # callbk is something link (opt, True|False) option, force_permissive = callbk + path = config.cfgimpl_get_description().impl_get_path_by_opt( + option) # get value try: - path = config.cfgimpl_get_description().impl_get_path_by_opt(option) value = config._getattr(path, force_permissive=True) except PropertiesOptionError as err: if force_permissive: continue raise ConfigError(_('unable to carry out a calculation, ' 'option {0} has properties: {1} for: ' - '{2}').format(option._name, err.proptype, + '{2}').format(option._name, + err.proptype, name)) is_multi = option.impl_is_multi() if is_multi: - 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' - ' multi types must have same ' - 'length for: {0}').format(name)) - len_multi = len_value + len_value = len(value) + if len_multi is not None and len_multi != len_value: + raise ConfigError(_('unable to carry out a ' + 'calculation, option value with' + ' multi types must have same ' + 'length for: {0}').format(name)) + len_multi = len_value one_is_multi = True tcparams.setdefault(key, []).append((value, is_multi)) else: + # callbk is a value and not a multi tcparams.setdefault(key, []).append((callbk, False)) + # if one value is a multi, launch several time calculate + # if index is set, return a value + # if no index, return a list if one_is_multi: ret = [] if index: @@ -160,19 +178,20 @@ def carry_out_calculation(name, config, callback, callback_params, else: range_ = range(len_multi) for incr in range_: - tcp = {} - params = [] + args = [] + kwargs = {} for key, couples in tcparams.items(): for couple in couples: value, ismulti = couple - if ismulti and value is not None: - if key == '': - params.append(value[incr]) - else: - tcp[key] = value[incr] + if ismulti: + val = value[incr] else: - params.append(value) - calc = calculate(name, callback, params, tcp) + val = value + if key == '': + args.append(val) + else: + kwargs[key] = val + calc = calculate(callback, args, kwargs) if index: ret = calc else: @@ -182,24 +201,26 @@ def carry_out_calculation(name, config, callback, callback_params, ret.append(calc) return ret else: - tcp = {} - params = [] + # no value is multi + # return a single value + args = [] + kwargs = {} for key, couples in tcparams.items(): for couple in couples: + # couple[1] (ismulti) is always False if key == '': - value = couple[0] - params.append(value) + args.append(couple[0]) else: - tcp[key] = couple[0] - return calculate(name, callback, params, tcp) + kwargs[key] = couple[0] + return calculate(callback, args, kwargs) -def calculate(name, callback, params, tcparams): +def calculate(callback, args, kwargs): """wrapper that launches the 'callback' - :param callback: callback name - :param params: in the callback's arity, the unnamed parameters - :param tcparams: in the callback's arity, the named parameters + :param callback: callback function + :param args: in the callback's arity, the unnamed parameters + :param kwargs: in the callback's arity, the named parameters """ - return callback(*params, **tcparams) + return callback(*args, **kwargs) diff --git a/tiramisu/config.py b/tiramisu/config.py index 02c6f74..1c05e22 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -"options handler global entry point" # Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors) # # This program is free software: you can redistribute it and/or modify it @@ -19,6 +18,7 @@ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the whole pypy projet is under MIT licence # ____________________________________________________________ +"options handler global entry point" import weakref from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.option import OptionDescription, Option, SymLinkOption @@ -30,7 +30,11 @@ from tiramisu.i18n import _ class SubConfig(object): - "sub configuration management entry" + """Sub configuration management entry. + Tree if OptionDescription's responsability. SubConfig are generated + on-demand. A Config is also a SubConfig. + Root Config is call context below + """ __slots__ = ('_impl_context', '_impl_descr', '_impl_path') def __init__(self, descr, context, subpath=None): @@ -55,6 +59,7 @@ class SubConfig(object): def cfgimpl_reset_cache(self, only_expired=False, only=('values', 'settings')): + "remove cache (in context)" self._cfgimpl_get_context().cfgimpl_reset_cache(only_expired, only) def cfgimpl_get_home_by_path(self, path, force_permissive=False, @@ -179,7 +184,9 @@ class SubConfig(object): homeconfig, name = self.cfgimpl_get_home_by_path(name) return homeconfig.__setattr__(name, value) child = getattr(self.cfgimpl_get_description(), name) - if not isinstance(child, SymLinkOption): + if isinstance(child, OptionDescription): + raise TypeError(_("can't assign to an OptionDescription")) + elif not isinstance(child, SymLinkOption): if self._impl_path is None: path = name else: diff --git a/tiramisu/error.py b/tiramisu/error.py index e4e5991..6a7c8be 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -56,3 +56,35 @@ class SlaveError(Exception): class ConstError(TypeError): "no uniq value in _NameSpace" pass + + +#Warning +class ValueWarning(UserWarning): + """Option could warn user and not raise ValueError. + + Example: + + >>> import warnings + >>> from tiramisu.error import ValueWarning + >>> from tiramisu.option import StrOption, OptionDescription + >>> from tiramisu.config import Config + >>> warnings.simplefilter("always", ValueWarning) + >>> def a(val): + ... raise ValueError('pouet') + ... + >>> s=StrOption('s', '', validator=a, warnings_only=True) + >>> o=OptionDescription('o', '', [s]) + >>> c=Config(o) + >>> c.s = 'val' + StrOption:0: ValueWarning: invalid value val for option s: pouet + >>> with warnings.catch_warnings(record=True) as w: + ... c.s = 'val' + ... + >>> w[0].message.opt == s + True + >>> print str(w[0].message) + invalid value val for option s: pouet + """ + def __init__(self, msg, opt): + self.opt = opt + super(ValueWarning, self).__init__(msg) diff --git a/tiramisu/option.py b/tiramisu/option.py index 64d3f7e..ca143a0 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -24,8 +24,9 @@ import sys from copy import copy, deepcopy from types import FunctionType from IPy import IP +import warnings -from tiramisu.error import ConflictError, ConfigError +from tiramisu.error import ConfigError, ConflictError, ValueWarning from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -59,9 +60,8 @@ class BaseOption(object): __setattr__ method """ __slots__ = ('_name', '_requires', '_properties', '_readonly', - '_consistencies', '_calc_properties', '_impl_informations', - '_state_consistencies', '_state_readonly', '_state_requires', - '_stated') + '_calc_properties', '_impl_informations', + '_state_readonly', '_state_requires', '_stated') def __init__(self, name, doc, requires, properties): if not valid_name(name): @@ -71,7 +71,6 @@ class BaseOption(object): self.impl_set_information('doc', doc) self._calc_properties, self._requires = validate_requires_arg( requires, self._name) - self._consistencies = None if properties is None: properties = tuple() if not isinstance(properties, tuple): @@ -96,8 +95,7 @@ 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 not name.startswith('_cache'): is_readonly = False # never change _name if name == '_name': @@ -107,15 +105,12 @@ class BaseOption(object): is_readonly = True except: pass - try: - if self._readonly is True: - if value is True: - # already readonly and try to re set readonly - # don't raise, just exit - return - is_readonly = True - except AttributeError: - pass + elif name != '_readonly': + try: + if self._readonly is True: + is_readonly = True + except AttributeError: + self._readonly = False if is_readonly: raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" " read-only").format( @@ -147,58 +142,6 @@ class BaseOption(object): raise ValueError(_("information's item not found: {0}").format( key)) - # serialize/unserialize - def _impl_convert_consistencies(self, descr, load=False): - """during serialization process, many things have to be done. - one of them is the localisation of the options. - The paths are set once for all. - - :type descr: :class:`tiramisu.option.OptionDescription` - :param load: `True` if we are at the init of the option description - :type load: bool - """ - if not load and self._consistencies is None: - self._state_consistencies = None - elif load and self._state_consistencies is None: - self._consistencies = None - del(self._state_consistencies) - else: - if load: - consistencies = self._state_consistencies - else: - consistencies = self._consistencies - if isinstance(consistencies, list): - new_value = [] - for consistency in consistencies: - if load: - new_value.append((consistency[0], - descr.impl_get_opt_by_path( - consistency[1]))) - else: - new_value.append((consistency[0], - descr.impl_get_path_by_opt( - consistency[1]))) - - else: - new_value = {} - for key, _consistencies in consistencies.items(): - new_value[key] = [] - for key_cons, _cons in _consistencies: - _list_cons = [] - for _con in _cons: - if load: - _list_cons.append( - descr.impl_get_opt_by_path(_con)) - else: - _list_cons.append( - descr.impl_get_path_by_opt(_con)) - new_value[key].append((key_cons, tuple(_list_cons))) - if load: - del(self._state_consistencies) - self._consistencies = new_value - else: - self._state_consistencies = new_value - def _impl_convert_requires(self, descr, load=False): """export of the requires during the serialization process @@ -244,10 +187,7 @@ class BaseOption(object): for func in dir(self): if func.startswith('_impl_convert_'): getattr(self, func)(descr) - try: - self._state_readonly = self._readonly - except AttributeError: - pass + self._state_readonly = self._readonly def __getstate__(self, stated=True): """special method to enable the serialization with pickle @@ -267,7 +207,8 @@ class BaseOption(object): for subclass in self.__class__.__mro__: if subclass is not object: slots.update(subclass.__slots__) - slots -= frozenset(['_cache_paths', '__weakref__']) + slots -= frozenset(['_cache_paths', '_cache_consistencies', + '__weakref__']) states = {} for slot in slots: # remove variable if save variable converted @@ -326,13 +267,14 @@ class Option(BaseOption): """ __slots__ = ('_multi', '_validator', '_default_multi', '_default', '_state_callback', '_callback', '_multitype', - '_master_slaves', '__weakref__') + '_consistencies', '_warnings_only', '_master_slaves', + '_state_consistencies', '__weakref__') _empty = '' def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None): + properties=None, warnings_only=False): """ :param name: the option's name :param doc: the option's description @@ -350,6 +292,8 @@ class Option(BaseOption): validation of the value :param validator_params: the validator's parameters :param properties: tuple of default properties + :param warnings_only: _validator and _consistencies don't raise if True + Values()._warning contain message """ super(Option, self).__init__(name, doc, requires, properties) @@ -387,58 +331,70 @@ class Option(BaseOption): default = [] self._multitype = multitypes.default self._default_multi = default_multi + self._warnings_only = warnings_only self.impl_validate(default) self._default = default + self._consistencies = None - def _launch_consistency(self, func, opt, vals, context, index, opt_): + def _launch_consistency(self, func, option, value, context, index, + all_cons_opts): + """Launch consistency now + + :param func: function name, this name should start with _cons_ + :type func: `str` + :param option: option that value is changing + :type option: `tiramisu.option.Option` + :param value: new value of this option + :param context: Config's context, if None, check default value instead + :type context: `tiramisu.config.Config` + :param index: only for multi option, consistency should be launch for + specified index + :type index: `int` + :param all_cons_opts: all options concerne by this consistency + :type all_cons_opts: `list` of `tiramisu.option.Option` + """ if context is not None: descr = context.cfgimpl_get_description() - if opt is self: - #values are for self, search opt_ values - values = vals - if context is not None: - path = descr.impl_get_path_by_opt(opt_) - values_ = context._getattr(path, validate=False) - else: - values_ = opt_.impl_getdefault() - if index is not None: - #value is not already set, could be higher - try: - values_ = values_[index] - except IndexError: - values_ = None - else: - #values are for opt_, search self values - values_ = vals - if context is not None: - path = descr.impl_get_path_by_opt(self) - values = context._getattr(path, validate=False) - else: - values = self.impl_getdefault() - if index is not None: - #value is not already set, could be higher - try: - values = values[index] - except IndexError: - values = None - if index is None and self.impl_is_multi(): - for index in range(0, len(values)): - try: - value = values[index] - value_ = values_[index] - except IndexError: - value = None - value_ = None - if None not in (value, value_): - getattr(self, func)(opt_._name, value, value_) - else: - if None not in (values, values_): - getattr(self, func)(opt_._name, values, values_) + #option is also in all_cons_opts + if option not in all_cons_opts: + raise ConfigError(_('option not in all_cons_opts')) - def impl_validate(self, value, context=None, validate=True): + all_cons_vals = [] + for opt in all_cons_opts: + #get value + if option == opt: + opt_value = value + else: + #if context, calculate value, otherwise get default value + if context is not None: + opt_value = context._getattr( + descr.impl_get_path_by_opt(opt), validate=False) + else: + opt_value = opt.impl_getdefault() + + #append value + if not self.impl_is_multi() or option == opt: + all_cons_vals.append(opt_value) + else: + #value is not already set, could be higher index + try: + all_cons_vals.append(opt_value[index]) + except IndexError: + #so return if no value + return + getattr(self, func)(all_cons_opts, all_cons_vals) + + def impl_validate(self, value, context=None, validate=True, + force_index=None): """ :param value: the option's value + :param context: Config's context + :type context: :class:`tiramisu.config.Config` :param validate: if true enables ``self._validator`` validation + :type validate: boolean + :param force_no_multi: if multi, value has to be a list + not if force_no_multi is True + :type force_no_multi: boolean """ if not validate: return @@ -455,45 +411,44 @@ class Option(BaseOption): 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 + # Raise ValueError if not valid + carry_out_calculation(self._name, config=context, + callback=self._validator[0], + callback_params=validator_params) def do_validation(_value, _index=None): if _value is None: - return True - if not val_validator(_value): - raise ValueError(_("invalid value {0} " - "for option {1} for object {2}" - ).format(_value, - self._name, - self.__class__.__name__)) + return + # option validation + self._validate(_value) try: - self._validate(_value) + # valid with self._validator + val_validator(_value) + # if not context launch consistency validation + if context is not None: + descr._valid_consistency(self, _value, context, _index) + self._second_level_validation(_value) except ValueError as err: - raise ValueError(_("invalid value {0} for option {1}: {2}" - "").format(_value, self._name, err)) - if context is not None: - descr._valid_consistency(self, _value, context, _index) + msg = _("invalid value {0} for option {1}: {2}").format( + _value, self._name, err) + if self._warnings_only: + warnings.warn_explicit(ValueWarning(msg, self), + ValueWarning, + self.__class__.__name__, 0) + else: + raise ValueError(msg) # generic calculation if context is not None: descr = context.cfgimpl_get_description() - if not self._multi: - do_validation(value) + + if not self._multi or force_index is not None: + do_validation(value, force_index) else: if not isinstance(value, list): - raise ValueError(_("invalid value {0} for option {1} " - "which must be a list").format(value, + raise ValueError(_("which must be a list").format(value, self._name)) - for index in range(0, len(value)): - val = value[index] + for index, val in enumerate(value): do_validation(val, index) def impl_getdefault(self, default_multi=False): @@ -538,29 +493,45 @@ class Option(BaseOption): def impl_is_multi(self): return self._multi - def impl_add_consistency(self, func, opt): + def impl_add_consistency(self, func, *other_opts): + """Add consistency means that value will be validate with other_opts + option's values. + + :param func: function's name + :type func: `str` + :param other_opts: options used to validate value + :type other_opts: `list` of `tiramisu.option.Option` + """ if self._consistencies is None: self._consistencies = [] - if not isinstance(opt, Option): - raise ValueError('consistency must be set with an option') - if self is opt: - raise ValueError('cannot add consistency with itself') - if self.impl_is_multi() != opt.impl_is_multi(): - raise ValueError('options in consistency' - ' should be multi in two sides') + for opt in other_opts: + if not isinstance(opt, Option): + raise ConfigError(_('consistency should be set with an option')) + if self is opt: + raise ConfigError(_('cannot add consistency with itself')) + if self.impl_is_multi() != opt.impl_is_multi(): + raise ConfigError(_('every options in consistency should be ' + 'multi or none')) func = '_cons_{0}'.format(func) - self._launch_consistency(func, - self, - self.impl_getdefault(), - None, None, opt) - self._consistencies.append((func, opt)) + all_cons_opts = tuple([self] + list(other_opts)) + value = self.impl_getdefault() + if value is not None: + if self.impl_is_multi(): + for idx, val in enumerate(value): + self._launch_consistency(func, self, val, None, + idx, all_cons_opts) + else: + self._launch_consistency(func, self, value, None, + None, all_cons_opts) + self._consistencies.append((func, all_cons_opts)) self.impl_validate(self.impl_getdefault()) - def _cons_not_equal(self, optname, value, value_): - if value == value_: - raise ValueError(_("invalid value {0} for option {1} " - "must be different as {2} option" - "").format(value, self._name, optname)) + def _cons_not_equal(self, opts, vals): + for idx_inf, val_inf in enumerate(vals): + for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]): + if val_inf == val_sup is not None: + raise ValueError(_("same value for {0} and {1}").format( + opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name)) def _impl_convert_callbacks(self, descr, load=False): if not load and self._callback is None: @@ -596,6 +567,60 @@ class Option(BaseOption): else: self._state_callback = (callback, cllbck_prms) + # serialize/unserialize + def _impl_convert_consistencies(self, descr, load=False): + """during serialization process, many things have to be done. + one of them is the localisation of the options. + The paths are set once for all. + + :type descr: :class:`tiramisu.option.OptionDescription` + :param load: `True` if we are at the init of the option description + :type load: bool + """ + if not load and self._consistencies is None: + self._state_consistencies = None + elif load and self._state_consistencies is None: + self._consistencies = None + del(self._state_consistencies) + else: + if load: + consistencies = self._state_consistencies + else: + consistencies = self._consistencies + if isinstance(consistencies, list): + new_value = [] + for consistency in consistencies: + values = [] + for obj in consistency[1]: + if load: + values.append(descr.impl_get_opt_by_path(obj)) + else: + values.append(descr.impl_get_path_by_opt(obj)) + new_value.append((consistency[0], tuple(values))) + + else: + new_value = {} + for key, _consistencies in consistencies.items(): + new_value[key] = [] + for key_cons, _cons in _consistencies: + _list_cons = [] + for _con in _cons: + if load: + _list_cons.append( + descr.impl_get_opt_by_path(_con)) + else: + _list_cons.append( + descr.impl_get_path_by_opt(_con)) + new_value[key].append((key_cons, tuple(_list_cons))) + if load: + del(self._state_consistencies) + self._consistencies = new_value + else: + self._state_consistencies = new_value + + def _second_level_validation(self, value): + pass + class ChoiceOption(Option): """represents a choice out of several objects. @@ -609,7 +634,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_params=None, properties=()): + validator_params=None, properties=None, warnings_only=False): """ :param values: is a list of values the option can possibly take """ @@ -628,7 +653,8 @@ class ChoiceOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + warnings_only=warnings_only) def impl_get_values(self): return self._values @@ -705,7 +731,7 @@ class SymLinkOption(BaseOption): __slots__ = ('_name', '_opt', '_state_opt') _opt_type = 'symlink' #not return _opt consistencies - _consistencies = {} + _consistencies = None def __init__(self, name, opt): self._name = name @@ -731,23 +757,18 @@ class SymLinkOption(BaseOption): del(self._state_opt) super(SymLinkOption, self)._impl_setstate(descr) - def _impl_convert_consistencies(self, descr, load=False): - if load: - del(self._state_consistencies) - else: - self._state_consistencies = None - class IPOption(Option): "represents the choice of an ip" - __slots__ = ('_only_private', '_allow_reserved') + __slots__ = ('_private_only', '_allow_reserved') _opt_type = 'ip' def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None, only_private=False, allow_reserved=False): - self._only_private = only_private + properties=None, private_only=False, allow_reserved=False, + warnings_only=False): + self._private_only = private_only self._allow_reserved = allow_reserved super(IPOption, self).__init__(name, doc, default=default, default_multi=default_multi, @@ -757,13 +778,20 @@ class IPOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + warnings_only=warnings_only) def _validate(self, value): + try: + IP('{0}/32'.format(value)) + except ValueError: + raise ValueError(_('invalid IP {0}').format(self._name)) + + def _second_level_validation(self, value): ip = IP('{0}/32'.format(value)) if not self._allow_reserved and ip.iptype() == 'RESERVED': raise ValueError(_("IP mustn't not be in reserved class")) - if self._only_private and not ip.iptype() == 'PRIVATE': + if self._private_only and not ip.iptype() == 'PRIVATE': raise ValueError(_("IP must be in private class")) @@ -785,7 +813,7 @@ class PortOption(Option): 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): + allow_private=False, warnings_only=False): self._allow_range = allow_range self._min_value = None self._max_value = None @@ -817,7 +845,8 @@ class PortOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + warnings_only=warnings_only) def _validate(self, value): if self._allow_range and ":" in str(value): @@ -842,6 +871,12 @@ class NetworkOption(Option): _opt_type = 'network' def _validate(self, value): + try: + IP(value) + except ValueError: + raise ValueError(_('invalid network address {0}').format(self._name)) + + def _second_level_validation(self, value): ip = IP(value) if ip.iptype() == 'RESERVED': raise ValueError(_("network shall not be in reserved class")) @@ -853,18 +888,26 @@ class NetmaskOption(Option): _opt_type = 'netmask' def _validate(self, value): - IP('0.0.0.0/{0}'.format(value)) + try: + IP('0.0.0.0/{0}'.format(value)) + except ValueError: + raise ValueError(_('invalid netmask address {0}').format(self._name)) - def _cons_network_netmask(self, optname, value, value_): + def _cons_network_netmask(self, opts, vals): #opts must be (netmask, network) options - self.__cons_netmask(optname, value, value_, False) + if None in vals: + return + self.__cons_netmask(opts, vals[0], vals[1], False) - def _cons_ip_netmask(self, optname, value, value_): + def _cons_ip_netmask(self, opts, vals): #opts must be (netmask, ip) options - self.__cons_netmask(optname, value, value_, True) + if None in vals: + return + self.__cons_netmask(opts, vals[0], vals[1], True) - #def __cons_netmask(self, opt, value, context, index, opts, make_net): - def __cons_netmask(self, optname, val_netmask, val_ipnetwork, make_net): + def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net): + if len(opts) != 2: + raise ConfigError(_('invalid len for opts')) msg = None try: ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask), @@ -890,23 +933,48 @@ class NetmaskOption(Option): else: msg = _("invalid network {0} ({1}) with netmask {2} ({3})") if msg is not None: - raise ValueError(msg.format(val_ipnetwork, optname, + raise ValueError(msg.format(val_ipnetwork, opts[1]._name, val_netmask, self._name)) +class BroadcastOption(Option): + __slots__ = tuple() + _opt_type = 'broadcast' + + def _validate(self, value): + try: + IP('{0}/32'.format(value)) + except ValueError: + raise ValueError(_('invalid broadcast address {0}').format(self._name)) + + def _cons_broadcast(self, opts, vals): + if len(vals) != 3: + raise ConfigError(_('invalid len for vals')) + if None in vals: + return + broadcast, network, netmask = vals + if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast): + raise ValueError(_('invalid broadcast {0} ({1}) with network {2} ' + '({3}) and netmask {4} ({5})').format( + broadcast, opts[0]._name, network, + opts[1]._name, netmask, opts[2]._name)) + + class DomainnameOption(Option): - "represents the choice of a domain name" + """represents the choice of a domain name + netbios: for MS domain + hostname: to identify the device + domainname: + fqdn: with tld, not supported yet + """ __slots__ = ('_type', '_allow_ip') _opt_type = 'domainname' def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=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 - #domainname: - #fqdn: with tld, not supported yet + properties=None, allow_ip=False, type_='domainname', + warnings_only=False): if type_ not in ['netbios', 'hostname', 'domainname']: raise ValueError(_('unknown type_ {0} for hostname').format(type_)) self._type = type_ @@ -921,7 +989,8 @@ class DomainnameOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + warnings_only=warnings_only) def _validate(self, value): if self._allow_ip is True: @@ -959,9 +1028,9 @@ class OptionDescription(BaseOption): """ __slots__ = ('_name', '_requires', '_cache_paths', '_group_type', '_state_group_type', '_properties', '_children', - '_consistencies', '_calc_properties', '__weakref__', + '_cache_consistencies', '_calc_properties', '__weakref__', '_readonly', '_impl_informations', '_state_requires', - '_state_consistencies', '_stated', '_state_readonly') + '_stated', '_state_readonly') _opt_type = 'optiondescription' def __init__(self, name, doc, children, requires=None, properties=None): @@ -982,6 +1051,7 @@ class OptionDescription(BaseOption): old = child self._children = (tuple(child_names), tuple(children)) self._cache_paths = None + self._cache_consistencies = None # the group_type is useful for filtering OptionDescriptions in a config self._group_type = groups.default @@ -1055,12 +1125,11 @@ class OptionDescription(BaseOption): if not force_no_consistencies and \ option._consistencies is not None: for consistency in option._consistencies: - func, opt = consistency - opts = (option, opt) - _consistencies.setdefault(opt, - []).append((func, opts)) - _consistencies.setdefault(option, - []).append((func, opts)) + func, all_cons_opts = consistency + for opt in all_cons_opts: + _consistencies.setdefault(opt, + []).append((func, + all_cons_opts)) else: _currpath.append(attr) option.impl_build_cache(cache_path, @@ -1072,7 +1141,12 @@ class OptionDescription(BaseOption): if save: self._cache_paths = (tuple(cache_option), tuple(cache_path)) if not force_no_consistencies: - self._consistencies = _consistencies + if _consistencies != {}: + self._cache_consistencies = {} + for opt, cons in _consistencies.items(): + if opt not in cache_option: + raise ConfigError(_('consistency with option {0} which is not in Config').format(opt._name)) + self._cache_consistencies[opt] = tuple(cons) self._readonly = True def impl_get_opt_by_path(self, path): @@ -1143,17 +1217,18 @@ class OptionDescription(BaseOption): def impl_get_group_type(self): return self._group_type - def _valid_consistency(self, opt, value, context=None, index=None): - consistencies = self._consistencies.get(opt) + def _valid_consistency(self, option, value, context, index): + if self._cache_consistencies is None: + return True + #consistencies is something like [('_cons_not_equal', (opt1, opt2))] + consistencies = self._cache_consistencies.get(option) if consistencies is not None: - for consistency in consistencies: - opt_ = consistency[1] - ret = opt_[0]._launch_consistency(consistency[0], - opt, - value, - context, - index, - opt_[1]) + for func, all_cons_opts in consistencies: + #all_cons_opts[0] is the option where func is set + ret = all_cons_opts[0]._launch_consistency(func, option, + value, + context, index, + all_cons_opts) if ret is False: return False return True @@ -1193,6 +1268,7 @@ class OptionDescription(BaseOption): """ if descr is None: self._cache_paths = None + self._cache_consistencies = None self.impl_build_cache(force_no_consistencies=True) descr = self self._group_type = getattr(groups, self._state_group_type) @@ -1324,6 +1400,6 @@ def validate_callback(callback, callback_params, type_): ).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' + ' not a {0} for second argument' ).format(type_, type( force_permissive))) diff --git a/tiramisu/value.py b/tiramisu/value.py index f1833fb..7fd34db 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -104,7 +104,8 @@ class Values(object): path = self._get_opt_path(opt) if self._p_.hasvalue(path): setting = self.context().cfgimpl_get_settings() - opt.impl_validate(opt.impl_getdefault(), self.context(), + opt.impl_validate(opt.impl_getdefault(), + self.context(), 'validator' in setting) self.context().cfgimpl_reset_cache() if (opt.impl_is_multi() and @@ -237,7 +238,7 @@ class Values(object): force_properties=force_properties, force_permissives=force_permissives) if config_error is not None: - raise ConfigError(config_error) + raise config_error return value def __setitem__(self, opt, value): @@ -368,6 +369,7 @@ class Values(object): def __setstate__(self, states): self._p_ = states['_p_'] + # ____________________________________________________________ # multi types @@ -451,10 +453,10 @@ class Multi(list): value_slave.append(slave.impl_getdefault_multi(), force=True) - def __setitem__(self, key, value): - self._validate(value) + def __setitem__(self, index, value): + self._validate(value, index) #assume not checking mandatory property - super(Multi, self).__setitem__(key, value) + super(Multi, self).__setitem__(index, value) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) def append(self, value, force=False): @@ -472,15 +474,17 @@ class Multi(list): #Force None il return a list if isinstance(value, list): value = None - self._validate(value) + index = self.__len__() + self._validate(value, index) super(Multi, self).append(value) - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force) + self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, + self, + validate_properties=not force) if not force and self.opt.impl_get_multitype() == multitypes.master: for slave in self.opt.impl_get_master_slaves(): path = values._get_opt_path(slave) if not values._is_default_owner(path): if slave.impl_has_callback(): - index = self.__len__() - 1 dvalue = values._getcallback_value(slave, index=index) else: dvalue = slave.impl_getdefault_multi() @@ -532,10 +536,11 @@ class Multi(list): super(Multi, self).extend(iterable) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) - def _validate(self, value): + def _validate(self, value, force_index): if value is not None: try: - self.opt._validate(value) + self.opt.impl_validate(value, context=self.context(), + force_index=force_index) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}" diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 8bceb43..0075059 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-08-31 09:52+CEST\n" +"POT-Creation-Date: 2013-09-26 22:33+CEST\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: LANGUAGE \n" @@ -11,436 +11,486 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" -#: tiramisu/autolib.py:58 -msgid "no config specified but needed" -msgstr "aucune config spécifié alors que c'est nécessaire" - -#: tiramisu/autolib.py:65 +#: tiramisu/autolib.py:145 msgid "" "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" "impossible d'effectuer le calcul, l'option {0} a les propriétés : {1} pour : " "{2}" -#: tiramisu/autolib.py:74 +#: tiramisu/autolib.py:154 msgid "" "unable to carry out a calculation, option value with multi types must have " "same length for: {0}" msgstr "" -"impossible d'effectuer le calcul, valeur d'un option avec le type multi doit " -"avoir la même longueur pour : {0}" +"impossible d'effectuer le calcul, la valeur d'une option avec le type multi " +"doit avoir la même longueur pour : {0}" -#: tiramisu/config.py:47 +#: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "descr doit être une optiondescription pas un {0}" -#: tiramisu/config.py:121 +#: tiramisu/config.py:127 msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:157 +#: tiramisu/config.py:163 msgid "" "no option description found for this config (may be metaconfig without meta)" msgstr "" -"pas d'option description pour cette config (peut être une metaconfig sans " -"meta)" +"pas d'option description trouvé pour cette config (peut être une metaconfig " +"sans meta)" -#: tiramisu/config.py:311 +#: tiramisu/config.py:189 +msgid "can't assign to an OptionDescription" +msgstr "ne peut pas attribuer une valeur à une OptionDescription" + +#: tiramisu/config.py:320 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:350 +#: tiramisu/config.py:359 msgid "no option found in config with these criteria" msgstr "aucune option trouvée dans la config avec ces critères" -#: tiramisu/config.py:400 +#: tiramisu/config.py:409 msgid "make_dict can't filtering with value without option" msgstr "make_dict ne peut filtrer sur une valeur mais sans option" -#: tiramisu/config.py:421 +#: tiramisu/config.py:430 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:481 +#: tiramisu/config.py:490 msgid "opt in getowner must be an option not {0}" msgstr "opt dans getowner doit être une option pas {0}" -#: tiramisu/option.py:71 -msgid "{0} has no attribute impl_set_information" -msgstr "{0} n'a pas d'attribut impl_set_information" - -#: tiramisu/option.py:86 -msgid "information's item not found: {0}" -msgstr "aucune config spécifié alors que c'est nécessaire" - -#: tiramisu/option.py:89 -msgid "{0} has no attribute impl_get_information" -msgstr "{0} n'a pas d'attribut impl_get_information" - -#: tiramisu/option.py:117 -msgid "'{0}' ({1}) object attribute '{2}' is read-only" -msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seul" - -#: tiramisu/option.py:159 +#: tiramisu/option.py:70 msgid "invalid name: {0} for option" msgstr "nom invalide : {0} pour l'option" -#: tiramisu/option.py:169 -msgid "validator must be a function" -msgstr "validator doit être une fonction" +#: tiramisu/option.py:80 +msgid "invalid properties type {0} for {1}, must be a tuple" +msgstr "type des properties invalide {0} pour {1}, doit être un tuple" -#: tiramisu/option.py:176 +#: tiramisu/option.py:122 +msgid "'{0}' ({1}) object attribute '{2}' is read-only" +msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule" + +#: tiramisu/option.py:149 tiramisu/value.py:362 +msgid "information's item not found: {0}" +msgstr "aucune config spécifié alors que c'est nécessaire" + +#: tiramisu/option.py:266 +msgid "cannot serialize Option, only in OptionDescription" +msgstr "ne peut serialiser une Option, seulement via une OptionDescription" + +#: tiramisu/option.py:367 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -"une default_multi est renseigné alors que multi est False dans l'option : {0}" +"une default_multi est renseignée alors que multi est False dans l'option : " +"{0}" -#: tiramisu/option.py:182 +#: tiramisu/option.py:373 msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}" -#: tiramisu/option.py:187 +#: tiramisu/option.py:378 msgid "default value not allowed if option: {0} is calculated" -msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculé" +msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculée" -#: tiramisu/option.py:190 +#: tiramisu/option.py:381 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" msgstr "" -"params définit pour une fonction callback mais par de callback défini encore " -"pour l'option {0}" +"params définis pour une fonction callback mais par de callback encore " +"définis pour l'option {0}" -#: tiramisu/option.py:212 tiramisu/option.py:753 -msgid "invalid properties type {0} for {1}, must be a tuple" -msgstr "type des properties invalide {0} pour {1}, doit être un tuple" - -#: tiramisu/option.py:285 -msgid "invalid value {0} for option {1} for object {2}" -msgstr "valeur invalide {0} pour l'option {1} pour l'objet {2}" - -#: tiramisu/option.py:293 tiramisu/value.py:468 +#: tiramisu/option.py:486 tiramisu/value.py:547 msgid "invalid value {0} for option {1}: {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}" -#: tiramisu/option.py:305 +#: tiramisu/option.py:506 msgid "invalid value {0} for option {1} which must be a list" msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste" -#: tiramisu/option.py:374 +#: tiramisu/option.py:577 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" -"valeur invalide {0} pour l'option {1} doit être différent que l'option {2}" +"valeur invalide {0} pour l'option {1} doit être différente de l'option {2}" -#: tiramisu/option.py:396 +#: tiramisu/option.py:636 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:399 +#: tiramisu/option.py:639 msgid "open_values must be a boolean for {0}" msgstr "open_values doit être un booléen pour {0}" -#: tiramisu/option.py:420 +#: tiramisu/option.py:661 msgid "value {0} is not permitted, only {1} is allowed" -msgstr "valeur {0} n'est pas permit, seules {1} sont autorisées" +msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées" -#: tiramisu/option.py:432 +#: tiramisu/option.py:673 msgid "value must be a boolean" msgstr "valeur doit être un booléen" -#: tiramisu/option.py:442 +#: tiramisu/option.py:683 msgid "value must be an integer" -msgstr "valeur doit être un numbre" +msgstr "valeur doit être un nombre entier" -#: tiramisu/option.py:452 +#: tiramisu/option.py:693 msgid "value must be a float" msgstr "valeur doit être un nombre flottant" -#: tiramisu/option.py:462 +#: tiramisu/option.py:703 msgid "value must be a string, not {0}" msgstr "valeur doit être une chaîne, pas {0}" -#: tiramisu/option.py:480 +#: tiramisu/option.py:721 msgid "value must be an unicode" msgstr "valeur doit être une valeur unicode" -#: tiramisu/option.py:490 +#: tiramisu/option.py:733 msgid "malformed symlinkoption must be an option for symlink {0}" -msgstr "symlinkoption mal formé doit être une option pour symlink {0}" +msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" -#: tiramisu/option.py:526 -msgid "IP shall not be in reserved class" +#: tiramisu/option.py:788 +msgid "invalid IP {0}" +msgstr "adresse IP invalide {0}" + +#: tiramisu/option.py:793 +msgid "IP mustn't not be in reserved class" msgstr "IP ne doit pas être d'une classe reservée" -#: tiramisu/option.py:528 +#: tiramisu/option.py:795 msgid "IP must be in private class" msgstr "IP doit être dans la classe privée" -#: tiramisu/option.py:566 +#: tiramisu/option.py:833 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:571 +#: tiramisu/option.py:838 msgid "max value is empty" -msgstr "valeur maximum est vide" +msgstr "la valeur maximum est vide" -#: tiramisu/option.py:608 +#: tiramisu/option.py:877 +msgid "invalid network address {0}" +msgstr "adresse réseau invalide {0}" + +#: tiramisu/option.py:882 msgid "network shall not be in reserved class" -msgstr "réseau ne doit pas être dans la classe reservée" +msgstr "le réseau ne doit pas être dans la classe reservée" -#: tiramisu/option.py:640 +#: tiramisu/option.py:894 +msgid "invalid netmask address {0}" +msgstr "masque de sous-réseau invalide {0}" + +#: tiramisu/option.py:916 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3}), ce réseau est une IP" -#: tiramisu/option.py:645 +#: tiramisu/option.py:921 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "IP invalide {0} ({1}) avec masque {2} ({3}), cette IP est un réseau" -#: tiramisu/option.py:650 +#: tiramisu/option.py:926 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "IP invalide {0} ({1}) avec masque {2} ({3})" -#: tiramisu/option.py:652 +#: tiramisu/option.py:928 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3})" -#: tiramisu/option.py:672 +#: tiramisu/option.py:939 +msgid "invalid broadcast address {0}" +msgstr "adresse de broadcast invalide {0}" + +#: tiramisu/option.py:957 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:675 +#: tiramisu/option.py:960 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:704 +#: tiramisu/option.py:990 msgid "invalid value for {0}, must have dot" msgstr "valeur invalide pour {0}, doit avoir un point" -#: tiramisu/option.py:707 +#: tiramisu/option.py:993 msgid "invalid domainname's length for {0} (max {1})" msgstr "longueur du nom de domaine invalide pour {0} (maximum {1})" -#: tiramisu/option.py:710 +#: tiramisu/option.py:996 msgid "invalid domainname's length for {0} (min 2)" msgstr "longueur du nom de domaine invalide pour {0} (minimum 2)" -#: tiramisu/option.py:714 +#: tiramisu/option.py:1000 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:731 -msgid "invalid name: {0} for optiondescription" -msgstr "nom invalide : {0} pour l'optiondescription" - -#: tiramisu/option.py:743 +#: tiramisu/option.py:1027 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:769 +#: tiramisu/option.py:1044 msgid "unknown Option {0} in OptionDescription {1}" -msgstr "Option {} inconnue pour l'OptionDescription{}" +msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:820 +#: tiramisu/option.py:1095 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:850 +#: tiramisu/option.py:1129 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:856 +#: tiramisu/option.py:1135 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:866 +#: tiramisu/option.py:1145 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})" -#: tiramisu/option.py:879 +#: tiramisu/option.py:1158 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:882 +#: tiramisu/option.py:1161 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:885 +#: tiramisu/option.py:1164 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" "option non autorisée {0} dans le groupe {1} : cette option n'est pas une " "multi" -#: tiramisu/option.py:896 +#: tiramisu/option.py:1175 msgid "master group with wrong master name for {0}" -msgstr "le groupe maître avec un nom de maître éroné pour {0}" +msgstr "le groupe maître avec un nom de maître érroné pour {0}" -#: tiramisu/option.py:905 +#: tiramisu/option.py:1184 msgid "no child has same nom has master group for: {0}" msgstr "pas d'enfant avec le nom du groupe maître pour {0} " -#: tiramisu/option.py:908 +#: tiramisu/option.py:1187 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:946 +#: tiramisu/option.py:1277 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" "type requirements malformé pour l'option : {0}, doit être un dictionnaire" -#: tiramisu/option.py:962 +#: tiramisu/option.py:1294 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " -"option, exptected et action" +"option, expected et action" -#: tiramisu/option.py:967 +#: tiramisu/option.py:1299 msgid "malformed requirements for option: {0} inverse must be boolean" -msgstr "requirements malformé pour l'option : {0} inverse doit être un booléen" +msgstr "" +"requirements mal formés pour l'option : {0} inverse doit être un booléen" -#: tiramisu/option.py:971 +#: tiramisu/option.py:1303 msgid "malformed requirements for option: {0} transitive must be boolean" -msgstr "requirements malformé pour l'option : {0} transitive doit être booléen" +msgstr "" +"requirements mal formés pour l'option : {0} transitive doit être booléen" -#: tiramisu/option.py:975 +#: tiramisu/option.py:1307 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -"requirements malformé pour l'option : {0} same_action doit être un booléen" +"requirements mal formés pour l'option : {0} same_action doit être un booléen" -#: tiramisu/option.py:979 +#: tiramisu/option.py:1311 msgid "malformed requirements must be an option in option {0}" -msgstr "requirements malformé doit être une option dans l'option {0}" +msgstr "requirements mal formés doit être une option dans l'option {0}" -#: tiramisu/option.py:982 +#: tiramisu/option.py:1314 msgid "malformed requirements option {0} should not be a multi" -msgstr "requirements malformé l'option {0} ne doit pas être une multi" +msgstr "requirements mal formés l'option {0} ne doit pas être une multi" -#: tiramisu/option.py:988 +#: tiramisu/option.py:1320 msgid "" "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -"requirements malformé deuxième argument doit être valide pour l'option {0} : " -"{1}" +"requirements mal formés deuxième argument doit être valide pour l'option " +"{0} : {1}" -#: tiramisu/option.py:993 +#: tiramisu/option.py:1325 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "incohérence dans les types action pour l'option : {0} action {1}" -#: tiramisu/setting.py:47 -msgid "storage_type is already set, cannot rebind it" -msgstr "storage_type est déjà défini, impossible de le redéfinir" +#: tiramisu/option.py:1350 +msgid "{0} should be a function" +msgstr "{0} doit être une fonction" -#: tiramisu/setting.py:67 +#: tiramisu/option.py:1353 +msgid "{0}_params should be a dict" +msgstr "{0}_params devrait être un dict" + +#: tiramisu/option.py:1356 +msgid "{0}_params with key {1} should not have length different to 1" +msgstr "" +"{0}_params avec la clef {1} devrait ne pas avoir une longueur différent de 1" + +#: tiramisu/option.py:1360 +msgid "{0}_params should be tuple for key \"{1}\"" +msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" + +#: tiramisu/option.py:1366 +msgid "validator not support tuple" +msgstr "validator n'accepte pas de tuple" + +#: tiramisu/option.py:1369 +msgid "{0}_params should have an option not a {0} for first argument" +msgstr "{0}_params devrait avoir une option pas un {0} pour premier argument" + +#: tiramisu/option.py:1373 +msgid "{0}_params should have a boolean not a {0} for second argument" +msgstr "{0}_params devrait avoir un boolean pas un {0} pour second argument" + +#: tiramisu/setting.py:116 msgid "can't rebind {0}" msgstr "ne peut redéfinir ({0})" -#: tiramisu/setting.py:72 +#: tiramisu/setting.py:121 msgid "can't unbind {0}" msgstr "ne peut supprimer ({0})" -#: tiramisu/setting.py:185 +#: tiramisu/setting.py:259 msgid "cannot append {0} property for option {1}: this property is calculated" msgstr "" "ne peut ajouter la propriété {0} dans l'option {1}: cette propriété est " "calculée" -#: tiramisu/setting.py:215 -msgid "option {0} not already exists in storage {1}" -msgstr "option {0} n'existe pas dans l'espace de stockage {1}" - -#: tiramisu/setting.py:282 +#: tiramisu/setting.py:322 msgid "opt and all_properties must not be set together in reset" msgstr "opt et all_properties ne doit pas être renseigné ensemble dans reset" -#: tiramisu/setting.py:297 +#: tiramisu/setting.py:337 msgid "if opt is not None, path should not be None in _getproperties" msgstr "" "si opt n'est pas None, path devrait ne pas être à None dans _getproperties" -#: tiramisu/setting.py:391 +#: tiramisu/setting.py:440 msgid "cannot change the value for option {0} this option is frozen" msgstr "" -"ne peut modifié la valeur de l'option {0} cette option n'est pas modifiable" +"ne peut modifier la valeur de l'option {0} cette option n'est pas modifiable" -#: tiramisu/setting.py:397 +#: tiramisu/setting.py:446 msgid "trying to access to an option named: {0} with properties {1}" msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1}" -#: tiramisu/setting.py:415 +#: tiramisu/setting.py:464 msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:422 tiramisu/value.py:277 +#: tiramisu/setting.py:471 tiramisu/value.py:301 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" -#: tiramisu/setting.py:503 +#: tiramisu/setting.py:558 msgid "" "malformed requirements imbrication detected for option: '{0}' with " "requirement on: '{1}'" msgstr "" -"imbrication de requirements malformé detectée pour l'option : '{0}' avec " +"imbrication de requirements mal formés detectée pour l'option : '{0}' avec " "requirement sur : '{1}'" -#: tiramisu/setting.py:515 +#: tiramisu/setting.py:570 msgid "option '{0}' has requirement's property error: {1} {2}" msgstr "l'option '{0}' a une erreur de propriété pour le requirement : {1} {2}" -#: tiramisu/storage/dictionary/storage.py:37 +#: tiramisu/storage/__init__.py:52 +msgid "storage_type is already set, cannot rebind it" +msgstr "storage_type est déjà défini, impossible de le redéfinir" + +#: tiramisu/storage/__init__.py:86 +msgid "option {0} not already exists in storage {1}" +msgstr "option {0} n'existe pas dans l'espace de stockage {1}" + +#: tiramisu/storage/dictionary/storage.py:39 msgid "dictionary storage cannot delete session" msgstr "" "impossible de supprimer une session dans un espace de stockage dictionary" -#: tiramisu/storage/dictionary/storage.py:46 +#: tiramisu/storage/dictionary/storage.py:50 msgid "session already used" msgstr "session déjà utilisée" -#: tiramisu/storage/dictionary/storage.py:48 +#: tiramisu/storage/dictionary/storage.py:52 msgid "a dictionary cannot be persistent" msgstr "un espace de stockage dictionary ne peut être persistant" -#: tiramisu/value.py:284 +#: tiramisu/value.py:308 msgid "no value for {0} cannot change owner to {1}" msgstr "pas de valeur pour {0} ne peut changer d'utilisateur pour {1}" -#: tiramisu/value.py:356 +#: tiramisu/value.py:416 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "longueur invalide pour une esclave : {0} qui a {1} comme maître" -#: tiramisu/value.py:373 +#: tiramisu/value.py:440 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" "longueur invalide pour un maître : {0} qui a {1} une esclave avec une plus " "grande longueur" -#: tiramisu/value.py:394 +#: tiramisu/value.py:470 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "ne peut ajouter une valeur sur l'option multi {0} qui est une esclave" -#: tiramisu/value.py:429 +#: tiramisu/value.py:507 msgid "cannot sort multi option {0} if master or slave" msgstr "ne peut trier une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:433 +#: tiramisu/value.py:511 msgid "cmp is not permitted in python v3 or greater" msgstr "cmp n'est pas permis en python v3 ou supérieure" -#: tiramisu/value.py:442 +#: tiramisu/value.py:520 msgid "cannot reverse multi option {0} if master or slave" msgstr "ne peut inverser une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:450 +#: tiramisu/value.py:528 msgid "cannot insert multi option {0} if master or slave" msgstr "ne peut insérer une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:458 +#: tiramisu/value.py:536 msgid "cannot extend multi option {0} if master or slave" msgstr "ne peut étendre une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:482 +#: tiramisu/value.py:564 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave" +#~ msgid "validator should return a boolean, not {0}" +#~ msgstr "le validator devrait retourner un boolean, pas un {0}" + +#~ msgid "invalid value {0} for option {1} for object {2}" +#~ msgstr "valeur invalide {0} pour l'option {1} pour l'objet {2}" + +#~ msgid "no config specified but needed" +#~ msgstr "aucune config spécifié alors que c'est nécessaire" + +#~ msgid "{0} has no attribute impl_set_information" +#~ msgstr "{0} n'a pas d'attribut impl_set_information" + +#~ msgid "{0} has no attribute impl_get_information" +#~ msgstr "{0} n'a pas d'attribut impl_get_information" + +#~ msgid "invalid name: {0} for optiondescription" +#~ msgstr "nom invalide : {0} pour l'optiondescription" + #~ msgid "metaconfig's children must be a list" #~ msgstr "enfants d'une metaconfig doit être une liste" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index ef40426..dfd4b86 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2013-09-02 11:30+CEST\n" +"POT-Creation-Date: 2013-09-26 22:33+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,395 +15,423 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/autolib.py:58 -msgid "no config specified but needed" -msgstr "" - -#: tiramisu/autolib.py:65 +#: tiramisu/autolib.py:145 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" -#: tiramisu/autolib.py:74 +#: tiramisu/autolib.py:154 msgid "unable to carry out a calculation, option value with multi types must have same length for: {0}" msgstr "" -#: tiramisu/config.py:47 +#: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "" -#: tiramisu/config.py:121 +#: tiramisu/config.py:127 msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:157 +#: tiramisu/config.py:163 msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:311 +#: tiramisu/config.py:189 +msgid "can't assign to an OptionDescription" +msgstr "" + +#: tiramisu/config.py:320 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:350 +#: tiramisu/config.py:359 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:400 +#: tiramisu/config.py:409 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:421 +#: tiramisu/config.py:430 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:481 +#: tiramisu/config.py:490 msgid "opt in getowner must be an option not {0}" msgstr "" -#: tiramisu/option.py:71 -msgid "{0} has no attribute impl_set_information" -msgstr "" - -#: tiramisu/option.py:86 -msgid "information's item not found: {0}" -msgstr "" - -#: tiramisu/option.py:89 -msgid "{0} has no attribute impl_get_information" -msgstr "" - -#: tiramisu/option.py:117 -msgid "'{0}' ({1}) object attribute '{2}' is read-only" -msgstr "" - -#: tiramisu/option.py:208 +#: tiramisu/option.py:70 msgid "invalid name: {0} for option" msgstr "" -#: tiramisu/option.py:218 -msgid "validator must be a function" -msgstr "" - -#: tiramisu/option.py:225 -msgid "a default_multi is set whereas multi is False in option: {0}" -msgstr "" - -#: tiramisu/option.py:231 -msgid "invalid default_multi value {0} for option {1}: {2}" -msgstr "" - -#: tiramisu/option.py:236 -msgid "default value not allowed if option: {0} is calculated" -msgstr "" - -#: tiramisu/option.py:239 -msgid "params defined for a callback function but no callback defined yet for option {0}" -msgstr "" - -#: tiramisu/option.py:261 tiramisu/option.py:809 +#: tiramisu/option.py:80 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "" -#: tiramisu/option.py:334 -msgid "invalid value {0} for option {1} for object {2}" +#: tiramisu/option.py:122 +msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option.py:342 tiramisu/value.py:468 +#: tiramisu/option.py:149 tiramisu/value.py:362 +msgid "information's item not found: {0}" +msgstr "" + +#: tiramisu/option.py:266 +msgid "cannot serialize Option, only in OptionDescription" +msgstr "" + +#: tiramisu/option.py:367 +msgid "a default_multi is set whereas multi is False in option: {0}" +msgstr "" + +#: tiramisu/option.py:373 +msgid "invalid default_multi value {0} for option {1}: {2}" +msgstr "" + +#: tiramisu/option.py:378 +msgid "default value not allowed if option: {0} is calculated" +msgstr "" + +#: tiramisu/option.py:381 +msgid "params defined for a callback function but no callback defined yet for option {0}" +msgstr "" + +#: tiramisu/option.py:486 tiramisu/value.py:547 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:354 +#: tiramisu/option.py:506 msgid "invalid value {0} for option {1} which must be a list" msgstr "" -#: tiramisu/option.py:423 +#: tiramisu/option.py:577 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" -#: tiramisu/option.py:445 +#: tiramisu/option.py:636 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:448 +#: tiramisu/option.py:639 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:469 +#: tiramisu/option.py:661 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:481 +#: tiramisu/option.py:673 msgid "value must be a boolean" msgstr "" -#: tiramisu/option.py:491 +#: tiramisu/option.py:683 msgid "value must be an integer" msgstr "" -#: tiramisu/option.py:501 +#: tiramisu/option.py:693 msgid "value must be a float" msgstr "" -#: tiramisu/option.py:511 +#: tiramisu/option.py:703 msgid "value must be a string, not {0}" msgstr "" -#: tiramisu/option.py:529 +#: tiramisu/option.py:721 msgid "value must be an unicode" msgstr "" -#: tiramisu/option.py:539 +#: tiramisu/option.py:733 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option.py:581 -msgid "IP shall not be in reserved class" +#: tiramisu/option.py:788 +msgid "invalid IP {0}" msgstr "" -#: tiramisu/option.py:583 +#: tiramisu/option.py:793 +msgid "IP mustn't not be in reserved class" +msgstr "" + +#: tiramisu/option.py:795 msgid "IP must be in private class" msgstr "" -#: tiramisu/option.py:621 +#: tiramisu/option.py:833 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option.py:626 +#: tiramisu/option.py:838 msgid "max value is empty" msgstr "" -#: tiramisu/option.py:663 +#: tiramisu/option.py:877 +msgid "invalid network address {0}" +msgstr "" + +#: tiramisu/option.py:882 msgid "network shall not be in reserved class" msgstr "" -#: tiramisu/option.py:695 +#: tiramisu/option.py:894 +msgid "invalid netmask address {0}" +msgstr "" + +#: tiramisu/option.py:916 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "" -#: tiramisu/option.py:700 +#: tiramisu/option.py:921 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "" -#: tiramisu/option.py:705 +#: tiramisu/option.py:926 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:707 +#: tiramisu/option.py:928 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:727 +#: tiramisu/option.py:939 +msgid "invalid broadcast address {0}" +msgstr "" + +#: tiramisu/option.py:957 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:730 +#: tiramisu/option.py:960 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:759 +#: tiramisu/option.py:990 msgid "invalid value for {0}, must have dot" msgstr "" -#: tiramisu/option.py:762 +#: tiramisu/option.py:993 msgid "invalid domainname's length for {0} (max {1})" msgstr "" -#: tiramisu/option.py:765 +#: tiramisu/option.py:996 msgid "invalid domainname's length for {0} (min 2)" msgstr "" -#: tiramisu/option.py:769 +#: tiramisu/option.py:1000 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:787 -msgid "invalid name: {0} for optiondescription" -msgstr "" - -#: tiramisu/option.py:799 +#: tiramisu/option.py:1027 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:825 +#: tiramisu/option.py:1044 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:874 +#: tiramisu/option.py:1095 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:904 +#: tiramisu/option.py:1129 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:910 +#: tiramisu/option.py:1135 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:920 +#: tiramisu/option.py:1145 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:933 +#: tiramisu/option.py:1158 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:936 +#: tiramisu/option.py:1161 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:939 +#: tiramisu/option.py:1164 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:950 +#: tiramisu/option.py:1175 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:959 +#: tiramisu/option.py:1184 msgid "no child has same nom has master group for: {0}" msgstr "" -#: tiramisu/option.py:962 +#: tiramisu/option.py:1187 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1021 +#: tiramisu/option.py:1277 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1037 +#: tiramisu/option.py:1294 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1042 +#: tiramisu/option.py:1299 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1046 +#: tiramisu/option.py:1303 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1050 +#: tiramisu/option.py:1307 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1054 +#: tiramisu/option.py:1311 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1057 +#: tiramisu/option.py:1314 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1063 +#: tiramisu/option.py:1320 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1068 +#: tiramisu/option.py:1325 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "" -#: tiramisu/setting.py:47 -msgid "storage_type is already set, cannot rebind it" +#: tiramisu/option.py:1350 +msgid "{0} should be a function" msgstr "" -#: tiramisu/setting.py:67 +#: tiramisu/option.py:1353 +msgid "{0}_params should be a dict" +msgstr "" + +#: tiramisu/option.py:1356 +msgid "{0}_params with key {1} should not have length different to 1" +msgstr "" + +#: tiramisu/option.py:1360 +msgid "{0}_params should be tuple for key \"{1}\"" +msgstr "" + +#: tiramisu/option.py:1366 +msgid "validator not support tuple" +msgstr "" + +#: tiramisu/option.py:1369 +msgid "{0}_params should have an option not a {0} for first argument" +msgstr "" + +#: tiramisu/option.py:1373 +msgid "{0}_params should have a boolean not a {0} for second argument" +msgstr "" + +#: tiramisu/setting.py:116 msgid "can't rebind {0}" msgstr "" -#: tiramisu/setting.py:72 +#: tiramisu/setting.py:121 msgid "can't unbind {0}" msgstr "" -#: tiramisu/setting.py:185 +#: tiramisu/setting.py:259 msgid "cannot append {0} property for option {1}: this property is calculated" msgstr "" -#: tiramisu/setting.py:215 -msgid "option {0} not already exists in storage {1}" -msgstr "" - -#: tiramisu/setting.py:282 +#: tiramisu/setting.py:322 msgid "opt and all_properties must not be set together in reset" msgstr "" -#: tiramisu/setting.py:297 +#: tiramisu/setting.py:337 msgid "if opt is not None, path should not be None in _getproperties" msgstr "" -#: tiramisu/setting.py:391 +#: tiramisu/setting.py:440 msgid "cannot change the value for option {0} this option is frozen" msgstr "" -#: tiramisu/setting.py:397 +#: tiramisu/setting.py:446 msgid "trying to access to an option named: {0} with properties {1}" msgstr "" -#: tiramisu/setting.py:415 +#: tiramisu/setting.py:464 msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:422 tiramisu/value.py:277 +#: tiramisu/setting.py:471 tiramisu/value.py:301 msgid "invalid generic owner {0}" msgstr "" -#: tiramisu/setting.py:503 +#: tiramisu/setting.py:558 msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" msgstr "" -#: tiramisu/setting.py:515 +#: tiramisu/setting.py:570 msgid "option '{0}' has requirement's property error: {1} {2}" msgstr "" -#: tiramisu/storage/dictionary/storage.py:37 +#: tiramisu/storage/__init__.py:52 +msgid "storage_type is already set, cannot rebind it" +msgstr "" + +#: tiramisu/storage/__init__.py:86 +msgid "option {0} not already exists in storage {1}" +msgstr "" + +#: tiramisu/storage/dictionary/storage.py:39 msgid "dictionary storage cannot delete session" msgstr "" -#: tiramisu/storage/dictionary/storage.py:46 +#: tiramisu/storage/dictionary/storage.py:50 msgid "session already used" msgstr "" -#: tiramisu/storage/dictionary/storage.py:48 +#: tiramisu/storage/dictionary/storage.py:52 msgid "a dictionary cannot be persistent" msgstr "" -#: tiramisu/value.py:284 +#: tiramisu/value.py:308 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:356 +#: tiramisu/value.py:416 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:373 +#: tiramisu/value.py:440 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" -#: tiramisu/value.py:394 +#: tiramisu/value.py:470 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:429 +#: tiramisu/value.py:507 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:433 +#: tiramisu/value.py:511 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:442 +#: tiramisu/value.py:520 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:450 +#: tiramisu/value.py:528 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:458 +#: tiramisu/value.py:536 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:482 +#: tiramisu/value.py:564 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr ""