From 7165f732921ff6128ad2de1c14fc11ae1ac6349c Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 17 Sep 2013 09:02:10 +0200 Subject: [PATCH 01/61] add metaconfig --- test/test_metaconfig.py | 302 ++++++++++++++++++++-------------------- tiramisu/config.py | 174 +++++++++++------------ 2 files changed, 238 insertions(+), 238 deletions(-) diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index 35d65d5..9214cb4 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -1,172 +1,172 @@ -#import autopath +import autopath -#from py.test import raises +from py.test import raises -#from tiramisu.setting import owners -#from tiramisu.config import Config, MetaConfig -#from tiramisu.option import IntOption, OptionDescription -#from tiramisu.error import ConfigError +from tiramisu.setting import owners +from tiramisu.config import Config, MetaConfig +from tiramisu.option import IntOption, OptionDescription +from tiramisu.error import ConfigError -#owners.addowner('meta') +owners.addowner('meta') -#def make_description(): -# i1 = IntOption('i1', '') -# i2 = IntOption('i2', '', default=1) -# i3 = IntOption('i3', '') -# i4 = IntOption('i4', '', default=2) -# od1 = OptionDescription('od1', '', [i1, i2, i3, i4]) -# od2 = OptionDescription('od2', '', [od1]) -# conf1 = Config(od2) -# conf2 = Config(od2) -# meta = MetaConfig([conf1, conf2]) -# meta.cfgimpl_get_settings().setowner(owners.meta) -# return meta +def make_description(): + i1 = IntOption('i1', '') + i2 = IntOption('i2', '', default=1) + i3 = IntOption('i3', '') + i4 = IntOption('i4', '', default=2) + od1 = OptionDescription('od1', '', [i1, i2, i3, i4]) + od2 = OptionDescription('od2', '', [od1]) + conf1 = Config(od2) + conf2 = Config(od2) + meta = MetaConfig([conf1, conf2]) + meta.cfgimpl_get_settings().setowner(owners.meta) + return meta -##FIXME ne pas mettre 2 meta dans une config -##FIXME ne pas mettre 2 OD differents dans un meta -#def test_none(): -# meta = make_description() -# conf1, conf2 = meta._impl_children -# assert conf1.od1.i3 is conf2.od1.i3 is None -# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default -# meta.od1.i3 = 3 -# assert conf1.od1.i3 == conf2.od1.i3 == 3 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta -# meta.od1.i3 = 3 -# conf1.od1.i3 = 2 -# assert conf1.od1.i3 == 2 -# assert conf2.od1.i3 == 3 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta -# meta.od1.i3 = 4 -# assert conf1.od1.i3 == 2 -# assert conf2.od1.i3 == 4 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta -# del(meta.od1.i3) -# assert conf1.od1.i3 == 2 -# assert conf2.od1.i3 is None -# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default -# del(conf1.od1.i3) -# assert conf1.od1.i3 is conf2.od1.i3 is None -# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default +#FIXME ne pas mettre 2 meta dans une config +#FIXME ne pas mettre 2 OD differents dans un meta +def test_none(): + meta = make_description() + conf1, conf2 = meta._impl_children + assert conf1.od1.i3 is conf2.od1.i3 is None + assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default + meta.od1.i3 = 3 + assert conf1.od1.i3 == conf2.od1.i3 == 3 + assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta + meta.od1.i3 = 3 + conf1.od1.i3 = 2 + assert conf1.od1.i3 == 2 + assert conf2.od1.i3 == 3 + assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta + meta.od1.i3 = 4 + assert conf1.od1.i3 == 2 + assert conf2.od1.i3 == 4 + assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta + del(meta.od1.i3) + assert conf1.od1.i3 == 2 + assert conf2.od1.i3 is None + assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default + del(conf1.od1.i3) + assert conf1.od1.i3 is conf2.od1.i3 is None + assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default -#def test_default(): -# meta = make_description() -# conf1, conf2 = meta._impl_children -# assert conf1.od1.i2 == conf2.od1.i2 == 1 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default -# meta.od1.i2 = 3 -# assert conf1.od1.i2 == conf2.od1.i2 == 3 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta -# meta.od1.i2 = 3 -# conf1.od1.i2 = 2 -# assert conf1.od1.i2 == 2 -# assert conf2.od1.i2 == 3 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta -# meta.od1.i2 = 4 -# assert conf1.od1.i2 == 2 -# assert conf2.od1.i2 == 4 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta -# del(meta.od1.i2) -# assert conf1.od1.i2 == 2 -# assert conf2.od1.i2 == 1 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default -# del(conf1.od1.i2) -# assert conf1.od1.i2 == conf2.od1.i2 == 1 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default +def test_default(): + meta = make_description() + conf1, conf2 = meta._impl_children + assert conf1.od1.i2 == conf2.od1.i2 == 1 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default + meta.od1.i2 = 3 + assert conf1.od1.i2 == conf2.od1.i2 == 3 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta + meta.od1.i2 = 3 + conf1.od1.i2 = 2 + assert conf1.od1.i2 == 2 + assert conf2.od1.i2 == 3 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta + meta.od1.i2 = 4 + assert conf1.od1.i2 == 2 + assert conf2.od1.i2 == 4 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta + del(meta.od1.i2) + assert conf1.od1.i2 == 2 + assert conf2.od1.i2 == 1 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default + del(conf1.od1.i2) + assert conf1.od1.i2 == conf2.od1.i2 == 1 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default -#def test_contexts(): -# meta = make_description() -# conf1, conf2 = meta._impl_children -# assert conf1.od1.i2 == conf2.od1.i2 == 1 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default -# meta.set_contexts('od1.i2', 6) -# assert meta.od1.i2 == 1 -# assert conf1.od1.i2 == conf2.od1.i2 == 6 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.user +def test_contexts(): + meta = make_description() + conf1, conf2 = meta._impl_children + assert conf1.od1.i2 == conf2.od1.i2 == 1 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default + meta.set_contexts('od1.i2', 6) + assert meta.od1.i2 == 1 + assert conf1.od1.i2 == conf2.od1.i2 == 6 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.user -#def test_find(): -# meta = make_description() -# i2 = meta.unwrap_from_path('od1.i2') -# assert [i2] == meta.find(byname='i2') -# assert i2 == meta.find_first(byname='i2') -# assert meta.make_dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None, 'od1.i2': 1} +def test_find(): + meta = make_description() + i2 = meta.unwrap_from_path('od1.i2') + assert [i2] == meta.find(byname='i2') + assert i2 == meta.find_first(byname='i2') + assert meta.make_dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None, 'od1.i2': 1} -#def test_meta_meta(): -# meta1 = make_description() -# meta2 = MetaConfig([meta1]) -# meta2.cfgimpl_get_settings().setowner(owners.meta) -# conf1, conf2 = meta1._impl_children -# assert conf1.od1.i2 == conf2.od1.i2 == 1 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default -# meta2.od1.i2 = 3 -# assert conf1.od1.i2 == conf2.od1.i2 == 3 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta -# meta2.od1.i2 = 3 -# conf1.od1.i2 = 2 -# assert conf1.od1.i2 == 2 -# assert conf2.od1.i2 == 3 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta -# meta2.od1.i2 = 4 -# assert conf1.od1.i2 == 2 -# assert conf2.od1.i2 == 4 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta -# del(meta2.od1.i2) -# assert conf1.od1.i2 == 2 -# assert conf2.od1.i2 == 1 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user -# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default -# del(conf1.od1.i2) -# assert conf1.od1.i2 == conf2.od1.i2 == 1 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default -# meta1.od1.i2 = 6 -# assert conf1.od1.i2 == conf2.od1.i2 == 6 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta +def test_meta_meta(): + meta1 = make_description() + meta2 = MetaConfig([meta1]) + meta2.cfgimpl_get_settings().setowner(owners.meta) + conf1, conf2 = meta1._impl_children + assert conf1.od1.i2 == conf2.od1.i2 == 1 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default + meta2.od1.i2 = 3 + assert conf1.od1.i2 == conf2.od1.i2 == 3 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta + meta2.od1.i2 = 3 + conf1.od1.i2 = 2 + assert conf1.od1.i2 == 2 + assert conf2.od1.i2 == 3 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta + meta2.od1.i2 = 4 + assert conf1.od1.i2 == 2 + assert conf2.od1.i2 == 4 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta + del(meta2.od1.i2) + assert conf1.od1.i2 == 2 + assert conf2.od1.i2 == 1 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user + assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default + del(conf1.od1.i2) + assert conf1.od1.i2 == conf2.od1.i2 == 1 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default + meta1.od1.i2 = 6 + assert conf1.od1.i2 == conf2.od1.i2 == 6 + assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta -#def test_meta_meta_set(): -# meta1 = make_description() -# meta2 = MetaConfig([meta1]) -# meta2.cfgimpl_get_settings().setowner(owners.meta) -# conf1, conf2 = meta1._impl_children -# meta2.set_contexts('od1.i1', 7) -# assert conf1.od1.i1 == conf2.od1.i1 == 7 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user -# assert [conf1, conf2] == meta2.find_first_contexts(byname='i1', byvalue=7) -# conf1.od1.i1 = 8 -# assert [conf2] == meta2.find_first_contexts(byname='i1', byvalue=7) -# assert [conf1] == meta2.find_first_contexts(byname='i1', byvalue=8) -# raises(AttributeError, "meta2.find_first_contexts(byname='i1', byvalue=10)") +def test_meta_meta_set(): + meta1 = make_description() + meta2 = MetaConfig([meta1]) + meta2.cfgimpl_get_settings().setowner(owners.meta) + conf1, conf2 = meta1._impl_children + meta2.set_contexts('od1.i1', 7) + assert conf1.od1.i1 == conf2.od1.i1 == 7 + assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user + assert [conf1, conf2] == meta2.find_first_contexts(byname='i1', byvalue=7) + conf1.od1.i1 = 8 + assert [conf2] == meta2.find_first_contexts(byname='i1', byvalue=7) + assert [conf1] == meta2.find_first_contexts(byname='i1', byvalue=8) + raises(AttributeError, "meta2.find_first_contexts(byname='i1', byvalue=10)") -#def test_not_meta(): -# i1 = IntOption('i1', '') -# od1 = OptionDescription('od1', '', [i1]) -# od2 = OptionDescription('od2', '', [od1]) -# conf1 = Config(od2) -# conf2 = Config(od2) -# meta = MetaConfig([conf1, conf2], False) -# raises(ConfigError, 'meta.od1.i1') -# conf1, conf2 = meta._impl_children -# meta.set_contexts('od1.i1', 7) -# assert conf1.od1.i1 == conf2.od1.i1 == 7 -# assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user +def test_not_meta(): + i1 = IntOption('i1', '') + od1 = OptionDescription('od1', '', [i1]) + od2 = OptionDescription('od2', '', [od1]) + conf1 = Config(od2) + conf2 = Config(od2) + meta = MetaConfig([conf1, conf2], False) + raises(ConfigError, 'meta.od1.i1') + conf1, conf2 = meta._impl_children + meta.set_contexts('od1.i1', 7) + assert conf1.od1.i1 == conf2.od1.i1 == 7 + assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user -#def test_meta_path(): -# meta = make_description() -# assert meta._impl_path is None -# assert meta.od1._impl_path == 'od1' +def test_meta_path(): + meta = make_description() + assert meta._impl_path is None + assert meta.od1._impl_path == 'od1' diff --git a/tiramisu/config.py b/tiramisu/config.py index fac3caf..591eb68 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -43,7 +43,7 @@ class SubConfig(object): :type subpath: `str` with the path name """ # main option description - if not isinstance(descr, OptionDescription): + if descr is not None and not isinstance(descr, OptionDescription): raise TypeError(_('descr must be an optiondescription, not {0}' ).format(type(descr))) self._impl_descr = descr @@ -552,99 +552,99 @@ class Config(CommonConfig): self.cfgimpl_get_settings().reset_cache(only_expired=only_expired) -#class MetaConfig(CommonConfig): -# __slots__ = ('_impl_children',) +class MetaConfig(CommonConfig): + __slots__ = ('_impl_children', '__weakref__') -# def __init__(self, children, meta=True, session_id=None, persistent=False): -# if not isinstance(children, list): -# raise ValueError(_("metaconfig's children must be a list")) -# self._impl_descr = None -# self._impl_path = None -# if meta: -# for child in children: -# if not isinstance(child, CommonConfig): -# raise TypeError(_("metaconfig's children " -# "must be config, not {0}" -# ).format(type(child))) -# if self._impl_descr is None: -# self._impl_descr = child.cfgimpl_get_description() -# elif not self._impl_descr is child.cfgimpl_get_description(): -# raise ValueError(_('all config in metaconfig must ' -# 'have the same optiondescription')) -# if child.cfgimpl_get_meta() is not None: -# raise ValueError(_("child has already a metaconfig's")) -# child._impl_meta = self + def __init__(self, children, meta=True, session_id=None, persistent=False): + if not isinstance(children, list): + raise ValueError(_("metaconfig's children must be a list")) + descr = None + if meta: + for child in children: + if not isinstance(child, CommonConfig): + raise TypeError(_("metaconfig's children " + "must be config, not {0}" + ).format(type(child))) + if descr is None: + descr = child.cfgimpl_get_description() + elif not descr is child.cfgimpl_get_description(): + raise ValueError(_('all config in metaconfig must ' + 'have the same optiondescription')) + if child.cfgimpl_get_meta() is not None: + raise ValueError(_("child has already a metaconfig's")) + child._impl_meta = self -# self._impl_children = children -# settings, values = get_storages(self, session_id, persistent) -# self._impl_settings = Settings(self, settings) -# self._impl_values = Values(self, values) -# self._impl_meta = None + self._impl_children = children + settings, values = get_storages(self, session_id, persistent) + self._impl_settings = Settings(self, settings) + self._impl_values = Values(self, values) + super(MetaConfig, self).__init__(descr, weakref.ref(self)) + self._impl_meta = None -# def cfgimpl_get_children(self): -# return self._impl_children + def cfgimpl_get_children(self): + return self._impl_children -# def cfgimpl_get_context(self): -# "a meta config is a config wich has a setting, that is itself" -# return self + def cfgimpl_get_context(self): + "a meta config is a config wich has a setting, that is itself" + return self -# def cfgimpl_reset_cache(self, -# only_expired=False, -# only=('values', 'settings')): -# if 'values' in only: -# self.cfgimpl_get_values().reset_cache(only_expired=only_expired) -# if 'settings' in only: -# self.cfgimpl_get_settings().reset_cache(only_expired=only_expired) -# for child in self._impl_children: -# child.cfgimpl_reset_cache(only_expired=only_expired, only=only) + def cfgimpl_reset_cache(self, + only_expired=False, + only=('values', 'settings')): + if 'values' in only: + self.cfgimpl_get_values().reset_cache(only_expired=only_expired) + if 'settings' in only: + self.cfgimpl_get_settings().reset_cache(only_expired=only_expired) + for child in self._impl_children: + child.cfgimpl_reset_cache(only_expired=only_expired, only=only) -# def set_contexts(self, path, value): -# for child in self._impl_children: -# try: -# if not isinstance(child, MetaConfig): -# setattr(child, path, value) -# else: -# child.set_contexts(path, value) -# except PropertiesOptionError: -# pass + def set_contexts(self, path, value): + for child in self._impl_children: + try: + if not isinstance(child, MetaConfig): + setattr(child, path, value) + else: + child.set_contexts(path, value) + except PropertiesOptionError: + pass -# def find_first_contexts(self, byname=None, bypath=None, byvalue=None, -# type_='path', display_error=True): -# ret = [] -# try: -# if bypath is None and byname is not None and \ -# self.cfgimpl_get_description() is not None: -# bypath = self._find(bytype=None, byvalue=None, byname=byname, -# first=True, type_='path', -# check_properties=False, -# display_error=display_error) -# except ConfigError: -# pass -# for child in self._impl_children: -# try: -# if not isinstance(child, MetaConfig): -# if bypath is not None: -# if byvalue is not None: -# if getattr(child, bypath) == byvalue: -# ret.append(child) -# else: -# #not raise -# getattr(child, bypath) -# ret.append(child) -# else: -# ret.append(child.find_first(byname=byname, -# byvalue=byvalue, -# type_=type_, -# display_error=False)) -# else: -# ret.extend(child.find_first_contexts(byname=byname, -# bypath=bypath, -# byvalue=byvalue, -# type_=type_, -# display_error=False)) -# except AttributeError: -# pass -# return self._find_return_results(ret, display_error) + def find_first_contexts(self, byname=None, bypath=None, byvalue=None, + type_='path', display_error=True): + ret = [] + try: + if bypath is None and byname is not None and \ + self.cfgimpl_get_description() is not None: + bypath = self._find(bytype=None, byvalue=None, byname=byname, + first=True, type_='path', + check_properties=False, + display_error=display_error) + except ConfigError: + pass + for child in self._impl_children: + try: + if not isinstance(child, MetaConfig): + if bypath is not None: + if byvalue is not None: + if getattr(child, bypath) == byvalue: + ret.append(child) + else: + #not raise + getattr(child, bypath) + ret.append(child) + else: + ret.append(child.find_first(byname=byname, + byvalue=byvalue, + type_=type_, + display_error=False)) + else: + ret.extend(child.find_first_contexts(byname=byname, + bypath=bypath, + byvalue=byvalue, + type_=type_, + display_error=False)) + except AttributeError: + pass + return self._find_return_results(ret, display_error) def mandatory_warnings(config): From feeb9842f5dd554913690b763aec0fd306fa8446 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 30 Sep 2013 16:22:08 +0200 Subject: [PATCH 02/61] serialize metaconfig/groupconfig --- test/test_dereference.py | 32 +++++++- test/test_metaconfig.py | 19 ++--- test/test_state.py | 111 ++++++++++++++++++++----- tiramisu/config.py | 170 ++++++++++++++++++++++----------------- 4 files changed, 227 insertions(+), 105 deletions(-) diff --git a/test/test_dereference.py b/test/test_dereference.py index be8dfde..358c1b9 100644 --- a/test/test_dereference.py +++ b/test/test_dereference.py @@ -2,8 +2,8 @@ import autopath #from py.test import raises -from tiramisu.config import Config -from tiramisu.option import BoolOption, OptionDescription +from tiramisu.config import Config, GroupConfig, MetaConfig +from tiramisu.option import BoolOption, IntOption, OptionDescription import weakref @@ -109,3 +109,31 @@ def test_deref_optiondescription_config(): assert w() is not None del(c) assert w() is None + + +def test_deref_groupconfig(): + i1 = IntOption('i1', '') + od1 = OptionDescription('od1', '', [i1]) + od2 = OptionDescription('od2', '', [od1]) + conf1 = Config(od2) + conf2 = Config(od2) + meta = GroupConfig([conf1, conf2]) + w = weakref.ref(conf1) + del(conf1) + assert w() is not None + del(meta) + assert w() is None + + +def test_deref_metaconfig(): + i1 = IntOption('i1', '') + od1 = OptionDescription('od1', '', [i1]) + od2 = OptionDescription('od2', '', [od1]) + conf1 = Config(od2) + conf2 = Config(od2) + meta = MetaConfig([conf1, conf2]) + w = weakref.ref(conf1) + del(conf1) + assert w() is not None + del(meta) + assert w() is None diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index 9214cb4..6986edc 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -3,7 +3,7 @@ import autopath from py.test import raises from tiramisu.setting import owners -from tiramisu.config import Config, MetaConfig +from tiramisu.config import Config, GroupConfig, MetaConfig from tiramisu.option import IntOption, OptionDescription from tiramisu.error import ConfigError @@ -26,6 +26,7 @@ def make_description(): #FIXME ne pas mettre 2 meta dans une config #FIXME ne pas mettre 2 OD differents dans un meta +#FIXME serialization def test_none(): meta = make_description() conf1, conf2 = meta._impl_children @@ -89,7 +90,7 @@ def test_contexts(): conf1, conf2 = meta._impl_children assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default - meta.set_contexts('od1.i2', 6) + meta.setattrs('od1.i2', 6) assert meta.od1.i2 == 1 assert conf1.od1.i2 == conf2.od1.i2 == 6 assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.user @@ -142,14 +143,14 @@ def test_meta_meta_set(): meta2 = MetaConfig([meta1]) meta2.cfgimpl_get_settings().setowner(owners.meta) conf1, conf2 = meta1._impl_children - meta2.set_contexts('od1.i1', 7) + meta2.setattrs('od1.i1', 7) assert conf1.od1.i1 == conf2.od1.i1 == 7 assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user - assert [conf1, conf2] == meta2.find_first_contexts(byname='i1', byvalue=7) + assert [conf1, conf2] == meta2.find_firsts(byname='i1', byvalue=7) conf1.od1.i1 = 8 - assert [conf2] == meta2.find_first_contexts(byname='i1', byvalue=7) - assert [conf1] == meta2.find_first_contexts(byname='i1', byvalue=8) - raises(AttributeError, "meta2.find_first_contexts(byname='i1', byvalue=10)") + assert [conf2] == meta2.find_firsts(byname='i1', byvalue=7) + assert [conf1] == meta2.find_firsts(byname='i1', byvalue=8) + raises(AttributeError, "meta2.find_firsts(byname='i1', byvalue=10)") def test_not_meta(): @@ -158,10 +159,10 @@ def test_not_meta(): od2 = OptionDescription('od2', '', [od1]) conf1 = Config(od2) conf2 = Config(od2) - meta = MetaConfig([conf1, conf2], False) + meta = GroupConfig([conf1, conf2]) raises(ConfigError, 'meta.od1.i1') conf1, conf2 = meta._impl_children - meta.set_contexts('od1.i1', 7) + meta.setattrs('od1.i1', 7) assert conf1.od1.i1 == conf2.od1.i1 == 7 assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user diff --git a/test/test_state.py b/test/test_state.py index ef46ce2..3188d3b 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -1,6 +1,6 @@ from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \ - OptionDescription -from tiramisu.config import Config + IntOption, OptionDescription +from tiramisu.config import Config, GroupConfig, MetaConfig from tiramisu.setting import owners from tiramisu.storage import delete_session from tiramisu.error import ConfigError @@ -90,6 +90,45 @@ def _diff_opt(opt1, opt2): assert val1 == val2 +def _diff_conf(cfg1, cfg2): + attr1 = set(_get_slots(cfg1)) + attr2 = set(_get_slots(cfg2)) + diff1 = attr1 - attr2 + diff2 = attr2 - attr1 + if diff1 != set(): + raise Exception('more attribute in cfg1 {0}'.format(list(diff1))) + if diff2 != set(): + raise Exception('more attribute in cfg2 {0}'.format(list(diff2))) + for attr in attr1: + if attr in ('_impl_context', '__weakref__'): + continue + err1 = False + err2 = False + val1 = None + val2 = None + try: + val1 = getattr(cfg1, attr) + except: + err1 = True + + try: + val2 = getattr(cfg2, attr) + except: + err2 = True + assert err1 == err2 + if val1 is None: + assert val1 == val2 + elif attr == '_impl_values': + assert cfg1.cfgimpl_get_values().get_modified_values() == cfg2.cfgimpl_get_values().get_modified_values() + elif attr == '_impl_settings': + assert cfg1.cfgimpl_get_settings().get_modified_properties() == cfg2.cfgimpl_get_settings().get_modified_properties() + assert cfg1.cfgimpl_get_settings().get_modified_permissives() == cfg2.cfgimpl_get_settings().get_modified_permissives() + elif attr == '_impl_descr': + _diff_opt(cfg1.cfgimpl_get_description(), cfg2.cfgimpl_get_description()) + else: + assert val1 == val2 + + def test_diff_opt(): b = BoolOption('b', '') u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}]) @@ -169,10 +208,7 @@ def test_state_config(): 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() + _diff_conf(cfg, q) try: delete_session('29090931') except ConfigError: @@ -191,12 +227,9 @@ def test_state_properties(): 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() + _diff_conf(cfg, q) try: - delete_session('29090931') + delete_session('29090932') except ConfigError: pass @@ -212,15 +245,12 @@ def test_state_values(): 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() + _diff_conf(cfg, q) q.val1 = False #assert cfg.val1 is True assert q.val1 is False try: - delete_session('29090931') + delete_session('29090933') except ConfigError: pass @@ -238,14 +268,53 @@ def test_state_values_owner(): 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() + _diff_conf(cfg, q) q.val1 = False nval1 = q.cfgimpl_get_description().val1 assert q.getowner(nval1) == owners.newowner try: - delete_session('29090931') + delete_session('29090934') + except ConfigError: + pass + + +def test_state_metaconfig(): + i1 = IntOption('i1', '') + od1 = OptionDescription('od1', '', [i1]) + od2 = OptionDescription('od2', '', [od1]) + conf1 = Config(od2, session_id='29090935') + conf1._impl_test = True + conf2 = Config(od2, session_id='29090936') + conf2._impl_test = True + meta = MetaConfig([conf1, conf2], session_id='29090937') + meta._impl_test = True + a = dumps(meta) + q = loads(a) + _diff_conf(meta, q) + try: + delete_session('29090935') + delete_session('29090936') + delete_session('29090937') + except ConfigError: + pass + + +def test_state_groupconfig(): + i1 = IntOption('i1', '') + od1 = OptionDescription('od1', '', [i1]) + od2 = OptionDescription('od2', '', [od1]) + conf1 = Config(od2, session_id='29090935') + conf1._impl_test = True + conf2 = Config(od2, session_id='29090936') + conf2._impl_test = True + meta = GroupConfig([conf1, conf2], session_id='29090937') + meta._impl_test = True + a = dumps(meta) + q = loads(a) + _diff_conf(meta, q) + try: + delete_session('29090935') + delete_session('29090936') + delete_session('29090937') except ConfigError: pass diff --git a/tiramisu/config.py b/tiramisu/config.py index 79527e8..d6399a2 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -161,7 +161,7 @@ class SubConfig(object): def cfgimpl_get_description(self): if self._impl_descr is None: raise ConfigError(_('no option description found for this config' - ' (may be metaconfig without meta)')) + ' (may be GroupConfig)')) else: return self._impl_descr @@ -467,9 +467,9 @@ class SubConfig(object): return context_descr.impl_get_path_by_opt(descr) -class CommonConfig(SubConfig): - "abstract base class for the Config and the MetaConfig" - __slots__ = ('_impl_values', '_impl_settings', '_impl_meta') +class _CommonConfig(SubConfig): + "abstract base class for the Config, GroupConfig and the MetaConfig" + __slots__ = ('_impl_values', '_impl_settings', '_impl_meta', '_impl_test') def _impl_build_all_paths(self): self.cfgimpl_get_description().impl_build_cache() @@ -508,7 +508,8 @@ class CommonConfig(SubConfig): return None def cfgimpl_get_meta(self): - return self._impl_meta + if self._impl_meta is not None: + return self._impl_meta() # information def impl_set_information(self, key, value): @@ -526,37 +527,12 @@ class CommonConfig(SubConfig): """ return self._impl_values.get_information(key, default) - -# ____________________________________________________________ -class Config(CommonConfig): - "main configuration management entry" - __slots__ = ('__weakref__', '_impl_test') - - def __init__(self, descr, session_id=None, persistent=False): - """ Configuration option management master class - - :param descr: describes the configuration schema - :type descr: an instance of ``option.OptionDescription`` - :param context: the current root config - :type context: `Config` - :param session_id: session ID is import with persistent Config to - retrieve good session - :type session_id: `str` - :param persistent: if persistent, don't delete storage when leaving - :type persistent: `boolean` - """ - settings, values = get_storages(self, session_id, persistent) - self._impl_settings = Settings(self, settings) - self._impl_values = Values(self, values) - 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 - + # ----- state def __getstate__(self): if self._impl_meta is not None: - raise ConfigError('cannot serialize Config with meta') + #FIXME _impl_meta est un weakref => faut pas sauvegarder mais faut bien savoir si c'est un méta ou pas au final ... + #en fait il faut ne pouvoir sérialisé que depuis une MetaConfig ... et pas directement comme pour les options + raise ConfigError('cannot serialize Config with MetaConfig') slots = set() for subclass in self.__class__.__mro__: if subclass is not object: @@ -589,6 +565,34 @@ class Config(CommonConfig): self._impl_values._impl_setstate(storage) self._impl_settings._impl_setstate(storage) + +# ____________________________________________________________ +class Config(_CommonConfig): + "main configuration management entry" + __slots__ = ('__weakref__',) + + def __init__(self, descr, session_id=None, persistent=False): + """ Configuration option management master class + + :param descr: describes the configuration schema + :type descr: an instance of ``option.OptionDescription`` + :param context: the current root config + :type context: `Config` + :param session_id: session ID is import with persistent Config to + retrieve good session + :type session_id: `str` + :param persistent: if persistent, don't delete storage when leaving + :type persistent: `boolean` + """ + settings, values = get_storages(self, session_id, persistent) + self._impl_settings = Settings(self, settings) + self._impl_values = Values(self, values) + 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 cfgimpl_reset_cache(self, only_expired=False, only=('values', 'settings')): @@ -598,42 +602,29 @@ class Config(CommonConfig): self.cfgimpl_get_settings().reset_cache(only_expired=only_expired) -class MetaConfig(CommonConfig): +class GroupConfig(_CommonConfig): __slots__ = ('_impl_children', '__weakref__') - def __init__(self, children, meta=True, session_id=None, persistent=False): + def __init__(self, children, session_id=None, persistent=False, + _descr=None): if not isinstance(children, list): raise ValueError(_("metaconfig's children must be a list")) - descr = None - if meta: - for child in children: - if not isinstance(child, CommonConfig): - raise TypeError(_("metaconfig's children " - "must be config, not {0}" - ).format(type(child))) - if descr is None: - descr = child.cfgimpl_get_description() - elif not descr is child.cfgimpl_get_description(): - raise ValueError(_('all config in metaconfig must ' - 'have the same optiondescription')) - if child.cfgimpl_get_meta() is not None: - raise ValueError(_("child has already a metaconfig's")) - child._impl_meta = self - self._impl_children = children settings, values = get_storages(self, session_id, persistent) self._impl_settings = Settings(self, settings) self._impl_values = Values(self, values) - super(MetaConfig, self).__init__(descr, weakref.ref(self)) + super(GroupConfig, self).__init__(_descr, weakref.ref(self)) self._impl_meta = None + #undocumented option used only in test script + self._impl_test = False def cfgimpl_get_children(self): return self._impl_children - def cfgimpl_get_context(self): - "a meta config is a config wich has a setting, that is itself" - return self - + #def cfgimpl_get_context(self): + # "a meta config is a config which has a setting, that is itself" + # return self + # def cfgimpl_reset_cache(self, only_expired=False, only=('values', 'settings')): @@ -644,38 +635,49 @@ class MetaConfig(CommonConfig): for child in self._impl_children: child.cfgimpl_reset_cache(only_expired=only_expired, only=only) - def set_contexts(self, path, value): + def setattrs(self, path, value): + """Setattr not in current GroupConfig, but in each children + """ for child in self._impl_children: try: - if not isinstance(child, MetaConfig): + if not isinstance(child, GroupConfig): setattr(child, path, value) else: - child.set_contexts(path, value) + child.setattrs(path, value) except PropertiesOptionError: pass - def find_first_contexts(self, byname=None, bypath=None, byvalue=None, - type_='path', display_error=True): + def find_firsts(self, byname=None, bypath=None, byvalue=None, + type_='path', display_error=True): + """Find first not in current GroupConfig, but in each children + """ ret = [] + #if MetaConfig, all children have same OptionDescription as context + #so search only one time for all children try: if bypath is None and byname is not None and \ - self.cfgimpl_get_description() is not None: + isinstance(self, MetaConfig): bypath = self._find(bytype=None, byvalue=None, byname=byname, first=True, type_='path', check_properties=False, display_error=display_error) - except ConfigError: + byname = None + except AttributeError: pass for child in self._impl_children: try: if not isinstance(child, MetaConfig): if bypath is not None: + #if byvalue is None, try if not raise + value = getattr(child, bypath) if byvalue is not None: - if getattr(child, bypath) == byvalue: - ret.append(child) + if isinstance(value, Multi): + if byvalue in value: + ret.append(child) + else: + if value == byvalue: + ret.append(child) else: - #not raise - getattr(child, bypath) ret.append(child) else: ret.append(child.find_first(byname=byname, @@ -683,16 +685,38 @@ class MetaConfig(CommonConfig): type_=type_, display_error=False)) else: - ret.extend(child.find_first_contexts(byname=byname, - bypath=bypath, - byvalue=byvalue, - type_=type_, - display_error=False)) + ret.extend(child.find_firsts(byname=byname, + bypath=bypath, + byvalue=byvalue, + type_=type_, + display_error=False)) except AttributeError: pass return self._find_return_results(ret, display_error) +class MetaConfig(GroupConfig): + __slots__ = tuple() + + def __init__(self, children, session_id=None, persistent=False): + descr = None + for child in children: + if not isinstance(child, _CommonConfig): + raise TypeError(_("metaconfig's children " + "should be config, not {0}" + ).format(type(child))) + if child.cfgimpl_get_meta() is not None: + raise ValueError(_("child has already a metaconfig's")) + if descr is None: + descr = child.cfgimpl_get_description() + elif not descr is child.cfgimpl_get_description(): + raise ValueError(_('all config in metaconfig must ' + 'have the same optiondescription')) + child._impl_meta = weakref.ref(self) + + super(MetaConfig, self).__init__(children, session_id, persistent, descr) + + def mandatory_warnings(config): """convenience function to trace Options that are mandatory and where no value has been set From 1a294e3d093b4dd51c4530807892bcda1732a0b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 30 Sep 2013 19:41:56 +0200 Subject: [PATCH 03/61] refactor DomainnameOption add options EmailOption and URLOption --- test/test_config_domain.py | 34 ++++++++++- tiramisu/option.py | 112 ++++++++++++++++++++++++++++++------- 2 files changed, 122 insertions(+), 24 deletions(-) diff --git a/test/test_config_domain.py b/test/test_config_domain.py index da04235..9c5c16f 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -2,7 +2,7 @@ import autopath from py.test import raises from tiramisu.config import Config -from tiramisu.option import DomainnameOption, OptionDescription +from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDescription def test_domainname(): @@ -14,11 +14,12 @@ def test_domainname(): c.d = 'toto.com' raises(ValueError, "c.d = 'toto'") c.d = 'toto3.com' - c.d = 'toto3.3la' + raises(ValueError, "c.d = 'toto3.3la'") raises(ValueError, "c.d = '3toto.com'") - c.d = 'toto.co3' + raises(ValueError, "c.d = 'toto.co3'") raises(ValueError, "c.d = 'toto_super.com'") c.d = 'toto-.com' + raises(ValueError, "c.d = 'toto..com'") def test_domainname_netbios(): @@ -41,3 +42,30 @@ def test_domainname_hostname(): raises(ValueError, "c.d = 'toto.com'") c.d = 'toto' c.d = 'domainnametoolong' + + +def test_email(): + e = EmailOption('e', '') + od = OptionDescription('a', '', [e]) + c = Config(od) + c.read_write() + c.e = 'root@foo.com' + raises(ValueError, "c.e = 'root'") + raises(ValueError, "c.e = 'root@domain'") + + +def test_url(): + u = URLOption('u', '') + od = OptionDescription('a', '', [u]) + c = Config(od) + c.read_write() + c.u = 'http://foo.com' + c.u = 'https://foo.com' + c.u = 'https://foo.com/' + raises(ValueError, "c.u = 'ftp://foo.com'") + c.u = 'https://foo.com/index.html' + c.u = 'https://foo.com/index.html?var=value&var2=val2' + raises(ValueError, "c.u = 'https://foo.com/index\\n.html'") + c.u = 'https://foo.com:8443' + c.u = 'https://foo.com:8443/' + c.u = 'https://foo.com:8443/index.html' diff --git a/tiramisu/option.py b/tiramisu/option.py index c7a28c2..dec7b02 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -968,20 +968,35 @@ class DomainnameOption(Option): domainname: fqdn: with tld, not supported yet """ - __slots__ = ('_type', '_allow_ip') + __slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re') _opt_type = 'domainname' def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, properties=None, allow_ip=False, type_='domainname', - warnings_only=False): + warnings_only=False, allow_without_dot=False): if type_ not in ['netbios', 'hostname', 'domainname']: raise ValueError(_('unknown type_ {0} for hostname').format(type_)) self._type = type_ if allow_ip not in [True, False]: raise ValueError(_('allow_ip must be a boolean')) + if allow_without_dot not in [True, False]: + raise ValueError(_('allow_without_dot must be a boolean')) self._allow_ip = allow_ip + self._allow_without_dot = allow_without_dot + end = '' + extrachar = '' + if self._type == 'netbios': + length = 14 + elif self._type == 'hostname': + length = 62 + elif self._type == 'domainname': + length = 62 + extrachar = '\.' + end = '+[a-z]*' + self._domain_re = re.compile(r'^(?:[a-z][a-z\d\-]{{,{0}}}{1}){2}$' + ''.format(length, extrachar, end)) super(DomainnameOption, self).__init__(name, doc, default=default, default_multi=default_multi, callback=callback, @@ -1000,27 +1015,82 @@ class DomainnameOption(Option): return except ValueError: pass - if self._type == 'netbios': - length = 15 - extrachar = '' - elif self._type == 'hostname': - length = 63 - extrachar = '' - elif self._type == 'domainname': - length = 255 - extrachar = '\.' - if '.' not in value: - raise ValueError(_("invalid value for {0}, must have dot" - "").format(self._name)) - if len(value) > length: - raise ValueError(_("invalid domainname's length for" - " {0} (max {1})").format(self._name, length)) - if len(value) == 1: + if self._type == 'domainname' and not self._allow_without_dot and \ + '.' not in value: + raise ValueError(_("invalid domainname for {0}, must have dot" + "").format(self._name)) + if len(value) > 255: + raise ValueError(_("invalid domainname's length for" + " {0} (max 255)").format(self._name)) + if len(value) < 2: raise ValueError(_("invalid domainname's length for {0} (min 2)" "").format(self._name)) - regexp = r'^[a-z]([a-z\d{0}-])*[a-z\d]$'.format(extrachar) - if re.match(regexp, value) is None: - raise ValueError(_('invalid domainname')) + if not self._domain_re.search(value): + raise ValueError(_('invalid domainname: {0}'.format(self._name))) + + +class EmailOption(DomainnameOption): + __slots__ = tuple() + username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$") + + def __init__(self, *args, **kwargs): + kwargs['type_'] = 'domainname' + kwargs['allow_ip'] = False + kwargs['allow_without_dot'] = False + super(EmailOption, self).__init__(*args, **kwargs) + + def _validate(self, value): + splitted = value.split('@', 1) + try: + username, domain = splitted + except ValueError: + raise ValueError(_('invalid email address, should contains one @ ' + 'for {0}').format(self._name)) + if not self.username_re.search(username): + raise ValueError(_('invalid username in email address for {0}').format(self._name)) + super(EmailOption, self)._validate(domain) + + +class URLOption(DomainnameOption): + __slots__ = tuple() + proto_re = re.compile(r'(http|https)://') + path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") + + def __init__(self, *args, **kwargs): + kwargs['type_'] = 'domainname' + kwargs['allow_ip'] = False + kwargs['allow_without_dot'] = False + super(URLOption, self).__init__(*args, **kwargs) + + def _validate(self, value): + match = self.proto_re.search(value) + if not match: + raise ValueError(_('invalid url, should start with http:// or ' + 'https:// for {0}').format(self._name)) + value = value[len(match.group(0)):] + # get domain/files + splitted = value.split('/', 1) + try: + domain, files = splitted + except ValueError: + domain = value + files = None + # if port in domain + splitted = domain.split(':', 1) + try: + domain, port = splitted + + except ValueError: + domain = splitted[0] + port = 0 + if not 0 <= int(port) <= 65535: + raise ValueError(_('port must be an between 0 and 65536')) + # validate domainname + super(URLOption, self)._validate(domain) + # validate file + if files is not None and files != '' and not self.path_re.search(files): + raise ValueError(_('invalid url, should endswith with filename for' + ' {0}').format(self._name)) class OptionDescription(BaseOption): From cce080cbd33a36e88347f20708616eccd9d00be3 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 30 Sep 2013 21:21:47 +0200 Subject: [PATCH 04/61] add FileOption --- test/test_config_api.py | 17 ++++++++++++++++- test/test_slots.py | 30 ++++++++++++++++++++++++++---- tiramisu/option.py | 13 +++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/test/test_config_api.py b/test/test_config_api.py index ab4b484..c0cc9a7 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -4,7 +4,7 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ - BoolOption, OptionDescription + BoolOption, FileOption, OptionDescription def make_description(): @@ -137,3 +137,18 @@ def test_does_not_find_in_config(): descr = make_description() conf = Config(descr) raises(AttributeError, "conf.find(byname='IDontExist')") + + +def test_file(): + a = FileOption('a', '') + o = OptionDescription('o', '', [a]) + c = Config(o) + c.a = u'/' + c.a = u'/tmp' + c.a = u'/tmp/' + c.a = u'/tmp/text.txt' + c.a = u'tmp' + c.a = u'tmp/' + c.a = u'tmp/text.txt' + raises(ValueError, "c.a = u'/tmp/with space.txt'") + raises(ValueError, "c.a = u'/tmp/with$.txt'") diff --git a/test/test_slots.py b/test/test_slots.py index 1f2aee6..1f65f6d 100644 --- a/test/test_slots.py +++ b/test/test_slots.py @@ -3,9 +3,10 @@ import autopath from py.test import raises from tiramisu.config import Config, SubConfig -from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ +from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption,\ StrOption, SymLinkOption, UnicodeOption, IPOption, OptionDescription, \ - PortOption, NetworkOption, NetmaskOption, DomainnameOption + PortOption, NetworkOption, NetmaskOption, DomainnameOption, EmailOption, \ + URLOption, FileOption def test_slots_option(): @@ -35,6 +36,12 @@ def test_slots_option(): raises(AttributeError, "c.x = 1") c = DomainnameOption('a', '') raises(AttributeError, "c.x = 1") + c = EmailOption('a', '') + raises(AttributeError, "c.x = 1") + c = URLOption('a', '') + raises(AttributeError, "c.x = 1") + c = FileOption('a', '') + raises(AttributeError, "c.x = 1") def test_slots_option_readonly(): @@ -49,7 +56,10 @@ def test_slots_option_readonly(): j = NetworkOption('j', '') k = NetmaskOption('k', '') l = DomainnameOption('l', '') - m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l]) + o = EmailOption('o', '') + p = URLOption('p', '') + q = FileOption('q', '') + m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l, o, p, q]) a._requires = 'a' b._requires = 'b' c._requires = 'c' @@ -62,6 +72,9 @@ def test_slots_option_readonly(): k._requires = 'k' l._requires = 'l' m._requires = 'm' + o._requires = 'o' + p._requires = 'p' + q._requires = 'q' Config(m) raises(AttributeError, "a._requires = 'a'") raises(AttributeError, "b._requires = 'b'") @@ -75,6 +88,9 @@ def test_slots_option_readonly(): raises(AttributeError, "k._requires = 'k'") raises(AttributeError, "l._requires = 'l'") raises(AttributeError, "m._requires = 'm'") + raises(AttributeError, "o._requires = 'o'") + raises(AttributeError, "p._requires = 'p'") + raises(AttributeError, "q._requires = 'q'") def test_slots_option_readonly_name(): @@ -90,7 +106,10 @@ def test_slots_option_readonly_name(): j = NetworkOption('j', '') k = NetmaskOption('k', '') l = DomainnameOption('l', '') - m = OptionDescription('m', '', [a, b, c, d, e, f, g, h, i, j, k, l]) + o = DomainnameOption('o', '') + p = DomainnameOption('p', '') + q = DomainnameOption('q', '') + m = OptionDescription('m', '', [a, b, c, d, e, f, g, h, i, j, k, l, o, p, q]) raises(AttributeError, "a._name = 'a'") raises(AttributeError, "b._name = 'b'") raises(AttributeError, "c._name = 'c'") @@ -104,6 +123,9 @@ def test_slots_option_readonly_name(): raises(AttributeError, "k._name = 'k'") raises(AttributeError, "l._name = 'l'") raises(AttributeError, "m._name = 'm'") + raises(AttributeError, "o._name = 'o'") + raises(AttributeError, "p._name = 'p'") + raises(AttributeError, "q._name = 'q'") def test_slots_description(): diff --git a/tiramisu/option.py b/tiramisu/option.py index dec7b02..2bb9328 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -1031,6 +1031,7 @@ class DomainnameOption(Option): class EmailOption(DomainnameOption): __slots__ = tuple() + _opt_type = 'email' username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$") def __init__(self, *args, **kwargs): @@ -1053,6 +1054,7 @@ class EmailOption(DomainnameOption): class URLOption(DomainnameOption): __slots__ = tuple() + _opt_type = 'url' proto_re = re.compile(r'(http|https)://') path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") @@ -1093,6 +1095,17 @@ class URLOption(DomainnameOption): ' {0}').format(self._name)) +class FileOption(Option): + __slots__ = tuple() + _opt_type = 'file' + path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$") + + def _validate(self, value): + match = self.path_re.search(value) + if not match: + raise ValueError(_('invalid filename for {0}').format(self._name)) + + class OptionDescription(BaseOption): """Config's schema (organisation, group) and container of Options The `OptionsDescription` objects lives in the `tiramisu.config.Config`. From c8a20eefb5645f7f7a4b7711e7993835c8f42286 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 30 Sep 2013 22:50:49 +0200 Subject: [PATCH 05/61] update invalid's message and display all informations when raises --- tiramisu/option.py | 75 ++++---- translations/fr/tiramisu.po | 346 ++++++++++++++++++++---------------- translations/tiramisu.pot | 300 +++++++++++++++++-------------- 3 files changed, 401 insertions(+), 320 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 2bb9328..125f27f 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -421,7 +421,11 @@ class Option(BaseOption): if _value is None: return # option validation - self._validate(_value) + try: + self._validate(_value) + except ValueError as err: + raise ValueError(_('invalid value for option {0}: {1}' + '').format(self._name, err.message)) try: # valid with self._validator val_validator(_value) @@ -430,8 +434,8 @@ class Option(BaseOption): 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) + msg = _("invalid value for option {0}: {1}").format( + self._name, err.message) if self._warnings_only: warnings.warn_explicit(ValueWarning(msg, self), ValueWarning, @@ -677,7 +681,7 @@ class BoolOption(Option): def _validate(self, value): if not isinstance(value, bool): - raise ValueError(_('value must be a boolean')) + raise ValueError(_('invalid boolean')) class IntOption(Option): @@ -687,7 +691,7 @@ class IntOption(Option): def _validate(self, value): if not isinstance(value, int): - raise ValueError(_('value must be an integer')) + raise ValueError(_('invalid integer')) class FloatOption(Option): @@ -697,7 +701,7 @@ class FloatOption(Option): def _validate(self, value): if not isinstance(value, float): - raise ValueError(_('value must be a float')) + raise ValueError(_('invalid float')) class StrOption(Option): @@ -707,8 +711,7 @@ class StrOption(Option): def _validate(self, value): if not isinstance(value, str): - raise ValueError(_('value must be a string, not ' - '{0}').format(type(value))) + raise ValueError(_('invalid string')) if sys.version_info[0] >= 3: @@ -725,7 +728,7 @@ else: def _validate(self, value): if not isinstance(value, unicode): - raise ValueError(_('value must be an unicode')) + raise ValueError(_('invalid unicode')) class SymLinkOption(BaseOption): @@ -786,14 +789,14 @@ class IPOption(Option): try: IP('{0}/32'.format(value)) except ValueError: - raise ValueError(_('invalid IP {0}').format(self._name)) + raise ValueError(_('invalid IP')) 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")) + raise ValueError(_("invalid IP, mustn't not be in reserved class")) if self._private_only and not ip.iptype() == 'PRIVATE': - raise ValueError(_("IP must be in private class")) + raise ValueError(_("invalid IP, must be in private class")) class PortOption(Option): @@ -853,16 +856,17 @@ class PortOption(Option): if self._allow_range and ":" in str(value): value = str(value).split(':') if len(value) != 2: - raise ValueError('range must have two values only') + raise ValueError('invalid part, range must have two values ' + 'only') if not value[0] < value[1]: - raise ValueError('first port in range must be' + raise ValueError('invalid port, first port in range must be' ' smaller than the second one') else: value = [value] for val in value: if not self._min_value <= int(val) <= self._max_value: - raise ValueError('port must be an between {0} and {1}' + raise ValueError('invalid port, must be an between {0} and {1}' ''.format(self._min_value, self._max_value)) @@ -875,12 +879,12 @@ class NetworkOption(Option): try: IP(value) except ValueError: - raise ValueError(_('invalid network address {0}').format(self._name)) + raise ValueError(_('invalid network address')) def _second_level_validation(self, value): ip = IP(value) if ip.iptype() == 'RESERVED': - raise ValueError(_("network shall not be in reserved class")) + raise ValueError(_("invalid network address, must not be in reserved class")) class NetmaskOption(Option): @@ -892,7 +896,7 @@ class NetmaskOption(Option): try: IP('0.0.0.0/{0}'.format(value)) except ValueError: - raise ValueError(_('invalid netmask address {0}').format(self._name)) + raise ValueError(_('invalid netmask address')) def _cons_network_netmask(self, opts, vals): #opts must be (netmask, network) options @@ -930,12 +934,12 @@ class NetmaskOption(Option): except ValueError: if make_net: - msg = _("invalid IP {0} ({1}) with netmask {2} ({3})") + msg = _('invalid IP {0} ({1}) with netmask {2}') else: - msg = _("invalid network {0} ({1}) with netmask {2} ({3})") + msg = _('invalid network {0} ({1}) with netmask {2}') if msg is not None: raise ValueError(msg.format(val_ipnetwork, opts[1]._name, - val_netmask, self._name)) + val_netmask)) class BroadcastOption(Option): @@ -946,7 +950,7 @@ class BroadcastOption(Option): try: IP('{0}/32'.format(value)) except ValueError: - raise ValueError(_('invalid broadcast address {0}').format(self._name)) + raise ValueError(_('invalid broadcast address')) def _cons_broadcast(self, opts, vals): if len(vals) != 3: @@ -1017,16 +1021,13 @@ class DomainnameOption(Option): pass if self._type == 'domainname' and not self._allow_without_dot and \ '.' not in value: - raise ValueError(_("invalid domainname for {0}, must have dot" - "").format(self._name)) + raise ValueError(_("invalid domainname, must have dot")) if len(value) > 255: - raise ValueError(_("invalid domainname's length for" - " {0} (max 255)").format(self._name)) + raise ValueError(_("invalid domainname's length (max 255)")) if len(value) < 2: - raise ValueError(_("invalid domainname's length for {0} (min 2)" - "").format(self._name)) + raise ValueError(_("invalid domainname's length (min 2)")) if not self._domain_re.search(value): - raise ValueError(_('invalid domainname: {0}'.format(self._name))) + raise ValueError(_('invalid domainname')) class EmailOption(DomainnameOption): @@ -1045,10 +1046,10 @@ class EmailOption(DomainnameOption): try: username, domain = splitted except ValueError: - raise ValueError(_('invalid email address, should contains one @ ' - 'for {0}').format(self._name)) + raise ValueError(_('invalid email address, should contains one @' + )) if not self.username_re.search(username): - raise ValueError(_('invalid username in email address for {0}').format(self._name)) + raise ValueError(_('invalid username in email address')) super(EmailOption, self)._validate(domain) @@ -1068,7 +1069,7 @@ class URLOption(DomainnameOption): match = self.proto_re.search(value) if not match: raise ValueError(_('invalid url, should start with http:// or ' - 'https:// for {0}').format(self._name)) + 'https://')) value = value[len(match.group(0)):] # get domain/files splitted = value.split('/', 1) @@ -1086,13 +1087,13 @@ class URLOption(DomainnameOption): domain = splitted[0] port = 0 if not 0 <= int(port) <= 65535: - raise ValueError(_('port must be an between 0 and 65536')) + raise ValueError(_('invalid url, port must be an between 0 and ' + '65536')) # validate domainname super(URLOption, self)._validate(domain) # validate file if files is not None and files != '' and not self.path_re.search(files): - raise ValueError(_('invalid url, should endswith with filename for' - ' {0}').format(self._name)) + raise ValueError(_('invalid url, should ends with filename')) class FileOption(Option): @@ -1103,7 +1104,7 @@ class FileOption(Option): def _validate(self, value): match = self.path_re.search(value) if not match: - raise ValueError(_('invalid filename for {0}').format(self._name)) + raise ValueError(_('invalid filename')) class OptionDescription(BaseOption): diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 256a0da..ec9e66d 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-28 19:06+CEST\n" +"POT-Creation-Date: 2013-09-30 22:49+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:144 +#: 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:153 +#: tiramisu/autolib.py:154 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:51 +#: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "descr doit être une optiondescription pas un {0}" -#: tiramisu/config.py:126 +#: tiramisu/config.py:127 msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:162 +#: 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)" -#: tiramisu/config.py:188 +#: tiramisu/config.py:189 msgid "can't assign to an OptionDescription" msgstr "ne peut pas attribuer une valeur à une OptionDescription" -#: tiramisu/config.py:319 +#: tiramisu/config.py:320 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:358 +#: 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:408 +#: 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:429 +#: tiramisu/config.py:430 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:489 +#: tiramisu/config.py:490 msgid "opt in getowner must be an option not {0}" msgstr "opt dans getowner doit être une option pas {0}" -#: tiramisu/option.py:68 +#: tiramisu/option.py:69 msgid "invalid name: {0} for option" msgstr "nom invalide : {0} pour l'option" -#: tiramisu/option.py:77 +#: tiramisu/option.py:78 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:115 +#: tiramisu/option.py:116 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:142 tiramisu/value.py:360 +#: tiramisu/option.py:143 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:204 +#: tiramisu/option.py:205 msgid "cannot serialize Option, only in OptionDescription" msgstr "ne peut serialiser une Option, seulement via une OptionDescription" -#: tiramisu/option.py:307 +#: tiramisu/option.py:308 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:313 +#: tiramisu/option.py:314 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:318 +#: tiramisu/option.py:319 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:321 +#: tiramisu/option.py:322 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" @@ -107,221 +107,250 @@ msgstr "" "params définis pour une fonction callback mais par de callback encore " "définis pour l'option {0}" -#: tiramisu/option.py:360 +#: tiramisu/option.py:361 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:427 tiramisu/option.py:437 +msgid "invalid value for option {0}: {1}" +msgstr "valeur invalide pour l'option {0} : {1}" -#: tiramisu/option.py:449 +#: tiramisu/option.py:454 msgid "which must be a list" msgstr "lequel doit être une liste" -#: tiramisu/option.py:509 +#: tiramisu/option.py:514 msgid "consistency should be set with an option" msgstr "consistency doit être configuré avec une option" -#: tiramisu/option.py:511 +#: tiramisu/option.py:516 msgid "cannot add consistency with itself" msgstr "ne peut ajouter une consistency avec lui même" -#: tiramisu/option.py:513 +#: tiramisu/option.py:518 msgid "every options in consistency should be multi or none" msgstr "" "toutes les options d'une consistency devrait être multi ou ne pas l'être" -#: tiramisu/option.py:533 +#: tiramisu/option.py:538 msgid "same value for {0} and {1}" msgstr "même valeur pour {0} et {1}" -#: tiramisu/option.py:642 +#: tiramisu/option.py:647 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:645 +#: tiramisu/option.py:650 msgid "open_values must be a boolean for {0}" msgstr "open_values doit être un booléen pour {0}" -#: tiramisu/option.py:667 +#: tiramisu/option.py:672 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:679 -msgid "value must be a boolean" -msgstr "valeur doit être un booléen" +#: tiramisu/option.py:684 +msgid "invalid boolean" +msgstr "booléen invalide" -#: tiramisu/option.py:689 -msgid "value must be an integer" -msgstr "valeur doit être un nombre entier" +#: tiramisu/option.py:694 +msgid "invalid integer" +msgstr "nombre invalide" -#: tiramisu/option.py:699 -msgid "value must be a float" -msgstr "valeur doit être un nombre flottant" +#: tiramisu/option.py:704 +msgid "invalid float" +msgstr "invalide nombre flottan" -#: tiramisu/option.py:709 -msgid "value must be a string, not {0}" -msgstr "valeur doit être une chaîne, pas {0}" +#: tiramisu/option.py:714 +msgid "invalid string" +msgstr "invalide caractère" -#: tiramisu/option.py:727 -msgid "value must be an unicode" -msgstr "valeur doit être une valeur unicode" +#: tiramisu/option.py:731 +msgid "invalid unicode" +msgstr "invalide unicode" -#: tiramisu/option.py:739 +#: tiramisu/option.py:743 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" -#: tiramisu/option.py:788 -msgid "invalid IP {0}" -msgstr "adresse IP invalide {0}" +#: tiramisu/option.py:792 +msgid "invalid IP" +msgstr "adresse IP invalide" -#: 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:797 +msgid "invalid IP, mustn't not be in reserved class" +msgstr "adresse IP invalide, ne doit pas être d'une classe reservée" -#: tiramisu/option.py:795 -msgid "IP must be in private class" -msgstr "IP doit être dans la classe privée" +#: tiramisu/option.py:799 +msgid "invalid IP, must be in private class" +msgstr "adresse IP invalide, doit être dans la classe privée" -#: tiramisu/option.py:833 +#: tiramisu/option.py:837 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:838 +#: tiramisu/option.py:842 msgid "max value is empty" msgstr "la valeur maximum est vide" -#: 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" +msgid "invalid network address" +msgstr "adresse réseau invalide" -#: tiramisu/option.py:894 -msgid "invalid netmask address {0}" -msgstr "masque de sous-réseau invalide {0}" +#: tiramisu/option.py:887 +msgid "invalid network address, must not be in reserved class" +msgstr "adresse réseau invalide, ne doit pas être dans la classe reservée" -#: tiramisu/option.py:910 +#: tiramisu/option.py:899 +msgid "invalid netmask address" +msgstr "masque de sous-réseau invalide" + +#: tiramisu/option.py:915 msgid "invalid len for opts" msgstr "longueur invalide pour opts" -#: tiramisu/option.py:922 +#: tiramisu/option.py:927 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:927 +#: tiramisu/option.py:932 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:932 -msgid "invalid IP {0} ({1}) with netmask {2} ({3})" -msgstr "IP invalide {0} ({1}) avec masque {2} ({3})" +#: tiramisu/option.py:937 +msgid "invalid IP {0} ({1}) with netmask {2}" +msgstr "IP invalide {0} ({1}) avec masque {2}" -#: 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 +msgid "invalid network {0} ({1}) with netmask {2}" +msgstr "réseau invalide {0} ({1}) avec masque {2}" -#: tiramisu/option.py:948 -msgid "invalid broadcast address {0}" -msgstr "adresse de broadcast invalide {0}" +#: tiramisu/option.py:953 +msgid "invalid broadcast address" +msgstr "adresse de broadcast invalide" -#: tiramisu/option.py:952 +#: tiramisu/option.py:957 msgid "invalid len for vals" msgstr "longueur invalide pour vals" -#: tiramisu/option.py:957 +#: tiramisu/option.py:962 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 +#: tiramisu/option.py:984 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:982 +#: tiramisu/option.py:987 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:1012 -msgid "invalid value for {0}, must have dot" -msgstr "valeur invalide pour {0}, doit avoir un point" +#: tiramisu/option.py:989 +msgid "allow_without_dot must be a boolean" +msgstr "allow_without_dot doit être un booléen" -#: 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:1024 +msgid "invalid domainname, must have dot" +msgstr "nom de domaine invalide, doit avoir un point" -#: 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:1026 +msgid "invalid domainname's length (max 255)" +msgstr "longueur du nom de domaine invalide (maximum {1})" -#: tiramisu/option.py:1022 +#: tiramisu/option.py:1028 +msgid "invalid domainname's length (min 2)" +msgstr "longueur du nom de domaine invalide (minimum 2)" + +#: tiramisu/option.py:1030 msgid "invalid domainname" msgstr "nom de domaine invalide" #: tiramisu/option.py:1049 +msgid "invalid email address, should contains one @" +msgstr "adresse email invalide, devrait contenir un @" + +#: tiramisu/option.py:1052 +msgid "invalid username in email address" +msgstr "nom d'utilisateur invalide dans une adresse email" + +#: tiramisu/option.py:1071 +msgid "invalid url, should start with http:// or https://" +msgstr "URL invalide, devrait démarré avec http:// ou https://" + +#: tiramisu/option.py:1090 +msgid "invalid url, port must be an between 0 and 65536" +msgstr "URL invalide, port doit être entre 0 et 65536" + +#: tiramisu/option.py:1096 +#, fuzzy +msgid "invalid url, should ends with filename" +msgstr "URL invalide, devrait finir avec un nom de fichier" + +#: tiramisu/option.py:1107 +msgid "invalid filename" +msgstr "nom de fichier invalide" + +#: tiramisu/option.py:1134 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:1067 +#: tiramisu/option.py:1152 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:1118 +#: tiramisu/option.py:1203 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:1148 +#: tiramisu/option.py:1233 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 +#: tiramisu/option.py:1241 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:1162 +#: tiramisu/option.py:1247 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:1172 +#: tiramisu/option.py:1257 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:1185 +#: tiramisu/option.py:1270 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:1188 +#: tiramisu/option.py:1273 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:1191 +#: tiramisu/option.py:1276 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:1202 +#: tiramisu/option.py:1287 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:1211 +#: tiramisu/option.py:1296 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:1214 +#: tiramisu/option.py:1299 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:1306 +#: tiramisu/option.py:1391 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:1323 +#: tiramisu/option.py:1408 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -329,110 +358,110 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, expected et action" -#: tiramisu/option.py:1328 +#: tiramisu/option.py:1413 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:1332 +#: tiramisu/option.py:1417 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:1336 +#: tiramisu/option.py:1421 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:1340 +#: tiramisu/option.py:1425 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:1343 +#: tiramisu/option.py:1428 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:1349 +#: tiramisu/option.py:1434 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:1354 +#: tiramisu/option.py:1439 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:1379 +#: tiramisu/option.py:1464 msgid "{0} should be a function" msgstr "{0} doit être une fonction" -#: tiramisu/option.py:1382 +#: tiramisu/option.py:1467 msgid "{0}_params should be a dict" msgstr "{0}_params devrait être un dict" -#: tiramisu/option.py:1385 +#: tiramisu/option.py:1470 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:1389 +#: tiramisu/option.py:1474 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" -#: tiramisu/option.py:1395 +#: tiramisu/option.py:1480 msgid "validator not support tuple" msgstr "validator n'accepte pas de tuple" -#: tiramisu/option.py:1398 +#: tiramisu/option.py:1483 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:1402 +#: tiramisu/option.py:1487 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:111 +#: tiramisu/setting.py:116 msgid "can't rebind {0}" msgstr "ne peut redéfinir ({0})" -#: tiramisu/setting.py:116 +#: tiramisu/setting.py:121 msgid "can't unbind {0}" msgstr "ne peut supprimer ({0})" -#: tiramisu/setting.py:254 +#: 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:317 +#: 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:332 +#: 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:435 +#: tiramisu/setting.py:440 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:441 +#: 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:459 +#: tiramisu/setting.py:464 msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:466 tiramisu/value.py:299 +#: tiramisu/setting.py:471 tiramisu/value.py:301 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" -#: tiramisu/setting.py:553 +#: tiramisu/setting.py:558 msgid "" "malformed requirements imbrication detected for option: '{0}' with " "requirement on: '{1}'" @@ -440,73 +469,92 @@ msgstr "" "imbrication de requirements mal formés detectée pour l'option : '{0}' avec " "requirement sur : '{1}'" -#: tiramisu/setting.py:565 +#: 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/__init__.py:47 +#: 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:81 +#: 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:37 +#: 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:48 +#: tiramisu/storage/dictionary/storage.py:50 msgid "session already used" msgstr "session déjà utilisée" -#: tiramisu/storage/dictionary/storage.py:50 +#: 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:306 +#: 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:505 +#: 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:509 +#: 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:518 +#: 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:526 +#: 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:534 +#: 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:562 +#: tiramisu/value.py:547 +msgid "invalid value {0} for option {1}: {2}" +msgstr "valeur invalide {0} pour l'option {1} : {2}" + +#: 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 "value must be a boolean" +#~ msgstr "valeur doit être un booléen" + +#~ msgid "value must be an integer" +#~ msgstr "valeur doit être un nombre entier" + +#~ msgid "value must be a float" +#~ msgstr "valeur doit être un nombre flottant" + +#~ msgid "value must be a string, not {0}" +#~ msgstr "valeur doit être une chaîne, pas {0}" + +#~ msgid "value must be an unicode" +#~ msgstr "valeur doit être une valeur unicode" + #~ 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" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 57b90e8..89808a9 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-28 19:06+CEST\n" +"POT-Creation-Date: 2013-09-30 22:49+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,455 +15,487 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/autolib.py:144 +#: tiramisu/autolib.py:145 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" -#: tiramisu/autolib.py:153 +#: 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:51 +#: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "" -#: tiramisu/config.py:126 +#: tiramisu/config.py:127 msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:162 +#: tiramisu/config.py:163 msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:188 +#: tiramisu/config.py:189 msgid "can't assign to an OptionDescription" msgstr "" -#: tiramisu/config.py:319 +#: tiramisu/config.py:320 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:358 +#: tiramisu/config.py:359 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:408 +#: tiramisu/config.py:409 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:429 +#: tiramisu/config.py:430 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:489 +#: tiramisu/config.py:490 msgid "opt in getowner must be an option not {0}" msgstr "" -#: tiramisu/option.py:68 +#: tiramisu/option.py:69 msgid "invalid name: {0} for option" msgstr "" -#: tiramisu/option.py:77 +#: tiramisu/option.py:78 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "" -#: tiramisu/option.py:115 +#: tiramisu/option.py:116 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option.py:142 tiramisu/value.py:360 +#: tiramisu/option.py:143 tiramisu/value.py:362 msgid "information's item not found: {0}" msgstr "" -#: tiramisu/option.py:204 +#: tiramisu/option.py:205 msgid "cannot serialize Option, only in OptionDescription" msgstr "" -#: tiramisu/option.py:307 +#: tiramisu/option.py:308 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -#: tiramisu/option.py:313 +#: tiramisu/option.py:314 msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:318 +#: tiramisu/option.py:319 msgid "default value not allowed if option: {0} is calculated" msgstr "" -#: tiramisu/option.py:321 +#: tiramisu/option.py:322 msgid "params defined for a callback function but no callback defined yet for option {0}" msgstr "" -#: tiramisu/option.py:360 +#: tiramisu/option.py:361 msgid "option not in all_cons_opts" msgstr "" -#: tiramisu/option.py:432 tiramisu/value.py:545 -msgid "invalid value {0} for option {1}: {2}" +#: tiramisu/option.py:427 tiramisu/option.py:437 +msgid "invalid value for option {0}: {1}" msgstr "" -#: tiramisu/option.py:449 +#: tiramisu/option.py:454 msgid "which must be a list" msgstr "" -#: tiramisu/option.py:509 +#: tiramisu/option.py:514 msgid "consistency should be set with an option" msgstr "" -#: tiramisu/option.py:511 +#: tiramisu/option.py:516 msgid "cannot add consistency with itself" msgstr "" -#: tiramisu/option.py:513 +#: tiramisu/option.py:518 msgid "every options in consistency should be multi or none" msgstr "" -#: tiramisu/option.py:533 +#: tiramisu/option.py:538 msgid "same value for {0} and {1}" msgstr "" -#: tiramisu/option.py:642 +#: tiramisu/option.py:647 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:645 +#: tiramisu/option.py:650 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:667 +#: tiramisu/option.py:672 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:679 -msgid "value must be a boolean" +#: tiramisu/option.py:684 +msgid "invalid boolean" msgstr "" -#: tiramisu/option.py:689 -msgid "value must be an integer" +#: tiramisu/option.py:694 +msgid "invalid integer" msgstr "" -#: tiramisu/option.py:699 -msgid "value must be a float" +#: tiramisu/option.py:704 +msgid "invalid float" msgstr "" -#: tiramisu/option.py:709 -msgid "value must be a string, not {0}" +#: tiramisu/option.py:714 +msgid "invalid string" msgstr "" -#: tiramisu/option.py:727 -msgid "value must be an unicode" +#: tiramisu/option.py:731 +msgid "invalid unicode" msgstr "" -#: tiramisu/option.py:739 +#: tiramisu/option.py:743 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option.py:788 -msgid "invalid IP {0}" +#: tiramisu/option.py:792 +msgid "invalid IP" msgstr "" -#: tiramisu/option.py:793 -msgid "IP mustn't not be in reserved class" +#: tiramisu/option.py:797 +msgid "invalid IP, mustn't not be in reserved class" msgstr "" -#: tiramisu/option.py:795 -msgid "IP must be in private class" +#: tiramisu/option.py:799 +msgid "invalid IP, must be in private class" msgstr "" -#: tiramisu/option.py:833 +#: tiramisu/option.py:837 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option.py:838 +#: tiramisu/option.py:842 msgid "max value is empty" msgstr "" -#: tiramisu/option.py:877 -msgid "invalid network address {0}" -msgstr "" - #: tiramisu/option.py:882 -msgid "network shall not be in reserved class" +msgid "invalid network address" msgstr "" -#: tiramisu/option.py:894 -msgid "invalid netmask address {0}" +#: tiramisu/option.py:887 +msgid "invalid network address, must not be in reserved class" msgstr "" -#: tiramisu/option.py:910 +#: tiramisu/option.py:899 +msgid "invalid netmask address" +msgstr "" + +#: tiramisu/option.py:915 msgid "invalid len for opts" msgstr "" -#: tiramisu/option.py:922 +#: tiramisu/option.py:927 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "" -#: tiramisu/option.py:927 +#: tiramisu/option.py:932 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "" -#: tiramisu/option.py:932 -msgid "invalid IP {0} ({1}) with netmask {2} ({3})" +#: tiramisu/option.py:937 +msgid "invalid IP {0} ({1}) with netmask {2}" msgstr "" -#: tiramisu/option.py:934 -msgid "invalid network {0} ({1}) with netmask {2} ({3})" +#: tiramisu/option.py:939 +msgid "invalid network {0} ({1}) with netmask {2}" msgstr "" -#: tiramisu/option.py:948 -msgid "invalid broadcast address {0}" -msgstr "" - -#: tiramisu/option.py:952 -msgid "invalid len for vals" +#: tiramisu/option.py:953 +msgid "invalid broadcast address" msgstr "" #: tiramisu/option.py:957 +msgid "invalid len for vals" +msgstr "" + +#: tiramisu/option.py:962 msgid "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" msgstr "" -#: tiramisu/option.py:979 +#: tiramisu/option.py:984 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:982 +#: tiramisu/option.py:987 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:1012 -msgid "invalid value for {0}, must have dot" +#: tiramisu/option.py:989 +msgid "allow_without_dot must be a boolean" msgstr "" -#: tiramisu/option.py:1015 -msgid "invalid domainname's length for {0} (max {1})" +#: tiramisu/option.py:1024 +msgid "invalid domainname, must have dot" msgstr "" -#: tiramisu/option.py:1018 -msgid "invalid domainname's length for {0} (min 2)" +#: tiramisu/option.py:1026 +msgid "invalid domainname's length (max 255)" msgstr "" -#: tiramisu/option.py:1022 +#: tiramisu/option.py:1028 +msgid "invalid domainname's length (min 2)" +msgstr "" + +#: tiramisu/option.py:1030 msgid "invalid domainname" msgstr "" #: tiramisu/option.py:1049 +msgid "invalid email address, should contains one @" +msgstr "" + +#: tiramisu/option.py:1052 +msgid "invalid username in email address" +msgstr "" + +#: tiramisu/option.py:1071 +msgid "invalid url, should start with http:// or https://" +msgstr "" + +#: tiramisu/option.py:1090 +msgid "invalid url, port must be an between 0 and 65536" +msgstr "" + +#: tiramisu/option.py:1096 +msgid "invalid url, should ends with filename" +msgstr "" + +#: tiramisu/option.py:1107 +msgid "invalid filename" +msgstr "" + +#: tiramisu/option.py:1134 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:1067 +#: tiramisu/option.py:1152 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:1118 +#: tiramisu/option.py:1203 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:1148 +#: tiramisu/option.py:1233 msgid "consistency with option {0} which is not in Config" msgstr "" -#: tiramisu/option.py:1156 +#: tiramisu/option.py:1241 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:1162 +#: tiramisu/option.py:1247 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:1172 +#: tiramisu/option.py:1257 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:1185 +#: tiramisu/option.py:1270 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:1188 +#: tiramisu/option.py:1273 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:1191 +#: tiramisu/option.py:1276 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:1202 +#: tiramisu/option.py:1287 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:1211 +#: tiramisu/option.py:1296 msgid "no child has same nom has master group for: {0}" msgstr "" -#: tiramisu/option.py:1214 +#: tiramisu/option.py:1299 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1306 +#: tiramisu/option.py:1391 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1323 +#: tiramisu/option.py:1408 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1328 +#: tiramisu/option.py:1413 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1332 +#: tiramisu/option.py:1417 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1336 +#: tiramisu/option.py:1421 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1340 +#: tiramisu/option.py:1425 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1343 +#: tiramisu/option.py:1428 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1349 +#: tiramisu/option.py:1434 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1354 +#: tiramisu/option.py:1439 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "" -#: tiramisu/option.py:1379 +#: tiramisu/option.py:1464 msgid "{0} should be a function" msgstr "" -#: tiramisu/option.py:1382 +#: tiramisu/option.py:1467 msgid "{0}_params should be a dict" msgstr "" -#: tiramisu/option.py:1385 +#: tiramisu/option.py:1470 msgid "{0}_params with key {1} should not have length different to 1" msgstr "" -#: tiramisu/option.py:1389 +#: tiramisu/option.py:1474 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "" -#: tiramisu/option.py:1395 +#: tiramisu/option.py:1480 msgid "validator not support tuple" msgstr "" -#: tiramisu/option.py:1398 +#: tiramisu/option.py:1483 msgid "{0}_params should have an option not a {0} for first argument" msgstr "" -#: tiramisu/option.py:1402 +#: tiramisu/option.py:1487 msgid "{0}_params should have a boolean not a {0} for second argument" msgstr "" -#: tiramisu/setting.py:111 +#: tiramisu/setting.py:116 msgid "can't rebind {0}" msgstr "" -#: tiramisu/setting.py:116 +#: tiramisu/setting.py:121 msgid "can't unbind {0}" msgstr "" -#: tiramisu/setting.py:254 +#: tiramisu/setting.py:259 msgid "cannot append {0} property for option {1}: this property is calculated" msgstr "" -#: tiramisu/setting.py:317 +#: tiramisu/setting.py:322 msgid "opt and all_properties must not be set together in reset" msgstr "" -#: tiramisu/setting.py:332 +#: tiramisu/setting.py:337 msgid "if opt is not None, path should not be None in _getproperties" msgstr "" -#: tiramisu/setting.py:435 +#: tiramisu/setting.py:440 msgid "cannot change the value for option {0} this option is frozen" msgstr "" -#: tiramisu/setting.py:441 +#: tiramisu/setting.py:446 msgid "trying to access to an option named: {0} with properties {1}" msgstr "" -#: tiramisu/setting.py:459 +#: tiramisu/setting.py:464 msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:466 tiramisu/value.py:299 +#: tiramisu/setting.py:471 tiramisu/value.py:301 msgid "invalid generic owner {0}" msgstr "" -#: tiramisu/setting.py:553 +#: tiramisu/setting.py:558 msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" msgstr "" -#: tiramisu/setting.py:565 +#: tiramisu/setting.py:570 msgid "option '{0}' has requirement's property error: {1} {2}" msgstr "" -#: tiramisu/storage/__init__.py:47 +#: tiramisu/storage/__init__.py:52 msgid "storage_type is already set, cannot rebind it" msgstr "" -#: tiramisu/storage/__init__.py:81 +#: tiramisu/storage/__init__.py:86 msgid "option {0} not already exists in storage {1}" msgstr "" -#: tiramisu/storage/dictionary/storage.py:37 +#: tiramisu/storage/dictionary/storage.py:39 msgid "dictionary storage cannot delete session" msgstr "" -#: tiramisu/storage/dictionary/storage.py:48 +#: tiramisu/storage/dictionary/storage.py:50 msgid "session already used" msgstr "" -#: tiramisu/storage/dictionary/storage.py:50 +#: tiramisu/storage/dictionary/storage.py:52 msgid "a dictionary cannot be persistent" msgstr "" -#: tiramisu/value.py:306 +#: 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:505 +#: tiramisu/value.py:507 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:509 +#: tiramisu/value.py:511 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:518 +#: tiramisu/value.py:520 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:526 +#: tiramisu/value.py:528 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:534 +#: tiramisu/value.py:536 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:562 +#: tiramisu/value.py:547 +msgid "invalid value {0} for option {1}: {2}" +msgstr "" + +#: tiramisu/value.py:564 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "" From 0a83a9261f8dbeaa1f041eebe783a4f72b03aa8e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 30 Sep 2013 22:56:08 +0200 Subject: [PATCH 06/61] update invalid's message and display all informations when raises --- test/test_option_validator.py | 18 +++++++++--------- tiramisu/option.py | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 001f9f7..9a39816 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -87,7 +87,7 @@ def test_validator_warning(): 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' + assert str(w[0].message) == 'invalid value for option opt2: error' # with warnings.catch_warnings(record=True) as w: cfg.opt3.append('val') @@ -97,7 +97,7 @@ def test_validator_warning(): 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' + assert str(w[0].message) == 'invalid value for option opt3: error' raises(ValueError, "cfg.opt2 = 1") # with warnings.catch_warnings(record=True) as w: @@ -105,9 +105,9 @@ def test_validator_warning(): 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 str(w[0].message) == 'invalid value for option opt2: error' assert w[1].message.opt == opt3 - assert str(w[1].message) == 'invalid value val1 for option opt3: error' + assert str(w[1].message) == 'invalid value for option opt3: error' def test_validator_warning_master_slave(): @@ -127,29 +127,29 @@ def test_validator_warning_master_slave(): 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' + assert str(w[0].message) == 'invalid value 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' + assert str(w[0].message) == 'invalid value 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' + assert str(w[0].message) == 'invalid value 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' + assert str(w[0].message) == 'invalid value 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' + assert str(w[0].message) == 'invalid value for option ip_admin_eth0: error' diff --git a/tiramisu/option.py b/tiramisu/option.py index 125f27f..7dfe3d5 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -925,11 +925,11 @@ class NetmaskOption(Option): except ValueError: if not make_net: msg = _("invalid network {0} ({1}) " - "with netmask {2} ({3})," + "with netmask {2}," " this network is an IP") else: if make_net: - msg = _("invalid IP {0} ({1}) with netmask {2} ({3})," + msg = _("invalid IP {0} ({1}) with netmask {2}," " this IP is a network") except ValueError: From 2dcdbb137e73d3cfe91cf143325bd12b93c5148e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 1 Oct 2013 08:19:10 +0200 Subject: [PATCH 07/61] rename FileOption to FilenameOption python 3 support --- test/test_config_api.py | 6 +++--- test/test_slots.py | 6 +++--- tiramisu/option.py | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/test_config_api.py b/test/test_config_api.py index c0cc9a7..f0681ea 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -4,7 +4,7 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ - BoolOption, FileOption, OptionDescription + BoolOption, FilenameOption, OptionDescription def make_description(): @@ -139,8 +139,8 @@ def test_does_not_find_in_config(): raises(AttributeError, "conf.find(byname='IDontExist')") -def test_file(): - a = FileOption('a', '') +def test_filename(): + a = FilenameOption('a', '') o = OptionDescription('o', '', [a]) c = Config(o) c.a = u'/' diff --git a/test/test_slots.py b/test/test_slots.py index 1f65f6d..2031cf8 100644 --- a/test/test_slots.py +++ b/test/test_slots.py @@ -6,7 +6,7 @@ from tiramisu.config import Config, SubConfig from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption,\ StrOption, SymLinkOption, UnicodeOption, IPOption, OptionDescription, \ PortOption, NetworkOption, NetmaskOption, DomainnameOption, EmailOption, \ - URLOption, FileOption + URLOption, FilenameOption def test_slots_option(): @@ -40,7 +40,7 @@ def test_slots_option(): raises(AttributeError, "c.x = 1") c = URLOption('a', '') raises(AttributeError, "c.x = 1") - c = FileOption('a', '') + c = FilenameOption('a', '') raises(AttributeError, "c.x = 1") @@ -58,7 +58,7 @@ def test_slots_option_readonly(): l = DomainnameOption('l', '') o = EmailOption('o', '') p = URLOption('p', '') - q = FileOption('q', '') + q = FilenameOption('q', '') m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l, o, p, q]) a._requires = 'a' b._requires = 'b' diff --git a/tiramisu/option.py b/tiramisu/option.py index 7dfe3d5..b894a74 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -425,7 +425,7 @@ class Option(BaseOption): self._validate(_value) except ValueError as err: raise ValueError(_('invalid value for option {0}: {1}' - '').format(self._name, err.message)) + '').format(self._name, err)) try: # valid with self._validator val_validator(_value) @@ -435,7 +435,7 @@ class Option(BaseOption): self._second_level_validation(_value) except ValueError as err: msg = _("invalid value for option {0}: {1}").format( - self._name, err.message) + self._name, err) if self._warnings_only: warnings.warn_explicit(ValueWarning(msg, self), ValueWarning, @@ -1096,7 +1096,7 @@ class URLOption(DomainnameOption): raise ValueError(_('invalid url, should ends with filename')) -class FileOption(Option): +class FilenameOption(Option): __slots__ = tuple() _opt_type = 'file' path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$") From 6902ad4f18b31604afe106a9126ef8fe7a94e5ba Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 1 Oct 2013 08:23:10 +0200 Subject: [PATCH 08/61] some extra tests for filename --- test/test_config_domain.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_config_domain.py b/test/test_config_domain.py index 9c5c16f..018e4c0 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -63,6 +63,9 @@ def test_url(): c.u = 'https://foo.com' c.u = 'https://foo.com/' raises(ValueError, "c.u = 'ftp://foo.com'") + raises(ValueError, "c.u = 'foo.com'") + raises(ValueError, "c.u = ':/foo.com'") + raises(ValueError, "c.u = 'foo.com/http://'") c.u = 'https://foo.com/index.html' c.u = 'https://foo.com/index.html?var=value&var2=val2' raises(ValueError, "c.u = 'https://foo.com/index\\n.html'") From eb3327cd7558148c39ec240e86ade7ebd0d955d1 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 1 Oct 2013 10:13:17 +0200 Subject: [PATCH 09/61] correction in allow_without_dot + test --- test/test_config_domain.py | 6 +++++- tiramisu/option.py | 10 +++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/test/test_config_domain.py b/test/test_config_domain.py index 018e4c0..f4579a6 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -8,7 +8,8 @@ from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDesc def test_domainname(): d = DomainnameOption('d', '') e = DomainnameOption('e', '', "toto.com") - od = OptionDescription('a', '', [d, e]) + f = DomainnameOption('f', '', allow_without_dot=True) + od = OptionDescription('a', '', [d, f]) c = Config(od) c.read_write() c.d = 'toto.com' @@ -20,6 +21,9 @@ def test_domainname(): raises(ValueError, "c.d = 'toto_super.com'") c.d = 'toto-.com' raises(ValueError, "c.d = 'toto..com'") + # + c.f = 'toto.com' + c.f = 'toto' def test_domainname_netbios(): diff --git a/tiramisu/option.py b/tiramisu/option.py index b894a74..0d0c69a 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -991,16 +991,20 @@ class DomainnameOption(Option): self._allow_without_dot = allow_without_dot end = '' extrachar = '' + extrachar_mandatory = '' if self._type == 'netbios': length = 14 elif self._type == 'hostname': length = 62 elif self._type == 'domainname': length = 62 - extrachar = '\.' + if allow_without_dot is False: + extrachar_mandatory = '\.' + else: + extrachar = '\.' end = '+[a-z]*' - self._domain_re = re.compile(r'^(?:[a-z][a-z\d\-]{{,{0}}}{1}){2}$' - ''.format(length, extrachar, end)) + self._domain_re = re.compile(r'^(?:[a-z][a-z\d\-{0}]{{,{1}}}{2}){3}$' + ''.format(extrachar, length, extrachar_mandatory, end)) super(DomainnameOption, self).__init__(name, doc, default=default, default_multi=default_multi, callback=callback, From 615cad4b4908cbb3a8bbbbec789c2d4213705421 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 1 Oct 2013 20:01:38 +0200 Subject: [PATCH 10/61] EmailOption and URLOption : let user choose attr for DomainnameOption --- tiramisu/option.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 0d0c69a..88da41a 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -1039,12 +1039,6 @@ class EmailOption(DomainnameOption): _opt_type = 'email' username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$") - def __init__(self, *args, **kwargs): - kwargs['type_'] = 'domainname' - kwargs['allow_ip'] = False - kwargs['allow_without_dot'] = False - super(EmailOption, self).__init__(*args, **kwargs) - def _validate(self, value): splitted = value.split('@', 1) try: @@ -1063,12 +1057,6 @@ class URLOption(DomainnameOption): proto_re = re.compile(r'(http|https)://') path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") - def __init__(self, *args, **kwargs): - kwargs['type_'] = 'domainname' - kwargs['allow_ip'] = False - kwargs['allow_without_dot'] = False - super(URLOption, self).__init__(*args, **kwargs) - def _validate(self, value): match = self.proto_re.search(value) if not match: From b606d23801f04d87c7e6eb3293579b457574e036 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 15 Oct 2013 18:23:36 +0200 Subject: [PATCH 11/61] add imp_meta --- tiramisu/config.py | 3 ++- translations/fr/tiramisu.po | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tiramisu/config.py b/tiramisu/config.py index d6399a2..d024159 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -537,7 +537,7 @@ class _CommonConfig(SubConfig): for subclass in self.__class__.__mro__: if subclass is not object: slots.update(subclass.__slots__) - slots -= frozenset(['_impl_context', '__weakref__']) + slots -= frozenset(['_impl_context', '_impl_meta', '__weakref__']) state = {} for slot in slots: try: @@ -564,6 +564,7 @@ class _CommonConfig(SubConfig): storage = get_storage(test=self._impl_test, **state['_storage']) self._impl_values._impl_setstate(storage) self._impl_settings._impl_setstate(storage) + self._impl_meta = None # ____________________________________________________________ diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 256a0da..93e30f4 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -79,7 +79,7 @@ msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule" #: 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" +msgstr "aucune config spécifiée alors que c'est nécessaire" #: tiramisu/option.py:204 msgid "cannot serialize Option, only in OptionDescription" From 384b30210c74bb0c1df4320747a53471006a7640 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 4 Nov 2013 17:15:47 +0100 Subject: [PATCH 12/61] find and find_first's new argument --- tiramisu/config.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tiramisu/config.py b/tiramisu/config.py index 1493e22..a923d47 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -250,7 +250,8 @@ class SubConfig(object): force_properties=force_properties, force_permissive=force_permissive) - def find(self, bytype=None, byname=None, byvalue=None, type_='option'): + def find(self, bytype=None, byname=None, byvalue=None, type_='option', + check_properties=True): """ finds a list of options recursively in the config @@ -262,11 +263,11 @@ class SubConfig(object): return self._cfgimpl_get_context()._find(bytype, byname, byvalue, first=False, type_=type_, - _subpath=self.cfgimpl_get_path() - ) + _subpath=self.cfgimpl_get_path(), + check_properties=check_properties) def find_first(self, bytype=None, byname=None, byvalue=None, - type_='option', display_error=True): + type_='option', display_error=True, check_properties=True): """ finds an option recursively in the config @@ -277,7 +278,8 @@ class SubConfig(object): """ return self._cfgimpl_get_context()._find( bytype, byname, byvalue, first=True, type_=type_, - _subpath=self.cfgimpl_get_path(), display_error=display_error) + _subpath=self.cfgimpl_get_path(), display_error=display_error, + check_properties=check_properties) def _find(self, bytype, byname, byvalue, first, type_='option', _subpath=None, check_properties=True, display_error=True): From 029452ccbc69f1d1d36a6bfd90c73ad3b0cef5f9 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 2 Dec 2013 15:10:05 +0100 Subject: [PATCH 13/61] validation of an ip if an ip term starts with a zero --- test/test_config_ip.py | 5 +++++ tiramisu/option.py | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/test/test_config_ip.py b/test/test_config_ip.py index b7d3010..581cbd2 100644 --- a/test/test_config_ip.py +++ b/test/test_config_ip.py @@ -21,6 +21,11 @@ def test_ip(): c.b = '0.0.0.0' raises(ValueError, "c.b = '255.255.255.0'") + raises(ValueError, "IPOption('a', 'ip', default='192.000.023.01')") + d = IPOption('a', 'ip', default='192.0.23.1') + od = OptionDescription('od', '', [d]) + c = Config(od) + raises(ValueError, "c.a = '192.000.023.01'") def test_ip_default(): a = IPOption('a', '', '88.88.88.88') diff --git a/tiramisu/option.py b/tiramisu/option.py index 88da41a..7cdf0a4 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -786,6 +786,12 @@ class IPOption(Option): warnings_only=warnings_only) def _validate(self, value): + # sometimes an ip term starts with a zero + # but this does not fit in some case, for example bind does not like it + for val in value.split('.'): + if val.startswith("0") and len(val)>1: + raise ValueError(_('invalid IP')) + # 'standard' validation try: IP('{0}/32'.format(value)) except ValueError: From 48b662997ecc82bc5f423cfb1056ba17c02b4efe Mon Sep 17 00:00:00 2001 From: gwen Date: Wed, 4 Dec 2013 15:48:19 +0100 Subject: [PATCH 14/61] an error message has been deleted by error --- tiramisu/option.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 7cdf0a4..f6a42c2 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -451,7 +451,8 @@ class Option(BaseOption): do_validation(value, force_index) else: if not isinstance(value, list): - raise ValueError(_("which must be a list").format(value, + raise ValueError(_("invalid value {0} for option {1} " + "which must be a list").format(value, self._name)) for index, val in enumerate(value): do_validation(val, index) From 5fb79fed383853431f8cd2fb931943dd1c098e5b Mon Sep 17 00:00:00 2001 From: gwen Date: Wed, 4 Dec 2013 15:55:53 +0100 Subject: [PATCH 15/61] an error message has been deleted by error, ref #6740 --- tiramisu/option.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index f6a42c2..df7c985 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -451,8 +451,8 @@ class Option(BaseOption): 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(_("invalid value {0} for option {1} ") + _("which must be a list").format(value, self._name)) for index, val in enumerate(value): do_validation(val, index) From deb174f36e87babb7e29ca6095e8fb7a843562de Mon Sep 17 00:00:00 2001 From: gwen Date: Thu, 5 Dec 2013 09:59:07 +0100 Subject: [PATCH 16/61] imprecise error message --- tiramisu/option.py | 4 +--- translations/fr/tiramisu.po | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index df7c985..f85c3ad 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -451,9 +451,7 @@ class Option(BaseOption): 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, - self._name)) + raise ValueError(_("invalid value {0} for option {1} which must be a list").format(value, self._name)) for index, val in enumerate(value): do_validation(val, index) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index ec9e66d..bff777c 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -116,8 +116,8 @@ msgid "invalid value for option {0}: {1}" msgstr "valeur invalide pour l'option {0} : {1}" #: tiramisu/option.py:454 -msgid "which must be a list" -msgstr "lequel doit être une liste" +msgid ""invalid value {0} for option {1} which must be a list" +msgstr "valeur invalide pour l'option {0} : {1} laquelle doit être une liste" #: tiramisu/option.py:514 msgid "consistency should be set with an option" From 20bef5ff0458ea7f6c2aef75189922f854b13827 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 9 Dec 2013 15:08:14 +0100 Subject: [PATCH 17/61] Important behavior change in callback with multi. Before, tiramisu iterable multi's callback_params in all cases. Now, this append only if multi's callback_params are in master/slave group. --- test/test_option_calculation.py | 203 +++++++++++++++----------------- tiramisu/autolib.py | 120 +++++++++++-------- tiramisu/option.py | 15 ++- tiramisu/value.py | 2 +- 4 files changed, 176 insertions(+), 164 deletions(-) diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 117de9d..680303d 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -21,7 +21,13 @@ def return_list(value=None): def return_list2(*args): - return list(args) + l = [] + for arg in args: + if isinstance(arg, list): + l.extend(arg) + else: + l.append(arg) + return l def return_value(value=None): @@ -34,6 +40,10 @@ def return_value2(*args, **kwargs): return value +def return_calc(i, j, k): + return i + j + k + + def make_description(): gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False) @@ -93,83 +103,6 @@ def test_identical_paths(): raises(ConflictError, "make_description_duplicates()") -def make_description2(): - gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') - gcdummy = BoolOption('dummy', 'dummy', default=False) - - floatoption = FloatOption('float', 'Test float option', default=2.3) - - objspaceoption = ChoiceOption('objspace', 'Object space', - ['std', 'thunk'], 'std') - booloption = BoolOption('bool', 'Test boolean option', default=True) - intoption = IntOption('int', 'Test int option', default=0) - stroption = StrOption('str', 'Test string option', default="abc") - # first multi - boolop = BoolOption('boolop', 'Test boolean option op', default=True) - boolop.enable_multi() - wantref_option = BoolOption('wantref', 'Test requires', default=False, - requires=({'option': boolop, 'expected': True, 'action': 'hidden'},)) - # second multi - wantframework_option = BoolOption('wantframework', 'Test requires', - default=False, - requires=({'option': boolop, 'expected': True, 'action': 'hidden'},)) - wantframework_option.enable_multi() - - gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) - descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption, - wantref_option, stroption, - wantframework_option, - intoption, boolop]) - return descr - - -# FIXME: il faudra tester les validations sur les multis -#def test_multi_constraints(): -# "a multi in a constraint has to have the same length" -# descr = make_description2() -# cfg = Config(descr) -# cfg.boolop = [True, True, False] -# cfg.wantframework = [False, False, True] -# -#def test_multi_raise(): -# "a multi in a constraint has to have the same length" -# # FIXME fusionner les deux tests, MAIS PROBLEME : -# # il ne devrait pas etre necessaire de refaire une config -# # si la valeur est modifiee une deuxieme fois -> -# #raises(ConflictConfigError, "cfg.wantframework = [False, False, True]") -# # ExceptionFailure: 'DID NOT RAISE' -# descr = make_description2() -# cfg = Config(descr) -# cfg.boolop = [True] -# raises(ConflictConfigError, "cfg.wantframework = [False, False, True]") -# ____________________________________________________________ -# adding dynamically new options description schema -#def test_newoption_add_in_descr(): -# descr = make_description() -# newoption = BoolOption('newoption', 'dummy twoo', default=False) -# descr.add_child(newoption) -# config = Config(descr) -# assert config.newoption == False - -#def test_newoption_add_in_subdescr(): -# descr = make_description() -# newoption = BoolOption('newoption', 'dummy twoo', default=False) -# descr.gc.add_child(newoption) -# config = Config(descr) -# config.bool = False -# assert config.gc.newoption == False - -#def test_newoption_add_in_config(): -# descr = make_description() -# config = Config(descr) -# config.bool = False -# newoption = BoolOption('newoption', 'dummy twoo', default=False) -# descr.add_child(newoption) -# config.cfgimpl_update() -# assert config.newoption == False -# ____________________________________________________________ - - def make_description_requires(): gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False) @@ -421,7 +354,7 @@ def test_callback_multi_value(): 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'] + assert cfg.val4 == ['new-val', 'new-val2', 'yes'] del(cfg.val1) assert cfg.val1 == ['val'] assert cfg.val2 == ['val'] @@ -443,6 +376,14 @@ def test_callback_multi_list(): assert cfg.val1 == ['val', 'val'] +def test_callback_multi_list_extend(): + val1 = StrOption('val1', "", callback=return_list2, callback_params={'': (['1', '2', '3'], ['4', '5'])}, multi=True) + maconfig = OptionDescription('rootconfig', '', [val1]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1 == ['1', '2', '3', '4', '5'] + + def test_callback_master_and_slaves_master(): val1 = StrOption('val1', "", multi=True, callback=return_val) val2 = StrOption('val2', "", multi=True) @@ -535,7 +476,8 @@ def test_callback_master_and_slaves_value(): 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]) + val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val5, False),)}) + interface1 = OptionDescription('val1', '', [val1, val2, val3, val5, val6]) interface1.impl_set_group_type(groups.master) maconfig = OptionDescription('rootconfig', '', [interface1, val4]) cfg = Config(maconfig) @@ -544,30 +486,35 @@ def test_callback_master_and_slaves_value(): assert cfg.val1.val2 == [] assert cfg.val1.val3 == [] assert cfg.val1.val5 == [] + assert cfg.val1.val6 == [] # cfg.val1.val1 = ['val1'] assert cfg.val1.val1 == ['val1'] assert cfg.val1.val2 == ['val1'] assert cfg.val1.val3 == ['yes'] assert cfg.val1.val5 == ['val10'] + assert cfg.val1.val6 == ['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'] + assert cfg.val1.val6 == ['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] + assert cfg.val1.val6 == ['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'] + assert cfg.val1.val6 == ['val10', 'val11'] # cfg.val1.val2 = ['val2', 'val2'] cfg.val1.val3 = ['val2', 'val2'] @@ -575,11 +522,13 @@ def test_callback_master_and_slaves_value(): assert cfg.val1.val2 == ['val2', 'val2'] assert cfg.val1.val3 == ['val2', 'val2'] assert cfg.val1.val5 == ['val2', 'val2'] + assert cfg.val1.val6 == ['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] + assert cfg.val1.val6 == ['val2', 'val2', None] cfg.cfgimpl_get_settings().remove('cache') cfg.val4 = ['val10', 'val11', 'val12'] #if value is already set, not updated ! @@ -587,6 +536,69 @@ def test_callback_master_and_slaves_value(): cfg.val1.val1.append('val3') cfg.val1.val1 = ['val1', 'val2', 'val3'] assert cfg.val1.val5 == ['val2', 'val2', 'val12'] + assert cfg.val1.val6 == ['val2', 'val2', 'val12'] + + +def test_callback_master(): + val2 = StrOption('val2', "", multi=True, callback=return_value) + val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val2, False),)}) + interface1 = OptionDescription('val1', '', [val1, val2]) + raises(ValueError, "interface1.impl_set_group_type(groups.master)") + + +def test_callback_master_and_other_master_slave(): + val1 = StrOption('val1', "", multi=True) + val2 = StrOption('val2', "", multi=True) + val3 = StrOption('val3', "", multi=True) + val4 = StrOption('val4', '', multi=True, default=['val10', 'val11']) + val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)}) + val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val2, False),)}) + interface1 = OptionDescription('val1', '', [val1, val2, val3]) + interface1.impl_set_group_type(groups.master) + interface2 = OptionDescription('val4', '', [val4, val5, val6]) + interface2.impl_set_group_type(groups.master) + maconfig = OptionDescription('rootconfig', '', [interface1, interface2]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val4.val4 == ['val10', 'val11'] + assert cfg.val4.val5 == [None, None] + assert cfg.val4.val6 == [None, None] + cfg.val1.val1 = ['yes'] + assert cfg.val4.val4 == ['val10', 'val11'] + assert cfg.val4.val5 == ['yes', None] + assert cfg.val4.val6 == [None, None] + cfg.val1.val2 = ['no'] + assert cfg.val4.val4 == ['val10', 'val11'] + assert cfg.val4.val5 == ['yes', None] + assert cfg.val4.val6 == ['no', None] + cfg.val1.val1 = ['yes', 'yes', 'yes'] + cfg.val1.val2 = ['no', 'no', 'no'] + assert cfg.val4.val4 == ['val10', 'val11'] + assert cfg.val4.val5 == ['yes', 'yes'] + assert cfg.val4.val6 == ['no', 'no'] + + +def test_callback_different_type(): + val = IntOption('val', "", default=2) + val_ = IntOption('val_', "", default=3) + val1 = IntOption('val1', "", multi=True) + val2 = IntOption('val2', "", multi=True, callback=return_calc, callback_params={'': ((val, False), (val1, False)), 'k': ((val_, False),)}) + interface1 = OptionDescription('val1', '', [val1, val2]) + interface1.impl_set_group_type(groups.master) + maconfig = OptionDescription('rootconfig', '', [interface1, val, val_]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val1.val1 == [] + assert cfg.val1.val2 == [] + cfg.val1.val1 = [1] + assert cfg.val1.val1 == [1] + assert cfg.val1.val2 == [6] + cfg.val1.val1 = [1, 3] + assert cfg.val1.val1 == [1, 3] + assert cfg.val1.val2 == [6, 8] + cfg.val1.val1 = [1, 3, 5] + assert cfg.val1.val1 == [1, 3, 5] + assert cfg.val1.val2 == [6, 8, 10] def test_callback_hidden(): @@ -653,7 +665,7 @@ def test_callback_multi_list_params(): maconfig = OptionDescription('rootconfig', '', [val1, oval2]) cfg = Config(maconfig) cfg.read_write() - assert cfg.val2.val2 == ['val', 'val', 'val', 'val'] + assert cfg.val2.val2 == ['val', 'val'] def test_callback_multi_list_params_key(): @@ -663,31 +675,4 @@ def test_callback_multi_list_params_key(): 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'] - - -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") + assert cfg.val2.val2 == ['val', 'val'] diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index efb7c0e..a176c8d 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -20,15 +20,16 @@ # ____________________________________________________________ "enables us to carry out a calculation and return an option's value" from tiramisu.error import PropertiesOptionError, ConfigError +from tiramisu.setting import multitypes from tiramisu.i18n import _ # ____________________________________________________________ -def carry_out_calculation(name, config, callback, callback_params, +def carry_out_calculation(option, 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`) + :param name: the option :param config: the context config in order to have the whole options available :param callback: the name of the callback function @@ -49,13 +50,13 @@ def carry_out_calculation(name, config, callback, callback_params, Values could have multiple values only when key is ''. * if no callback_params: - => calculate() + => calculate(, [], {}) * if callback_params={'': ('yes',)} - => calculate('yes') + => calculate(, ['yes'], {}) * if callback_params={'value': ('yes',)} - => calculate(value='yes') + => calculate(, [], {'value': 'yes'}) * if callback_params={'': ('yes', 'no')} => calculate('yes', 'no') @@ -63,58 +64,71 @@ def carry_out_calculation(name, config, callback, callback_params, * if callback_params={'value': ('yes', 'no')} => ValueError() + * if callback_params={'': (['yes', 'no'],)} + => calculate(, ['yes', 'no'], {}) + + * if callback_params={'value': ('yes', 'no')} + => raises ValueError() + * if callback_params={'': ((opt1, False),)} - a simple option: opt1 == 11 - => calculate(11) + => calculate(, [11], {}) - - a multi option: + - a multi option and not master/slave: opt1 == [1, 2, 3] - => calculate(1) - => calculate(2) - => calculate(3) + => calculate(, [[1, 2, 3]], {}) + + - option is master or slave of opt1: + opt1 == [1, 2, 3] + => calculate(, [1], {}) + => calculate(, [2], {}) + => calculate(, [3], {}) + + - opt is a master or slave but not related to option: + opt1 == [1, 2, 3] + => calculate(, [[1, 2, 3]], {}) * if callback_params={'value': ((opt1, False),)} - a simple option: opt1 == 11 - => calculate(value=11) + => calculate(, [], {'value': 11}) - a multi option: opt1 == [1, 2, 3] - => calculate(value=1) - => calculate(value=2) - => calculate(value=3) + => calculate(, [], {'value': [1, 2, 3]}) * if callback_params={'': ((opt1, False), (opt2, False))} + - two single options + opt1 = 11 + opt2 = 12 + => calculate(, [11, 12], {}) + - a multi option with a simple option opt1 == [1, 2, 3] - opt2 == 11 - => calculate(1, 11) - => calculate(2, 11) - => calculate(3, 11) + opt2 == 12 + => calculate(, [[1, 2, 3], 12], {}) - a multi option with an other multi option but with same length opt1 == [1, 2, 3] opt2 == [11, 12, 13] - => calculate(1, 11) - => calculate(2, 12) - => calculate(3, 13) + => calculate(, [[1, 2, 3], [11, 12, 13]], {}) - a multi option with an other multi option but with different length opt1 == [1, 2, 3] opt2 == [11, 12] - => ConfigError() + => calculate(, [[1, 2, 3], [11, 12]], {}) - a multi option without value with a simple option opt1 == [] opt2 == 11 - => [] + => calculate(, [[], 12], {}) * if callback_params={'value': ((opt1, False), (opt2, False))} - => ConfigError() + => raises ValueError() If index is not None, return a value, otherwise return: @@ -133,9 +147,9 @@ def carry_out_calculation(name, config, callback, callback_params, for callbk in callbacks: if isinstance(callbk, tuple): # callbk is something link (opt, True|False) - option, force_permissive = callbk + opt, force_permissive = callbk path = config.cfgimpl_get_description().impl_get_path_by_opt( - option) + opt) # get value try: value = config._getattr(path, force_permissive=True) @@ -144,18 +158,22 @@ def carry_out_calculation(name, config, callback, callback_params, continue raise ConfigError(_('unable to carry out a calculation, ' 'option {0} has properties: {1} for: ' - '{2}').format(option._name, + '{2}').format(opt._name, err.proptype, - name)) - is_multi = option.impl_is_multi() + option._name)) + + is_multi = False + if opt.impl_is_multi(): + #opt is master, search if option is a slave + if opt.impl_get_multitype() == multitypes.master: + if option in opt.impl_get_master_slaves(): + is_multi = True + #opt is slave, search if option is an other slaves + elif opt.impl_get_multitype() == multitypes.slave: + if option in opt.impl_get_master_slaves().impl_get_master_slaves(): + is_multi = True if is_multi: - 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 + len_multi = len(value) one_is_multi = True tcparams.setdefault(key, []).append((value, is_multi)) else: @@ -168,16 +186,9 @@ def carry_out_calculation(name, config, callback, callback_params, if one_is_multi: ret = [] if index: - if index < len_multi: - range_ = [index] - else: - range_ = [] - ret = None + range_ = [index] else: - if max_len and max_len < len_multi: - range_ = range(max_len) - else: - range_ = range(len_multi) + range_ = range(len_multi) for incr in range_: args = [] kwargs = {} @@ -196,10 +207,7 @@ def carry_out_calculation(name, config, callback, callback_params, if index: ret = calc else: - if isinstance(calc, list): - ret.extend(calc) - else: - ret.append(calc) + ret.append(calc) return ret else: # no value is multi @@ -213,7 +221,18 @@ def carry_out_calculation(name, config, callback, callback_params, args.append(couple[0]) else: kwargs[key] = couple[0] - return calculate(callback, args, kwargs) + ret = calculate(callback, args, kwargs) + if callback_params != {}: + if isinstance(ret, list) and max_len: + ret = ret[:max_len] + if len(ret) < max_len: + ret = ret + [None] * (max_len - len(ret)) + if isinstance(ret, list) and index: + if len(ret) < index + 1: + ret = None + else: + ret = ret[index] + return ret def calculate(callback, args, kwargs): @@ -224,4 +243,5 @@ def calculate(callback, args, kwargs): :param kwargs: in the callback's arity, the named parameters """ + print args, kwargs, callback(*args, **kwargs) return callback(*args, **kwargs) diff --git a/tiramisu/option.py b/tiramisu/option.py index f85c3ad..246363c 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -413,7 +413,7 @@ class Option(BaseOption): else: validator_params = {'': (val,)} # Raise ValueError if not valid - carry_out_calculation(self._name, config=context, + carry_out_calculation(self, config=context, callback=self._validator[0], callback_params=validator_params) @@ -714,7 +714,7 @@ class StrOption(Option): if sys.version_info[0] >= 3: - #UnicodeOption is same has StrOption in python 3+ + #UnicodeOption is same as StrOption in python 3+ class UnicodeOption(StrOption): __slots__ = tuple() pass @@ -788,7 +788,7 @@ class IPOption(Option): # sometimes an ip term starts with a zero # but this does not fit in some case, for example bind does not like it for val in value.split('.'): - if val.startswith("0") and len(val)>1: + if val.startswith("0") and len(val) > 1: raise ValueError(_('invalid IP')) # 'standard' validation try: @@ -1284,13 +1284,20 @@ class OptionDescription(BaseOption): raise ValueError(_('master group with wrong' ' master name for {0}' ).format(self._name)) + if master._callback is not None and master._callback[1] is not None: + for key, callbacks in master._callback[1].items(): + for callbk in callbacks: + if isinstance(callbk, tuple): + if callbk[0] in slaves: + raise ValueError(_("callback of master's option shall " + "not refered a slave's ones")) master._master_slaves = tuple(slaves) for child in self.impl_getchildren(): if child != master: child._master_slaves = master child._multitype = multitypes.slave if not identical_master_child_name: - raise ValueError(_("no child has same nom has master group" + raise ValueError(_("no child has same name has master group" " for: {0}").format(self._name)) else: raise ValueError(_('group_type: {0}' diff --git a/tiramisu/value.py b/tiramisu/value.py index 4426742..ed7d39d 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -137,7 +137,7 @@ class Values(object): callback, callback_params = opt._callback if callback_params is None: callback_params = {} - return carry_out_calculation(opt._name, config=self.context(), + return carry_out_calculation(opt, config=self.context(), callback=callback, callback_params=callback_params, index=index, max_len=max_len) From 0f966f6d2602cf888519de691a78352da8d400a0 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 9 Dec 2013 15:29:37 +0100 Subject: [PATCH 18/61] check if permissive is in global properties before allow permissive for an option --- test/test_option_owner.py | 13 ++++- test/test_permissive.py | 116 +++++++++++++++++++++++++++++++++++++- tiramisu/autolib.py | 1 - tiramisu/setting.py | 7 ++- 4 files changed, 131 insertions(+), 6 deletions(-) diff --git a/test/test_option_owner.py b/test/test_option_owner.py index bc96285..b758e91 100644 --- a/test/test_option_owner.py +++ b/test/test_option_owner.py @@ -5,7 +5,7 @@ from tiramisu.setting import owners from tiramisu.config import Config from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ StrOption, OptionDescription -from tiramisu.error import ConfigError +from tiramisu.error import ConfigError, ConstError def make_description(): @@ -52,6 +52,17 @@ def test_addowner(): assert cfg.getowner(gcdummy) == owners.gen_config +def test_addowner_multiple_time(): + owners.addowner("testowner") + raises(ConstError, 'owners.addowner("testowner")') + + +def test_delete_owner(): + owners.addowner('deleted') + raises(ConstError, 'del(owners.deleted)') + raises(ValueError, 'del(owners.deleted2)') + + def test_owner_is_not_a_string(): gcdummy = BoolOption('dummy', 'dummy', default=False) descr = OptionDescription('tiramisu', '', [gcdummy]) diff --git a/test/test_permissive.py b/test/test_permissive.py index 38a8bcf..20e6535 100644 --- a/test/test_permissive.py +++ b/test/test_permissive.py @@ -9,7 +9,8 @@ from tiramisu.error import PropertiesOptionError def make_description(): u1 = IntOption('u1', '', properties=('frozen', 'mandatory', 'disabled', )) - return OptionDescription('od1', '', [u1]) + u2 = IntOption('u2', '', properties=('frozen', 'mandatory', 'disabled', )) + return OptionDescription('od1', '', [u1, u2]) def test_permissive(): @@ -91,3 +92,116 @@ def test_invalid_permissive(): setting = config.cfgimpl_get_settings() config.read_write() raises(TypeError, "setting.setpermissive(['frozen', 'disabled',])") + + +def test_permissive_option(): + descr = make_description() + u1 = descr.u1 + config = Config(descr) + setting = config.cfgimpl_get_settings() + config.read_write() + + props = [] + try: + config.u1 + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + props = [] + try: + config.u2 + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + + setting.setpermissive(('disabled',), u1) + props = [] + try: + config.u1 + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + props = [] + try: + config.u2 + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + + setting.append('permissive') + config.u1 + props = [] + try: + config.u2 + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + + setting.remove('permissive') + props = [] + try: + config.u1 + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + props = [] + try: + config.u2 + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + + +def test_permissive_option_mandatory(): + descr = make_description() + u1 = descr.u1 + config = Config(descr) + setting = config.cfgimpl_get_settings() + config.read_only() + props = [] + try: + config.u1 + except PropertiesOptionError as err: + props = err.proptype + assert set(props) == set(['disabled', 'mandatory']) + setting.setpermissive(('mandatory', 'disabled',), u1) + setting.append('permissive') + config.u1 + setting.remove('permissive') + try: + config.u1 + except PropertiesOptionError as err: + props = err.proptype + assert set(props) == set(['disabled', 'mandatory']) + + +def test_permissive_option_frozen(): + descr = make_description() + config = Config(descr) + u1 = descr.u1 + setting = config.cfgimpl_get_settings() + config.read_write() + setting.setpermissive(('frozen', 'disabled'), u1) + try: + config.u1 = 1 + except PropertiesOptionError as err: + props = err.proptype + assert set(props) == set(['frozen', 'disabled']) + setting.append('permissive') + config.u1 = 1 + assert config.u1 == 1 + setting.remove('permissive') + try: + config.u1 = 1 + except PropertiesOptionError as err: + props = err.proptype + assert set(props) == set(['frozen', 'disabled']) + + +def test_invalid_option_permissive(): + descr = make_description() + u1 = descr.u1 + config = Config(descr) + setting = config.cfgimpl_get_settings() + config.read_write() + raises(TypeError, "setting.setpermissive(['frozen', 'disabled',], u1)") diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index a176c8d..ffdbb0a 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -243,5 +243,4 @@ def calculate(callback, args, kwargs): :param kwargs: in the callback's arity, the named parameters """ - print args, kwargs, callback(*args, **kwargs) return callback(*args, **kwargs) diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 684ec74..e8feae5 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -406,10 +406,11 @@ class Settings(object): """ # opt properties properties = copy(self._getproperties(opt_or_descr, path)) - # remove opt permissive - properties -= self._p_.getpermissive(path) - # remove global permissive if need self_properties = copy(self._getproperties()) + # remove opt permissive + if force_permissive is True or 'permissive' in self_properties: + properties -= self._p_.getpermissive(path) + # remove global permissive if need if force_permissive is True or 'permissive' in self_properties: properties -= self._p_.getpermissive() if force_permissives is not None: From 6e4f19eebe80460e1296f09290fcc21c246e4090 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 9 Dec 2013 15:43:45 +0100 Subject: [PATCH 19/61] more tests --- test/test_config.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index e091294..f6f9fdf 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -131,8 +131,10 @@ def test_cfgimpl_get_home_by_path(): config.bool = False assert config.cfgimpl_get_home_by_path('gc.dummy')[1] == 'dummy' assert config.cfgimpl_get_home_by_path('dummy')[1] == 'dummy' - #assert config.getpaths(include_groups=False) == ['gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop'] - #assert config.getpaths(include_groups=True) == ['gc', 'gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop'] + + +def test_not_valid_properties(): + raises(TypeError, "stroption = StrOption('str', 'Test string option', default='abc', properties=['mandatory',])") def test_information_config(): @@ -142,6 +144,7 @@ def test_information_config(): config.impl_set_information('info', string) assert config.impl_get_information('info') == string raises(ValueError, "config.impl_get_information('noinfo')") + assert config.impl_get_information('noinfo', 'default') == 'default' def test_config_impl_get_path_by_opt(): @@ -231,8 +234,8 @@ def test_duplicated_option(): #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") - From bb1f6947e388366ec0abd6f2534a02d9def1b65e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 9 Dec 2013 17:55:52 +0100 Subject: [PATCH 20/61] better name's validation --- test/test_option.py | 24 ++++++++++++++++++++++++ tiramisu/option.py | 4 +--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/test/test_option.py b/test/test_option.py index 0492ed2..4bb31cb 100644 --- a/test/test_option.py +++ b/test/test_option.py @@ -2,6 +2,7 @@ and to compare them """ import autopath +from py.test import raises from tiramisu.option import BoolOption, IntOption @@ -36,3 +37,26 @@ from tiramisu.option import BoolOption, IntOption # assert dummy1 != dummy5 # assert dummy1 == dummy6 # assert dummy1 != dummy7 + + +def test_option_valid_name(): + IntOption('test', '') + raises(ValueError, 'IntOption(1, "")') + raises(ValueError, 'IntOption("impl_test", "")') + raises(ValueError, 'IntOption("_test", "")') + raises(ValueError, 'IntOption("unwrap_from_path", "")') + + +def test_option_get_information(): + i = IntOption('test', '') + string = 'some informations' + i.impl_set_information('info', string) + assert i.impl_get_information('info') == string + raises(ValueError, "i.impl_get_information('noinfo')") + assert i.impl_get_information('noinfo', 'default') == 'default' + +def test_option_multi(): + IntOption('test', '', multi=True) + IntOption('test', '', multi=True, default_multi=1) + IntOption('test', '', default=[1], multi=True, default_multi=1) + raises(ValueError, "IntOption('test', '', default_multi=1)") diff --git a/tiramisu/option.py b/tiramisu/option.py index 246363c..09e0dbd 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -40,9 +40,7 @@ forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first', def valid_name(name): "an option's name is a str and does not start with 'impl' or 'cfgimpl'" - try: - name = str(name) - except: + if not isinstance(name, str): return False if re.match(name_regexp, name) is None and not name.startswith('_') \ and name not in forbidden_names \ From 73745be4404e14ac120eeb3fcd16490be446e512 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 9 Dec 2013 17:59:39 +0100 Subject: [PATCH 21/61] Important behavior change : to add default_multi value, now use Multi.append(), not Multi.append(None) --- test/test_config.py | 15 +++++++++++++++ test/test_option_calculation.py | 4 ++-- tiramisu/setting.py | 6 ++++++ tiramisu/value.py | 11 ++++++----- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index f6f9fdf..6a4f6b4 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -239,3 +239,18 @@ def test_cannot_assign_value_to_option_description(): descr = make_description() cfg = Config(descr) raises(TypeError, "cfg.gc = 3") + + +def test_config_multi(): + i1 = IntOption('test1', '', multi=True) + i2 = IntOption('test2', '', multi=True, default_multi=1) + i3 = IntOption('test3', '', default=[2], multi=True, default_multi=1) + od = OptionDescription('test', '', [i1, i2, i3]) + config = Config(od) + assert config.test1 == [] + assert config.test2 == [] + config.test2.append() + assert config.test2 == [1] + assert config.test3 == [2] + config.test3.append() + assert config.test3 == [2, 1] diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 680303d..7994aa1 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -393,7 +393,7 @@ def test_callback_master_and_slaves_master(): cfg = Config(maconfig) cfg.read_write() assert cfg.val1.val1 == ['val'] - cfg.val1.val1.append(None) + cfg.val1.val1.append() assert cfg.val1.val1 == ['val', 'val'] assert cfg.val1.val2 == [None, None] @@ -408,7 +408,7 @@ def test_callback_master_and_slaves_master_list(): cfg.read_write() assert cfg.val1.val1 == ['val', 'val'] assert cfg.val1.val2 == [None, None] - cfg.val1.val1.append(None) + cfg.val1.val1.append() assert cfg.val1.val1 == ['val', 'val', None] assert cfg.val1.val2 == [None, None, None] del(cfg.val1.val1) diff --git a/tiramisu/setting.py b/tiramisu/setting.py index e8feae5..470ec94 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -242,6 +242,12 @@ multitypes = MultiTypeModule() populate_multitypes() +# ____________________________________________________________ +class Undefined(): + pass + + +undefined = Undefined() # ____________________________________________________________ class Property(object): "a property is responsible of the option's value access rules" diff --git a/tiramisu/value.py b/tiramisu/value.py index ed7d39d..8c50373 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -22,7 +22,7 @@ from copy import copy import sys import weakref from tiramisu.error import ConfigError, SlaveError -from tiramisu.setting import owners, multitypes, expires_time +from tiramisu.setting import owners, multitypes, expires_time, undefined from tiramisu.autolib import carry_out_calculation from tiramisu.i18n import _ from tiramisu.option import SymLinkOption @@ -452,8 +452,7 @@ class Multi(list): values._getcallback_value(slave, index=index), force=True) else: - value_slave.append(slave.impl_getdefault_multi(), - force=True) + value_slave.append(undefined, force=True) def __setitem__(self, index, value): self._validate(value, index) @@ -461,7 +460,7 @@ class Multi(list): super(Multi, self).__setitem__(index, value) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) - def append(self, value, force=False): + def append(self, value=undefined, force=False): """the list value can be updated (appened) only if the option is a master """ @@ -471,12 +470,14 @@ class Multi(list): " which is a slave").format(self.opt._name)) elif self.opt.impl_get_multitype() == multitypes.master: values = self.context().cfgimpl_get_values() - if value is None and self.opt.impl_has_callback(): + if value is undefined and self.opt.impl_has_callback(): value = values._getcallback_value(self.opt) #Force None il return a list if isinstance(value, list): value = None index = self.__len__() + if value is undefined: + value = self.opt.impl_getdefault_multi() self._validate(value, index) super(Multi, self).append(value) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, From 1e880f4bc6f8d325eae252ea6c3ace8c3c463d1c Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 9 Dec 2013 18:48:44 +0100 Subject: [PATCH 22/61] remove unused code --- tiramisu/option.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 09e0dbd..ad99416 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -453,12 +453,9 @@ class Option(BaseOption): for index, val in enumerate(value): do_validation(val, index) - def impl_getdefault(self, default_multi=False): + def impl_getdefault(self): "accessing the default value" - if not default_multi or not self.impl_is_multi(): - return self._default - else: - return self.getdefault_multi() + return self._default def impl_getdefault_multi(self): "accessing the default value for a multi" @@ -1256,7 +1253,6 @@ class OptionDescription(BaseOption): self._group_type = group_type if isinstance(group_type, groups.MasterGroupType): #if master (same name has group) is set - identical_master_child_name = False #for collect all slaves slaves = [] master = None @@ -1273,7 +1269,6 @@ class OptionDescription(BaseOption): ": this option is not a multi" "").format(child._name, self._name)) if child._name == self._name: - identical_master_child_name = True child._multitype = multitypes.master master = child else: @@ -1294,9 +1289,6 @@ class OptionDescription(BaseOption): if child != master: child._master_slaves = master child._multitype = multitypes.slave - if not identical_master_child_name: - raise ValueError(_("no child has same name has master group" - " for: {0}").format(self._name)) else: raise ValueError(_('group_type: {0}' ' not allowed').format(group_type)) From c58de18b6215dfffd12f99afd8d0497a8c438b62 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 9 Dec 2013 18:56:29 +0100 Subject: [PATCH 23/61] add more tests --- test/test_config.py | 20 ++++++++++++++++ test/test_option.py | 42 +++++++++++++++++++++++++++++++-- test/test_option_calculation.py | 13 ++++++++++ test/test_option_consistency.py | 11 +++++++++ test/test_requires.py | 19 +++++++++++++++ 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index 6a4f6b4..5cdbe7d 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -152,8 +152,10 @@ def test_config_impl_get_path_by_opt(): config = Config(descr) dummy = config.unwrap_from_path('gc.dummy') boo = config.unwrap_from_path('bool') + unknown = IntOption('test', '') assert config.cfgimpl_get_description().impl_get_path_by_opt(boo) == 'bool' assert config.cfgimpl_get_description().impl_get_path_by_opt(dummy) == 'gc.dummy' + raises(AttributeError, "config.cfgimpl_get_description().impl_get_path_by_opt(unknown)") def test_config_impl_get_opt_by_path(): @@ -163,6 +165,7 @@ def test_config_impl_get_opt_by_path(): boo = config.unwrap_from_path('bool') assert config.cfgimpl_get_description().impl_get_opt_by_path('bool') == boo assert config.cfgimpl_get_description().impl_get_opt_by_path('gc.dummy') == dummy + raises(AttributeError, "config.cfgimpl_get_description().impl_get_opt_by_path('gc.unknown')") def test_information_display(): @@ -254,3 +257,20 @@ def test_config_multi(): assert config.test3 == [2] config.test3.append() assert config.test3 == [2, 1] + + +def test_no_validation(): + i1 = IntOption('test1', '') + od = OptionDescription('test', '', [i1]) + c = Config(od) + setting = c.cfgimpl_get_settings() + c.test1 = 1 + raises(ValueError, 'c.test1 = "yes"') + assert c.test1 == 1 + setting.remove('validator') + c.test1 = "yes" + assert c.test1 == "yes" + setting.append('validator') + raises(ValueError, 'c.test1') + del(c.test1) + assert c.test1 == None diff --git a/test/test_option.py b/test/test_option.py index 4bb31cb..ec769c5 100644 --- a/test/test_option.py +++ b/test/test_option.py @@ -4,7 +4,11 @@ and to compare them import autopath from py.test import raises -from tiramisu.option import BoolOption, IntOption +from tiramisu.option import IntOption, OptionDescription + + +def a_func(): + return None #def test_option_comparison(): @@ -47,16 +51,50 @@ def test_option_valid_name(): raises(ValueError, 'IntOption("unwrap_from_path", "")') +def test_option_with_callback(): + #no default value with callback + raises(ValueError, "IntOption('test', '', default=1, callback=a_func)") + + def test_option_get_information(): - i = IntOption('test', '') + description = "it's ok" string = 'some informations' + i = IntOption('test', description) i.impl_set_information('info', string) assert i.impl_get_information('info') == string raises(ValueError, "i.impl_get_information('noinfo')") assert i.impl_get_information('noinfo', 'default') == 'default' + assert i.impl_get_information('doc') == description + assert i.impl_getdoc() == description + + +def test_optiondescription_get_information(): + description = "it's ok" + string = 'some informations' + o = OptionDescription('test', description, []) + o.impl_set_information('info', string) + assert o.impl_get_information('info') == string + raises(ValueError, "o.impl_get_information('noinfo')") + assert o.impl_get_information('noinfo', 'default') == 'default' + assert o.impl_get_information('doc') == description + assert o.impl_getdoc() == description + def test_option_multi(): IntOption('test', '', multi=True) IntOption('test', '', multi=True, default_multi=1) IntOption('test', '', default=[1], multi=True, default_multi=1) + #add default_multi to not multi's option raises(ValueError, "IntOption('test', '', default_multi=1)") + #unvalid default_multi + raises(ValueError, "IntOption('test', '', multi=True, default_multi='yes')") + #not default_multi with callback + raises(ValueError, "IntOption('test', '', multi=True, default_multi=1, callback=a_func)") + + +def test_option_is_multi_by_default(): + assert IntOption('test', '').impl_is_empty_by_default() is True + assert IntOption('test', '', 1).impl_is_empty_by_default() is False + assert IntOption('test', '', multi=True).impl_is_empty_by_default() is True + assert IntOption('test', '', [1], multi=True).impl_is_empty_by_default() is False + assert IntOption('test', '', multi=True, default_multi=1).impl_is_empty_by_default() is True diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 7994aa1..91d61a4 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -243,6 +243,19 @@ def test_callback(): assert cfg.val1 == 'val' +def test_callback_params_without_callback(): + raises(ValueError, "StrOption('val2', '', callback_params={'': ('yes',)})") + + +def test_callback_invalid(): + raises(ValueError, 'val1 = StrOption("val1", "", callback="string")') + raises(ValueError, 'val1 = StrOption("val1", "", callback=return_val, callback_params="string")') + val1 = StrOption('val1', "", 'val') + raises(ValueError, "StrOption('val2', '', callback=return_value, callback_params={'': 'string'})") + raises(ValueError, "StrOption('val4', '', callback=return_value, callback_params={'value': (('string', False),)})") + raises(ValueError, "StrOption('val4', '', callback=return_value, callback_params={'value': ((val1, 'string'),)})") + + def test_callback_value(): val1 = StrOption('val1', "", 'val') val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)}) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index d5226db..4db3410 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -8,6 +8,17 @@ from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ from tiramisu.error import ConfigError +def test_consistency(): + a = IntOption('a', '') + b = IntOption('b', '') + od = OptionDescription('od', '', [a, b]) + a.impl_add_consistency('not_equal', b) + #consistency to itself + raises(ConfigError, "a.impl_add_consistency('not_equal', a)") + #consistency with string + raises(ConfigError, "a.impl_add_consistency('not_equal', 'a')") + + def test_consistency_not_equal(): a = IntOption('a', '') b = IntOption('b', '') diff --git a/test/test_requires.py b/test/test_requires.py index ba6cf3f..4cf9372 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -26,6 +26,20 @@ def test_requires(): assert props == ['disabled'] +def test_requires_invalid(): + a = BoolOption('activate_service', '', True) + raises(ValueError, "IPOption('ip_address_service', '', requires='string')") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'unknown': True}])") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False}])") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'action': 'disabled'}])") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'expected': False, 'action': 'disabled'}])") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'inverse': 'string'}])") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'transitive': 'string'}])") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'same_action': 'string'}])") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': 'string', 'expected': False, 'action': 'disabled'}])") + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': 'string', 'action': 'disabled'}])") + + def test_requires_same_action(): a = BoolOption('activate_service', '', True) b = BoolOption('activate_service_web', '', True, @@ -504,6 +518,11 @@ def test_requires_requirement_append(): c.cfgimpl_get_settings()[b].append("test") +def test_requires_different_inverse(): + a = BoolOption('activate_service', '', True) + raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': True, 'action': 'disabled', 'inverse': True}, {'option': a, 'expected': True, 'action': 'disabled', 'inverse': False}])") + + def test_requires_recursive_path(): a = BoolOption('activate_service', '', True) b = IPOption('ip_address_service', '', From 172a33f84200f2e7cf4c7daec9a39a7e0c13e51c Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 16 Dec 2013 14:20:35 +0100 Subject: [PATCH 24/61] mandatory_warnings never raises ConfigError --- tiramisu/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tiramisu/config.py b/tiramisu/config.py index a923d47..1381d57 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -711,4 +711,6 @@ def mandatory_warnings(config): except PropertiesOptionError as err: if err.proptype == ['mandatory']: yield path + except ConfigError: + pass config.cfgimpl_reset_cache(only=('values',)) From f0ecbf49140d13938d240b8e6abb080cfb238a0e Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 6 Jan 2014 14:32:56 +0100 Subject: [PATCH 25/61] adds an extend API for the settings --- doc/config.txt | 16 +++++++++++----- tiramisu/setting.py | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/doc/config.txt b/doc/config.txt index 3c6d9bc..f1ab1ce 100644 --- a/doc/config.txt +++ b/doc/config.txt @@ -10,8 +10,6 @@ Tiramisu is made of almost three main objects : - :class:`tiramisu.option.OptionDescription` is the shema, the option's structure - :class:`tiramisu.config.Config` which is the whole configuration entry point -.. image:: config.png - Accessing the `Option`'s ------------------------- @@ -47,9 +45,13 @@ object is returned, and if no `Option` has been declared in the The `Option` objects (in this case the :class:`~tiramisu.option.BoolOption`), are organized into a tree into nested -:class:`~tiramisu.option.OptionDescription` objects. Every option has a name, -as does every option group. The parts of the full name of the option are -separated by dots: e.g. ``cfg.optgroup.optname``. +:class:`~tiramisu.option.OptionDescription` objects. + +.. image:: config.png + +Every option has a name, as does every option group. The parts +of the full name of the option are separated by dots: e.g. +``cfg.optgroup.optname``. Let's make the protocol of accessing a `Config`'s attribute explicit (because explicit is better than implicit): @@ -362,6 +364,10 @@ read/write or read only mode:: >>> c.cfgimpl_get_settings().remove('unknown') >>> print c.od1.var3 value + +Many properties can be defined at the same time on an option:: + + >>> c.cfgimpl_get_settings().extend(['unknown1', 'unknown2']) Properties can also be defined on an option group (that is, on an :term:`option description`) let's hide a group and try to access to it:: diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 470ec94..3b4d2f7 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -260,6 +260,11 @@ class Property(object): self._properties = prop def append(self, propname): + """Appends a property named propname + + :param propname: a predefined or user defined property name + :type propname: string + """ if self._opt is not None and self._opt._calc_properties is not None \ and propname in self._opt._calc_properties: raise ValueError(_('cannot append {0} property for option {1}: ' @@ -269,12 +274,28 @@ class Property(object): self._setting._setproperties(self._properties, self._opt, self._path) def remove(self, propname): + """Removes a property named propname + + :param propname: a predefined or user defined property name + :type propname: string + """ if propname in self._properties: self._properties.remove(propname) self._setting._setproperties(self._properties, self._opt, self._path) + def extend(self, propnames): + """Extends properties to the existing properties + + :param propnames: an iterable made of property names + :type propnames: iterable of string + """ + for propname in propname: + self.append(propname) def reset(self): + """resets the properties (does not **clear** the properties, + default properties are still present) + """ self._setting.reset(_path=self._path) def __contains__(self, propname): From 3c36e05d82659c8710bb6646dd851b204d4226e5 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 6 Jan 2014 14:40:29 +0100 Subject: [PATCH 26/61] adds test for an API --- test/test_option_type.py | 10 ++++++++++ tiramisu/setting.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/test/test_option_type.py b/test/test_option_type.py index 6f1970a..3537d6d 100644 --- a/test/test_option_type.py +++ b/test/test_option_type.py @@ -75,6 +75,16 @@ def test_group_is_hidden(): prop = err.proptype assert 'hidden' in prop +def test_extend_properties(): + descr = make_description() + config = Config(descr) + setting = config.cfgimpl_get_settings() + config.read_write() + gc = config.unwrap_from_path('gc') + config.unwrap_from_path('gc.dummy') + setting[gc].extend(['hidden', 'user_defined_property']) + assert 'hidden' in setting[gc] + assert 'user_defined_property' in setting[gc] def test_group_is_hidden_multi(): descr = make_description() diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 3b4d2f7..a9fce8d 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -289,7 +289,7 @@ class Property(object): :param propnames: an iterable made of property names :type propnames: iterable of string """ - for propname in propname: + for propname in propnames: self.append(propname) def reset(self): From 40ecddf242f2e2d1c64d978d3f629317232c1749 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 6 Jan 2014 15:32:28 +0100 Subject: [PATCH 27/61] docstring --- tiramisu/setting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiramisu/setting.py b/tiramisu/setting.py index a9fce8d..dc49257 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -307,7 +307,7 @@ class Property(object): #____________________________________________________________ class Settings(object): - "``Config()``'s configuration options" + "``config.Config()``'s configuration options settings" __slots__ = ('context', '_owner', '_p_', '__weakref__') def __init__(self, context, storage): From 9d92ab84d7682da111719de74f599cbf938a7f38 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 16 Jan 2014 09:49:37 +0100 Subject: [PATCH 28/61] regression: permissive for option is apply every time, not only when global permissive is set --- test/test_permissive.py | 18 +++++------------- tiramisu/setting.py | 8 ++++++-- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/test/test_permissive.py b/test/test_permissive.py index 20e6535..615fef7 100644 --- a/test/test_permissive.py +++ b/test/test_permissive.py @@ -120,7 +120,7 @@ def test_permissive_option(): config.u1 except PropertiesOptionError as err: props = err.proptype - assert props == ['disabled'] + assert props == [] props = [] try: config.u2 @@ -143,7 +143,7 @@ def test_permissive_option(): config.u1 except PropertiesOptionError as err: props = err.proptype - assert props == ['disabled'] + assert props == [] props = [] try: config.u2 @@ -182,20 +182,12 @@ def test_permissive_option_frozen(): setting = config.cfgimpl_get_settings() config.read_write() setting.setpermissive(('frozen', 'disabled'), u1) - try: - config.u1 = 1 - except PropertiesOptionError as err: - props = err.proptype - assert set(props) == set(['frozen', 'disabled']) - setting.append('permissive') config.u1 = 1 assert config.u1 == 1 + setting.append('permissive') + assert config.u1 == 1 setting.remove('permissive') - try: - config.u1 = 1 - except PropertiesOptionError as err: - props = err.proptype - assert set(props) == set(['frozen', 'disabled']) + assert config.u1 == 1 def test_invalid_option_permissive(): diff --git a/tiramisu/setting.py b/tiramisu/setting.py index dc49257..99347f1 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -248,6 +248,8 @@ class Undefined(): undefined = Undefined() + + # ____________________________________________________________ class Property(object): "a property is responsible of the option's value access rules" @@ -283,6 +285,7 @@ class Property(object): self._properties.remove(propname) self._setting._setproperties(self._properties, self._opt, self._path) + def extend(self, propnames): """Extends properties to the existing properties @@ -435,8 +438,9 @@ class Settings(object): properties = copy(self._getproperties(opt_or_descr, path)) self_properties = copy(self._getproperties()) # remove opt permissive - if force_permissive is True or 'permissive' in self_properties: - properties -= self._p_.getpermissive(path) + # permissive affect option's permission with or without permissive + # global property + properties -= self._p_.getpermissive(path) # remove global permissive if need if force_permissive is True or 'permissive' in self_properties: properties -= self._p_.getpermissive() From 21a67971c54a6d83220e051bb4c7b735256a77ae Mon Sep 17 00:00:00 2001 From: gwen Date: Fri, 24 Jan 2014 09:17:46 +0100 Subject: [PATCH 29/61] typo propertive -> property --- tiramisu/setting.py | 2 +- tiramisu/storage/dictionary/setting.py | 4 ++-- tiramisu/storage/sqlite3/setting.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 99347f1..02da5ae 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -352,7 +352,7 @@ class Settings(object): raise ValueError(_('opt and all_properties must not be set ' 'together in reset')) if all_properties: - self._p_.reset_all_propertives() + self._p_.reset_all_properties() else: if opt is not None and _path is None: _path = self._get_path_by_opt(opt) diff --git a/tiramisu/storage/dictionary/setting.py b/tiramisu/storage/dictionary/setting.py index 1b7001b..899af4a 100644 --- a/tiramisu/storage/dictionary/setting.py +++ b/tiramisu/storage/dictionary/setting.py @@ -31,7 +31,7 @@ class Settings(Cache): self._permissives = {} super(Settings, self).__init__(storage) - # propertives + # properties def setproperties(self, path, properties): self._properties[path] = properties @@ -41,7 +41,7 @@ class Settings(Cache): def hasproperties(self, path): return path in self._properties - def reset_all_propertives(self): + def reset_all_properties(self): self._properties.clear() def reset_properties(self, path): diff --git a/tiramisu/storage/sqlite3/setting.py b/tiramisu/storage/sqlite3/setting.py index ed79181..9a5d2f9 100644 --- a/tiramisu/storage/sqlite3/setting.py +++ b/tiramisu/storage/sqlite3/setting.py @@ -33,7 +33,7 @@ class Settings(Sqlite3DB): self._storage.execute(settings_table, commit=False) self._storage.execute(permissives_table) - # propertives + # properties def setproperties(self, path, properties): path = self._sqlite_encode_path(path) self._storage.execute("DELETE FROM property WHERE path = ?", (path,), @@ -56,7 +56,7 @@ class Settings(Sqlite3DB): return self._storage.select("SELECT properties FROM property WHERE " "path = ?", (path,)) is not None - def reset_all_propertives(self): + def reset_all_properties(self): self._storage.execute("DELETE FROM property") def reset_properties(self, path): From 138018dfe95093ca81a1d009e62c0ff00820692a Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 25 Jan 2014 11:20:11 +0100 Subject: [PATCH 30/61] if we delete all reference to a Config and we have reference to old SubConfig, Values, Multi or Settings, make a ConfigError instead of AttributError on NoneType object --- test/test_config.py | 24 +++++++++++- tiramisu/config.py | 10 ++++- tiramisu/setting.py | 25 ++++++++---- tiramisu/value.py | 94 +++++++++++++++++++++++++++++---------------- 4 files changed, 110 insertions(+), 43 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index 5cdbe7d..6db3fcf 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -9,7 +9,7 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ BoolOption, UnicodeOption, OptionDescription -from tiramisu.error import ConflictError +from tiramisu.error import ConflictError, ConfigError def make_description(): @@ -273,4 +273,24 @@ def test_no_validation(): setting.append('validator') raises(ValueError, 'c.test1') del(c.test1) - assert c.test1 == None + assert c.test1 is None + + +def test_delete_config_with_subconfig(): + test = IntOption('test', '') + multi = IntOption('multi', '', multi=True) + od = OptionDescription('od', '', [test, multi]) + odroot = OptionDescription('odroot', '', [od]) + c = Config(odroot) + sub = c.od + val = c.cfgimpl_get_values() + setting = c.cfgimpl_get_settings() + val[test] + val[multi] + setting[test] + sub.make_dict() + del(c) + raises(ConfigError, 'val[test]') + raises(ConfigError, 'val[multi]') + raises(ConfigError, 'setting[test]') + raises(ConfigError, 'sub.make_dict()') diff --git a/tiramisu/config.py b/tiramisu/config.py index 1381d57..4bc0e15 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -156,7 +156,15 @@ class SubConfig(object): __repr__ = __str__ def _cfgimpl_get_context(self): - return self._impl_context() + """context could be None, we need to test it + context is None only if all reference to `Config` object is deleted + (for example we delete a `Config` and we manipulate a reference to + old `SubConfig`, `Values`, `Multi` or `Settings`) + """ + context = self._impl_context() + if context is None: + raise ConfigError(_('the context does not exist anymore')) + return context def cfgimpl_get_description(self): if self._impl_descr is None: diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 02da5ae..101b551 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -24,7 +24,7 @@ from time import time from copy import copy import weakref from tiramisu.error import (RequirementError, PropertiesOptionError, - ConstError) + ConstError, ConfigError) from tiramisu.i18n import _ @@ -328,6 +328,17 @@ class Settings(object): self.context = weakref.ref(context) self._p_ = storage + def _getcontext(self): + """context could be None, we need to test it + context is None only if all reference to `Config` object is deleted + (for example we delete a `Config` and we manipulate a reference to + old `SubConfig`, `Values`, `Multi` or `Settings`) + """ + context = self.context() + if context is None: + raise ConfigError(_('the context does not exist anymore')) + return context + #____________________________________________________________ # properties methods def __contains__(self, propname): @@ -357,7 +368,7 @@ class Settings(object): if opt is not None and _path is None: _path = self._get_path_by_opt(opt) self._p_.reset_properties(_path) - self.context().cfgimpl_reset_cache() + self._getcontext().cfgimpl_reset_cache() def _getproperties(self, opt=None, path=None, is_apply_req=True): if opt is None: @@ -410,7 +421,7 @@ class Settings(object): self._p_.reset_properties(path) else: self._p_.setproperties(path, properties) - self.context().cfgimpl_reset_cache() + self._getcontext().cfgimpl_reset_cache() #____________________________________________________________ def validate_properties(self, opt_or_descr, is_descr, is_write, path, @@ -458,7 +469,7 @@ class Settings(object): properties -= frozenset(('mandatory', 'frozen')) else: if 'mandatory' in properties and \ - not self.context().cfgimpl_get_values()._isempty( + not self._getcontext().cfgimpl_get_values()._isempty( opt_or_descr, value): properties.remove('mandatory') if is_write and 'everything_frozen' in self_properties: @@ -581,6 +592,7 @@ class Settings(object): # filters the callbacks calc_properties = set() + context = self._getcontext() for requires in opt._requires: for require in requires: option, expected, action, inverse, \ @@ -592,8 +604,7 @@ class Settings(object): " '{0}' with requirement on: " "'{1}'").format(path, reqpath)) try: - value = self.context()._getattr(reqpath, - force_permissive=True) + value = context._getattr(reqpath, force_permissive=True) except PropertiesOptionError as err: if not transitive: continue @@ -622,7 +633,7 @@ class Settings(object): :param opt: `Option`'s object :returns: path """ - return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt) + return self._getcontext().cfgimpl_get_description().impl_get_path_by_opt(opt) def get_modified_properties(self): return self._p_.get_modified_properties() diff --git a/tiramisu/value.py b/tiramisu/value.py index 8c50373..a02196a 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -46,13 +46,24 @@ class Values(object): # the storage type is dictionary or sqlite3 self._p_ = storage + def _getcontext(self): + """context could be None, we need to test it + context is None only if all reference to `Config` object is deleted + (for example we delete a `Config` and we manipulate a reference to + old `SubConfig`, `Values`, `Multi` or `Settings`) + """ + context = self.context() + if context is None: + raise ConfigError(_('the context does not exist anymore')) + return context + def _getdefault(self, opt): """ actually retrieves the default value :param opt: the `option.Option()` object """ - meta = self.context().cfgimpl_get_meta() + meta = self._getcontext().cfgimpl_get_meta() if meta is not None: value = meta.cfgimpl_get_values()[opt] else: @@ -105,11 +116,11 @@ class Values(object): if path is None: path = self._get_opt_path(opt) if self._p_.hasvalue(path): - setting = self.context().cfgimpl_get_settings() + context = self._getcontext() + setting = context.cfgimpl_get_settings() opt.impl_validate(opt.impl_getdefault(), - self.context(), - 'validator' in setting) - self.context().cfgimpl_reset_cache() + context, 'validator' in setting) + context.cfgimpl_reset_cache() if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master): for slave in opt.impl_get_master_slaves(): @@ -137,7 +148,7 @@ class Values(object): callback, callback_params = opt._callback if callback_params is None: callback_params = {} - return carry_out_calculation(opt, config=self.context(), + return carry_out_calculation(opt, config=self._getcontext(), callback=callback, callback_params=callback_params, index=index, max_len=max_len) @@ -151,7 +162,7 @@ class Values(object): if path is None: path = self._get_opt_path(opt) ntime = None - setting = self.context().cfgimpl_get_settings() + setting = self._getcontext().cfgimpl_get_settings() if 'cache' in setting and self._p_.hascache(path): if 'expire' in setting: ntime = int(time()) @@ -176,7 +187,8 @@ class Values(object): def _getitem(self, opt, path, validate, force_permissive, force_properties, validate_properties): # options with callbacks - setting = self.context().cfgimpl_get_settings() + context = self._getcontext() + setting = 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 @@ -196,7 +208,7 @@ class Values(object): if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave): masterp = self._get_opt_path(opt.impl_get_master_slaves()) - mastervalue = getattr(self.context(), masterp) + mastervalue = getattr(context, masterp) lenmaster = len(mastervalue) if lenmaster == 0: value = [] @@ -230,7 +242,7 @@ 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) + opt.impl_validate(value, 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) @@ -251,8 +263,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()) + context = self._getcontext() + opt.impl_validate(value, context, + 'validator' in 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, @@ -261,14 +274,15 @@ class Values(object): def _setvalue(self, opt, path, value, force_permissive=False, force_properties=None, is_write=True, validate_properties=True): - self.context().cfgimpl_reset_cache() + context = self._getcontext() + context.cfgimpl_reset_cache() if validate_properties: - setting = self.context().cfgimpl_get_settings() + setting = context.cfgimpl_get_settings() setting.validate_properties(opt, False, is_write, value=value, path=path, force_permissive=force_permissive, force_properties=force_properties) - owner = self.context().cfgimpl_get_settings().getowner() + owner = context.cfgimpl_get_settings().getowner() self._p_.setvalue(path, value, owner) def getowner(self, opt): @@ -285,7 +299,7 @@ class Values(object): def _getowner(self, path): owner = self._p_.getowner(path, owners.default) - meta = self.context().cfgimpl_get_meta() + meta = self._getcontext().cfgimpl_get_meta() if owner is owners.default and meta is not None: owner = meta.cfgimpl_get_values()._getowner(path) return owner @@ -337,7 +351,7 @@ class Values(object): :param opt: the `option.Option` object :returns: a string with points like "gc.dummy.my_option" """ - return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt) + return self._getcontext().cfgimpl_get_description().impl_get_path_by_opt(opt) # information def set_information(self, key, value): @@ -402,12 +416,24 @@ class Multi(list): self._valid_master(value) super(Multi, self).__init__(value) + def _getcontext(self): + """context could be None, we need to test it + context is None only if all reference to `Config` object is deleted + (for example we delete a `Config` and we manipulate a reference to + old `SubConfig`, `Values`, `Multi` or `Settings`) + """ + context = self.context() + if context is None: + raise ConfigError(_('the context does not exist anymore')) + return context + 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( + context = self._getcontext() + values = context.cfgimpl_get_values() + masterp = context.cfgimpl_get_description().impl_get_path_by_opt( self.opt.impl_get_master_slaves()) - mastervalue = getattr(self.context(), masterp) + mastervalue = getattr(context, masterp) masterlen = len(mastervalue) valuelen = len(value) is_default_owner = not values._is_default_owner(self.path) or setitem @@ -431,7 +457,7 @@ class Multi(list): def _valid_master(self, value): masterlen = len(value) - values = self.context().cfgimpl_get_values() + values = self._getcontext().cfgimpl_get_values() for slave in self.opt._master_slaves: path = values._get_opt_path(slave) if not values._is_default_owner(path): @@ -458,18 +484,19 @@ class Multi(list): self._validate(value, index) #assume not checking mandatory property super(Multi, self).__setitem__(index, value) - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) + self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self) def append(self, value=undefined, force=False): """the list value can be updated (appened) only if the option is a master """ + context = self._getcontext() if not force: if self.opt.impl_get_multitype() == multitypes.slave: raise SlaveError(_("cannot append a value on a multi option {0}" " which is a slave").format(self.opt._name)) elif self.opt.impl_get_multitype() == multitypes.master: - values = self.context().cfgimpl_get_values() + values = context.cfgimpl_get_values() if value is undefined and self.opt.impl_has_callback(): value = values._getcallback_value(self.opt) #Force None il return a list @@ -480,9 +507,9 @@ class Multi(list): value = self.opt.impl_getdefault_multi() self._validate(value, index) super(Multi, self).append(value) - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, - self, - validate_properties=not force) + 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) @@ -513,7 +540,7 @@ class Multi(list): super(Multi, self).sort(key=key, reverse=reverse) else: super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse) - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) + self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self) def reverse(self): if self.opt.impl_get_multitype() in [multitypes.slave, @@ -521,7 +548,7 @@ class Multi(list): raise SlaveError(_("cannot reverse multi option {0} if master or " "slave").format(self.opt._name)) super(Multi, self).reverse() - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) + self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self) def insert(self, index, obj): if self.opt.impl_get_multitype() in [multitypes.slave, @@ -529,7 +556,7 @@ class Multi(list): raise SlaveError(_("cannot insert multi option {0} if master or " "slave").format(self.opt._name)) super(Multi, self).insert(index, obj) - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) + self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self) def extend(self, iterable): if self.opt.impl_get_multitype() in [multitypes.slave, @@ -537,12 +564,12 @@ class Multi(list): raise SlaveError(_("cannot extend multi option {0} if master or " "slave").format(self.opt._name)) super(Multi, self).extend(iterable) - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) + self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self) def _validate(self, value, force_index): if value is not None: try: - self.opt.impl_validate(value, context=self.context(), + self.opt.impl_validate(value, context=self._getcontext(), force_index=force_index) except ValueError as err: raise ValueError(_("invalid value {0} " @@ -560,13 +587,14 @@ class Multi(list): :type force: boolean :returns: item at index """ + context = self._getcontext() if not force: if self.opt.impl_get_multitype() == multitypes.slave: raise SlaveError(_("cannot pop a value on a multi option {0}" " which is a slave").format(self.opt._name)) elif self.opt.impl_get_multitype() == multitypes.master: for slave in self.opt.impl_get_master_slaves(): - values = self.context().cfgimpl_get_values() + values = context.cfgimpl_get_values() if not values.is_default_owner(slave): #get multi without valid properties values.getitem(slave, @@ -574,5 +602,5 @@ class Multi(list): ).pop(index, force=True) #set value without valid properties ret = super(Multi, self).pop(index) - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force) + context.cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force) return ret From 77adc934e023dfcbc6f36ce3fb312656407407c2 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 25 Jan 2014 11:40:04 +0100 Subject: [PATCH 31/61] update translation --- translations/fr/tiramisu.po | 238 +++++++++++++++++++----------------- translations/tiramisu.pot | 205 +++++++++++++++---------------- 2 files changed, 226 insertions(+), 217 deletions(-) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index bff777c..d9bbc3e 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -1,31 +1,26 @@ msgid "" msgstr "" -"Project-Id-Version: \n" +"Project-Id-Version: Tiramisu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-09-30 22:49+CEST\n" +"POT-Creation-Date: 2014-01-25 11:30+CET\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" -"Language-Team: LANGUAGE \n" +"Language-Team: Tiramisu's team \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"X-Poedit-SourceCharset: UTF-8\n" -#: tiramisu/autolib.py:145 +#: tiramisu/autolib.py:159 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 -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}" - #: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "descr doit être une optiondescription pas un {0}" @@ -34,72 +29,77 @@ msgstr "descr doit être une optiondescription pas un {0}" msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:163 +#: tiramisu/config.py:166 tiramisu/setting.py:339 tiramisu/value.py:57 +#: tiramisu/value.py:427 +msgid "the context does not exist anymore" +msgstr "le context n'existe plus" + +#: tiramisu/config.py:171 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:197 msgid "can't assign to an OptionDescription" msgstr "ne peut pas attribuer une valeur à une OptionDescription" -#: tiramisu/config.py:320 +#: tiramisu/config.py:330 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:359 +#: tiramisu/config.py:369 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:419 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:440 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:490 +#: tiramisu/config.py:500 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:67 msgid "invalid name: {0} for option" msgstr "nom invalide : {0} pour l'option" -#: tiramisu/option.py:78 +#: tiramisu/option.py:76 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:116 +#: tiramisu/option.py:114 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:143 tiramisu/value.py:362 +#: tiramisu/option.py:141 tiramisu/value.py:376 msgid "information's item not found: {0}" msgstr "aucune config spécifié alors que c'est nécessaire" -#: tiramisu/option.py:205 +#: tiramisu/option.py:203 msgid "cannot serialize Option, only in OptionDescription" msgstr "ne peut serialiser une Option, seulement via une OptionDescription" -#: tiramisu/option.py:308 +#: tiramisu/option.py:306 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:314 +#: tiramisu/option.py:312 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:319 +#: tiramisu/option.py:317 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:322 +#: tiramisu/option.py:320 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" @@ -107,72 +107,72 @@ msgstr "" "params définis pour une fonction callback mais par de callback encore " "définis pour l'option {0}" -#: tiramisu/option.py:361 +#: tiramisu/option.py:359 msgid "option not in all_cons_opts" msgstr "option non présentante dans all_cons_opts" -#: tiramisu/option.py:427 tiramisu/option.py:437 +#: tiramisu/option.py:425 tiramisu/option.py:435 msgid "invalid value for option {0}: {1}" msgstr "valeur invalide pour l'option {0} : {1}" -#: tiramisu/option.py:454 -msgid ""invalid value {0} for option {1} which must be a list" +#: tiramisu/option.py:452 +msgid "invalid value {0} for option {1} which must be a list" msgstr "valeur invalide pour l'option {0} : {1} laquelle doit être une liste" -#: tiramisu/option.py:514 +#: tiramisu/option.py:508 msgid "consistency should be set with an option" msgstr "consistency doit être configuré avec une option" -#: tiramisu/option.py:516 +#: tiramisu/option.py:510 msgid "cannot add consistency with itself" msgstr "ne peut ajouter une consistency avec lui même" -#: tiramisu/option.py:518 +#: tiramisu/option.py:512 msgid "every options in consistency should be multi or none" msgstr "" "toutes les options d'une consistency devrait être multi ou ne pas l'être" -#: tiramisu/option.py:538 +#: tiramisu/option.py:532 msgid "same value for {0} and {1}" msgstr "même valeur pour {0} et {1}" -#: tiramisu/option.py:647 +#: tiramisu/option.py:641 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:650 +#: tiramisu/option.py:644 msgid "open_values must be a boolean for {0}" msgstr "open_values doit être un booléen pour {0}" -#: tiramisu/option.py:672 +#: tiramisu/option.py:666 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:684 +#: tiramisu/option.py:678 msgid "invalid boolean" msgstr "booléen invalide" -#: tiramisu/option.py:694 +#: tiramisu/option.py:688 msgid "invalid integer" msgstr "nombre invalide" -#: tiramisu/option.py:704 +#: tiramisu/option.py:698 msgid "invalid float" msgstr "invalide nombre flottan" -#: tiramisu/option.py:714 +#: tiramisu/option.py:708 msgid "invalid string" msgstr "invalide caractère" -#: tiramisu/option.py:731 +#: tiramisu/option.py:725 msgid "invalid unicode" msgstr "invalide unicode" -#: tiramisu/option.py:743 +#: tiramisu/option.py:737 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" -#: tiramisu/option.py:792 +#: tiramisu/option.py:787 tiramisu/option.py:792 msgid "invalid IP" msgstr "adresse IP invalide" @@ -209,12 +209,12 @@ msgid "invalid len for opts" msgstr "longueur invalide pour opts" #: tiramisu/option.py:927 -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" +msgid "invalid network {0} ({1}) with netmask {2}, this network is an IP" +msgstr "réseau invalide {0} ({1}) avec masque {2}, ce réseau est une IP" #: tiramisu/option.py:932 -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" +msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" +msgstr "IP invalide {0} ({1}) avec masque {2}, cette IP est un réseau" #: tiramisu/option.py:937 msgid "invalid IP {0} ({1}) with netmask {2}" @@ -250,107 +250,108 @@ msgstr "allow_ip doit être un booléen" msgid "allow_without_dot must be a boolean" msgstr "allow_without_dot doit être un booléen" -#: tiramisu/option.py:1024 +#: tiramisu/option.py:1028 msgid "invalid domainname, must have dot" msgstr "nom de domaine invalide, doit avoir un point" -#: tiramisu/option.py:1026 +#: tiramisu/option.py:1030 msgid "invalid domainname's length (max 255)" msgstr "longueur du nom de domaine invalide (maximum {1})" -#: tiramisu/option.py:1028 +#: tiramisu/option.py:1032 msgid "invalid domainname's length (min 2)" msgstr "longueur du nom de domaine invalide (minimum 2)" -#: tiramisu/option.py:1030 +#: tiramisu/option.py:1034 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:1049 +#: tiramisu/option.py:1047 msgid "invalid email address, should contains one @" msgstr "adresse email invalide, devrait contenir un @" -#: tiramisu/option.py:1052 +#: tiramisu/option.py:1050 msgid "invalid username in email address" msgstr "nom d'utilisateur invalide dans une adresse email" -#: tiramisu/option.py:1071 +#: tiramisu/option.py:1063 msgid "invalid url, should start with http:// or https://" msgstr "URL invalide, devrait démarré avec http:// ou https://" -#: tiramisu/option.py:1090 +#: tiramisu/option.py:1082 msgid "invalid url, port must be an between 0 and 65536" msgstr "URL invalide, port doit être entre 0 et 65536" -#: tiramisu/option.py:1096 -#, fuzzy +#: tiramisu/option.py:1088 msgid "invalid url, should ends with filename" msgstr "URL invalide, devrait finir avec un nom de fichier" -#: tiramisu/option.py:1107 +#: tiramisu/option.py:1099 msgid "invalid filename" msgstr "nom de fichier invalide" -#: tiramisu/option.py:1134 +#: tiramisu/option.py:1126 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:1152 +#: tiramisu/option.py:1144 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:1203 +#: tiramisu/option.py:1195 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:1233 +#: tiramisu/option.py:1225 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:1241 +#: tiramisu/option.py:1233 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:1247 +#: tiramisu/option.py:1239 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:1257 +#: tiramisu/option.py:1249 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:1270 +#: tiramisu/option.py:1261 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:1273 +#: tiramisu/option.py:1264 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:1276 +#: tiramisu/option.py:1267 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:1287 +#: tiramisu/option.py:1277 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:1296 -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:1285 +msgid "callback of master's option shall not refered a slave's ones" +msgstr "" +"callback d'une variable maitre ne devrait pas référencer des variables " +"esclaves" -#: tiramisu/option.py:1299 +#: tiramisu/option.py:1293 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:1391 +#: tiramisu/option.py:1385 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:1408 +#: tiramisu/option.py:1402 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -358,66 +359,66 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, expected et action" -#: tiramisu/option.py:1413 +#: tiramisu/option.py:1407 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:1417 +#: tiramisu/option.py:1411 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:1421 +#: tiramisu/option.py:1415 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:1425 +#: tiramisu/option.py:1419 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:1428 +#: tiramisu/option.py:1422 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:1434 +#: tiramisu/option.py:1428 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:1439 +#: tiramisu/option.py:1433 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:1464 +#: tiramisu/option.py:1458 msgid "{0} should be a function" msgstr "{0} doit être une fonction" -#: tiramisu/option.py:1467 +#: tiramisu/option.py:1461 msgid "{0}_params should be a dict" msgstr "{0}_params devrait être un dict" -#: tiramisu/option.py:1470 +#: tiramisu/option.py:1464 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:1474 +#: tiramisu/option.py:1468 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" -#: tiramisu/option.py:1480 +#: tiramisu/option.py:1474 msgid "validator not support tuple" msgstr "validator n'accepte pas de tuple" -#: tiramisu/option.py:1483 +#: tiramisu/option.py:1477 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:1487 +#: tiramisu/option.py:1481 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" @@ -429,39 +430,39 @@ msgstr "ne peut redéfinir ({0})" msgid "can't unbind {0}" msgstr "ne peut supprimer ({0})" -#: tiramisu/setting.py:259 +#: tiramisu/setting.py:272 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:363 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:378 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:483 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:489 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:507 msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:471 tiramisu/value.py:301 +#: tiramisu/setting.py:514 tiramisu/value.py:315 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" -#: tiramisu/setting.py:558 +#: tiramisu/setting.py:602 msgid "" "malformed requirements imbrication detected for option: '{0}' with " "requirement on: '{1}'" @@ -469,7 +470,7 @@ msgstr "" "imbrication de requirements mal formés detectée pour l'option : '{0}' avec " "requirement sur : '{1}'" -#: tiramisu/setting.py:570 +#: tiramisu/setting.py:613 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}" @@ -494,52 +495,62 @@ 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:308 +#: tiramisu/value.py:322 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:442 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:466 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:496 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:535 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:539 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:548 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:556 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:564 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:547 +#: tiramisu/value.py:575 msgid "invalid value {0} for option {1}: {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}" -#: tiramisu/value.py:564 +#: tiramisu/value.py:593 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 "" +#~ "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}" + +#~ msgid "no child has same nom has master group for: {0}" +#~ msgstr "pas d'enfant avec le nom du groupe maître pour {0} " + #~ msgid "value must be a boolean" #~ msgstr "valeur doit être un booléen" @@ -555,9 +566,6 @@ msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave" #~ msgid "value must be an unicode" #~ msgstr "valeur doit être une valeur unicode" -#~ 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}" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 89808a9..e572a40 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-30 22:49+CEST\n" +"POT-Creation-Date: 2014-01-25 11:30+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,14 +15,10 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/autolib.py:145 +#: tiramisu/autolib.py:159 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" -#: 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:52 msgid "descr must be an optiondescription, not {0}" msgstr "" @@ -31,135 +27,140 @@ msgstr "" msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:163 +#: tiramisu/config.py:166 tiramisu/setting.py:339 tiramisu/value.py:57 +#: tiramisu/value.py:427 +msgid "the context does not exist anymore" +msgstr "" + +#: tiramisu/config.py:171 msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:189 +#: tiramisu/config.py:197 msgid "can't assign to an OptionDescription" msgstr "" -#: tiramisu/config.py:320 +#: tiramisu/config.py:330 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:359 +#: tiramisu/config.py:369 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:409 +#: tiramisu/config.py:419 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:430 +#: tiramisu/config.py:440 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:490 +#: tiramisu/config.py:500 msgid "opt in getowner must be an option not {0}" msgstr "" -#: tiramisu/option.py:69 +#: tiramisu/option.py:67 msgid "invalid name: {0} for option" msgstr "" -#: tiramisu/option.py:78 +#: tiramisu/option.py:76 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "" -#: tiramisu/option.py:116 +#: tiramisu/option.py:114 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option.py:143 tiramisu/value.py:362 +#: tiramisu/option.py:141 tiramisu/value.py:376 msgid "information's item not found: {0}" msgstr "" -#: tiramisu/option.py:205 +#: tiramisu/option.py:203 msgid "cannot serialize Option, only in OptionDescription" msgstr "" -#: tiramisu/option.py:308 +#: tiramisu/option.py:306 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -#: tiramisu/option.py:314 +#: tiramisu/option.py:312 msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:319 +#: tiramisu/option.py:317 msgid "default value not allowed if option: {0} is calculated" msgstr "" -#: tiramisu/option.py:322 +#: tiramisu/option.py:320 msgid "params defined for a callback function but no callback defined yet for option {0}" msgstr "" -#: tiramisu/option.py:361 +#: tiramisu/option.py:359 msgid "option not in all_cons_opts" msgstr "" -#: tiramisu/option.py:427 tiramisu/option.py:437 +#: tiramisu/option.py:425 tiramisu/option.py:435 msgid "invalid value for option {0}: {1}" msgstr "" -#: tiramisu/option.py:454 -msgid "which must be a list" +#: tiramisu/option.py:452 +msgid "invalid value {0} for option {1} which must be a list" msgstr "" -#: tiramisu/option.py:514 +#: tiramisu/option.py:508 msgid "consistency should be set with an option" msgstr "" -#: tiramisu/option.py:516 +#: tiramisu/option.py:510 msgid "cannot add consistency with itself" msgstr "" -#: tiramisu/option.py:518 +#: tiramisu/option.py:512 msgid "every options in consistency should be multi or none" msgstr "" -#: tiramisu/option.py:538 +#: tiramisu/option.py:532 msgid "same value for {0} and {1}" msgstr "" -#: tiramisu/option.py:647 +#: tiramisu/option.py:641 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:650 +#: tiramisu/option.py:644 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:672 +#: tiramisu/option.py:666 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:684 +#: tiramisu/option.py:678 msgid "invalid boolean" msgstr "" -#: tiramisu/option.py:694 +#: tiramisu/option.py:688 msgid "invalid integer" msgstr "" -#: tiramisu/option.py:704 +#: tiramisu/option.py:698 msgid "invalid float" msgstr "" -#: tiramisu/option.py:714 +#: tiramisu/option.py:708 msgid "invalid string" msgstr "" -#: tiramisu/option.py:731 +#: tiramisu/option.py:725 msgid "invalid unicode" msgstr "" -#: tiramisu/option.py:743 +#: tiramisu/option.py:737 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option.py:792 +#: tiramisu/option.py:787 tiramisu/option.py:792 msgid "invalid IP" msgstr "" @@ -196,11 +197,11 @@ msgid "invalid len for opts" msgstr "" #: tiramisu/option.py:927 -msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" +msgid "invalid network {0} ({1}) with netmask {2}, this network is an IP" msgstr "" #: tiramisu/option.py:932 -msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" +msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" msgstr "" #: tiramisu/option.py:937 @@ -235,159 +236,159 @@ msgstr "" msgid "allow_without_dot must be a boolean" msgstr "" -#: tiramisu/option.py:1024 +#: tiramisu/option.py:1028 msgid "invalid domainname, must have dot" msgstr "" -#: tiramisu/option.py:1026 +#: tiramisu/option.py:1030 msgid "invalid domainname's length (max 255)" msgstr "" -#: tiramisu/option.py:1028 +#: tiramisu/option.py:1032 msgid "invalid domainname's length (min 2)" msgstr "" -#: tiramisu/option.py:1030 +#: tiramisu/option.py:1034 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:1049 +#: tiramisu/option.py:1047 msgid "invalid email address, should contains one @" msgstr "" -#: tiramisu/option.py:1052 +#: tiramisu/option.py:1050 msgid "invalid username in email address" msgstr "" -#: tiramisu/option.py:1071 +#: tiramisu/option.py:1063 msgid "invalid url, should start with http:// or https://" msgstr "" -#: tiramisu/option.py:1090 +#: tiramisu/option.py:1082 msgid "invalid url, port must be an between 0 and 65536" msgstr "" -#: tiramisu/option.py:1096 +#: tiramisu/option.py:1088 msgid "invalid url, should ends with filename" msgstr "" -#: tiramisu/option.py:1107 +#: tiramisu/option.py:1099 msgid "invalid filename" msgstr "" -#: tiramisu/option.py:1134 +#: tiramisu/option.py:1126 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:1152 +#: tiramisu/option.py:1144 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:1203 +#: tiramisu/option.py:1195 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:1233 +#: tiramisu/option.py:1225 msgid "consistency with option {0} which is not in Config" msgstr "" -#: tiramisu/option.py:1241 +#: tiramisu/option.py:1233 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:1247 +#: tiramisu/option.py:1239 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:1257 +#: tiramisu/option.py:1249 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:1270 +#: tiramisu/option.py:1261 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:1273 +#: tiramisu/option.py:1264 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:1276 +#: tiramisu/option.py:1267 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:1287 +#: tiramisu/option.py:1277 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:1296 -msgid "no child has same nom has master group for: {0}" +#: tiramisu/option.py:1285 +msgid "callback of master's option shall not refered a slave's ones" msgstr "" -#: tiramisu/option.py:1299 +#: tiramisu/option.py:1293 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1391 +#: tiramisu/option.py:1385 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1408 +#: tiramisu/option.py:1402 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1413 +#: tiramisu/option.py:1407 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1417 +#: tiramisu/option.py:1411 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1421 +#: tiramisu/option.py:1415 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1425 +#: tiramisu/option.py:1419 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1428 +#: tiramisu/option.py:1422 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1434 +#: tiramisu/option.py:1428 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1439 +#: tiramisu/option.py:1433 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "" -#: tiramisu/option.py:1464 +#: tiramisu/option.py:1458 msgid "{0} should be a function" msgstr "" -#: tiramisu/option.py:1467 +#: tiramisu/option.py:1461 msgid "{0}_params should be a dict" msgstr "" -#: tiramisu/option.py:1470 +#: tiramisu/option.py:1464 msgid "{0}_params with key {1} should not have length different to 1" msgstr "" -#: tiramisu/option.py:1474 +#: tiramisu/option.py:1468 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "" -#: tiramisu/option.py:1480 +#: tiramisu/option.py:1474 msgid "validator not support tuple" msgstr "" -#: tiramisu/option.py:1483 +#: tiramisu/option.py:1477 msgid "{0}_params should have an option not a {0} for first argument" msgstr "" -#: tiramisu/option.py:1487 +#: tiramisu/option.py:1481 msgid "{0}_params should have a boolean not a {0} for second argument" msgstr "" @@ -399,39 +400,39 @@ msgstr "" msgid "can't unbind {0}" msgstr "" -#: tiramisu/setting.py:259 +#: tiramisu/setting.py:272 msgid "cannot append {0} property for option {1}: this property is calculated" msgstr "" -#: tiramisu/setting.py:322 +#: tiramisu/setting.py:363 msgid "opt and all_properties must not be set together in reset" msgstr "" -#: tiramisu/setting.py:337 +#: tiramisu/setting.py:378 msgid "if opt is not None, path should not be None in _getproperties" msgstr "" -#: tiramisu/setting.py:440 +#: tiramisu/setting.py:483 msgid "cannot change the value for option {0} this option is frozen" msgstr "" -#: tiramisu/setting.py:446 +#: tiramisu/setting.py:489 msgid "trying to access to an option named: {0} with properties {1}" msgstr "" -#: tiramisu/setting.py:464 +#: tiramisu/setting.py:507 msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:471 tiramisu/value.py:301 +#: tiramisu/setting.py:514 tiramisu/value.py:315 msgid "invalid generic owner {0}" msgstr "" -#: tiramisu/setting.py:558 +#: tiramisu/setting.py:602 msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" msgstr "" -#: tiramisu/setting.py:570 +#: tiramisu/setting.py:613 msgid "option '{0}' has requirement's property error: {1} {2}" msgstr "" @@ -455,47 +456,47 @@ msgstr "" msgid "a dictionary cannot be persistent" msgstr "" -#: tiramisu/value.py:308 +#: tiramisu/value.py:322 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:416 +#: tiramisu/value.py:442 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:440 +#: tiramisu/value.py:466 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" -#: tiramisu/value.py:470 +#: tiramisu/value.py:496 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:507 +#: tiramisu/value.py:535 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:511 +#: tiramisu/value.py:539 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:520 +#: tiramisu/value.py:548 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:528 +#: tiramisu/value.py:556 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:536 +#: tiramisu/value.py:564 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:547 +#: tiramisu/value.py:575 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/value.py:564 +#: tiramisu/value.py:593 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "" From 9deb67f88786a670fdfa33ee5e9c5bc6b5cf5668 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 27 Jan 2014 14:55:53 +0100 Subject: [PATCH 32/61] warning and error messages not translated in the tests --- test/test_option_validator.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 9a39816..dc08c82 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -6,6 +6,7 @@ from tiramisu.config import Config from tiramisu.option import StrOption, OptionDescription from tiramisu.setting import groups from tiramisu.error import ValueWarning +from tiramisu.i18n import _ def return_true(value, param=None): @@ -87,7 +88,7 @@ def test_validator_warning(): cfg.opt2 = 'val' assert len(w) == 1 assert w[0].message.opt == opt2 - assert str(w[0].message) == 'invalid value for option opt2: error' + assert str(w[0].message) == _('invalid value for option {0}: {1}').format('opt2', 'error') # with warnings.catch_warnings(record=True) as w: cfg.opt3.append('val') @@ -97,7 +98,7 @@ def test_validator_warning(): cfg.opt3.append('val1') assert len(w) == 1 assert w[0].message.opt == opt3 - assert str(w[0].message) == 'invalid value for option opt3: error' + assert str(w[0].message) == _('invalid value for option {0}: {1}').format('opt3', 'error') raises(ValueError, "cfg.opt2 = 1") # with warnings.catch_warnings(record=True) as w: @@ -105,9 +106,9 @@ def test_validator_warning(): cfg.opt3.append('val') assert len(w) == 2 assert w[0].message.opt == opt2 - assert str(w[0].message) == 'invalid value for option opt2: error' + assert str(w[0].message) == _('invalid value for option {0}: {1}').format('opt2', 'error') assert w[1].message.opt == opt3 - assert str(w[1].message) == 'invalid value for option opt3: error' + assert str(w[1].message) == _('invalid value for option {0}: {1}').format('opt3', 'error') def test_validator_warning_master_slave(): @@ -127,29 +128,29 @@ def test_validator_warning_master_slave(): 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 for option netmask_admin_eth0: error' + assert str(w[0].message) == _('invalid value for option {0}: {1}').format('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 for option ip_admin_eth0: error' + assert str(w[0].message) == _('invalid value for option {0}: {1}').format('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 for option ip_admin_eth0: error' + assert str(w[0].message) == _('invalid value for option {0}: {1}').format('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 for option ip_admin_eth0: error' + assert str(w[0].message) == _('invalid value for option {0}: {1}').format('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 for option ip_admin_eth0: error' + assert str(w[0].message) == _('invalid value for option {0}: {1}').format('ip_admin_eth0', 'error') From f48f3e754402169136b4beedf80c745f587226c9 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 1 Feb 2014 16:49:16 +0100 Subject: [PATCH 33/61] add tests for find() --- test/test_config_api.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/test/test_config_api.py b/test/test_config_api.py index f0681ea..7489766 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -13,17 +13,20 @@ def make_description(): objspaceoption = ChoiceOption('objspace', 'Object space', ('std', 'thunk'), 'std') booloption = BoolOption('bool', 'Test boolean option', default=True) + booloption2 = BoolOption('bool', 'Test boolean option', default=True) intoption = IntOption('int', 'Test int option', default=0) + floatoption2 = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3) stroption = StrOption('str', 'Test string option', default="abc") boolop = BoolOption('boolop', 'Test boolean option op', default=True) wantref_option = BoolOption('wantref', 'Tests', default=False) wantframework_option = BoolOption('wantframework', 'Test', default=False) - gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) + gcgroup2 = OptionDescription('gc2', '', [booloption2]) + gcgroup = OptionDescription('gc', '', [gcgroup2, gcoption, gcdummy, floatoption]) descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption, wantref_option, stroption, wantframework_option, - intoption, boolop]) + intoption, boolop, floatoption2]) return descr @@ -101,7 +104,10 @@ def test_find_in_config(): descr = make_description() conf = Config(descr) assert conf.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')] + assert conf.find(byname='float') == [conf.unwrap_from_path('gc.float'), conf.unwrap_from_path('float')] + assert conf.find_first(byname='bool') == conf.unwrap_from_path('gc.gc2.bool') assert conf.find_first(byname='dummy') == conf.unwrap_from_path('gc.dummy') + assert conf.find_first(byname='float') == conf.unwrap_from_path('gc.float') assert conf.find(bytype=ChoiceOption) == [conf.unwrap_from_path('gc.name'), conf.unwrap_from_path('objspace')] assert conf.find_first(bytype=ChoiceOption) == conf.unwrap_from_path('gc.name') assert conf.find(byvalue='ref') == [conf.unwrap_from_path('gc.name')] @@ -111,9 +117,14 @@ def test_find_in_config(): assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_path('gc.dummy') assert conf.find(byvalue=False, byname='dummy') == [conf.unwrap_from_path('gc.dummy')] assert conf.find_first(byvalue=False, byname='dummy') == conf.unwrap_from_path('gc.dummy') - ## byattrs - #assert conf.find_first(byattrs= dict(default=2.3)) == conf.unwrap_from_path('gc.float') - #assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_path('gc.dummy') + #subconfig + assert conf.gc.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')] + assert conf.gc.find(byname='float') == [conf.unwrap_from_path('gc.float')] + assert conf.gc.find(byname='bool') == [conf.unwrap_from_path('gc.gc2.bool')] + raises(AttributeError, "conf.gc.find(byname='wantref').first()") + # not OptionDescription + raises(AttributeError, "conf.find_first(byname='gc')") + raises(AttributeError, "conf.gc.find_first(byname='gc2')") def test_find_multi(): From 71b235551eca7335c5f9a1fc5e5dc5efd8472a4e Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 1 Feb 2014 17:25:31 +0100 Subject: [PATCH 34/61] add tests for find() --- test/test_config_api.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/test/test_config_api.py b/test/test_config_api.py index 7489766..db40c57 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -10,10 +10,12 @@ from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ def make_description(): gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False) + prop = BoolOption('prop', '', properties=('disabled',)) + prop2 = BoolOption('prop', '', properties=('hidden',)) objspaceoption = ChoiceOption('objspace', 'Object space', ('std', 'thunk'), 'std') booloption = BoolOption('bool', 'Test boolean option', default=True) - booloption2 = BoolOption('bool', 'Test boolean option', default=True) + booloption2 = BoolOption('bool', 'Test boolean option', default=False) intoption = IntOption('int', 'Test int option', default=0) floatoption2 = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3) @@ -21,8 +23,8 @@ def make_description(): boolop = BoolOption('boolop', 'Test boolean option op', default=True) wantref_option = BoolOption('wantref', 'Tests', default=False) wantframework_option = BoolOption('wantframework', 'Test', default=False) - gcgroup2 = OptionDescription('gc2', '', [booloption2]) - gcgroup = OptionDescription('gc', '', [gcgroup2, gcoption, gcdummy, floatoption]) + gcgroup2 = OptionDescription('gc2', '', [booloption2, prop]) + gcgroup = OptionDescription('gc', '', [gcgroup2, gcoption, gcdummy, floatoption, prop2]) descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption, wantref_option, stroption, wantframework_option, @@ -103,15 +105,22 @@ def test_find_in_config(): "finds option in config" descr = make_description() conf = Config(descr) + conf.read_only() assert conf.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')] assert conf.find(byname='float') == [conf.unwrap_from_path('gc.float'), conf.unwrap_from_path('float')] assert conf.find_first(byname='bool') == conf.unwrap_from_path('gc.gc2.bool') + assert conf.find_first(byname='bool', byvalue=True) == conf.unwrap_from_path('bool') assert conf.find_first(byname='dummy') == conf.unwrap_from_path('gc.dummy') assert conf.find_first(byname='float') == conf.unwrap_from_path('gc.float') assert conf.find(bytype=ChoiceOption) == [conf.unwrap_from_path('gc.name'), conf.unwrap_from_path('objspace')] assert conf.find_first(bytype=ChoiceOption) == conf.unwrap_from_path('gc.name') assert conf.find(byvalue='ref') == [conf.unwrap_from_path('gc.name')] assert conf.find_first(byvalue='ref') == conf.unwrap_from_path('gc.name') + assert conf.find(byname='prop') == [conf.unwrap_from_path('gc.prop')] + conf.read_write() + raises(AttributeError, "assert conf.find(byname='prop')") + assert conf.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.gc2.prop'), conf.unwrap_from_path('gc.prop')] + #assert conf.find_first(byname='prop') == conf.unwrap_from_path('gc.prop') # combinaison of filters assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_path('gc.dummy')] assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_path('gc.dummy') @@ -121,7 +130,12 @@ def test_find_in_config(): assert conf.gc.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')] assert conf.gc.find(byname='float') == [conf.unwrap_from_path('gc.float')] assert conf.gc.find(byname='bool') == [conf.unwrap_from_path('gc.gc2.bool')] + assert conf.gc.find_first(byname='bool', byvalue=False) == conf.unwrap_from_path('gc.gc2.bool') + raises(AttributeError, "assert conf.gc.find_first(byname='bool', byvalue=True)") raises(AttributeError, "conf.gc.find(byname='wantref').first()") + assert conf.gc.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.gc2.prop'), conf.unwrap_from_path('gc.prop')] + conf.read_only() + assert conf.gc.find(byname='prop') == [conf.unwrap_from_path('gc.prop')] # not OptionDescription raises(AttributeError, "conf.find_first(byname='gc')") raises(AttributeError, "conf.gc.find_first(byname='gc2')") From 683e40fbb5143190932edbf0fe03a7ee3464b341 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 2 Feb 2014 18:20:01 +0100 Subject: [PATCH 35/61] when change len of calculated master, change len of slave too --- test/test_option_calculation.py | 63 ++++++++++++++++++++++ test/test_state.py | 43 +++++++++++++++ tiramisu/autolib.py | 5 +- tiramisu/value.py | 95 ++++++++++++++++----------------- 4 files changed, 157 insertions(+), 49 deletions(-) diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 91d61a4..89f9291 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -373,6 +373,9 @@ def test_callback_multi_value(): assert cfg.val2 == ['val'] assert cfg.val3 == ['yes'] assert cfg.val4 == ['val', 'yes'] + cfg.val2.append('new') + assert cfg.val1 == ['val'] + assert cfg.val2 == ['val', 'new'] def test_callback_multi_list(): @@ -466,6 +469,66 @@ def test_callback_master_and_slaves_slave(): assert cfg.val1.val2 == ['val2', 'val2', 'val'] +def test_callback_master_and_slaves_slave_cal(): + val3 = StrOption('val3', "", multi=True) + val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val3, False),)}) + val2 = StrOption('val2', "", multi=True, callback=return_val) + interface1 = OptionDescription('val1', '', [val1, val2]) + interface1.impl_set_group_type(groups.master) + maconfig = OptionDescription('rootconfig', '', [interface1, val3]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val3 == [] + assert cfg.val1.val1 == [] + assert cfg.val1.val2 == [] + cfg.val1.val1 = ['val1'] + cfg.val3 = ['val1'] + assert cfg.val1.val1 == ['val1'] + assert cfg.val1.val2 == ['val'] + assert cfg.val1.val1 == ['val1'] + assert cfg.val1.val2 == ['val'] + del(cfg.val1.val1) + cfg.val1.val2 = ['val'] + cfg.val3 = ['val1', 'val2'] + assert cfg.val1.val2 == ['val', 'val'] + assert cfg.val1.val1 == ['val1', 'val2'] + cfg.val1.val2 = ['val1', 'val2'] + cfg.val3.pop(1) + # cannot remove slave's value because master is calculated + # so raise + raises(SlaveError, "cfg.val1.val1") + raises(SlaveError, "cfg.val1.val2") + cfg.val3 = ['val1', 'val2', 'val3'] + assert cfg.val1.val2 == ['val1', 'val2', 'val'] + + +def test_callback_master_and_slaves_slave_cal2(): + val3 = StrOption('val3', "", ['val', 'val'], multi=True) + val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val3, False),)}) + val2 = StrOption('val2', "", ['val2', 'val2'], multi=True) + interface1 = OptionDescription('val1', '', [val1, val2]) + interface1.impl_set_group_type(groups.master) + maconfig = OptionDescription('rootconfig', '', [interface1, val3]) + cfg = Config(maconfig) + cfg.read_write() + assert cfg.val3 == ['val', 'val'] + assert cfg.val1.val1 == ['val', 'val'] + assert cfg.val1.val2 == ['val2', 'val2'] + cfg.val3.pop(1) +# # cannot remove slave's value because master is calculated +# # so raise + raises(SlaveError, "cfg.val1.val1") + raises(SlaveError, "cfg.val1.val2") + cfg.val3 = ['val', 'val'] + assert cfg.val3 == ['val', 'val'] + assert cfg.val1.val1 == ['val', 'val'] + assert cfg.val1.val2 == ['val2', 'val2'] + raises(SlaveError, "cfg.val1.val1 = ['val']") + assert cfg.val3 == ['val', 'val'] + assert cfg.val1.val1 == ['val', 'val'] + assert cfg.val1.val2 == ['val2', 'val2'] + + def test_callback_master_and_slaves_slave_list(): val1 = StrOption('val1', "", multi=True) val2 = StrOption('val2', "", multi=True, callback=return_list) diff --git a/test/test_state.py b/test/test_state.py index ef46ce2..7693cb6 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -249,3 +249,46 @@ def test_state_values_owner(): delete_session('29090931') except ConfigError: pass + + +def test_state_unkown_setting_owner(): + """load an unknow _owner, should create it""" + assert not 'supernewuser' in owners.__dict__ + loads("""ccopy_reg +_reconstructor +p0 +(ctiramisu.setting +Settings +p1 +c__builtin__ +object +p2 +Ntp3 +Rp4 +(dp5 +S'_owner' +p6 +S'supernewuser' +p7 +sS'_p_' +p8 +g0 +(ctiramisu.storage.dictionary.setting +Settings +p9 +g2 +Ntp10 +Rp11 +(dp12 +S'_cache' +p13 +(dp14 +sS'_permissives' +p15 +(dp16 +sS'_properties' +p17 +(dp18 +sbsb. +.""") + assert 'supernewuser' in owners.__dict__ diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index ffdbb0a..cb3552a 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -152,7 +152,10 @@ def carry_out_calculation(option, config, callback, callback_params, opt) # get value try: - value = config._getattr(path, force_permissive=True) + value = config._getattr(path, force_permissive=True, validate=False) + # convert to list, not modifie this multi + if value.__class__.__name__ == 'Multi': + value = list(value) except PropertiesOptionError as err: if force_permissive: continue diff --git a/tiramisu/value.py b/tiramisu/value.py index a02196a..863f2a1 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -73,7 +73,7 @@ class Values(object): else: return value - def _getvalue(self, opt, path, validate=True): + def _getvalue(self, opt, path): """actually retrieves the value :param opt: the `option.Option()` object @@ -82,14 +82,9 @@ class Values(object): if not self._p_.hasvalue(path): # if there is no value value = self._getdefault(opt) - if opt.impl_is_multi(): - value = Multi(value, self.context, opt, path, validate) else: # if there is a value value = self._p_.getvalue(path) - if opt.impl_is_multi() and not isinstance(value, Multi): - # load value so don't need to validate if is not a Multi - value = Multi(value, self.context, opt, path, validate=False) return value def get_modified_values(self): @@ -198,7 +193,7 @@ class Values(object): # ConfigError if properties did not raise. config_error = None force_permissives = None - # if value is callback and is not set + # if value has callback and is not set # or frozen with force_default_on_freeze if opt.impl_has_callback() and ( self._is_default_owner(path) or @@ -208,7 +203,7 @@ class Values(object): if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.slave): masterp = self._get_opt_path(opt.impl_get_master_slaves()) - mastervalue = getattr(context, masterp) + mastervalue = context._getattr(masterp, validate=validate) lenmaster = len(mastervalue) if lenmaster == 0: value = [] @@ -240,7 +235,10 @@ class Values(object): if opt.impl_is_multi(): value = Multi(value, self.context, opt, path, validate) else: - value = self._getvalue(opt, path, validate) + value = self._getvalue(opt, path) + if opt.impl_is_multi(): + # load value so don't need to validate if is not a Multi + value = Multi(value, self.context, opt, path, validate=validate) if config_error is None and validate: opt.impl_validate(value, context, 'validator' in setting) if config_error is None and self._is_default_owner(path) and \ @@ -266,10 +264,27 @@ class Values(object): context = self._getcontext() opt.impl_validate(value, context, 'validator' in context.cfgimpl_get_settings()) - if opt.impl_is_multi() and not isinstance(value, Multi): + if opt.impl_is_multi(): value = Multi(value, self.context, opt, path, setitem=True) + # Save old value + if opt.impl_get_multitype() == multitypes.master and \ + self._p_.hasvalue(path): + old_value = self._p_.getvalue(path) + old_owner = self._p_.getowner(path, None) + else: + old_value = undefined + old_owner = undefined self._setvalue(opt, path, value, force_permissive=force_permissive, is_write=is_write) + if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master: + try: + value._valid_master() + except Exception, err: + if old_value is not undefined: + self._p_.setvalue(path, old_value, old_owner) + else: + self._p_.resetvalue(path) + raise err def _setvalue(self, opt, path, value, force_permissive=False, force_properties=None, @@ -283,6 +298,8 @@ class Values(object): force_permissive=force_permissive, force_properties=force_properties) owner = context.cfgimpl_get_settings().getowner() + if isinstance(value, Multi): + value = list(value) self._p_.setvalue(path, value, owner) def getowner(self, opt): @@ -403,6 +420,8 @@ class Multi(list): :param opt: the option object that have this Multi value :param setitem: only if set a value """ + if isinstance(value, Multi): + raise ValueError(_('{0} is already a Multi ').format(opt._name)) self.opt = opt self.path = path if not isinstance(context, weakref.ReferenceType): @@ -412,8 +431,9 @@ class Multi(list): value = [value] if validate and self.opt.impl_get_multitype() == multitypes.slave: value = self._valid_slave(value, setitem) - elif validate and self.opt.impl_get_multitype() == multitypes.master: - self._valid_master(value) + elif not setitem and validate and \ + self.opt.impl_get_multitype() == multitypes.master: + self._valid_master() super(Multi, self).__init__(value) def _getcontext(self): @@ -433,12 +453,10 @@ class Multi(list): values = context.cfgimpl_get_values() masterp = context.cfgimpl_get_description().impl_get_path_by_opt( self.opt.impl_get_master_slaves()) - mastervalue = getattr(context, masterp) + mastervalue = context._getattr(masterp, validate=False) 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 - is_default_owner): + if valuelen > masterlen or (valuelen < masterlen and setitem): raise SlaveError(_("invalid len for the slave: {0}" " which has {1} as master").format( self.opt._name, masterp)) @@ -455,30 +473,12 @@ class Multi(list): #else: same len so do nothing return value - def _valid_master(self, value): - masterlen = len(value) + def _valid_master(self): + #masterlen = len(value) values = self._getcontext().cfgimpl_get_values() for slave in self.opt._master_slaves: path = values._get_opt_path(slave) - if not values._is_default_owner(path): - value_slave = values._getvalue(slave, path) - if len(value_slave) > masterlen: - raise SlaveError(_("invalid len for the master: {0}" - " which has {1} as slave with" - " greater len").format( - self.opt._name, slave._name)) - elif len(value_slave) < masterlen: - for num in range(0, masterlen - len(value_slave)): - 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(undefined, force=True) + Multi(values._getvalue(slave, path), self.context, slave, path) def __setitem__(self, index, value): self._validate(value, index) @@ -518,16 +518,15 @@ class Multi(list): dvalue = values._getcallback_value(slave, index=index) else: dvalue = slave.impl_getdefault_multi() - old_value = values.getitem(slave, path, + old_value = values.getitem(slave, path, validate=False, validate_properties=False) - if len(old_value) < self.__len__(): - values.getitem(slave, path, - validate_properties=False).append( - dvalue, force=True) - else: - values.getitem(slave, path, - validate_properties=False)[ - index] = dvalue + if len(old_value) + 1 != self.__len__(): + raise SlaveError(_("invalid len for the slave: {0}" + " which has {1} as master").format( + self.opt._name, self.__len__())) + values.getitem(slave, path, validate=False, + validate_properties=False).append( + dvalue, force=True) def sort(self, cmp=None, key=None, reverse=False): if self.opt.impl_get_multitype() in [multitypes.slave, @@ -592,12 +591,12 @@ class Multi(list): if self.opt.impl_get_multitype() == multitypes.slave: raise SlaveError(_("cannot pop a value on a multi option {0}" " which is a slave").format(self.opt._name)) - elif self.opt.impl_get_multitype() == multitypes.master: + if self.opt.impl_get_multitype() == multitypes.master: for slave in self.opt.impl_get_master_slaves(): values = context.cfgimpl_get_values() if not values.is_default_owner(slave): #get multi without valid properties - values.getitem(slave, + values.getitem(slave, validate=False, validate_properties=False ).pop(index, force=True) #set value without valid properties From ced260046c89f2e98a8ec26b859e9750e512749c Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 2 Feb 2014 18:21:09 +0100 Subject: [PATCH 36/61] test for multi --- test/test_multi.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 test/test_multi.py diff --git a/test/test_multi.py b/test/test_multi.py new file mode 100644 index 0000000..42fd8e1 --- /dev/null +++ b/test/test_multi.py @@ -0,0 +1,21 @@ +# coding: utf-8 +import autopath +from tiramisu.value import Multi +from tiramisu.option import IntOption, OptionDescription +from tiramisu.config import Config +from tiramisu.error import ConfigError + +import weakref +from py.test import raises + + +def test_multi(): + i = IntOption('int', '', multi=True) + o = OptionDescription('od', '', [i]) + c = Config(o) + multi = Multi([1,2,3], weakref.ref(c), i, 'int') + raises(ValueError, "Multi([1,2,3], c, i, 'int')") + raises(ValueError, "Multi(multi, weakref.ref(c), i, 'int')") + assert c is multi._getcontext() + del(c) + raises(ConfigError, "multi._getcontext()") From 26158fc3c41b5cd9e5125a68588f9c9b1c1d1cf5 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 2 Feb 2014 18:33:21 +0100 Subject: [PATCH 37/61] update test for metaconfig serialization --- test/test_state.py | 5 ++--- tiramisu/config.py | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/test/test_state.py b/test/test_state.py index e7777f1..fd547ef 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -5,6 +5,7 @@ from tiramisu.setting import owners from tiramisu.storage import delete_session from tiramisu.error import ConfigError from pickle import dumps, loads +from py.test import raises def return_value(value=None): @@ -288,9 +289,7 @@ def test_state_metaconfig(): conf2._impl_test = True meta = MetaConfig([conf1, conf2], session_id='29090937') meta._impl_test = True - a = dumps(meta) - q = loads(a) - _diff_conf(meta, q) + raises(ConfigError, "dumps(meta)") try: delete_session('29090935') delete_session('29090936') diff --git a/tiramisu/config.py b/tiramisu/config.py index 2c1a6b1..e8fc3ca 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -540,8 +540,6 @@ class _CommonConfig(SubConfig): # ----- state def __getstate__(self): if self._impl_meta is not None: - #FIXME _impl_meta est un weakref => faut pas sauvegarder mais faut bien savoir si c'est un méta ou pas au final ... - #en fait il faut ne pouvoir sérialisé que depuis une MetaConfig ... et pas directement comme pour les options raise ConfigError('cannot serialize Config with MetaConfig') slots = set() for subclass in self.__class__.__mro__: From 8d10ad40022f35267a3edd7b2968cf784bfe93cf Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 2 Feb 2014 22:47:46 +0100 Subject: [PATCH 38/61] add tests --- test/test_config.py | 26 +++++++++++++++++++++++++- test/test_config_api.py | 31 +++++++++++++++++++++++++++++++ test/test_metaconfig.py | 18 ++++++++++++++++++ test/test_parsing_group.py | 13 +++++++++++++ tiramisu/config.py | 10 +++------- 5 files changed, 90 insertions(+), 8 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index 6db3fcf..8de8a57 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -6,10 +6,11 @@ import autopath from py.test import raises -from tiramisu.config import Config +from tiramisu.config import Config, SubConfig from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ BoolOption, UnicodeOption, OptionDescription from tiramisu.error import ConflictError, ConfigError +import weakref def make_description(): @@ -294,3 +295,26 @@ def test_delete_config_with_subconfig(): raises(ConfigError, 'val[multi]') raises(ConfigError, 'setting[test]') raises(ConfigError, 'sub.make_dict()') + + +def test_config_weakref(): + o = OptionDescription('val', '', []) + o2 = OptionDescription('val', '', [o]) + c = Config(o2) + SubConfig(o, weakref.ref(c)) + raises(ValueError, "SubConfig(o, c)") + s = SubConfig(o, weakref.ref(c)) + assert s._cfgimpl_get_context() == c + del(c) + raises(ConfigError, "s._cfgimpl_get_context()") + + +def test_config_str(): + gcdummy = BoolOption('dummy', 'dummy', default=False) + gcdummy1 = BoolOption('dummy1', 'dummy', default=False, properties=('disabled',)) + o = OptionDescription('o', '', [gcdummy, gcdummy1]) + descr = OptionDescription('tiramisu', '', [o]) + cfg = Config(descr) + cfg.read_only() + str(cfg) + str(cfg.o) diff --git a/test/test_config_api.py b/test/test_config_api.py index db40c57..75920eb 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -67,6 +67,17 @@ def test_iter_config(): [('string', 'string'), ('string2', 'string2')] +def test_iter_config_property(): + "iteration on config object" + s = StrOption("string", "", default="string", properties=('disabled',)) + s2 = StrOption("string2", "", default="string2") + descr = OptionDescription("options", "", [s, s2]) + config = Config(descr) + config.read_only() + assert [(name, value) for name, value in config] == \ + [('string2', 'string2')] + + def test_iter_subconfig(): "iteration on config sub object" descr = make_description() @@ -177,3 +188,23 @@ def test_filename(): c.a = u'tmp/text.txt' raises(ValueError, "c.a = u'/tmp/with space.txt'") raises(ValueError, "c.a = u'/tmp/with$.txt'") + + +def test_iter_all(): + s = StrOption("string", "", default="string") + s2 = StrOption("string2", "", default="string2") + descr = OptionDescription("options", "", [s, s2]) + config = Config(descr) + assert list(config.iter_all()) == [('string', 'string'), ('string2', 'string2')] + for i in config.iter_all(): + #test StopIteration + break + + +def test_iter_all_prop(): + s = StrOption("string", "", default="string", properties=('disabled',)) + s2 = StrOption("string2", "", default="string2") + descr = OptionDescription("options", "", [s, s2]) + config = Config(descr) + config.read_only() + assert list(config.iter_all()) == [('string2', 'string2')] diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index 6986edc..9409d85 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -171,3 +171,21 @@ def test_meta_path(): meta = make_description() assert meta._impl_path is None assert meta.od1._impl_path == 'od1' + + +def test_meta_unconsistent(): + i1 = IntOption('i1', '') + i2 = IntOption('i2', '', default=1) + i3 = IntOption('i3', '') + i4 = IntOption('i4', '', default=2) + od1 = OptionDescription('od1', '', [i1, i2, i3, i4]) + od2 = OptionDescription('od2', '', [od1]) + conf1 = Config(od2) + conf2 = Config(od2) + conf3 = Config(od2) + conf4 = Config(od1) + meta = MetaConfig([conf1, conf2]) + meta.cfgimpl_get_settings().setowner(owners.meta) + raises(TypeError, 'MetaConfig("string")') + raises(ValueError, "MetaConfig([conf1, conf3])") + raises(ValueError, "MetaConfig([conf3, conf4])") diff --git a/test/test_parsing_group.py b/test/test_parsing_group.py index 7ecd860..c3b6ffc 100644 --- a/test/test_parsing_group.py +++ b/test/test_parsing_group.py @@ -94,6 +94,19 @@ def test_iter_on_groups(): result = list(config.creole.iter_groups(group_type=groups.family)) group_names = [res[0] for res in result] assert group_names == ['general', 'interface1'] + for i in config.creole.iter_groups(group_type=groups.family): + #test StopIteration + break + + +def test_iter_on_groups_props(): + descr = make_description() + config = Config(descr) + config.read_write() + config.cfgimpl_get_settings()[descr.creole.interface1].append('disabled') + result = list(config.creole.iter_groups(group_type=groups.family)) + group_names = [res[0] for res in result] + assert group_names == ['general'] def test_iter_on_empty_group(): diff --git a/tiramisu/config.py b/tiramisu/config.py index e8fc3ca..e3ac94f 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -149,8 +149,6 @@ class SubConfig(object): except UnicodeEncodeError: lines.append("{0} = {1}".format(name, value.encode(default_encoding))) - except PropertiesOptionError: - pass return '\n'.join(lines) __repr__ = __str__ @@ -540,7 +538,7 @@ class _CommonConfig(SubConfig): # ----- state def __getstate__(self): if self._impl_meta is not None: - raise ConfigError('cannot serialize Config with MetaConfig') + raise ConfigError(_('cannot serialize Config with MetaConfig')) slots = set() for subclass in self.__class__.__mro__: if subclass is not object: @@ -554,8 +552,8 @@ class _CommonConfig(SubConfig): pass storage = self._impl_values._p_._storage if not storage.serializable: - raise ConfigError('this storage is not serialisable, could be a ' - 'none persistent storage') + 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() @@ -742,6 +740,4 @@ def mandatory_warnings(config): except PropertiesOptionError as err: if err.proptype == ['mandatory']: yield path - except ConfigError: - pass config.cfgimpl_reset_cache(only=('values',)) From a067d2cdd9f27a153f1a994567bbcad4f907b91d Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 4 Feb 2014 21:14:30 +0100 Subject: [PATCH 39/61] add some tests --- test/test_config_api.py | 19 ++++++++++++++++++- test/test_metaconfig.py | 30 +++++++++++++++++++++--------- test/test_option_owner.py | 1 + tiramisu/config.py | 33 ++++++++++++--------------------- tiramisu/option.py | 30 +++++++++++++++++++----------- tiramisu/value.py | 2 ++ 6 files changed, 73 insertions(+), 42 deletions(-) diff --git a/test/test_config_api.py b/test/test_config_api.py index 75920eb..f5bc2c1 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -4,7 +4,8 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ - BoolOption, FilenameOption, OptionDescription + BoolOption, FilenameOption, UnicodeOption, SymLinkOption, IPOption, \ + PortOption, OptionDescription def make_description(): @@ -150,6 +151,7 @@ def test_find_in_config(): # not OptionDescription raises(AttributeError, "conf.find_first(byname='gc')") raises(AttributeError, "conf.gc.find_first(byname='gc2')") + raises(ValueError, "conf.find(byname='bool', type_='unknown')") def test_find_multi(): @@ -208,3 +210,18 @@ def test_iter_all_prop(): config = Config(descr) config.read_only() assert list(config.iter_all()) == [('string2', 'string2')] + + +def test_invalid_option(): + raises(TypeError, "ChoiceOption('a', '', [1, 2])") + raises(TypeError, "ChoiceOption('a', '', 1)") + raises(TypeError, "ChoiceOption('a', '', (1,), open_values='string')") + raises(ValueError, "ChoiceOption('a', '', (1,), 3)") + raises(ValueError, "FloatOption('a', '', 'string')") + raises(ValueError, "UnicodeOption('a', '', 1)") + raises(ValueError, "SymLinkOption('a', 'string')") + raises(ValueError, "IPOption('a', '', 1)") + raises(ValueError, "IPOption('a', '', 'string')") + raises(ValueError, "PortOption('a', '', 'string')") + raises(ValueError, "PortOption('a', '', '11:12:13', allow_range=True)") + raises(ValueError, "PortOption('a', '', 11111111111111111111)") diff --git a/test/test_metaconfig.py b/test/test_metaconfig.py index 9409d85..a8fc395 100644 --- a/test/test_metaconfig.py +++ b/test/test_metaconfig.py @@ -5,7 +5,7 @@ from py.test import raises from tiramisu.setting import owners from tiramisu.config import Config, GroupConfig, MetaConfig from tiramisu.option import IntOption, OptionDescription -from tiramisu.error import ConfigError +from tiramisu.error import ConfigError, PropertiesOptionError owners.addowner('meta') @@ -15,10 +15,14 @@ def make_description(): i2 = IntOption('i2', '', default=1) i3 = IntOption('i3', '') i4 = IntOption('i4', '', default=2) - od1 = OptionDescription('od1', '', [i1, i2, i3, i4]) + i5 = IntOption('i5', '', default=[2], multi=True) + i6 = IntOption('i6', '', properties=('disabled',)) + od1 = OptionDescription('od1', '', [i1, i2, i3, i4, i5, i6]) od2 = OptionDescription('od2', '', [od1]) conf1 = Config(od2) conf2 = Config(od2) + conf1.read_write() + conf2.read_write() meta = MetaConfig([conf1, conf2]) meta.cfgimpl_get_settings().setowner(owners.meta) return meta @@ -29,7 +33,7 @@ def make_description(): #FIXME serialization def test_none(): meta = make_description() - conf1, conf2 = meta._impl_children + conf1, conf2 = meta.cfgimpl_get_children() assert conf1.od1.i3 is conf2.od1.i3 is None assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default meta.od1.i3 = 3 @@ -58,7 +62,7 @@ def test_none(): def test_default(): meta = make_description() - conf1, conf2 = meta._impl_children + conf1, conf2 = meta.cfgimpl_get_children() assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default meta.od1.i2 = 3 @@ -87,7 +91,7 @@ def test_default(): def test_contexts(): meta = make_description() - conf1, conf2 = meta._impl_children + conf1, conf2 = meta.cfgimpl_get_children() assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default meta.setattrs('od1.i2', 6) @@ -101,14 +105,15 @@ def test_find(): i2 = meta.unwrap_from_path('od1.i2') assert [i2] == meta.find(byname='i2') assert i2 == meta.find_first(byname='i2') - assert meta.make_dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None, 'od1.i2': 1} + assert meta.make_dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None, + 'od1.i2': 1, 'od1.i5': [2], 'od1.i6': None} def test_meta_meta(): meta1 = make_description() meta2 = MetaConfig([meta1]) meta2.cfgimpl_get_settings().setowner(owners.meta) - conf1, conf2 = meta1._impl_children + conf1, conf2 = meta1.cfgimpl_get_children() assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default meta2.od1.i2 = 3 @@ -142,15 +147,21 @@ def test_meta_meta_set(): meta1 = make_description() meta2 = MetaConfig([meta1]) meta2.cfgimpl_get_settings().setowner(owners.meta) - conf1, conf2 = meta1._impl_children + conf1, conf2 = meta1.cfgimpl_get_children() meta2.setattrs('od1.i1', 7) + #PropertiesOptionError + meta2.setattrs('od1.i6', 7) assert conf1.od1.i1 == conf2.od1.i1 == 7 assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user assert [conf1, conf2] == meta2.find_firsts(byname='i1', byvalue=7) conf1.od1.i1 = 8 + assert [conf1, conf2] == meta2.find_firsts(byname='i1') assert [conf2] == meta2.find_firsts(byname='i1', byvalue=7) assert [conf1] == meta2.find_firsts(byname='i1', byvalue=8) + assert [conf1, conf2] == meta2.find_firsts(byname='i5', byvalue=2) raises(AttributeError, "meta2.find_firsts(byname='i1', byvalue=10)") + raises(AttributeError, "meta2.find_firsts(byname='not', byvalue=10)") + raises(AttributeError, "meta2.find_firsts(byname='i6')") def test_not_meta(): @@ -159,9 +170,10 @@ def test_not_meta(): od2 = OptionDescription('od2', '', [od1]) conf1 = Config(od2) conf2 = Config(od2) + raises(ValueError, "GroupConfig(conf1)") meta = GroupConfig([conf1, conf2]) raises(ConfigError, 'meta.od1.i1') - conf1, conf2 = meta._impl_children + conf1, conf2 = meta.cfgimpl_get_children() meta.setattrs('od1.i1', 7) assert conf1.od1.i1 == conf2.od1.i1 == 7 assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user diff --git a/test/test_option_owner.py b/test/test_option_owner.py index b758e91..d4c3f6b 100644 --- a/test/test_option_owner.py +++ b/test/test_option_owner.py @@ -36,6 +36,7 @@ def test_default_owner(): cfg = Config(descr) assert cfg.dummy is False assert cfg.getowner(gcdummy) == 'default' + raises(TypeError, "cfg.getowner('gcdummy')") cfg.dummy = True assert cfg.getowner(gcdummy) == owners.user diff --git a/tiramisu/config.py b/tiramisu/config.py index e3ac94f..c9fb992 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -296,12 +296,9 @@ class SubConfig(object): :return: find list or an exception if nothing has been found """ def _filter_by_name(): - try: - if byname is None or path == byname or \ - path.endswith('.' + byname): - return True - except IndexError: - pass + if byname is None or path == byname or \ + path.endswith('.' + byname): + return True return False def _filter_by_value(): @@ -452,22 +449,16 @@ class SubConfig(object): def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten): if isinstance(opt, OptionDescription): - try: - pathsvalues += getattr(self, path).make_dict(flatten, - _currpath + - path.split('.')) - except PropertiesOptionError: - pass # this just a hidden or disabled option + pathsvalues += getattr(self, path).make_dict(flatten, + _currpath + + path.split('.')) else: - try: - value = self._getattr(opt._name) - if flatten: - name = opt._name - else: - name = '.'.join(_currpath + [opt._name]) - pathsvalues.append((name, value)) - except PropertiesOptionError: - pass # this just a hidden or disabled option + value = self._getattr(opt._name) + if flatten: + name = opt._name + else: + name = '.'.join(_currpath + [opt._name]) + pathsvalues.append((name, value)) def cfgimpl_get_path(self): descr = self.cfgimpl_get_description() diff --git a/tiramisu/option.py b/tiramisu/option.py index ad99416..409d6eb 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -662,7 +662,7 @@ class ChoiceOption(Option): return self._open_values def _validate(self, value): - if not self._open_values and not value in self._values: + if not self.impl_is_openvalues() and not value in self.impl_get_values(): raise ValueError(_('value {0} is not permitted, ' 'only {1} is allowed' '').format(value, self._values)) @@ -782,9 +782,13 @@ class IPOption(Option): def _validate(self, value): # sometimes an ip term starts with a zero # but this does not fit in some case, for example bind does not like it - for val in value.split('.'): - if val.startswith("0") and len(val) > 1: - raise ValueError(_('invalid IP')) + try: + for val in value.split('.'): + if val.startswith("0") and len(val) > 1: + raise ValueError(_('invalid IP')) + except AttributeError: + #if integer for example + raise ValueError(_('invalid IP')) # 'standard' validation try: IP('{0}/32'.format(value)) @@ -856,18 +860,22 @@ class PortOption(Option): if self._allow_range and ":" in str(value): value = str(value).split(':') if len(value) != 2: - raise ValueError('invalid part, range must have two values ' - 'only') + raise ValueError(_('invalid part, range must have two values ' + 'only')) if not value[0] < value[1]: - raise ValueError('invalid port, first port in range must be' - ' smaller than the second one') + raise ValueError(_('invalid port, first port in range must be' + ' smaller than the second one')) else: value = [value] for val in value: - if not self._min_value <= int(val) <= self._max_value: - raise ValueError('invalid port, must be an between {0} and {1}' - ''.format(self._min_value, self._max_value)) + try: + if not self._min_value <= int(val) <= self._max_value: + raise ValueError(_('invalid port, must be an between {0} ' + 'and {1}').format(self._min_value, + self._max_value)) + except ValueError: + raise ValueError(_('invalid port')) class NetworkOption(Option): diff --git a/tiramisu/value.py b/tiramisu/value.py index 863f2a1..ed70f90 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -66,6 +66,8 @@ class Values(object): meta = self._getcontext().cfgimpl_get_meta() if meta is not None: value = meta.cfgimpl_get_values()[opt] + if isinstance(value, Multi): + value = list(value) else: value = opt.impl_getdefault() if opt.impl_is_multi(): From c52b2f84f42dbfab7c9ab8ee8459d271b45d9593 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 4 Feb 2014 21:40:07 +0100 Subject: [PATCH 40/61] if option with requires has a property, calculated properties are store in storage --- test/test_requires.py | 22 ++++++++++++++++++++++ tiramisu/setting.py | 10 +++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/test/test_requires.py b/test/test_requires.py index 4cf9372..ff9c2a6 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -24,6 +24,28 @@ def test_requires(): except PropertiesOptionError as err: props = err.proptype assert props == ['disabled'] + c.activate_service = True + c.ip_address_service + + +def test_requires_with_requires(): + a = BoolOption('activate_service', '', True) + b = IPOption('ip_address_service', '', + requires=[{'option': a, 'expected': False, 'action': 'disabled'}]) + od = OptionDescription('service', '', [a, b]) + c = Config(od) + c.read_write() + c.cfgimpl_get_settings()[b].append('test') + c.ip_address_service + c.activate_service = False + props = [] + try: + c.ip_address_service + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + c.activate_service = True + c.ip_address_service def test_requires_invalid(): diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 101b551..38cf7b5 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -372,7 +372,7 @@ class Settings(object): def _getproperties(self, opt=None, path=None, is_apply_req=True): if opt is None: - props = self._p_.getproperties(path, default_properties) + props = copy(self._p_.getproperties(path, default_properties)) else: if path is None: raise ValueError(_('if opt is not None, path should not be' @@ -383,8 +383,8 @@ class Settings(object): ntime = int(time()) is_cached, props = self._p_.getcache(path, ntime) if is_cached: - return props - props = self._p_.getproperties(path, opt._properties) + return copy(props) + props = copy(self._p_.getproperties(path, opt._properties)) if is_apply_req: props |= self.apply_requires(opt, path) if 'cache' in self: @@ -446,8 +446,8 @@ class Settings(object): (typically with the `frozen` property) """ # opt properties - properties = copy(self._getproperties(opt_or_descr, path)) - self_properties = copy(self._getproperties()) + properties = self._getproperties(opt_or_descr, path) + self_properties = self._getproperties() # remove opt permissive # permissive affect option's permission with or without permissive # global property From 6f5d471db19abca10c43c3c0b0907eebd268e221 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 4 Feb 2014 21:40:07 +0100 Subject: [PATCH 41/61] if option with requires has a property, calculated properties are store in storage --- test/test_requires.py | 22 ++++++++++++++++++++++ tiramisu/setting.py | 10 +++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/test/test_requires.py b/test/test_requires.py index 4cf9372..ff9c2a6 100644 --- a/test/test_requires.py +++ b/test/test_requires.py @@ -24,6 +24,28 @@ def test_requires(): except PropertiesOptionError as err: props = err.proptype assert props == ['disabled'] + c.activate_service = True + c.ip_address_service + + +def test_requires_with_requires(): + a = BoolOption('activate_service', '', True) + b = IPOption('ip_address_service', '', + requires=[{'option': a, 'expected': False, 'action': 'disabled'}]) + od = OptionDescription('service', '', [a, b]) + c = Config(od) + c.read_write() + c.cfgimpl_get_settings()[b].append('test') + c.ip_address_service + c.activate_service = False + props = [] + try: + c.ip_address_service + except PropertiesOptionError as err: + props = err.proptype + assert props == ['disabled'] + c.activate_service = True + c.ip_address_service def test_requires_invalid(): diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 99347f1..4092ae6 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -361,7 +361,7 @@ class Settings(object): def _getproperties(self, opt=None, path=None, is_apply_req=True): if opt is None: - props = self._p_.getproperties(path, default_properties) + props = copy(self._p_.getproperties(path, default_properties)) else: if path is None: raise ValueError(_('if opt is not None, path should not be' @@ -372,8 +372,8 @@ class Settings(object): ntime = int(time()) is_cached, props = self._p_.getcache(path, ntime) if is_cached: - return props - props = self._p_.getproperties(path, opt._properties) + return copy(props) + props = copy(self._p_.getproperties(path, opt._properties)) if is_apply_req: props |= self.apply_requires(opt, path) if 'cache' in self: @@ -435,8 +435,8 @@ class Settings(object): (typically with the `frozen` property) """ # opt properties - properties = copy(self._getproperties(opt_or_descr, path)) - self_properties = copy(self._getproperties()) + properties = self._getproperties(opt_or_descr, path) + self_properties = self._getproperties() # remove opt permissive # permissive affect option's permission with or without permissive # global property From 72f06bc29d8daedad27c43ac32015b7860aabf70 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 6 Feb 2014 19:19:48 +0100 Subject: [PATCH 42/61] properties option in consistencies are now allowed --- test/test_option_consistency.py | 11 +++++++++++ tiramisu/option.py | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 4db3410..a7ff41c 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -283,3 +283,14 @@ def test_consistency_not_all(): c.a = ['192.168.1.0'] c.b = ['255.255.255.0'] c.c = ['192.168.1.255'] + + +def test_consistency_permissive(): + a = IntOption('a', '', 1) + b = IntOption('b', '', 2, properties=('hidden',)) + od = OptionDescription('od', '', [a, b]) + a.impl_add_consistency('not_equal', b) + c = Config(od) + c.cfgimpl_get_settings().setpermissive(('hidden',)) + c.read_write() + c.a = 1 diff --git a/tiramisu/option.py b/tiramisu/option.py index 409d6eb..aafd43b 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -367,7 +367,8 @@ class Option(BaseOption): #if context, calculate value, otherwise get default value if context is not None: opt_value = context._getattr( - descr.impl_get_path_by_opt(opt), validate=False) + descr.impl_get_path_by_opt(opt), validate=False, + force_permissive=True) else: opt_value = opt.impl_getdefault() @@ -873,7 +874,7 @@ class PortOption(Option): if not self._min_value <= int(val) <= self._max_value: raise ValueError(_('invalid port, must be an between {0} ' 'and {1}').format(self._min_value, - self._max_value)) + self._max_value)) except ValueError: raise ValueError(_('invalid port')) From e7531e1fda5340b61b6766aaae900d588d05e25a Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 6 Feb 2014 22:17:20 +0100 Subject: [PATCH 43/61] more tests --- test/test_config_api.py | 32 ++++++++++++++++- test/test_config_domain.py | 12 +++++-- test/test_option_consistency.py | 28 +++++++++++++++ test/test_state.py | 8 ++++- tiramisu/option.py | 61 +++++++++------------------------ 5 files changed, 93 insertions(+), 48 deletions(-) diff --git a/test/test_config_api.py b/test/test_config_api.py index f5bc2c1..4be9e80 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -5,7 +5,9 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ BoolOption, FilenameOption, UnicodeOption, SymLinkOption, IPOption, \ - PortOption, OptionDescription + PortOption, NetworkOption, NetmaskOption, BroadcastOption, \ + DomainnameOption, OptionDescription +from tiramisu.error import PropertiesOptionError def make_description(): @@ -212,6 +214,22 @@ def test_iter_all_prop(): assert list(config.iter_all()) == [('string2', 'string2')] +def test_impl_getpaths(): + s = StrOption("string", "", default="string", properties=('disabled',)) + s2 = StrOption("string2", "", default="string2") + s3 = StrOption("string3", "", default="string3") + s4 = StrOption("string4", "", default="string4", properties=('hidden',)) + od = OptionDescription('od', '', [s3, s4]) + descr = OptionDescription("options", "", [s, s2, od]) + config = Config(descr) + assert ['string', 'string2', 'od.string3', 'od.string4'] == config.cfgimpl_get_description().impl_getpaths() + assert ['string', 'string2', 'od', 'od.string3', 'od.string4'] == config.cfgimpl_get_description().impl_getpaths(include_groups=True) + config.read_write() + raises(PropertiesOptionError, "config.od.string4") + assert ['string', 'string2', 'od.string3', 'od.string4'] == config.cfgimpl_get_description().impl_getpaths() + assert ['string', 'string2', 'od', 'od.string3', 'od.string4'] == config.cfgimpl_get_description().impl_getpaths(include_groups=True) + + def test_invalid_option(): raises(TypeError, "ChoiceOption('a', '', [1, 2])") raises(TypeError, "ChoiceOption('a', '', 1)") @@ -225,3 +243,15 @@ def test_invalid_option(): raises(ValueError, "PortOption('a', '', 'string')") raises(ValueError, "PortOption('a', '', '11:12:13', allow_range=True)") raises(ValueError, "PortOption('a', '', 11111111111111111111)") + raises(ValueError, "PortOption('a', '', allow_zero=True, allow_wellknown=False, allow_registred=True, allow_private=False)") + raises(ValueError, "PortOption('a', '', allow_zero=True, allow_wellknown=True, allow_registred=False, allow_private=True)") + raises(ValueError, "PortOption('a', '', allow_zero=True, allow_wellknown=False, allow_registred=False, allow_private=True)") + raises(ValueError, "PortOption('a', '', allow_zero=True, allow_wellknown=False, allow_registred=True, allow_private=True)") + raises(ValueError, "PortOption('a', '', allow_zero=False, allow_wellknown=False, allow_registred=False, allow_private=False)") + raises(ValueError, "NetworkOption('a', '', 'string')") + raises(ValueError, "NetmaskOption('a', '', 'string')") + raises(ValueError, "BroadcastOption('a', '', 'string')") + raises(ValueError, "DomainnameOption('a', '', 'string')") + raises(ValueError, "DomainnameOption('a', '', type_='string')") + raises(ValueError, "DomainnameOption('a', '', allow_ip='string')") + raises(ValueError, "DomainnameOption('a', '', allow_without_dot='string')") diff --git a/test/test_config_domain.py b/test/test_config_domain.py index f4579a6..8fcf1f1 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -7,9 +7,9 @@ from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDesc def test_domainname(): d = DomainnameOption('d', '') - e = DomainnameOption('e', '', "toto.com") f = DomainnameOption('f', '', allow_without_dot=True) - od = OptionDescription('a', '', [d, f]) + g = DomainnameOption('g', '', allow_ip=True) + od = OptionDescription('a', '', [d, f, g]) c = Config(od) c.read_write() c.d = 'toto.com' @@ -24,6 +24,12 @@ def test_domainname(): # c.f = 'toto.com' c.f = 'toto' + raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnameanditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnametoolongthathavemorethanmaximumsizeforatruedomainnameanditsnoteasytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowiendityeah'") + raises(ValueError, "c.f = 'd'") + # + c.g = 'toto.com' + c.g = '192.168.1.0' + c.g = '192.168.1.29' def test_domainname_netbios(): @@ -56,6 +62,7 @@ def test_email(): c.e = 'root@foo.com' raises(ValueError, "c.e = 'root'") raises(ValueError, "c.e = 'root@domain'") + raises(ValueError, "c.e = 'root[]@domain'") def test_url(): @@ -76,3 +83,4 @@ def test_url(): c.u = 'https://foo.com:8443' c.u = 'https://foo.com:8443/' c.u = 'https://foo.com:8443/index.html' + raises(ValueError, "c.u = 'https://foo.com:84438989'") diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index a7ff41c..1beb4bb 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -169,6 +169,18 @@ def test_consistency_network_netmask(): raises(ValueError, "c.a = '192.168.1.1'") +def test_consistency_ip_netmask_network_error(): + a = IPOption('a', '') + b = NetworkOption('b', '') + c = NetmaskOption('c', '') + od = OptionDescription('od', '', [a, b, c]) + c.impl_add_consistency('ip_netmask', a, b) + c = Config(od) + c.a = '192.168.1.1' + c.b = '192.168.1.0' + raises(ConfigError, "c.c = '255.255.255.0'") + + def test_consistency_ip_netmask_error_multi(): a = IPOption('a', '', multi=True) b = NetmaskOption('b', '') @@ -217,6 +229,8 @@ def test_consistency_ip_netmask_multi_master(): c.b = ['255.255.255.255'] c.b = ['255.255.255.0'] raises(ValueError, "c.a = ['192.168.1.0']") + c.a = ['192.168.1.128'] + raises(ValueError, "c.b = ['255.255.255.128']") c.a = ['192.168.1.2', '192.168.1.3'] @@ -260,6 +274,20 @@ def test_consistency_broadcast(): c.c[1] = '192.168.2.255' +def test_consistency_broadcast_error(): + 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) + c = Config(od) + c.a = ['192.168.1.0'] + c.b = ['255.255.255.0'] + raises(ConfigError, "c.c = ['192.168.1.255']") + + def test_consistency_broadcast_default(): a = NetworkOption('a', '', '192.168.1.0') b = NetmaskOption('b', '', '255.255.255.128') diff --git a/test/test_state.py b/test/test_state.py index fd547ef..03be06e 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -1,3 +1,5 @@ +import autopath + from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \ IntOption, OptionDescription from tiramisu.config import Config, GroupConfig, MetaConfig @@ -133,7 +135,6 @@ def _diff_conf(cfg1, cfg2): def test_diff_opt(): b = BoolOption('b', '') u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}]) - #u.impl_add_consistency('not_equal', b) s = SymLinkOption('s', u) o = OptionDescription('o', '', [b, u, s]) o1 = OptionDescription('o1', '', [o]) @@ -147,6 +148,11 @@ def test_diff_opt(): _diff_opt(o1.o.s, q.o.s) +def test_only_optiondescription(): + b = BoolOption('b', '') + raises(SystemError, "a = dumps(b)") + + def test_diff_opt_cache(): b = BoolOption('b', '') u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}]) diff --git a/tiramisu/option.py b/tiramisu/option.py index aafd43b..af7a67a 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -354,9 +354,6 @@ class Option(BaseOption): """ if context is not None: descr = context.cfgimpl_get_description() - #option is also in all_cons_opts - if option not in all_cons_opts: - raise ConfigError(_('option not in all_cons_opts')) all_cons_vals = [] for opt in all_cons_opts: @@ -587,31 +584,15 @@ class Option(BaseOption): 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))) + 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))) if load: del(self._state_consistencies) self._consistencies = new_value @@ -932,19 +913,14 @@ class NetmaskOption(Option): IP('{0}/{1}'.format(val_ipnetwork, val_netmask), make_net=not make_net) except ValueError: - if not make_net: - msg = _("invalid network {0} ({1}) " - "with netmask {2}," - " this network is an IP") + pass else: if make_net: msg = _("invalid IP {0} ({1}) with netmask {2}," " this IP is a network") except ValueError: - if make_net: - msg = _('invalid IP {0} ({1}) with netmask {2}') - else: + if not make_net: msg = _('invalid network {0} ({1}) with netmask {2}') if msg is not None: raise ValueError(msg.format(val_ipnetwork, opts[1]._name, @@ -1035,8 +1011,8 @@ class DomainnameOption(Option): if self._type == 'domainname' and not self._allow_without_dot and \ '.' not in value: raise ValueError(_("invalid domainname, must have dot")) - if len(value) > 255: - raise ValueError(_("invalid domainname's length (max 255)")) + if len(value) > 255: + raise ValueError(_("invalid domainname's length (max 255)")) if len(value) < 2: raise ValueError(_("invalid domainname's length (min 2)")) if not self._domain_re.search(value): @@ -1313,13 +1289,10 @@ class OptionDescription(BaseOption): if consistencies is not None: 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 + all_cons_opts[0]._launch_consistency(func, option, + value, + context, index, + all_cons_opts) def _impl_getstate(self, descr=None): """enables us to export into a dict From fc7f3c24a5882040f775481bcc6c3a9898e6cc43 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 6 Feb 2014 19:19:48 +0100 Subject: [PATCH 44/61] properties option in consistencies are now allowed Conflicts: tiramisu/option.py (PortOption._validate): Strange try/except removed. --- test/test_option_consistency.py | 11 +++++++++++ tiramisu/option.py | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 4db3410..a7ff41c 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -283,3 +283,14 @@ def test_consistency_not_all(): c.a = ['192.168.1.0'] c.b = ['255.255.255.0'] c.c = ['192.168.1.255'] + + +def test_consistency_permissive(): + a = IntOption('a', '', 1) + b = IntOption('b', '', 2, properties=('hidden',)) + od = OptionDescription('od', '', [a, b]) + a.impl_add_consistency('not_equal', b) + c = Config(od) + c.cfgimpl_get_settings().setpermissive(('hidden',)) + c.read_write() + c.a = 1 diff --git a/tiramisu/option.py b/tiramisu/option.py index ad99416..9df3d01 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -367,7 +367,8 @@ class Option(BaseOption): #if context, calculate value, otherwise get default value if context is not None: opt_value = context._getattr( - descr.impl_get_path_by_opt(opt), validate=False) + descr.impl_get_path_by_opt(opt), validate=False, + force_permissive=True) else: opt_value = opt.impl_getdefault() From 8d751ecc9beca1a328f0e52a52325ea9d7d8c559 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 17 Feb 2014 18:36:29 +0100 Subject: [PATCH 45/61] valid port type before compare to min and max value --- tiramisu/option.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index af7a67a..d46c8c9 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -852,12 +852,13 @@ class PortOption(Option): for val in value: try: - if not self._min_value <= int(val) <= self._max_value: - raise ValueError(_('invalid port, must be an between {0} ' - 'and {1}').format(self._min_value, - self._max_value)) + int(val) except ValueError: raise ValueError(_('invalid port')) + if not self._min_value <= int(val) <= self._max_value: + raise ValueError(_('invalid port, must be an between {0} ' + 'and {1}').format(self._min_value, + self._max_value)) class NetworkOption(Option): From bf0501b1d623fcc76232e79f0fd7518d35060dd5 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 20 Feb 2014 14:27:29 +0100 Subject: [PATCH 46/61] tiramisu/setting.py : properties store in cache was a reference to a list modified in validation, should copy properties in cache --- test/test_option_setting.py | 11 +++++++++++ tiramisu/setting.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/test/test_option_setting.py b/test/test_option_setting.py index ed5f7d7..94755e8 100644 --- a/test/test_option_setting.py +++ b/test/test_option_setting.py @@ -367,3 +367,14 @@ def test_reset_multiple(): setting[option].append('test') setting.reset(all_properties=True) setting.reset(all_properties=True) + + +def test_properties_cached(): + b1 = BoolOption("b1", "", properties=('test',)) + descr = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])]) + c = Config(descr) + c.read_write() + setting = c.cfgimpl_get_settings() + option = c.cfgimpl_get_description().sub.b1 + c._setattr('sub.b1', True, force_permissive=True) + assert str(setting[b1]) == "['test']" diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 38cf7b5..e2ddb7d 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -392,7 +392,7 @@ class Settings(object): if ntime is None: ntime = int(time()) ntime = ntime + expires_time - self._p_.setcache(path, props, ntime) + self._p_.setcache(path, copy(props), ntime) return props def append(self, propname): From d5b83c123c76e084b3688e8d7c0203bae77e89d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 20 Feb 2014 14:27:29 +0100 Subject: [PATCH 47/61] tiramisu/setting.py : properties store in cache was a reference to a list modified in validation, should copy properties in cache --- test/test_option_setting.py | 11 +++++++++++ tiramisu/setting.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/test/test_option_setting.py b/test/test_option_setting.py index ed5f7d7..94755e8 100644 --- a/test/test_option_setting.py +++ b/test/test_option_setting.py @@ -367,3 +367,14 @@ def test_reset_multiple(): setting[option].append('test') setting.reset(all_properties=True) setting.reset(all_properties=True) + + +def test_properties_cached(): + b1 = BoolOption("b1", "", properties=('test',)) + descr = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])]) + c = Config(descr) + c.read_write() + setting = c.cfgimpl_get_settings() + option = c.cfgimpl_get_description().sub.b1 + c._setattr('sub.b1', True, force_permissive=True) + assert str(setting[b1]) == "['test']" diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 38cf7b5..e2ddb7d 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -392,7 +392,7 @@ class Settings(object): if ntime is None: ntime = int(time()) ntime = ntime + expires_time - self._p_.setcache(path, props, ntime) + self._p_.setcache(path, copy(props), ntime) return props def append(self, propname): From 716d2b2348424fa02e2e5fc1f6b77707dd248fd1 Mon Sep 17 00:00:00 2001 From: gwen Date: Tue, 25 Feb 2014 14:19:54 +0100 Subject: [PATCH 48/61] allows a DomainnameOption to start with a number --- test/test_config_domain.py | 11 ++++++++++- tiramisu/option.py | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/test/test_config_domain.py b/test/test_config_domain.py index f4579a6..fd9f0cc 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -16,7 +16,7 @@ def test_domainname(): raises(ValueError, "c.d = 'toto'") c.d = 'toto3.com' raises(ValueError, "c.d = 'toto3.3la'") - raises(ValueError, "c.d = '3toto.com'") + #raises(ValueError, "c.d = '3toto.com'") raises(ValueError, "c.d = 'toto.co3'") raises(ValueError, "c.d = 'toto_super.com'") c.d = 'toto-.com' @@ -25,6 +25,15 @@ def test_domainname(): c.f = 'toto.com' c.f = 'toto' +def test_domainname_special(): + """domain name option that starts with a number + """ + d = DomainnameOption('d', '') + od = OptionDescription('a', '', [d]) + c = Config(od) + c.read_write() + c.d = '1toto.com' + c.d = '123toto.com' def test_domainname_netbios(): d = DomainnameOption('d', '', type_='netbios') diff --git a/tiramisu/option.py b/tiramisu/option.py index 9df3d01..4c16aa9 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -1004,7 +1004,7 @@ class DomainnameOption(Option): else: extrachar = '\.' end = '+[a-z]*' - self._domain_re = re.compile(r'^(?:[a-z][a-z\d\-{0}]{{,{1}}}{2}){3}$' + self._domain_re = re.compile(r'^(?:[a-z\d][a-z\d\-{0}]{{,{1}}}{2}){3}$' ''.format(extrachar, length, extrachar_mandatory, end)) super(DomainnameOption, self).__init__(name, doc, default=default, default_multi=default_multi, From 0c9d6554ae71b9bace5f3e5b4004a2b2dcadf0de Mon Sep 17 00:00:00 2001 From: gwen Date: Tue, 25 Feb 2014 15:20:03 +0100 Subject: [PATCH 49/61] a hostname shall not start with a number --- test/test_config_domain.py | 10 +++++++--- tiramisu/option.py | 9 +++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/test/test_config_domain.py b/test/test_config_domain.py index fd9f0cc..9fd9ed3 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -25,15 +25,19 @@ def test_domainname(): c.f = 'toto.com' c.f = 'toto' -def test_domainname_special(): - """domain name option that starts with a number +def test_special_domain_name(): + """domain name option that starts with a number or not """ d = DomainnameOption('d', '') - od = OptionDescription('a', '', [d]) + e = DomainnameOption('e', '', type_='netbios') + od = OptionDescription('a', '', [d,e]) c = Config(od) c.read_write() c.d = '1toto.com' c.d = '123toto.com' + c.e = 'toto' + raises(ValueError, "c.e = '1toto'") + def test_domainname_netbios(): d = DomainnameOption('d', '', type_='netbios') diff --git a/tiramisu/option.py b/tiramisu/option.py index 4c16aa9..8b5eb68 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -993,6 +993,10 @@ class DomainnameOption(Option): end = '' extrachar = '' extrachar_mandatory = '' + if self._type != 'netbios': + allow_number = '\d' + else: + allow_number = '' if self._type == 'netbios': length = 14 elif self._type == 'hostname': @@ -1004,8 +1008,9 @@ class DomainnameOption(Option): else: extrachar = '\.' end = '+[a-z]*' - self._domain_re = re.compile(r'^(?:[a-z\d][a-z\d\-{0}]{{,{1}}}{2}){3}$' - ''.format(extrachar, length, extrachar_mandatory, end)) + self._domain_re = re.compile(r'^(?:[a-z{0}][a-z\d\-{1}]{{,{2}}}{3}){4}$' + ''.format(allow_number, extrachar, length, + extrachar_mandatory, end)) super(DomainnameOption, self).__init__(name, doc, default=default, default_multi=default_multi, callback=callback, From 9f903d3e042da303d580f1125a36d52b5b744f5d Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 6 Mar 2014 22:09:12 +0100 Subject: [PATCH 50/61] add username's option --- test/test_option_username.py | 25 +++++++++++++++++++++++++ tiramisu/option.py | 12 ++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 test/test_option_username.py diff --git a/test/test_option_username.py b/test/test_option_username.py new file mode 100644 index 0000000..472e9cd --- /dev/null +++ b/test/test_option_username.py @@ -0,0 +1,25 @@ +"configuration objects global API" +import autopath +from py.test import raises + +from tiramisu.config import Config +from tiramisu.option import UsernameOption + +def test_username(): + UsernameOption('a', '', 'string') + UsernameOption('a', '', '_string') + UsernameOption('a', '', 's_tring') + UsernameOption('a', '', 'string_') + UsernameOption('a', '', 'string$') + UsernameOption('a', '', '_string$') + raises(ValueError, "UsernameOption('a', '', 'strin$g')") + UsernameOption('a', '', 's-tring') + raises(ValueError, "UsernameOption('a', '', '-string')") + UsernameOption('a', '', 's9tring') + raises(ValueError, "UsernameOption('a', '', '9string')") + raises(ValueError, "UsernameOption('a', '', '')") + UsernameOption('a', '', 's') + UsernameOption('a', '', 's2345678901234567890123456789012') + raises(ValueError, "UsernameOption('a', '', 's23456789012345678901234567890123')") + UsernameOption('a', '', 's234567890123456789012345678901$') + raises(ValueError, "UsernameOption('a', '', 's2345678901234567890123456789012$')") diff --git a/tiramisu/option.py b/tiramisu/option.py index b9960b6..da93102 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -1079,6 +1079,18 @@ class URLOption(DomainnameOption): raise ValueError(_('invalid url, should ends with filename')) +class UsernameOption(Option): + __slots__ = tuple() + _opt_type = 'username' + #regexp build with 'man 8 adduser' informations + username_re = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$") + + def _validate(self, value): + match = self.username_re.search(value) + if not match: + raise ValueError(_('invalid username')) + + class FilenameOption(Option): __slots__ = tuple() _opt_type = 'file' From c1755192118cdae368fffd59478925f374580022 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 6 Mar 2014 22:09:44 +0100 Subject: [PATCH 51/61] pep8 --- tiramisu/option.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index da93102..851872d 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -857,8 +857,8 @@ class PortOption(Option): raise ValueError(_('invalid port')) if not self._min_value <= int(val) <= self._max_value: raise ValueError(_('invalid port, must be an between {0} ' - 'and {1}').format(self._min_value, - self._max_value)) + 'and {1}').format(self._min_value, + self._max_value)) class NetworkOption(Option): From d3f9d20ab5d33592304298194fe4199c98b13f9c Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 8 Mar 2014 18:53:22 +0100 Subject: [PATCH 52/61] add extend in config setting --- test/test_option_setting.py | 9 +++++++++ tiramisu/setting.py | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/test/test_option_setting.py b/test/test_option_setting.py index 94755e8..28327d5 100644 --- a/test/test_option_setting.py +++ b/test/test_option_setting.py @@ -310,6 +310,15 @@ def test_access_by_get_whith_hide(): raises(AttributeError, "c.find(byname='b1')") +def test_extend_config_properties(): + descr = make_description() + cfg = Config(descr) + setting = cfg.cfgimpl_get_settings() + assert str(setting) == str(['cache', 'expire', 'validator']) + setting.extend(['test', 'test2']) + assert str(setting) == str(['test', 'cache', 'test2', 'expire', 'validator']) + + def test_append_properties(): descr = make_description() cfg = Config(descr) diff --git a/tiramisu/setting.py b/tiramisu/setting.py index e2ddb7d..d3b0a3b 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -408,6 +408,10 @@ class Settings(object): props.remove(propname) self._setproperties(props, None, None) + def extend(self, propnames): + for propname in propnames: + self.append(propname) + def _setproperties(self, properties, opt, path): """save properties for specified opt (never save properties if same has option properties) From 6e8b570a372d25dbcba50ac2247abb4a8ac20b53 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 9 Mar 2014 20:06:44 +0100 Subject: [PATCH 53/61] mandatory_warnings is now in values and add force_cache to values --- test/test_cache.py | 19 +++++++++++++++++++ test/test_mandatory.py | 18 +++++++++--------- tiramisu/config.py | 18 ++---------------- tiramisu/value.py | 38 +++++++++++++++++++++++++++++++++++++- 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/test/test_cache.py b/test/test_cache.py index f483c76..ef4f1a3 100644 --- a/test/test_cache.py +++ b/test/test_cache.py @@ -253,3 +253,22 @@ def test_reset_cache_only(): c.cfgimpl_reset_cache(only=('settings',)) assert 'u1' in values._p_.get_cached(c) assert 'u1' not in settings._p_.get_cached(c) + + +def test_force_cache(): + u1 = IntOption('u1', '', multi=True) + u2 = IntOption('u2', '') + u3 = IntOption('u3', '', multi=True) + u4 = IntOption('u4', '', properties=('disabled',)) + od = OptionDescription('od1', '', [u1, u2, u3, u4]) + c = Config(od) + c.cfgimpl_get_settings().remove('expire') + + c.cfgimpl_get_values().force_cache() + assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': ([], None), 'u3': ([], None), 'u2': (None, None), 'u4': (None, None)} + assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u4': (set(['disabled']), None), 'u1': (set([]), None), 'u3': (set([]), None), 'u2': (set([]), None)} + c.read_only() + + c.cfgimpl_get_values().force_cache() + assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': ([], None), 'u3': ([], None), 'u2': (None, None)} + assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u4': (set(['disabled']), None), 'u1': (set([]), None), 'u3': (set([]), None), 'u2': (set([]), None)} diff --git a/test/test_mandatory.py b/test/test_mandatory.py index c535e31..5b9878a 100644 --- a/test/test_mandatory.py +++ b/test/test_mandatory.py @@ -1,7 +1,7 @@ import autopath #from py.test import raises -from tiramisu.config import Config, mandatory_warnings +from tiramisu.config import Config from tiramisu.option import StrOption, UnicodeOption, OptionDescription from tiramisu.error import PropertiesOptionError @@ -205,11 +205,11 @@ def test_mandatory_warnings_ro(): except PropertiesOptionError as err: proc = err.proptype assert proc == ['mandatory'] - assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] + assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3'] config.read_write() config.str = 'a' config.read_only() - assert list(mandatory_warnings(config)) == ['str1', 'unicode2', 'str3'] + assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str1', 'unicode2', 'str3'] def test_mandatory_warnings_rw(): @@ -218,9 +218,9 @@ def test_mandatory_warnings_rw(): config.str = '' config.read_write() config.str - assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] + assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3'] config.str = 'a' - assert list(mandatory_warnings(config)) == ['str1', 'unicode2', 'str3'] + assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str1', 'unicode2', 'str3'] def test_mandatory_warnings_disabled(): @@ -230,9 +230,9 @@ def test_mandatory_warnings_disabled(): setting = config.cfgimpl_get_settings() config.read_write() config.str - assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] + assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3'] setting[descr.str].append('disabled') - assert list(mandatory_warnings(config)) == ['str1', 'unicode2', 'str3'] + assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str1', 'unicode2', 'str3'] def test_mandatory_warnings_frozen(): @@ -242,7 +242,7 @@ def test_mandatory_warnings_frozen(): setting = config.cfgimpl_get_settings() config.read_write() config.str - assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] + assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3'] setting[descr.str].append('frozen') config.read_only() - assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] + assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3'] diff --git a/tiramisu/config.py b/tiramisu/config.py index c9fb992..473cdb4 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -716,19 +716,5 @@ class MetaConfig(GroupConfig): def mandatory_warnings(config): - """convenience function to trace Options that are mandatory and - where no value has been set - - :returns: generator of mandatory Option's path - - """ - #if value in cache, properties are not calculated - config.cfgimpl_reset_cache(only=('values',)) - for path in config.cfgimpl_get_description().impl_getpaths( - include_groups=True): - try: - config._getattr(path, force_properties=frozenset(('mandatory',))) - except PropertiesOptionError as err: - if err.proptype == ['mandatory']: - yield path - config.cfgimpl_reset_cache(only=('values',)) + #only for retro-compatibility + config.cfgimpl_get_values().mandatory_warnings() diff --git a/tiramisu/value.py b/tiramisu/value.py index ed70f90..c07ddfc 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -21,7 +21,7 @@ from time import time from copy import copy import sys import weakref -from tiramisu.error import ConfigError, SlaveError +from tiramisu.error import ConfigError, SlaveError, PropertiesOptionError from tiramisu.setting import owners, multitypes, expires_time, undefined from tiramisu.autolib import carry_out_calculation from tiramisu.i18n import _ @@ -395,6 +395,42 @@ class Values(object): raise ValueError(_("information's item" " not found: {0}").format(key)) + def mandatory_warnings(self): + """convenience function to trace Options that are mandatory and + where no value has been set + + :returns: generator of mandatory Option's path + + """ + #if value in cache, properties are not calculated + self.reset_cache(False) + context = self.context() + for path in context.cfgimpl_get_description().impl_getpaths( + include_groups=True): + try: + context._getattr(path, + force_properties=frozenset(('mandatory',))) + except PropertiesOptionError as err: + if err.proptype == ['mandatory']: + yield path + self.reset_cache(False) + + def force_cache(self): + """parse all option to force data in cache + """ + context = self.context() + if not 'cache' in context.cfgimpl_get_settings(): + raise PropertiesOptionError(_('can force cache only if cache ' + 'is actived in config')) + #remove all cached properties and value to update "expired" time + context.cfgimpl_reset_cache() + for path in context.cfgimpl_get_description().impl_getpaths( + include_groups=True): + try: + context._getattr(path) + except PropertiesOptionError: + pass + def __getstate__(self): return {'_p_': self._p_} From f2154e23227e0322e183abf6890e405a6fcaaadb Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 9 Mar 2014 20:14:17 +0100 Subject: [PATCH 54/61] update translation --- tiramisu/option.py | 2 +- translations/fr/tiramisu.po | 258 ++++++++++++++++++++---------------- translations/tiramisu.pot | 222 ++++++++++++++++++------------- 3 files changed, 274 insertions(+), 208 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 851872d..20cdf86 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -842,7 +842,7 @@ class PortOption(Option): if self._allow_range and ":" in str(value): value = str(value).split(':') if len(value) != 2: - raise ValueError(_('invalid part, range must have two values ' + raise ValueError(_('invalid port, range must have two values ' 'only')) if not value[0] < value[1]: raise ValueError(_('invalid port, first port in range must be' diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 83a9289..459e281 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,19 +2,19 @@ msgid "" msgstr "" "Project-Id-Version: Tiramisu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-01-25 11:30+CET\n" +"POT-Creation-Date: 2014-03-09 20:13+CET\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: Tiramisu's team \n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" -"Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" "X-Poedit-SourceCharset: UTF-8\n" -#: tiramisu/autolib.py:159 +#: tiramisu/autolib.py:162 msgid "" "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" @@ -29,42 +29,65 @@ msgstr "descr doit être une optiondescription pas un {0}" msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:166 tiramisu/setting.py:339 tiramisu/value.py:57 -#: tiramisu/value.py:427 +#: tiramisu/config.py:164 tiramisu/setting.py:339 tiramisu/value.py:57 +#: tiramisu/value.py:449 msgid "the context does not exist anymore" msgstr "le context n'existe plus" -#: tiramisu/config.py:171 -msgid "" -"no option description found for this config (may be metaconfig without meta)" +#: tiramisu/config.py:169 +msgid "no option description found for this config (may be GroupConfig)" 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 un GroupConfig)" -#: tiramisu/config.py:197 +#: tiramisu/config.py:195 msgid "can't assign to an OptionDescription" msgstr "ne peut pas attribuer une valeur à une OptionDescription" -#: tiramisu/config.py:330 +#: tiramisu/config.py:325 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:369 +#: tiramisu/config.py:364 msgid "no option found in config with these criteria" msgstr "aucune option trouvée dans la config avec ces critères" -#: tiramisu/config.py:419 +#: tiramisu/config.py:414 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:440 +#: tiramisu/config.py:435 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:500 +#: 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/config.py:532 +msgid "cannot serialize Config with MetaConfig" +msgstr "impossible de sérialiser une Config avec une MetaConfig" + +#: tiramisu/config.py:546 +msgid "this storage is not serialisable, could be a none persistent storage" +msgstr "ce storage n'est sérialisable, devrait être une storage non persistant" + +#: tiramisu/config.py:609 +msgid "metaconfig's children must be a list" +msgstr "enfants d'une metaconfig doit être une liste" + +#: tiramisu/config.py:703 +msgid "metaconfig's children should be config, not {0}" +msgstr "enfants d'une metaconfig doit être une config, pas {0}" + +#: tiramisu/config.py:707 +msgid "child has already a metaconfig's" +msgstr "enfant a déjà une metaconfig" + +#: tiramisu/config.py:711 +msgid "all config in metaconfig must have the same optiondescription" +msgstr "" +"toutes les configs d'une metaconfig doivent avoir la même optiondescription" + #: tiramisu/option.py:67 msgid "invalid name: {0} for option" msgstr "nom invalide : {0} pour l'option" @@ -77,7 +100,7 @@ msgstr "type des properties invalide {0} pour {1}, doit être un tuple" 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:141 tiramisu/value.py:376 +#: tiramisu/option.py:141 tiramisu/value.py:395 msgid "information's item not found: {0}" msgstr "aucune config spécifiée alors que c'est nécessaire" @@ -107,242 +130,251 @@ msgstr "" "params définis pour une fonction callback mais par de callback encore " "définis pour l'option {0}" -#: tiramisu/option.py:359 -msgid "option not in all_cons_opts" -msgstr "option non présentante dans all_cons_opts" - -#: tiramisu/option.py:425 tiramisu/option.py:435 +#: tiramisu/option.py:423 tiramisu/option.py:433 msgid "invalid value for option {0}: {1}" msgstr "valeur invalide pour l'option {0} : {1}" -#: tiramisu/option.py:452 +#: tiramisu/option.py:450 msgid "invalid value {0} for option {1} which must be a list" msgstr "valeur invalide pour l'option {0} : {1} laquelle doit être une liste" -#: tiramisu/option.py:508 +#: tiramisu/option.py:506 msgid "consistency should be set with an option" msgstr "consistency doit être configuré avec une option" -#: tiramisu/option.py:510 +#: tiramisu/option.py:508 msgid "cannot add consistency with itself" msgstr "ne peut ajouter une consistency avec lui même" -#: tiramisu/option.py:512 +#: tiramisu/option.py:510 msgid "every options in consistency should be multi or none" msgstr "" "toutes les options d'une consistency devrait être multi ou ne pas l'être" -#: tiramisu/option.py:532 +#: tiramisu/option.py:530 msgid "same value for {0} and {1}" msgstr "même valeur pour {0} et {1}" -#: tiramisu/option.py:641 +#: tiramisu/option.py:623 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:644 +#: tiramisu/option.py:626 msgid "open_values must be a boolean for {0}" msgstr "open_values doit être un booléen pour {0}" -#: tiramisu/option.py:666 +#: tiramisu/option.py:648 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:678 +#: tiramisu/option.py:660 msgid "invalid boolean" msgstr "booléen invalide" -#: tiramisu/option.py:688 +#: tiramisu/option.py:670 msgid "invalid integer" msgstr "nombre invalide" -#: tiramisu/option.py:698 +#: tiramisu/option.py:680 msgid "invalid float" msgstr "invalide nombre flottan" -#: tiramisu/option.py:708 +#: tiramisu/option.py:690 msgid "invalid string" msgstr "invalide caractère" -#: tiramisu/option.py:725 +#: tiramisu/option.py:707 msgid "invalid unicode" msgstr "invalide unicode" -#: tiramisu/option.py:737 +#: tiramisu/option.py:719 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" -#: tiramisu/option.py:787 tiramisu/option.py:792 +#: tiramisu/option.py:770 tiramisu/option.py:773 tiramisu/option.py:778 msgid "invalid IP" msgstr "adresse IP invalide" -#: tiramisu/option.py:797 +#: tiramisu/option.py:783 msgid "invalid IP, mustn't not be in reserved class" msgstr "adresse IP invalide, ne doit pas être d'une classe reservée" -#: tiramisu/option.py:799 +#: tiramisu/option.py:785 msgid "invalid IP, must be in private class" msgstr "adresse IP invalide, doit être dans la classe privée" -#: tiramisu/option.py:837 +#: tiramisu/option.py:823 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:842 +#: tiramisu/option.py:828 msgid "max value is empty" msgstr "la valeur maximum est vide" -#: tiramisu/option.py:882 +#: tiramisu/option.py:845 +msgid "invalid port, range must have two values only" +msgstr "port invalide, une plage doit avoir deux valeurs seulement" + +#: tiramisu/option.py:848 +msgid "invalid port, first port in range must be smaller than the second one" +msgstr "" +"port invalide, le premier port d'une plage doit être plus petit que le second" + +#: tiramisu/option.py:857 +msgid "invalid port" +msgstr "port invalide" + +#: tiramisu/option.py:859 +msgid "invalid port, must be an between {0} and {1}" +msgstr "port invalide, port doit être entre {0} et {1}" + +#: tiramisu/option.py:873 msgid "invalid network address" msgstr "adresse réseau invalide" -#: tiramisu/option.py:887 +#: tiramisu/option.py:878 msgid "invalid network address, must not be in reserved class" msgstr "adresse réseau invalide, ne doit pas être dans la classe reservée" -#: tiramisu/option.py:899 +#: tiramisu/option.py:890 msgid "invalid netmask address" msgstr "masque de sous-réseau invalide" -#: tiramisu/option.py:915 +#: tiramisu/option.py:906 msgid "invalid len for opts" msgstr "longueur invalide pour opts" -#: tiramisu/option.py:927 -msgid "invalid network {0} ({1}) with netmask {2}, this network is an IP" -msgstr "réseau invalide {0} ({1}) avec masque {2}, ce réseau est une IP" - -#: tiramisu/option.py:932 +#: tiramisu/option.py:920 msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" msgstr "IP invalide {0} ({1}) avec masque {2}, cette IP est un réseau" -#: tiramisu/option.py:937 -msgid "invalid IP {0} ({1}) with netmask {2}" -msgstr "IP invalide {0} ({1}) avec masque {2}" - -#: tiramisu/option.py:939 +#: tiramisu/option.py:925 msgid "invalid network {0} ({1}) with netmask {2}" msgstr "réseau invalide {0} ({1}) avec masque {2}" -#: tiramisu/option.py:953 +#: tiramisu/option.py:939 msgid "invalid broadcast address" msgstr "adresse de broadcast invalide" -#: tiramisu/option.py:957 +#: tiramisu/option.py:943 msgid "invalid len for vals" msgstr "longueur invalide pour vals" -#: tiramisu/option.py:962 +#: tiramisu/option.py:948 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:984 +#: tiramisu/option.py:970 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:987 +#: tiramisu/option.py:973 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:989 +#: tiramisu/option.py:975 msgid "allow_without_dot must be a boolean" msgstr "allow_without_dot doit être un booléen" -#: tiramisu/option.py:1028 +#: tiramisu/option.py:1019 msgid "invalid domainname, must have dot" msgstr "nom de domaine invalide, doit avoir un point" -#: tiramisu/option.py:1030 +#: tiramisu/option.py:1021 msgid "invalid domainname's length (max 255)" msgstr "longueur du nom de domaine invalide (maximum {1})" -#: tiramisu/option.py:1032 +#: tiramisu/option.py:1023 msgid "invalid domainname's length (min 2)" msgstr "longueur du nom de domaine invalide (minimum 2)" -#: tiramisu/option.py:1034 +#: tiramisu/option.py:1025 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:1047 +#: tiramisu/option.py:1038 msgid "invalid email address, should contains one @" msgstr "adresse email invalide, devrait contenir un @" -#: tiramisu/option.py:1050 +#: tiramisu/option.py:1041 msgid "invalid username in email address" msgstr "nom d'utilisateur invalide dans une adresse email" -#: tiramisu/option.py:1063 +#: tiramisu/option.py:1054 msgid "invalid url, should start with http:// or https://" msgstr "URL invalide, devrait démarré avec http:// ou https://" -#: tiramisu/option.py:1082 +#: tiramisu/option.py:1073 msgid "invalid url, port must be an between 0 and 65536" msgstr "URL invalide, port doit être entre 0 et 65536" -#: tiramisu/option.py:1088 +#: tiramisu/option.py:1079 msgid "invalid url, should ends with filename" msgstr "URL invalide, devrait finir avec un nom de fichier" -#: tiramisu/option.py:1099 +#: tiramisu/option.py:1091 +msgid "invalid username" +msgstr "utilisateur invalide" + +#: tiramisu/option.py:1102 msgid "invalid filename" msgstr "nom de fichier invalide" -#: tiramisu/option.py:1126 +#: tiramisu/option.py:1129 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:1144 +#: tiramisu/option.py:1147 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:1195 +#: tiramisu/option.py:1198 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:1225 +#: tiramisu/option.py:1228 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:1233 +#: tiramisu/option.py:1236 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:1239 +#: tiramisu/option.py:1242 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:1249 +#: tiramisu/option.py:1252 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:1261 +#: tiramisu/option.py:1264 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:1264 +#: tiramisu/option.py:1267 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:1267 +#: tiramisu/option.py:1270 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:1277 +#: tiramisu/option.py:1280 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:1285 +#: tiramisu/option.py:1288 msgid "callback of master's option shall not refered a slave's ones" msgstr "" "callback d'une variable maitre ne devrait pas référencer des variables " "esclaves" -#: tiramisu/option.py:1293 +#: tiramisu/option.py:1296 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" @@ -458,7 +490,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:514 tiramisu/value.py:315 +#: tiramisu/setting.py:514 tiramisu/value.py:334 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" @@ -495,52 +527,65 @@ 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:322 +#: tiramisu/value.py:341 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:442 +#: tiramisu/value.py:426 +msgid "{0} is already a Multi " +msgstr "{0} est déjà une Multi" + +#: tiramisu/value.py:462 tiramisu/value.py:526 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:466 -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:496 +#: tiramisu/value.py:498 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:535 +#: tiramisu/value.py:536 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:539 +#: tiramisu/value.py:540 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:548 +#: tiramisu/value.py:549 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:556 +#: tiramisu/value.py:557 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:564 +#: tiramisu/value.py:565 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:575 +#: tiramisu/value.py:576 msgid "invalid value {0} for option {1}: {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}" -#: tiramisu/value.py:593 +#: tiramisu/value.py:594 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 "option not in all_cons_opts" +#~ msgstr "option non présentante dans all_cons_opts" + +#~ msgid "invalid network {0} ({1}) with netmask {2}, this network is an IP" +#~ msgstr "réseau invalide {0} ({1}) avec masque {2}, ce réseau est une IP" + +#~ msgid "invalid IP {0} ({1}) with netmask {2}" +#~ msgstr "IP invalide {0} ({1}) avec masque {2}" + +#~ 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" + #~ msgid "" #~ "unable to carry out a calculation, option value with multi types must " #~ "have same length for: {0}" @@ -588,17 +633,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 config, not {0}" -#~ msgstr "enfants d'une metaconfig doit être une config, pas {0}" - -#~ msgid "all config in metaconfig must have same optiondescription" -#~ msgstr "" -#~ "toutes les configs d'une metaconfig doivent avoir la même " -#~ "optiondescription" - -#~ msgid "child has already a metaconfig's" -#~ msgstr "enfant a déjà une metaconfig" - #~ msgid "not allowed group_type : {0}" #~ msgstr "group_type non autorisé : {0}" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index e572a40..2a20646 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2014-01-25 11:30+CET\n" +"POT-Creation-Date: 2014-03-09 20:13+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,7 +15,7 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/autolib.py:159 +#: tiramisu/autolib.py:162 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" @@ -27,39 +27,63 @@ msgstr "" msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:166 tiramisu/setting.py:339 tiramisu/value.py:57 -#: tiramisu/value.py:427 +#: tiramisu/config.py:164 tiramisu/setting.py:339 tiramisu/value.py:57 +#: tiramisu/value.py:449 msgid "the context does not exist anymore" msgstr "" -#: tiramisu/config.py:171 -msgid "no option description found for this config (may be metaconfig without meta)" +#: tiramisu/config.py:169 +msgid "no option description found for this config (may be GroupConfig)" msgstr "" -#: tiramisu/config.py:197 +#: tiramisu/config.py:195 msgid "can't assign to an OptionDescription" msgstr "" -#: tiramisu/config.py:330 +#: tiramisu/config.py:325 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:369 +#: tiramisu/config.py:364 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:419 +#: tiramisu/config.py:414 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:440 +#: tiramisu/config.py:435 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:500 +#: tiramisu/config.py:489 msgid "opt in getowner must be an option not {0}" msgstr "" +#: tiramisu/config.py:532 +msgid "cannot serialize Config with MetaConfig" +msgstr "" + +#: tiramisu/config.py:546 +msgid "this storage is not serialisable, could be a none persistent storage" +msgstr "" + +#: tiramisu/config.py:609 +msgid "metaconfig's children must be a list" +msgstr "" + +#: tiramisu/config.py:703 +msgid "metaconfig's children should be config, not {0}" +msgstr "" + +#: tiramisu/config.py:707 +msgid "child has already a metaconfig's" +msgstr "" + +#: tiramisu/config.py:711 +msgid "all config in metaconfig must have the same optiondescription" +msgstr "" + #: tiramisu/option.py:67 msgid "invalid name: {0} for option" msgstr "" @@ -72,7 +96,7 @@ msgstr "" msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option.py:141 tiramisu/value.py:376 +#: tiramisu/option.py:141 tiramisu/value.py:395 msgid "information's item not found: {0}" msgstr "" @@ -96,235 +120,243 @@ msgstr "" msgid "params defined for a callback function but no callback defined yet for option {0}" msgstr "" -#: tiramisu/option.py:359 -msgid "option not in all_cons_opts" -msgstr "" - -#: tiramisu/option.py:425 tiramisu/option.py:435 +#: tiramisu/option.py:423 tiramisu/option.py:433 msgid "invalid value for option {0}: {1}" msgstr "" -#: tiramisu/option.py:452 +#: tiramisu/option.py:450 msgid "invalid value {0} for option {1} which must be a list" msgstr "" -#: tiramisu/option.py:508 +#: tiramisu/option.py:506 msgid "consistency should be set with an option" msgstr "" -#: tiramisu/option.py:510 +#: tiramisu/option.py:508 msgid "cannot add consistency with itself" msgstr "" -#: tiramisu/option.py:512 +#: tiramisu/option.py:510 msgid "every options in consistency should be multi or none" msgstr "" -#: tiramisu/option.py:532 +#: tiramisu/option.py:530 msgid "same value for {0} and {1}" msgstr "" -#: tiramisu/option.py:641 +#: tiramisu/option.py:623 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:644 +#: tiramisu/option.py:626 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:666 +#: tiramisu/option.py:648 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:678 +#: tiramisu/option.py:660 msgid "invalid boolean" msgstr "" -#: tiramisu/option.py:688 +#: tiramisu/option.py:670 msgid "invalid integer" msgstr "" -#: tiramisu/option.py:698 +#: tiramisu/option.py:680 msgid "invalid float" msgstr "" -#: tiramisu/option.py:708 +#: tiramisu/option.py:690 msgid "invalid string" msgstr "" -#: tiramisu/option.py:725 +#: tiramisu/option.py:707 msgid "invalid unicode" msgstr "" -#: tiramisu/option.py:737 +#: tiramisu/option.py:719 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option.py:787 tiramisu/option.py:792 +#: tiramisu/option.py:770 tiramisu/option.py:773 tiramisu/option.py:778 msgid "invalid IP" msgstr "" -#: tiramisu/option.py:797 +#: tiramisu/option.py:783 msgid "invalid IP, mustn't not be in reserved class" msgstr "" -#: tiramisu/option.py:799 +#: tiramisu/option.py:785 msgid "invalid IP, must be in private class" msgstr "" -#: tiramisu/option.py:837 +#: tiramisu/option.py:823 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option.py:842 +#: tiramisu/option.py:828 msgid "max value is empty" msgstr "" -#: tiramisu/option.py:882 +#: tiramisu/option.py:845 +msgid "invalid port, range must have two values only" +msgstr "" + +#: tiramisu/option.py:848 +msgid "invalid port, first port in range must be smaller than the second one" +msgstr "" + +#: tiramisu/option.py:857 +msgid "invalid port" +msgstr "" + +#: tiramisu/option.py:859 +msgid "invalid port, must be an between {0} and {1}" +msgstr "" + +#: tiramisu/option.py:873 msgid "invalid network address" msgstr "" -#: tiramisu/option.py:887 +#: tiramisu/option.py:878 msgid "invalid network address, must not be in reserved class" msgstr "" -#: tiramisu/option.py:899 +#: tiramisu/option.py:890 msgid "invalid netmask address" msgstr "" -#: tiramisu/option.py:915 +#: tiramisu/option.py:906 msgid "invalid len for opts" msgstr "" -#: tiramisu/option.py:927 -msgid "invalid network {0} ({1}) with netmask {2}, this network is an IP" -msgstr "" - -#: tiramisu/option.py:932 +#: tiramisu/option.py:920 msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" msgstr "" -#: tiramisu/option.py:937 -msgid "invalid IP {0} ({1}) with netmask {2}" -msgstr "" - -#: tiramisu/option.py:939 +#: tiramisu/option.py:925 msgid "invalid network {0} ({1}) with netmask {2}" msgstr "" -#: tiramisu/option.py:953 +#: tiramisu/option.py:939 msgid "invalid broadcast address" msgstr "" -#: tiramisu/option.py:957 +#: tiramisu/option.py:943 msgid "invalid len for vals" msgstr "" -#: tiramisu/option.py:962 +#: tiramisu/option.py:948 msgid "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" msgstr "" -#: tiramisu/option.py:984 +#: tiramisu/option.py:970 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:987 +#: tiramisu/option.py:973 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:989 +#: tiramisu/option.py:975 msgid "allow_without_dot must be a boolean" msgstr "" -#: tiramisu/option.py:1028 +#: tiramisu/option.py:1019 msgid "invalid domainname, must have dot" msgstr "" -#: tiramisu/option.py:1030 +#: tiramisu/option.py:1021 msgid "invalid domainname's length (max 255)" msgstr "" -#: tiramisu/option.py:1032 +#: tiramisu/option.py:1023 msgid "invalid domainname's length (min 2)" msgstr "" -#: tiramisu/option.py:1034 +#: tiramisu/option.py:1025 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:1047 +#: tiramisu/option.py:1038 msgid "invalid email address, should contains one @" msgstr "" -#: tiramisu/option.py:1050 +#: tiramisu/option.py:1041 msgid "invalid username in email address" msgstr "" -#: tiramisu/option.py:1063 +#: tiramisu/option.py:1054 msgid "invalid url, should start with http:// or https://" msgstr "" -#: tiramisu/option.py:1082 +#: tiramisu/option.py:1073 msgid "invalid url, port must be an between 0 and 65536" msgstr "" -#: tiramisu/option.py:1088 +#: tiramisu/option.py:1079 msgid "invalid url, should ends with filename" msgstr "" -#: tiramisu/option.py:1099 +#: tiramisu/option.py:1091 +msgid "invalid username" +msgstr "" + +#: tiramisu/option.py:1102 msgid "invalid filename" msgstr "" -#: tiramisu/option.py:1126 +#: tiramisu/option.py:1129 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:1144 +#: tiramisu/option.py:1147 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:1195 +#: tiramisu/option.py:1198 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:1225 +#: tiramisu/option.py:1228 msgid "consistency with option {0} which is not in Config" msgstr "" -#: tiramisu/option.py:1233 +#: tiramisu/option.py:1236 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:1239 +#: tiramisu/option.py:1242 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:1249 +#: tiramisu/option.py:1252 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:1261 +#: tiramisu/option.py:1264 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:1264 +#: tiramisu/option.py:1267 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:1267 +#: tiramisu/option.py:1270 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:1277 +#: tiramisu/option.py:1280 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:1285 +#: tiramisu/option.py:1288 msgid "callback of master's option shall not refered a slave's ones" msgstr "" -#: tiramisu/option.py:1293 +#: tiramisu/option.py:1296 msgid "group_type: {0} not allowed" msgstr "" @@ -424,7 +456,7 @@ msgstr "" msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:514 tiramisu/value.py:315 +#: tiramisu/setting.py:514 tiramisu/value.py:334 msgid "invalid generic owner {0}" msgstr "" @@ -456,47 +488,47 @@ msgstr "" msgid "a dictionary cannot be persistent" msgstr "" -#: tiramisu/value.py:322 +#: tiramisu/value.py:341 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:442 +#: tiramisu/value.py:426 +msgid "{0} is already a Multi " +msgstr "" + +#: tiramisu/value.py:462 tiramisu/value.py:526 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:466 -msgid "invalid len for the master: {0} which has {1} as slave with greater len" -msgstr "" - -#: tiramisu/value.py:496 +#: tiramisu/value.py:498 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:535 +#: tiramisu/value.py:536 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:539 +#: tiramisu/value.py:540 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:548 +#: tiramisu/value.py:549 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:556 +#: tiramisu/value.py:557 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:564 +#: tiramisu/value.py:565 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:575 +#: tiramisu/value.py:576 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/value.py:593 +#: tiramisu/value.py:594 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "" From fbc79ced078def2550753c52832d74d5cfd18164 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 9 Mar 2014 20:16:38 +0100 Subject: [PATCH 55/61] update translation --- translations/fr/tiramisu.po | 41 +++++++++++++++++++++---------------- translations/tiramisu.pot | 40 ++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 459e281..90a62dc 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Tiramisu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-03-09 20:13+CET\n" +"POT-Creation-Date: 2014-03-09 20:14+CET\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: Tiramisu's team \n" @@ -30,7 +30,7 @@ msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" #: tiramisu/config.py:164 tiramisu/setting.py:339 tiramisu/value.py:57 -#: tiramisu/value.py:449 +#: tiramisu/value.py:485 msgid "the context does not exist anymore" msgstr "le context n'existe plus" @@ -477,24 +477,24 @@ 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:483 +#: tiramisu/setting.py:487 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:489 +#: tiramisu/setting.py:493 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:507 +#: tiramisu/setting.py:511 msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:514 tiramisu/value.py:334 +#: tiramisu/setting.py:518 tiramisu/value.py:334 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" -#: tiramisu/setting.py:602 +#: tiramisu/setting.py:606 msgid "" "malformed requirements imbrication detected for option: '{0}' with " "requirement on: '{1}'" @@ -502,7 +502,7 @@ msgstr "" "imbrication de requirements mal formés detectée pour l'option : '{0}' avec " "requirement sur : '{1}'" -#: tiramisu/setting.py:613 +#: tiramisu/setting.py:617 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}" @@ -531,43 +531,48 @@ msgstr "un espace de stockage dictionary ne peut être persistant" 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:426 +#: tiramisu/value.py:423 +msgid "can force cache only if cache is actived in config" +msgstr "" +"peut force la mise en cache seulement si le cache est activé dans la config" + +#: tiramisu/value.py:462 msgid "{0} is already a Multi " msgstr "{0} est déjà une Multi" -#: tiramisu/value.py:462 tiramisu/value.py:526 +#: tiramisu/value.py:498 tiramisu/value.py:562 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:498 +#: tiramisu/value.py:534 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:536 +#: tiramisu/value.py:572 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:540 +#: tiramisu/value.py:576 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:549 +#: tiramisu/value.py:585 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:557 +#: tiramisu/value.py:593 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:565 +#: tiramisu/value.py:601 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:576 +#: tiramisu/value.py:612 msgid "invalid value {0} for option {1}: {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}" -#: tiramisu/value.py:594 +#: tiramisu/value.py:630 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" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 2a20646..df39688 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2014-03-09 20:13+CET\n" +"POT-Creation-Date: 2014-03-09 20:14+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -28,7 +28,7 @@ msgid "unknown group_type: {0}" msgstr "" #: tiramisu/config.py:164 tiramisu/setting.py:339 tiramisu/value.py:57 -#: tiramisu/value.py:449 +#: tiramisu/value.py:485 msgid "the context does not exist anymore" msgstr "" @@ -444,27 +444,27 @@ msgstr "" msgid "if opt is not None, path should not be None in _getproperties" msgstr "" -#: tiramisu/setting.py:483 +#: tiramisu/setting.py:487 msgid "cannot change the value for option {0} this option is frozen" msgstr "" -#: tiramisu/setting.py:489 +#: tiramisu/setting.py:493 msgid "trying to access to an option named: {0} with properties {1}" msgstr "" -#: tiramisu/setting.py:507 +#: tiramisu/setting.py:511 msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:514 tiramisu/value.py:334 +#: tiramisu/setting.py:518 tiramisu/value.py:334 msgid "invalid generic owner {0}" msgstr "" -#: tiramisu/setting.py:602 +#: tiramisu/setting.py:606 msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" msgstr "" -#: tiramisu/setting.py:613 +#: tiramisu/setting.py:617 msgid "option '{0}' has requirement's property error: {1} {2}" msgstr "" @@ -492,43 +492,47 @@ msgstr "" msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:426 +#: tiramisu/value.py:423 +msgid "can force cache only if cache is actived in config" +msgstr "" + +#: tiramisu/value.py:462 msgid "{0} is already a Multi " msgstr "" -#: tiramisu/value.py:462 tiramisu/value.py:526 +#: tiramisu/value.py:498 tiramisu/value.py:562 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:498 +#: tiramisu/value.py:534 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:536 +#: tiramisu/value.py:572 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:540 +#: tiramisu/value.py:576 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:549 +#: tiramisu/value.py:585 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:557 +#: tiramisu/value.py:593 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:565 +#: tiramisu/value.py:601 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:576 +#: tiramisu/value.py:612 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/value.py:594 +#: tiramisu/value.py:630 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "" From db9ab7a1e991759ccbb7dc5c13af41a0b24cca40 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sun, 9 Mar 2014 20:22:29 +0100 Subject: [PATCH 56/61] test if cache property is not set in config when force_cache --- test/test_cache.py | 7 +++++++ tiramisu/value.py | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/test_cache.py b/test/test_cache.py index ef4f1a3..e1cd609 100644 --- a/test/test_cache.py +++ b/test/test_cache.py @@ -4,7 +4,11 @@ from tiramisu import setting setting.expires_time = 1 from tiramisu.option import IntOption, OptionDescription from tiramisu.config import Config +from tiramisu.error import ConfigError + + from time import sleep, time +from py.test import raises def make_description(): @@ -272,3 +276,6 @@ def test_force_cache(): c.cfgimpl_get_values().force_cache() assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': ([], None), 'u3': ([], None), 'u2': (None, None)} assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u4': (set(['disabled']), None), 'u1': (set([]), None), 'u3': (set([]), None), 'u2': (set([]), None)} + + c.cfgimpl_get_settings().remove('cache') + raises(ConfigError, "c.cfgimpl_get_values().force_cache()") diff --git a/tiramisu/value.py b/tiramisu/value.py index c07ddfc..a82e1a4 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -420,8 +420,8 @@ class Values(object): """ context = self.context() if not 'cache' in context.cfgimpl_get_settings(): - raise PropertiesOptionError(_('can force cache only if cache ' - 'is actived in config')) + raise ConfigError(_('can force cache only if cache ' + 'is actived in config')) #remove all cached properties and value to update "expired" time context.cfgimpl_reset_cache() for path in context.cfgimpl_get_description().impl_getpaths( From d7b04ebed0011a87d92594a6a42d41196aefe850 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 11 Mar 2014 18:57:19 +0100 Subject: [PATCH 57/61] add consistency in_network for IPOption This new consistency can validate that an IPv4 is a specified (network/netmask) network --- test/test_option_consistency.py | 23 ++++ tiramisu/option.py | 12 ++ translations/fr/tiramisu.po | 128 ++++++++++---------- translations/tiramisu.pot | 200 ++++++++++++++++---------------- 4 files changed, 203 insertions(+), 160 deletions(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 1beb4bb..b40c340 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -169,6 +169,29 @@ def test_consistency_network_netmask(): raises(ValueError, "c.a = '192.168.1.1'") +def test_consistency_ip_in_network(): + a = NetworkOption('a', '') + b = NetmaskOption('b', '') + c = IPOption('c', '') + od = OptionDescription('od', '', [a, b, c]) + c.impl_add_consistency('in_network', a, b) + cfg = Config(od) + cfg.a = '192.168.1.0' + cfg.b = '255.255.255.0' + cfg.c = '192.168.1.1' + raises(ValueError, "cfg.c = '192.168.2.1'") + + +def test_consistency_ip_in_network_len_error(): + a = NetworkOption('a', '') + b = NetmaskOption('b', '') + c = IPOption('c', '') + od = OptionDescription('od', '', [a, b, c]) + c.impl_add_consistency('in_network', a) + cfg = Config(od) + raises(ConfigError, "cfg.a = '192.168.2.0'") + + def test_consistency_ip_netmask_network_error(): a = IPOption('a', '') b = NetworkOption('b', '') diff --git a/tiramisu/option.py b/tiramisu/option.py index 20cdf86..7622811 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -784,6 +784,18 @@ class IPOption(Option): if self._private_only and not ip.iptype() == 'PRIVATE': raise ValueError(_("invalid IP, must be in private class")) + def _cons_in_network(self, opts, vals): + if len(vals) != 3: + raise ConfigError(_('invalid len for vals')) + if None in vals: + return + ip, network, netmask = vals + if IP(ip) not in IP('{0}/{1}'.format(network, netmask)): + raise ValueError(_('invalid IP {0} ({1}) with network {2} ' + '({3}) and netmask {4} ({5})').format( + ip, opts[0]._name, network, + opts[1]._name, netmask, opts[2]._name)) + class PortOption(Option): """represents the choice of a port diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 459e281..d80b026 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Tiramisu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-03-09 20:13+CET\n" +"POT-Creation-Date: 2014-03-11 18:51+CET\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: Tiramisu's team \n" @@ -203,187 +203,191 @@ msgstr "adresse IP invalide, ne doit pas être d'une classe reservée" msgid "invalid IP, must be in private class" msgstr "adresse IP invalide, doit être dans la classe privée" -#: tiramisu/option.py:823 +#: tiramisu/option.py:789 tiramisu/option.py:955 +msgid "invalid len for vals" +msgstr "longueur invalide pour vals" + +#: tiramisu/option.py:794 +msgid "invalid IP {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" +msgstr "IP invalide {0} ({1}) avec le réseau {2} ({3}) et le masque {4} ({5})" + +#: tiramisu/option.py:835 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:828 +#: tiramisu/option.py:840 msgid "max value is empty" msgstr "la valeur maximum est vide" -#: tiramisu/option.py:845 +#: tiramisu/option.py:857 msgid "invalid port, range must have two values only" msgstr "port invalide, une plage doit avoir deux valeurs seulement" -#: tiramisu/option.py:848 +#: tiramisu/option.py:860 msgid "invalid port, first port in range must be smaller than the second one" msgstr "" "port invalide, le premier port d'une plage doit être plus petit que le second" -#: tiramisu/option.py:857 +#: tiramisu/option.py:869 msgid "invalid port" msgstr "port invalide" -#: tiramisu/option.py:859 +#: tiramisu/option.py:871 msgid "invalid port, must be an between {0} and {1}" msgstr "port invalide, port doit être entre {0} et {1}" -#: tiramisu/option.py:873 +#: tiramisu/option.py:885 msgid "invalid network address" msgstr "adresse réseau invalide" -#: tiramisu/option.py:878 +#: tiramisu/option.py:890 msgid "invalid network address, must not be in reserved class" msgstr "adresse réseau invalide, ne doit pas être dans la classe reservée" -#: tiramisu/option.py:890 +#: tiramisu/option.py:902 msgid "invalid netmask address" msgstr "masque de sous-réseau invalide" -#: tiramisu/option.py:906 +#: tiramisu/option.py:918 msgid "invalid len for opts" msgstr "longueur invalide pour opts" -#: tiramisu/option.py:920 +#: tiramisu/option.py:932 msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" msgstr "IP invalide {0} ({1}) avec masque {2}, cette IP est un réseau" -#: tiramisu/option.py:925 +#: tiramisu/option.py:937 msgid "invalid network {0} ({1}) with netmask {2}" msgstr "réseau invalide {0} ({1}) avec masque {2}" -#: tiramisu/option.py:939 +#: tiramisu/option.py:951 msgid "invalid broadcast address" msgstr "adresse de broadcast invalide" -#: tiramisu/option.py:943 -msgid "invalid len for vals" -msgstr "longueur invalide pour vals" - -#: tiramisu/option.py:948 +#: tiramisu/option.py:960 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:970 +#: tiramisu/option.py:982 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:973 +#: tiramisu/option.py:985 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:975 +#: tiramisu/option.py:987 msgid "allow_without_dot must be a boolean" msgstr "allow_without_dot doit être un booléen" -#: tiramisu/option.py:1019 +#: tiramisu/option.py:1031 msgid "invalid domainname, must have dot" msgstr "nom de domaine invalide, doit avoir un point" -#: tiramisu/option.py:1021 +#: tiramisu/option.py:1033 msgid "invalid domainname's length (max 255)" msgstr "longueur du nom de domaine invalide (maximum {1})" -#: tiramisu/option.py:1023 +#: tiramisu/option.py:1035 msgid "invalid domainname's length (min 2)" msgstr "longueur du nom de domaine invalide (minimum 2)" -#: tiramisu/option.py:1025 +#: tiramisu/option.py:1037 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:1038 +#: tiramisu/option.py:1050 msgid "invalid email address, should contains one @" msgstr "adresse email invalide, devrait contenir un @" -#: tiramisu/option.py:1041 +#: tiramisu/option.py:1053 msgid "invalid username in email address" msgstr "nom d'utilisateur invalide dans une adresse email" -#: tiramisu/option.py:1054 +#: tiramisu/option.py:1066 msgid "invalid url, should start with http:// or https://" msgstr "URL invalide, devrait démarré avec http:// ou https://" -#: tiramisu/option.py:1073 +#: tiramisu/option.py:1085 msgid "invalid url, port must be an between 0 and 65536" msgstr "URL invalide, port doit être entre 0 et 65536" -#: tiramisu/option.py:1079 +#: tiramisu/option.py:1091 msgid "invalid url, should ends with filename" msgstr "URL invalide, devrait finir avec un nom de fichier" -#: tiramisu/option.py:1091 +#: tiramisu/option.py:1103 msgid "invalid username" msgstr "utilisateur invalide" -#: tiramisu/option.py:1102 +#: tiramisu/option.py:1114 msgid "invalid filename" msgstr "nom de fichier invalide" -#: tiramisu/option.py:1129 +#: tiramisu/option.py:1141 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:1147 +#: tiramisu/option.py:1159 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:1198 +#: tiramisu/option.py:1210 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:1228 +#: tiramisu/option.py:1240 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:1236 +#: tiramisu/option.py:1248 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:1242 +#: tiramisu/option.py:1254 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:1252 +#: tiramisu/option.py:1264 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:1264 +#: tiramisu/option.py:1276 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:1267 +#: tiramisu/option.py:1279 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:1270 +#: tiramisu/option.py:1282 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:1280 +#: tiramisu/option.py:1292 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:1288 +#: tiramisu/option.py:1300 msgid "callback of master's option shall not refered a slave's ones" msgstr "" "callback d'une variable maitre ne devrait pas référencer des variables " "esclaves" -#: tiramisu/option.py:1296 +#: tiramisu/option.py:1308 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:1385 +#: tiramisu/option.py:1397 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:1402 +#: tiramisu/option.py:1414 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -391,66 +395,66 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, expected et action" -#: tiramisu/option.py:1407 +#: tiramisu/option.py:1419 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:1411 +#: tiramisu/option.py:1423 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:1415 +#: tiramisu/option.py:1427 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:1419 +#: tiramisu/option.py:1431 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:1422 +#: tiramisu/option.py:1434 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:1428 +#: tiramisu/option.py:1440 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:1433 +#: tiramisu/option.py:1445 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:1458 +#: tiramisu/option.py:1470 msgid "{0} should be a function" msgstr "{0} doit être une fonction" -#: tiramisu/option.py:1461 +#: tiramisu/option.py:1473 msgid "{0}_params should be a dict" msgstr "{0}_params devrait être un dict" -#: tiramisu/option.py:1464 +#: tiramisu/option.py:1476 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:1468 +#: tiramisu/option.py:1480 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" -#: tiramisu/option.py:1474 +#: tiramisu/option.py:1486 msgid "validator not support tuple" msgstr "validator n'accepte pas de tuple" -#: tiramisu/option.py:1477 +#: tiramisu/option.py:1489 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:1481 +#: tiramisu/option.py:1493 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" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 2a20646..d35e6a5 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2014-03-09 20:13+CET\n" +"POT-Creation-Date: 2014-03-11 18:51+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -192,235 +192,239 @@ msgstr "" msgid "invalid IP, must be in private class" msgstr "" -#: tiramisu/option.py:823 -msgid "inconsistency in allowed range" -msgstr "" - -#: tiramisu/option.py:828 -msgid "max value is empty" -msgstr "" - -#: tiramisu/option.py:845 -msgid "invalid port, range must have two values only" -msgstr "" - -#: tiramisu/option.py:848 -msgid "invalid port, first port in range must be smaller than the second one" -msgstr "" - -#: tiramisu/option.py:857 -msgid "invalid port" -msgstr "" - -#: tiramisu/option.py:859 -msgid "invalid port, must be an between {0} and {1}" -msgstr "" - -#: tiramisu/option.py:873 -msgid "invalid network address" -msgstr "" - -#: tiramisu/option.py:878 -msgid "invalid network address, must not be in reserved class" -msgstr "" - -#: tiramisu/option.py:890 -msgid "invalid netmask address" -msgstr "" - -#: tiramisu/option.py:906 -msgid "invalid len for opts" -msgstr "" - -#: tiramisu/option.py:920 -msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" -msgstr "" - -#: tiramisu/option.py:925 -msgid "invalid network {0} ({1}) with netmask {2}" -msgstr "" - -#: tiramisu/option.py:939 -msgid "invalid broadcast address" -msgstr "" - -#: tiramisu/option.py:943 +#: tiramisu/option.py:789 tiramisu/option.py:955 msgid "invalid len for vals" msgstr "" -#: tiramisu/option.py:948 +#: tiramisu/option.py:794 +msgid "invalid IP {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" +msgstr "" + +#: tiramisu/option.py:835 +msgid "inconsistency in allowed range" +msgstr "" + +#: tiramisu/option.py:840 +msgid "max value is empty" +msgstr "" + +#: tiramisu/option.py:857 +msgid "invalid port, range must have two values only" +msgstr "" + +#: tiramisu/option.py:860 +msgid "invalid port, first port in range must be smaller than the second one" +msgstr "" + +#: tiramisu/option.py:869 +msgid "invalid port" +msgstr "" + +#: tiramisu/option.py:871 +msgid "invalid port, must be an between {0} and {1}" +msgstr "" + +#: tiramisu/option.py:885 +msgid "invalid network address" +msgstr "" + +#: tiramisu/option.py:890 +msgid "invalid network address, must not be in reserved class" +msgstr "" + +#: tiramisu/option.py:902 +msgid "invalid netmask address" +msgstr "" + +#: tiramisu/option.py:918 +msgid "invalid len for opts" +msgstr "" + +#: tiramisu/option.py:932 +msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" +msgstr "" + +#: tiramisu/option.py:937 +msgid "invalid network {0} ({1}) with netmask {2}" +msgstr "" + +#: tiramisu/option.py:951 +msgid "invalid broadcast address" +msgstr "" + +#: tiramisu/option.py:960 msgid "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" msgstr "" -#: tiramisu/option.py:970 +#: tiramisu/option.py:982 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:973 +#: tiramisu/option.py:985 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:975 +#: tiramisu/option.py:987 msgid "allow_without_dot must be a boolean" msgstr "" -#: tiramisu/option.py:1019 +#: tiramisu/option.py:1031 msgid "invalid domainname, must have dot" msgstr "" -#: tiramisu/option.py:1021 +#: tiramisu/option.py:1033 msgid "invalid domainname's length (max 255)" msgstr "" -#: tiramisu/option.py:1023 +#: tiramisu/option.py:1035 msgid "invalid domainname's length (min 2)" msgstr "" -#: tiramisu/option.py:1025 +#: tiramisu/option.py:1037 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:1038 +#: tiramisu/option.py:1050 msgid "invalid email address, should contains one @" msgstr "" -#: tiramisu/option.py:1041 +#: tiramisu/option.py:1053 msgid "invalid username in email address" msgstr "" -#: tiramisu/option.py:1054 +#: tiramisu/option.py:1066 msgid "invalid url, should start with http:// or https://" msgstr "" -#: tiramisu/option.py:1073 +#: tiramisu/option.py:1085 msgid "invalid url, port must be an between 0 and 65536" msgstr "" -#: tiramisu/option.py:1079 +#: tiramisu/option.py:1091 msgid "invalid url, should ends with filename" msgstr "" -#: tiramisu/option.py:1091 +#: tiramisu/option.py:1103 msgid "invalid username" msgstr "" -#: tiramisu/option.py:1102 +#: tiramisu/option.py:1114 msgid "invalid filename" msgstr "" -#: tiramisu/option.py:1129 +#: tiramisu/option.py:1141 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:1147 +#: tiramisu/option.py:1159 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:1198 +#: tiramisu/option.py:1210 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:1228 +#: tiramisu/option.py:1240 msgid "consistency with option {0} which is not in Config" msgstr "" -#: tiramisu/option.py:1236 +#: tiramisu/option.py:1248 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:1242 +#: tiramisu/option.py:1254 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:1252 +#: tiramisu/option.py:1264 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:1264 +#: tiramisu/option.py:1276 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:1267 +#: tiramisu/option.py:1279 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:1270 +#: tiramisu/option.py:1282 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:1280 +#: tiramisu/option.py:1292 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:1288 +#: tiramisu/option.py:1300 msgid "callback of master's option shall not refered a slave's ones" msgstr "" -#: tiramisu/option.py:1296 +#: tiramisu/option.py:1308 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1385 +#: tiramisu/option.py:1397 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1402 +#: tiramisu/option.py:1414 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1407 +#: tiramisu/option.py:1419 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1411 +#: tiramisu/option.py:1423 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1415 +#: tiramisu/option.py:1427 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1419 +#: tiramisu/option.py:1431 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1422 +#: tiramisu/option.py:1434 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1428 +#: tiramisu/option.py:1440 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1433 +#: tiramisu/option.py:1445 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "" -#: tiramisu/option.py:1458 +#: tiramisu/option.py:1470 msgid "{0} should be a function" msgstr "" -#: tiramisu/option.py:1461 +#: tiramisu/option.py:1473 msgid "{0}_params should be a dict" msgstr "" -#: tiramisu/option.py:1464 +#: tiramisu/option.py:1476 msgid "{0}_params with key {1} should not have length different to 1" msgstr "" -#: tiramisu/option.py:1468 +#: tiramisu/option.py:1480 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "" -#: tiramisu/option.py:1474 +#: tiramisu/option.py:1486 msgid "validator not support tuple" msgstr "" -#: tiramisu/option.py:1477 +#: tiramisu/option.py:1489 msgid "{0}_params should have an option not a {0} for first argument" msgstr "" -#: tiramisu/option.py:1481 +#: tiramisu/option.py:1493 msgid "{0}_params should have a boolean not a {0} for second argument" msgstr "" From 544cd93c733c6033549b5972b95d894cd5cf2461 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 12 Mar 2014 14:57:36 +0100 Subject: [PATCH 58/61] can make_dict with disabled suboption --- test/test_config_api.py | 12 ++++++++++++ tiramisu/config.py | 23 +++++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/test/test_config_api.py b/test/test_config_api.py index 4be9e80..93f8c95 100644 --- a/test/test_config_api.py +++ b/test/test_config_api.py @@ -115,6 +115,18 @@ def test_make_dict(): raises(ValueError, 'd2 = config.make_dict(withvalue="3")') +def test_make_dict_with_disabled(): + descr = OptionDescription("opt", "", [ + OptionDescription("s1", "", [ + BoolOption("a", "", default=False), + BoolOption("b", "", default=False, properties=('disabled',))]), + IntOption("int", "", default=42)]) + config = Config(descr) + config.read_only() + d = config.make_dict() + assert d == {"s1.a": False, "int": 42} + + def test_find_in_config(): "finds option in config" descr = make_description() diff --git a/tiramisu/config.py b/tiramisu/config.py index c9fb992..4c59dbe 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -448,17 +448,20 @@ class SubConfig(object): return pathsvalues def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten): - if isinstance(opt, OptionDescription): - pathsvalues += getattr(self, path).make_dict(flatten, - _currpath + - path.split('.')) - else: - value = self._getattr(opt._name) - if flatten: - name = opt._name + try: + if isinstance(opt, OptionDescription): + pathsvalues += getattr(self, path).make_dict(flatten, + _currpath + + path.split('.')) else: - name = '.'.join(_currpath + [opt._name]) - pathsvalues.append((name, value)) + value = self._getattr(opt._name) + if flatten: + name = opt._name + else: + name = '.'.join(_currpath + [opt._name]) + pathsvalues.append((name, value)) + except PropertiesOptionError: + pass def cfgimpl_get_path(self): descr = self.cfgimpl_get_description() From 5f46763696f2b5cc3758d6efa568005cbb250a68 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 12 Mar 2014 16:44:48 +0100 Subject: [PATCH 59/61] add test for consistency with callback --- test/test_option_consistency.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index b40c340..45e8213 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -345,3 +345,17 @@ def test_consistency_permissive(): c.cfgimpl_get_settings().setpermissive(('hidden',)) c.read_write() c.a = 1 + + +def return_val(*args, **kwargs): + return '192.168.1.1' + + +def test_consistency_with_callback(): + a = NetworkOption('a', '', default='192.168.1.0') + b = NetmaskOption('b', '', default='255.255.255.0') + c = IPOption('c', '', callback=return_val, callback_params={'': ((a, False),)}) + od = OptionDescription('od', '', [a, b, c]) + c.impl_add_consistency('in_network', a, b) + cfg = Config(od) + cfg.c From ff802b46e5bb9a465807fc5f8853695e1661e6e7 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 12 Mar 2014 21:56:53 +0100 Subject: [PATCH 60/61] consistencies can make a warning instead of raises for that, you have to set something like: a.impl_add_consistency('not_equal', b, warnings_only=True) warning product now adapted message --- test/test_option_consistency.py | 16 ++- test/test_option_validator.py | 18 +-- tiramisu/option.py | 164 ++++++++++++++-------- translations/fr/tiramisu.po | 239 ++++++++++++++++++-------------- translations/tiramisu.pot | 236 +++++++++++++++++-------------- 5 files changed, 391 insertions(+), 282 deletions(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index b40c340..0a7de3c 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -5,7 +5,8 @@ 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 +from tiramisu.error import ConfigError, ValueWarning +import warnings def test_consistency(): @@ -19,6 +20,19 @@ def test_consistency(): raises(ConfigError, "a.impl_add_consistency('not_equal', 'a')") +def test_consistency_warnings_only(): + a = IntOption('a', '') + b = IntOption('b', '') + od = OptionDescription('od', '', [a, b]) + a.impl_add_consistency('not_equal', b, warnings_only=True) + c = Config(od) + c.a = 1 + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + c.b = 1 + assert w != [] + + def test_consistency_not_equal(): a = IntOption('a', '') b = IntOption('b', '') diff --git a/test/test_option_validator.py b/test/test_option_validator.py index dc08c82..76ebfc8 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -88,7 +88,7 @@ def test_validator_warning(): cfg.opt2 = 'val' assert len(w) == 1 assert w[0].message.opt == opt2 - assert str(w[0].message) == _('invalid value for option {0}: {1}').format('opt2', 'error') + assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('opt2', 'error') # with warnings.catch_warnings(record=True) as w: cfg.opt3.append('val') @@ -98,7 +98,7 @@ def test_validator_warning(): cfg.opt3.append('val1') assert len(w) == 1 assert w[0].message.opt == opt3 - assert str(w[0].message) == _('invalid value for option {0}: {1}').format('opt3', 'error') + assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('opt3', 'error') raises(ValueError, "cfg.opt2 = 1") # with warnings.catch_warnings(record=True) as w: @@ -106,9 +106,9 @@ def test_validator_warning(): cfg.opt3.append('val') assert len(w) == 2 assert w[0].message.opt == opt2 - assert str(w[0].message) == _('invalid value for option {0}: {1}').format('opt2', 'error') + assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('opt2', 'error') assert w[1].message.opt == opt3 - assert str(w[1].message) == _('invalid value for option {0}: {1}').format('opt3', 'error') + assert str(w[1].message) == _("warning on the value of the option {0}: {1}").format('opt3', 'error') def test_validator_warning_master_slave(): @@ -128,29 +128,29 @@ def test_validator_warning_master_slave(): 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 for option {0}: {1}').format('netmask_admin_eth0', 'error') + assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('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 for option {0}: {1}').format('ip_admin_eth0', 'error') + assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('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 for option {0}: {1}').format('ip_admin_eth0', 'error') + assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('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 for option {0}: {1}').format('ip_admin_eth0', 'error') + assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('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 for option {0}: {1}').format('ip_admin_eth0', 'error') + assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('ip_admin_eth0', 'error') diff --git a/tiramisu/option.py b/tiramisu/option.py index 7622811..db6acbd 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -336,7 +336,7 @@ class Option(BaseOption): self._consistencies = None def _launch_consistency(self, func, option, value, context, index, - all_cons_opts): + all_cons_opts, warnings_only): """Launch consistency now :param func: function name, this name should start with _cons_ @@ -351,6 +351,8 @@ class Option(BaseOption): :type index: `int` :param all_cons_opts: all options concerne by this consistency :type all_cons_opts: `list` of `tiramisu.option.Option` + :param warnings_only: specific raise error for warning + :type warnings_only: `boolean` """ if context is not None: descr = context.cfgimpl_get_description() @@ -379,7 +381,7 @@ class Option(BaseOption): except IndexError: #so return if no value return - getattr(self, func)(all_cons_opts, all_cons_vals) + getattr(self, func)(all_cons_opts, all_cons_vals, warnings_only) def impl_validate(self, value, context=None, validate=True, force_index=None): @@ -422,22 +424,31 @@ class Option(BaseOption): except ValueError as err: raise ValueError(_('invalid value for option {0}: {1}' '').format(self._name, err)) + error = None + warning = None try: # valid with self._validator val_validator(_value) - # if not context launch consistency validation + # if 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 for option {0}: {1}").format( - self._name, err) + descr._valid_consistency(self, _value, context, _index, + self._warnings_only) + self._second_level_validation(_value, self._warnings_only) + except ValueError as error: if self._warnings_only: - warnings.warn_explicit(ValueWarning(msg, self), - ValueWarning, - self.__class__.__name__, 0) - else: - raise ValueError(msg) + warning = error + error = None + except ValueWarning as warning: + pass + if warning: + msg = _("warning on the value of the option {0}: {1}").format( + self._name, warning) + warnings.warn_explicit(ValueWarning(msg, self), + ValueWarning, + self.__class__.__name__, 0) + elif error: + raise ValueError(_("invalid value for option {0}: {1}").format( + self._name, error)) # generic calculation if context is not None: @@ -490,7 +501,7 @@ class Option(BaseOption): def impl_is_multi(self): return self._multi - def impl_add_consistency(self, func, *other_opts): + def impl_add_consistency(self, func, *other_opts, **params): """Add consistency means that value will be validate with other_opts option's values. @@ -498,16 +509,18 @@ class Option(BaseOption): :type func: `str` :param other_opts: options used to validate value :type other_opts: `list` of `tiramisu.option.Option` + :param params: extra params (only warnings_only are allowed) """ if self._consistencies is None: self._consistencies = [] + warnings_only = params.get('warnings_only', False) for opt in other_opts: if not isinstance(opt, Option): - raise ConfigError(_('consistency should be set with an option')) + raise ConfigError(_('consistency must be set with an option')) if self is opt: raise ConfigError(_('cannot add consistency with itself')) if self.impl_is_multi() != opt.impl_is_multi(): - raise ConfigError(_('every options in consistency should be ' + raise ConfigError(_('every options in consistency must be ' 'multi or none')) func = '_cons_{0}'.format(func) all_cons_opts = tuple([self] + list(other_opts)) @@ -516,19 +529,23 @@ class Option(BaseOption): if self.impl_is_multi(): for idx, val in enumerate(value): self._launch_consistency(func, self, val, None, - idx, all_cons_opts) + idx, all_cons_opts, warnings_only) else: self._launch_consistency(func, self, value, None, - None, all_cons_opts) - self._consistencies.append((func, all_cons_opts)) + None, all_cons_opts, warnings_only) + self._consistencies.append((func, all_cons_opts, params)) self.impl_validate(self.impl_getdefault()) - def _cons_not_equal(self, opts, vals): + def _cons_not_equal(self, opts, vals, warnings_only): 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)) + if warnings_only: + msg = _("same value for {0} and {1}, should be different") + else: + msg = _("same value for {0} and {1}, must be different") + raise ValueError(msg.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: @@ -592,14 +609,14 @@ class Option(BaseOption): 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))) + new_value.append((consistency[0], tuple(values), consistency[2])) if load: del(self._state_consistencies) self._consistencies = new_value else: self._state_consistencies = new_value - def _second_level_validation(self, value): + def _second_level_validation(self, value, warnings_only): pass @@ -777,24 +794,36 @@ class IPOption(Option): except ValueError: raise ValueError(_('invalid IP')) - def _second_level_validation(self, value): + def _second_level_validation(self, value, warnings_only): ip = IP('{0}/32'.format(value)) if not self._allow_reserved and ip.iptype() == 'RESERVED': - raise ValueError(_("invalid IP, mustn't not be in reserved class")) + if warnings_only: + msg = _("IP shouldn't be in reserved class") + else: + msg = _("invalid IP, mustn't be in reserved class") + raise ValueError(msg) if self._private_only and not ip.iptype() == 'PRIVATE': - raise ValueError(_("invalid IP, must be in private class")) + if warnings_only: + msg = _("IP should be in private class") + else: + msg = _("invalid IP, must be in private class") + raise ValueError(msg) - def _cons_in_network(self, opts, vals): + def _cons_in_network(self, opts, vals, warnings_only): if len(vals) != 3: raise ConfigError(_('invalid len for vals')) if None in vals: return ip, network, netmask = vals if IP(ip) not in IP('{0}/{1}'.format(network, netmask)): - raise ValueError(_('invalid IP {0} ({1}) with network {2} ' - '({3}) and netmask {4} ({5})').format( - ip, opts[0]._name, network, - opts[1]._name, netmask, opts[2]._name)) + if warnings_only: + msg = _('IP {0} ({1}) not in network {2} ({3}) with netmask {4}' + ' ({5})') + else: + msg = _('invalid IP {0} ({1}) not in network {2} ({3}) with ' + 'netmask {4} ({5})') + raise ValueError(msg.format(ip, opts[0]._name, network, + opts[1]._name, netmask, opts[2]._name)) class PortOption(Option): @@ -884,10 +913,14 @@ class NetworkOption(Option): except ValueError: raise ValueError(_('invalid network address')) - def _second_level_validation(self, value): + def _second_level_validation(self, value, warnings_only): ip = IP(value) if ip.iptype() == 'RESERVED': - raise ValueError(_("invalid network address, must not be in reserved class")) + if warnings_only: + msg = _("network address shouldn't be in reserved class") + else: + msg = _("invalid network address, mustn't be in reserved class") + raise ValueError(msg) class NetmaskOption(Option): @@ -901,19 +934,20 @@ class NetmaskOption(Option): except ValueError: raise ValueError(_('invalid netmask address')) - def _cons_network_netmask(self, opts, vals): + def _cons_network_netmask(self, opts, vals, warnings_only): #opts must be (netmask, network) options if None in vals: return - self.__cons_netmask(opts, vals[0], vals[1], False) + self.__cons_netmask(opts, vals[0], vals[1], False, warnings_only) - def _cons_ip_netmask(self, opts, vals): + def _cons_ip_netmask(self, opts, vals, warnings_only): #opts must be (netmask, ip) options if None in vals: return - self.__cons_netmask(opts, vals[0], vals[1], True) + self.__cons_netmask(opts, vals[0], vals[1], True, warnings_only) - def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net): + def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net, + warnings_only): if len(opts) != 2: raise ConfigError(_('invalid len for opts')) msg = None @@ -950,7 +984,7 @@ class BroadcastOption(Option): except ValueError: raise ValueError(_('invalid broadcast address')) - def _cons_broadcast(self, opts, vals): + def _cons_broadcast(self, opts, vals, warnings_only): if len(vals) != 3: raise ConfigError(_('invalid len for vals')) if None in vals: @@ -1047,7 +1081,7 @@ class EmailOption(DomainnameOption): try: username, domain = splitted except ValueError: - raise ValueError(_('invalid email address, should contains one @' + raise ValueError(_('invalid email address, must contains one @' )) if not self.username_re.search(username): raise ValueError(_('invalid username in email address')) @@ -1063,7 +1097,7 @@ class URLOption(DomainnameOption): def _validate(self, value): match = self.proto_re.search(value) if not match: - raise ValueError(_('invalid url, should start with http:// or ' + raise ValueError(_('invalid url, must start with http:// or ' 'https://')) value = value[len(match.group(0)):] # get domain/files @@ -1088,7 +1122,7 @@ class URLOption(DomainnameOption): super(URLOption, self)._validate(domain) # validate file if files is not None and files != '' and not self.path_re.search(files): - raise ValueError(_('invalid url, should ends with filename')) + raise ValueError(_('invalid url, must ends with filename')) class UsernameOption(Option): @@ -1217,11 +1251,12 @@ class OptionDescription(BaseOption): if not force_no_consistencies and \ option._consistencies is not None: for consistency in option._consistencies: - func, all_cons_opts = consistency + func, all_cons_opts, params = consistency for opt in all_cons_opts: _consistencies.setdefault(opt, []).append((func, - all_cons_opts)) + all_cons_opts, + params)) else: _currpath.append(attr) option.impl_build_cache(cache_path, @@ -1311,18 +1346,29 @@ class OptionDescription(BaseOption): def impl_get_group_type(self): return self._group_type - def _valid_consistency(self, option, value, context, index): + def _valid_consistency(self, option, value, context, index, warnings_only): 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, all_cons_opts in consistencies: + for func, all_cons_opts, params in consistencies: + if not warnings_only: + l_warnings_only = params.get('warnings_only', False) + else: + l_warnings_only = warnings_only #all_cons_opts[0] is the option where func is set - all_cons_opts[0]._launch_consistency(func, option, - value, - context, index, - all_cons_opts) + try: + all_cons_opts[0]._launch_consistency(func, option, + value, + context, index, + all_cons_opts, + l_warnings_only) + except ValueError as err: + if l_warnings_only: + raise ValueWarning(err.message, option) + else: + raise err def _impl_getstate(self, descr=None): """enables us to export into a dict @@ -1432,7 +1478,7 @@ def validate_requires_arg(requires, name): 'must be an option in option {0}').format(name)) if option.impl_is_multi(): raise ValueError(_('malformed requirements option {0} ' - 'should not be a multi').format(name)) + 'must not be a multi').format(name)) if expected is not None: try: option._validate(expected) @@ -1467,17 +1513,17 @@ def validate_requires_arg(requires, name): def validate_callback(callback, callback_params, type_): if type(callback) != FunctionType: - raise ValueError(_('{0} should be a function').format(type_)) + raise ValueError(_('{0} must 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_)) + raise ValueError(_('{0}_params must 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_, + raise ValueError(_("{0}_params with key {1} mustn't have " + "length different to 1").format(type_, key)) if not isinstance(callbacks, tuple): - raise ValueError(_('{0}_params should be tuple for key "{1}"' + raise ValueError(_('{0}_params must be tuple for key "{1}"' ).format(type_, key)) for callbk in callbacks: if isinstance(callbk, tuple): @@ -1486,11 +1532,11 @@ def validate_callback(callback, callback_params, type_): raise ValueError(_('validator not support tuple')) if not isinstance(option, Option) and not \ isinstance(option, SymLinkOption): - raise ValueError(_('{0}_params should have an option ' + raise ValueError(_('{0}_params must 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' + raise ValueError(_('{0}_params must have a boolean' ' not a {0} for second argument' ).format(type_, type( force_permissive))) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index d80b026..e5ce1a1 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Tiramisu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-03-11 18:51+CET\n" +"POT-Creation-Date: 2014-03-12 21:49+CET\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: Tiramisu's team \n" @@ -130,264 +130,289 @@ msgstr "" "params définis pour une fonction callback mais par de callback encore " "définis pour l'option {0}" -#: tiramisu/option.py:423 tiramisu/option.py:433 +#: tiramisu/option.py:425 tiramisu/option.py:450 msgid "invalid value for option {0}: {1}" msgstr "valeur invalide pour l'option {0} : {1}" -#: tiramisu/option.py:450 +#: tiramisu/option.py:444 +msgid "warning on the value of the option {0}: {1}" +msgstr "avertissement sur la valeur de l'option {0} : {1}" + +#: tiramisu/option.py:461 msgid "invalid value {0} for option {1} which must be a list" msgstr "valeur invalide pour l'option {0} : {1} laquelle doit être une liste" -#: tiramisu/option.py:506 -msgid "consistency should be set with an option" +#: tiramisu/option.py:519 +msgid "consistency must be set with an option" msgstr "consistency doit être configuré avec une option" -#: tiramisu/option.py:508 +#: tiramisu/option.py:521 msgid "cannot add consistency with itself" msgstr "ne peut ajouter une consistency avec lui même" -#: tiramisu/option.py:510 -msgid "every options in consistency should be multi or none" +#: tiramisu/option.py:523 +msgid "every options in consistency must be multi or none" msgstr "" -"toutes les options d'une consistency devrait être multi ou ne pas l'être" +"toutes les options d'une consistency doivent être multi ou ne pas l'être" -#: tiramisu/option.py:530 -msgid "same value for {0} and {1}" -msgstr "même valeur pour {0} et {1}" +#: tiramisu/option.py:544 +msgid "same value for {0} and {1}, should be different" +msgstr "même valeur pour {0} et {1}, devrait être différent" -#: tiramisu/option.py:623 +#: tiramisu/option.py:546 +msgid "same value for {0} and {1}, must be different" +msgstr "même valeur pour {0} et {1}, doit être différent" + +#: tiramisu/option.py:640 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:626 +#: tiramisu/option.py:643 msgid "open_values must be a boolean for {0}" msgstr "open_values doit être un booléen pour {0}" -#: tiramisu/option.py:648 +#: tiramisu/option.py:665 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:660 +#: tiramisu/option.py:677 msgid "invalid boolean" msgstr "booléen invalide" -#: tiramisu/option.py:670 +#: tiramisu/option.py:687 msgid "invalid integer" msgstr "nombre invalide" -#: tiramisu/option.py:680 +#: tiramisu/option.py:697 msgid "invalid float" msgstr "invalide nombre flottan" -#: tiramisu/option.py:690 +#: tiramisu/option.py:707 msgid "invalid string" msgstr "invalide caractère" -#: tiramisu/option.py:707 +#: tiramisu/option.py:724 msgid "invalid unicode" msgstr "invalide unicode" -#: tiramisu/option.py:719 +#: tiramisu/option.py:736 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" -#: tiramisu/option.py:770 tiramisu/option.py:773 tiramisu/option.py:778 +#: tiramisu/option.py:787 tiramisu/option.py:790 tiramisu/option.py:795 msgid "invalid IP" msgstr "adresse IP invalide" -#: tiramisu/option.py:783 -msgid "invalid IP, mustn't not be in reserved class" -msgstr "adresse IP invalide, ne doit pas être d'une classe reservée" +#: tiramisu/option.py:801 +msgid "IP shouldn't be in reserved class" +msgstr "l'adresse IP ne devrait pas être d'une classe réservée" -#: tiramisu/option.py:785 +#: tiramisu/option.py:803 +msgid "invalid IP, mustn't be in reserved class" +msgstr "adresse IP invalide, ne doit pas être dans une classe réservée" + +#: tiramisu/option.py:807 +msgid "IP should be in private class" +msgstr "l'adresse IP devrait être dans une classe privée" + +#: tiramisu/option.py:809 msgid "invalid IP, must be in private class" msgstr "adresse IP invalide, doit être dans la classe privée" -#: tiramisu/option.py:789 tiramisu/option.py:955 +#: tiramisu/option.py:814 tiramisu/option.py:989 msgid "invalid len for vals" msgstr "longueur invalide pour vals" -#: tiramisu/option.py:794 -msgid "invalid IP {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" -msgstr "IP invalide {0} ({1}) avec le réseau {2} ({3}) et le masque {4} ({5})" +#: tiramisu/option.py:820 +msgid "IP {0} ({1}) not in network {2} ({3}) with netmask {4} ({5})" +msgstr "IP {0} ({1}) pas dans le réseau {2} ({3}) avec le masque {4} ({5})" -#: tiramisu/option.py:835 +#: tiramisu/option.py:823 +msgid "invalid IP {0} ({1}) not in network {2} ({3}) with netmask {4} ({5})" +msgstr "" +"IP invalide {0} ({1}) pas dans le réseau {2} ({3}) avec le masque {4} ({5})" + +#: tiramisu/option.py:864 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:840 +#: tiramisu/option.py:869 msgid "max value is empty" msgstr "la valeur maximum est vide" -#: tiramisu/option.py:857 +#: tiramisu/option.py:886 msgid "invalid port, range must have two values only" msgstr "port invalide, une plage doit avoir deux valeurs seulement" -#: tiramisu/option.py:860 +#: tiramisu/option.py:889 msgid "invalid port, first port in range must be smaller than the second one" msgstr "" "port invalide, le premier port d'une plage doit être plus petit que le second" -#: tiramisu/option.py:869 +#: tiramisu/option.py:898 msgid "invalid port" msgstr "port invalide" -#: tiramisu/option.py:871 +#: tiramisu/option.py:900 msgid "invalid port, must be an between {0} and {1}" msgstr "port invalide, port doit être entre {0} et {1}" -#: tiramisu/option.py:885 +#: tiramisu/option.py:914 msgid "invalid network address" msgstr "adresse réseau invalide" -#: tiramisu/option.py:890 -msgid "invalid network address, must not be in reserved class" -msgstr "adresse réseau invalide, ne doit pas être dans la classe reservée" +#: tiramisu/option.py:920 +msgid "network address shouldn't be in reserved class" +msgstr "l'adresse réseau ne devait pas être dans la classe réservée" -#: tiramisu/option.py:902 +#: tiramisu/option.py:922 +msgid "invalid network address, mustn't be in reserved class" +msgstr "adresse réseau invalide, ne doit pas être dans la classe réservée" + +#: tiramisu/option.py:935 msgid "invalid netmask address" msgstr "masque de sous-réseau invalide" -#: tiramisu/option.py:918 +#: tiramisu/option.py:952 msgid "invalid len for opts" msgstr "longueur invalide pour opts" -#: tiramisu/option.py:932 +#: tiramisu/option.py:966 msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" msgstr "IP invalide {0} ({1}) avec masque {2}, cette IP est un réseau" -#: tiramisu/option.py:937 +#: tiramisu/option.py:971 msgid "invalid network {0} ({1}) with netmask {2}" msgstr "réseau invalide {0} ({1}) avec masque {2}" -#: tiramisu/option.py:951 +#: tiramisu/option.py:985 msgid "invalid broadcast address" msgstr "adresse de broadcast invalide" -#: tiramisu/option.py:960 +#: tiramisu/option.py:994 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:982 +#: tiramisu/option.py:1016 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:985 +#: tiramisu/option.py:1019 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:987 +#: tiramisu/option.py:1021 msgid "allow_without_dot must be a boolean" msgstr "allow_without_dot doit être un booléen" -#: tiramisu/option.py:1031 +#: tiramisu/option.py:1065 msgid "invalid domainname, must have dot" msgstr "nom de domaine invalide, doit avoir un point" -#: tiramisu/option.py:1033 +#: tiramisu/option.py:1067 msgid "invalid domainname's length (max 255)" msgstr "longueur du nom de domaine invalide (maximum {1})" -#: tiramisu/option.py:1035 +#: tiramisu/option.py:1069 msgid "invalid domainname's length (min 2)" msgstr "longueur du nom de domaine invalide (minimum 2)" -#: tiramisu/option.py:1037 +#: tiramisu/option.py:1071 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:1050 -msgid "invalid email address, should contains one @" -msgstr "adresse email invalide, devrait contenir un @" +#: tiramisu/option.py:1084 +msgid "invalid email address, must contains one @" +msgstr "adresse email invalide, doit contenir un @" -#: tiramisu/option.py:1053 +#: tiramisu/option.py:1087 msgid "invalid username in email address" msgstr "nom d'utilisateur invalide dans une adresse email" -#: tiramisu/option.py:1066 -msgid "invalid url, should start with http:// or https://" -msgstr "URL invalide, devrait démarré avec http:// ou https://" +#: tiramisu/option.py:1100 +msgid "invalid url, must start with http:// or https://" +msgstr "URL invalide, doit démarrer avec http:// ou https://" -#: tiramisu/option.py:1085 +#: tiramisu/option.py:1119 msgid "invalid url, port must be an between 0 and 65536" msgstr "URL invalide, port doit être entre 0 et 65536" -#: tiramisu/option.py:1091 -msgid "invalid url, should ends with filename" -msgstr "URL invalide, devrait finir avec un nom de fichier" +#: tiramisu/option.py:1125 +msgid "invalid url, must ends with filename" +msgstr "URL invalide, doit finir avec un nom de fichier" -#: tiramisu/option.py:1103 +#: tiramisu/option.py:1137 msgid "invalid username" msgstr "utilisateur invalide" -#: tiramisu/option.py:1114 +#: tiramisu/option.py:1148 msgid "invalid filename" msgstr "nom de fichier invalide" -#: tiramisu/option.py:1141 +#: tiramisu/option.py:1175 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:1159 +#: tiramisu/option.py:1193 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:1210 +#: tiramisu/option.py:1244 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:1240 +#: tiramisu/option.py:1275 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:1248 +#: tiramisu/option.py:1283 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:1254 +#: tiramisu/option.py:1289 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:1264 +#: tiramisu/option.py:1299 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:1276 +#: tiramisu/option.py:1311 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:1279 +#: tiramisu/option.py:1314 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:1282 +#: tiramisu/option.py:1317 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:1292 +#: tiramisu/option.py:1327 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:1300 +#: tiramisu/option.py:1335 msgid "callback of master's option shall not refered a slave's ones" msgstr "" "callback d'une variable maitre ne devrait pas référencer des variables " "esclaves" -#: tiramisu/option.py:1308 +#: tiramisu/option.py:1343 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:1397 +#: tiramisu/option.py:1443 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:1414 +#: tiramisu/option.py:1460 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -395,68 +420,68 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, expected et action" -#: tiramisu/option.py:1419 +#: tiramisu/option.py:1465 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:1423 +#: tiramisu/option.py:1469 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:1427 +#: tiramisu/option.py:1473 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:1431 +#: tiramisu/option.py:1477 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:1434 -msgid "malformed requirements option {0} should not be a multi" +#: tiramisu/option.py:1480 +msgid "malformed requirements option {0} must not be a multi" msgstr "requirements mal formés l'option {0} ne doit pas être une multi" -#: tiramisu/option.py:1440 +#: tiramisu/option.py:1486 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:1445 +#: tiramisu/option.py:1491 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:1470 -msgid "{0} should be a function" +#: tiramisu/option.py:1516 +msgid "{0} must be a function" msgstr "{0} doit être une fonction" -#: tiramisu/option.py:1473 -msgid "{0}_params should be a dict" -msgstr "{0}_params devrait être un dict" +#: tiramisu/option.py:1519 +msgid "{0}_params must be a dict" +msgstr "{0}_params doit être un dict" -#: tiramisu/option.py:1476 -msgid "{0}_params with key {1} should not have length different to 1" +#: tiramisu/option.py:1522 +msgid "{0}_params with key {1} mustn't have length different to 1" msgstr "" -"{0}_params avec la clef {1} devrait ne pas avoir une longueur différent de 1" +"{0}_params avec la clef {1} ne doit pas avoir une longueur différent de 1" -#: tiramisu/option.py:1480 -msgid "{0}_params should be tuple for key \"{1}\"" -msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" +#: tiramisu/option.py:1526 +msgid "{0}_params must be tuple for key \"{1}\"" +msgstr "{0}_params doit être un tuple pour la clef \"{1}\"" -#: tiramisu/option.py:1486 +#: tiramisu/option.py:1532 msgid "validator not support tuple" msgstr "validator n'accepte pas de tuple" -#: tiramisu/option.py:1489 -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:1535 +msgid "{0}_params must have an option not a {0} for first argument" +msgstr "{0}_params doit avoir une option pas un {0} pour premier argument" -#: tiramisu/option.py:1493 -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/option.py:1539 +msgid "{0}_params must have a boolean not a {0} for second argument" +msgstr "{0}_params doit avoir un booléen pas un {0} pour second argument" #: tiramisu/setting.py:116 msgid "can't rebind {0}" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index d35e6a5..738fa53 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2014-03-11 18:51+CET\n" +"POT-Creation-Date: 2014-03-12 21:49+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -120,312 +120,336 @@ msgstr "" msgid "params defined for a callback function but no callback defined yet for option {0}" msgstr "" -#: tiramisu/option.py:423 tiramisu/option.py:433 +#: tiramisu/option.py:425 tiramisu/option.py:450 msgid "invalid value for option {0}: {1}" msgstr "" -#: tiramisu/option.py:450 +#: tiramisu/option.py:444 +msgid "warning on the value of the option {0}: {1}" +msgstr "" + +#: tiramisu/option.py:461 msgid "invalid value {0} for option {1} which must be a list" msgstr "" -#: tiramisu/option.py:506 -msgid "consistency should be set with an option" +#: tiramisu/option.py:519 +msgid "consistency must be set with an option" msgstr "" -#: tiramisu/option.py:508 +#: tiramisu/option.py:521 msgid "cannot add consistency with itself" msgstr "" -#: tiramisu/option.py:510 -msgid "every options in consistency should be multi or none" +#: tiramisu/option.py:523 +msgid "every options in consistency must be multi or none" msgstr "" -#: tiramisu/option.py:530 -msgid "same value for {0} and {1}" +#: tiramisu/option.py:544 +msgid "same value for {0} and {1}, should be different" msgstr "" -#: tiramisu/option.py:623 +#: tiramisu/option.py:546 +msgid "same value for {0} and {1}, must be different" +msgstr "" + +#: tiramisu/option.py:640 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:626 +#: tiramisu/option.py:643 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:648 +#: tiramisu/option.py:665 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:660 +#: tiramisu/option.py:677 msgid "invalid boolean" msgstr "" -#: tiramisu/option.py:670 +#: tiramisu/option.py:687 msgid "invalid integer" msgstr "" -#: tiramisu/option.py:680 +#: tiramisu/option.py:697 msgid "invalid float" msgstr "" -#: tiramisu/option.py:690 +#: tiramisu/option.py:707 msgid "invalid string" msgstr "" -#: tiramisu/option.py:707 +#: tiramisu/option.py:724 msgid "invalid unicode" msgstr "" -#: tiramisu/option.py:719 +#: tiramisu/option.py:736 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option.py:770 tiramisu/option.py:773 tiramisu/option.py:778 +#: tiramisu/option.py:787 tiramisu/option.py:790 tiramisu/option.py:795 msgid "invalid IP" msgstr "" -#: tiramisu/option.py:783 -msgid "invalid IP, mustn't not be in reserved class" +#: tiramisu/option.py:801 +msgid "IP shouldn't be in reserved class" msgstr "" -#: tiramisu/option.py:785 +#: tiramisu/option.py:803 +msgid "invalid IP, mustn't be in reserved class" +msgstr "" + +#: tiramisu/option.py:807 +msgid "IP should be in private class" +msgstr "" + +#: tiramisu/option.py:809 msgid "invalid IP, must be in private class" msgstr "" -#: tiramisu/option.py:789 tiramisu/option.py:955 +#: tiramisu/option.py:814 tiramisu/option.py:989 msgid "invalid len for vals" msgstr "" -#: tiramisu/option.py:794 -msgid "invalid IP {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" +#: tiramisu/option.py:820 +msgid "IP {0} ({1}) not in network {2} ({3}) with netmask {4} ({5})" msgstr "" -#: tiramisu/option.py:835 +#: tiramisu/option.py:823 +msgid "invalid IP {0} ({1}) not in network {2} ({3}) with netmask {4} ({5})" +msgstr "" + +#: tiramisu/option.py:864 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option.py:840 +#: tiramisu/option.py:869 msgid "max value is empty" msgstr "" -#: tiramisu/option.py:857 +#: tiramisu/option.py:886 msgid "invalid port, range must have two values only" msgstr "" -#: tiramisu/option.py:860 +#: tiramisu/option.py:889 msgid "invalid port, first port in range must be smaller than the second one" msgstr "" -#: tiramisu/option.py:869 +#: tiramisu/option.py:898 msgid "invalid port" msgstr "" -#: tiramisu/option.py:871 +#: tiramisu/option.py:900 msgid "invalid port, must be an between {0} and {1}" msgstr "" -#: tiramisu/option.py:885 +#: tiramisu/option.py:914 msgid "invalid network address" msgstr "" -#: tiramisu/option.py:890 -msgid "invalid network address, must not be in reserved class" +#: tiramisu/option.py:920 +msgid "network address shouldn't be in reserved class" msgstr "" -#: tiramisu/option.py:902 +#: tiramisu/option.py:922 +msgid "invalid network address, mustn't be in reserved class" +msgstr "" + +#: tiramisu/option.py:935 msgid "invalid netmask address" msgstr "" -#: tiramisu/option.py:918 +#: tiramisu/option.py:952 msgid "invalid len for opts" msgstr "" -#: tiramisu/option.py:932 +#: tiramisu/option.py:966 msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network" msgstr "" -#: tiramisu/option.py:937 +#: tiramisu/option.py:971 msgid "invalid network {0} ({1}) with netmask {2}" msgstr "" -#: tiramisu/option.py:951 +#: tiramisu/option.py:985 msgid "invalid broadcast address" msgstr "" -#: tiramisu/option.py:960 +#: tiramisu/option.py:994 msgid "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" msgstr "" -#: tiramisu/option.py:982 +#: tiramisu/option.py:1016 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:985 +#: tiramisu/option.py:1019 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:987 +#: tiramisu/option.py:1021 msgid "allow_without_dot must be a boolean" msgstr "" -#: tiramisu/option.py:1031 +#: tiramisu/option.py:1065 msgid "invalid domainname, must have dot" msgstr "" -#: tiramisu/option.py:1033 +#: tiramisu/option.py:1067 msgid "invalid domainname's length (max 255)" msgstr "" -#: tiramisu/option.py:1035 +#: tiramisu/option.py:1069 msgid "invalid domainname's length (min 2)" msgstr "" -#: tiramisu/option.py:1037 +#: tiramisu/option.py:1071 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:1050 -msgid "invalid email address, should contains one @" +#: tiramisu/option.py:1084 +msgid "invalid email address, must contains one @" msgstr "" -#: tiramisu/option.py:1053 +#: tiramisu/option.py:1087 msgid "invalid username in email address" msgstr "" -#: tiramisu/option.py:1066 -msgid "invalid url, should start with http:// or https://" +#: tiramisu/option.py:1100 +msgid "invalid url, must start with http:// or https://" msgstr "" -#: tiramisu/option.py:1085 +#: tiramisu/option.py:1119 msgid "invalid url, port must be an between 0 and 65536" msgstr "" -#: tiramisu/option.py:1091 -msgid "invalid url, should ends with filename" +#: tiramisu/option.py:1125 +msgid "invalid url, must ends with filename" msgstr "" -#: tiramisu/option.py:1103 +#: tiramisu/option.py:1137 msgid "invalid username" msgstr "" -#: tiramisu/option.py:1114 +#: tiramisu/option.py:1148 msgid "invalid filename" msgstr "" -#: tiramisu/option.py:1141 +#: tiramisu/option.py:1175 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:1159 +#: tiramisu/option.py:1193 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:1210 +#: tiramisu/option.py:1244 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:1240 +#: tiramisu/option.py:1275 msgid "consistency with option {0} which is not in Config" msgstr "" -#: tiramisu/option.py:1248 +#: tiramisu/option.py:1283 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:1254 +#: tiramisu/option.py:1289 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:1264 +#: tiramisu/option.py:1299 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:1276 +#: tiramisu/option.py:1311 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:1279 +#: tiramisu/option.py:1314 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:1282 +#: tiramisu/option.py:1317 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:1292 +#: tiramisu/option.py:1327 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:1300 +#: tiramisu/option.py:1335 msgid "callback of master's option shall not refered a slave's ones" msgstr "" -#: tiramisu/option.py:1308 +#: tiramisu/option.py:1343 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1397 +#: tiramisu/option.py:1443 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1414 +#: tiramisu/option.py:1460 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1419 +#: tiramisu/option.py:1465 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1423 +#: tiramisu/option.py:1469 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1427 +#: tiramisu/option.py:1473 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1431 +#: tiramisu/option.py:1477 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1434 -msgid "malformed requirements option {0} should not be a multi" -msgstr "" - -#: tiramisu/option.py:1440 -msgid "malformed requirements second argument must be valid for option {0}: {1}" -msgstr "" - -#: tiramisu/option.py:1445 -msgid "inconsistency in action types for option: {0} action: {1}" -msgstr "" - -#: tiramisu/option.py:1470 -msgid "{0} should be a function" -msgstr "" - -#: tiramisu/option.py:1473 -msgid "{0}_params should be a dict" -msgstr "" - -#: tiramisu/option.py:1476 -msgid "{0}_params with key {1} should not have length different to 1" -msgstr "" - #: tiramisu/option.py:1480 -msgid "{0}_params should be tuple for key \"{1}\"" +msgid "malformed requirements option {0} must not be a multi" msgstr "" #: tiramisu/option.py:1486 +msgid "malformed requirements second argument must be valid for option {0}: {1}" +msgstr "" + +#: tiramisu/option.py:1491 +msgid "inconsistency in action types for option: {0} action: {1}" +msgstr "" + +#: tiramisu/option.py:1516 +msgid "{0} must be a function" +msgstr "" + +#: tiramisu/option.py:1519 +msgid "{0}_params must be a dict" +msgstr "" + +#: tiramisu/option.py:1522 +msgid "{0}_params with key {1} mustn't have length different to 1" +msgstr "" + +#: tiramisu/option.py:1526 +msgid "{0}_params must be tuple for key \"{1}\"" +msgstr "" + +#: tiramisu/option.py:1532 msgid "validator not support tuple" msgstr "" -#: tiramisu/option.py:1489 -msgid "{0}_params should have an option not a {0} for first argument" +#: tiramisu/option.py:1535 +msgid "{0}_params must have an option not a {0} for first argument" msgstr "" -#: tiramisu/option.py:1493 -msgid "{0}_params should have a boolean not a {0} for second argument" +#: tiramisu/option.py:1539 +msgid "{0}_params must have a boolean not a {0} for second argument" msgstr "" #: tiramisu/setting.py:116 From f43ef2dafee43fec2945dfdf28c638803a9a10d3 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 13 Mar 2014 14:12:44 +0100 Subject: [PATCH 61/61] now there are two warnings_only's level: - option's level for validation and _second_level_validation - consistencies level --- tiramisu/option.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index db6acbd..af862af 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -429,10 +429,6 @@ class Option(BaseOption): try: # valid with self._validator val_validator(_value) - # if context launch consistency validation - if context is not None: - descr._valid_consistency(self, _value, context, _index, - self._warnings_only) self._second_level_validation(_value, self._warnings_only) except ValueError as error: if self._warnings_only: @@ -440,6 +436,15 @@ class Option(BaseOption): error = None except ValueWarning as warning: pass + if error is None and warning is None: + try: + # if context launch consistency validation + if context is not None: + descr._valid_consistency(self, _value, context, _index) + except ValueError as error: + pass + except ValueWarning as warning: + pass if warning: msg = _("warning on the value of the option {0}: {1}").format( self._name, warning) @@ -1346,26 +1351,23 @@ class OptionDescription(BaseOption): def impl_get_group_type(self): return self._group_type - def _valid_consistency(self, option, value, context, index, warnings_only): + 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, all_cons_opts, params in consistencies: - if not warnings_only: - l_warnings_only = params.get('warnings_only', False) - else: - l_warnings_only = warnings_only + warnings_only = params.get('warnings_only', False) #all_cons_opts[0] is the option where func is set try: all_cons_opts[0]._launch_consistency(func, option, value, context, index, all_cons_opts, - l_warnings_only) + warnings_only) except ValueError as err: - if l_warnings_only: + if warnings_only: raise ValueWarning(err.message, option) else: raise err