From 1a294e3d093b4dd51c4530807892bcda1732a0b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 30 Sep 2013 19:41:56 +0200 Subject: [PATCH 01/33] 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 02/33] 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 03/33] 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 04/33] 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 05/33] 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 06/33] 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 07/33] 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 08/33] 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 384b30210c74bb0c1df4320747a53471006a7640 Mon Sep 17 00:00:00 2001 From: gwen Date: Mon, 4 Nov 2013 17:15:47 +0100 Subject: [PATCH 09/33] 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 10/33] 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 11/33] 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 12/33] 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 13/33] 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 14/33] 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 15/33] 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 16/33] 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 17/33] 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 18/33] 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 19/33] 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 20/33] 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 21/33] 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 22/33] 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 23/33] 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 24/33] 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 25/33] 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 26/33] 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 27/33] 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 28/33] 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 29/33] 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 30/33] 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 31/33] 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 32/33] 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 33/33] 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()")