From e929745ebf91987a7f3e79f7a1e79b20f905ec6e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 16 Sep 2013 14:02:55 +0200 Subject: [PATCH 01/36] corrections in setup.py --- setup.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/setup.py b/setup.py index 1b73eea..1e67a75 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- from distutils.core import setup -from os.path import dirname, abspath, join, normpath, isdir, basename +from os.path import dirname, abspath, join, normpath, isdir from os import listdir @@ -9,15 +9,16 @@ def fetch_version(): """Get version from version.in""" return file('VERSION', 'r').readline().strip() + def return_storages(): "returns all the storage plugins that are living in tiramisu/storage" here = dirname(abspath(__file__)) storages_path = normpath(join(here, 'tiramisu', 'storage')) - dir_content = [ content for content in listdir(storages_path) \ - if not content =='__pycache__'] - storages = filter(isdir, [join(storages_path, content) \ + dir_content = [content for content in listdir(storages_path) + if not content == '__pycache__'] + storages = filter(isdir, [join(storages_path, content) for content in dir_content]) - storage_list = [basename(storage) for storage in storages] + storage_list = ['.'.join(storage.split('/')[-3:]) for storage in storages] return storage_list packages = ['tiramisu', 'tiramisu.storage'] @@ -58,6 +59,6 @@ producing flexible and fast options access. This version requires Python 2.6 or later. -""" +""", packages=packages ) From 1249e1e2bc72854d455bc181a740ec5141149bef Mon Sep 17 00:00:00 2001 From: Daniel Dehennin Date: Mon, 16 Sep 2013 14:41:12 +0200 Subject: [PATCH 02/36] Incorrect filename for gettext catablog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Makefile: Do not use basename option “-s” as it's not compatible with, at least, Ubuntu Precise Pangolin. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ca436a5..8579680 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ endef define build_translation if [ -d ${1} ]; then \ for f in `find ${1} -name "*.po"`; do \ - msgfmt -o `dirname $$f`/`basename -s ".po" $$f`.mo $$f || true; \ + msgfmt -o `dirname $$f`/`basename $$f .po`.mo $$f || true; \ done; \ fi endef From 3ffbb4ab8fb06e597521a5be0a493c895f46e6ef Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 16 Sep 2013 14:43:58 +0200 Subject: [PATCH 03/36] Makefile for non-gnu version of basename --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ca436a5..9ead4ab 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ endef define build_translation if [ -d ${1} ]; then \ for f in `find ${1} -name "*.po"`; do \ - msgfmt -o `dirname $$f`/`basename -s ".po" $$f`.mo $$f || true; \ + msgfmt -o `dirname $$f`/`basename $$f ".po"`.mo $$f || true; \ done; \ fi endef From 9ddf100118d913c2f4225cca1adb65317b512d84 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 16 Sep 2013 15:02:14 +0200 Subject: [PATCH 04/36] when we get an option's value, we need it's values to calculate properties (ie for mandatory's option) if a disabled option has a callback to an other disabled value, it's raise ConfigError now only raise if option has no other propertiesError --- test/test_option_calculation.py | 11 +++++++++++ tiramisu/value.py | 17 ++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 0266855..d57072f 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -498,3 +498,14 @@ def test_callback_hidden(): cfg.read_write() raises(PropertiesOptionError, 'cfg.od1.opt1') cfg.od2.opt2 + + +def test_callback_disable_make_dict(): + opt1 = BoolOption('opt1', '', properties=('disabled',)) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('disabled',)) + od1 = OptionDescription('od1', '', [opt1]) + od2 = OptionDescription('od2', '', [opt2]) + maconfig = OptionDescription('rootconfig', '', [od1, od2]) + cfg = Config(maconfig) + cfg.read_write() + raises(PropertiesOptionError, 'cfg.od1.opt1') diff --git a/tiramisu/value.py b/tiramisu/value.py index ffd34d6..3d172dc 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -179,6 +179,7 @@ class Values(object): is_frozen = 'frozen' in setting[opt] # if value is callback and is not set # or frozen with force_default_on_freeze + config_error = None if opt.impl_has_callback() and ( self._is_default_owner(path) or (is_frozen and 'force_default_on_freeze' in setting[opt])): @@ -193,11 +194,15 @@ class Values(object): no_value_slave = True if not no_value_slave: - value = self._getcallback_value(opt) - if (opt.impl_is_multi() and - opt.impl_get_multitype() == multitypes.slave): - if not isinstance(value, list): - value = [value for i in range(lenmaster)] + try: + value = self._getcallback_value(opt) + except ConfigError as config_error: + value = None + else: + if (opt.impl_is_multi() and + opt.impl_get_multitype() == multitypes.slave): + if not isinstance(value, list): + value = [value for i in range(lenmaster)] if opt.impl_is_multi(): value = Multi(value, self.context, opt, path, validate) # suppress value if already set @@ -218,6 +223,8 @@ class Values(object): setting.validate_properties(opt, False, False, value=value, path=path, force_permissive=force_permissive, force_properties=force_properties) + if config_error is not None: + raise ConfigError(config_error) return value def __setitem__(self, opt, value): From ffc9d086f9c41fe75fabd3581ac88b5a953faa26 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 16 Sep 2013 15:21:08 +0200 Subject: [PATCH 05/36] double negation in error msg --- tiramisu/option.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 313299d..0058866 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -718,7 +718,7 @@ class IPOption(Option): def _validate(self, value): ip = IP('{0}/32'.format(value)) if ip.iptype() == 'RESERVED': - raise ValueError(_("IP mustn't not be in reserved class")) + raise ValueError(_("IP shall not be in reserved class")) if self._only_private and not ip.iptype() == 'PRIVATE': raise ValueError(_("IP must be in private class")) @@ -800,7 +800,7 @@ class NetworkOption(Option): def _validate(self, value): ip = IP(value) if ip.iptype() == 'RESERVED': - raise ValueError(_("network mustn't not be in reserved class")) + raise ValueError(_("network shall not be in reserved class")) class NetmaskOption(Option): From 8def6c85d73337451274c558c53ba5fed014f921 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 16 Sep 2013 15:24:46 +0200 Subject: [PATCH 06/36] double negation in error msg --- translations/fr/tiramisu.po | 4 ++-- translations/tiramisu.pot | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index fd00df1..8bceb43 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -168,7 +168,7 @@ msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "symlinkoption mal formé doit être une option pour symlink {0}" #: tiramisu/option.py:526 -msgid "IP mustn't not be in reserved class" +msgid "IP shall not be in reserved class" msgstr "IP ne doit pas être d'une classe reservée" #: tiramisu/option.py:528 @@ -184,7 +184,7 @@ msgid "max value is empty" msgstr "valeur maximum est vide" #: tiramisu/option.py:608 -msgid "network mustn't not be in reserved class" +msgid "network shall not be in reserved class" msgstr "réseau ne doit pas être dans la classe reservée" #: tiramisu/option.py:640 diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 3b7b989..ef40426 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -156,7 +156,7 @@ msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" #: tiramisu/option.py:581 -msgid "IP mustn't not be in reserved class" +msgid "IP shall not be in reserved class" msgstr "" #: tiramisu/option.py:583 @@ -172,7 +172,7 @@ msgid "max value is empty" msgstr "" #: tiramisu/option.py:663 -msgid "network mustn't not be in reserved class" +msgid "network shall not be in reserved class" msgstr "" #: tiramisu/option.py:695 From 57f4dd8d3f383889f8c3358e0d90090d505a51dc Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 16 Sep 2013 20:51:13 +0200 Subject: [PATCH 07/36] allow mandatory value (see 9ddf100118d913c2f4225cca1adb65317b512d84 for more details) --- test/test_option_calculation.py | 28 +++++++++++++++++++++++++--- tiramisu/setting.py | 8 +++++++- tiramisu/value.py | 15 +++++++++++++-- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index d57072f..1441460 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -5,7 +5,7 @@ from tiramisu.setting import groups from tiramisu.config import Config from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ StrOption, OptionDescription -from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError +from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError, ConfigError def return_val(): @@ -500,7 +500,7 @@ def test_callback_hidden(): cfg.od2.opt2 -def test_callback_disable_make_dict(): +def test_callback_two_disabled(): opt1 = BoolOption('opt1', '', properties=('disabled',)) opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('disabled',)) od1 = OptionDescription('od1', '', [opt1]) @@ -508,4 +508,26 @@ def test_callback_disable_make_dict(): maconfig = OptionDescription('rootconfig', '', [od1, od2]) cfg = Config(maconfig) cfg.read_write() - raises(PropertiesOptionError, 'cfg.od1.opt1') + raises(PropertiesOptionError, 'cfg.od2.opt2') + + +def test_callback_calculating_disabled(): + opt1 = BoolOption('opt1', '', properties=('disabled',)) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}) + od1 = OptionDescription('od1', '', [opt1]) + od2 = OptionDescription('od2', '', [opt2]) + maconfig = OptionDescription('rootconfig', '', [od1, od2]) + cfg = Config(maconfig) + cfg.read_write() + raises(ConfigError, 'cfg.od2.opt2') + + +def test_callback_calculating_mandatory(): + opt1 = BoolOption('opt1', '', properties=('disabled',)) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('mandatory',)) + od1 = OptionDescription('od1', '', [opt1]) + od2 = OptionDescription('od2', '', [opt2]) + maconfig = OptionDescription('rootconfig', '', [od1, od2]) + cfg = Config(maconfig) + cfg.read_only() + raises(ConfigError, 'cfg.od2.opt2') diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 249af37..531e846 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -385,13 +385,17 @@ class Settings(object): #____________________________________________________________ def validate_properties(self, opt_or_descr, is_descr, is_write, path, value=None, force_permissive=False, - force_properties=None): + force_properties=None, force_permissives=None): """ validation upon the properties related to `opt_or_descr` :param opt_or_descr: an option or an option description object :param force_permissive: behaves as if the permissive property was present + :param force_properties: set() with properties that is force to add + in global properties + :param force_permissives: set() with permissives that is force to add + in global permissives :param is_descr: we have to know if we are in an option description, just because the mandatory property doesn't exist here @@ -408,6 +412,8 @@ class Settings(object): self_properties = copy(self._getproperties()) if force_permissive is True or 'permissive' in self_properties: properties -= self._p_.getpermissive() + if force_permissives is not None: + properties -= force_permissives # global properties if force_properties is not None: diff --git a/tiramisu/value.py b/tiramisu/value.py index 3d172dc..911f4a8 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -177,9 +177,16 @@ class Values(object): # options with callbacks setting = self.context().cfgimpl_get_settings() is_frozen = 'frozen' in setting[opt] + # For calculating properties, we need value (ie for mandatory value). + # If value is calculating with a PropertiesOptionError's option + # _getcallback_value raise a ConfigError. + # We can not raise ConfigError if this option should raise + # PropertiesOptionError too. So we get config_error and raise + # ConfigError if properties did not raise. + config_error = None + force_permissives = None # if value is callback and is not set # or frozen with force_default_on_freeze - config_error = None if opt.impl_has_callback() and ( self._is_default_owner(path) or (is_frozen and 'force_default_on_freeze' in setting[opt])): @@ -198,6 +205,9 @@ class Values(object): value = self._getcallback_value(opt) except ConfigError as config_error: value = None + # should not raise PropertiesOptionError if option is + # mandatory + force_permissives = set(['mandatory']) else: if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave): @@ -222,7 +232,8 @@ class Values(object): if validate_properties: setting.validate_properties(opt, False, False, value=value, path=path, force_permissive=force_permissive, - force_properties=force_properties) + force_properties=force_properties, + force_permissives=force_permissives) if config_error is not None: raise ConfigError(config_error) return value From 866364059c33cebd3b0840cdfe82364a6f6541f6 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 17 Sep 2013 09:10:08 +0200 Subject: [PATCH 08/36] dont change anything if config_error --- test/test_option_calculation.py | 11 +++++++++++ tiramisu/value.py | 13 +++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 1441460..17f685c 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -531,3 +531,14 @@ def test_callback_calculating_mandatory(): cfg = Config(maconfig) cfg.read_only() raises(ConfigError, 'cfg.od2.opt2') + + +def test_callback_two_disabled_multi(): + opt1 = BoolOption('opt1', '', properties=('disabled',)) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('disabled',), multi=True) + od1 = OptionDescription('od1', '', [opt1]) + od2 = OptionDescription('od2', '', [opt2]) + maconfig = OptionDescription('rootconfig', '', [od1, od2]) + cfg = Config(maconfig) + cfg.read_write() + raises(PropertiesOptionError, 'cfg.od2.opt2') diff --git a/tiramisu/value.py b/tiramisu/value.py index 911f4a8..578c4ee 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -213,10 +213,11 @@ class Values(object): opt.impl_get_multitype() == multitypes.slave): if not isinstance(value, list): value = [value for i in range(lenmaster)] - if opt.impl_is_multi(): - value = Multi(value, self.context, opt, path, validate) - # suppress value if already set - self.reset(opt, path) + if config_error is None: + if opt.impl_is_multi(): + value = Multi(value, self.context, opt, path, validate) + # suppress value if already set + self.reset(opt, path) # frozen and force default elif is_frozen and 'force_default_on_freeze' in setting[opt]: value = self._getdefault(opt) @@ -224,9 +225,9 @@ class Values(object): value = Multi(value, self.context, opt, path, validate) else: value = self._getvalue(opt, path, validate) - if validate: + if config_error is None and validate: opt.impl_validate(value, self.context(), 'validator' in setting) - if self._is_default_owner(path) and \ + if config_error is None and self._is_default_owner(path) and \ 'force_store_value' in setting[opt]: self.setitem(opt, value, path, is_write=False) if validate_properties: From 90ae9aa70dae57821c6ae01dc27eeb799fb41e1e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 19 Sep 2013 21:38:46 +0200 Subject: [PATCH 09/36] refactore carry_out_calculation + test + documentation --- test/test_option_calculation.py | 163 +++++++++++++++++++++++++++++--- test/test_option_validator.py | 59 ++++++++++++ tiramisu/autolib.py | 139 +++++++++++++++++++-------- tiramisu/option.py | 91 ++++++++++++------ tiramisu/value.py | 33 +++++-- 5 files changed, 397 insertions(+), 88 deletions(-) create mode 100644 test/test_option_validator.py diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 17f685c..8ca8687 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -4,7 +4,7 @@ from py.test import raises from tiramisu.setting import groups from tiramisu.config import Config from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ - StrOption, OptionDescription + StrOption, OptionDescription, SymLinkOption from tiramisu.error import PropertiesOptionError, ConflictError, SlaveError, ConfigError @@ -12,11 +12,19 @@ def return_val(): return 'val' -def return_list(): +def return_concat(*args): + return '.'.join(list(args)) + + +def return_list(value=None): return ['val', 'val'] -def return_value(value): +def return_list2(*args): + return list(args) + + +def return_value(value=None): return value @@ -298,18 +306,73 @@ def test_callback(): def test_callback_value(): val1 = StrOption('val1', "", 'val') - val2 = StrOption('val2', "", callback=return_value, callback_params={'': (('val1', False),)}) - maconfig = OptionDescription('rootconfig', '', [val1, val2]) + val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)}) + val3 = StrOption('val3', "", callback=return_value, callback_params={'': ('yes',)}) + val4 = StrOption('val4', "", callback=return_value, callback_params={'value': ((val1, False),)}) + val5 = StrOption('val5', "", callback=return_value, callback_params={'value': ('yes',)}) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5]) cfg = Config(maconfig) cfg.read_write() assert cfg.val1 == 'val' assert cfg.val2 == 'val' + assert cfg.val4 == 'val' cfg.val1 = 'new-val' assert cfg.val1 == 'new-val' assert cfg.val2 == 'new-val' + assert cfg.val4 == 'new-val' del(cfg.val1) assert cfg.val1 == 'val' assert cfg.val2 == 'val' + assert cfg.val3 == 'yes' + assert cfg.val4 == 'val' + assert cfg.val5 == 'yes' + + +def test_callback_value_tuple(): + val1 = StrOption('val1', "", 'val1') + val2 = StrOption('val2', "", 'val2') + val3 = StrOption('val3', "", callback=return_concat, callback_params={'': ((val1, False), (val2, False))}) + val4 = StrOption('val4', "", callback=return_concat, callback_params={'': ('yes', 'no')}) + raises(ValueError, "StrOption('val4', '', callback=return_concat, callback_params={'value': ('yes', 'no')})") + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == 'val1' + assert cfg.val2 == 'val2' + assert cfg.val3 == 'val1.val2' + assert cfg.val4 == 'yes.no' + cfg.val1 = 'new-val' + assert cfg.val3 == 'new-val.val2' + del(cfg.val1) + assert cfg.val3 == 'val1.val2' + + +def test_callback_value_force_permissive(): + val1 = StrOption('val1', "", 'val', properties=('disabled',)) + val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)}) + val3 = StrOption('val3', "", callback=return_value, callback_params={'': ((val1, True),)}) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3]) + cfg = Config(maconfig) + cfg.read_only() + raises(ConfigError, "cfg.val2") + assert cfg.val3 is None + + +def test_callback_symlink(): + val1 = StrOption('val1', "", 'val') + val2 = SymLinkOption('val2', val1) + val3 = StrOption('val3', "", callback=return_value, callback_params={'': ((val2, False),)}) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == 'val' + assert cfg.val3 == 'val' + cfg.val1 = 'new-val' + assert cfg.val1 == 'new-val' + assert cfg.val3 == 'new-val' + del(cfg.val1) + assert cfg.val1 == 'val' + assert cfg.val3 == 'val' def test_callback_list(): @@ -336,21 +399,28 @@ def test_callback_multi(): def test_callback_multi_value(): val1 = StrOption('val1', "", ['val'], multi=True) - val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': (('val1', False),)}) - maconfig = OptionDescription('rootconfig', '', [val1, val2]) + val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)}) + val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)}) + val4 = StrOption('val4', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), 'yes')}) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4]) cfg = Config(maconfig) cfg.read_write() assert cfg.val1 == ['val'] assert cfg.val2 == ['val'] + assert cfg.val4 == ['val', 'yes'] cfg.val1 = ['new-val'] assert cfg.val1 == ['new-val'] assert cfg.val2 == ['new-val'] + assert cfg.val4 == ['new-val', 'yes'] cfg.val1.append('new-val2') assert cfg.val1 == ['new-val', 'new-val2'] assert cfg.val2 == ['new-val', 'new-val2'] + assert cfg.val4 == ['new-val', 'yes', 'new-val2', 'yes'] del(cfg.val1) assert cfg.val1 == ['val'] assert cfg.val2 == ['val'] + assert cfg.val3 == ['yes'] + assert cfg.val4 == ['val', 'yes'] def test_callback_multi_list(): @@ -455,41 +525,67 @@ def test_callback_master_and_slaves_slave_list(): def test_callback_master_and_slaves_value(): val1 = StrOption('val1', "", multi=True) - val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': (('val1.val1', False),)}) - interface1 = OptionDescription('val1', '', [val1, val2]) + val2 = StrOption('val2', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)}) + val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)}) + val4 = StrOption('val4', '', multi=True, default=['val10', 'val11']) + val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)}) + interface1 = OptionDescription('val1', '', [val1, val2, val3, val5]) interface1.impl_set_group_type(groups.master) - maconfig = OptionDescription('rootconfig', '', [interface1]) + maconfig = OptionDescription('rootconfig', '', [interface1, val4]) cfg = Config(maconfig) cfg.read_write() assert cfg.val1.val1 == [] assert cfg.val1.val2 == [] + assert cfg.val1.val3 == [] + assert cfg.val1.val5 == [] # cfg.val1.val1 = ['val1'] assert cfg.val1.val1 == ['val1'] assert cfg.val1.val2 == ['val1'] + assert cfg.val1.val3 == ['yes'] + assert cfg.val1.val5 == ['val10'] # cfg.val1.val1.append('val2') assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2'] + assert cfg.val1.val3 == ['yes', 'yes'] + assert cfg.val1.val5 == ['val10', 'val11'] # cfg.val1.val1 = ['val1', 'val2', 'val3'] assert cfg.val1.val1 == ['val1', 'val2', 'val3'] assert cfg.val1.val2 == ['val1', 'val2', 'val3'] + assert cfg.val1.val3 == ['yes', 'yes', 'yes'] + assert cfg.val1.val5 == ['val10', 'val11', None] # cfg.val1.val1.pop(2) assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2'] + assert cfg.val1.val3 == ['yes', 'yes'] + assert cfg.val1.val5 == ['val10', 'val11'] # cfg.val1.val2 = ['val2', 'val2'] + cfg.val1.val3 = ['val2', 'val2'] + cfg.val1.val5 = ['val2', 'val2'] assert cfg.val1.val2 == ['val2', 'val2'] + assert cfg.val1.val3 == ['val2', 'val2'] + assert cfg.val1.val5 == ['val2', 'val2'] # cfg.val1.val1.append('val3') assert cfg.val1.val2 == ['val2', 'val2', 'val3'] + assert cfg.val1.val3 == ['val2', 'val2', 'yes'] + assert cfg.val1.val5 == ['val2', 'val2', None] + cfg.cfgimpl_get_settings().remove('cache') + cfg.val4 = ['val10', 'val11', 'val12'] + #if value is already set, not updated ! + cfg.val1.val1.pop(2) + cfg.val1.val1.append('val3') + cfg.val1.val1 = ['val1', 'val2', 'val3'] + assert cfg.val1.val5 == ['val2', 'val2', 'val12'] def test_callback_hidden(): opt1 = BoolOption('opt1', '') - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}) od1 = OptionDescription('od1', '', [opt1], properties=('hidden',)) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) @@ -502,7 +598,7 @@ def test_callback_hidden(): def test_callback_two_disabled(): opt1 = BoolOption('opt1', '', properties=('disabled',)) - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('disabled',)) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('disabled',)) od1 = OptionDescription('od1', '', [opt1]) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) @@ -513,7 +609,7 @@ def test_callback_two_disabled(): def test_callback_calculating_disabled(): opt1 = BoolOption('opt1', '', properties=('disabled',)) - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}) od1 = OptionDescription('od1', '', [opt1]) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) @@ -524,7 +620,7 @@ def test_callback_calculating_disabled(): def test_callback_calculating_mandatory(): opt1 = BoolOption('opt1', '', properties=('disabled',)) - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('mandatory',)) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('mandatory',)) od1 = OptionDescription('od1', '', [opt1]) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) @@ -535,10 +631,47 @@ def test_callback_calculating_mandatory(): def test_callback_two_disabled_multi(): opt1 = BoolOption('opt1', '', properties=('disabled',)) - opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': (('od1.opt1', False),)}, properties=('disabled',), multi=True) + opt2 = BoolOption('opt2', '', callback=return_value, callback_params={'': ((opt1, False),)}, properties=('disabled',), multi=True) od1 = OptionDescription('od1', '', [opt1]) od2 = OptionDescription('od2', '', [opt2]) maconfig = OptionDescription('rootconfig', '', [od1, od2]) cfg = Config(maconfig) cfg.read_write() raises(PropertiesOptionError, 'cfg.od2.opt2') + + +def test_callback_multi_list_params(): + val1 = StrOption('val1', "", multi=True, default=['val1', 'val2']) + val2 = StrOption('val2', "", multi=True, callback=return_list, callback_params={'': ((val1, False),)}) + oval2 = OptionDescription('val2', '', [val2]) + maconfig = OptionDescription('rootconfig', '', [val1, oval2]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val2.val2 == ['val', 'val', 'val', 'val'] + + +def test_callback_multi_list_params_key(): + val1 = StrOption('val1', "", multi=True, default=['val1', 'val2']) + val2 = StrOption('val2', "", multi=True, callback=return_list, callback_params={'value': ((val1, False),)}) + oval2 = OptionDescription('val2', '', [val2]) + maconfig = OptionDescription('rootconfig', '', [val1, oval2]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val2.val2 == ['val', 'val', 'val', 'val'] + + +def test_callback_multi_multi(): + val1 = StrOption('val1', "", multi=True, default=['val1', 'val2', 'val3']) + val2 = StrOption('val2', "", multi=True, default=['val11', 'val12']) + val3 = StrOption('val3', "", default='val4') + val4 = StrOption('val4', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val2, False))}) + val5 = StrOption('val5', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val3, False))}) + val6 = StrOption('val6', "", multi=True, default=['val21', 'val22', 'val23']) + val7 = StrOption('val7', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val6, False))}) + raises(ValueError, "StrOption('val8', '', multi=True, callback=return_list2, callback_params={'value': ((val1, False), (val6, False))})") + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5, val6, val7]) + cfg = Config(maconfig) + cfg.read_write() + raises(ConfigError, "cfg.val4") + assert cfg.val5 == ['val1', 'val4', 'val2', 'val4', 'val3', 'val4'] + assert cfg.val7 == ['val1', 'val21', 'val2', 'val22', 'val3', 'val23'] diff --git a/test/test_option_validator.py b/test/test_option_validator.py new file mode 100644 index 0000000..8e00916 --- /dev/null +++ b/test/test_option_validator.py @@ -0,0 +1,59 @@ +import autopath +from py.test import raises + +from tiramisu.config import Config +from tiramisu.option import StrOption, OptionDescription +from tiramisu.error import ConfigError + + +def return_true(value, param=None): + if value == 'val' and param in [None, 'yes']: + return True + + +def return_false(value, param=None): + if value == 'val' and param in [None, 'yes']: + return False + + +def return_val(value, param=None): + return 'val' + + +def test_validator(): + opt1 = StrOption('opt1', '', validator=return_true, default='val') + raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')") + raises(ConfigError, "StrOption('opt3', '', validator=return_val, default='val')") + opt2 = StrOption('opt2', '', validator=return_false) + opt3 = StrOption('opt3', '', validator=return_val) + root = OptionDescription('root', '', [opt1, opt2, opt3]) + cfg = Config(root) + assert cfg.opt1 == 'val' + raises(ValueError, "cfg.opt2 = 'val'") + raises(ConfigError, "cfg.opt3 = 'val'") + + +def test_validator_params(): + opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ('yes',)}, default='val') + raises(ValueError, "StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}, default='val')") + raises(ConfigError, "StrOption('opt3', '', validator=return_val, validator_params={'': ('yes',)}, default='val')") + opt2 = StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}) + opt3 = StrOption('opt3', '', validator=return_val, validator_params={'': ('yes',)}) + root = OptionDescription('root', '', [opt1, opt2, opt3]) + cfg = Config(root) + assert cfg.opt1 == 'val' + raises(ValueError, "cfg.opt2 = 'val'") + raises(ConfigError, "cfg.opt3 = 'val'") + + +def test_validator_params_key(): + opt1 = StrOption('opt1', '', validator=return_true, validator_params={'param': ('yes',)}, default='val') + raises(TypeError, "StrOption('opt2', '', validator=return_true, validator_params={'param_unknown': ('yes',)}, default='val')") + root = OptionDescription('root', '', [opt1]) + cfg = Config(root) + assert cfg.opt1 == 'val' + + +def test_validator_params_option(): + opt0 = StrOption('opt0', '', default='val') + raises(ValueError, "opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ((opt0, False),)}, default='val')") diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index 2cf41ca..de8a8c5 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -23,11 +23,9 @@ from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.i18n import _ # ____________________________________________________________ -def carry_out_calculation(name, - config, - callback, - callback_params, - index=None): + +def carry_out_calculation(name, config, callback, callback_params, + index=None, max_len=None): """a function that carries out a calculation for an option's value :param name: the option name (`opt._name`) @@ -40,36 +38,104 @@ def carry_out_calculation(name, :type callback_params: dict :param index: if an option is multi, only calculates the nth value :type index: int + :param max_len: max length for a multi + :type max_len: int + + * if no callback_params: + => calculate() + + * if callback_params={'': ('yes',)} + => calculate('yes') + + * if callback_params={'value': ('yes',)} + => calculate(value='yes') + + * if callback_params={'': ('yes', 'no')} + => calculate('yes', 'no') + + * if callback_params={'value': ('yes', 'no')} + => ValueError() + + * if callback_params={'': ((opt1, False),)} + + - a simple option: + opt1 == 11 + => calculate(11) + + - a multi option: + opt1 == [1, 2, 3] + => calculate(1) + => calculate(2) + => calculate(3) + + * if callback_params={'value': ((opt1, False),)} + + - a simple option: + opt1 == 11 + => calculate(value=11) + + - a multi option: + opt1 == [1, 2, 3] + => calculate(value=1) + => calculate(value=2) + => calculate(value=3) + + * if callback_params={'': ((opt1, False), (opt2, False))} + + - a multi option with a simple option + opt1 == [1, 2, 3] + opt2 == 11 + => calculate(1, 11) + => calculate(2, 11) + => calculate(3, 11) + + - a multi option with an other multi option but with same length + opt1 == [1, 2, 3] + opt2 == [11, 12, 13] + callback_params={'': ((opt1, False), (opt2, False))} + => calculate(1, 11) + => calculate(2, 12) + => calculate(3, 13) + + - a multi option with an other multi option but with different length + opt1 == [1, 2, 3] + opt2 == [11, 12] + callback_params={'': ((opt1, False), (opt2, False))} + => ConfigError() + + * if callback_params={'value': ((opt1, False), (opt2, False))} + => ConfigError() + + If index is not None, return a value, otherwise return: + + * a list if one parameters have multi option + * a value otherwise + + If calculate return list, this list is extend to return value. """ - #callback, callback_params = option.getcallback() - #if callback_params is None: - # callback_params = {} tcparams = {} one_is_multi = False len_multi = 0 - for key, values in callback_params.items(): - for value in values: - if type(value) == tuple: - path, check_disabled = value - if config is None: - if check_disabled: - continue - raise ConfigError(_('no config specified but needed')) + for key, callbacks in callback_params.items(): + for callbk in callbacks: + if isinstance(callbk, tuple): + option, force_permissive = callbk + # get value try: - opt_value = config._getattr(path, force_permissive=True) - opt = config.unwrap_from_path(path, force_permissive=True) + path = config.cfgimpl_get_description().impl_get_path_by_opt(option) + value = config._getattr(path, force_permissive=True) except PropertiesOptionError as err: - if check_disabled: + if force_permissive: continue raise ConfigError(_('unable to carry out a calculation, ' 'option {0} has properties: {1} for: ' - '{2}').format(path, err.proptype, + '{2}').format(option._name, err.proptype, name)) - is_multi = opt.impl_is_multi() + is_multi = option.impl_is_multi() if is_multi: - if opt_value is not None: - len_value = len(opt_value) + if value is not None: + len_value = len(value) if len_multi != 0 and len_multi != len_value: raise ConfigError(_('unable to carry out a ' 'calculation, option value with' @@ -77,16 +143,23 @@ def carry_out_calculation(name, 'length for: {0}').format(name)) len_multi = len_value one_is_multi = True - tcparams.setdefault(key, []).append((opt_value, is_multi)) + tcparams.setdefault(key, []).append((value, is_multi)) else: - tcparams.setdefault(key, []).append((value, False)) + tcparams.setdefault(key, []).append((callbk, False)) if one_is_multi: ret = [] if index: - range_ = [index] + if index < len_multi: + range_ = [index] + else: + range_ = [] + ret = None else: - range_ = range(len_multi) + if max_len and max_len < len_multi: + range_ = range(max_len) + else: + range_ = range(len_multi) for incr in range_: tcp = {} params = [] @@ -97,15 +170,9 @@ def carry_out_calculation(name, if key == '': params.append(value[incr]) else: - if len(value) > incr: - tcp[key] = value[incr] - else: - tcp[key] = '' + tcp[key] = value[incr] else: - if key == '': - params.append(value) - else: - tcp[key] = value + params.append(value) calc = calculate(name, callback, params, tcp) if index: ret = calc @@ -114,7 +181,6 @@ def carry_out_calculation(name, ret.extend(calc) else: ret.append(calc) - return ret else: tcp = {} @@ -130,7 +196,6 @@ def carry_out_calculation(name, def calculate(name, callback, params, tcparams): - # FIXME we don't need the option's name down there. """wrapper that launches the 'callback' :param callback: callback name diff --git a/tiramisu/option.py b/tiramisu/option.py index 0058866..1948f39 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -26,7 +26,7 @@ from copy import copy, deepcopy from types import FunctionType from IPy import IP -from tiramisu.error import ConflictError +from tiramisu.error import ConflictError, ConfigError from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -97,8 +97,8 @@ class BaseOption(object): "frozen" (which has noting to do with the high level "freeze" propertie or "read_only" property) """ - if not name.startswith('_state') and \ - name not in ('_cache_paths', '_consistencies'): + if not name.startswith('_state') and name not in ('_cache_paths', + '_consistencies'): is_readonly = False # never change _name if name == '_name': @@ -327,7 +327,7 @@ class Option(BaseOption): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_args=None, + callback_params=None, validator=None, validator_params=None, properties=None): """ :param name: the option's name @@ -344,18 +344,15 @@ class Option(BaseOption): :param callback_params: the callback's parameter :param validator: the name of a function which stands for a custom validation of the value - :param validator_args: the validator's parameters + :param validator_params: the validator's parameters :param properties: tuple of default properties """ super(Option, self).__init__(name, doc, requires, properties) self._multi = multi if validator is not None: - if type(validator) != FunctionType: - raise TypeError(_("validator must be a function")) - if validator_args is None: - validator_args = {} - self._validator = (validator, validator_args) + validate_callback(validator, validator_params, 'validator') + self._validator = (validator, validator_params) else: self._validator = None if not self._multi and default_multi is not None: @@ -377,11 +374,7 @@ class Option(BaseOption): "no callback defined" " yet for option {0}").format(name)) if callback is not None: - if type(callback) != FunctionType: - raise ValueError('callback must be a function') - if callback_params is not None and \ - not isinstance(callback_params, dict): - raise ValueError('callback_params must be a dict') + validate_callback(callback, callback_params, 'callback') self._callback = (callback, callback_params) else: self._callback = None @@ -448,11 +441,23 @@ class Option(BaseOption): def val_validator(val): if self._validator is not None: - callback_params = deepcopy(self._validator[1]) - callback_params.setdefault('', []).insert(0, val) - return carry_out_calculation(self._name, config=context, - callback=self._validator[0], - callback_params=callback_params) + if self._validator[1] is not None: + validator_params = deepcopy(self._validator[1]) + if '' in validator_params: + lst = list(validator_params['']) + lst.insert(0, val) + validator_params[''] = tuple(lst) + else: + validator_params[''] = (val,) + else: + validator_params = {'': (val,)} + ret = carry_out_calculation(self._name, config=context, + callback=self._validator[0], + callback_params=validator_params) + if ret not in [False, True]: + raise ConfigError(_('validator should return a boolean, ' + 'not {0}').format(ret)) + return ret else: return True @@ -566,7 +571,7 @@ class ChoiceOption(Option): def __init__(self, name, doc, values, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, open_values=False, validator=None, - validator_args=None, properties=()): + validator_params=None, properties=()): """ :param values: is a list of values the option can possibly take """ @@ -584,7 +589,7 @@ class ChoiceOption(Option): requires=requires, multi=multi, validator=validator, - validator_args=validator_args, + validator_params=validator_params, properties=properties) def impl_get_values(self): @@ -702,7 +707,7 @@ class IPOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_args=None, + callback_params=None, validator=None, validator_params=None, properties=None, only_private=False): self._only_private = only_private super(IPOption, self).__init__(name, doc, default=default, @@ -712,7 +717,7 @@ class IPOption(Option): requires=requires, multi=multi, validator=validator, - validator_args=validator_args, + validator_params=validator_params, properties=properties) def _validate(self, value): @@ -738,7 +743,7 @@ class PortOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_args=None, + callback_params=None, validator=None, validator_params=None, properties=None, allow_range=False, allow_zero=False, allow_wellknown=True, allow_registred=True, allow_private=False): @@ -772,7 +777,7 @@ class PortOption(Option): requires=requires, multi=multi, validator=validator, - validator_args=validator_args, + validator_params=validator_params, properties=properties) def _validate(self, value): @@ -857,7 +862,7 @@ class DomainnameOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, - callback_params=None, validator=None, validator_args=None, + callback_params=None, validator=None, validator_params=None, properties=None, allow_ip=False, type_='domainname'): #netbios: for MS domain #hostname: to identify the device @@ -876,7 +881,7 @@ class DomainnameOption(Option): requires=requires, multi=multi, validator=validator, - validator_args=validator_args, + validator_params=validator_params, properties=properties) def _validate(self, value): @@ -1252,3 +1257,33 @@ def validate_requires_arg(requires, name): require[3], require[4], require[5])) ret.append(tuple(ret_action)) return frozenset(config_action.keys()), tuple(ret) + + +def validate_callback(callback, callback_params, type_): + if type(callback) != FunctionType: + raise ValueError(_('{0} should be a function').format(type_)) + if callback_params is not None: + if not isinstance(callback_params, dict): + raise ValueError(_('{0}_params should be a dict').format(type_)) + for key, callbacks in callback_params.items(): + if key != '' and len(callbacks) != 1: + raise ValueError(_('{0}_params with key {1} should not have ' + 'length different to 1').format(type_, + key)) + if not isinstance(callbacks, tuple): + raise ValueError(_('{0}_params should be tuple for key "{1}"' + ).format(type_, key)) + for callbk in callbacks: + if isinstance(callbk, tuple): + option, force_permissive = callbk + if type_ == 'validator' and not force_permissive: + raise ValueError(_('validator not support tuple')) + if not isinstance(option, Option) and not \ + isinstance(option, SymLinkOption): + raise ValueError(_('{0}_params should have an option ' + 'not a {0} for first argument' + ).format(type_, type(option))) + if force_permissive not in [True, False]: + raise ValueError(_('{0}_params should have a boolean' + 'not a {0} for second argument' + ).format(type_, type(force_permissive))) diff --git a/tiramisu/value.py b/tiramisu/value.py index 578c4ee..b600eb8 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -124,7 +124,7 @@ class Values(object): return True return False - def _getcallback_value(self, opt, index=None): + def _getcallback_value(self, opt, index=None, max_len=None): """ retrieves a value for the options that have a callback @@ -139,7 +139,7 @@ class Values(object): return carry_out_calculation(opt._name, config=self.context(), callback=callback, callback_params=callback_params, - index=index) + index=index, max_len=max_len) def __getitem__(self, opt): "enables us to use the pythonic dictionary-like access to values" @@ -190,6 +190,7 @@ class Values(object): if opt.impl_has_callback() and ( self._is_default_owner(path) or (is_frozen and 'force_default_on_freeze' in setting[opt])): + lenmaster = None no_value_slave = False if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave): @@ -202,7 +203,7 @@ class Values(object): if not no_value_slave: try: - value = self._getcallback_value(opt) + value = self._getcallback_value(opt, max_len=lenmaster) except ConfigError as config_error: value = None # should not raise PropertiesOptionError if option is @@ -389,20 +390,27 @@ class Multi(list): def _valid_slave(self, value): #if slave, had values until master's one + values = self.context().cfgimpl_get_values() masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt( self.opt.impl_get_master_slaves()) mastervalue = getattr(self.context(), masterp) masterlen = len(mastervalue) valuelen = len(value) if valuelen > masterlen or (valuelen < masterlen and - not self.context().cfgimpl_get_values( - )._is_default_owner(self.path)): + not values._is_default_owner(self.path)): raise SlaveError(_("invalid len for the slave: {0}" " which has {1} as master").format( self.opt._name, masterp)) elif valuelen < masterlen: for num in range(0, masterlen - valuelen): - value.append(self.opt.impl_getdefault_multi()) + if self.opt.impl_has_callback(): + # if callback add a value, but this value will not change + # anymore automaticly (because this value has owner) + index = value.__len__() + value.append(values._getcallback_value(self.opt, + index=index)) + else: + value.append(self.opt.impl_getdefault_multi()) #else: same len so do nothing return value @@ -420,8 +428,17 @@ class Multi(list): self.opt._name, slave._name)) elif len(value_slave) < masterlen: for num in range(0, masterlen - len(value_slave)): - value_slave.append(slave.impl_getdefault_multi(), - force=True) + if slave.impl_has_callback(): + # if callback add a value, but this value will not + # change anymore automaticly (because this value + # has owner) + index = value_slave.__len__() + value_slave.append( + values._getcallback_value(slave, index=index), + force=True) + else: + value_slave.append(slave.impl_getdefault_multi(), + force=True) def __setitem__(self, key, value): self._validate(value) From 30c376e3ead25b14c3bc0b20189e10ae01e6ecf3 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 19 Sep 2013 21:39:17 +0200 Subject: [PATCH 10/36] add doc/config.png --- doc/config.png | Bin 0 -> 15539 bytes doc/config.svg | 257 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 doc/config.png create mode 100644 doc/config.svg diff --git a/doc/config.png b/doc/config.png new file mode 100644 index 0000000000000000000000000000000000000000..a468275da56ecf74534d340d77374e631f4deaae GIT binary patch literal 15539 zcmdVBcQlv(A2)uHY#}qrEG3eWC?czYh7}cAX-HN?nb{*mp+Qzkl2OUXo=JsDsgPAk zGAeuBk5}J&oZlb!Iludy`@YZb=bX>?`}ySE^}epx>-l;;_p{c%3<)NSZc`R$lSRkf74?H_zBMn^vXc1`}7B+f;rL2=?|@2ixr`Y4`8nA2bY8la$xspwr>g&^8uI z-tMkxXt;8Cc$nKlH|qCbo4EF_U1IX`EAb0Mw{6-f+__eq28C9(wt+r#lai&E1-?Fy z6(9Uul$YKtGP~A={eS0z{-wFCdLf{uriOXdTum@b$K#U+f-mpWj;O{y{pHWzwL3VPWda^y1v8Ug7AMN@2Z3nN3b5+JBj>vF#TF zhld@Woc+xE%wx1+Y2nL~sGJ<nMQb;}#37ajW@7!72S?scmmzS5KjEs!jhTbs=2nbwP_fw;17h=`Y z(xR#g&wLpBcrr!9#DwSj>trn#7rEhIzYK6$YUha)5{5=b2FAugQl_~SHPrrqK|w*` zU7n?|u5RX1`RdiHBbyU9XB;cPIM`7jF0kd4h){D=lZJzX^pU3y;W;_dNjtoF-G|<3 zOpSD<<>nr16K3ZukJ+hhT<<>IVc_iQ8s|_J5J0P8VPV0q?mNFa=HA`A%(}X|H37?6 z>Yg4cv}byI?*N~GqWxMkGc(2)FJ1(srKLG9%(|l#tZ}mew{PE0#Uq*>KFoCJ(4l~$ zq9PH-09n69@9IyVjtD*|E;hTq-94My+4|uF-@EqqVC(^7D7!H4914b+3KI64H#lfy z#Kp?Sww%)Z{Pb*aS($2+TBHXP8ygSB7#kZKFE4ChQezaPt-TbNlyrH1-rH@kwKuAf zjg{5v`0=36MNZ9yP96C)i;Ih4QBm~|j~1oo<;4`(HqufdE7nA;(@RLj>V$=dYv}8< z&d<+xD=$7h`?=@mPZmo{%eCv*i;IfVtXQ#PY;3IR>iPq^6H(F84PDPXT!!9BnwPpU z9W8QV{a z26tzaoVr=DRr)vNm)qOh%gV}Dm0kFAB|bj5r^l+KqQ4**ZP&9; zpFTa+no6^B<;sCiOIDk$uj~`dvy?d8BmMky5VMEp3eYdV$x#GV&MyT9nX$EBb8~Zf79VpN%#apcu92}Tid1gL$B)&oUa?L5{16%( zy!`xVchnk{^Hwvzzh-tn_8hmNV_?uSHRTJWXA1}qXPRGFsP8W=Ej4>~e$(P~KRfE& z`tM+yQ_?dme@EE`h4UkwOxWhXL+vcV!NJOIgB%B#R+@0i_f9CD5=-BhWpr-+Z& zvEsWo9-mK77prS*B$wa5eY?}J(X;bs$+I;#vrLVC3DuX-x+<}6?TsR=4vx#_4`w{Q z|LK~W#l*$i?i;PwN!0qgw>P;nJgi&m@ZnWBaKfESUrhN9y)V=L`=duv^WQ*nSsod- zhqlP+OM=%S5zcl8vX3ie<>d|Cn9$sM|LdpzFO99c>4UUkVK4L~u8N9@(Wdw0V=D9e(&EGUdowMmGQ-H{ZQbw`#-^wn1bno*l^9JSBB*VkQi+vZi zqZch#Pg((I)x4F-cJ8~i)fz1~LMA+G7kS!r z`#pR2GF2`us<@szH$L&fQVR`7NJuC`CNis`hnbhtKrtaDh5h8olR=@OwrFbns-F4j zdScm}WZ-9)Bqa&{{q>m-y{O}v$L86o(Y(R9u_gHyEAOm~4 zY2GpNy+;Ug$QH$1qTRiFcU^tG=$0+qU8U|uZhN)cc>yO3*y3ViX&4w7?%ciGH$C2u z?wvutGJ$-h{dER*p;UC9=PzH@g>$XP7|kGGx#r(jZdkW&`Q+pzj$8eGqkFB7?DTXJ zwT&OjVj0>Gy12OXspTk~d|g#jqgl&>WXvWo$gKbo0ljLMd|#3%PUO z^d)sAW{;DjoE74QMvVtrhH@GHT*SU@+qRBUcd1WL&qjWHd@>_7HT9+KBnp&k{eJ28 zTy*m7?n8SFwzjR0lETSXOp1wNU!4DQKybSoBfuEXXTP~$N}gU`TDx~MsHv&Ny)1P4 zqPDj|boSl9Z=-*9?)wVgg$dJ_N!yHM%xV1Z-CIuvDUZ}45h*Daiet?hHa`1wK(+Tr46j%}UAIz2F}HRH)~8dtq*>wyBBf z+qZ839yOPiF$_dA(9!ig8?C5~mok<09J4Yza)fmH@$qq<@mP+%0W^`^x*T-qnYQ>6 z<#XRyZ>#xioDtN}(6}yV8#2)UM#^KfyFN`TlyrR*_FlC`Oaochfjv!)nWnFkwgsZ) zdS1L(F+PpIFgE;Afz0%(2PdTy=+c3EUZ$uEW6nCexpB`aZ{MzY-~c-&CjV9k{t&j+ z5^4t&@Bngha(&ajzP`>bE*jR>qP#NZVK`f?g325yLY_GS?f^m>^Q~)JpPV+*zpc8e z$gwNn@neOE$VjQ74AG5z^wNgc@!9Hz27182>2J53#=a(6YVO|sJ!8sSP}P$oIyyS@ zlT&B$rP@$-Jp=i22}MPo9T)%l7Zk`fM6C`L)e6~(JF{)LT>a;JOS5WseIy^o!97D! z6-h}ZugT%qY=_xI6_1gf7z!aFAx^4}j*crRz@hjv9_P2Bdu@WYQ4R?Pj7k7hlM{a>erQ=giw2&LPF7)rsP!OD%TZGu0VsRtN8KbN5_-X zT!-?GMV)?o*BT6=*k^8Qu4Sc6+j@<)oA8KX0|UpOIDHYidGqET07zh{*yQ9WoJov0 z-**u@yLQp1r>E-{w%kx);FB@GlI}3ObbE(a2uinqabd35^(!-a1VKoxtp`c5mU~Tw zh%f?Vk5(?p@Z;U;zjkavar|Nj&A}mIh4PCN7yCym zJ@(=T`Ws{Pc7%1J;LyK{Jw|VLyvITB`20CC@0r^V+8&cJu~}}Ht16N~I3QcLY;pcO zXz=cVDLa*#lf%l+&aUb+%Z=$>5Asm2eo)Dw!#~&JSs#{O*|~>eW?_*-y}Au|a8k~H zzU>oK_Z7ta2NzgFRXu;sh@~SBG}2iTXppfh{0|>Dw}`APM_XGP5W$M1q@=NWKFhF1 z;T;!))EB3hk*)pl_8wV#%(-eDEWpY52|1rfghIT0sSmg`1g>YAiAKSAWQ@Pk&%&8 zIEi*X`X&JYPgsTVp6X@ZlOIoJMz5EUpxt@jatbZsir3k*XW1|w7z4)M=TADVtNsW! zkXvr@;833n7Z(?m=c}F=w1djBc(#>F&CeeT{3e#|U|yZBJJq7TWHs7d&adQ{#GiZY z`>$V7SP;TAY(96EoRjmKbWq;5O;lWbS#@>w;HSa|#fP`5sI0~gZ;+AM1%z*7W8*wE zVnI~_apk*y)pg0i_;&mCi3jDd`{KpzAbugJx`=y9{-*aT?;c2rX5x~tstICx{`z%8 z!lt9GxfUv_o@1#2YB#cN8>81apF77@+x`<%KL()F27hAn8MCOsxmOdVBrYSf;^gZj z4czf~ExS79IgxzL+Y&>V@C{8(T1sT&M&>U*Q_0E+B8>dX&LW*%T`qHzXSH|lw#BsL zS2%IGWBvN|3?NyJ9$$PyH3I2-duu}(nVB^cWh~HYEjWeu#N{*>Ik-QvqH0Qp* zuE&RaM@CjiNlEFasI3WIv8D#=+=jMV6ukgZqNk@ikl-)0C>jdgw0^KHgM1##SKtJ` zJMyKHec!%)+&nzhRaI2w{BIUu_vRT%W8t%(o>Y(Z))v^eZ&X%RP6Zg~E4XrngN~lQ zrm>MBNq~VY13AxFWmT_9R%>f(S5r}6jAv!P#d($QTeofvLAj$-g-ROT-DzR5 zW@culqtZ_uB%7@NX+N`c-8vrWY`1lZUX`aOvV*|OI`XtJ2wJhYlz&77bH(hC)SY|x z`k@%u-CV-`oa!nqoZjquLq7!VHX|#G5w&&o=g;0=8Z08^pOCGraox`sh0Yhc3-c{0x2md=Dn2&CBa@n8*6}<*r@3HiqfZkIIl$ zP^kU;Z;_#vvKRHXmML}}m6Etf(8^((Rv$oLWXl#eWP9^|hvHLH}U~z{1J}K-o(_J_R#T>bCacU$As!(-p?K0>mfuW%c zGRMlyvReSyzkV|{GOEQmC|Mt;c34$sPyBvMOJOiYLS63ePd+l_2~jY6Qx^%6srY%0pO)BD6;cAlkyBOFqhktLeDB+0mo@7&cC-i+_I@kq$A|Q|k!_5zysv%M4nf7cXY!GWssq z1h0gq&_yK=`1D-Q=_#&dUg5n8 z%Pi^c01)^bqa$hCxq(mO;^MI>DVr-~N*;SOojmqvcD$l_1fzjdSU3ivQNb^q@I40( ze4pB_ox+-H6|1R#a|;uWffi$n7iX38XBNeBRiHaUk=Dt_wt4U zCvYF(dwq4`eHA$v(COIa>&h--71Mnz`~m_M(@3L>_0t`l$Mt6>F-}=YHB(-H1wkDVp`pk({ItU=+SAcCWk*xe^2qtKk-W7 z`mI}uM<&+(SjEq8g+2?(%g9YqJ7o)S6C;j0#;etzZ?C3tkP6&?9>TTK&whS7_UDgR ze?_{FJhE;QuyKW;py0s=hc=Z6KR5c$t#WZzs$%}P9)T4A2Vgoo zA;r0jbZ#qYc~%Dv!l2UE$9rjE3PUmkS9+goA@yOsvNHeNZSo@ z^1jz8P9J%?^eJa&XAV3b_mMyVsC<|HUBT}Ue%yasRg&Vs{~peD*%c$SiGTHKJ;S$+ z_HXY-$)0>INInu57e`0cI8`nj8Yuue3j$0?uosrK@;`WQ|C%%a0ZlyWaG@v~!0Y41*y3ox zZ2|a-+UjbW9bS`kl(a=DH<-LlWz=dVo9ck&gp4I6C%5GsRsg^18}DzV{GrXVuG!8_ z<%8gZ{Z-Z1FK@ZMqZYbIPhX#5c5bF#V$JKeAo|kTNq~Xa4N`zP5D-w+X6wo3*Wbrwo0z0mIFCV#ov|C>8zb;D@ zW3$I6*s!fGzdk8uWo21CzamQ1Kfvd1|?yKys-eC)FZcIA<$ z%E%Dk>ePK14Xp=KO<%(p#&zo6s}j@Wjlu;NC+z8#FHdvqDxK~P6|OtUyj?{l($?|w zQ#QYa3BFwO5;3(aGy7a!U9Ti1&3A{Y-OJ4E&8u8o=_JruFdyO5lB^PnBDWpx zD1b_SDKU|c&@Bw5e&9j$8?Vt$fXA6sfmg3y>2>X|`SeKuH!J*iaV5L3T2OTK{`;X? zMfUB?gwCE(kduq`vcSY3bMdrVIy>>7jM--9<}z^iHXU+GN<{n7)YPnNf2i~W^7nR+ zk;{;$Y%uM{{{Gd*0Fd*Y_e6PCOmOba^IEuVGV1#C{iZU{aay!+nlbyrO`sFd2pA|A z9(~W2@@Cj3u{Umn7Z)p|35NYbsrD^OrN3w(c1ip5NWRUo9wX+%oh4jShjKVl@_>*c z=B7q%fd?|X;a%yKEG<9waF|tCO%SpNu&%1V>0ALQl&-~-u?p(%R!~}3+ho-XE3qd$ zd8+T;y$gzukDu;rE5g<@0Q%*h`5;s93os|_@@4MEeSM zo#=P#r(`E^f@?m1UTx_!!2{qHv$=A9CFv3PWC1Gun)2MO^cyz%?X8-c(n@i#x$)@HmbAHKQeG_9YXpToyT%b|9Y+nx2Z1;Q{zKaV5HTzBEa<%bK=I7`RG?=@Hnw_n#WDDpgvz_dJiWQy-TwZ9 zHJ&f-S=X{y;Sic+s0uk8c4W)wFHpLA8alZkC zwYB2`k5*tvLcPtZA-c3d=4wUzYw3C-*$4l9;D2hKcMj|qh9}1W%?+CI5iYvA3y-}i zN^mX70%&+o3XBaI2pcxFwB#L{I+#uLqP1&AaaPR=?c>cqqs6x7TWghADy>b1z9m^o zmtDgTg_~%@7-FdaQV}wl4F-n}?ZUpjPz*^+6Mggctu?rafyv)Ah_Eo;UhH_yXX$+j z*RsJpU1_XiNBA-JQ?t$PAWxl2$y+bGlPATuY%y-LGd=?zh^#{u%FqBOA44w{f+Tp5 z7G}r`#q+SK(a^Y*-hA*n1icKb;PtI%1soh4piK~MFGMQ+fZ#ITw)I)y zNy=W6(wLF#8XP^t!>nFjUP&q*``a>2N^c6}6D<~?%4V=NjVNHm`2(s-18j|ui3(Gr z$TeO<`h^fx3mFD8>M}69O;6ROV)vg3>3R>gC~mUqs_O2KeRtR{xq zA(K8F_Zkb!-nf{%E?W#rpe%6nS_z3hB21%eSf4%}4o6W`N=mQotI72tq5NZIo|j-O zS9Nv@($LVr;NIBR-w*3@85K$TPz&QW8wnr zMF2Yn1B1=T>hu}MiegTG(g*+`#* zwazgwSqamjqu529$mzI0*iTfUeLEIT+n5QKTeh6LyE`O}WlT&=gbGiNbg@8)@}B!u z;l))+?S$iO^Z->g@{Nv3!htE&p_<$OIS%c64-90qqjE3xq2|=2959|1DU` z|0~I+CT>1HqJ2Vyo&K3uc_le{(~Kap5g{=#F$4W8IrriKgspV}TLvAb2Nfg2VjHH( zmQ$@1JdE8?*lskktXQ!t?^wBRq2;slZ0Jgc*YvfGt+5?hoj6Zz6+T`dlA^FXp>BFk z4zD8E7l9E@WKQ4;KqoDRUw?mcISa3dn3!h7qwuhetfK?_wZCq~0e<)JDBtdAK@B95 zC<;&%S}QBb3Bez-`907?YL@EpTbdIt^%#vt2%!)9A~fWNE23Jh_+~0kD9O98j%ncg zcU!Owih^v7_TB^ej)0NS&`@PR7IoN`*t)*64cd+NJ#ARzHPZ@4hA5HRus#EMeBQIlFaT$O*6Q?W$;nq>s5tX!>FEKmHn!NmV5q9eF{?$;q8Ran%Zsq@t*^ zPEMOYyoaTS(`3LCUVk2ffR{7f84 z!jRBUH+kgb=W`$!b16D{1)BN>k6~<`YhGE7Oh<1~({^my$AfeYo) zkB|lDXHs`4l8#TG629*i7n?l=Xr=vi@|DsTV#>l=1L{*~EwJ6K>O0R5;zxGusbd#2 z5OT>BQae59JXQDzamD6mf98=PC$QClks9nSS3yG?LyjT?AWL@Z)?I+_daue)wTXd$ z5|fi;d60`j#wrr^S5~0TX<$ck7V)=k-V}f$-HTrUh}c53GpqF70S%1|gW{5s_=_!& zjoR++TTMEzvg-ijx7W#19&^hvSK(IG!dSyZ<50@Mg(9Y!X(<*K7V~0f8VL!BvGMV0 z_&Pn9{gA%HeMnLcKTuqrx3{9*79CsIgW%$Si!ky?iHK0>BZQg&a;GV1#2VwrN;#Z|LfQJF^;a;t9y(H!oS|2b6Q(FbBtS*Q$tH@8PbSI zr}8u?^OeIO9>$H>#S9=S&3*gWFwty| zqM;Ii$hLYbVO>ZTo@&qL0fDPi!;RLEB*8!v*C6bP?b`)_#(85>lwD{EkOC1VtpI?Y zfC)tLD1RITo2IyRgYO@%CiAPW&jzPU1W{SAT;9fTL>9pZxy#W-6u zo;)=Os)QpdB`P674^reg`&W`8@mxkGCeB6`fHi{EVXLM=3?up~ipd!pQ_zy86`Bun z`*-*wSLMRwYKSv(PTi`QV0-3&a2c31{(ENPHP_YA;lh+Z-{xah$TIM6Id!GDtqjB+ zy}gz+09+2_!5UbGI9|XXbp^VPMTxg=k+T9bwl_w7(I5J$^6Q^1Ol3%FJ$3j$+CE71hi_nEs{T2lg zSE;|V_bL(m3M#ORFK_Qy4eN%Y9J)$*FyV-m*alf%-lsv^*oVYdx^<5pU5%2Y{Gn;H zz!oOL%ArHNLh8PkF>8n|jvUwXmX=WXYcR{$LA#NQFc7@AHVOVF0UPLEoV7e-L(B~y zmL-IQ(8ID-&)S97ot2%9?B`a8F!)csF!1bVx4nJ))@A0m9hNmL=I{NZMKXxD24NC5 z|5`9KHEqO)>OUaJK~W&JiKq9AWp`s@3_c_|O#DzgVG&V;F+$Q}QkxrAfMa3v?d1(P zFZCc`2qIht$GVf5nTC}h?i#!LyvXfEsgDlFjq~rl;UBmz{HTcb{O-eY|!$z8Dj3 z=>0<$o_O{zG!O@+DxgM``wIGZ>aON&`wZS;fG#45bZkg$EGJP_sk}oG5YKx?N0&5g zk@+ElGBIt?I2b76FT>P>c_M!J;ZpZ49;8W+9z8l)sZESPYV6&chvYC6geL z8u;yqE^>bC)%!-YgE0P9;c0b{cU{TFb`*h{n>Vu~uS4uRNO>e; zfY3#27XpbLt=$U3r&0(U;RG3r2-wL) zCdVWqS&o2!fU*7<^@P(pQDKaLDA{d@8nfccgh+cE?|*#aRSzt0SS=+{gA`GHGC)kH z5hju)@G(2P20IPGpQ8Br`HA?3=)oF!dHK|P_ZSdwgvqeSkrjnU2Kw}<-;z`}gC2Sw zgd3%AkiS1AxZ@%>i9Db^K*9~i zmNh@rhd;~o>C-1N&51q-(1^frvqjS>DC-cqnqKAIxr1OWL&@7PZEg9qf!Why*-wF; z$+*V3#s)z}+KUlSQq9kwKcBu+8-?vA&>UZ>lc))1<@G<_zA%Zyxx~Gt<>chx&4e)B zULk3*?6^|5A2qkUR<^B=B*C};`THBF62<4Bu$)|6M!9l`Y3X(OU+b1pQZj8@#}P}5 zf^DY|Ha>XhPy>2BdBfqjwRPIy%CM}s@%{L_bp+%kCodR`Mny&vTkr}HIY~vquw7|t z>Vsk@2NvfkLMAwDBPa5nI!zBHOp$?2QN;Tt+~lBpiXkH<={OAct5zS$@nVW4FWF+9vc$C0wyd$TD0O+_sW( zhD!hU=Z6D`eR7)YAc%#^bF3s&fJ{F;P_f&Om9;#aZM*vb#z=;SOnKN3W##1r`ur0w zAX(etYulX>Rcr)cADbSxCAuUk6v5W8tQY-~n&|>Bk>&?03Iv{m1+pp^+_47Sa#=Z~ zYFKq7;znXhBy9ID^4knicq>|;{dFXjGJt`INq;EUoCr@^IKFLmxMiu%K+sb}1zEbg zyR%Fl@RFQK1vp>h0xqhG zyYlB}>0%7%fKu_`@4rdaSf-PR_crX1SOYZr-}y2U^0k9I01(IjWOYKnCe!nTt)6!K=^u#@hGk20e#UCPehNRfd@Ud#af z`?{ZpIlv#{WG|FF*)3bN5iClD?km*t{CO1-jFF!P=OuPEp)v&Xs*!mBluh0v!CN=a z+cThvPL%>-k@zln=XSS2A_th4U*y55YJKx2J1izBEKH3SBIX%l$ai#@;hY-7Avn`n zTrj=#_|YR-hYnc+mm!NIjjjQ&hf`3H6-!1{A>J5`WL^#YZ8DfpHDHkRPEJnnGifNo zXQ5a@aIx*kx9*#UfQbm+4id7Zuo9PU-C7MXl^LLx>IyIS$peMRa5c&pr{{@w|apOkF6>&ck!CZ+Eip(HXL$mUWis(8- z#i4e(xotrjh7FP=bce7dHX&6rM?t(nGY=j}h#E;c;<1B5LUjLL<>HLJhSPqGgoxmp zXdXQ}m%M;J%gMpvk9pV!t&5H#Iuv{$6tm4iDLMG?BtC#wkN6esxv3rqn0DF7K%7O$ zPYR%uA`J`>Lgp>Mwzly$a`m@&_Hsc)=|c#5bT|==5U?Aru3m2WfV;PMS;t-YlqjpP zJmkzSg93`Fs65jzT(@>D3BS6Y#Spg>2n-9`$UwFYi36Il?PsTC)~)WcEb|oousA<+ zj`ByKr(^PNxw-s}oR@{|_l+1Jms~reci_P0Kgzg|adICDI3r+1y>p|Lp(2c#(dt01 zpu}1@x}f1#E1@V2y`B4i^J1rDTz}BEA!9l0aIF&oL~d6^0IU`?#{FU@(19LBO}_@zMZ! z2Mdi`=l6xMu&jC9=VGR1r7z;|HK5+)Y(ZN~!_Xqc87~=FBQf05-Y@|!OTSi zrD-t`u)t%O2=AG^T!B&qQ5OY&K^lM>>)tnmWhgmbbL({=hmYp$-yeZQOM^Tbujb+F zs|GfPaEQ1Ed(MZiU%w(!LzCh&c{uaS@E;u8hIjXkVy|Ck1YFqLsP_LbmG*4k%4y)< zxH@WOc_6?bJc^muYGA6Pzr=$fYdCZkbHZdgXllCK*;&>X;Y5(A-M^jjV&>Oc45DC% zPfwWOX(6acJ_EU#WO8&8@%tq*sB|_+N%h0$g+qmNOJR(n42it`fAkg3fA7Mm)uHQ< z0iu&=kg;~%I=EEu8Y9WP04S(|FeHaS2{ed)@|GW@Q*w`4FJk}mZcO^Ww;QJQ3V*kw zW-tOx+*SbytoW~Qs1`RR{~vvV{|~?VI_F0#rl{ZQ^TP=*7*l(7_G{;AS_S+Uj-Bws literal 0 HcmV?d00001 diff --git a/doc/config.svg b/doc/config.svg new file mode 100644 index 0000000..3ff4bc9 --- /dev/null +++ b/doc/config.svg @@ -0,0 +1,257 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + Config + + + + + OptionDescription + + + + Option + + + + Option + + + + OptionDescription + + + + Option + + + + + + + + From 28c416dd84bba8eb1e5164af9f00de43a5aa5a6e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 19 Sep 2013 21:51:55 +0200 Subject: [PATCH 11/36] add allow_reserved in IPOption --- test/test_config_ip.py | 9 +++++++++ tiramisu/option.py | 9 +++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/test/test_config_ip.py b/test/test_config_ip.py index e889c92..2bdba53 100644 --- a/test/test_config_ip.py +++ b/test/test_config_ip.py @@ -29,6 +29,15 @@ def test_ip_default(): c.a == '88.88.88.88' +def test_ip_reserved(): + a = IPOption('a', '') + b = IPOption('b', '', allow_reserved=True) + od = OptionDescription('od', '', [a, b]) + c = Config(od) + raises(ValueError, "c.a = '226.94.1.1'") + c.b = '226.94.1.1' + + def test_network(): a = NetworkOption('a', '') od = OptionDescription('od', '', [a]) diff --git a/tiramisu/option.py b/tiramisu/option.py index 1948f39..0c7e732 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -702,14 +702,15 @@ class SymLinkOption(BaseOption): class IPOption(Option): "represents the choice of an ip" - __slots__ = ('_only_private',) + __slots__ = ('_only_private', '_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): + properties=None, only_private=False, allow_reserved=False): self._only_private = only_private + self._allow_reserved = allow_reserved super(IPOption, self).__init__(name, doc, default=default, default_multi=default_multi, callback=callback, @@ -722,8 +723,8 @@ class IPOption(Option): def _validate(self, value): ip = IP('{0}/32'.format(value)) - if ip.iptype() == 'RESERVED': - raise ValueError(_("IP shall not be in reserved class")) + 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': raise ValueError(_("IP must be in private class")) From ae4df32d0e1cbe48d2d4c1353a40f60ccf4c9e6e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 19 Sep 2013 23:02:15 +0200 Subject: [PATCH 12/36] error if change slave len for default's slave option --- test/test_parsing_group.py | 23 +++++++++++++++++++---- tiramisu/value.py | 13 ++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/test/test_parsing_group.py b/test/test_parsing_group.py index 7b9dffe..7ecd860 100644 --- a/test/test_parsing_group.py +++ b/test/test_parsing_group.py @@ -64,9 +64,9 @@ def test_make_dict_filter(): config = Config(descr) config.read_write() subresult = {'numero_etab': None, 'nombre_interfaces': 1, - 'serveur_ntp': [], 'mode_conteneur_actif': False, - 'time_zone': 'Paris', 'nom_machine': 'eoleng', - 'activer_proxy_client': False} + 'serveur_ntp': [], 'mode_conteneur_actif': False, + 'time_zone': 'Paris', 'nom_machine': 'eoleng', + 'activer_proxy_client': False} result = {} for key, value in subresult.items(): result['general.' + key] = value @@ -114,7 +114,6 @@ def test_iter_not_group(): raises(TypeError, "list(config.iter_groups(group_type='family'))") - def test_groups_with_master(): ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True) @@ -252,6 +251,22 @@ def test_values_with_master_and_slaves_master(): assert cfg.ip_admin_eth0.netmask_admin_eth0 == [] +def test_values_with_master_and_slaves_master_error(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True) + interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + interface1.impl_set_group_type(groups.master) + maconfig = OptionDescription('toto', '', [interface1]) + cfg = Config(maconfig) + cfg.read_write() + cfg.ip_admin_eth0.ip_admin_eth0 = ["192.168.230.145", "192.168.230.145"] + raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']") + raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0', '255.255.255.0']") + cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0'] + raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0']") + raises(SlaveError, "cfg.ip_admin_eth0.netmask_admin_eth0 = ['255.255.255.0', '255.255.255.0', '255.255.255.0']") + + def test_values_with_master_owner(): ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau", multi=True) diff --git a/tiramisu/value.py b/tiramisu/value.py index b600eb8..c6f0e76 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -251,7 +251,7 @@ class Values(object): opt.impl_validate(value, self.context(), 'validator' in self.context().cfgimpl_get_settings()) if opt.impl_is_multi() and not isinstance(value, Multi): - value = Multi(value, self.context, opt, path) + value = Multi(value, self.context, opt, path, setitem=True) self._setvalue(opt, path, value, force_permissive=force_permissive, is_write=is_write) @@ -369,11 +369,13 @@ class Multi(list): that support item notation for the values of multi options""" __slots__ = ('opt', 'path', 'context') - def __init__(self, value, context, opt, path, validate=True): + def __init__(self, value, context, opt, path, validate=True, + setitem=False): """ :param value: the Multi wraps a list value :param context: the home config that has the values :param opt: the option object that have this Multi value + :param setitem: only if set a value """ self.opt = opt self.path = path @@ -383,12 +385,12 @@ class Multi(list): if not isinstance(value, list): value = [value] if validate and self.opt.impl_get_multitype() == multitypes.slave: - value = self._valid_slave(value) + value = self._valid_slave(value, setitem) elif self.opt.impl_get_multitype() == multitypes.master: self._valid_master(value) super(Multi, self).__init__(value) - def _valid_slave(self, value): + def _valid_slave(self, value, setitem): #if slave, had values until master's one values = self.context().cfgimpl_get_values() masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt( @@ -396,8 +398,9 @@ class Multi(list): mastervalue = getattr(self.context(), masterp) masterlen = len(mastervalue) valuelen = len(value) + is_default_owner = not values._is_default_owner(self.path) or setitem if valuelen > masterlen or (valuelen < masterlen and - not values._is_default_owner(self.path)): + is_default_owner): raise SlaveError(_("invalid len for the slave: {0}" " which has {1} as master").format( self.opt._name, masterp)) From 972dff0a1ca356b5622ee92ce43d3ff2ff46ef3a Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Fri, 20 Sep 2013 23:47:40 +0200 Subject: [PATCH 13/36] serialize new callback --- test/test_state.py | 33 ++++++++++++++++++++++++ tiramisu/option.py | 63 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/test/test_state.py b/test/test_state.py index 03ab670..ea1956c 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -3,6 +3,10 @@ from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \ from pickle import dumps, loads +def return_value(value=None): + return value + + def _get_slots(opt): slots = set() for subclass in opt.__class__.__mro__: @@ -65,6 +69,18 @@ def _diff_opt(opt1, opt2): for index, consistency in enumerate(val1): assert consistency[0] == val2[index][0] assert consistency[1]._name == val2[index][1]._name + elif attr == '_callback': + assert val1[0] == val2[0] + if val1[1] is not None: + for key, values in val1[1].items(): + for idx, value in enumerate(values): + if isinstance(value, tuple): + assert val1[1][key][idx][0]._name == val2[1][key][idx][0]._name + assert val1[1][key][idx][1] == val2[1][key][idx][1] + else: + assert val1[1][key][idx] == val2[1][key][idx] + else: + assert val1[1] == val2[1] else: assert val1 == val2 @@ -104,6 +120,23 @@ def test_diff_opt_cache(): _diff_opt(o1.o.s, q.o.s) +def test_diff_opt_callback(): + b = BoolOption('b', '', callback=return_value) + b2 = BoolOption('b2', '', callback=return_value, callback_params={'': ('yes',)}) + b3 = BoolOption('b3', '', callback=return_value, callback_params={'': ('yes', (b, False)), 'value': ('no',)}) + o = OptionDescription('o', '', [b, b2, b3]) + o1 = OptionDescription('o1', '', [o]) + o1.impl_build_cache() + + a = dumps(o1) + q = loads(a) + _diff_opt(o1, q) + _diff_opt(o1.o, q.o) + _diff_opt(o1.o.b, q.o.b) + _diff_opt(o1.o.b2, q.o.b2) + _diff_opt(o1.o.b3, q.o.b3) + + def test_no_state_attr(): # all _state_xxx attributes should be deleted b = BoolOption('b', '') diff --git a/tiramisu/option.py b/tiramisu/option.py index 0c7e732..89d960a 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -188,9 +188,11 @@ class BaseOption(object): _list_cons = [] for _con in _cons: if load: - _list_cons.append(descr.impl_get_opt_by_path(_con)) + _list_cons.append( + descr.impl_get_opt_by_path(_con)) else: - _list_cons.append(descr.impl_get_path_by_opt(_con)) + _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) @@ -322,7 +324,8 @@ class Option(BaseOption): Reminder: an Option object is **not** a container for the value. """ __slots__ = ('_multi', '_validator', '_default_multi', '_default', - '_callback', '_multitype', '_master_slaves', '__weakref__') + '_state_callback', '_callback', '_multitype', + '_master_slaves', '__weakref__') _empty = '' def __init__(self, name, doc, default=None, default_multi=None, @@ -558,6 +561,57 @@ class Option(BaseOption): "must be different as {2} option" "").format(value, self._name, optname)) + def _impl_convert_callbacks(self, descr, load=False): + if not load and self._callback is None: + self._state_callback = None + elif load and self._state_callback is None: + self._callback = None + del(self._state_callback) + else: + if load: + callback, callback_params = self._state_callback + else: + callback, callback_params = self._callback + if callback_params is not None: + cllbck_prms = {} + for key, values in callback_params.items(): + vls = [] + for value in values: + if isinstance(value, tuple): + if load: + value = (descr.impl_get_opt_by_path(value[0]), + value[1]) + else: + value = (descr.impl_get_path_by_opt(value[0]), + value[1]) + vls.append(value) + cllbck_prms[key] = tuple(vls) + else: + cllbck_prms = None + + if load: + del(self._state_callback) + self._callback = (callback, cllbck_prms) + else: + self._state_callback = (callback, cllbck_prms) + + # serialize + def _impl_getstate(self, descr): + """the under the hood stuff that need to be done + before the serialization. + """ + self._stated = True + self._impl_convert_callbacks(descr) + super(Option, self)._impl_getstate(descr) + + # unserialize + def _impl_setstate(self, descr): + """the under the hood stuff that need to be done + before the serialization. + """ + self._impl_convert_callbacks(descr, load=True) + super(Option, self)._impl_setstate(descr) + class ChoiceOption(Option): """represents a choice out of several objects. @@ -1287,4 +1341,5 @@ def validate_callback(callback, callback_params, type_): if force_permissive not in [True, False]: raise ValueError(_('{0}_params should have a boolean' 'not a {0} for second argument' - ).format(type_, type(force_permissive))) + ).format(type_, type( + force_permissive))) From c84d13a1c6c59e82d570d6c6fe645f4035528423 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 22 Sep 2013 20:57:52 +0200 Subject: [PATCH 14/36] we can serialize Config now --- test/test_option_setting.py | 18 ++--- test/test_state.py | 96 +++++++++++++++++++++++++ tiramisu/config.py | 42 ++++++++++- tiramisu/option.py | 27 ++----- tiramisu/setting.py | 22 +++++- tiramisu/storage/__init__.py | 25 +++++-- tiramisu/storage/dictionary/__init__.py | 4 +- tiramisu/storage/dictionary/setting.py | 17 +++-- tiramisu/storage/dictionary/storage.py | 12 ++-- tiramisu/storage/dictionary/value.py | 2 +- tiramisu/storage/sqlite3/__init__.py | 4 +- tiramisu/storage/sqlite3/setting.py | 77 +++++++++++--------- tiramisu/storage/sqlite3/sqlite3db.py | 7 +- tiramisu/storage/sqlite3/storage.py | 17 +++-- tiramisu/storage/sqlite3/value.py | 24 +++---- tiramisu/storage/{cache.py => util.py} | 54 +++++++++++++- tiramisu/value.py | 14 +++- 17 files changed, 354 insertions(+), 108 deletions(-) rename tiramisu/storage/{cache.py => util.py} (51%) diff --git a/test/test_option_setting.py b/test/test_option_setting.py index 2dc76ab..ed5f7d7 100644 --- a/test/test_option_setting.py +++ b/test/test_option_setting.py @@ -327,23 +327,23 @@ def test_reset_properties(): cfg = Config(descr) setting = cfg.cfgimpl_get_settings() option = cfg.cfgimpl_get_description().gc.dummy - assert setting._p_.get_properties(cfg) == {} + assert setting._p_.get_modified_properties() == {} setting.append('frozen') - assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'cache', 'validator'))} + assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'cache', 'validator'))} setting.reset() - assert setting._p_.get_properties(cfg) == {} + assert setting._p_.get_modified_properties() == {} setting[option].append('test') - assert setting._p_.get_properties(cfg) == {'gc.dummy': set(('test',))} + assert setting._p_.get_modified_properties() == {'gc.dummy': set(('test',))} setting.reset() - assert setting._p_.get_properties(cfg) == {'gc.dummy': set(('test',))} + assert setting._p_.get_modified_properties() == {'gc.dummy': set(('test',))} setting.append('frozen') - assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))} + assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))} setting.reset(option) - assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache'))} + assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache'))} setting[option].append('test') - assert setting._p_.get_properties(cfg) == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))} + assert setting._p_.get_modified_properties() == {None: set(('frozen', 'expire', 'validator', 'cache')), 'gc.dummy': set(('test',))} setting.reset(all_properties=True) - assert setting._p_.get_properties(cfg) == {} + assert setting._p_.get_modified_properties() == {} raises(ValueError, 'setting.reset(all_properties=True, opt=option)') a = descr.wantref setting[a].append('test') diff --git a/test/test_state.py b/test/test_state.py index ea1956c..8587b4a 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -1,5 +1,9 @@ from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \ OptionDescription +from tiramisu.config import Config +from tiramisu.setting import owners +from tiramisu.storage import delete_session +from tiramisu.error import ConfigError from pickle import dumps, loads @@ -152,3 +156,95 @@ def test_no_state_attr(): _no_state(q.o.b) _no_state(q.o.u) _no_state(q.o.s) + + +def test_state_config(): + val1 = BoolOption('val1', "") + maconfig = OptionDescription('rootconfig', '', [val1]) + try: + cfg = Config(maconfig, persistent=True, session_id='29090931') + except ValueError: + cfg = Config(maconfig, session_id='29090931') + cfg._impl_test = True + a = dumps(cfg) + q = loads(a) + _diff_opt(maconfig, q.cfgimpl_get_description()) + assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values() + assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties() + assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives() + try: + delete_session('29090931') + except ConfigError: + pass + + +def test_state_properties(): + val1 = BoolOption('val1', "") + maconfig = OptionDescription('rootconfig', '', [val1]) + try: + cfg = Config(maconfig, persistent=True, session_id='29090932') + except ValueError: + cfg = Config(maconfig, session_id='29090932') + cfg._impl_test = True + cfg.read_write() + cfg.cfgimpl_get_settings()[val1].append('test') + a = dumps(cfg) + q = loads(a) + _diff_opt(maconfig, q.cfgimpl_get_description()) + assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values() + assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties() + assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives() + try: + delete_session('29090931') + except ConfigError: + pass + + +def test_state_values(): + val1 = BoolOption('val1', "") + maconfig = OptionDescription('rootconfig', '', [val1]) + try: + cfg = Config(maconfig, persistent=True, session_id='29090933') + except ValueError: + cfg = Config(maconfig, session_id='29090933') + cfg._impl_test = True + cfg.val1 = True + a = dumps(cfg) + q = loads(a) + _diff_opt(maconfig, q.cfgimpl_get_description()) + assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values() + assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties() + assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives() + q.val1 = False + #assert cfg.val1 is True + assert q.val1 is False + try: + delete_session('29090931') + except ConfigError: + pass + + +def test_state_values_owner(): + val1 = BoolOption('val1', "") + maconfig = OptionDescription('rootconfig', '', [val1]) + try: + cfg = Config(maconfig, persistent=True, session_id='29090934') + except ValueError: + cfg = Config(maconfig, session_id='29090934') + cfg._impl_test = True + owners.addowner('newowner') + cfg.cfgimpl_get_settings().setowner(owners.newowner) + cfg.val1 = True + a = dumps(cfg) + q = loads(a) + _diff_opt(maconfig, q.cfgimpl_get_description()) + assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values() + assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties() + assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives() + q.val1 = False + nval1 = q.cfgimpl_get_description().val1 + assert q.getowner(nval1) == owners.newowner + try: + delete_session('29090931') + except ConfigError: + pass diff --git a/tiramisu/config.py b/tiramisu/config.py index fac3caf..d817fa1 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -24,7 +24,8 @@ import weakref from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.option import OptionDescription, Option, SymLinkOption from tiramisu.setting import groups, Settings, default_encoding -from tiramisu.storage import get_storages +from tiramisu.storage import get_storages, get_storage, set_storage, \ + _impl_getstate_setting from tiramisu.value import Values from tiramisu.i18n import _ @@ -521,7 +522,7 @@ class CommonConfig(SubConfig): # ____________________________________________________________ class Config(CommonConfig): "main configuration management entry" - __slots__ = ('__weakref__', ) + __slots__ = ('__weakref__', '_impl_test') def __init__(self, descr, session_id=None, persistent=False): """ Configuration option management master class @@ -542,6 +543,43 @@ class Config(CommonConfig): super(Config, self).__init__(descr, weakref.ref(self)) self._impl_build_all_paths() self._impl_meta = None + #undocumented option used only in test script + self._impl_test = False + + def __getstate__(self): + if self._impl_meta is not None: + raise ConfigError('cannot serialize Config with meta') + slots = set() + for subclass in self.__class__.__mro__: + if subclass is not object: + slots.update(subclass.__slots__) + slots -= frozenset(['_impl_context', '__weakref__']) + state = {} + for slot in slots: + try: + state[slot] = getattr(self, slot) + except AttributeError: + pass + storage = self._impl_values._p_._storage + if not storage.serializable: + raise ConfigError('this storage is not serialisable, could be a ' + 'none persistent storage') + state['_storage'] = {'session_id': storage.session_id, + 'persistent': storage.persistent} + state['_impl_setting'] = _impl_getstate_setting() + return state + + def __setstate__(self, state): + for key, value in state.items(): + if key not in ['_storage', '_impl_setting']: + setattr(self, key, value) + set_storage(**state['_impl_setting']) + self._impl_context = weakref.ref(self) + self._impl_settings.context = weakref.ref(self) + self._impl_values.context = weakref.ref(self) + storage = get_storage(test=self._impl_test, **state['_storage']) + self._impl_values._impl_setstate(storage) + self._impl_settings._impl_setstate(storage) def cfgimpl_reset_cache(self, only_expired=False, diff --git a/tiramisu/option.py b/tiramisu/option.py index 89d960a..10aba5c 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -242,8 +242,9 @@ class BaseOption(object): :param descr: the parent :class:`tiramisu.option.OptionDescription` """ self._stated = True - self._impl_convert_consistencies(descr) - self._impl_convert_requires(descr) + for func in dir(self): + if func.startswith('_impl_convert_'): + getattr(self, func)(descr) try: self._state_readonly = self._readonly except AttributeError: @@ -294,8 +295,9 @@ class BaseOption(object): :type descr: :class:`tiramisu.option.OptionDescription` """ - self._impl_convert_consistencies(descr, load=True) - self._impl_convert_requires(descr, load=True) + for func in dir(self): + if func.startswith('_impl_convert_'): + getattr(self, func)(descr, load=True) try: self._readonly = self._state_readonly del(self._state_readonly) @@ -595,23 +597,6 @@ class Option(BaseOption): else: self._state_callback = (callback, cllbck_prms) - # serialize - def _impl_getstate(self, descr): - """the under the hood stuff that need to be done - before the serialization. - """ - self._stated = True - self._impl_convert_callbacks(descr) - super(Option, self)._impl_getstate(descr) - - # unserialize - def _impl_setstate(self, descr): - """the under the hood stuff that need to be done - before the serialization. - """ - self._impl_convert_callbacks(descr, load=True) - super(Option, self)._impl_setstate(descr) - class ChoiceOption(Option): """represents a choice out of several objects. diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 531e846..684ec74 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -105,7 +105,7 @@ rw_remove = set(['permissive', 'everything_frozen', 'mandatory']) # ____________________________________________________________ -class _NameSpace: +class _NameSpace(object): """convenient class that emulates a module and builds constants (that is, unique names) when attribute is added, we cannot delete it @@ -591,3 +591,23 @@ class Settings(object): :returns: path """ return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt) + + def get_modified_properties(self): + return self._p_.get_modified_properties() + + def get_modified_permissives(self): + return self._p_.get_modified_permissives() + + def __getstate__(self): + return {'_p_': self._p_, '_owner': str(self._owner)} + + def _impl_setstate(self, storage): + self._p_._storage = storage + + def __setstate__(self, states): + self._p_ = states['_p_'] + try: + self._owner = getattr(owners, states['_owner']) + except AttributeError: + owners.addowner(states['_owner']) + self._owner = getattr(owners, states['_owner']) diff --git a/tiramisu/storage/__init__.py b/tiramisu/storage/__init__.py index 1394258..c232472 100644 --- a/tiramisu/storage/__init__.py +++ b/tiramisu/storage/__init__.py @@ -68,7 +68,7 @@ class StorageType(object): storage_type = StorageType() -def set_storage(name, **args): +def set_storage(name, **kwargs): """Change storage's configuration :params name: is the storage name. If storage is already set, cannot @@ -77,16 +77,31 @@ def set_storage(name, **args): Other attributes are differents according to the selected storage's name """ storage_type.set(name) - settings = storage_type.get().Setting() - for option, value in args.items(): + setting = storage_type.get().setting + for option, value in kwargs.items(): try: - getattr(settings, option) - setattr(settings, option, value) + getattr(setting, option) + setattr(setting, option, value) except AttributeError: raise ValueError(_('option {0} not already exists in storage {1}' '').format(option, name)) +def _impl_getstate_setting(): + setting = storage_type.get().setting + state = {'name': storage_type.storage_type} + for var in dir(setting): + if not var.startswith('_'): + state[var] = getattr(setting, var) + return state + + +def get_storage(session_id, persistent, test): + """all used when __setstate__ a Config + """ + return storage_type.get().Storage(session_id, persistent, test) + + def get_storages(context, session_id, persistent): def gen_id(config): return str(id(config)) + str(time()) diff --git a/tiramisu/storage/dictionary/__init__.py b/tiramisu/storage/dictionary/__init__.py index dadce23..bc81450 100644 --- a/tiramisu/storage/dictionary/__init__.py +++ b/tiramisu/storage/dictionary/__init__.py @@ -26,6 +26,6 @@ use it. But if something goes wrong, you will lost your modifications. """ from .value import Values from .setting import Settings -from .storage import Setting, Storage, list_sessions, delete_session +from .storage import setting, Storage, list_sessions, delete_session -__all__ = (Setting, Values, Settings, Storage, list_sessions, delete_session) +__all__ = (setting, Values, Settings, Storage, list_sessions, delete_session) diff --git a/tiramisu/storage/dictionary/setting.py b/tiramisu/storage/dictionary/setting.py index 706ab2a..1b7001b 100644 --- a/tiramisu/storage/dictionary/setting.py +++ b/tiramisu/storage/dictionary/setting.py @@ -17,7 +17,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ____________________________________________________________ -from ..cache import Cache +from ..util import Cache class Settings(Cache): @@ -50,12 +50,21 @@ class Settings(Cache): except KeyError: pass - def get_properties(self, context): - return self._properties - # permissive def setpermissive(self, path, permissive): self._permissives[path] = frozenset(permissive) def getpermissive(self, path=None): return self._permissives.get(path, frozenset()) + + def get_modified_properties(self): + """return all modified settings in a dictionary + example: {'path1': set(['prop1', 'prop2'])} + """ + return self._properties + + def get_modified_permissives(self): + """return all modified permissives in a dictionary + example: {'path1': set(['perm1', 'perm2'])} + """ + return self._permissives diff --git a/tiramisu/storage/dictionary/storage.py b/tiramisu/storage/dictionary/storage.py index 6e15c1b..465fe26 100644 --- a/tiramisu/storage/dictionary/storage.py +++ b/tiramisu/storage/dictionary/storage.py @@ -18,9 +18,10 @@ # ____________________________________________________________ from tiramisu.i18n import _ from tiramisu.error import ConfigError +from ..util import SerializeObject -class Setting(object): +class Setting(SerializeObject): """Dictionary storage has no particular setting. """ pass @@ -39,15 +40,18 @@ def delete_session(session_id): class Storage(object): - __slots__ = ('session_id', 'values', 'settings') + __slots__ = ('session_id', 'persistent') storage = 'dictionary' + #if object could be serializable + serializable = True - def __init__(self, session_id, persistent): - if session_id in _list_sessions: + def __init__(self, session_id, persistent, test=False): + if not test and session_id in _list_sessions: raise ValueError(_('session already used')) if persistent: raise ValueError(_('a dictionary cannot be persistent')) self.session_id = session_id + self.persistent = persistent _list_sessions.append(self.session_id) def __del__(self): diff --git a/tiramisu/storage/dictionary/value.py b/tiramisu/storage/dictionary/value.py index c435d06..fedf1ec 100644 --- a/tiramisu/storage/dictionary/value.py +++ b/tiramisu/storage/dictionary/value.py @@ -18,7 +18,7 @@ # # ____________________________________________________________ -from ..cache import Cache +from ..util import Cache class Values(Cache): diff --git a/tiramisu/storage/sqlite3/__init__.py b/tiramisu/storage/sqlite3/__init__.py index dc6c14b..8d79070 100644 --- a/tiramisu/storage/sqlite3/__init__.py +++ b/tiramisu/storage/sqlite3/__init__.py @@ -24,6 +24,6 @@ You should not configure differents Configs with same session_id. """ from .value import Values from .setting import Settings -from .storage import Setting, Storage, list_sessions, delete_session +from .storage import setting, Storage, list_sessions, delete_session -__all__ = (Setting, Values, Settings, Storage, list_sessions, delete_session) +__all__ = (setting, Values, Settings, Storage, list_sessions, delete_session) diff --git a/tiramisu/storage/sqlite3/setting.py b/tiramisu/storage/sqlite3/setting.py index 720849b..ed79181 100644 --- a/tiramisu/storage/sqlite3/setting.py +++ b/tiramisu/storage/sqlite3/setting.py @@ -30,22 +30,22 @@ class Settings(Sqlite3DB): permissives_table += 'primary key, permissives text)' # should init cache too super(Settings, self).__init__(storage) - self.storage.execute(settings_table, commit=False) - self.storage.execute(permissives_table) + self._storage.execute(settings_table, commit=False) + self._storage.execute(permissives_table) # propertives def setproperties(self, path, properties): path = self._sqlite_encode_path(path) - self.storage.execute("DELETE FROM property WHERE path = ?", (path,), - False) - self.storage.execute("INSERT INTO property(path, properties) VALUES " - "(?, ?)", (path, - self._sqlite_encode(properties))) + self._storage.execute("DELETE FROM property WHERE path = ?", (path,), + False) + self._storage.execute("INSERT INTO property(path, properties) VALUES " + "(?, ?)", (path, + self._sqlite_encode(properties))) def getproperties(self, path, default_properties): path = self._sqlite_encode_path(path) - value = self.storage.select("SELECT properties FROM property WHERE " - "path = ?", (path,)) + value = self._storage.select("SELECT properties FROM property WHERE " + "path = ?", (path,)) if value is None: return set(default_properties) else: @@ -53,42 +53,53 @@ class Settings(Sqlite3DB): def hasproperties(self, path): path = self._sqlite_encode_path(path) - return self.storage.select("SELECT properties FROM property WHERE " - "path = ?", (path,)) is not None + return self._storage.select("SELECT properties FROM property WHERE " + "path = ?", (path,)) is not None def reset_all_propertives(self): - self.storage.execute("DELETE FROM property") + self._storage.execute("DELETE FROM property") def reset_properties(self, path): path = self._sqlite_encode_path(path) - self.storage.execute("DELETE FROM property WHERE path = ?", (path,)) - - def get_properties(self, context): - """return all properties in a dictionary - """ - ret = {} - for path, properties in self.storage.select("SELECT * FROM property", - only_one=False): - path = self._sqlite_decode_path(path) - properties = self._sqlite_decode(properties) - ret[path] = properties - return ret + self._storage.execute("DELETE FROM property WHERE path = ?", (path,)) # permissive def setpermissive(self, path, permissive): path = self._sqlite_encode_path(path) - self.storage.execute("DELETE FROM permissive WHERE path = ?", (path,), - False) - self.storage.execute("INSERT INTO permissive(path, permissives) " - "VALUES (?, ?)", (path, - self._sqlite_encode(permissive) - )) + self._storage.execute("DELETE FROM permissive WHERE path = ?", (path,), + False) + self._storage.execute("INSERT INTO permissive(path, permissives) " + "VALUES (?, ?)", (path, + self._sqlite_encode(permissive) + )) def getpermissive(self, path='_none'): - permissives = self.storage.select("SELECT permissives FROM " - "permissive WHERE path = ?", - (path,)) + permissives = self._storage.select("SELECT permissives FROM " + "permissive WHERE path = ?", + (path,)) if permissives is None: return frozenset() else: return frozenset(self._sqlite_decode(permissives[0])) + + def get_modified_properties(self): + """return all modified settings in a dictionary + example: {'path1': set(['prop1', 'prop2'])} + """ + ret = {} + for path, properties in self._storage.select("SELECT * FROM property", + only_one=False): + path = self._sqlite_decode_path(path) + ret[path] = self._sqlite_decode(properties) + return ret + + def get_modified_permissives(self): + """return all modified permissives in a dictionary + example: {'path1': set(['perm1', 'perm2'])} + """ + ret = {} + for path, permissives in self._storage.select("SELECT * FROM permissive", + only_one=False): + path = self._sqlite_decode_path(path) + ret[path] = self._sqlite_decode(permissives) + return ret diff --git a/tiramisu/storage/sqlite3/sqlite3db.py b/tiramisu/storage/sqlite3/sqlite3db.py index 9a967cd..68f2886 100644 --- a/tiramisu/storage/sqlite3/sqlite3db.py +++ b/tiramisu/storage/sqlite3/sqlite3db.py @@ -17,8 +17,11 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ____________________________________________________________ -from cPickle import loads, dumps -from ..cache import Cache +try: + from cPickle import loads, dumps +except ImportError: + from pickle import loads, dumps +from ..util import Cache class Sqlite3DB(Cache): diff --git a/tiramisu/storage/sqlite3/storage.py b/tiramisu/storage/sqlite3/storage.py index 2ab8e08..3b4f265 100644 --- a/tiramisu/storage/sqlite3/storage.py +++ b/tiramisu/storage/sqlite3/storage.py @@ -22,9 +22,10 @@ from os import unlink from os.path import basename, splitext, join import sqlite3 from glob import glob +from ..util import SerializeObject -class Setting(object): +class Setting(SerializeObject): """:param extension: database file extension (by default: db) :param dir_database: root database directory (by default: /tmp) """ @@ -52,13 +53,17 @@ def delete_session(session_id): class Storage(object): - __slots__ = ('_conn', '_cursor', 'persistent', '_session_id') + __slots__ = ('_conn', '_cursor', 'persistent', 'session_id', 'serializable') storage = 'sqlite3' - def __init__(self, session_id, persistent): + def __init__(self, session_id, persistent, test=False): self.persistent = persistent - self._session_id = session_id - self._conn = sqlite3.connect(_gen_filename(self._session_id)) + if self.persistent: + self.serializable = True + else: + self.serializable = False + self.session_id = session_id + self._conn = sqlite3.connect(_gen_filename(self.session_id)) self._conn.text_factory = str self._cursor = self._conn.cursor() @@ -80,4 +85,4 @@ class Storage(object): self._cursor.close() self._conn.close() if not self.persistent: - delete_session(self._session_id) + delete_session(self.session_id) diff --git a/tiramisu/storage/sqlite3/value.py b/tiramisu/storage/sqlite3/value.py index 3f76e2c..672ecab 100644 --- a/tiramisu/storage/sqlite3/value.py +++ b/tiramisu/storage/sqlite3/value.py @@ -32,11 +32,11 @@ class Values(Sqlite3DB): super(Values, self).__init__(storage) values_table = 'CREATE TABLE IF NOT EXISTS value(path text primary ' values_table += 'key, value text, owner text)' - self.storage.execute(values_table, commit=False) + self._storage.execute(values_table, commit=False) informations_table = 'CREATE TABLE IF NOT EXISTS information(key text primary ' informations_table += 'key, value text)' - self.storage.execute(informations_table) - for owner in self.storage.select("SELECT DISTINCT owner FROM value", tuple(), False): + self._storage.execute(informations_table) + for owner in self._storage.select("SELECT DISTINCT owner FROM value", tuple(), False): try: getattr(owners, owner[0]) except AttributeError: @@ -44,7 +44,7 @@ class Values(Sqlite3DB): # sqlite def _sqlite_select(self, path): - return self.storage.select("SELECT value FROM value WHERE path = ?", + return self._storage.select("SELECT value FROM value WHERE path = ?", (path,)) # value @@ -54,7 +54,7 @@ class Values(Sqlite3DB): """ self.resetvalue(path) path = self._sqlite_encode_path(path) - self.storage.execute("INSERT INTO value(path, value, owner) VALUES " + self._storage.execute("INSERT INTO value(path, value, owner) VALUES " "(?, ?, ?)", (path, self._sqlite_encode(value), str(owner))) @@ -76,14 +76,14 @@ class Values(Sqlite3DB): """remove value means delete value in storage """ path = self._sqlite_encode_path(path) - self.storage.execute("DELETE FROM value WHERE path = ?", (path,)) + self._storage.execute("DELETE FROM value WHERE path = ?", (path,)) def get_modified_values(self): """return all values in a dictionary example: {option1: (owner, 'value1'), option2: (owner, 'value2')} """ ret = {} - for path, value, owner in self.storage.select("SELECT * FROM value", + for path, value, owner in self._storage.select("SELECT * FROM value", only_one=False): path = self._sqlite_decode_path(path) owner = getattr(owners, owner) @@ -97,7 +97,7 @@ class Values(Sqlite3DB): """change owner for an option """ path = self._sqlite_encode_path(path) - self.storage.execute("UPDATE value SET owner = ? WHERE path = ?", + self._storage.execute("UPDATE value SET owner = ? WHERE path = ?", (str(owner), path)) def getowner(self, path, default): @@ -105,7 +105,7 @@ class Values(Sqlite3DB): return: owner object """ path = self._sqlite_encode_path(path) - owner = self.storage.select("SELECT owner FROM value WHERE path = ?", + owner = self._storage.select("SELECT owner FROM value WHERE path = ?", (path,)) if owner is None: return default @@ -125,9 +125,9 @@ class Values(Sqlite3DB): :param key: information's key (ex: "help", "doc" :param value: information's value (ex: "the help string") """ - self.storage.execute("DELETE FROM information WHERE key = ?", (key,), + self._storage.execute("DELETE FROM information WHERE key = ?", (key,), False) - self.storage.execute("INSERT INTO information(key, value) VALUES " + self._storage.execute("INSERT INTO information(key, value) VALUES " "(?, ?)", (key, self._sqlite_encode(value))) def get_information(self, key): @@ -135,7 +135,7 @@ class Values(Sqlite3DB): :param key: the item string (ex: "help") """ - value = self.storage.select("SELECT value FROM information WHERE key = ?", + value = self._storage.select("SELECT value FROM information WHERE key = ?", (key,)) if value is None: raise ValueError("not found") diff --git a/tiramisu/storage/cache.py b/tiramisu/storage/util.py similarity index 51% rename from tiramisu/storage/cache.py rename to tiramisu/storage/util.py index 347d270..68482e6 100644 --- a/tiramisu/storage/cache.py +++ b/tiramisu/storage/util.py @@ -17,15 +17,65 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ____________________________________________________________ +from tiramisu.setting import owners + + +class SerializeObject(object): + def __getstate__(self): + ret = {} + for key in dir(self): + if not key.startswith('__'): + ret[key] = getattr(self, key) + return ret class Cache(object): - __slots__ = ('_cache', 'storage') + __slots__ = ('_cache', '_storage') key_is_path = False def __init__(self, storage): self._cache = {} - self.storage = storage + self._storage = storage + + def __getstate__(self): + slots = set() + for subclass in self.__class__.__mro__: + if subclass is not object: + slots.update(subclass.__slots__) + slots -= frozenset(['__weakref__', '_storage']) + states = {} + for slot in slots: + try: + value = getattr(self, slot) + #value has owners object, need 'str()' it + if slot == '_values': + _value = {} + for key, values in value.items(): + vals = list(values) + vals[0] = str(vals[0]) + _value[key] = tuple(vals) + states[slot] = _value + else: + states[slot] = value + except AttributeError: + pass + return states + + def __setstate__(self, states): + for key, value in states.items(): + #value has owners object, need to reconstruct it + if key == '_values': + _value = {} + for key_, values_ in value.items(): + vals = list(values_) + try: + vals[0] = getattr(owners, vals[0]) + except AttributeError: + owners.addowner(vals[0]) + vals[0] = getattr(owners, vals[0]) + _value[key_] = tuple(vals) + value = _value + setattr(self, key, value) def setcache(self, path, val, time): self._cache[path] = (val, time) diff --git a/tiramisu/value.py b/tiramisu/value.py index c6f0e76..043f15f 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -204,7 +204,9 @@ class Values(object): if not no_value_slave: try: value = self._getcallback_value(opt, max_len=lenmaster) - except ConfigError as config_error: + except ConfigError as err: + # cannot assign config_err directly in python 3.3 + config_error = err value = None # should not raise PropertiesOptionError if option is # mandatory @@ -359,6 +361,14 @@ class Values(object): raise ValueError(_("information's item" " not found: {0}").format(key)) + def __getstate__(self): + return {'_p_': self._p_} + + def _impl_setstate(self, storage): + self._p_._storage = storage + + def __setstate__(self, states): + self._p_ = states['_p_'] # ____________________________________________________________ # multi types @@ -386,7 +396,7 @@ class Multi(list): value = [value] if validate and self.opt.impl_get_multitype() == multitypes.slave: value = self._valid_slave(value, setitem) - elif self.opt.impl_get_multitype() == multitypes.master: + elif validate and self.opt.impl_get_multitype() == multitypes.master: self._valid_master(value) super(Multi, self).__init__(value) From 051f1c877414c5793c4905aa529593ffc3b80ea0 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 22 Sep 2013 21:23:12 +0200 Subject: [PATCH 15/36] tiramisu/config.py: - find byvalue support Multi tiramisu/value.py: - Multi's pop comment --- tiramisu/config.py | 11 ++++++----- tiramisu/value.py | 13 ++++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/tiramisu/config.py b/tiramisu/config.py index d817fa1..261f6ed 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -26,7 +26,7 @@ from tiramisu.option import OptionDescription, Option, SymLinkOption from tiramisu.setting import groups, Settings, default_encoding from tiramisu.storage import get_storages, get_storage, set_storage, \ _impl_getstate_setting -from tiramisu.value import Values +from tiramisu.value import Values, Multi from tiramisu.i18n import _ @@ -294,12 +294,13 @@ class SubConfig(object): return True try: value = getattr(self, path) - if value == byvalue: - return True + if isinstance(value, Multi): + return byvalue in value + else: + return value == byvalue except PropertiesOptionError: # a property is a restriction # upon the access of the value - pass - return False + return False def _filter_by_type(): if bytype is None: diff --git a/tiramisu/value.py b/tiramisu/value.py index 043f15f..d587de1 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -544,12 +544,15 @@ class Multi(list): "").format(str(value), self.opt._name, err)) - def pop(self, key, force=False): + def pop(self, index, force=False): """the list value can be updated (poped) only if the option is a master - :param key: index of the element to pop - :return: the requested element + :param index: remove item a index + :type index: int + :param force: force pop item (withoud check master/slave) + :type force: boolean + :returns: item at index """ if not force: if self.opt.impl_get_multitype() == multitypes.slave: @@ -562,8 +565,8 @@ class Multi(list): #get multi without valid properties values.getitem(slave, validate_properties=False - ).pop(key, force=True) + ).pop(index, force=True) #set value without valid properties - ret = super(Multi, self).pop(key) + ret = super(Multi, self).pop(index) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force) return ret From ff7714d8d319aa86f9b9ed82a8bf6af3499feafd Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 22 Sep 2013 21:31:37 +0200 Subject: [PATCH 16/36] add find test value in a multi's option --- test/test_config_api.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/test_config_api.py b/test/test_config_api.py index ba268bc..ab4b484 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -116,6 +116,23 @@ def test_find_in_config(): #assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_path('gc.dummy') +def test_find_multi(): + b = BoolOption('bool', '', multi=True) + o = OptionDescription('od', '', [b]) + conf = Config(o) + raises(AttributeError, "conf.find(byvalue=True)") + raises(AttributeError, "conf.find_first(byvalue=True)") + conf.bool.append(False) + raises(AttributeError, "conf.find(byvalue=True)") + raises(AttributeError, "conf.find_first(byvalue=True)") + conf.bool.append(False) + raises(AttributeError, "conf.find(byvalue=True)") + raises(AttributeError, "conf.find_first(byvalue=True)") + conf.bool.append(True) + assert conf.find(byvalue=True) == [b] + assert conf.find_first(byvalue=True) == b + + def test_does_not_find_in_config(): descr = make_description() conf = Config(descr) From d2f101b7bbd99375c1c85e2ff32a3c067bea1e67 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 22 Sep 2013 21:54:07 +0200 Subject: [PATCH 17/36] didnot getattr a second time in find if not needed --- tiramisu/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tiramisu/config.py b/tiramisu/config.py index 261f6ed..f1b2851 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -325,15 +325,15 @@ class SubConfig(object): continue if not _filter_by_value(): continue + if not _filter_by_type(): + continue #remove option with propertyerror, ... - if check_properties: + if byvalue is None and check_properties: try: value = getattr(self, path) except PropertiesOptionError: # a property restricts the access of the value continue - if not _filter_by_type(): - continue if type_ == 'value': retval = value elif type_ == 'path': From 26e3651848cf1372577c5ca140f208b87e5eb3c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 23 Sep 2013 21:00:45 +0200 Subject: [PATCH 18/36] update french translation --- tiramisu/option.py | 2 +- translations/fr/tiramisu.po | 283 +++++++++++++++++++---------------- translations/tiramisu.pot | 290 +++++++++++++++++++----------------- 3 files changed, 310 insertions(+), 265 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 10aba5c..fb76444 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -1325,6 +1325,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/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 8bceb43..9e8e2f4 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-23 20:59+CEST\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: LANGUAGE \n" @@ -11,18 +11,14 @@ 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:131 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:140 msgid "" "unable to carry out a calculation, option value with multi types must have " "same length for: {0}" @@ -30,79 +26,75 @@ msgstr "" "impossible d'effectuer le calcul, valeur d'un option avec le type multi doit " "avoir la même longueur pour : {0}" -#: tiramisu/config.py:47 +#: tiramisu/config.py:48 msgid "descr must be an optiondescription, not {0}" msgstr "descr doit être une optiondescription pas un {0}" -#: tiramisu/config.py:121 +#: tiramisu/config.py:122 msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:157 +#: tiramisu/config.py:158 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)" -#: tiramisu/config.py:311 +#: tiramisu/config.py:313 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:350 +#: tiramisu/config.py:352 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:402 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:423 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:481 +#: tiramisu/config.py:483 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:69 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:79 +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:121 +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:148 tiramisu/value.py:361 +msgid "information's item not found: {0}" +msgstr "aucune config spécifié alors que c'est nécessaire" + +#: tiramisu/option.py:265 +msgid "cannot serialize Option, only in OptionDescription" +msgstr "ne peut serialiser une Option, seulement via une OptionDescription" + +#: tiramisu/option.py:364 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}" -#: tiramisu/option.py:182 +#: tiramisu/option.py:370 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:375 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é" -#: tiramisu/option.py:190 +#: tiramisu/option.py:378 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" @@ -110,183 +102,179 @@ msgstr "" "params définit pour une fonction callback mais par de callback défini encore " "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:463 +msgid "validator should return a boolean, not {0}" +msgstr "validator devrait retourner un boolean, pas un {0}" -#: tiramisu/option.py:285 +#: tiramisu/option.py:473 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:481 tiramisu/value.py:542 msgid "invalid value {0} for option {1}: {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}" -#: tiramisu/option.py:305 +#: tiramisu/option.py:493 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:562 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}" -#: tiramisu/option.py:396 +#: tiramisu/option.py:618 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:399 +#: tiramisu/option.py:621 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:642 msgid "value {0} is not permitted, only {1} is allowed" msgstr "valeur {0} n'est pas permit, seules {1} sont autorisées" -#: tiramisu/option.py:432 +#: tiramisu/option.py:654 msgid "value must be a boolean" msgstr "valeur doit être un booléen" -#: tiramisu/option.py:442 +#: tiramisu/option.py:664 msgid "value must be an integer" msgstr "valeur doit être un numbre" -#: tiramisu/option.py:452 +#: tiramisu/option.py:674 msgid "value must be a float" msgstr "valeur doit être un nombre flottant" -#: tiramisu/option.py:462 +#: tiramisu/option.py:684 msgid "value must be a string, not {0}" msgstr "valeur doit être une chaîne, pas {0}" -#: tiramisu/option.py:480 +#: tiramisu/option.py:702 msgid "value must be an unicode" msgstr "valeur doit être une valeur unicode" -#: tiramisu/option.py:490 +#: tiramisu/option.py:714 msgid "malformed symlinkoption must be an option for 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:766 +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:768 msgid "IP must be in private class" msgstr "IP doit être dans la classe privée" -#: tiramisu/option.py:566 +#: tiramisu/option.py:806 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:571 +#: tiramisu/option.py:811 msgid "max value is empty" msgstr "valeur maximum est vide" -#: tiramisu/option.py:608 +#: tiramisu/option.py:848 msgid "network shall not be in reserved class" msgstr "réseau ne doit pas être dans la classe reservée" -#: tiramisu/option.py:640 +#: tiramisu/option.py:880 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:885 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:890 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:892 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:912 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:675 +#: tiramisu/option.py:915 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:704 +#: tiramisu/option.py:944 msgid "invalid value for {0}, must have dot" msgstr "valeur invalide pour {0}, doit avoir un point" -#: tiramisu/option.py:707 +#: tiramisu/option.py:947 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:950 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:954 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:981 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:769 +#: tiramisu/option.py:998 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {} inconnue pour l'OptionDescription{}" -#: tiramisu/option.py:820 +#: tiramisu/option.py:1049 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:850 +#: tiramisu/option.py:1083 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:856 +#: tiramisu/option.py:1089 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:866 +#: tiramisu/option.py:1099 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:1112 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:1115 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:1118 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:1129 msgid "master group with wrong master name for {0}" msgstr "le groupe maître avec un nom de maître éroné pour {0}" -#: tiramisu/option.py:905 +#: tiramisu/option.py:1138 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:1141 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:946 +#: tiramisu/option.py:1231 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:1248 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -294,87 +282,108 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, exptected et action" -#: tiramisu/option.py:967 +#: tiramisu/option.py:1253 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "requirements malformé pour l'option : {0} inverse doit être un booléen" -#: tiramisu/option.py:971 +#: tiramisu/option.py:1257 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "requirements malformé pour l'option : {0} transitive doit être booléen" -#: tiramisu/option.py:975 +#: tiramisu/option.py:1261 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" -#: tiramisu/option.py:979 +#: tiramisu/option.py:1265 msgid "malformed requirements must be an option in option {0}" msgstr "requirements malformé doit être une option dans l'option {0}" -#: tiramisu/option.py:982 +#: tiramisu/option.py:1268 msgid "malformed requirements option {0} should not be a multi" msgstr "requirements malformé l'option {0} ne doit pas être une multi" -#: tiramisu/option.py:988 +#: tiramisu/option.py:1274 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}" -#: tiramisu/option.py:993 +#: tiramisu/option.py:1279 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:1304 +msgid "{0} should be a function" +msgstr "{0} doit être une fonction" -#: tiramisu/setting.py:67 +#: tiramisu/option.py:1307 +msgid "{0}_params should be a dict" +msgstr "{0}_params devrait être un dict" + +#: tiramisu/option.py:1310 +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:1314 +msgid "{0}_params should be tuple for key \"{1}\"" +msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" + +#: tiramisu/option.py:1320 +msgid "validator not support tuple" +msgstr "validator n'accepte pas de tuple" + +#: tiramisu/option.py:1323 +msgid "{0}_params should have an option not a {0} for first argument" +msgstr "{0}_params devrait avoir une option pas un {0} pour première argument" + +#: tiramisu/option.py:1327 +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" -#: 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:300 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}'" @@ -382,65 +391,85 @@ msgstr "" "imbrication de requirements malformé 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:307 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:414 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:438 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:468 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:503 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:507 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:516 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:524 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:532 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:559 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 "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..8b37d6a 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-23 20:59+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,395 +15,411 @@ 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:131 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" -#: tiramisu/autolib.py:74 +#: tiramisu/autolib.py:140 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:48 msgid "descr must be an optiondescription, not {0}" msgstr "" -#: tiramisu/config.py:121 +#: tiramisu/config.py:122 msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:157 +#: tiramisu/config.py:158 msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:311 +#: tiramisu/config.py:313 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:350 +#: tiramisu/config.py:352 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:400 +#: tiramisu/config.py:402 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:421 +#: tiramisu/config.py:423 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:481 +#: tiramisu/config.py:483 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:69 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:79 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "" -#: tiramisu/option.py:334 +#: tiramisu/option.py:121 +msgid "'{0}' ({1}) object attribute '{2}' is read-only" +msgstr "" + +#: tiramisu/option.py:148 tiramisu/value.py:361 +msgid "information's item not found: {0}" +msgstr "" + +#: tiramisu/option.py:265 +msgid "cannot serialize Option, only in OptionDescription" +msgstr "" + +#: tiramisu/option.py:364 +msgid "a default_multi is set whereas multi is False in option: {0}" +msgstr "" + +#: tiramisu/option.py:370 +msgid "invalid default_multi value {0} for option {1}: {2}" +msgstr "" + +#: tiramisu/option.py:375 +msgid "default value not allowed if option: {0} is calculated" +msgstr "" + +#: tiramisu/option.py:378 +msgid "params defined for a callback function but no callback defined yet for option {0}" +msgstr "" + +#: tiramisu/option.py:463 +msgid "validator should return a boolean, not {0}" +msgstr "" + +#: tiramisu/option.py:473 msgid "invalid value {0} for option {1} for object {2}" msgstr "" -#: tiramisu/option.py:342 tiramisu/value.py:468 +#: tiramisu/option.py:481 tiramisu/value.py:542 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:354 +#: tiramisu/option.py:493 msgid "invalid value {0} for option {1} which must be a list" msgstr "" -#: tiramisu/option.py:423 +#: tiramisu/option.py:562 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" -#: tiramisu/option.py:445 +#: tiramisu/option.py:618 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:448 +#: tiramisu/option.py:621 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:469 +#: tiramisu/option.py:642 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:481 +#: tiramisu/option.py:654 msgid "value must be a boolean" msgstr "" -#: tiramisu/option.py:491 +#: tiramisu/option.py:664 msgid "value must be an integer" msgstr "" -#: tiramisu/option.py:501 +#: tiramisu/option.py:674 msgid "value must be a float" msgstr "" -#: tiramisu/option.py:511 +#: tiramisu/option.py:684 msgid "value must be a string, not {0}" msgstr "" -#: tiramisu/option.py:529 +#: tiramisu/option.py:702 msgid "value must be an unicode" msgstr "" -#: tiramisu/option.py:539 +#: tiramisu/option.py:714 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:766 +msgid "IP mustn't not be in reserved class" msgstr "" -#: tiramisu/option.py:583 +#: tiramisu/option.py:768 msgid "IP must be in private class" msgstr "" -#: tiramisu/option.py:621 +#: tiramisu/option.py:806 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option.py:626 +#: tiramisu/option.py:811 msgid "max value is empty" msgstr "" -#: tiramisu/option.py:663 +#: tiramisu/option.py:848 msgid "network shall not be in reserved class" msgstr "" -#: tiramisu/option.py:695 +#: tiramisu/option.py:880 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "" -#: tiramisu/option.py:700 +#: tiramisu/option.py:885 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "" -#: tiramisu/option.py:705 +#: tiramisu/option.py:890 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:707 +#: tiramisu/option.py:892 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:727 +#: tiramisu/option.py:912 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:730 +#: tiramisu/option.py:915 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:759 +#: tiramisu/option.py:944 msgid "invalid value for {0}, must have dot" msgstr "" -#: tiramisu/option.py:762 +#: tiramisu/option.py:947 msgid "invalid domainname's length for {0} (max {1})" msgstr "" -#: tiramisu/option.py:765 +#: tiramisu/option.py:950 msgid "invalid domainname's length for {0} (min 2)" msgstr "" -#: tiramisu/option.py:769 +#: tiramisu/option.py:954 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:787 -msgid "invalid name: {0} for optiondescription" -msgstr "" - -#: tiramisu/option.py:799 +#: tiramisu/option.py:981 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:825 +#: tiramisu/option.py:998 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:874 +#: tiramisu/option.py:1049 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:904 +#: tiramisu/option.py:1083 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:910 +#: tiramisu/option.py:1089 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:920 +#: tiramisu/option.py:1099 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:933 +#: tiramisu/option.py:1112 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:936 +#: tiramisu/option.py:1115 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:939 +#: tiramisu/option.py:1118 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:950 +#: tiramisu/option.py:1129 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:959 +#: tiramisu/option.py:1138 msgid "no child has same nom has master group for: {0}" msgstr "" -#: tiramisu/option.py:962 +#: tiramisu/option.py:1141 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1021 +#: tiramisu/option.py:1231 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1037 +#: tiramisu/option.py:1248 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1042 +#: tiramisu/option.py:1253 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1046 +#: tiramisu/option.py:1257 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1050 +#: tiramisu/option.py:1261 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1054 +#: tiramisu/option.py:1265 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1057 +#: tiramisu/option.py:1268 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1063 +#: tiramisu/option.py:1274 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1068 +#: tiramisu/option.py:1279 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:1304 +msgid "{0} should be a function" msgstr "" -#: tiramisu/setting.py:67 +#: tiramisu/option.py:1307 +msgid "{0}_params should be a dict" +msgstr "" + +#: tiramisu/option.py:1310 +msgid "{0}_params with key {1} should not have length different to 1" +msgstr "" + +#: tiramisu/option.py:1314 +msgid "{0}_params should be tuple for key \"{1}\"" +msgstr "" + +#: tiramisu/option.py:1320 +msgid "validator not support tuple" +msgstr "" + +#: tiramisu/option.py:1323 +msgid "{0}_params should have an option not a {0} for first argument" +msgstr "" + +#: tiramisu/option.py:1327 +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:300 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:307 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:356 +#: tiramisu/value.py:414 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:373 +#: tiramisu/value.py:438 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" -#: tiramisu/value.py:394 +#: tiramisu/value.py:468 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:429 +#: tiramisu/value.py:503 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:433 +#: tiramisu/value.py:507 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:442 +#: tiramisu/value.py:516 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:450 +#: tiramisu/value.py:524 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:458 +#: tiramisu/value.py:532 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:482 +#: tiramisu/value.py:559 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "" From 3fc89be40e913b61383041b4a699693335c03565 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 23 Sep 2013 22:40:10 +0200 Subject: [PATCH 19/36] comment tiramisu/autolib.py + some modification --- test/test_option_calculation.py | 16 ++++++ tiramisu/autolib.py | 87 ++++++++++++++++++++------------- 2 files changed, 70 insertions(+), 33 deletions(-) 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/tiramisu/autolib.py b/tiramisu/autolib.py index de8a8c5..efb7c0e 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -41,6 +41,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() @@ -92,7 +99,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) @@ -100,9 +106,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() @@ -114,39 +124,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: @@ -161,19 +179,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: @@ -183,24 +202,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) From a08af2383d1561a568a85ca71bf22d73e6f8fc31 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 23 Sep 2013 22:55:54 +0200 Subject: [PATCH 20/36] comment config --- tiramisu/config.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tiramisu/config.py b/tiramisu/config.py index f1b2851..3687349 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 @@ -20,6 +19,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 @@ -31,7 +31,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): @@ -56,6 +60,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, From 8bebbf483e897bd90e83f2dd0a35bf776f6aa66a Mon Sep 17 00:00:00 2001 From: gwen Date: Tue, 24 Sep 2013 10:34:03 +0200 Subject: [PATCH 21/36] add storage automatic api doc --- doc/api/tiramisu.storage.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 doc/api/tiramisu.storage.txt 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 From 06baff2f3b1006c4d025a6b1292eb6ca0b7dc992 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 24 Sep 2013 23:19:20 +0200 Subject: [PATCH 22/36] add warning ability --- test/test_option_validator.py | 51 +++++++++++++++---- tiramisu/option.py | 94 +++++++++++++++++++++-------------- tiramisu/value.py | 39 ++++++++++++--- 3 files changed, 128 insertions(+), 56 deletions(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 8e00916..030a7c0 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -3,7 +3,6 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import StrOption, OptionDescription -from tiramisu.error import ConfigError def return_true(value, param=None): @@ -13,37 +12,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 +55,36 @@ 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', only_warning=True) + opt2 = StrOption('opt2', '', validator=return_false, only_warning=True) + opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, only_warning=True) + root = OptionDescription('root', '', [opt1, opt2, opt3]) + cfg = Config(root) + assert cfg.opt1 == 'val' + cfg.opt1 = 'val' + assert cfg.cfgimpl_get_values().has_warning() is False + cfg.opt2 = 'val' + assert cfg.cfgimpl_get_values().has_warning() is True + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option opt2: error' + assert cfg.cfgimpl_get_values().has_warning() is False + cfg.opt3.append('val') + assert cfg.cfgimpl_get_values().has_warning() is False + cfg.opt3.append('val1') + assert cfg.cfgimpl_get_values().has_warning() is True + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option opt3: error' + assert cfg.cfgimpl_get_values().has_warning() is False + raises(ValueError, "cfg.opt2 = 1") diff --git a/tiramisu/option.py b/tiramisu/option.py index fb76444..4112050 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -26,7 +26,7 @@ from copy import copy, deepcopy from types import FunctionType from IPy import IP -from tiramisu.error import ConflictError, ConfigError +from tiramisu.error import ConflictError from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -327,13 +327,13 @@ class Option(BaseOption): """ __slots__ = ('_multi', '_validator', '_default_multi', '_default', '_state_callback', '_callback', '_multitype', - '_master_slaves', '__weakref__') + '_only_warning', '_master_slaves', '__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, only_warning=False): """ :param name: the option's name :param doc: the option's description @@ -351,6 +351,8 @@ class Option(BaseOption): validation of the value :param validator_params: the validator's parameters :param properties: tuple of default properties + :param only_warning: _validator and _consistencies don't raise if True + Values()._warning contain message """ super(Option, self).__init__(name, doc, requires, properties) @@ -388,6 +390,7 @@ class Option(BaseOption): default = [] self._multitype = multitypes.default self._default_multi = default_multi + self._only_warning = only_warning self.impl_validate(default) self._default = default @@ -436,10 +439,17 @@ class Option(BaseOption): if None not in (values, values_): getattr(self, func)(opt_._name, values, values_) - def impl_validate(self, value, context=None, validate=True): + def impl_validate(self, value, context=None, validate=True, + force_no_multi=False): """ :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 @@ -456,46 +466,49 @@ 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__)) + ret_validation = None 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) 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._only_warning: + ret_validation = msg + else: + raise ValueError(msg) + # option validation + self._validate(_value) + return ret_validation # generic calculation if context is not None: descr = context.cfgimpl_get_description() - if not self._multi: - do_validation(value) + + ret = None + if not self._multi or force_no_multi: + ret = do_validation(value) else: if not isinstance(value, list): raise ValueError(_("invalid value {0} for option {1} " "which must be a list").format(value, self._name)) - for index in range(0, len(value)): - val = value[index] - do_validation(val, index) + for index, val in enumerate(value): + ret_ = do_validation(val, index) + if ret_ is not None: + ret = ret_ + return ret def impl_getdefault(self, default_multi=False): "accessing the default value" @@ -610,7 +623,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, only_warning=False): """ :param values: is a list of values the option can possibly take """ @@ -629,7 +642,8 @@ class ChoiceOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + only_warning=only_warning) def impl_get_values(self): return self._values @@ -747,7 +761,8 @@ class IPOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None, only_private=False, allow_reserved=False): + properties=None, only_private=False, allow_reserved=False, + only_warning=False): self._only_private = only_private self._allow_reserved = allow_reserved super(IPOption, self).__init__(name, doc, default=default, @@ -758,7 +773,8 @@ class IPOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + only_warning=only_warning) def _validate(self, value): ip = IP('{0}/32'.format(value)) @@ -786,7 +802,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, only_warning=False): self._allow_range = allow_range self._min_value = None self._max_value = None @@ -818,7 +834,8 @@ class PortOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + only_warning=only_warning) def _validate(self, value): if self._allow_range and ":" in str(value): @@ -864,7 +881,6 @@ class NetmaskOption(Option): #opts must be (netmask, ip) options self.__cons_netmask(optname, value, value_, True) - #def __cons_netmask(self, opt, value, context, index, opts, make_net): def __cons_netmask(self, optname, val_netmask, val_ipnetwork, make_net): msg = None try: @@ -903,7 +919,8 @@ class DomainnameOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None, allow_ip=False, type_='domainname'): + properties=None, allow_ip=False, type_='domainname', + only_warning=False): #netbios: for MS domain #hostname: to identify the device #domainname: @@ -922,7 +939,8 @@ class DomainnameOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + only_warning=only_warning) def _validate(self, value): if self._allow_ip is True: diff --git a/tiramisu/value.py b/tiramisu/value.py index d587de1..247d273 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -33,7 +33,7 @@ class Values(object): but the values are physicaly located here, in `Values`, wich is also responsible of a caching utility. """ - __slots__ = ('context', '_p_', '__weakref__') + __slots__ = ('context', '_warning', '_p_', '__weakref__') def __init__(self, context, storage): """ @@ -106,8 +106,9 @@ 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(), - 'validator' in setting) + self._warning = opt.impl_validate(opt.impl_getdefault(), + self.context(), + 'validator' in setting) self.context().cfgimpl_reset_cache() if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master): @@ -229,7 +230,8 @@ class Values(object): else: value = self._getvalue(opt, path, validate) if config_error is None and validate: - opt.impl_validate(value, self.context(), 'validator' in setting) + self._warning = opt.impl_validate(value, self.context(), + 'validator' in setting) if config_error is None and self._is_default_owner(path) and \ 'force_store_value' in setting[opt]: self.setitem(opt, value, path, is_write=False) @@ -250,8 +252,9 @@ class Values(object): # is_write is, for example, used with "force_store_value" # user didn't change value, so not write # valid opt - opt.impl_validate(value, self.context(), - 'validator' in self.context().cfgimpl_get_settings()) + self._warning = opt.impl_validate(value, self.context(), + 'validator' in self.context( + ).cfgimpl_get_settings()) if opt.impl_is_multi() and not isinstance(value, Multi): value = Multi(value, self.context, opt, path, setitem=True) self._setvalue(opt, path, value, force_permissive=force_permissive, @@ -370,6 +373,22 @@ class Values(object): def __setstate__(self, states): self._p_ = states['_p_'] + def has_warning(self): + """If option is "only_warning", validation error is store in + self._warning. + has_warning just indicate that a warning message is store + """ + return self._warning is not None + + def get_last_warning(self): + """Get last warning message in self._warning. + We can get only one time this message. + """ + ret = self._warning + self._warning = None + return ret + + # ____________________________________________________________ # multi types @@ -476,7 +495,9 @@ class Multi(list): value = None self._validate(value) 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) @@ -537,7 +558,9 @@ class Multi(list): def _validate(self, value): if value is not None: try: - self.opt._validate(value) + self.context().cfgimpl_get_values()._warning = \ + self.opt.impl_validate(value, context=self.context(), + force_no_multi=True) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}" From 4e0f0a5b70d45168b182738e429c53f9b5f28363 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 25 Sep 2013 10:22:31 +0200 Subject: [PATCH 23/36] config_error is an exception, raise directly config_error --- tiramisu/value.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiramisu/value.py b/tiramisu/value.py index 247d273..d2aa6a1 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -241,7 +241,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): From 329b9ac3493bcbbece10856b9dcc48dccf47ec13 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 25 Sep 2013 21:10:45 +0200 Subject: [PATCH 24/36] add _second_level_validation (second's one return only warning almost _validator raise) --- test/test_option_validator.py | 20 ++++++++++++++++++++ tiramisu/option.py | 12 +++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 030a7c0..c26e5f6 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -3,6 +3,7 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import StrOption, OptionDescription +from tiramisu.setting import groups def return_true(value, param=None): @@ -88,3 +89,22 @@ def test_validator_warning(): assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option opt3: error' assert cfg.cfgimpl_get_values().has_warning() is False raises(ValueError, "cfg.opt2 = 1") + + +def test_validator_warning_master_slave(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=return_false, only_warning=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=return_if_val, only_warning=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) + cfg.ip_admin_eth0.ip_admin_eth0.append(None) + assert cfg.cfgimpl_get_values().has_warning() is False + cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] + assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['val1'] + assert cfg.cfgimpl_get_values().has_warning() is True + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option netmask_admin_eth0: error' + cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] + assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' diff --git a/tiramisu/option.py b/tiramisu/option.py index 4112050..c49e5e4 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -473,7 +473,7 @@ class Option(BaseOption): def do_validation(_value, _index=None): if _value is None: - return True + return ret_validation = None try: # valid with self._validator @@ -481,6 +481,7 @@ class Option(BaseOption): # 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: msg = _("invalid value {0} for option {1}: {2}").format( _value, self._name, err) @@ -610,6 +611,9 @@ class Option(BaseOption): else: self._state_callback = (callback, cllbck_prms) + def _second_level_validation(self, value): + pass + class ChoiceOption(Option): """represents a choice out of several objects. @@ -777,6 +781,9 @@ class IPOption(Option): only_warning=only_warning) def _validate(self, value): + IP('{0}/32'.format(value)) + + 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")) @@ -860,6 +867,9 @@ class NetworkOption(Option): _opt_type = 'network' def _validate(self, value): + IP(value) + + def _second_level_validation(self, value): ip = IP(value) if ip.iptype() == 'RESERVED': raise ValueError(_("network shall not be in reserved class")) From e63dbf308d17af4802f21004fa2447e74578ef77 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 09:31:51 +0200 Subject: [PATCH 25/36] more tests for warnings --- test/test_option_validator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index c26e5f6..f2ccd8b 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -108,3 +108,9 @@ def test_validator_warning_master_slave(): cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' From a5587c91b85a536cd9e03f261a49596ed104753a Mon Sep 17 00:00:00 2001 From: gwen Date: Thu, 26 Sep 2013 16:56:14 +0200 Subject: [PATCH 26/36] errors in french translation --- translations/fr/tiramisu.po | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 9e8e2f4..66623c1 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -23,7 +23,7 @@ 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 " +"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:48 @@ -38,7 +38,7 @@ msgstr "group_type inconnu: {0}" 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 " +"pas d'option description trouvé pour cette config (peut être une metaconfig sans " "meta)" #: tiramisu/config.py:313 @@ -71,7 +71,7 @@ msgstr "type des properties invalide {0} pour {1}, doit être un tuple" #: tiramisu/option.py:121 msgid "'{0}' ({1}) object attribute '{2}' is read-only" -msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seul" +msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule" #: tiramisu/option.py:148 tiramisu/value.py:361 msgid "information's item not found: {0}" @@ -84,7 +84,7 @@ msgstr "ne peut serialiser une Option, seulement via une OptionDescription" #: tiramisu/option.py:364 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:370 msgid "invalid default_multi value {0} for option {1}: {2}" @@ -92,19 +92,19 @@ msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}" #: tiramisu/option.py:375 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:378 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 " +"params définis pour une fonction callback mais par de callback encore définis " "pour l'option {0}" #: tiramisu/option.py:463 msgid "validator should return a boolean, not {0}" -msgstr "validator devrait retourner un boolean, pas un {0}" +msgstr "le validator devrait retourner un boolean, pas un {0}" #: tiramisu/option.py:473 msgid "invalid value {0} for option {1} for object {2}" @@ -121,7 +121,7 @@ msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste" #: tiramisu/option.py:562 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:618 msgid "values must be a tuple for {0}" @@ -133,7 +133,7 @@ msgstr "open_values doit être un booléen pour {0}" #: tiramisu/option.py:642 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:654 msgid "value must be a boolean" @@ -141,7 +141,7 @@ msgstr "valeur doit être un booléen" #: tiramisu/option.py:664 msgid "value must be an integer" -msgstr "valeur doit être un numbre" +msgstr "valeur doit être un nombre entier" #: tiramisu/option.py:674 msgid "value must be a float" @@ -157,7 +157,7 @@ msgstr "valeur doit être une valeur unicode" #: tiramisu/option.py:714 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:766 msgid "IP mustn't not be in reserved class" @@ -173,11 +173,11 @@ msgstr "inconsistence dans la plage autorisée" #: tiramisu/option.py:811 msgid "max value is empty" -msgstr "valeur maximum est vide" +msgstr "la valeur maximum est vide" #: tiramisu/option.py:848 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:880 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" @@ -259,7 +259,7 @@ msgstr "" #: tiramisu/option.py:1129 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:1138 msgid "no child has same nom has master group for: {0}" @@ -280,34 +280,34 @@ msgid "" "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:1253 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:1257 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:1261 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:1265 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:1268 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:1274 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} : " +"requirements mal formés deuxième argument doit être valide pour l'option {0} : " "{1}" #: tiramisu/option.py:1279 @@ -337,7 +337,7 @@ msgstr "validator n'accepte pas de tuple" #: tiramisu/option.py:1323 msgid "{0}_params should have an option not a {0} for first argument" -msgstr "{0}_params devrait avoir une option pas un {0} pour première argument" +msgstr "{0}_params devrait avoir une option pas un {0} pour premier argument" #: tiramisu/option.py:1327 msgid "{0}_params should have a boolean not a {0} for second argument" @@ -369,7 +369,7 @@ msgstr "" #: 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:446 msgid "trying to access to an option named: {0} with properties {1}" @@ -388,7 +388,7 @@ 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:570 From f040d3da61fa87a5abb250b423555b64a6caf964 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 18:35:11 +0200 Subject: [PATCH 27/36] warning is now a dict --- test/test_option_validator.py | 19 +++++++++++------- tiramisu/value.py | 36 ++++++++++++++++++++--------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index f2ccd8b..057aa4d 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -80,15 +80,20 @@ def test_validator_warning(): assert cfg.cfgimpl_get_values().has_warning() is False cfg.opt2 = 'val' assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option opt2: error' + assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error'} assert cfg.cfgimpl_get_values().has_warning() is False cfg.opt3.append('val') assert cfg.cfgimpl_get_values().has_warning() is False cfg.opt3.append('val1') assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option opt3: error' + assert cfg.cfgimpl_get_values().get_warnings() == {opt3: 'invalid value val1 for option opt3: error'} assert cfg.cfgimpl_get_values().has_warning() is False raises(ValueError, "cfg.opt2 = 1") + cfg.opt2 = 'val' + cfg.opt3.append('val') + assert cfg.cfgimpl_get_values().has_warning() is True + assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error', opt3: 'invalid value val1 for option opt3: error'} + assert cfg.cfgimpl_get_values().has_warning() is False def test_validator_warning_master_slave(): @@ -104,13 +109,13 @@ def test_validator_warning_master_slave(): cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['val1'] assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option netmask_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {netmask_admin_eth0: 'invalid value val1 for option netmask_admin_eth0: error'} cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} diff --git a/tiramisu/value.py b/tiramisu/value.py index d2aa6a1..a180806 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -45,6 +45,7 @@ class Values(object): self.context = weakref.ref(context) # the storage type is dictionary or sqlite3 self._p_ = storage + self._warning = {} def _getdefault(self, opt): """ @@ -106,9 +107,9 @@ class Values(object): path = self._get_opt_path(opt) if self._p_.hasvalue(path): setting = self.context().cfgimpl_get_settings() - self._warning = opt.impl_validate(opt.impl_getdefault(), - self.context(), - 'validator' in setting) + self._setwarning(opt.impl_validate(opt.impl_getdefault(), + self.context(), + 'validator' in setting), opt) self.context().cfgimpl_reset_cache() if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master): @@ -230,8 +231,8 @@ class Values(object): else: value = self._getvalue(opt, path, validate) if config_error is None and validate: - self._warning = opt.impl_validate(value, self.context(), - 'validator' in setting) + self._setwarning(opt.impl_validate(value, self.context(), + 'validator' in setting), opt) if config_error is None and self._is_default_owner(path) and \ 'force_store_value' in setting[opt]: self.setitem(opt, value, path, is_write=False) @@ -252,9 +253,9 @@ class Values(object): # is_write is, for example, used with "force_store_value" # user didn't change value, so not write # valid opt - self._warning = opt.impl_validate(value, self.context(), - 'validator' in self.context( - ).cfgimpl_get_settings()) + self._setwarning(opt.impl_validate(value, self.context(), + 'validator' in self.context( + ).cfgimpl_get_settings()), opt) if opt.impl_is_multi() and not isinstance(value, Multi): value = Multi(value, self.context, opt, path, setitem=True) self._setvalue(opt, path, value, force_permissive=force_permissive, @@ -373,19 +374,24 @@ class Values(object): def __setstate__(self, states): self._p_ = states['_p_'] + def _setwarning(self, msg, opt): + if msg is not None: + self._warning[opt] = msg + def has_warning(self): """If option is "only_warning", validation error is store in self._warning. has_warning just indicate that a warning message is store """ - return self._warning is not None + return self._warning != {} - def get_last_warning(self): - """Get last warning message in self._warning. - We can get only one time this message. + def get_warnings(self): + """Get last warnings messages in self._warning. + We can get only one time those messages. + :returns: {opt: msg, opt1: msg1} """ ret = self._warning - self._warning = None + self._warning = {} return ret @@ -558,9 +564,9 @@ class Multi(list): def _validate(self, value): if value is not None: try: - self.context().cfgimpl_get_values()._warning = \ + self.context().cfgimpl_get_values()._setwarning( self.opt.impl_validate(value, context=self.context(), - force_no_multi=True) + force_no_multi=True), self.opt) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}" From f4677b9ef94a82e2c8f19fbcbebee251183cbe0f Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 21:56:06 +0200 Subject: [PATCH 28/36] use warnings instead of a new dictionary --- test/test_option_validator.py | 98 +++++++++++++++++++++++------------ tiramisu/error.py | 32 ++++++++++++ tiramisu/option.py | 9 ++-- tiramisu/value.py | 42 ++++----------- 4 files changed, 112 insertions(+), 69 deletions(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 057aa4d..76078f1 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.setting import groups +from tiramisu.error import ValueWarning def return_true(value, param=None): @@ -76,24 +78,36 @@ def test_validator_warning(): root = OptionDescription('root', '', [opt1, opt2, opt3]) cfg = Config(root) assert cfg.opt1 == 'val' - cfg.opt1 = 'val' - assert cfg.cfgimpl_get_values().has_warning() is False - cfg.opt2 = 'val' - assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error'} - assert cfg.cfgimpl_get_values().has_warning() is False - cfg.opt3.append('val') - assert cfg.cfgimpl_get_values().has_warning() is False - cfg.opt3.append('val1') - assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_warnings() == {opt3: 'invalid value val1 for option opt3: error'} - assert cfg.cfgimpl_get_values().has_warning() is False + 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") - cfg.opt2 = 'val' - cfg.opt3.append('val') - assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error', opt3: 'invalid value val1 for option opt3: error'} - assert cfg.cfgimpl_get_values().has_warning() is False + # + 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(): @@ -104,18 +118,38 @@ def test_validator_warning_master_slave(): assert interface1.impl_get_group_type() == groups.master root = OptionDescription('root', '', [interface1]) cfg = Config(root) - cfg.ip_admin_eth0.ip_admin_eth0.append(None) - assert cfg.cfgimpl_get_values().has_warning() is False - cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] - assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['val1'] - assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_warnings() == {netmask_admin_eth0: 'invalid value val1 for option netmask_admin_eth0: error'} - cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] - assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] - assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} - cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] - assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} - cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] - assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} - cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] - assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} + 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/tiramisu/error.py b/tiramisu/error.py index 6c92be3..a4b3f41 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -61,3 +61,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, only_warning=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 c49e5e4..74f9899 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -25,8 +25,9 @@ import sys from copy import copy, deepcopy from types import FunctionType from IPy import IP +import warnings -from tiramisu.error import ConflictError +from tiramisu.error import ConflictError, ValueWarning from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -474,7 +475,6 @@ class Option(BaseOption): def do_validation(_value, _index=None): if _value is None: return - ret_validation = None try: # valid with self._validator val_validator(_value) @@ -486,12 +486,13 @@ class Option(BaseOption): msg = _("invalid value {0} for option {1}: {2}").format( _value, self._name, err) if self._only_warning: - ret_validation = msg + warnings.warn_explicit(ValueWarning(msg, self), + ValueWarning, + self.__class__.__name__, 0) else: raise ValueError(msg) # option validation self._validate(_value) - return ret_validation # generic calculation if context is not None: diff --git a/tiramisu/value.py b/tiramisu/value.py index a180806..6942453 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -33,7 +33,7 @@ class Values(object): but the values are physicaly located here, in `Values`, wich is also responsible of a caching utility. """ - __slots__ = ('context', '_warning', '_p_', '__weakref__') + __slots__ = ('context', '_p_', '__weakref__') def __init__(self, context, storage): """ @@ -45,7 +45,6 @@ class Values(object): self.context = weakref.ref(context) # the storage type is dictionary or sqlite3 self._p_ = storage - self._warning = {} def _getdefault(self, opt): """ @@ -107,9 +106,9 @@ class Values(object): path = self._get_opt_path(opt) if self._p_.hasvalue(path): setting = self.context().cfgimpl_get_settings() - self._setwarning(opt.impl_validate(opt.impl_getdefault(), - self.context(), - 'validator' in setting), opt) + opt.impl_validate(opt.impl_getdefault(), + self.context(), + 'validator' in setting) self.context().cfgimpl_reset_cache() if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master): @@ -231,8 +230,7 @@ class Values(object): else: value = self._getvalue(opt, path, validate) if config_error is None and validate: - self._setwarning(opt.impl_validate(value, self.context(), - 'validator' in setting), opt) + opt.impl_validate(value, self.context(), 'validator' in setting) if config_error is None and self._is_default_owner(path) and \ 'force_store_value' in setting[opt]: self.setitem(opt, value, path, is_write=False) @@ -253,9 +251,8 @@ class Values(object): # is_write is, for example, used with "force_store_value" # user didn't change value, so not write # valid opt - self._setwarning(opt.impl_validate(value, self.context(), - 'validator' in self.context( - ).cfgimpl_get_settings()), opt) + opt.impl_validate(value, self.context(), + 'validator' in self.context().cfgimpl_get_settings()) if opt.impl_is_multi() and not isinstance(value, Multi): value = Multi(value, self.context, opt, path, setitem=True) self._setvalue(opt, path, value, force_permissive=force_permissive, @@ -374,26 +371,6 @@ class Values(object): def __setstate__(self, states): self._p_ = states['_p_'] - def _setwarning(self, msg, opt): - if msg is not None: - self._warning[opt] = msg - - def has_warning(self): - """If option is "only_warning", validation error is store in - self._warning. - has_warning just indicate that a warning message is store - """ - return self._warning != {} - - def get_warnings(self): - """Get last warnings messages in self._warning. - We can get only one time those messages. - :returns: {opt: msg, opt1: msg1} - """ - ret = self._warning - self._warning = {} - return ret - # ____________________________________________________________ # multi types @@ -564,9 +541,8 @@ class Multi(list): def _validate(self, value): if value is not None: try: - self.context().cfgimpl_get_values()._setwarning( - self.opt.impl_validate(value, context=self.context(), - force_no_multi=True), self.opt) + self.opt.impl_validate(value, context=self.context(), + force_no_multi=True) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}" From a004f30e344928da1f0833214014058559cccd53 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 22:11:25 +0200 Subject: [PATCH 29/36] french translation correction --- tiramisu/option.py | 5 +++++ translations/fr/tiramisu.po | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 74f9899..6fd0568 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -922,6 +922,11 @@ class NetmaskOption(Option): val_netmask, self._name)) +class BroadcastOption(Option): + def _validate(self, value): + IP('{0}/32'.format(value)) + + class DomainnameOption(Option): "represents the choice of a domain name" __slots__ = ('_type', '_allow_ip') diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 66623c1..e1cc0b3 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -225,7 +225,7 @@ msgstr "nom de l'option dupliqué : {0}" #: tiramisu/option.py:998 msgid "unknown Option {0} in OptionDescription {1}" -msgstr "Option {} inconnue pour l'OptionDescription{}" +msgstr "Option {0} inconnue pour l'OptionDescription {1}" #: tiramisu/option.py:1049 msgid "duplicate option: {0}" From 1d2008fd84df7dbbf1ffb9d4be632626a3a94512 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 22:27:39 +0200 Subject: [PATCH 30/36] error message for ip/broadcast/netmask/network validation --- tiramisu/option.py | 20 +++- translations/fr/tiramisu.po | 227 +++++++++++++++++++----------------- translations/tiramisu.pot | 196 ++++++++++++++++--------------- 3 files changed, 240 insertions(+), 203 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 6fd0568..cf35508 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -782,7 +782,10 @@ class IPOption(Option): only_warning=only_warning) def _validate(self, value): - IP('{0}/32'.format(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)) @@ -868,7 +871,10 @@ class NetworkOption(Option): _opt_type = 'network' def _validate(self, value): - IP(value) + try: + IP(value) + except ValueError: + raise ValueError(_('invalid network address {0}').format(self._name)) def _second_level_validation(self, value): ip = IP(value) @@ -882,7 +888,10 @@ 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_): #opts must be (netmask, network) options @@ -924,7 +933,10 @@ class NetmaskOption(Option): class BroadcastOption(Option): def _validate(self, value): - IP('{0}/32'.format(value)) + try: + IP('{0}/32'.format(value)) + except ValueError: + raise ValueError(_('invalid broadcast address {0}').format(self._name)) class DomainnameOption(Option): diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index e1cc0b3..a81e14e 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-09-23 20:59+CEST\n" +"POT-Creation-Date: 2013-09-26 22:24+CEST\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: LANGUAGE \n" @@ -11,270 +11,279 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" -#: tiramisu/autolib.py:131 +#: 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:140 +#: 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, la valeur d'une 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:48 +#: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "descr doit être une optiondescription pas un {0}" -#: tiramisu/config.py:122 +#: tiramisu/config.py:127 msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:158 +#: tiramisu/config.py:163 msgid "" "no option description found for this config (may be metaconfig without meta)" msgstr "" -"pas d'option description trouvé 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:313 +#: tiramisu/config.py:318 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:352 +#: tiramisu/config.py:357 msgid "no option found in config with these criteria" msgstr "aucune option trouvée dans la config avec ces critères" -#: tiramisu/config.py:402 +#: tiramisu/config.py:407 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:423 +#: tiramisu/config.py:428 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:483 +#: tiramisu/config.py:488 msgid "opt in getowner must be an option not {0}" msgstr "opt dans getowner doit être une option pas {0}" -#: tiramisu/option.py:69 +#: tiramisu/option.py:70 msgid "invalid name: {0} for option" msgstr "nom invalide : {0} pour l'option" -#: tiramisu/option.py:79 +#: 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:121 +#: 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:148 tiramisu/value.py:361 +#: 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:265 +#: tiramisu/option.py:266 msgid "cannot serialize Option, only in OptionDescription" msgstr "ne peut serialiser une Option, seulement via une OptionDescription" -#: tiramisu/option.py:364 +#: tiramisu/option.py:367 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -"une default_multi est renseignée 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:370 +#: 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:375 +#: 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ée" -#: tiramisu/option.py:378 +#: tiramisu/option.py:381 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" msgstr "" -"params définis pour une fonction callback mais par de callback encore définis " -"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:463 -msgid "validator should return a boolean, not {0}" -msgstr "le validator devrait retourner un boolean, pas un {0}" - -#: tiramisu/option.py:473 -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:481 tiramisu/value.py:542 +#: 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:493 +#: 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:562 +#: 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érente de l'option {2}" -#: tiramisu/option.py:618 +#: tiramisu/option.py:636 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:621 +#: 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:642 +#: tiramisu/option.py:661 msgid "value {0} is not permitted, only {1} is allowed" msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées" -#: tiramisu/option.py:654 +#: tiramisu/option.py:673 msgid "value must be a boolean" msgstr "valeur doit être un booléen" -#: tiramisu/option.py:664 +#: tiramisu/option.py:683 msgid "value must be an integer" msgstr "valeur doit être un nombre entier" -#: tiramisu/option.py:674 +#: tiramisu/option.py:693 msgid "value must be a float" msgstr "valeur doit être un nombre flottant" -#: tiramisu/option.py:684 +#: tiramisu/option.py:703 msgid "value must be a string, not {0}" msgstr "valeur doit être une chaîne, pas {0}" -#: tiramisu/option.py:702 +#: tiramisu/option.py:721 msgid "value must be an unicode" msgstr "valeur doit être une valeur unicode" -#: tiramisu/option.py:714 +#: 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}" -#: tiramisu/option.py:766 +#: 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:768 +#: tiramisu/option.py:795 msgid "IP must be in private class" msgstr "IP doit être dans la classe privée" -#: tiramisu/option.py:806 +#: tiramisu/option.py:833 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:811 +#: tiramisu/option.py:838 msgid "max value is empty" msgstr "la valeur maximum est vide" -#: tiramisu/option.py:848 +#: 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 "le réseau ne doit pas être dans la classe reservée" -#: tiramisu/option.py:880 +#: 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:885 +#: 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:890 +#: 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:892 +#: 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:912 +#: 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:915 +#: tiramisu/option.py:960 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:944 +#: tiramisu/option.py:990 msgid "invalid value for {0}, must have dot" msgstr "valeur invalide pour {0}, doit avoir un point" -#: tiramisu/option.py:947 +#: 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:950 +#: 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:954 +#: tiramisu/option.py:1000 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:981 +#: tiramisu/option.py:1027 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:998 +#: tiramisu/option.py:1044 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:1049 +#: tiramisu/option.py:1095 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:1083 +#: tiramisu/option.py:1129 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:1089 +#: tiramisu/option.py:1135 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:1099 +#: 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:1112 +#: 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:1115 +#: 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:1118 +#: 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:1129 +#: tiramisu/option.py:1175 msgid "master group with wrong master name for {0}" msgstr "le groupe maître avec un nom de maître érroné pour {0}" -#: tiramisu/option.py:1138 +#: 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:1141 +#: tiramisu/option.py:1187 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:1231 +#: 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:1248 +#: tiramisu/option.py:1294 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -282,64 +291,66 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, expected et action" -#: tiramisu/option.py:1253 +#: tiramisu/option.py:1299 msgid "malformed requirements for option: {0} inverse must be boolean" -msgstr "requirements mal formés 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:1257 +#: tiramisu/option.py:1303 msgid "malformed requirements for option: {0} transitive must be boolean" -msgstr "requirements mal formés 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:1261 +#: tiramisu/option.py:1307 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" "requirements mal formés pour l'option : {0} same_action doit être un booléen" -#: tiramisu/option.py:1265 +#: tiramisu/option.py:1311 msgid "malformed requirements must be an option in option {0}" msgstr "requirements mal formés doit être une option dans l'option {0}" -#: tiramisu/option.py:1268 +#: tiramisu/option.py:1314 msgid "malformed requirements option {0} should not be a multi" msgstr "requirements mal formés l'option {0} ne doit pas être une multi" -#: tiramisu/option.py:1274 +#: tiramisu/option.py:1320 msgid "" "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -"requirements mal formés 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:1279 +#: 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/option.py:1304 +#: tiramisu/option.py:1350 msgid "{0} should be a function" msgstr "{0} doit être une fonction" -#: tiramisu/option.py:1307 +#: tiramisu/option.py:1353 msgid "{0}_params should be a dict" msgstr "{0}_params devrait être un dict" -#: tiramisu/option.py:1310 +#: 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:1314 +#: 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:1320 +#: tiramisu/option.py:1366 msgid "validator not support tuple" msgstr "validator n'accepte pas de tuple" -#: tiramisu/option.py:1323 +#: 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:1327 +#: 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" @@ -379,7 +390,7 @@ msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1} msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:471 tiramisu/value.py:300 +#: tiramisu/setting.py:471 tiramisu/value.py:301 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" @@ -416,48 +427,54 @@ msgstr "session déjà utilisée" msgid "a dictionary cannot be persistent" msgstr "un espace de stockage dictionary ne peut être persistant" -#: tiramisu/value.py:307 +#: 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:414 +#: 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:438 +#: 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:468 +#: 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:503 +#: 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:507 +#: 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:516 +#: 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:524 +#: 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:532 +#: 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:559 +#: 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" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 8b37d6a..7304c9f 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-23 20:59+CEST\n" +"POT-Creation-Date: 2013-09-26 22:24+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,307 +15,315 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/autolib.py:131 +#: tiramisu/autolib.py:145 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" -#: tiramisu/autolib.py:140 +#: 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:48 +#: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "" -#: tiramisu/config.py:122 +#: tiramisu/config.py:127 msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:158 +#: tiramisu/config.py:163 msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:313 +#: tiramisu/config.py:318 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:352 +#: tiramisu/config.py:357 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:402 +#: tiramisu/config.py:407 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:423 +#: tiramisu/config.py:428 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:483 +#: tiramisu/config.py:488 msgid "opt in getowner must be an option not {0}" msgstr "" -#: tiramisu/option.py:69 +#: tiramisu/option.py:70 msgid "invalid name: {0} for option" msgstr "" -#: tiramisu/option.py:79 +#: tiramisu/option.py:80 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "" -#: tiramisu/option.py:121 +#: tiramisu/option.py:122 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option.py:148 tiramisu/value.py:361 +#: tiramisu/option.py:149 tiramisu/value.py:362 msgid "information's item not found: {0}" msgstr "" -#: tiramisu/option.py:265 +#: tiramisu/option.py:266 msgid "cannot serialize Option, only in OptionDescription" msgstr "" -#: tiramisu/option.py:364 +#: tiramisu/option.py:367 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -#: tiramisu/option.py:370 +#: tiramisu/option.py:373 msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:375 +#: tiramisu/option.py:378 msgid "default value not allowed if option: {0} is calculated" msgstr "" -#: tiramisu/option.py:378 +#: tiramisu/option.py:381 msgid "params defined for a callback function but no callback defined yet for option {0}" msgstr "" -#: tiramisu/option.py:463 -msgid "validator should return a boolean, not {0}" -msgstr "" - -#: tiramisu/option.py:473 -msgid "invalid value {0} for option {1} for object {2}" -msgstr "" - -#: tiramisu/option.py:481 tiramisu/value.py:542 +#: tiramisu/option.py:486 tiramisu/value.py:547 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:493 +#: tiramisu/option.py:506 msgid "invalid value {0} for option {1} which must be a list" msgstr "" -#: tiramisu/option.py:562 +#: tiramisu/option.py:577 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" -#: tiramisu/option.py:618 +#: tiramisu/option.py:636 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:621 +#: tiramisu/option.py:639 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:642 +#: tiramisu/option.py:661 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:654 +#: tiramisu/option.py:673 msgid "value must be a boolean" msgstr "" -#: tiramisu/option.py:664 +#: tiramisu/option.py:683 msgid "value must be an integer" msgstr "" -#: tiramisu/option.py:674 +#: tiramisu/option.py:693 msgid "value must be a float" msgstr "" -#: tiramisu/option.py:684 +#: tiramisu/option.py:703 msgid "value must be a string, not {0}" msgstr "" -#: tiramisu/option.py:702 +#: tiramisu/option.py:721 msgid "value must be an unicode" msgstr "" -#: tiramisu/option.py:714 +#: tiramisu/option.py:733 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option.py:766 +#: tiramisu/option.py:788 +msgid "invalid IP {0}" +msgstr "" + +#: tiramisu/option.py:793 msgid "IP mustn't not be in reserved class" msgstr "" -#: tiramisu/option.py:768 +#: tiramisu/option.py:795 msgid "IP must be in private class" msgstr "" -#: tiramisu/option.py:806 +#: tiramisu/option.py:833 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option.py:811 +#: tiramisu/option.py:838 msgid "max value is empty" msgstr "" -#: tiramisu/option.py:848 +#: 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:880 +#: 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:885 +#: tiramisu/option.py:921 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "" -#: tiramisu/option.py:890 +#: tiramisu/option.py:926 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:892 +#: tiramisu/option.py:928 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:912 +#: tiramisu/option.py:939 +msgid "invalid broadcast address {0}" +msgstr "" + +#: tiramisu/option.py:957 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:915 +#: tiramisu/option.py:960 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:944 +#: tiramisu/option.py:990 msgid "invalid value for {0}, must have dot" msgstr "" -#: tiramisu/option.py:947 +#: tiramisu/option.py:993 msgid "invalid domainname's length for {0} (max {1})" msgstr "" -#: tiramisu/option.py:950 +#: tiramisu/option.py:996 msgid "invalid domainname's length for {0} (min 2)" msgstr "" -#: tiramisu/option.py:954 +#: tiramisu/option.py:1000 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:981 +#: tiramisu/option.py:1027 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:998 +#: tiramisu/option.py:1044 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:1049 +#: tiramisu/option.py:1095 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:1083 +#: tiramisu/option.py:1129 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:1089 +#: tiramisu/option.py:1135 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:1099 +#: tiramisu/option.py:1145 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:1112 +#: tiramisu/option.py:1158 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:1115 +#: tiramisu/option.py:1161 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:1118 +#: tiramisu/option.py:1164 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:1129 +#: tiramisu/option.py:1175 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:1138 +#: tiramisu/option.py:1184 msgid "no child has same nom has master group for: {0}" msgstr "" -#: tiramisu/option.py:1141 +#: tiramisu/option.py:1187 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1231 +#: tiramisu/option.py:1277 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1248 +#: tiramisu/option.py:1294 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1253 +#: tiramisu/option.py:1299 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1257 +#: tiramisu/option.py:1303 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1261 +#: tiramisu/option.py:1307 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1265 +#: tiramisu/option.py:1311 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1268 +#: tiramisu/option.py:1314 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1274 +#: tiramisu/option.py:1320 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1279 +#: tiramisu/option.py:1325 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "" -#: tiramisu/option.py:1304 +#: tiramisu/option.py:1350 msgid "{0} should be a function" msgstr "" -#: tiramisu/option.py:1307 +#: tiramisu/option.py:1353 msgid "{0}_params should be a dict" msgstr "" -#: tiramisu/option.py:1310 +#: tiramisu/option.py:1356 msgid "{0}_params with key {1} should not have length different to 1" msgstr "" -#: tiramisu/option.py:1314 +#: tiramisu/option.py:1360 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "" -#: tiramisu/option.py:1320 +#: tiramisu/option.py:1366 msgid "validator not support tuple" msgstr "" -#: tiramisu/option.py:1323 +#: tiramisu/option.py:1369 msgid "{0}_params should have an option not a {0} for first argument" msgstr "" -#: tiramisu/option.py:1327 +#: tiramisu/option.py:1373 msgid "{0}_params should have a boolean not a {0} for second argument" msgstr "" @@ -351,7 +359,7 @@ msgstr "" msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:471 tiramisu/value.py:300 +#: tiramisu/setting.py:471 tiramisu/value.py:301 msgid "invalid generic owner {0}" msgstr "" @@ -383,43 +391,43 @@ msgstr "" msgid "a dictionary cannot be persistent" msgstr "" -#: tiramisu/value.py:307 +#: tiramisu/value.py:308 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:414 +#: tiramisu/value.py:416 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:438 +#: tiramisu/value.py:440 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" -#: tiramisu/value.py:468 +#: tiramisu/value.py:470 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:503 +#: tiramisu/value.py:507 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:507 +#: tiramisu/value.py:511 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:516 +#: tiramisu/value.py:520 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:524 +#: tiramisu/value.py:528 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:532 +#: tiramisu/value.py:536 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:559 +#: tiramisu/value.py:564 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "" From 3073940ca46e91e54daad91ec371580d990c39c9 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 22:35:12 +0200 Subject: [PATCH 31/36] can't assign to an OptionDescription --- tiramisu/config.py | 4 +++- translations/fr/tiramisu.po | 16 ++++++++++------ translations/tiramisu.pot | 16 ++++++++++------ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/tiramisu/config.py b/tiramisu/config.py index 3687349..25dbaf9 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -185,7 +185,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 SyntaxError(_("can't assign to an OptionDescription")) + elif not isinstance(child, SymLinkOption): if self._impl_path is None: path = name else: diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index a81e14e..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-09-26 22:24+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" @@ -41,23 +41,27 @@ msgstr "" "pas d'option description trouvé pour cette config (peut être une metaconfig " "sans meta)" -#: tiramisu/config.py:318 +#: 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:357 +#: 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:407 +#: 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:428 +#: tiramisu/config.py:430 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:488 +#: tiramisu/config.py:490 msgid "opt in getowner must be an option not {0}" msgstr "opt dans getowner doit être une option pas {0}" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 7304c9f..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-26 22:24+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" @@ -35,23 +35,27 @@ msgstr "" msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:318 +#: 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:357 +#: tiramisu/config.py:359 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:407 +#: tiramisu/config.py:409 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:428 +#: tiramisu/config.py:430 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:488 +#: tiramisu/config.py:490 msgid "opt in getowner must be an option not {0}" msgstr "" From 162ae02df8fd05acbf9548b137baad73063a3f52 Mon Sep 17 00:00:00 2001 From: gwen Date: Fri, 27 Sep 2013 09:52:18 +0200 Subject: [PATCH 32/36] refactor (warnings_only) --- test/test_config.py | 6 ++++++ test/test_option_validator.py | 10 ++++----- tiramisu/config.py | 2 +- tiramisu/error.py | 2 +- tiramisu/option.py | 38 ++++++++++++++++------------------- 5 files changed, 30 insertions(+), 28 deletions(-) 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_option_validator.py b/test/test_option_validator.py index 76078f1..001f9f7 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -72,9 +72,9 @@ def test_validator_multi(): def test_validator_warning(): - opt1 = StrOption('opt1', '', validator=return_true, default='val', only_warning=True) - opt2 = StrOption('opt2', '', validator=return_false, only_warning=True) - opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, only_warning=True) + 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' @@ -111,8 +111,8 @@ def test_validator_warning(): def test_validator_warning_master_slave(): - ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=return_false, only_warning=True) - netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=return_if_val, only_warning=True) + 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 diff --git a/tiramisu/config.py b/tiramisu/config.py index 25dbaf9..1493e22 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -186,7 +186,7 @@ class SubConfig(object): return homeconfig.__setattr__(name, value) child = getattr(self.cfgimpl_get_description(), name) if isinstance(child, OptionDescription): - raise SyntaxError(_("can't assign to an OptionDescription")) + raise TypeError(_("can't assign to an OptionDescription")) elif not isinstance(child, SymLinkOption): if self._impl_path is None: path = name diff --git a/tiramisu/error.py b/tiramisu/error.py index a4b3f41..5694e4d 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -77,7 +77,7 @@ class ValueWarning(UserWarning): >>> def a(val): ... raise ValueError('pouet') ... - >>> s=StrOption('s', '', validator=a, only_warning=True) + >>> s=StrOption('s', '', validator=a, warnings_only=True) >>> o=OptionDescription('o', '', [s]) >>> c=Config(o) >>> c.s = 'val' diff --git a/tiramisu/option.py b/tiramisu/option.py index cf35508..df4839a 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -328,13 +328,13 @@ class Option(BaseOption): """ __slots__ = ('_multi', '_validator', '_default_multi', '_default', '_state_callback', '_callback', '_multitype', - '_only_warning', '_master_slaves', '__weakref__') + '_warnings_only', '_master_slaves', '__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, only_warning=False): + properties=None, warnings_only=False): """ :param name: the option's name :param doc: the option's description @@ -352,7 +352,7 @@ class Option(BaseOption): validation of the value :param validator_params: the validator's parameters :param properties: tuple of default properties - :param only_warning: _validator and _consistencies don't raise if True + :param warnings_only: _validator and _consistencies don't raise if True Values()._warning contain message """ @@ -391,7 +391,7 @@ class Option(BaseOption): default = [] self._multitype = multitypes.default self._default_multi = default_multi - self._only_warning = only_warning + self._warnings_only = warnings_only self.impl_validate(default) self._default = default @@ -475,6 +475,8 @@ class Option(BaseOption): def do_validation(_value, _index=None): if _value is None: return + # option validation + self._validate(_value) try: # valid with self._validator val_validator(_value) @@ -485,32 +487,26 @@ class Option(BaseOption): except ValueError as err: msg = _("invalid value {0} for option {1}: {2}").format( _value, self._name, err) - if self._only_warning: + if self._warnings_only: warnings.warn_explicit(ValueWarning(msg, self), ValueWarning, self.__class__.__name__, 0) else: raise ValueError(msg) - # option validation - self._validate(_value) # generic calculation if context is not None: descr = context.cfgimpl_get_description() - ret = None if not self._multi or force_no_multi: - ret = do_validation(value) + do_validation(value) else: if not isinstance(value, list): raise ValueError(_("invalid value {0} for option {1} " "which must be a list").format(value, self._name)) for index, val in enumerate(value): - ret_ = do_validation(val, index) - if ret_ is not None: - ret = ret_ - return ret + do_validation(val, index) def impl_getdefault(self, default_multi=False): "accessing the default value" @@ -628,7 +624,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=None, only_warning=False): + validator_params=None, properties=None, warnings_only=False): """ :param values: is a list of values the option can possibly take """ @@ -648,7 +644,7 @@ class ChoiceOption(Option): validator=validator, validator_params=validator_params, properties=properties, - only_warning=only_warning) + warnings_only=warnings_only) def impl_get_values(self): return self._values @@ -767,7 +763,7 @@ class IPOption(Option): requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, properties=None, only_private=False, allow_reserved=False, - only_warning=False): + warnings_only=False): self._only_private = only_private self._allow_reserved = allow_reserved super(IPOption, self).__init__(name, doc, default=default, @@ -779,7 +775,7 @@ class IPOption(Option): validator=validator, validator_params=validator_params, properties=properties, - only_warning=only_warning) + warnings_only=warnings_only) def _validate(self, value): try: @@ -813,7 +809,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, only_warning=False): + allow_private=False, warnings_only=False): self._allow_range = allow_range self._min_value = None self._max_value = None @@ -846,7 +842,7 @@ class PortOption(Option): validator=validator, validator_params=validator_params, properties=properties, - only_warning=only_warning) + warnings_only=warnings_only) def _validate(self, value): if self._allow_range and ":" in str(value): @@ -948,7 +944,7 @@ class DomainnameOption(Option): requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, properties=None, allow_ip=False, type_='domainname', - only_warning=False): + warnings_only=False): #netbios: for MS domain #hostname: to identify the device #domainname: @@ -968,7 +964,7 @@ class DomainnameOption(Option): validator=validator, validator_params=validator_params, properties=properties, - only_warning=only_warning) + warnings_only=warnings_only) def _validate(self, value): if self._allow_ip is True: From 2490d009353e5c860b9c6858175d81ea4d9ae002 Mon Sep 17 00:00:00 2001 From: gwen Date: Fri, 27 Sep 2013 11:28:23 +0200 Subject: [PATCH 33/36] refactor name only_private --- test/test_config_ip.py | 2 +- tiramisu/option.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) 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/tiramisu/option.py b/tiramisu/option.py index df4839a..f9a3541 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -756,15 +756,15 @@ class SymLinkOption(BaseOption): 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, + properties=None, private_only=False, allow_reserved=False, warnings_only=False): - self._only_private = only_private + self._private_only = private_only self._allow_reserved = allow_reserved super(IPOption, self).__init__(name, doc, default=default, default_multi=default_multi, @@ -787,7 +787,7 @@ class IPOption(Option): 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")) From 482dfec7f2811d872747bd33e1b0db9df4298ba0 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Fri, 27 Sep 2013 23:26:10 +0200 Subject: [PATCH 34/36] consistancies can have more than one option add _cons_broadcast --- test/test_option_consistency.py | 21 +++- test/test_state.py | 3 +- tiramisu/option.py | 191 ++++++++++++++++++-------------- 3 files changed, 130 insertions(+), 85 deletions(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 5cf53cd..1c23dac 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -4,7 +4,7 @@ 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 def test_consistency_not_equal(): @@ -159,3 +159,22 @@ 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) + 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'") diff --git a/test/test_state.py b/test/test_state.py index 8587b4a..4430bd9 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -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/option.py b/tiramisu/option.py index f9a3541..d3c4bf9 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -27,7 +27,7 @@ from types import FunctionType from IPy import IP import warnings -from tiramisu.error import ConflictError, ValueWarning +from tiramisu.error import ConfigError, ConflictError, ValueWarning from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -172,14 +172,13 @@ class BaseOption(object): 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]))) + 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 = {} @@ -395,50 +394,62 @@ class Option(BaseOption): self.impl_validate(default) self._default = default - def _launch_consistency(self, func, opt, vals, context, index, opt_): + def _launch_consistency(self, func, right_opt, right_val, context, index, + left_opts): 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) + #right_opt is also in left_opts + if right_opt not in left_opts: + raise ConfigError(_('right_opt not in left_opts')) + + left_vals = [] + for opt in left_opts: + if right_opt == opt: + value = right_val + else: + if context is not None: + path = descr.impl_get_path_by_opt(opt) + value = context._getattr(path, validate=False) + else: + value = opt.impl_getdefault() + if index is None: + #could be multi or not + left_vals.append(value) else: - values_ = opt_.impl_getdefault() - if index is not None: #value is not already set, could be higher try: - values_ = values_[index] + if right_opt == opt: + val = value + else: + val = value[index] + if val is None: + #no value so no consistencies + return + left_vals.append(val) 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) + #so return if no value + return + + if self.impl_is_multi(): + if index is None: + for idx, right_v in enumerate(right_val): + try: + left_v = [] + for left_val in left_vals: + left_v.append(left_val[idx]) + if None in left_v: + continue + except IndexError: + continue + getattr(self, func)(left_opts, left_v) 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_) + if None in left_vals: + return + getattr(self, func)(left_opts, left_vals) else: - if None not in (values, values_): - getattr(self, func)(opt_._name, values, values_) + if None in left_vals: + return + getattr(self, func)(left_opts, left_vals) def impl_validate(self, value, context=None, validate=True, force_no_multi=False): @@ -550,29 +561,31 @@ class Option(BaseOption): def impl_is_multi(self): return self._multi - def impl_add_consistency(self, func, opt): + def impl_add_consistency(self, func, *left_opts): 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 left_opts: + if not isinstance(opt, Option): + raise ValueError(_('consistency should 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')) func = '_cons_{0}'.format(func) - self._launch_consistency(func, - self, - self.impl_getdefault(), - None, None, opt) - self._consistencies.append((func, opt)) + opts = tuple([self] + list(left_opts)) + self._launch_consistency(func, self, self.impl_getdefault(), None, + None, opts) + self._consistencies.append((func, opts)) self.impl_validate(self.impl_getdefault()) - def _cons_not_equal(self, optname, value, value_): - if value == value_: + def _cons_not_equal(self, opts, vals): + if len(opts) != 2: + raise ConfigError(_('invalid len for opts')) + if vals[0] == vals[1]: raise ValueError(_("invalid value {0} for option {1} " "must be different as {2} option" - "").format(value, self._name, optname)) + "").format(vals[0], self._name, opts[1]._name)) def _impl_convert_callbacks(self, descr, load=False): if not load and self._callback is None: @@ -889,15 +902,17 @@ class NetmaskOption(Option): 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) + 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) + self.__cons_netmask(opts, vals[0], vals[1], True) - 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), @@ -923,17 +938,30 @@ 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')) + 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" @@ -1098,12 +1126,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, left_opts = consistency + for opt in left_opts: + _consistencies.setdefault(opt, + []).append((func, + left_opts)) else: _currpath.append(attr) option.impl_build_cache(cache_path, @@ -1186,17 +1213,15 @@ 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, right_opt, right_val, context=None, index=None): + #[('_cons_not_equal', (opt1, opt2))] + consistencies = self._consistencies.get(right_opt) 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, opts in consistencies: + #opts[0] is the option where func is set + #opts is left_opts + ret = opts[0]._launch_consistency(func, right_opt, right_val, + context, index, opts) if ret is False: return False return True From 70f684e70c5e4964a9e1acb810622b482ba92551 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 28 Sep 2013 17:05:01 +0200 Subject: [PATCH 35/36] tiramisu/option.py: separate _consistencies (for Option) and _cache_consistencies (for OptionDescription) _launch_consistency need index for multi's option _cons_not_equal support multi options tiramisu/value.py: Multi._validate support consistency --- test/test_option_consistency.py | 98 +++++++++- test/test_state.py | 2 +- tiramisu/option.py | 337 ++++++++++++++++---------------- tiramisu/value.py | 14 +- 4 files changed, 277 insertions(+), 174 deletions(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 1c23dac..d5226db 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -5,6 +5,7 @@ from tiramisu.setting import owners, groups from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ 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(): @@ -170,11 +233,42 @@ def test_consistency_broadcast(): 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_state.py b/test/test_state.py index 4430bd9..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 diff --git a/tiramisu/option.py b/tiramisu/option.py index d3c4bf9..c7a28c2 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -61,9 +61,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): @@ -73,7 +72,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): @@ -98,8 +96,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': @@ -109,15 +106,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( @@ -149,57 +143,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: - 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 _impl_convert_requires(self, descr, load=False): """export of the requires during the serialization process @@ -245,10 +188,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 @@ -268,7 +208,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 @@ -327,7 +268,8 @@ class Option(BaseOption): """ __slots__ = ('_multi', '_validator', '_default_multi', '_default', '_state_callback', '_callback', '_multitype', - '_warnings_only', '_master_slaves', '__weakref__') + '_consistencies', '_warnings_only', '_master_slaves', + '_state_consistencies', '__weakref__') _empty = '' def __init__(self, name, doc, default=None, default_multi=None, @@ -393,66 +335,58 @@ class Option(BaseOption): self._warnings_only = warnings_only self.impl_validate(default) self._default = default + self._consistencies = None - def _launch_consistency(self, func, right_opt, right_val, context, index, - left_opts): + 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() - #right_opt is also in left_opts - if right_opt not in left_opts: - raise ConfigError(_('right_opt not in left_opts')) + #option is also in all_cons_opts + if option not in all_cons_opts: + raise ConfigError(_('option not in all_cons_opts')) - left_vals = [] - for opt in left_opts: - if right_opt == opt: - value = right_val + 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: - path = descr.impl_get_path_by_opt(opt) - value = context._getattr(path, validate=False) + opt_value = context._getattr( + descr.impl_get_path_by_opt(opt), validate=False) else: - value = opt.impl_getdefault() - if index is None: - #could be multi or not - left_vals.append(value) + 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 + #value is not already set, could be higher index try: - if right_opt == opt: - val = value - else: - val = value[index] - if val is None: - #no value so no consistencies - return - left_vals.append(val) + all_cons_vals.append(opt_value[index]) except IndexError: #so return if no value return - - if self.impl_is_multi(): - if index is None: - for idx, right_v in enumerate(right_val): - try: - left_v = [] - for left_val in left_vals: - left_v.append(left_val[idx]) - if None in left_v: - continue - except IndexError: - continue - getattr(self, func)(left_opts, left_v) - else: - if None in left_vals: - return - getattr(self, func)(left_opts, left_vals) - else: - if None in left_vals: - return - getattr(self, func)(left_opts, left_vals) + getattr(self, func)(all_cons_opts, all_cons_vals) def impl_validate(self, value, context=None, validate=True, - force_no_multi=False): + force_index=None): """ :param value: the option's value :param context: Config's context @@ -509,12 +443,11 @@ class Option(BaseOption): if context is not None: descr = context.cfgimpl_get_description() - if not self._multi or force_no_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, val in enumerate(value): do_validation(val, index) @@ -561,31 +494,45 @@ class Option(BaseOption): def impl_is_multi(self): return self._multi - def impl_add_consistency(self, func, *left_opts): + 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 = [] - for opt in left_opts: + for opt in other_opts: if not isinstance(opt, Option): - raise ValueError(_('consistency should be set with an option')) + raise ConfigError(_('consistency should be set with an option')) if self is opt: - raise ValueError(_('cannot add consistency with itself')) + raise ConfigError(_('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')) + raise ConfigError(_('every options in consistency should be ' + 'multi or none')) func = '_cons_{0}'.format(func) - opts = tuple([self] + list(left_opts)) - self._launch_consistency(func, self, self.impl_getdefault(), None, - None, opts) - self._consistencies.append((func, opts)) + 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, opts, vals): - if len(opts) != 2: - raise ConfigError(_('invalid len for opts')) - if vals[0] == vals[1]: - raise ValueError(_("invalid value {0} for option {1} " - "must be different as {2} option" - "").format(vals[0], self._name, opts[1]._name)) + 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: @@ -621,6 +568,57 @@ 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 @@ -734,7 +732,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 @@ -760,12 +758,6 @@ 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" @@ -904,10 +896,14 @@ class NetmaskOption(Option): def _cons_network_netmask(self, opts, vals): #opts must be (netmask, network) options + if None in vals: + return self.__cons_netmask(opts, vals[0], vals[1], False) def _cons_ip_netmask(self, opts, vals): #opts must be (netmask, ip) options + if None in vals: + return self.__cons_netmask(opts, vals[0], vals[1], True) def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net): @@ -955,6 +951,8 @@ class BroadcastOption(Option): 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} ' @@ -964,7 +962,12 @@ class BroadcastOption(Option): 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' @@ -973,10 +976,6 @@ class DomainnameOption(Option): callback_params=None, validator=None, validator_params=None, properties=None, allow_ip=False, type_='domainname', warnings_only=False): - #netbios: for MS domain - #hostname: to identify the device - #domainname: - #fqdn: with tld, not supported yet if type_ not in ['netbios', 'hostname', 'domainname']: raise ValueError(_('unknown type_ {0} for hostname').format(type_)) self._type = type_ @@ -1030,9 +1029,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): @@ -1053,6 +1052,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 @@ -1126,11 +1126,11 @@ class OptionDescription(BaseOption): if not force_no_consistencies and \ option._consistencies is not None: for consistency in option._consistencies: - func, left_opts = consistency - for opt in left_opts: + func, all_cons_opts = consistency + for opt in all_cons_opts: _consistencies.setdefault(opt, []).append((func, - left_opts)) + all_cons_opts)) else: _currpath.append(attr) option.impl_build_cache(cache_path, @@ -1142,7 +1142,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): @@ -1213,15 +1218,18 @@ class OptionDescription(BaseOption): def impl_get_group_type(self): return self._group_type - def _valid_consistency(self, right_opt, right_val, context=None, index=None): - #[('_cons_not_equal', (opt1, opt2))] - consistencies = self._consistencies.get(right_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 func, opts in consistencies: - #opts[0] is the option where func is set - #opts is left_opts - ret = opts[0]._launch_consistency(func, right_opt, right_val, - context, index, opts) + 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 @@ -1261,6 +1269,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) diff --git a/tiramisu/value.py b/tiramisu/value.py index 6942453..4426742 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -455,10 +455,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): @@ -476,7 +476,8 @@ 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, @@ -486,7 +487,6 @@ class Multi(list): 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() @@ -538,11 +538,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.impl_validate(value, context=self.context(), - force_no_multi=True) + force_index=force_index) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}" From 4fa33e1a6cfaeb914578702849ef3dceaf8cc44b Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 28 Sep 2013 20:02:41 +0200 Subject: [PATCH 36/36] update translation --- translations/fr/tiramisu.po | 246 +++++++++++++++++++++--------------- translations/tiramisu.pot | 230 ++++++++++++++++++--------------- 2 files changed, 273 insertions(+), 203 deletions(-) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 0075059..256a0da 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-09-26 22:33+CEST\n" +"POT-Creation-Date: 2013-09-28 19:06+CEST\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: LANGUAGE \n" @@ -11,14 +11,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" -#: tiramisu/autolib.py:145 +#: tiramisu/autolib.py:144 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:154 +#: tiramisu/autolib.py:153 msgid "" "unable to carry out a calculation, option value with multi types must have " "same length for: {0}" @@ -26,80 +26,80 @@ msgstr "" "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:52 +#: tiramisu/config.py:51 msgid "descr must be an optiondescription, not {0}" msgstr "descr doit être une optiondescription pas un {0}" -#: tiramisu/config.py:127 +#: tiramisu/config.py:126 msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:163 +#: tiramisu/config.py:162 msgid "" "no option description found for this config (may be metaconfig without meta)" msgstr "" "pas d'option description trouvé pour cette config (peut être une metaconfig " "sans meta)" -#: tiramisu/config.py:189 +#: tiramisu/config.py:188 msgid "can't assign to an OptionDescription" msgstr "ne peut pas attribuer une valeur à une OptionDescription" -#: tiramisu/config.py:320 +#: tiramisu/config.py:319 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:359 +#: tiramisu/config.py:358 msgid "no option found in config with these criteria" msgstr "aucune option trouvée dans la config avec ces critères" -#: tiramisu/config.py:409 +#: tiramisu/config.py:408 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:430 +#: tiramisu/config.py:429 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:490 +#: tiramisu/config.py:489 msgid "opt in getowner must be an option not {0}" msgstr "opt dans getowner doit être une option pas {0}" -#: tiramisu/option.py:70 +#: tiramisu/option.py:68 msgid "invalid name: {0} for option" msgstr "nom invalide : {0} pour l'option" -#: tiramisu/option.py:80 +#: tiramisu/option.py:77 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:122 +#: tiramisu/option.py:115 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 +#: tiramisu/option.py:142 tiramisu/value.py:360 msgid "information's item not found: {0}" msgstr "aucune config spécifié alors que c'est nécessaire" -#: tiramisu/option.py:266 +#: tiramisu/option.py:204 msgid "cannot serialize Option, only in OptionDescription" msgstr "ne peut serialiser une Option, seulement via une OptionDescription" -#: tiramisu/option.py:367 +#: tiramisu/option.py:307 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" "une default_multi est renseignée alors que multi est False dans l'option : " "{0}" -#: tiramisu/option.py:373 +#: tiramisu/option.py:313 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:378 +#: tiramisu/option.py:318 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ée" -#: tiramisu/option.py:381 +#: tiramisu/option.py:321 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" @@ -107,52 +107,68 @@ msgstr "" "params définis pour une fonction callback mais par de callback encore " "définis pour l'option {0}" -#: tiramisu/option.py:486 tiramisu/value.py:547 +#: tiramisu/option.py:360 +msgid "option not in all_cons_opts" +msgstr "option non présentante dans all_cons_opts" + +#: tiramisu/option.py:432 tiramisu/value.py:545 msgid "invalid value {0} for option {1}: {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}" -#: 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:449 +msgid "which must be a list" +msgstr "lequel doit être une liste" -#: tiramisu/option.py:577 -msgid "invalid value {0} for option {1} must be different as {2} option" +#: tiramisu/option.py:509 +msgid "consistency should be set with an option" +msgstr "consistency doit être configuré avec une option" + +#: tiramisu/option.py:511 +msgid "cannot add consistency with itself" +msgstr "ne peut ajouter une consistency avec lui même" + +#: tiramisu/option.py:513 +msgid "every options in consistency should be multi or none" msgstr "" -"valeur invalide {0} pour l'option {1} doit être différente de l'option {2}" +"toutes les options d'une consistency devrait être multi ou ne pas l'être" -#: tiramisu/option.py:636 +#: tiramisu/option.py:533 +msgid "same value for {0} and {1}" +msgstr "même valeur pour {0} et {1}" + +#: tiramisu/option.py:642 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:639 +#: tiramisu/option.py:645 msgid "open_values must be a boolean for {0}" msgstr "open_values doit être un booléen pour {0}" -#: tiramisu/option.py:661 +#: tiramisu/option.py:667 msgid "value {0} is not permitted, only {1} is allowed" msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées" -#: tiramisu/option.py:673 +#: tiramisu/option.py:679 msgid "value must be a boolean" msgstr "valeur doit être un booléen" -#: tiramisu/option.py:683 +#: tiramisu/option.py:689 msgid "value must be an integer" msgstr "valeur doit être un nombre entier" -#: tiramisu/option.py:693 +#: tiramisu/option.py:699 msgid "value must be a float" msgstr "valeur doit être un nombre flottant" -#: tiramisu/option.py:703 +#: tiramisu/option.py:709 msgid "value must be a string, not {0}" msgstr "valeur doit être une chaîne, pas {0}" -#: tiramisu/option.py:721 +#: tiramisu/option.py:727 msgid "value must be an unicode" msgstr "valeur doit être une valeur unicode" -#: tiramisu/option.py:733 +#: tiramisu/option.py:739 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" @@ -188,106 +204,124 @@ msgstr "le réseau ne doit pas être dans la classe reservée" msgid "invalid netmask address {0}" msgstr "masque de sous-réseau invalide {0}" -#: tiramisu/option.py:916 +#: tiramisu/option.py:910 +msgid "invalid len for opts" +msgstr "longueur invalide pour opts" + +#: tiramisu/option.py:922 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:921 +#: tiramisu/option.py:927 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:926 +#: tiramisu/option.py:932 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "IP invalide {0} ({1}) avec masque {2} ({3})" -#: tiramisu/option.py:928 +#: tiramisu/option.py:934 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3})" -#: tiramisu/option.py:939 +#: tiramisu/option.py:948 msgid "invalid broadcast address {0}" msgstr "adresse de broadcast invalide {0}" +#: tiramisu/option.py:952 +msgid "invalid len for vals" +msgstr "longueur invalide pour vals" + #: tiramisu/option.py:957 +msgid "" +"invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" +msgstr "" +"Broadcast invalide {0} ({1}) avec le réseau {2} ({3}) et le masque {4} ({5})" + +#: tiramisu/option.py:979 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:960 +#: tiramisu/option.py:982 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:990 +#: tiramisu/option.py:1012 msgid "invalid value for {0}, must have dot" msgstr "valeur invalide pour {0}, doit avoir un point" -#: tiramisu/option.py:993 +#: tiramisu/option.py:1015 msgid "invalid domainname's length for {0} (max {1})" msgstr "longueur du nom de domaine invalide pour {0} (maximum {1})" -#: tiramisu/option.py:996 +#: tiramisu/option.py:1018 msgid "invalid domainname's length for {0} (min 2)" msgstr "longueur du nom de domaine invalide pour {0} (minimum 2)" -#: tiramisu/option.py:1000 +#: tiramisu/option.py:1022 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:1027 +#: tiramisu/option.py:1049 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:1044 +#: tiramisu/option.py:1067 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:1095 +#: tiramisu/option.py:1118 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:1129 +#: tiramisu/option.py:1148 +msgid "consistency with option {0} which is not in Config" +msgstr "consistency avec l'option {0} qui n'est pas dans une Config" + +#: tiramisu/option.py:1156 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:1135 +#: tiramisu/option.py:1162 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:1145 +#: tiramisu/option.py:1172 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:1158 +#: tiramisu/option.py:1185 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:1161 +#: tiramisu/option.py:1188 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:1164 +#: tiramisu/option.py:1191 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:1175 +#: tiramisu/option.py:1202 msgid "master group with wrong master name for {0}" msgstr "le groupe maître avec un nom de maître érroné pour {0}" -#: tiramisu/option.py:1184 +#: tiramisu/option.py:1211 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:1187 +#: tiramisu/option.py:1214 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:1277 +#: tiramisu/option.py:1306 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:1294 +#: tiramisu/option.py:1323 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -295,110 +329,110 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, expected et action" -#: tiramisu/option.py:1299 +#: tiramisu/option.py:1328 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" "requirements mal formés pour l'option : {0} inverse doit être un booléen" -#: tiramisu/option.py:1303 +#: tiramisu/option.py:1332 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" "requirements mal formés pour l'option : {0} transitive doit être booléen" -#: tiramisu/option.py:1307 +#: tiramisu/option.py:1336 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" "requirements mal formés pour l'option : {0} same_action doit être un booléen" -#: tiramisu/option.py:1311 +#: tiramisu/option.py:1340 msgid "malformed requirements must be an option in option {0}" msgstr "requirements mal formés doit être une option dans l'option {0}" -#: tiramisu/option.py:1314 +#: tiramisu/option.py:1343 msgid "malformed requirements option {0} should not be a multi" msgstr "requirements mal formés l'option {0} ne doit pas être une multi" -#: tiramisu/option.py:1320 +#: tiramisu/option.py:1349 msgid "" "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" "requirements mal formés deuxième argument doit être valide pour l'option " "{0} : {1}" -#: tiramisu/option.py:1325 +#: tiramisu/option.py:1354 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "incohérence dans les types action pour l'option : {0} action {1}" -#: tiramisu/option.py:1350 +#: tiramisu/option.py:1379 msgid "{0} should be a function" msgstr "{0} doit être une fonction" -#: tiramisu/option.py:1353 +#: tiramisu/option.py:1382 msgid "{0}_params should be a dict" msgstr "{0}_params devrait être un dict" -#: tiramisu/option.py:1356 +#: tiramisu/option.py:1385 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 +#: tiramisu/option.py:1389 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" -#: tiramisu/option.py:1366 +#: tiramisu/option.py:1395 msgid "validator not support tuple" msgstr "validator n'accepte pas de tuple" -#: tiramisu/option.py:1369 +#: tiramisu/option.py:1398 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 +#: tiramisu/option.py:1402 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 +#: tiramisu/setting.py:111 msgid "can't rebind {0}" msgstr "ne peut redéfinir ({0})" -#: tiramisu/setting.py:121 +#: tiramisu/setting.py:116 msgid "can't unbind {0}" msgstr "ne peut supprimer ({0})" -#: tiramisu/setting.py:259 +#: tiramisu/setting.py:254 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:322 +#: tiramisu/setting.py:317 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:337 +#: tiramisu/setting.py:332 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:440 +#: tiramisu/setting.py:435 msgid "cannot change the value for option {0} this option is frozen" msgstr "" "ne peut modifier la valeur de l'option {0} cette option n'est pas modifiable" -#: tiramisu/setting.py:446 +#: tiramisu/setting.py:441 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:464 +#: tiramisu/setting.py:459 msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:471 tiramisu/value.py:301 +#: tiramisu/setting.py:466 tiramisu/value.py:299 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" -#: tiramisu/setting.py:558 +#: tiramisu/setting.py:553 msgid "" "malformed requirements imbrication detected for option: '{0}' with " "requirement on: '{1}'" @@ -406,73 +440,80 @@ msgstr "" "imbrication de requirements mal formés detectée pour l'option : '{0}' avec " "requirement sur : '{1}'" -#: tiramisu/setting.py:570 +#: tiramisu/setting.py:565 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/__init__.py:52 +#: tiramisu/storage/__init__.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/storage/__init__.py:86 +#: tiramisu/storage/__init__.py:81 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 +#: tiramisu/storage/dictionary/storage.py:37 msgid "dictionary storage cannot delete session" msgstr "" "impossible de supprimer une session dans un espace de stockage dictionary" -#: tiramisu/storage/dictionary/storage.py:50 +#: tiramisu/storage/dictionary/storage.py:48 msgid "session already used" msgstr "session déjà utilisée" -#: tiramisu/storage/dictionary/storage.py:52 +#: tiramisu/storage/dictionary/storage.py:50 msgid "a dictionary cannot be persistent" msgstr "un espace de stockage dictionary ne peut être persistant" -#: tiramisu/value.py:308 +#: tiramisu/value.py:306 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:416 +#: tiramisu/value.py:414 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:440 +#: tiramisu/value.py:438 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:470 +#: tiramisu/value.py:468 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:507 +#: tiramisu/value.py:505 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:511 +#: tiramisu/value.py:509 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:520 +#: tiramisu/value.py:518 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:528 +#: tiramisu/value.py:526 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:536 +#: tiramisu/value.py:534 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:564 +#: tiramisu/value.py:562 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 "invalid value {0} for option {1} which must be a list" +#~ msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste" + +#~ msgid "invalid value {0} for option {1} must be different as {2} option" +#~ msgstr "" +#~ "valeur invalide {0} pour l'option {1} doit être différente de l'option {2}" + #~ msgid "validator should return a boolean, not {0}" #~ msgstr "le validator devrait retourner un boolean, pas un {0}" @@ -491,9 +532,6 @@ msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave" #~ 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" - #~ msgid "metaconfig's children must be config, not {0}" #~ msgstr "enfants d'une metaconfig doit être une config, pas {0}" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index dfd4b86..57b90e8 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-26 22:33+CEST\n" +"POT-Creation-Date: 2013-09-28 19:06+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,131 +15,147 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/autolib.py:145 +#: tiramisu/autolib.py:144 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" -#: tiramisu/autolib.py:154 +#: tiramisu/autolib.py:153 msgid "unable to carry out a calculation, option value with multi types must have same length for: {0}" msgstr "" -#: tiramisu/config.py:52 +#: tiramisu/config.py:51 msgid "descr must be an optiondescription, not {0}" msgstr "" -#: tiramisu/config.py:127 +#: tiramisu/config.py:126 msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:163 +#: tiramisu/config.py:162 msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:189 +#: tiramisu/config.py:188 msgid "can't assign to an OptionDescription" msgstr "" -#: tiramisu/config.py:320 +#: tiramisu/config.py:319 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:359 +#: tiramisu/config.py:358 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:409 +#: tiramisu/config.py:408 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:430 +#: tiramisu/config.py:429 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:490 +#: tiramisu/config.py:489 msgid "opt in getowner must be an option not {0}" msgstr "" -#: tiramisu/option.py:70 +#: tiramisu/option.py:68 msgid "invalid name: {0} for option" msgstr "" -#: tiramisu/option.py:80 +#: tiramisu/option.py:77 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "" -#: tiramisu/option.py:122 +#: tiramisu/option.py:115 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option.py:149 tiramisu/value.py:362 +#: tiramisu/option.py:142 tiramisu/value.py:360 msgid "information's item not found: {0}" msgstr "" -#: tiramisu/option.py:266 +#: tiramisu/option.py:204 msgid "cannot serialize Option, only in OptionDescription" msgstr "" -#: tiramisu/option.py:367 +#: tiramisu/option.py:307 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -#: tiramisu/option.py:373 +#: tiramisu/option.py:313 msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:378 +#: tiramisu/option.py:318 msgid "default value not allowed if option: {0} is calculated" msgstr "" -#: tiramisu/option.py:381 +#: tiramisu/option.py:321 msgid "params defined for a callback function but no callback defined yet for option {0}" msgstr "" -#: tiramisu/option.py:486 tiramisu/value.py:547 +#: tiramisu/option.py:360 +msgid "option not in all_cons_opts" +msgstr "" + +#: tiramisu/option.py:432 tiramisu/value.py:545 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:506 -msgid "invalid value {0} for option {1} which must be a list" +#: tiramisu/option.py:449 +msgid "which must be a list" msgstr "" -#: tiramisu/option.py:577 -msgid "invalid value {0} for option {1} must be different as {2} option" +#: tiramisu/option.py:509 +msgid "consistency should be set with an option" msgstr "" -#: tiramisu/option.py:636 +#: tiramisu/option.py:511 +msgid "cannot add consistency with itself" +msgstr "" + +#: tiramisu/option.py:513 +msgid "every options in consistency should be multi or none" +msgstr "" + +#: tiramisu/option.py:533 +msgid "same value for {0} and {1}" +msgstr "" + +#: tiramisu/option.py:642 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:639 +#: tiramisu/option.py:645 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:661 +#: tiramisu/option.py:667 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:673 +#: tiramisu/option.py:679 msgid "value must be a boolean" msgstr "" -#: tiramisu/option.py:683 +#: tiramisu/option.py:689 msgid "value must be an integer" msgstr "" -#: tiramisu/option.py:693 +#: tiramisu/option.py:699 msgid "value must be a float" msgstr "" -#: tiramisu/option.py:703 +#: tiramisu/option.py:709 msgid "value must be a string, not {0}" msgstr "" -#: tiramisu/option.py:721 +#: tiramisu/option.py:727 msgid "value must be an unicode" msgstr "" -#: tiramisu/option.py:733 +#: tiramisu/option.py:739 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" @@ -175,263 +191,279 @@ msgstr "" msgid "invalid netmask address {0}" msgstr "" -#: tiramisu/option.py:916 +#: tiramisu/option.py:910 +msgid "invalid len for opts" +msgstr "" + +#: tiramisu/option.py:922 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "" -#: tiramisu/option.py:921 +#: tiramisu/option.py:927 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "" -#: tiramisu/option.py:926 +#: tiramisu/option.py:932 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:928 +#: tiramisu/option.py:934 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:939 +#: tiramisu/option.py:948 msgid "invalid broadcast address {0}" msgstr "" +#: tiramisu/option.py:952 +msgid "invalid len for vals" +msgstr "" + #: tiramisu/option.py:957 +msgid "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" +msgstr "" + +#: tiramisu/option.py:979 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:960 +#: tiramisu/option.py:982 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:990 +#: tiramisu/option.py:1012 msgid "invalid value for {0}, must have dot" msgstr "" -#: tiramisu/option.py:993 +#: tiramisu/option.py:1015 msgid "invalid domainname's length for {0} (max {1})" msgstr "" -#: tiramisu/option.py:996 +#: tiramisu/option.py:1018 msgid "invalid domainname's length for {0} (min 2)" msgstr "" -#: tiramisu/option.py:1000 +#: tiramisu/option.py:1022 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:1027 +#: tiramisu/option.py:1049 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:1044 +#: tiramisu/option.py:1067 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:1095 +#: tiramisu/option.py:1118 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:1129 +#: tiramisu/option.py:1148 +msgid "consistency with option {0} which is not in Config" +msgstr "" + +#: tiramisu/option.py:1156 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:1135 +#: tiramisu/option.py:1162 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:1145 +#: tiramisu/option.py:1172 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:1158 +#: tiramisu/option.py:1185 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:1161 +#: tiramisu/option.py:1188 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:1164 +#: tiramisu/option.py:1191 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:1175 +#: tiramisu/option.py:1202 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:1184 +#: tiramisu/option.py:1211 msgid "no child has same nom has master group for: {0}" msgstr "" -#: tiramisu/option.py:1187 +#: tiramisu/option.py:1214 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1277 +#: tiramisu/option.py:1306 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1294 +#: tiramisu/option.py:1323 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1299 +#: tiramisu/option.py:1328 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1303 +#: tiramisu/option.py:1332 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1307 +#: tiramisu/option.py:1336 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1311 +#: tiramisu/option.py:1340 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1314 +#: tiramisu/option.py:1343 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1320 +#: tiramisu/option.py:1349 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1325 +#: tiramisu/option.py:1354 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "" -#: tiramisu/option.py:1350 +#: tiramisu/option.py:1379 msgid "{0} should be a function" msgstr "" -#: tiramisu/option.py:1353 +#: tiramisu/option.py:1382 msgid "{0}_params should be a dict" msgstr "" -#: tiramisu/option.py:1356 +#: tiramisu/option.py:1385 msgid "{0}_params with key {1} should not have length different to 1" msgstr "" -#: tiramisu/option.py:1360 +#: tiramisu/option.py:1389 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "" -#: tiramisu/option.py:1366 +#: tiramisu/option.py:1395 msgid "validator not support tuple" msgstr "" -#: tiramisu/option.py:1369 +#: tiramisu/option.py:1398 msgid "{0}_params should have an option not a {0} for first argument" msgstr "" -#: tiramisu/option.py:1373 +#: tiramisu/option.py:1402 msgid "{0}_params should have a boolean not a {0} for second argument" msgstr "" -#: tiramisu/setting.py:116 +#: tiramisu/setting.py:111 msgid "can't rebind {0}" msgstr "" -#: tiramisu/setting.py:121 +#: tiramisu/setting.py:116 msgid "can't unbind {0}" msgstr "" -#: tiramisu/setting.py:259 +#: tiramisu/setting.py:254 msgid "cannot append {0} property for option {1}: this property is calculated" msgstr "" -#: tiramisu/setting.py:322 +#: tiramisu/setting.py:317 msgid "opt and all_properties must not be set together in reset" msgstr "" -#: tiramisu/setting.py:337 +#: tiramisu/setting.py:332 msgid "if opt is not None, path should not be None in _getproperties" msgstr "" -#: tiramisu/setting.py:440 +#: tiramisu/setting.py:435 msgid "cannot change the value for option {0} this option is frozen" msgstr "" -#: tiramisu/setting.py:446 +#: tiramisu/setting.py:441 msgid "trying to access to an option named: {0} with properties {1}" msgstr "" -#: tiramisu/setting.py:464 +#: tiramisu/setting.py:459 msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:471 tiramisu/value.py:301 +#: tiramisu/setting.py:466 tiramisu/value.py:299 msgid "invalid generic owner {0}" msgstr "" -#: tiramisu/setting.py:558 +#: tiramisu/setting.py:553 msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" msgstr "" -#: tiramisu/setting.py:570 +#: tiramisu/setting.py:565 msgid "option '{0}' has requirement's property error: {1} {2}" msgstr "" -#: tiramisu/storage/__init__.py:52 +#: tiramisu/storage/__init__.py:47 msgid "storage_type is already set, cannot rebind it" msgstr "" -#: tiramisu/storage/__init__.py:86 +#: tiramisu/storage/__init__.py:81 msgid "option {0} not already exists in storage {1}" msgstr "" -#: tiramisu/storage/dictionary/storage.py:39 +#: tiramisu/storage/dictionary/storage.py:37 msgid "dictionary storage cannot delete session" msgstr "" -#: tiramisu/storage/dictionary/storage.py:50 +#: tiramisu/storage/dictionary/storage.py:48 msgid "session already used" msgstr "" -#: tiramisu/storage/dictionary/storage.py:52 +#: tiramisu/storage/dictionary/storage.py:50 msgid "a dictionary cannot be persistent" msgstr "" -#: tiramisu/value.py:308 +#: tiramisu/value.py:306 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:416 +#: tiramisu/value.py:414 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:440 +#: tiramisu/value.py:438 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" -#: tiramisu/value.py:470 +#: tiramisu/value.py:468 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:507 +#: tiramisu/value.py:505 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:511 +#: tiramisu/value.py:509 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:520 +#: tiramisu/value.py:518 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:528 +#: tiramisu/value.py:526 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:536 +#: tiramisu/value.py:534 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:564 +#: tiramisu/value.py:562 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr ""