diff --git a/ChangeLog b/ChangeLog index a8d7602..bb4c863 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ Sun Oct 26 08:50:38 2014 +0200 Emmanuel Garette * if option is frozen with force_default_on_freeze property, owner must be 'default' check property when tried to change owner + * bad characters in DomainnameOption could be in warning level Sat Oct 25 22:48:08 2014 +0200 Emmanuel Garette * cannot add unvalaible consistency for an option diff --git a/test/test_config_domain.py b/test/test_config_domain.py index 3edb2cb..85f48b9 100644 --- a/test/test_config_domain.py +++ b/test/test_config_domain.py @@ -1,8 +1,10 @@ import autopath +import warnings from py.test import raises from tiramisu.config import Config from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDescription +from tiramisu.error import ValueWarning def test_domainname(): @@ -15,28 +17,63 @@ def test_domainname(): c.d = 'toto.com' raises(ValueError, "c.d = 'toto'") c.d = 'toto3.com' - raises(ValueError, "c.d = 'toto3.3la'") - #raises(ValueError, "c.d = '3toto.com'") - raises(ValueError, "c.d = 'toto.co3'") raises(ValueError, "c.d = 'toto_super.com'") c.d = 'toto-.com' raises(ValueError, "c.d = 'toto..com'") # c.f = 'toto.com' c.f = 'toto' - raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnameanditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnametoolongthathavemorethanmaximumsizeforatruedomainnameanditsnoteasytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowiendityeah'") + c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea' + raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamean'") + c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nd' + c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnameto.olongthathavemorethanmaximumsizeforatruedomainnameanditsnoteas.ytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowie' + raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnameto.olongthathavemorethanmaximumsizeforatruedomainnameanditsnoteas.ytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowien'") raises(ValueError, "c.f = 'd'") + raises(ValueError, "c.f = 'd.t'") # c.g = 'toto.com' c.g = '192.168.1.0' c.g = '192.168.1.29' + +def test_domainname_warning(): + d = DomainnameOption('d', '', warnings_only=True) + f = DomainnameOption('f', '', allow_without_dot=True, warnings_only=True) + g = DomainnameOption('g', '', allow_ip=True, warnings_only=True) + od = OptionDescription('a', '', [d, f, g]) + warnings.simplefilter("always", ValueWarning) + c = Config(od) + c.read_write() + c.d = 'toto.com' + raises(ValueError, "c.d = 'toto'") + c.d = 'toto3.com' + with warnings.catch_warnings(record=True) as w: + c.d = 'toto_super.com' + assert len(w) == 1 + c.d = 'toto-.com' + raises(ValueError, "c.d = 'toto..com'") + # + c.f = 'toto.com' + c.f = 'toto' + c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea' + raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamean'") + c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nd' + c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnameto.olongthathavemorethanmaximumsizeforatruedomainnameanditsnoteas.ytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowie' + raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnamea.nditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnameto.olongthathavemorethanmaximumsizeforatruedomainnameanditsnoteas.ytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowien'") + raises(ValueError, "c.f = 'd'") + raises(ValueError, "c.f = 'd.t'") + # + c.g = 'toto.com' + c.g = '192.168.1.0' + c.g = '192.168.1.29' + + def test_special_domain_name(): """domain name option that starts with a number or not """ d = DomainnameOption('d', '') e = DomainnameOption('e', '', type_='netbios') - od = OptionDescription('a', '', [d,e]) + od = OptionDescription('a', '', [d, e]) c = Config(od) c.read_write() c.d = '1toto.com' diff --git a/test/test_option_default.py b/test/test_option_default.py index 3eda6e4..f7b483f 100644 --- a/test/test_option_default.py +++ b/test/test_option_default.py @@ -77,6 +77,11 @@ def test_force_default_on_freeze(): assert config.getowner(dummy2) == owners.user raises(ConfigError, "config.cfgimpl_get_values().setowner(dummy1, owners.frozen)") raises(PropertiesOptionError, "config.cfgimpl_get_values().setowner(dummy2, owners.frozen)") + raises(PropertiesOptionError, "del(config.dummy1)") + setting[dummy1].remove('frozen') + del(config.dummy1) + setting[dummy1].append('frozen') + raises(ConfigError, "config.cfgimpl_get_values().setowner(dummy1, owners.frozen)") def test_force_default_on_freeze_multi(): @@ -99,6 +104,11 @@ def test_force_default_on_freeze_multi(): assert config.getowner(dummy2) == owners.user raises(ConfigError, "config.cfgimpl_get_values().setowner(dummy1, owners.frozenmulti)") raises(PropertiesOptionError, "config.cfgimpl_get_values().setowner(dummy2, owners.frozenmulti)") + raises(PropertiesOptionError, "del(config.dummy1)") + setting[dummy1].remove('frozen') + del(config.dummy1) + setting[dummy1].append('frozen') + raises(ConfigError, "config.cfgimpl_get_values().setowner(dummy1, owners.frozenmulti)") def test_overrides_changes_option_value(): diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index d311173..412553c 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -408,23 +408,8 @@ class DomainnameOption(Option): raise ValueError(_('allow_without_dot must be a boolean')) # pragma: optional cover extra['_allow_ip'] = allow_ip extra['_allow_without_dot'] = allow_without_dot - end = '' - extrachar = '' - extrachar_mandatory = '' - if extra['_dom_type'] == 'netbios': - length = 14 # pragma: optional cover - elif extra['_dom_type'] == 'hostname': - length = 62 # pragma: optional cover - elif extra['_dom_type'] == 'domainname': - length = 62 - if allow_without_dot is False: - extrachar_mandatory = '\.' - else: - extrachar = '\.' # pragma: optional cover - end = '+[a-z]*' - extra['_domain_re'] = re.compile(r'^(?:[a-z\d][a-z\d\-{0}]{{,{1}}}{2}){3}$' - ''.format(extrachar, length, - extrachar_mandatory, end)) + extra['_domain_re'] = re.compile(r'^[a-z\d][a-z\d\-]*$') + super(DomainnameOption, self).__init__(name, doc, default=default, default_multi=default_multi, callback=callback, @@ -438,22 +423,52 @@ class DomainnameOption(Option): extra=extra) def _validate(self, value, context=undefined): + def _valid_length(val): + if len(val) < 2: + raise ValueError(_("invalid domainname's length (min 2)")) + if len(val) > part_name_length: + raise ValueError(_("invalid domainname's length (max {0})" + "").format(part_name_length)) + if self._get_extra('_allow_ip') is True: # pragma: optional cover try: IP('{0}/32'.format(value)) return except ValueError: pass - if self._get_extra('_dom_type') == 'domainname' and \ - not self._get_extra('_allow_without_dot') and \ - '.' not in value: # pragma: optional cover - raise ValueError(_("invalid domainname, must have dot")) - if len(value) > 255: - raise ValueError(_("invalid domainname's length (max 255)")) # pragma: optional cover - if len(value) < 2: - raise ValueError(_("invalid domainname's length (min 2)")) # pragma: optional cover - if not self._get_extra('_domain_re').search(value): - raise ValueError(_('invalid domainname')) # pragma: optional cover + if self._get_extra('_dom_type') == 'netbios': + part_name_length = 15 + else: + part_name_length = 63 + if self._get_extra('_dom_type') == 'domainname': + if not self._get_extra('_allow_without_dot') and not "." in value: + raise ValueError(_("invalid domainname, must have dot")) + if len(value) > 255: + raise ValueError(_("invalid domainname's length (max 255)")) + for dom in value.split('.'): + _valid_length(dom) + else: + _valid_length(value) + + def _second_level_validation(self, value, warnings_only): + def _valid_char(val): + if not self._get_extra('_domain_re').search(val): + if warnings_only: + raise ValueError(_('same characters may cause problems')) + else: + raise ValueError(_('invalid domainname')) + #not for IP + if self._get_extra('_allow_ip') is True: + try: + IP('{0}/32'.format(value)) + return + except ValueError: + pass + if self._get_extra('_dom_type') == 'domainname': + for dom in value.split('.'): + _valid_char(dom) + else: + _valid_char(value) class EmailOption(DomainnameOption): @@ -470,6 +485,10 @@ class EmailOption(DomainnameOption): if not self.username_re.search(username): raise ValueError(_('invalid username in email address')) # pragma: optional cover super(EmailOption, self)._validate(domain) + super(EmailOption, self)._second_level_validation(domain, False) + + def _second_level_validation(self, value, warnings_only): + pass class URLOption(DomainnameOption): @@ -503,10 +522,14 @@ class URLOption(DomainnameOption): '65536')) # pragma: optional cover # validate domainname super(URLOption, self)._validate(domain) + super(URLOption, self)._second_level_validation(domain, False) # validate file if files is not None and files != '' and not self.path_re.search(files): raise ValueError(_('invalid url, must ends with filename')) # pragma: optional cover + def _second_level_validation(self, value, warnings_only): + pass + class UsernameOption(Option): __slots__ = tuple() diff --git a/tiramisu/setting.py b/tiramisu/setting.py index eae628e..713e72e 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -338,7 +338,7 @@ class Settings(object): return Property(self, self._getproperties(opt, path), opt, path) def __setitem__(self, opt, value): # pragma: optional cover - raise ValueError('you should only append/remove properties') + raise ValueError(_('you should only append/remove properties')) def reset(self, opt=None, _path=None, all_properties=False): if all_properties and (_path or opt): # pragma: optional cover diff --git a/tiramisu/value.py b/tiramisu/value.py index ef11389..d0a8eaa 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -149,8 +149,10 @@ class Values(object): def reset(self, opt, path=None): if path is None: path = opt.impl_getpath(self._getcontext()) + context = self._getcontext() + context.cfgimpl_get_settings().validate_properties(opt, False, True, + path) if self._p_.hasvalue(path): - context = self._getcontext() setting = context.cfgimpl_get_settings() opt.impl_validate(opt.impl_getdefault(), context, 'validator' in setting) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index d882812..660bee4 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Tiramisu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-10-25 22:43+CEST\n" +"POT-Creation-Date: 2014-10-26 09:35+CET\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: Tiramisu's team \n" @@ -30,7 +30,7 @@ msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" #: tiramisu/config.py:176 tiramisu/setting.py:321 tiramisu/value.py:54 -#: tiramisu/value.py:541 +#: tiramisu/value.py:555 msgid "the context does not exist anymore" msgstr "le context n'existe plus" @@ -481,47 +481,55 @@ 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/option.py:454 -msgid "invalid domainname, must have dot" -msgstr "nom de domaine invalide, doit avoir un point" - -#: tiramisu/option/option.py:456 -msgid "invalid domainname's length (max 255)" -msgstr "longueur du nom de domaine invalide (maximum {1})" - -#: tiramisu/option/option.py:458 +#: tiramisu/option/option.py:428 msgid "invalid domainname's length (min 2)" msgstr "longueur du nom de domaine invalide (minimum 2)" -#: tiramisu/option/option.py:460 +#: tiramisu/option/option.py:430 +msgid "invalid domainname's length (max {0})" +msgstr "longueur du nom de domaine invalide (maximum {0})" + +#: tiramisu/option/option.py:445 +msgid "invalid domainname, must have dot" +msgstr "nom de domaine invalide, doit avoir un point" + +#: tiramisu/option/option.py:447 +msgid "invalid domainname's length (max 255)" +msgstr "longueur du nom de domaine invalide (maximum {1})" + +#: tiramisu/option/option.py:457 +msgid "same characters may cause problems" +msgstr "des caractères peuvent poser problèmes" + +#: tiramisu/option/option.py:459 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option/option.py:472 +#: tiramisu/option/option.py:483 msgid "invalid email address, must contains one @" msgstr "adresse email invalide, doit contenir un @" -#: tiramisu/option/option.py:475 +#: tiramisu/option/option.py:486 msgid "invalid username in email address" msgstr "nom d'utilisateur invalide dans une adresse email" -#: tiramisu/option/option.py:487 +#: tiramisu/option/option.py:502 msgid "invalid url, must start with http:// or https://" msgstr "URL invalide, doit démarrer avec http:// ou https://" -#: tiramisu/option/option.py:506 +#: tiramisu/option/option.py:521 msgid "invalid url, port must be an between 0 and 65536" msgstr "URL invalide, port doit être entre 0 et 65536" -#: tiramisu/option/option.py:512 +#: tiramisu/option/option.py:528 msgid "invalid url, must ends with filename" msgstr "URL invalide, doit finir avec un nom de fichier" -#: tiramisu/option/option.py:523 +#: tiramisu/option/option.py:542 msgid "invalid username" msgstr "utilisateur invalide" -#: tiramisu/option/option.py:533 +#: tiramisu/option/option.py:552 msgid "invalid filename" msgstr "nom de fichier invalide" @@ -584,6 +592,10 @@ msgstr "" "ne peut ajouter la propriété {0} dans l'option {1}: cette propriété est " "calculée" +#: tiramisu/setting.py:341 +msgid "you should only append/remove properties" +msgstr "pour pouvait seulement ajouter/supprimer des propriétés" + #: tiramisu/setting.py:345 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" @@ -610,7 +622,7 @@ msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1} msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:500 tiramisu/value.py:370 +#: tiramisu/setting.py:500 tiramisu/value.py:379 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" @@ -642,7 +654,7 @@ msgstr "option {0} n'existe pas dans l'espace de stockage {1}" msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}" -#: tiramisu/storage/dictionary/option.py:131 tiramisu/value.py:427 +#: tiramisu/storage/dictionary/option.py:131 tiramisu/value.py:441 msgid "information's item not found: {0}" msgstr "aucune config spécifiée alors que c'est nécessaire" @@ -694,48 +706,52 @@ msgstr "un espace de stockage dictionary ne peut être persistant" msgid "optiondescription has no value" msgstr "une optiondescription n'a pas de valeur" -#: tiramisu/value.py:301 +#: tiramisu/value.py:303 msgid "you should only set value with config" msgstr "vous devez seul affecter une valeur avec un config" -#: tiramisu/value.py:377 +#: tiramisu/value.py:356 +msgid "owner only avalaible for an option" +msgstr "owner seulement possible pour une option" + +#: tiramisu/value.py:386 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:467 +#: tiramisu/value.py:481 msgid "can force cache only if cache is actived in config" msgstr "" "peut force la mise en cache seulement si le cache est activé dans la config" -#: tiramisu/value.py:506 +#: tiramisu/value.py:520 msgid "{0} is already a Multi " msgstr "{0} est déjà une Multi" -#: tiramisu/value.py:572 +#: tiramisu/value.py:586 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:591 +#: tiramisu/value.py:605 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:595 +#: tiramisu/value.py:609 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:604 +#: tiramisu/value.py:618 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:612 +#: tiramisu/value.py:626 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:620 +#: tiramisu/value.py:634 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:648 +#: tiramisu/value.py:662 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 0e2dcc2..eee861d 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2014-10-25 22:43+CEST\n" +"POT-Creation-Date: 2014-10-26 09:35+CET\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -28,7 +28,7 @@ msgid "unknown group_type: {0}" msgstr "" #: tiramisu/config.py:176 tiramisu/setting.py:321 tiramisu/value.py:54 -#: tiramisu/value.py:541 +#: tiramisu/value.py:555 msgid "the context does not exist anymore" msgstr "" @@ -433,47 +433,55 @@ msgstr "" msgid "allow_without_dot must be a boolean" msgstr "" -#: tiramisu/option/option.py:454 -msgid "invalid domainname, must have dot" -msgstr "" - -#: tiramisu/option/option.py:456 -msgid "invalid domainname's length (max 255)" -msgstr "" - -#: tiramisu/option/option.py:458 +#: tiramisu/option/option.py:428 msgid "invalid domainname's length (min 2)" msgstr "" -#: tiramisu/option/option.py:460 +#: tiramisu/option/option.py:430 +msgid "invalid domainname's length (max {0})" +msgstr "" + +#: tiramisu/option/option.py:445 +msgid "invalid domainname, must have dot" +msgstr "" + +#: tiramisu/option/option.py:447 +msgid "invalid domainname's length (max 255)" +msgstr "" + +#: tiramisu/option/option.py:457 +msgid "same characters may cause problems" +msgstr "" + +#: tiramisu/option/option.py:459 msgid "invalid domainname" msgstr "" -#: tiramisu/option/option.py:472 +#: tiramisu/option/option.py:483 msgid "invalid email address, must contains one @" msgstr "" -#: tiramisu/option/option.py:475 +#: tiramisu/option/option.py:486 msgid "invalid username in email address" msgstr "" -#: tiramisu/option/option.py:487 +#: tiramisu/option/option.py:502 msgid "invalid url, must start with http:// or https://" msgstr "" -#: tiramisu/option/option.py:506 +#: tiramisu/option/option.py:521 msgid "invalid url, port must be an between 0 and 65536" msgstr "" -#: tiramisu/option/option.py:512 +#: tiramisu/option/option.py:528 msgid "invalid url, must ends with filename" msgstr "" -#: tiramisu/option/option.py:523 +#: tiramisu/option/option.py:542 msgid "invalid username" msgstr "" -#: tiramisu/option/option.py:533 +#: tiramisu/option/option.py:552 msgid "invalid filename" msgstr "" @@ -533,6 +541,10 @@ msgstr "" msgid "cannot append {0} property for option {1}: this property is calculated" msgstr "" +#: tiramisu/setting.py:341 +msgid "you should only append/remove properties" +msgstr "" + #: tiramisu/setting.py:345 msgid "opt and all_properties must not be set together in reset" msgstr "" @@ -557,7 +569,7 @@ msgstr "" msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:500 tiramisu/value.py:370 +#: tiramisu/setting.py:500 tiramisu/value.py:379 msgid "invalid generic owner {0}" msgstr "" @@ -585,7 +597,7 @@ msgstr "" msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "" -#: tiramisu/storage/dictionary/option.py:131 tiramisu/value.py:427 +#: tiramisu/storage/dictionary/option.py:131 tiramisu/value.py:441 msgid "information's item not found: {0}" msgstr "" @@ -636,47 +648,51 @@ msgstr "" msgid "optiondescription has no value" msgstr "" -#: tiramisu/value.py:301 +#: tiramisu/value.py:303 msgid "you should only set value with config" msgstr "" -#: tiramisu/value.py:377 +#: tiramisu/value.py:356 +msgid "owner only avalaible for an option" +msgstr "" + +#: tiramisu/value.py:386 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:467 +#: tiramisu/value.py:481 msgid "can force cache only if cache is actived in config" msgstr "" -#: tiramisu/value.py:506 +#: tiramisu/value.py:520 msgid "{0} is already a Multi " msgstr "" -#: tiramisu/value.py:572 +#: tiramisu/value.py:586 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:591 +#: tiramisu/value.py:605 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:595 +#: tiramisu/value.py:609 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:604 +#: tiramisu/value.py:618 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:612 +#: tiramisu/value.py:626 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:620 +#: tiramisu/value.py:634 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:648 +#: tiramisu/value.py:662 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr ""