From 26e3651848cf1372577c5ca140f208b87e5eb3c6 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 23 Sep 2013 21:00:45 +0200 Subject: [PATCH 01/18] update french translation --- tiramisu/option.py | 2 +- translations/fr/tiramisu.po | 283 +++++++++++++++++++---------------- translations/tiramisu.pot | 290 +++++++++++++++++++----------------- 3 files changed, 310 insertions(+), 265 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 10aba5c..fb76444 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -1325,6 +1325,6 @@ def validate_callback(callback, callback_params, type_): ).format(type_, type(option))) if force_permissive not in [True, False]: raise ValueError(_('{0}_params should have a boolean' - 'not a {0} for second argument' + ' not a {0} for second argument' ).format(type_, type( force_permissive))) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 8bceb43..9e8e2f4 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-08-31 09:52+CEST\n" +"POT-Creation-Date: 2013-09-23 20:59+CEST\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: LANGUAGE \n" @@ -11,18 +11,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" -#: tiramisu/autolib.py:58 -msgid "no config specified but needed" -msgstr "aucune config spécifié alors que c'est nécessaire" - -#: tiramisu/autolib.py:65 +#: tiramisu/autolib.py:131 msgid "" "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" "impossible d'effectuer le calcul, l'option {0} a les propriétés : {1} pour : " "{2}" -#: tiramisu/autolib.py:74 +#: tiramisu/autolib.py:140 msgid "" "unable to carry out a calculation, option value with multi types must have " "same length for: {0}" @@ -30,79 +26,75 @@ msgstr "" "impossible d'effectuer le calcul, valeur d'un option avec le type multi doit " "avoir la même longueur pour : {0}" -#: tiramisu/config.py:47 +#: tiramisu/config.py:48 msgid "descr must be an optiondescription, not {0}" msgstr "descr doit être une optiondescription pas un {0}" -#: tiramisu/config.py:121 +#: tiramisu/config.py:122 msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:157 +#: tiramisu/config.py:158 msgid "" "no option description found for this config (may be metaconfig without meta)" msgstr "" "pas d'option description pour cette config (peut être une metaconfig sans " "meta)" -#: tiramisu/config.py:311 +#: tiramisu/config.py:313 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:350 +#: tiramisu/config.py:352 msgid "no option found in config with these criteria" msgstr "aucune option trouvée dans la config avec ces critères" -#: tiramisu/config.py:400 +#: tiramisu/config.py:402 msgid "make_dict can't filtering with value without option" msgstr "make_dict ne peut filtrer sur une valeur mais sans option" -#: tiramisu/config.py:421 +#: tiramisu/config.py:423 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:481 +#: tiramisu/config.py:483 msgid "opt in getowner must be an option not {0}" msgstr "opt dans getowner doit être une option pas {0}" -#: tiramisu/option.py:71 -msgid "{0} has no attribute impl_set_information" -msgstr "{0} n'a pas d'attribut impl_set_information" - -#: tiramisu/option.py:86 -msgid "information's item not found: {0}" -msgstr "aucune config spécifié alors que c'est nécessaire" - -#: tiramisu/option.py:89 -msgid "{0} has no attribute impl_get_information" -msgstr "{0} n'a pas d'attribut impl_get_information" - -#: tiramisu/option.py:117 -msgid "'{0}' ({1}) object attribute '{2}' is read-only" -msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seul" - -#: tiramisu/option.py:159 +#: tiramisu/option.py:69 msgid "invalid name: {0} for option" msgstr "nom invalide : {0} pour l'option" -#: tiramisu/option.py:169 -msgid "validator must be a function" -msgstr "validator doit être une fonction" +#: tiramisu/option.py:79 +msgid "invalid properties type {0} for {1}, must be a tuple" +msgstr "type des properties invalide {0} pour {1}, doit être un tuple" -#: tiramisu/option.py:176 +#: tiramisu/option.py:121 +msgid "'{0}' ({1}) object attribute '{2}' is read-only" +msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seul" + +#: tiramisu/option.py:148 tiramisu/value.py:361 +msgid "information's item not found: {0}" +msgstr "aucune config spécifié alors que c'est nécessaire" + +#: tiramisu/option.py:265 +msgid "cannot serialize Option, only in OptionDescription" +msgstr "ne peut serialiser une Option, seulement via une OptionDescription" + +#: tiramisu/option.py:364 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" "une default_multi est renseigné alors que multi est False dans l'option : {0}" -#: tiramisu/option.py:182 +#: tiramisu/option.py:370 msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}" -#: tiramisu/option.py:187 +#: tiramisu/option.py:375 msgid "default value not allowed if option: {0} is calculated" msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculé" -#: tiramisu/option.py:190 +#: tiramisu/option.py:378 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" @@ -110,183 +102,179 @@ msgstr "" "params définit pour une fonction callback mais par de callback défini encore " "pour l'option {0}" -#: tiramisu/option.py:212 tiramisu/option.py:753 -msgid "invalid properties type {0} for {1}, must be a tuple" -msgstr "type des properties invalide {0} pour {1}, doit être un tuple" +#: tiramisu/option.py:463 +msgid "validator should return a boolean, not {0}" +msgstr "validator devrait retourner un boolean, pas un {0}" -#: tiramisu/option.py:285 +#: tiramisu/option.py:473 msgid "invalid value {0} for option {1} for object {2}" msgstr "valeur invalide {0} pour l'option {1} pour l'objet {2}" -#: tiramisu/option.py:293 tiramisu/value.py:468 +#: tiramisu/option.py:481 tiramisu/value.py:542 msgid "invalid value {0} for option {1}: {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}" -#: tiramisu/option.py:305 +#: tiramisu/option.py:493 msgid "invalid value {0} for option {1} which must be a list" msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste" -#: tiramisu/option.py:374 +#: tiramisu/option.py:562 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" "valeur invalide {0} pour l'option {1} doit être différent que l'option {2}" -#: tiramisu/option.py:396 +#: tiramisu/option.py:618 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:399 +#: tiramisu/option.py:621 msgid "open_values must be a boolean for {0}" msgstr "open_values doit être un booléen pour {0}" -#: tiramisu/option.py:420 +#: tiramisu/option.py:642 msgid "value {0} is not permitted, only {1} is allowed" msgstr "valeur {0} n'est pas permit, seules {1} sont autorisées" -#: tiramisu/option.py:432 +#: tiramisu/option.py:654 msgid "value must be a boolean" msgstr "valeur doit être un booléen" -#: tiramisu/option.py:442 +#: tiramisu/option.py:664 msgid "value must be an integer" msgstr "valeur doit être un numbre" -#: tiramisu/option.py:452 +#: tiramisu/option.py:674 msgid "value must be a float" msgstr "valeur doit être un nombre flottant" -#: tiramisu/option.py:462 +#: tiramisu/option.py:684 msgid "value must be a string, not {0}" msgstr "valeur doit être une chaîne, pas {0}" -#: tiramisu/option.py:480 +#: tiramisu/option.py:702 msgid "value must be an unicode" msgstr "valeur doit être une valeur unicode" -#: tiramisu/option.py:490 +#: tiramisu/option.py:714 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "symlinkoption mal formé doit être une option pour symlink {0}" -#: tiramisu/option.py:526 -msgid "IP shall not be in reserved class" +#: tiramisu/option.py:766 +msgid "IP mustn't not be in reserved class" msgstr "IP ne doit pas être d'une classe reservée" -#: tiramisu/option.py:528 +#: tiramisu/option.py:768 msgid "IP must be in private class" msgstr "IP doit être dans la classe privée" -#: tiramisu/option.py:566 +#: tiramisu/option.py:806 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:571 +#: tiramisu/option.py:811 msgid "max value is empty" msgstr "valeur maximum est vide" -#: tiramisu/option.py:608 +#: tiramisu/option.py:848 msgid "network shall not be in reserved class" msgstr "réseau ne doit pas être dans la classe reservée" -#: tiramisu/option.py:640 +#: tiramisu/option.py:880 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3}), ce réseau est une IP" -#: tiramisu/option.py:645 +#: tiramisu/option.py:885 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "IP invalide {0} ({1}) avec masque {2} ({3}), cette IP est un réseau" -#: tiramisu/option.py:650 +#: tiramisu/option.py:890 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "IP invalide {0} ({1}) avec masque {2} ({3})" -#: tiramisu/option.py:652 +#: tiramisu/option.py:892 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3})" -#: tiramisu/option.py:672 +#: tiramisu/option.py:912 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:675 +#: tiramisu/option.py:915 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:704 +#: tiramisu/option.py:944 msgid "invalid value for {0}, must have dot" msgstr "valeur invalide pour {0}, doit avoir un point" -#: tiramisu/option.py:707 +#: tiramisu/option.py:947 msgid "invalid domainname's length for {0} (max {1})" msgstr "longueur du nom de domaine invalide pour {0} (maximum {1})" -#: tiramisu/option.py:710 +#: tiramisu/option.py:950 msgid "invalid domainname's length for {0} (min 2)" msgstr "longueur du nom de domaine invalide pour {0} (minimum 2)" -#: tiramisu/option.py:714 +#: tiramisu/option.py:954 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:731 -msgid "invalid name: {0} for optiondescription" -msgstr "nom invalide : {0} pour l'optiondescription" - -#: tiramisu/option.py:743 +#: tiramisu/option.py:981 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:769 +#: tiramisu/option.py:998 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {} inconnue pour l'OptionDescription{}" -#: tiramisu/option.py:820 +#: tiramisu/option.py:1049 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:850 +#: tiramisu/option.py:1083 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:856 +#: tiramisu/option.py:1089 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:866 +#: tiramisu/option.py:1099 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})" -#: tiramisu/option.py:879 +#: tiramisu/option.py:1112 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:882 +#: tiramisu/option.py:1115 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:885 +#: tiramisu/option.py:1118 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" "option non autorisée {0} dans le groupe {1} : cette option n'est pas une " "multi" -#: tiramisu/option.py:896 +#: tiramisu/option.py:1129 msgid "master group with wrong master name for {0}" msgstr "le groupe maître avec un nom de maître éroné pour {0}" -#: tiramisu/option.py:905 +#: tiramisu/option.py:1138 msgid "no child has same nom has master group for: {0}" msgstr "pas d'enfant avec le nom du groupe maître pour {0} " -#: tiramisu/option.py:908 +#: tiramisu/option.py:1141 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:946 +#: tiramisu/option.py:1231 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" "type requirements malformé pour l'option : {0}, doit être un dictionnaire" -#: tiramisu/option.py:962 +#: tiramisu/option.py:1248 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -294,87 +282,108 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, exptected et action" -#: tiramisu/option.py:967 +#: tiramisu/option.py:1253 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "requirements malformé pour l'option : {0} inverse doit être un booléen" -#: tiramisu/option.py:971 +#: tiramisu/option.py:1257 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "requirements malformé pour l'option : {0} transitive doit être booléen" -#: tiramisu/option.py:975 +#: tiramisu/option.py:1261 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" "requirements malformé pour l'option : {0} same_action doit être un booléen" -#: tiramisu/option.py:979 +#: tiramisu/option.py:1265 msgid "malformed requirements must be an option in option {0}" msgstr "requirements malformé doit être une option dans l'option {0}" -#: tiramisu/option.py:982 +#: tiramisu/option.py:1268 msgid "malformed requirements option {0} should not be a multi" msgstr "requirements malformé l'option {0} ne doit pas être une multi" -#: tiramisu/option.py:988 +#: tiramisu/option.py:1274 msgid "" "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" "requirements malformé deuxième argument doit être valide pour l'option {0} : " "{1}" -#: tiramisu/option.py:993 +#: tiramisu/option.py:1279 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "incohérence dans les types action pour l'option : {0} action {1}" -#: tiramisu/setting.py:47 -msgid "storage_type is already set, cannot rebind it" -msgstr "storage_type est déjà défini, impossible de le redéfinir" +#: tiramisu/option.py:1304 +msgid "{0} should be a function" +msgstr "{0} doit être une fonction" -#: tiramisu/setting.py:67 +#: tiramisu/option.py:1307 +msgid "{0}_params should be a dict" +msgstr "{0}_params devrait être un dict" + +#: tiramisu/option.py:1310 +msgid "{0}_params with key {1} should not have length different to 1" +msgstr "" +"{0}_params avec la clef {1} devrait ne pas avoir une longueur différent de 1" + +#: tiramisu/option.py:1314 +msgid "{0}_params should be tuple for key \"{1}\"" +msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" + +#: tiramisu/option.py:1320 +msgid "validator not support tuple" +msgstr "validator n'accepte pas de tuple" + +#: tiramisu/option.py:1323 +msgid "{0}_params should have an option not a {0} for first argument" +msgstr "{0}_params devrait avoir une option pas un {0} pour première argument" + +#: tiramisu/option.py:1327 +msgid "{0}_params should have a boolean not a {0} for second argument" +msgstr "{0}_params devrait avoir un boolean pas un {0} pour second argument" + +#: tiramisu/setting.py:116 msgid "can't rebind {0}" msgstr "ne peut redéfinir ({0})" -#: tiramisu/setting.py:72 +#: tiramisu/setting.py:121 msgid "can't unbind {0}" msgstr "ne peut supprimer ({0})" -#: tiramisu/setting.py:185 +#: tiramisu/setting.py:259 msgid "cannot append {0} property for option {1}: this property is calculated" msgstr "" "ne peut ajouter la propriété {0} dans l'option {1}: cette propriété est " "calculée" -#: tiramisu/setting.py:215 -msgid "option {0} not already exists in storage {1}" -msgstr "option {0} n'existe pas dans l'espace de stockage {1}" - -#: tiramisu/setting.py:282 +#: tiramisu/setting.py:322 msgid "opt and all_properties must not be set together in reset" msgstr "opt et all_properties ne doit pas être renseigné ensemble dans reset" -#: tiramisu/setting.py:297 +#: tiramisu/setting.py:337 msgid "if opt is not None, path should not be None in _getproperties" msgstr "" "si opt n'est pas None, path devrait ne pas être à None dans _getproperties" -#: tiramisu/setting.py:391 +#: tiramisu/setting.py:440 msgid "cannot change the value for option {0} this option is frozen" msgstr "" "ne peut modifié la valeur de l'option {0} cette option n'est pas modifiable" -#: tiramisu/setting.py:397 +#: tiramisu/setting.py:446 msgid "trying to access to an option named: {0} with properties {1}" msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1}" -#: tiramisu/setting.py:415 +#: tiramisu/setting.py:464 msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:422 tiramisu/value.py:277 +#: tiramisu/setting.py:471 tiramisu/value.py:300 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" -#: tiramisu/setting.py:503 +#: tiramisu/setting.py:558 msgid "" "malformed requirements imbrication detected for option: '{0}' with " "requirement on: '{1}'" @@ -382,65 +391,85 @@ msgstr "" "imbrication de requirements malformé detectée pour l'option : '{0}' avec " "requirement sur : '{1}'" -#: tiramisu/setting.py:515 +#: tiramisu/setting.py:570 msgid "option '{0}' has requirement's property error: {1} {2}" msgstr "l'option '{0}' a une erreur de propriété pour le requirement : {1} {2}" -#: tiramisu/storage/dictionary/storage.py:37 +#: tiramisu/storage/__init__.py:52 +msgid "storage_type is already set, cannot rebind it" +msgstr "storage_type est déjà défini, impossible de le redéfinir" + +#: tiramisu/storage/__init__.py:86 +msgid "option {0} not already exists in storage {1}" +msgstr "option {0} n'existe pas dans l'espace de stockage {1}" + +#: tiramisu/storage/dictionary/storage.py:39 msgid "dictionary storage cannot delete session" msgstr "" "impossible de supprimer une session dans un espace de stockage dictionary" -#: tiramisu/storage/dictionary/storage.py:46 +#: tiramisu/storage/dictionary/storage.py:50 msgid "session already used" msgstr "session déjà utilisée" -#: tiramisu/storage/dictionary/storage.py:48 +#: tiramisu/storage/dictionary/storage.py:52 msgid "a dictionary cannot be persistent" msgstr "un espace de stockage dictionary ne peut être persistant" -#: tiramisu/value.py:284 +#: tiramisu/value.py:307 msgid "no value for {0} cannot change owner to {1}" msgstr "pas de valeur pour {0} ne peut changer d'utilisateur pour {1}" -#: tiramisu/value.py:356 +#: tiramisu/value.py:414 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "longueur invalide pour une esclave : {0} qui a {1} comme maître" -#: tiramisu/value.py:373 +#: tiramisu/value.py:438 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" "longueur invalide pour un maître : {0} qui a {1} une esclave avec une plus " "grande longueur" -#: tiramisu/value.py:394 +#: tiramisu/value.py:468 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "ne peut ajouter une valeur sur l'option multi {0} qui est une esclave" -#: tiramisu/value.py:429 +#: tiramisu/value.py:503 msgid "cannot sort multi option {0} if master or slave" msgstr "ne peut trier une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:433 +#: tiramisu/value.py:507 msgid "cmp is not permitted in python v3 or greater" msgstr "cmp n'est pas permis en python v3 ou supérieure" -#: tiramisu/value.py:442 +#: tiramisu/value.py:516 msgid "cannot reverse multi option {0} if master or slave" msgstr "ne peut inverser une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:450 +#: tiramisu/value.py:524 msgid "cannot insert multi option {0} if master or slave" msgstr "ne peut insérer une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:458 +#: tiramisu/value.py:532 msgid "cannot extend multi option {0} if master or slave" msgstr "ne peut étendre une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:482 +#: tiramisu/value.py:559 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave" +#~ msgid "no config specified but needed" +#~ msgstr "aucune config spécifié alors que c'est nécessaire" + +#~ msgid "{0} has no attribute impl_set_information" +#~ msgstr "{0} n'a pas d'attribut impl_set_information" + +#~ msgid "{0} has no attribute impl_get_information" +#~ msgstr "{0} n'a pas d'attribut impl_get_information" + +#~ msgid "invalid name: {0} for optiondescription" +#~ msgstr "nom invalide : {0} pour l'optiondescription" + #~ msgid "metaconfig's children must be a list" #~ msgstr "enfants d'une metaconfig doit être une liste" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index ef40426..8b37d6a 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2013-09-02 11:30+CEST\n" +"POT-Creation-Date: 2013-09-23 20:59+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,395 +15,411 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/autolib.py:58 -msgid "no config specified but needed" -msgstr "" - -#: tiramisu/autolib.py:65 +#: tiramisu/autolib.py:131 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" -#: tiramisu/autolib.py:74 +#: tiramisu/autolib.py:140 msgid "unable to carry out a calculation, option value with multi types must have same length for: {0}" msgstr "" -#: tiramisu/config.py:47 +#: tiramisu/config.py:48 msgid "descr must be an optiondescription, not {0}" msgstr "" -#: tiramisu/config.py:121 +#: tiramisu/config.py:122 msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:157 +#: tiramisu/config.py:158 msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:311 +#: tiramisu/config.py:313 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:350 +#: tiramisu/config.py:352 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:400 +#: tiramisu/config.py:402 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:421 +#: tiramisu/config.py:423 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:481 +#: tiramisu/config.py:483 msgid "opt in getowner must be an option not {0}" msgstr "" -#: tiramisu/option.py:71 -msgid "{0} has no attribute impl_set_information" -msgstr "" - -#: tiramisu/option.py:86 -msgid "information's item not found: {0}" -msgstr "" - -#: tiramisu/option.py:89 -msgid "{0} has no attribute impl_get_information" -msgstr "" - -#: tiramisu/option.py:117 -msgid "'{0}' ({1}) object attribute '{2}' is read-only" -msgstr "" - -#: tiramisu/option.py:208 +#: tiramisu/option.py:69 msgid "invalid name: {0} for option" msgstr "" -#: tiramisu/option.py:218 -msgid "validator must be a function" -msgstr "" - -#: tiramisu/option.py:225 -msgid "a default_multi is set whereas multi is False in option: {0}" -msgstr "" - -#: tiramisu/option.py:231 -msgid "invalid default_multi value {0} for option {1}: {2}" -msgstr "" - -#: tiramisu/option.py:236 -msgid "default value not allowed if option: {0} is calculated" -msgstr "" - -#: tiramisu/option.py:239 -msgid "params defined for a callback function but no callback defined yet for option {0}" -msgstr "" - -#: tiramisu/option.py:261 tiramisu/option.py:809 +#: tiramisu/option.py:79 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "" -#: tiramisu/option.py:334 +#: tiramisu/option.py:121 +msgid "'{0}' ({1}) object attribute '{2}' is read-only" +msgstr "" + +#: tiramisu/option.py:148 tiramisu/value.py:361 +msgid "information's item not found: {0}" +msgstr "" + +#: tiramisu/option.py:265 +msgid "cannot serialize Option, only in OptionDescription" +msgstr "" + +#: tiramisu/option.py:364 +msgid "a default_multi is set whereas multi is False in option: {0}" +msgstr "" + +#: tiramisu/option.py:370 +msgid "invalid default_multi value {0} for option {1}: {2}" +msgstr "" + +#: tiramisu/option.py:375 +msgid "default value not allowed if option: {0} is calculated" +msgstr "" + +#: tiramisu/option.py:378 +msgid "params defined for a callback function but no callback defined yet for option {0}" +msgstr "" + +#: tiramisu/option.py:463 +msgid "validator should return a boolean, not {0}" +msgstr "" + +#: tiramisu/option.py:473 msgid "invalid value {0} for option {1} for object {2}" msgstr "" -#: tiramisu/option.py:342 tiramisu/value.py:468 +#: tiramisu/option.py:481 tiramisu/value.py:542 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:354 +#: tiramisu/option.py:493 msgid "invalid value {0} for option {1} which must be a list" msgstr "" -#: tiramisu/option.py:423 +#: tiramisu/option.py:562 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" -#: tiramisu/option.py:445 +#: tiramisu/option.py:618 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:448 +#: tiramisu/option.py:621 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:469 +#: tiramisu/option.py:642 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:481 +#: tiramisu/option.py:654 msgid "value must be a boolean" msgstr "" -#: tiramisu/option.py:491 +#: tiramisu/option.py:664 msgid "value must be an integer" msgstr "" -#: tiramisu/option.py:501 +#: tiramisu/option.py:674 msgid "value must be a float" msgstr "" -#: tiramisu/option.py:511 +#: tiramisu/option.py:684 msgid "value must be a string, not {0}" msgstr "" -#: tiramisu/option.py:529 +#: tiramisu/option.py:702 msgid "value must be an unicode" msgstr "" -#: tiramisu/option.py:539 +#: tiramisu/option.py:714 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option.py:581 -msgid "IP shall not be in reserved class" +#: tiramisu/option.py:766 +msgid "IP mustn't not be in reserved class" msgstr "" -#: tiramisu/option.py:583 +#: tiramisu/option.py:768 msgid "IP must be in private class" msgstr "" -#: tiramisu/option.py:621 +#: tiramisu/option.py:806 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option.py:626 +#: tiramisu/option.py:811 msgid "max value is empty" msgstr "" -#: tiramisu/option.py:663 +#: tiramisu/option.py:848 msgid "network shall not be in reserved class" msgstr "" -#: tiramisu/option.py:695 +#: tiramisu/option.py:880 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "" -#: tiramisu/option.py:700 +#: tiramisu/option.py:885 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "" -#: tiramisu/option.py:705 +#: tiramisu/option.py:890 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:707 +#: tiramisu/option.py:892 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:727 +#: tiramisu/option.py:912 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:730 +#: tiramisu/option.py:915 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:759 +#: tiramisu/option.py:944 msgid "invalid value for {0}, must have dot" msgstr "" -#: tiramisu/option.py:762 +#: tiramisu/option.py:947 msgid "invalid domainname's length for {0} (max {1})" msgstr "" -#: tiramisu/option.py:765 +#: tiramisu/option.py:950 msgid "invalid domainname's length for {0} (min 2)" msgstr "" -#: tiramisu/option.py:769 +#: tiramisu/option.py:954 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:787 -msgid "invalid name: {0} for optiondescription" -msgstr "" - -#: tiramisu/option.py:799 +#: tiramisu/option.py:981 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:825 +#: tiramisu/option.py:998 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:874 +#: tiramisu/option.py:1049 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:904 +#: tiramisu/option.py:1083 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:910 +#: tiramisu/option.py:1089 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:920 +#: tiramisu/option.py:1099 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:933 +#: tiramisu/option.py:1112 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:936 +#: tiramisu/option.py:1115 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:939 +#: tiramisu/option.py:1118 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:950 +#: tiramisu/option.py:1129 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:959 +#: tiramisu/option.py:1138 msgid "no child has same nom has master group for: {0}" msgstr "" -#: tiramisu/option.py:962 +#: tiramisu/option.py:1141 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1021 +#: tiramisu/option.py:1231 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1037 +#: tiramisu/option.py:1248 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1042 +#: tiramisu/option.py:1253 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1046 +#: tiramisu/option.py:1257 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1050 +#: tiramisu/option.py:1261 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1054 +#: tiramisu/option.py:1265 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1057 +#: tiramisu/option.py:1268 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1063 +#: tiramisu/option.py:1274 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1068 +#: tiramisu/option.py:1279 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "" -#: tiramisu/setting.py:47 -msgid "storage_type is already set, cannot rebind it" +#: tiramisu/option.py:1304 +msgid "{0} should be a function" msgstr "" -#: tiramisu/setting.py:67 +#: tiramisu/option.py:1307 +msgid "{0}_params should be a dict" +msgstr "" + +#: tiramisu/option.py:1310 +msgid "{0}_params with key {1} should not have length different to 1" +msgstr "" + +#: tiramisu/option.py:1314 +msgid "{0}_params should be tuple for key \"{1}\"" +msgstr "" + +#: tiramisu/option.py:1320 +msgid "validator not support tuple" +msgstr "" + +#: tiramisu/option.py:1323 +msgid "{0}_params should have an option not a {0} for first argument" +msgstr "" + +#: tiramisu/option.py:1327 +msgid "{0}_params should have a boolean not a {0} for second argument" +msgstr "" + +#: tiramisu/setting.py:116 msgid "can't rebind {0}" msgstr "" -#: tiramisu/setting.py:72 +#: tiramisu/setting.py:121 msgid "can't unbind {0}" msgstr "" -#: tiramisu/setting.py:185 +#: tiramisu/setting.py:259 msgid "cannot append {0} property for option {1}: this property is calculated" msgstr "" -#: tiramisu/setting.py:215 -msgid "option {0} not already exists in storage {1}" -msgstr "" - -#: tiramisu/setting.py:282 +#: tiramisu/setting.py:322 msgid "opt and all_properties must not be set together in reset" msgstr "" -#: tiramisu/setting.py:297 +#: tiramisu/setting.py:337 msgid "if opt is not None, path should not be None in _getproperties" msgstr "" -#: tiramisu/setting.py:391 +#: tiramisu/setting.py:440 msgid "cannot change the value for option {0} this option is frozen" msgstr "" -#: tiramisu/setting.py:397 +#: tiramisu/setting.py:446 msgid "trying to access to an option named: {0} with properties {1}" msgstr "" -#: tiramisu/setting.py:415 +#: tiramisu/setting.py:464 msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:422 tiramisu/value.py:277 +#: tiramisu/setting.py:471 tiramisu/value.py:300 msgid "invalid generic owner {0}" msgstr "" -#: tiramisu/setting.py:503 +#: tiramisu/setting.py:558 msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" msgstr "" -#: tiramisu/setting.py:515 +#: tiramisu/setting.py:570 msgid "option '{0}' has requirement's property error: {1} {2}" msgstr "" -#: tiramisu/storage/dictionary/storage.py:37 +#: tiramisu/storage/__init__.py:52 +msgid "storage_type is already set, cannot rebind it" +msgstr "" + +#: tiramisu/storage/__init__.py:86 +msgid "option {0} not already exists in storage {1}" +msgstr "" + +#: tiramisu/storage/dictionary/storage.py:39 msgid "dictionary storage cannot delete session" msgstr "" -#: tiramisu/storage/dictionary/storage.py:46 +#: tiramisu/storage/dictionary/storage.py:50 msgid "session already used" msgstr "" -#: tiramisu/storage/dictionary/storage.py:48 +#: tiramisu/storage/dictionary/storage.py:52 msgid "a dictionary cannot be persistent" msgstr "" -#: tiramisu/value.py:284 +#: tiramisu/value.py:307 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:356 +#: tiramisu/value.py:414 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:373 +#: tiramisu/value.py:438 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" -#: tiramisu/value.py:394 +#: tiramisu/value.py:468 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:429 +#: tiramisu/value.py:503 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:433 +#: tiramisu/value.py:507 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:442 +#: tiramisu/value.py:516 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:450 +#: tiramisu/value.py:524 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:458 +#: tiramisu/value.py:532 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:482 +#: tiramisu/value.py:559 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "" From 3fc89be40e913b61383041b4a699693335c03565 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 23 Sep 2013 22:40:10 +0200 Subject: [PATCH 02/18] comment tiramisu/autolib.py + some modification --- test/test_option_calculation.py | 16 ++++++ tiramisu/autolib.py | 87 ++++++++++++++++++++------------- 2 files changed, 70 insertions(+), 33 deletions(-) diff --git a/test/test_option_calculation.py b/test/test_option_calculation.py index 8ca8687..117de9d 100644 --- a/test/test_option_calculation.py +++ b/test/test_option_calculation.py @@ -28,6 +28,12 @@ def return_value(value=None): return value +def return_value2(*args, **kwargs): + value = list(args) + value.extend(kwargs.values()) + return value + + def make_description(): gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcdummy = BoolOption('dummy', 'dummy', default=False) @@ -675,3 +681,13 @@ def test_callback_multi_multi(): raises(ConfigError, "cfg.val4") assert cfg.val5 == ['val1', 'val4', 'val2', 'val4', 'val3', 'val4'] assert cfg.val7 == ['val1', 'val21', 'val2', 'val22', 'val3', 'val23'] + + +def test_multi_with_no_value(): + #First option return [] (so without value) + val1 = StrOption('val1', "", ['val'], multi=True) + val2 = StrOption('val2', "", multi=True) + val3 = StrOption('val3', '', multi=True, callback=return_value, callback_params={'': ((val2, False),), 'value': ((val1, False),)}) + od = OptionDescription('od', '', [val1, val2, val3]) + c = Config(od) + raises(ConfigError, "c.val3") diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index de8a8c5..efb7c0e 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -41,6 +41,13 @@ def carry_out_calculation(name, config, callback, callback_params, :param max_len: max length for a multi :type max_len: int + The callback_params is a dict. Key is used to build args (if key is '') + and kwargs (otherwise). Values are tuple of: + - values + - tuple with option and boolean's force_permissive (True when don't raise + if PropertiesOptionError) + Values could have multiple values only when key is ''. + * if no callback_params: => calculate() @@ -92,7 +99,6 @@ def carry_out_calculation(name, config, callback, callback_params, - a multi option with an other multi option but with same length opt1 == [1, 2, 3] opt2 == [11, 12, 13] - callback_params={'': ((opt1, False), (opt2, False))} => calculate(1, 11) => calculate(2, 12) => calculate(3, 13) @@ -100,9 +106,13 @@ def carry_out_calculation(name, config, callback, callback_params, - a multi option with an other multi option but with different length opt1 == [1, 2, 3] opt2 == [11, 12] - callback_params={'': ((opt1, False), (opt2, False))} => ConfigError() + - a multi option without value with a simple option + opt1 == [] + opt2 == 11 + => [] + * if callback_params={'value': ((opt1, False), (opt2, False))} => ConfigError() @@ -114,39 +124,47 @@ def carry_out_calculation(name, config, callback, callback_params, If calculate return list, this list is extend to return value. """ tcparams = {} + # if callback_params has a callback, launch several time calculate() one_is_multi = False - len_multi = 0 + # multi's option should have same value for all option + len_multi = None for key, callbacks in callback_params.items(): for callbk in callbacks: if isinstance(callbk, tuple): + # callbk is something link (opt, True|False) option, force_permissive = callbk + path = config.cfgimpl_get_description().impl_get_path_by_opt( + option) # get value try: - path = config.cfgimpl_get_description().impl_get_path_by_opt(option) value = config._getattr(path, force_permissive=True) except PropertiesOptionError as err: if force_permissive: continue raise ConfigError(_('unable to carry out a calculation, ' 'option {0} has properties: {1} for: ' - '{2}').format(option._name, err.proptype, + '{2}').format(option._name, + err.proptype, name)) is_multi = option.impl_is_multi() if is_multi: - if value is not None: - len_value = len(value) - if len_multi != 0 and len_multi != len_value: - raise ConfigError(_('unable to carry out a ' - 'calculation, option value with' - ' multi types must have same ' - 'length for: {0}').format(name)) - len_multi = len_value + len_value = len(value) + if len_multi is not None and len_multi != len_value: + raise ConfigError(_('unable to carry out a ' + 'calculation, option value with' + ' multi types must have same ' + 'length for: {0}').format(name)) + len_multi = len_value one_is_multi = True tcparams.setdefault(key, []).append((value, is_multi)) else: + # callbk is a value and not a multi tcparams.setdefault(key, []).append((callbk, False)) + # if one value is a multi, launch several time calculate + # if index is set, return a value + # if no index, return a list if one_is_multi: ret = [] if index: @@ -161,19 +179,20 @@ def carry_out_calculation(name, config, callback, callback_params, else: range_ = range(len_multi) for incr in range_: - tcp = {} - params = [] + args = [] + kwargs = {} for key, couples in tcparams.items(): for couple in couples: value, ismulti = couple - if ismulti and value is not None: - if key == '': - params.append(value[incr]) - else: - tcp[key] = value[incr] + if ismulti: + val = value[incr] else: - params.append(value) - calc = calculate(name, callback, params, tcp) + val = value + if key == '': + args.append(val) + else: + kwargs[key] = val + calc = calculate(callback, args, kwargs) if index: ret = calc else: @@ -183,24 +202,26 @@ def carry_out_calculation(name, config, callback, callback_params, ret.append(calc) return ret else: - tcp = {} - params = [] + # no value is multi + # return a single value + args = [] + kwargs = {} for key, couples in tcparams.items(): for couple in couples: + # couple[1] (ismulti) is always False if key == '': - value = couple[0] - params.append(value) + args.append(couple[0]) else: - tcp[key] = couple[0] - return calculate(name, callback, params, tcp) + kwargs[key] = couple[0] + return calculate(callback, args, kwargs) -def calculate(name, callback, params, tcparams): +def calculate(callback, args, kwargs): """wrapper that launches the 'callback' - :param callback: callback name - :param params: in the callback's arity, the unnamed parameters - :param tcparams: in the callback's arity, the named parameters + :param callback: callback function + :param args: in the callback's arity, the unnamed parameters + :param kwargs: in the callback's arity, the named parameters """ - return callback(*params, **tcparams) + return callback(*args, **kwargs) From a08af2383d1561a568a85ca71bf22d73e6f8fc31 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Mon, 23 Sep 2013 22:55:54 +0200 Subject: [PATCH 03/18] comment config --- tiramisu/config.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tiramisu/config.py b/tiramisu/config.py index f1b2851..3687349 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -"options handler global entry point" # Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors) # # This program is free software; you can redistribute it and/or modify @@ -20,6 +19,7 @@ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the whole pypy projet is under MIT licence # ____________________________________________________________ +"options handler global entry point" import weakref from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.option import OptionDescription, Option, SymLinkOption @@ -31,7 +31,11 @@ from tiramisu.i18n import _ class SubConfig(object): - "sub configuration management entry" + """Sub configuration management entry. + Tree if OptionDescription's responsability. SubConfig are generated + on-demand. A Config is also a SubConfig. + Root Config is call context below + """ __slots__ = ('_impl_context', '_impl_descr', '_impl_path') def __init__(self, descr, context, subpath=None): @@ -56,6 +60,7 @@ class SubConfig(object): def cfgimpl_reset_cache(self, only_expired=False, only=('values', 'settings')): + "remove cache (in context)" self._cfgimpl_get_context().cfgimpl_reset_cache(only_expired, only) def cfgimpl_get_home_by_path(self, path, force_permissive=False, From 8bebbf483e897bd90e83f2dd0a35bf776f6aa66a Mon Sep 17 00:00:00 2001 From: gwen Date: Tue, 24 Sep 2013 10:34:03 +0200 Subject: [PATCH 04/18] add storage automatic api doc --- doc/api/tiramisu.storage.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 doc/api/tiramisu.storage.txt diff --git a/doc/api/tiramisu.storage.txt b/doc/api/tiramisu.storage.txt new file mode 100644 index 0000000..86cb062 --- /dev/null +++ b/doc/api/tiramisu.storage.txt @@ -0,0 +1,6 @@ +tiramisu.storage +================ + +.. automodule:: tiramisu.storage + :members: + :noindex: \ No newline at end of file From 06baff2f3b1006c4d025a6b1292eb6ca0b7dc992 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Tue, 24 Sep 2013 23:19:20 +0200 Subject: [PATCH 05/18] add warning ability --- test/test_option_validator.py | 51 +++++++++++++++---- tiramisu/option.py | 94 +++++++++++++++++++++-------------- tiramisu/value.py | 39 ++++++++++++--- 3 files changed, 128 insertions(+), 56 deletions(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 8e00916..030a7c0 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -3,7 +3,6 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import StrOption, OptionDescription -from tiramisu.error import ConfigError def return_true(value, param=None): @@ -13,37 +12,36 @@ def return_true(value, param=None): def return_false(value, param=None): if value == 'val' and param in [None, 'yes']: - return False + raise ValueError('error') def return_val(value, param=None): return 'val' +def return_if_val(value): + if value != 'val': + raise ValueError('error') + + def test_validator(): opt1 = StrOption('opt1', '', validator=return_true, default='val') raises(ValueError, "StrOption('opt2', '', validator=return_false, default='val')") - raises(ConfigError, "StrOption('opt3', '', validator=return_val, default='val')") opt2 = StrOption('opt2', '', validator=return_false) - opt3 = StrOption('opt3', '', validator=return_val) - root = OptionDescription('root', '', [opt1, opt2, opt3]) + root = OptionDescription('root', '', [opt1, opt2]) cfg = Config(root) assert cfg.opt1 == 'val' raises(ValueError, "cfg.opt2 = 'val'") - raises(ConfigError, "cfg.opt3 = 'val'") def test_validator_params(): opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ('yes',)}, default='val') raises(ValueError, "StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}, default='val')") - raises(ConfigError, "StrOption('opt3', '', validator=return_val, validator_params={'': ('yes',)}, default='val')") opt2 = StrOption('opt2', '', validator=return_false, validator_params={'': ('yes',)}) - opt3 = StrOption('opt3', '', validator=return_val, validator_params={'': ('yes',)}) - root = OptionDescription('root', '', [opt1, opt2, opt3]) + root = OptionDescription('root', '', [opt1, opt2]) cfg = Config(root) assert cfg.opt1 == 'val' raises(ValueError, "cfg.opt2 = 'val'") - raises(ConfigError, "cfg.opt3 = 'val'") def test_validator_params_key(): @@ -57,3 +55,36 @@ def test_validator_params_key(): def test_validator_params_option(): opt0 = StrOption('opt0', '', default='val') raises(ValueError, "opt1 = StrOption('opt1', '', validator=return_true, validator_params={'': ((opt0, False),)}, default='val')") + + +def test_validator_multi(): + opt1 = StrOption('opt1', '', validator=return_if_val, multi=True) + root = OptionDescription('root', '', [opt1]) + cfg = Config(root) + assert cfg.opt1 == [] + cfg.opt1.append('val') + assert cfg.opt1 == ['val'] + raises(ValueError, "cfg.opt1.append('val1')") + raises(ValueError, "cfg.opt1 = ['val', 'val1']") + + +def test_validator_warning(): + opt1 = StrOption('opt1', '', validator=return_true, default='val', only_warning=True) + opt2 = StrOption('opt2', '', validator=return_false, only_warning=True) + opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, only_warning=True) + root = OptionDescription('root', '', [opt1, opt2, opt3]) + cfg = Config(root) + assert cfg.opt1 == 'val' + cfg.opt1 = 'val' + assert cfg.cfgimpl_get_values().has_warning() is False + cfg.opt2 = 'val' + assert cfg.cfgimpl_get_values().has_warning() is True + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option opt2: error' + assert cfg.cfgimpl_get_values().has_warning() is False + cfg.opt3.append('val') + assert cfg.cfgimpl_get_values().has_warning() is False + cfg.opt3.append('val1') + assert cfg.cfgimpl_get_values().has_warning() is True + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option opt3: error' + assert cfg.cfgimpl_get_values().has_warning() is False + raises(ValueError, "cfg.opt2 = 1") diff --git a/tiramisu/option.py b/tiramisu/option.py index fb76444..4112050 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -26,7 +26,7 @@ from copy import copy, deepcopy from types import FunctionType from IPy import IP -from tiramisu.error import ConflictError, ConfigError +from tiramisu.error import ConflictError from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -327,13 +327,13 @@ class Option(BaseOption): """ __slots__ = ('_multi', '_validator', '_default_multi', '_default', '_state_callback', '_callback', '_multitype', - '_master_slaves', '__weakref__') + '_only_warning', '_master_slaves', '__weakref__') _empty = '' def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None): + properties=None, only_warning=False): """ :param name: the option's name :param doc: the option's description @@ -351,6 +351,8 @@ class Option(BaseOption): validation of the value :param validator_params: the validator's parameters :param properties: tuple of default properties + :param only_warning: _validator and _consistencies don't raise if True + Values()._warning contain message """ super(Option, self).__init__(name, doc, requires, properties) @@ -388,6 +390,7 @@ class Option(BaseOption): default = [] self._multitype = multitypes.default self._default_multi = default_multi + self._only_warning = only_warning self.impl_validate(default) self._default = default @@ -436,10 +439,17 @@ class Option(BaseOption): if None not in (values, values_): getattr(self, func)(opt_._name, values, values_) - def impl_validate(self, value, context=None, validate=True): + def impl_validate(self, value, context=None, validate=True, + force_no_multi=False): """ :param value: the option's value + :param context: Config's context + :type context: :class:`tiramisu.config.Config` :param validate: if true enables ``self._validator`` validation + :type validate: boolean + :param force_no_multi: if multi, value has to be a list + not if force_no_multi is True + :type force_no_multi: boolean """ if not validate: return @@ -456,46 +466,49 @@ class Option(BaseOption): validator_params[''] = (val,) else: validator_params = {'': (val,)} - ret = carry_out_calculation(self._name, config=context, - callback=self._validator[0], - callback_params=validator_params) - if ret not in [False, True]: - raise ConfigError(_('validator should return a boolean, ' - 'not {0}').format(ret)) - return ret - else: - return True + # Raise ValueError if not valid + carry_out_calculation(self._name, config=context, + callback=self._validator[0], + callback_params=validator_params) def do_validation(_value, _index=None): if _value is None: return True - if not val_validator(_value): - raise ValueError(_("invalid value {0} " - "for option {1} for object {2}" - ).format(_value, - self._name, - self.__class__.__name__)) + ret_validation = None try: - self._validate(_value) + # valid with self._validator + val_validator(_value) + # if not context launch consistency validation + if context is not None: + descr._valid_consistency(self, _value, context, _index) except ValueError as err: - raise ValueError(_("invalid value {0} for option {1}: {2}" - "").format(_value, self._name, err)) - if context is not None: - descr._valid_consistency(self, _value, context, _index) + msg = _("invalid value {0} for option {1}: {2}").format( + _value, self._name, err) + if self._only_warning: + ret_validation = msg + else: + raise ValueError(msg) + # option validation + self._validate(_value) + return ret_validation # generic calculation if context is not None: descr = context.cfgimpl_get_description() - if not self._multi: - do_validation(value) + + ret = None + if not self._multi or force_no_multi: + ret = do_validation(value) else: if not isinstance(value, list): raise ValueError(_("invalid value {0} for option {1} " "which must be a list").format(value, self._name)) - for index in range(0, len(value)): - val = value[index] - do_validation(val, index) + for index, val in enumerate(value): + ret_ = do_validation(val, index) + if ret_ is not None: + ret = ret_ + return ret def impl_getdefault(self, default_multi=False): "accessing the default value" @@ -610,7 +623,7 @@ class ChoiceOption(Option): def __init__(self, name, doc, values, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, open_values=False, validator=None, - validator_params=None, properties=()): + validator_params=None, properties=None, only_warning=False): """ :param values: is a list of values the option can possibly take """ @@ -629,7 +642,8 @@ class ChoiceOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + only_warning=only_warning) def impl_get_values(self): return self._values @@ -747,7 +761,8 @@ class IPOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None, only_private=False, allow_reserved=False): + properties=None, only_private=False, allow_reserved=False, + only_warning=False): self._only_private = only_private self._allow_reserved = allow_reserved super(IPOption, self).__init__(name, doc, default=default, @@ -758,7 +773,8 @@ class IPOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + only_warning=only_warning) def _validate(self, value): ip = IP('{0}/32'.format(value)) @@ -786,7 +802,7 @@ class PortOption(Option): callback_params=None, validator=None, validator_params=None, properties=None, allow_range=False, allow_zero=False, allow_wellknown=True, allow_registred=True, - allow_private=False): + allow_private=False, only_warning=False): self._allow_range = allow_range self._min_value = None self._max_value = None @@ -818,7 +834,8 @@ class PortOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + only_warning=only_warning) def _validate(self, value): if self._allow_range and ":" in str(value): @@ -864,7 +881,6 @@ class NetmaskOption(Option): #opts must be (netmask, ip) options self.__cons_netmask(optname, value, value_, True) - #def __cons_netmask(self, opt, value, context, index, opts, make_net): def __cons_netmask(self, optname, val_netmask, val_ipnetwork, make_net): msg = None try: @@ -903,7 +919,8 @@ class DomainnameOption(Option): def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None, allow_ip=False, type_='domainname'): + properties=None, allow_ip=False, type_='domainname', + only_warning=False): #netbios: for MS domain #hostname: to identify the device #domainname: @@ -922,7 +939,8 @@ class DomainnameOption(Option): multi=multi, validator=validator, validator_params=validator_params, - properties=properties) + properties=properties, + only_warning=only_warning) def _validate(self, value): if self._allow_ip is True: diff --git a/tiramisu/value.py b/tiramisu/value.py index d587de1..247d273 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -33,7 +33,7 @@ class Values(object): but the values are physicaly located here, in `Values`, wich is also responsible of a caching utility. """ - __slots__ = ('context', '_p_', '__weakref__') + __slots__ = ('context', '_warning', '_p_', '__weakref__') def __init__(self, context, storage): """ @@ -106,8 +106,9 @@ class Values(object): path = self._get_opt_path(opt) if self._p_.hasvalue(path): setting = self.context().cfgimpl_get_settings() - opt.impl_validate(opt.impl_getdefault(), self.context(), - 'validator' in setting) + self._warning = opt.impl_validate(opt.impl_getdefault(), + self.context(), + 'validator' in setting) self.context().cfgimpl_reset_cache() if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master): @@ -229,7 +230,8 @@ class Values(object): else: value = self._getvalue(opt, path, validate) if config_error is None and validate: - opt.impl_validate(value, self.context(), 'validator' in setting) + self._warning = opt.impl_validate(value, self.context(), + 'validator' in setting) if config_error is None and self._is_default_owner(path) and \ 'force_store_value' in setting[opt]: self.setitem(opt, value, path, is_write=False) @@ -250,8 +252,9 @@ class Values(object): # is_write is, for example, used with "force_store_value" # user didn't change value, so not write # valid opt - opt.impl_validate(value, self.context(), - 'validator' in self.context().cfgimpl_get_settings()) + self._warning = opt.impl_validate(value, self.context(), + 'validator' in self.context( + ).cfgimpl_get_settings()) if opt.impl_is_multi() and not isinstance(value, Multi): value = Multi(value, self.context, opt, path, setitem=True) self._setvalue(opt, path, value, force_permissive=force_permissive, @@ -370,6 +373,22 @@ class Values(object): def __setstate__(self, states): self._p_ = states['_p_'] + def has_warning(self): + """If option is "only_warning", validation error is store in + self._warning. + has_warning just indicate that a warning message is store + """ + return self._warning is not None + + def get_last_warning(self): + """Get last warning message in self._warning. + We can get only one time this message. + """ + ret = self._warning + self._warning = None + return ret + + # ____________________________________________________________ # multi types @@ -476,7 +495,9 @@ class Multi(list): value = None self._validate(value) super(Multi, self).append(value) - self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force) + self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, + self, + validate_properties=not force) if not force and self.opt.impl_get_multitype() == multitypes.master: for slave in self.opt.impl_get_master_slaves(): path = values._get_opt_path(slave) @@ -537,7 +558,9 @@ class Multi(list): def _validate(self, value): if value is not None: try: - self.opt._validate(value) + self.context().cfgimpl_get_values()._warning = \ + self.opt.impl_validate(value, context=self.context(), + force_no_multi=True) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}" From 4e0f0a5b70d45168b182738e429c53f9b5f28363 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 25 Sep 2013 10:22:31 +0200 Subject: [PATCH 06/18] config_error is an exception, raise directly config_error --- tiramisu/value.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tiramisu/value.py b/tiramisu/value.py index 247d273..d2aa6a1 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -241,7 +241,7 @@ class Values(object): force_properties=force_properties, force_permissives=force_permissives) if config_error is not None: - raise ConfigError(config_error) + raise config_error return value def __setitem__(self, opt, value): From 329b9ac3493bcbbece10856b9dcc48dccf47ec13 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Wed, 25 Sep 2013 21:10:45 +0200 Subject: [PATCH 07/18] add _second_level_validation (second's one return only warning almost _validator raise) --- test/test_option_validator.py | 20 ++++++++++++++++++++ tiramisu/option.py | 12 +++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 030a7c0..c26e5f6 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -3,6 +3,7 @@ from py.test import raises from tiramisu.config import Config from tiramisu.option import StrOption, OptionDescription +from tiramisu.setting import groups def return_true(value, param=None): @@ -88,3 +89,22 @@ def test_validator_warning(): assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option opt3: error' assert cfg.cfgimpl_get_values().has_warning() is False raises(ValueError, "cfg.opt2 = 1") + + +def test_validator_warning_master_slave(): + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=return_false, only_warning=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=return_if_val, only_warning=True) + interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) + interface1.impl_set_group_type(groups.master) + assert interface1.impl_get_group_type() == groups.master + root = OptionDescription('root', '', [interface1]) + cfg = Config(root) + cfg.ip_admin_eth0.ip_admin_eth0.append(None) + assert cfg.cfgimpl_get_values().has_warning() is False + cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] + assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['val1'] + assert cfg.cfgimpl_get_values().has_warning() is True + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option netmask_admin_eth0: error' + cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] + assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' diff --git a/tiramisu/option.py b/tiramisu/option.py index 4112050..c49e5e4 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -473,7 +473,7 @@ class Option(BaseOption): def do_validation(_value, _index=None): if _value is None: - return True + return ret_validation = None try: # valid with self._validator @@ -481,6 +481,7 @@ class Option(BaseOption): # if not context launch consistency validation if context is not None: descr._valid_consistency(self, _value, context, _index) + self._second_level_validation(_value) except ValueError as err: msg = _("invalid value {0} for option {1}: {2}").format( _value, self._name, err) @@ -610,6 +611,9 @@ class Option(BaseOption): else: self._state_callback = (callback, cllbck_prms) + def _second_level_validation(self, value): + pass + class ChoiceOption(Option): """represents a choice out of several objects. @@ -777,6 +781,9 @@ class IPOption(Option): only_warning=only_warning) def _validate(self, value): + IP('{0}/32'.format(value)) + + def _second_level_validation(self, value): ip = IP('{0}/32'.format(value)) if not self._allow_reserved and ip.iptype() == 'RESERVED': raise ValueError(_("IP mustn't not be in reserved class")) @@ -860,6 +867,9 @@ class NetworkOption(Option): _opt_type = 'network' def _validate(self, value): + IP(value) + + def _second_level_validation(self, value): ip = IP(value) if ip.iptype() == 'RESERVED': raise ValueError(_("network shall not be in reserved class")) From e63dbf308d17af4802f21004fa2447e74578ef77 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 09:31:51 +0200 Subject: [PATCH 08/18] more tests for warnings --- test/test_option_validator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index c26e5f6..f2ccd8b 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -108,3 +108,9 @@ def test_validator_warning_master_slave(): cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] + assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' From a5587c91b85a536cd9e03f261a49596ed104753a Mon Sep 17 00:00:00 2001 From: gwen Date: Thu, 26 Sep 2013 16:56:14 +0200 Subject: [PATCH 09/18] errors in french translation --- translations/fr/tiramisu.po | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 9e8e2f4..66623c1 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -23,7 +23,7 @@ msgid "" "unable to carry out a calculation, option value with multi types must have " "same length for: {0}" msgstr "" -"impossible d'effectuer le calcul, valeur d'un option avec le type multi doit " +"impossible d'effectuer le calcul, la valeur d'une option avec le type multi doit " "avoir la même longueur pour : {0}" #: tiramisu/config.py:48 @@ -38,7 +38,7 @@ msgstr "group_type inconnu: {0}" msgid "" "no option description found for this config (may be metaconfig without meta)" msgstr "" -"pas d'option description pour cette config (peut être une metaconfig sans " +"pas d'option description trouvé pour cette config (peut être une metaconfig sans " "meta)" #: tiramisu/config.py:313 @@ -71,7 +71,7 @@ msgstr "type des properties invalide {0} pour {1}, doit être un tuple" #: tiramisu/option.py:121 msgid "'{0}' ({1}) object attribute '{2}' is read-only" -msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seul" +msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule" #: tiramisu/option.py:148 tiramisu/value.py:361 msgid "information's item not found: {0}" @@ -84,7 +84,7 @@ msgstr "ne peut serialiser une Option, seulement via une OptionDescription" #: tiramisu/option.py:364 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -"une default_multi est renseigné alors que multi est False dans l'option : {0}" +"une default_multi est renseignée alors que multi est False dans l'option : {0}" #: tiramisu/option.py:370 msgid "invalid default_multi value {0} for option {1}: {2}" @@ -92,19 +92,19 @@ msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}" #: tiramisu/option.py:375 msgid "default value not allowed if option: {0} is calculated" -msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculé" +msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculée" #: tiramisu/option.py:378 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" msgstr "" -"params définit pour une fonction callback mais par de callback défini encore " +"params définis pour une fonction callback mais par de callback encore définis " "pour l'option {0}" #: tiramisu/option.py:463 msgid "validator should return a boolean, not {0}" -msgstr "validator devrait retourner un boolean, pas un {0}" +msgstr "le validator devrait retourner un boolean, pas un {0}" #: tiramisu/option.py:473 msgid "invalid value {0} for option {1} for object {2}" @@ -121,7 +121,7 @@ msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste" #: tiramisu/option.py:562 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" -"valeur invalide {0} pour l'option {1} doit être différent que l'option {2}" +"valeur invalide {0} pour l'option {1} doit être différente de l'option {2}" #: tiramisu/option.py:618 msgid "values must be a tuple for {0}" @@ -133,7 +133,7 @@ msgstr "open_values doit être un booléen pour {0}" #: tiramisu/option.py:642 msgid "value {0} is not permitted, only {1} is allowed" -msgstr "valeur {0} n'est pas permit, seules {1} sont autorisées" +msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées" #: tiramisu/option.py:654 msgid "value must be a boolean" @@ -141,7 +141,7 @@ msgstr "valeur doit être un booléen" #: tiramisu/option.py:664 msgid "value must be an integer" -msgstr "valeur doit être un numbre" +msgstr "valeur doit être un nombre entier" #: tiramisu/option.py:674 msgid "value must be a float" @@ -157,7 +157,7 @@ msgstr "valeur doit être une valeur unicode" #: tiramisu/option.py:714 msgid "malformed symlinkoption must be an option for symlink {0}" -msgstr "symlinkoption mal formé doit être une option pour symlink {0}" +msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" #: tiramisu/option.py:766 msgid "IP mustn't not be in reserved class" @@ -173,11 +173,11 @@ msgstr "inconsistence dans la plage autorisée" #: tiramisu/option.py:811 msgid "max value is empty" -msgstr "valeur maximum est vide" +msgstr "la valeur maximum est vide" #: tiramisu/option.py:848 msgid "network shall not be in reserved class" -msgstr "réseau ne doit pas être dans la classe reservée" +msgstr "le réseau ne doit pas être dans la classe reservée" #: tiramisu/option.py:880 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" @@ -259,7 +259,7 @@ msgstr "" #: tiramisu/option.py:1129 msgid "master group with wrong master name for {0}" -msgstr "le groupe maître avec un nom de maître éroné pour {0}" +msgstr "le groupe maître avec un nom de maître érroné pour {0}" #: tiramisu/option.py:1138 msgid "no child has same nom has master group for: {0}" @@ -280,34 +280,34 @@ msgid "" "and action keys" msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " -"option, exptected et action" +"option, expected et action" #: tiramisu/option.py:1253 msgid "malformed requirements for option: {0} inverse must be boolean" -msgstr "requirements malformé pour l'option : {0} inverse doit être un booléen" +msgstr "requirements mal formés pour l'option : {0} inverse doit être un booléen" #: tiramisu/option.py:1257 msgid "malformed requirements for option: {0} transitive must be boolean" -msgstr "requirements malformé pour l'option : {0} transitive doit être booléen" +msgstr "requirements mal formés pour l'option : {0} transitive doit être booléen" #: tiramisu/option.py:1261 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -"requirements malformé pour l'option : {0} same_action doit être un booléen" +"requirements mal formés pour l'option : {0} same_action doit être un booléen" #: tiramisu/option.py:1265 msgid "malformed requirements must be an option in option {0}" -msgstr "requirements malformé doit être une option dans l'option {0}" +msgstr "requirements mal formés doit être une option dans l'option {0}" #: tiramisu/option.py:1268 msgid "malformed requirements option {0} should not be a multi" -msgstr "requirements malformé l'option {0} ne doit pas être une multi" +msgstr "requirements mal formés l'option {0} ne doit pas être une multi" #: tiramisu/option.py:1274 msgid "" "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -"requirements malformé deuxième argument doit être valide pour l'option {0} : " +"requirements mal formés deuxième argument doit être valide pour l'option {0} : " "{1}" #: tiramisu/option.py:1279 @@ -337,7 +337,7 @@ msgstr "validator n'accepte pas de tuple" #: tiramisu/option.py:1323 msgid "{0}_params should have an option not a {0} for first argument" -msgstr "{0}_params devrait avoir une option pas un {0} pour première argument" +msgstr "{0}_params devrait avoir une option pas un {0} pour premier argument" #: tiramisu/option.py:1327 msgid "{0}_params should have a boolean not a {0} for second argument" @@ -369,7 +369,7 @@ msgstr "" #: tiramisu/setting.py:440 msgid "cannot change the value for option {0} this option is frozen" msgstr "" -"ne peut modifié la valeur de l'option {0} cette option n'est pas modifiable" +"ne peut modifier la valeur de l'option {0} cette option n'est pas modifiable" #: tiramisu/setting.py:446 msgid "trying to access to an option named: {0} with properties {1}" @@ -388,7 +388,7 @@ msgid "" "malformed requirements imbrication detected for option: '{0}' with " "requirement on: '{1}'" msgstr "" -"imbrication de requirements malformé detectée pour l'option : '{0}' avec " +"imbrication de requirements mal formés detectée pour l'option : '{0}' avec " "requirement sur : '{1}'" #: tiramisu/setting.py:570 From f040d3da61fa87a5abb250b423555b64a6caf964 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 18:35:11 +0200 Subject: [PATCH 10/18] warning is now a dict --- test/test_option_validator.py | 19 +++++++++++------- tiramisu/value.py | 36 ++++++++++++++++++++--------------- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index f2ccd8b..057aa4d 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -80,15 +80,20 @@ def test_validator_warning(): assert cfg.cfgimpl_get_values().has_warning() is False cfg.opt2 = 'val' assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option opt2: error' + assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error'} assert cfg.cfgimpl_get_values().has_warning() is False cfg.opt3.append('val') assert cfg.cfgimpl_get_values().has_warning() is False cfg.opt3.append('val1') assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option opt3: error' + assert cfg.cfgimpl_get_values().get_warnings() == {opt3: 'invalid value val1 for option opt3: error'} assert cfg.cfgimpl_get_values().has_warning() is False raises(ValueError, "cfg.opt2 = 1") + cfg.opt2 = 'val' + cfg.opt3.append('val') + assert cfg.cfgimpl_get_values().has_warning() is True + assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error', opt3: 'invalid value val1 for option opt3: error'} + assert cfg.cfgimpl_get_values().has_warning() is False def test_validator_warning_master_slave(): @@ -104,13 +109,13 @@ def test_validator_warning_master_slave(): cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['val1'] assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val1 for option netmask_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {netmask_admin_eth0: 'invalid value val1 for option netmask_admin_eth0: error'} cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] - assert cfg.cfgimpl_get_values().get_last_warning() == 'invalid value val for option ip_admin_eth0: error' + assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} diff --git a/tiramisu/value.py b/tiramisu/value.py index d2aa6a1..a180806 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -45,6 +45,7 @@ class Values(object): self.context = weakref.ref(context) # the storage type is dictionary or sqlite3 self._p_ = storage + self._warning = {} def _getdefault(self, opt): """ @@ -106,9 +107,9 @@ class Values(object): path = self._get_opt_path(opt) if self._p_.hasvalue(path): setting = self.context().cfgimpl_get_settings() - self._warning = opt.impl_validate(opt.impl_getdefault(), - self.context(), - 'validator' in setting) + self._setwarning(opt.impl_validate(opt.impl_getdefault(), + self.context(), + 'validator' in setting), opt) self.context().cfgimpl_reset_cache() if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master): @@ -230,8 +231,8 @@ class Values(object): else: value = self._getvalue(opt, path, validate) if config_error is None and validate: - self._warning = opt.impl_validate(value, self.context(), - 'validator' in setting) + self._setwarning(opt.impl_validate(value, self.context(), + 'validator' in setting), opt) if config_error is None and self._is_default_owner(path) and \ 'force_store_value' in setting[opt]: self.setitem(opt, value, path, is_write=False) @@ -252,9 +253,9 @@ class Values(object): # is_write is, for example, used with "force_store_value" # user didn't change value, so not write # valid opt - self._warning = opt.impl_validate(value, self.context(), - 'validator' in self.context( - ).cfgimpl_get_settings()) + self._setwarning(opt.impl_validate(value, self.context(), + 'validator' in self.context( + ).cfgimpl_get_settings()), opt) if opt.impl_is_multi() and not isinstance(value, Multi): value = Multi(value, self.context, opt, path, setitem=True) self._setvalue(opt, path, value, force_permissive=force_permissive, @@ -373,19 +374,24 @@ class Values(object): def __setstate__(self, states): self._p_ = states['_p_'] + def _setwarning(self, msg, opt): + if msg is not None: + self._warning[opt] = msg + def has_warning(self): """If option is "only_warning", validation error is store in self._warning. has_warning just indicate that a warning message is store """ - return self._warning is not None + return self._warning != {} - def get_last_warning(self): - """Get last warning message in self._warning. - We can get only one time this message. + def get_warnings(self): + """Get last warnings messages in self._warning. + We can get only one time those messages. + :returns: {opt: msg, opt1: msg1} """ ret = self._warning - self._warning = None + self._warning = {} return ret @@ -558,9 +564,9 @@ class Multi(list): def _validate(self, value): if value is not None: try: - self.context().cfgimpl_get_values()._warning = \ + self.context().cfgimpl_get_values()._setwarning( self.opt.impl_validate(value, context=self.context(), - force_no_multi=True) + force_no_multi=True), self.opt) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}" From f4677b9ef94a82e2c8f19fbcbebee251183cbe0f Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 21:56:06 +0200 Subject: [PATCH 11/18] use warnings instead of a new dictionary --- test/test_option_validator.py | 98 +++++++++++++++++++++++------------ tiramisu/error.py | 32 ++++++++++++ tiramisu/option.py | 9 ++-- tiramisu/value.py | 42 ++++----------- 4 files changed, 112 insertions(+), 69 deletions(-) diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 057aa4d..76078f1 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -1,9 +1,11 @@ import autopath +import warnings from py.test import raises from tiramisu.config import Config from tiramisu.option import StrOption, OptionDescription from tiramisu.setting import groups +from tiramisu.error import ValueWarning def return_true(value, param=None): @@ -76,24 +78,36 @@ def test_validator_warning(): root = OptionDescription('root', '', [opt1, opt2, opt3]) cfg = Config(root) assert cfg.opt1 == 'val' - cfg.opt1 = 'val' - assert cfg.cfgimpl_get_values().has_warning() is False - cfg.opt2 = 'val' - assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error'} - assert cfg.cfgimpl_get_values().has_warning() is False - cfg.opt3.append('val') - assert cfg.cfgimpl_get_values().has_warning() is False - cfg.opt3.append('val1') - assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_warnings() == {opt3: 'invalid value val1 for option opt3: error'} - assert cfg.cfgimpl_get_values().has_warning() is False + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + cfg.opt1 = 'val' + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + cfg.opt2 = 'val' + assert len(w) == 1 + assert w[0].message.opt == opt2 + assert str(w[0].message) == 'invalid value val for option opt2: error' + # + with warnings.catch_warnings(record=True) as w: + cfg.opt3.append('val') + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + cfg.opt3.append('val1') + assert len(w) == 1 + assert w[0].message.opt == opt3 + assert str(w[0].message) == 'invalid value val1 for option opt3: error' raises(ValueError, "cfg.opt2 = 1") - cfg.opt2 = 'val' - cfg.opt3.append('val') - assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error', opt3: 'invalid value val1 for option opt3: error'} - assert cfg.cfgimpl_get_values().has_warning() is False + # + with warnings.catch_warnings(record=True) as w: + cfg.opt2 = 'val' + cfg.opt3.append('val') + assert len(w) == 2 + assert w[0].message.opt == opt2 + assert str(w[0].message) == 'invalid value val for option opt2: error' + assert w[1].message.opt == opt3 + assert str(w[1].message) == 'invalid value val1 for option opt3: error' def test_validator_warning_master_slave(): @@ -104,18 +118,38 @@ def test_validator_warning_master_slave(): assert interface1.impl_get_group_type() == groups.master root = OptionDescription('root', '', [interface1]) cfg = Config(root) - cfg.ip_admin_eth0.ip_admin_eth0.append(None) - assert cfg.cfgimpl_get_values().has_warning() is False - cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] - assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['val1'] - assert cfg.cfgimpl_get_values().has_warning() is True - assert cfg.cfgimpl_get_values().get_warnings() == {netmask_admin_eth0: 'invalid value val1 for option netmask_admin_eth0: error'} - cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] - assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] - assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} - cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] - assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} - cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] - assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} - cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] - assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} + warnings.simplefilter("always", ValueWarning) + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0.append(None) + assert w == [] + # + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] + assert len(w) == 1 + assert w[0].message.opt == netmask_admin_eth0 + assert str(w[0].message) == 'invalid value val1 for option netmask_admin_eth0: error' + # + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' + # + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' + # + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' + # + warnings.resetwarnings() + with warnings.catch_warnings(record=True) as w: + cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] + assert len(w) == 1 + assert w[0].message.opt == ip_admin_eth0 + assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' diff --git a/tiramisu/error.py b/tiramisu/error.py index 6c92be3..a4b3f41 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -61,3 +61,35 @@ class SlaveError(Exception): class ConstError(TypeError): "no uniq value in _NameSpace" pass + + +#Warning +class ValueWarning(UserWarning): + """Option could warn user and not raise ValueError. + + Example: + + >>> import warnings + >>> from tiramisu.error import ValueWarning + >>> from tiramisu.option import StrOption, OptionDescription + >>> from tiramisu.config import Config + >>> warnings.simplefilter("always", ValueWarning) + >>> def a(val): + ... raise ValueError('pouet') + ... + >>> s=StrOption('s', '', validator=a, only_warning=True) + >>> o=OptionDescription('o', '', [s]) + >>> c=Config(o) + >>> c.s = 'val' + StrOption:0: ValueWarning: invalid value val for option s: pouet + >>> with warnings.catch_warnings(record=True) as w: + ... c.s = 'val' + ... + >>> w[0].message.opt == s + True + >>> print str(w[0].message) + invalid value val for option s: pouet + """ + def __init__(self, msg, opt): + self.opt = opt + super(ValueWarning, self).__init__(msg) diff --git a/tiramisu/option.py b/tiramisu/option.py index c49e5e4..74f9899 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -25,8 +25,9 @@ import sys from copy import copy, deepcopy from types import FunctionType from IPy import IP +import warnings -from tiramisu.error import ConflictError +from tiramisu.error import ConflictError, ValueWarning from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -474,7 +475,6 @@ class Option(BaseOption): def do_validation(_value, _index=None): if _value is None: return - ret_validation = None try: # valid with self._validator val_validator(_value) @@ -486,12 +486,13 @@ class Option(BaseOption): msg = _("invalid value {0} for option {1}: {2}").format( _value, self._name, err) if self._only_warning: - ret_validation = msg + warnings.warn_explicit(ValueWarning(msg, self), + ValueWarning, + self.__class__.__name__, 0) else: raise ValueError(msg) # option validation self._validate(_value) - return ret_validation # generic calculation if context is not None: diff --git a/tiramisu/value.py b/tiramisu/value.py index a180806..6942453 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -33,7 +33,7 @@ class Values(object): but the values are physicaly located here, in `Values`, wich is also responsible of a caching utility. """ - __slots__ = ('context', '_warning', '_p_', '__weakref__') + __slots__ = ('context', '_p_', '__weakref__') def __init__(self, context, storage): """ @@ -45,7 +45,6 @@ class Values(object): self.context = weakref.ref(context) # the storage type is dictionary or sqlite3 self._p_ = storage - self._warning = {} def _getdefault(self, opt): """ @@ -107,9 +106,9 @@ class Values(object): path = self._get_opt_path(opt) if self._p_.hasvalue(path): setting = self.context().cfgimpl_get_settings() - self._setwarning(opt.impl_validate(opt.impl_getdefault(), - self.context(), - 'validator' in setting), opt) + opt.impl_validate(opt.impl_getdefault(), + self.context(), + 'validator' in setting) self.context().cfgimpl_reset_cache() if (opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master): @@ -231,8 +230,7 @@ class Values(object): else: value = self._getvalue(opt, path, validate) if config_error is None and validate: - self._setwarning(opt.impl_validate(value, self.context(), - 'validator' in setting), opt) + opt.impl_validate(value, self.context(), 'validator' in setting) if config_error is None and self._is_default_owner(path) and \ 'force_store_value' in setting[opt]: self.setitem(opt, value, path, is_write=False) @@ -253,9 +251,8 @@ class Values(object): # is_write is, for example, used with "force_store_value" # user didn't change value, so not write # valid opt - self._setwarning(opt.impl_validate(value, self.context(), - 'validator' in self.context( - ).cfgimpl_get_settings()), opt) + opt.impl_validate(value, self.context(), + 'validator' in self.context().cfgimpl_get_settings()) if opt.impl_is_multi() and not isinstance(value, Multi): value = Multi(value, self.context, opt, path, setitem=True) self._setvalue(opt, path, value, force_permissive=force_permissive, @@ -374,26 +371,6 @@ class Values(object): def __setstate__(self, states): self._p_ = states['_p_'] - def _setwarning(self, msg, opt): - if msg is not None: - self._warning[opt] = msg - - def has_warning(self): - """If option is "only_warning", validation error is store in - self._warning. - has_warning just indicate that a warning message is store - """ - return self._warning != {} - - def get_warnings(self): - """Get last warnings messages in self._warning. - We can get only one time those messages. - :returns: {opt: msg, opt1: msg1} - """ - ret = self._warning - self._warning = {} - return ret - # ____________________________________________________________ # multi types @@ -564,9 +541,8 @@ class Multi(list): def _validate(self, value): if value is not None: try: - self.context().cfgimpl_get_values()._setwarning( - self.opt.impl_validate(value, context=self.context(), - force_no_multi=True), self.opt) + self.opt.impl_validate(value, context=self.context(), + force_no_multi=True) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}" From a004f30e344928da1f0833214014058559cccd53 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 22:11:25 +0200 Subject: [PATCH 12/18] french translation correction --- tiramisu/option.py | 5 +++++ translations/fr/tiramisu.po | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 74f9899..6fd0568 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -922,6 +922,11 @@ class NetmaskOption(Option): val_netmask, self._name)) +class BroadcastOption(Option): + def _validate(self, value): + IP('{0}/32'.format(value)) + + class DomainnameOption(Option): "represents the choice of a domain name" __slots__ = ('_type', '_allow_ip') diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index 66623c1..e1cc0b3 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -225,7 +225,7 @@ msgstr "nom de l'option dupliqué : {0}" #: tiramisu/option.py:998 msgid "unknown Option {0} in OptionDescription {1}" -msgstr "Option {} inconnue pour l'OptionDescription{}" +msgstr "Option {0} inconnue pour l'OptionDescription {1}" #: tiramisu/option.py:1049 msgid "duplicate option: {0}" From 1d2008fd84df7dbbf1ffb9d4be632626a3a94512 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 22:27:39 +0200 Subject: [PATCH 13/18] error message for ip/broadcast/netmask/network validation --- tiramisu/option.py | 20 +++- translations/fr/tiramisu.po | 227 +++++++++++++++++++----------------- translations/tiramisu.pot | 196 ++++++++++++++++--------------- 3 files changed, 240 insertions(+), 203 deletions(-) diff --git a/tiramisu/option.py b/tiramisu/option.py index 6fd0568..cf35508 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -782,7 +782,10 @@ class IPOption(Option): only_warning=only_warning) def _validate(self, value): - IP('{0}/32'.format(value)) + try: + IP('{0}/32'.format(value)) + except ValueError: + raise ValueError(_('invalid IP {0}').format(self._name)) def _second_level_validation(self, value): ip = IP('{0}/32'.format(value)) @@ -868,7 +871,10 @@ class NetworkOption(Option): _opt_type = 'network' def _validate(self, value): - IP(value) + try: + IP(value) + except ValueError: + raise ValueError(_('invalid network address {0}').format(self._name)) def _second_level_validation(self, value): ip = IP(value) @@ -882,7 +888,10 @@ class NetmaskOption(Option): _opt_type = 'netmask' def _validate(self, value): - IP('0.0.0.0/{0}'.format(value)) + try: + IP('0.0.0.0/{0}'.format(value)) + except ValueError: + raise ValueError(_('invalid netmask address {0}').format(self._name)) def _cons_network_netmask(self, optname, value, value_): #opts must be (netmask, network) options @@ -924,7 +933,10 @@ class NetmaskOption(Option): class BroadcastOption(Option): def _validate(self, value): - IP('{0}/32'.format(value)) + try: + IP('{0}/32'.format(value)) + except ValueError: + raise ValueError(_('invalid broadcast address {0}').format(self._name)) class DomainnameOption(Option): diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index e1cc0b3..a81e14e 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-09-23 20:59+CEST\n" +"POT-Creation-Date: 2013-09-26 22:24+CEST\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: LANGUAGE \n" @@ -11,270 +11,279 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" -#: tiramisu/autolib.py:131 +#: tiramisu/autolib.py:145 msgid "" "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" "impossible d'effectuer le calcul, l'option {0} a les propriétés : {1} pour : " "{2}" -#: tiramisu/autolib.py:140 +#: tiramisu/autolib.py:154 msgid "" "unable to carry out a calculation, option value with multi types must have " "same length for: {0}" msgstr "" -"impossible d'effectuer le calcul, la valeur d'une option avec le type multi doit " -"avoir la même longueur pour : {0}" +"impossible d'effectuer le calcul, la valeur d'une option avec le type multi " +"doit avoir la même longueur pour : {0}" -#: tiramisu/config.py:48 +#: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "descr doit être une optiondescription pas un {0}" -#: tiramisu/config.py:122 +#: tiramisu/config.py:127 msgid "unknown group_type: {0}" msgstr "group_type inconnu: {0}" -#: tiramisu/config.py:158 +#: tiramisu/config.py:163 msgid "" "no option description found for this config (may be metaconfig without meta)" msgstr "" -"pas d'option description trouvé pour cette config (peut être une metaconfig sans " -"meta)" +"pas d'option description trouvé pour cette config (peut être une metaconfig " +"sans meta)" -#: tiramisu/config.py:313 +#: tiramisu/config.py:318 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:352 +#: tiramisu/config.py:357 msgid "no option found in config with these criteria" msgstr "aucune option trouvée dans la config avec ces critères" -#: tiramisu/config.py:402 +#: tiramisu/config.py:407 msgid "make_dict can't filtering with value without option" msgstr "make_dict ne peut filtrer sur une valeur mais sans option" -#: tiramisu/config.py:423 +#: tiramisu/config.py:428 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:483 +#: tiramisu/config.py:488 msgid "opt in getowner must be an option not {0}" msgstr "opt dans getowner doit être une option pas {0}" -#: tiramisu/option.py:69 +#: tiramisu/option.py:70 msgid "invalid name: {0} for option" msgstr "nom invalide : {0} pour l'option" -#: tiramisu/option.py:79 +#: tiramisu/option.py:80 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "type des properties invalide {0} pour {1}, doit être un tuple" -#: tiramisu/option.py:121 +#: tiramisu/option.py:122 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule" -#: tiramisu/option.py:148 tiramisu/value.py:361 +#: tiramisu/option.py:149 tiramisu/value.py:362 msgid "information's item not found: {0}" msgstr "aucune config spécifié alors que c'est nécessaire" -#: tiramisu/option.py:265 +#: tiramisu/option.py:266 msgid "cannot serialize Option, only in OptionDescription" msgstr "ne peut serialiser une Option, seulement via une OptionDescription" -#: tiramisu/option.py:364 +#: tiramisu/option.py:367 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -"une default_multi est renseignée alors que multi est False dans l'option : {0}" +"une default_multi est renseignée alors que multi est False dans l'option : " +"{0}" -#: tiramisu/option.py:370 +#: tiramisu/option.py:373 msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}" -#: tiramisu/option.py:375 +#: tiramisu/option.py:378 msgid "default value not allowed if option: {0} is calculated" msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculée" -#: tiramisu/option.py:378 +#: tiramisu/option.py:381 msgid "" "params defined for a callback function but no callback defined yet for " "option {0}" msgstr "" -"params définis pour une fonction callback mais par de callback encore définis " -"pour l'option {0}" +"params définis pour une fonction callback mais par de callback encore " +"définis pour l'option {0}" -#: tiramisu/option.py:463 -msgid "validator should return a boolean, not {0}" -msgstr "le validator devrait retourner un boolean, pas un {0}" - -#: tiramisu/option.py:473 -msgid "invalid value {0} for option {1} for object {2}" -msgstr "valeur invalide {0} pour l'option {1} pour l'objet {2}" - -#: tiramisu/option.py:481 tiramisu/value.py:542 +#: tiramisu/option.py:486 tiramisu/value.py:547 msgid "invalid value {0} for option {1}: {2}" msgstr "valeur invalide {0} pour l'option {1} : {2}" -#: tiramisu/option.py:493 +#: tiramisu/option.py:506 msgid "invalid value {0} for option {1} which must be a list" msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste" -#: tiramisu/option.py:562 +#: tiramisu/option.py:577 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" "valeur invalide {0} pour l'option {1} doit être différente de l'option {2}" -#: tiramisu/option.py:618 +#: tiramisu/option.py:636 msgid "values must be a tuple for {0}" msgstr "values doit être un tuple pour {0}" -#: tiramisu/option.py:621 +#: tiramisu/option.py:639 msgid "open_values must be a boolean for {0}" msgstr "open_values doit être un booléen pour {0}" -#: tiramisu/option.py:642 +#: tiramisu/option.py:661 msgid "value {0} is not permitted, only {1} is allowed" msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées" -#: tiramisu/option.py:654 +#: tiramisu/option.py:673 msgid "value must be a boolean" msgstr "valeur doit être un booléen" -#: tiramisu/option.py:664 +#: tiramisu/option.py:683 msgid "value must be an integer" msgstr "valeur doit être un nombre entier" -#: tiramisu/option.py:674 +#: tiramisu/option.py:693 msgid "value must be a float" msgstr "valeur doit être un nombre flottant" -#: tiramisu/option.py:684 +#: tiramisu/option.py:703 msgid "value must be a string, not {0}" msgstr "valeur doit être une chaîne, pas {0}" -#: tiramisu/option.py:702 +#: tiramisu/option.py:721 msgid "value must be an unicode" msgstr "valeur doit être une valeur unicode" -#: tiramisu/option.py:714 +#: tiramisu/option.py:733 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" -#: tiramisu/option.py:766 +#: tiramisu/option.py:788 +msgid "invalid IP {0}" +msgstr "adresse IP invalide {0}" + +#: tiramisu/option.py:793 msgid "IP mustn't not be in reserved class" msgstr "IP ne doit pas être d'une classe reservée" -#: tiramisu/option.py:768 +#: tiramisu/option.py:795 msgid "IP must be in private class" msgstr "IP doit être dans la classe privée" -#: tiramisu/option.py:806 +#: tiramisu/option.py:833 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option.py:811 +#: tiramisu/option.py:838 msgid "max value is empty" msgstr "la valeur maximum est vide" -#: tiramisu/option.py:848 +#: tiramisu/option.py:877 +msgid "invalid network address {0}" +msgstr "adresse réseau invalide {0}" + +#: tiramisu/option.py:882 msgid "network shall not be in reserved class" msgstr "le réseau ne doit pas être dans la classe reservée" -#: tiramisu/option.py:880 +#: tiramisu/option.py:894 +msgid "invalid netmask address {0}" +msgstr "masque de sous-réseau invalide {0}" + +#: tiramisu/option.py:916 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3}), ce réseau est une IP" -#: tiramisu/option.py:885 +#: tiramisu/option.py:921 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "IP invalide {0} ({1}) avec masque {2} ({3}), cette IP est un réseau" -#: tiramisu/option.py:890 +#: tiramisu/option.py:926 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "IP invalide {0} ({1}) avec masque {2} ({3})" -#: tiramisu/option.py:892 +#: tiramisu/option.py:928 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "réseau invalide {0} ({1}) avec masque {2} ({3})" -#: tiramisu/option.py:912 +#: tiramisu/option.py:939 +msgid "invalid broadcast address {0}" +msgstr "adresse de broadcast invalide {0}" + +#: tiramisu/option.py:957 msgid "unknown type_ {0} for hostname" msgstr "type_ inconnu {0} pour le nom d'hôte" -#: tiramisu/option.py:915 +#: tiramisu/option.py:960 msgid "allow_ip must be a boolean" msgstr "allow_ip doit être un booléen" -#: tiramisu/option.py:944 +#: tiramisu/option.py:990 msgid "invalid value for {0}, must have dot" msgstr "valeur invalide pour {0}, doit avoir un point" -#: tiramisu/option.py:947 +#: tiramisu/option.py:993 msgid "invalid domainname's length for {0} (max {1})" msgstr "longueur du nom de domaine invalide pour {0} (maximum {1})" -#: tiramisu/option.py:950 +#: tiramisu/option.py:996 msgid "invalid domainname's length for {0} (min 2)" msgstr "longueur du nom de domaine invalide pour {0} (minimum 2)" -#: tiramisu/option.py:954 +#: tiramisu/option.py:1000 msgid "invalid domainname" msgstr "nom de domaine invalide" -#: tiramisu/option.py:981 +#: tiramisu/option.py:1027 msgid "duplicate option name: {0}" msgstr "nom de l'option dupliqué : {0}" -#: tiramisu/option.py:998 +#: tiramisu/option.py:1044 msgid "unknown Option {0} in OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}" -#: tiramisu/option.py:1049 +#: tiramisu/option.py:1095 msgid "duplicate option: {0}" msgstr "option dupliquée : {0}" -#: tiramisu/option.py:1083 +#: tiramisu/option.py:1129 msgid "no option for path {0}" msgstr "pas d'option pour le chemin {0}" -#: tiramisu/option.py:1089 +#: tiramisu/option.py:1135 msgid "no option {0} found" msgstr "pas d'option {0} trouvée" -#: tiramisu/option.py:1099 +#: tiramisu/option.py:1145 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})" -#: tiramisu/option.py:1112 +#: tiramisu/option.py:1158 msgid "master group {0} shall not have a subgroup" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" -#: tiramisu/option.py:1115 +#: tiramisu/option.py:1161 msgid "master group {0} shall not have a symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" -#: tiramisu/option.py:1118 +#: tiramisu/option.py:1164 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" "option non autorisée {0} dans le groupe {1} : cette option n'est pas une " "multi" -#: tiramisu/option.py:1129 +#: tiramisu/option.py:1175 msgid "master group with wrong master name for {0}" msgstr "le groupe maître avec un nom de maître érroné pour {0}" -#: tiramisu/option.py:1138 +#: tiramisu/option.py:1184 msgid "no child has same nom has master group for: {0}" msgstr "pas d'enfant avec le nom du groupe maître pour {0} " -#: tiramisu/option.py:1141 +#: tiramisu/option.py:1187 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" -#: tiramisu/option.py:1231 +#: tiramisu/option.py:1277 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" "type requirements malformé pour l'option : {0}, doit être un dictionnaire" -#: tiramisu/option.py:1248 +#: tiramisu/option.py:1294 msgid "" "malformed requirements for option: {0} require must have option, expected " "and action keys" @@ -282,64 +291,66 @@ msgstr "" "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "option, expected et action" -#: tiramisu/option.py:1253 +#: tiramisu/option.py:1299 msgid "malformed requirements for option: {0} inverse must be boolean" -msgstr "requirements mal formés pour l'option : {0} inverse doit être un booléen" +msgstr "" +"requirements mal formés pour l'option : {0} inverse doit être un booléen" -#: tiramisu/option.py:1257 +#: tiramisu/option.py:1303 msgid "malformed requirements for option: {0} transitive must be boolean" -msgstr "requirements mal formés pour l'option : {0} transitive doit être booléen" +msgstr "" +"requirements mal formés pour l'option : {0} transitive doit être booléen" -#: tiramisu/option.py:1261 +#: tiramisu/option.py:1307 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" "requirements mal formés pour l'option : {0} same_action doit être un booléen" -#: tiramisu/option.py:1265 +#: tiramisu/option.py:1311 msgid "malformed requirements must be an option in option {0}" msgstr "requirements mal formés doit être une option dans l'option {0}" -#: tiramisu/option.py:1268 +#: tiramisu/option.py:1314 msgid "malformed requirements option {0} should not be a multi" msgstr "requirements mal formés l'option {0} ne doit pas être une multi" -#: tiramisu/option.py:1274 +#: tiramisu/option.py:1320 msgid "" "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -"requirements mal formés deuxième argument doit être valide pour l'option {0} : " -"{1}" +"requirements mal formés deuxième argument doit être valide pour l'option " +"{0} : {1}" -#: tiramisu/option.py:1279 +#: tiramisu/option.py:1325 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "incohérence dans les types action pour l'option : {0} action {1}" -#: tiramisu/option.py:1304 +#: tiramisu/option.py:1350 msgid "{0} should be a function" msgstr "{0} doit être une fonction" -#: tiramisu/option.py:1307 +#: tiramisu/option.py:1353 msgid "{0}_params should be a dict" msgstr "{0}_params devrait être un dict" -#: tiramisu/option.py:1310 +#: tiramisu/option.py:1356 msgid "{0}_params with key {1} should not have length different to 1" msgstr "" "{0}_params avec la clef {1} devrait ne pas avoir une longueur différent de 1" -#: tiramisu/option.py:1314 +#: tiramisu/option.py:1360 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" -#: tiramisu/option.py:1320 +#: tiramisu/option.py:1366 msgid "validator not support tuple" msgstr "validator n'accepte pas de tuple" -#: tiramisu/option.py:1323 +#: tiramisu/option.py:1369 msgid "{0}_params should have an option not a {0} for first argument" msgstr "{0}_params devrait avoir une option pas un {0} pour premier argument" -#: tiramisu/option.py:1327 +#: tiramisu/option.py:1373 msgid "{0}_params should have a boolean not a {0} for second argument" msgstr "{0}_params devrait avoir un boolean pas un {0} pour second argument" @@ -379,7 +390,7 @@ msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1} msgid "permissive must be a tuple" msgstr "permissive doit être un tuple" -#: tiramisu/setting.py:471 tiramisu/value.py:300 +#: tiramisu/setting.py:471 tiramisu/value.py:301 msgid "invalid generic owner {0}" msgstr "invalide owner générique {0}" @@ -416,48 +427,54 @@ msgstr "session déjà utilisée" msgid "a dictionary cannot be persistent" msgstr "un espace de stockage dictionary ne peut être persistant" -#: tiramisu/value.py:307 +#: tiramisu/value.py:308 msgid "no value for {0} cannot change owner to {1}" msgstr "pas de valeur pour {0} ne peut changer d'utilisateur pour {1}" -#: tiramisu/value.py:414 +#: tiramisu/value.py:416 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "longueur invalide pour une esclave : {0} qui a {1} comme maître" -#: tiramisu/value.py:438 +#: tiramisu/value.py:440 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" "longueur invalide pour un maître : {0} qui a {1} une esclave avec une plus " "grande longueur" -#: tiramisu/value.py:468 +#: tiramisu/value.py:470 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "ne peut ajouter une valeur sur l'option multi {0} qui est une esclave" -#: tiramisu/value.py:503 +#: tiramisu/value.py:507 msgid "cannot sort multi option {0} if master or slave" msgstr "ne peut trier une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:507 +#: tiramisu/value.py:511 msgid "cmp is not permitted in python v3 or greater" msgstr "cmp n'est pas permis en python v3 ou supérieure" -#: tiramisu/value.py:516 +#: tiramisu/value.py:520 msgid "cannot reverse multi option {0} if master or slave" msgstr "ne peut inverser une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:524 +#: tiramisu/value.py:528 msgid "cannot insert multi option {0} if master or slave" msgstr "ne peut insérer une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:532 +#: tiramisu/value.py:536 msgid "cannot extend multi option {0} if master or slave" msgstr "ne peut étendre une option multi {0} pour une maître ou une esclave" -#: tiramisu/value.py:559 +#: tiramisu/value.py:564 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave" +#~ msgid "validator should return a boolean, not {0}" +#~ msgstr "le validator devrait retourner un boolean, pas un {0}" + +#~ msgid "invalid value {0} for option {1} for object {2}" +#~ msgstr "valeur invalide {0} pour l'option {1} pour l'objet {2}" + #~ msgid "no config specified but needed" #~ msgstr "aucune config spécifié alors que c'est nécessaire" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 8b37d6a..7304c9f 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2013-09-23 20:59+CEST\n" +"POT-Creation-Date: 2013-09-26 22:24+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -15,307 +15,315 @@ msgstr "" "Generated-By: pygettext.py 1.5\n" -#: tiramisu/autolib.py:131 +#: tiramisu/autolib.py:145 msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgstr "" -#: tiramisu/autolib.py:140 +#: tiramisu/autolib.py:154 msgid "unable to carry out a calculation, option value with multi types must have same length for: {0}" msgstr "" -#: tiramisu/config.py:48 +#: tiramisu/config.py:52 msgid "descr must be an optiondescription, not {0}" msgstr "" -#: tiramisu/config.py:122 +#: tiramisu/config.py:127 msgid "unknown group_type: {0}" msgstr "" -#: tiramisu/config.py:158 +#: tiramisu/config.py:163 msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:313 +#: tiramisu/config.py:318 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:352 +#: tiramisu/config.py:357 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:402 +#: tiramisu/config.py:407 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:423 +#: tiramisu/config.py:428 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:483 +#: tiramisu/config.py:488 msgid "opt in getowner must be an option not {0}" msgstr "" -#: tiramisu/option.py:69 +#: tiramisu/option.py:70 msgid "invalid name: {0} for option" msgstr "" -#: tiramisu/option.py:79 +#: tiramisu/option.py:80 msgid "invalid properties type {0} for {1}, must be a tuple" msgstr "" -#: tiramisu/option.py:121 +#: tiramisu/option.py:122 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option.py:148 tiramisu/value.py:361 +#: tiramisu/option.py:149 tiramisu/value.py:362 msgid "information's item not found: {0}" msgstr "" -#: tiramisu/option.py:265 +#: tiramisu/option.py:266 msgid "cannot serialize Option, only in OptionDescription" msgstr "" -#: tiramisu/option.py:364 +#: tiramisu/option.py:367 msgid "a default_multi is set whereas multi is False in option: {0}" msgstr "" -#: tiramisu/option.py:370 +#: tiramisu/option.py:373 msgid "invalid default_multi value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:375 +#: tiramisu/option.py:378 msgid "default value not allowed if option: {0} is calculated" msgstr "" -#: tiramisu/option.py:378 +#: tiramisu/option.py:381 msgid "params defined for a callback function but no callback defined yet for option {0}" msgstr "" -#: tiramisu/option.py:463 -msgid "validator should return a boolean, not {0}" -msgstr "" - -#: tiramisu/option.py:473 -msgid "invalid value {0} for option {1} for object {2}" -msgstr "" - -#: tiramisu/option.py:481 tiramisu/value.py:542 +#: tiramisu/option.py:486 tiramisu/value.py:547 msgid "invalid value {0} for option {1}: {2}" msgstr "" -#: tiramisu/option.py:493 +#: tiramisu/option.py:506 msgid "invalid value {0} for option {1} which must be a list" msgstr "" -#: tiramisu/option.py:562 +#: tiramisu/option.py:577 msgid "invalid value {0} for option {1} must be different as {2} option" msgstr "" -#: tiramisu/option.py:618 +#: tiramisu/option.py:636 msgid "values must be a tuple for {0}" msgstr "" -#: tiramisu/option.py:621 +#: tiramisu/option.py:639 msgid "open_values must be a boolean for {0}" msgstr "" -#: tiramisu/option.py:642 +#: tiramisu/option.py:661 msgid "value {0} is not permitted, only {1} is allowed" msgstr "" -#: tiramisu/option.py:654 +#: tiramisu/option.py:673 msgid "value must be a boolean" msgstr "" -#: tiramisu/option.py:664 +#: tiramisu/option.py:683 msgid "value must be an integer" msgstr "" -#: tiramisu/option.py:674 +#: tiramisu/option.py:693 msgid "value must be a float" msgstr "" -#: tiramisu/option.py:684 +#: tiramisu/option.py:703 msgid "value must be a string, not {0}" msgstr "" -#: tiramisu/option.py:702 +#: tiramisu/option.py:721 msgid "value must be an unicode" msgstr "" -#: tiramisu/option.py:714 +#: tiramisu/option.py:733 msgid "malformed symlinkoption must be an option for symlink {0}" msgstr "" -#: tiramisu/option.py:766 +#: tiramisu/option.py:788 +msgid "invalid IP {0}" +msgstr "" + +#: tiramisu/option.py:793 msgid "IP mustn't not be in reserved class" msgstr "" -#: tiramisu/option.py:768 +#: tiramisu/option.py:795 msgid "IP must be in private class" msgstr "" -#: tiramisu/option.py:806 +#: tiramisu/option.py:833 msgid "inconsistency in allowed range" msgstr "" -#: tiramisu/option.py:811 +#: tiramisu/option.py:838 msgid "max value is empty" msgstr "" -#: tiramisu/option.py:848 +#: tiramisu/option.py:877 +msgid "invalid network address {0}" +msgstr "" + +#: tiramisu/option.py:882 msgid "network shall not be in reserved class" msgstr "" -#: tiramisu/option.py:880 +#: tiramisu/option.py:894 +msgid "invalid netmask address {0}" +msgstr "" + +#: tiramisu/option.py:916 msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP" msgstr "" -#: tiramisu/option.py:885 +#: tiramisu/option.py:921 msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network" msgstr "" -#: tiramisu/option.py:890 +#: tiramisu/option.py:926 msgid "invalid IP {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:892 +#: tiramisu/option.py:928 msgid "invalid network {0} ({1}) with netmask {2} ({3})" msgstr "" -#: tiramisu/option.py:912 +#: tiramisu/option.py:939 +msgid "invalid broadcast address {0}" +msgstr "" + +#: tiramisu/option.py:957 msgid "unknown type_ {0} for hostname" msgstr "" -#: tiramisu/option.py:915 +#: tiramisu/option.py:960 msgid "allow_ip must be a boolean" msgstr "" -#: tiramisu/option.py:944 +#: tiramisu/option.py:990 msgid "invalid value for {0}, must have dot" msgstr "" -#: tiramisu/option.py:947 +#: tiramisu/option.py:993 msgid "invalid domainname's length for {0} (max {1})" msgstr "" -#: tiramisu/option.py:950 +#: tiramisu/option.py:996 msgid "invalid domainname's length for {0} (min 2)" msgstr "" -#: tiramisu/option.py:954 +#: tiramisu/option.py:1000 msgid "invalid domainname" msgstr "" -#: tiramisu/option.py:981 +#: tiramisu/option.py:1027 msgid "duplicate option name: {0}" msgstr "" -#: tiramisu/option.py:998 +#: tiramisu/option.py:1044 msgid "unknown Option {0} in OptionDescription {1}" msgstr "" -#: tiramisu/option.py:1049 +#: tiramisu/option.py:1095 msgid "duplicate option: {0}" msgstr "" -#: tiramisu/option.py:1083 +#: tiramisu/option.py:1129 msgid "no option for path {0}" msgstr "" -#: tiramisu/option.py:1089 +#: tiramisu/option.py:1135 msgid "no option {0} found" msgstr "" -#: tiramisu/option.py:1099 +#: tiramisu/option.py:1145 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option.py:1112 +#: tiramisu/option.py:1158 msgid "master group {0} shall not have a subgroup" msgstr "" -#: tiramisu/option.py:1115 +#: tiramisu/option.py:1161 msgid "master group {0} shall not have a symlinkoption" msgstr "" -#: tiramisu/option.py:1118 +#: tiramisu/option.py:1164 msgid "not allowed option {0} in group {1}: this option is not a multi" msgstr "" -#: tiramisu/option.py:1129 +#: tiramisu/option.py:1175 msgid "master group with wrong master name for {0}" msgstr "" -#: tiramisu/option.py:1138 +#: tiramisu/option.py:1184 msgid "no child has same nom has master group for: {0}" msgstr "" -#: tiramisu/option.py:1141 +#: tiramisu/option.py:1187 msgid "group_type: {0} not allowed" msgstr "" -#: tiramisu/option.py:1231 +#: tiramisu/option.py:1277 msgid "malformed requirements type for option: {0}, must be a dict" msgstr "" -#: tiramisu/option.py:1248 +#: tiramisu/option.py:1294 msgid "malformed requirements for option: {0} require must have option, expected and action keys" msgstr "" -#: tiramisu/option.py:1253 +#: tiramisu/option.py:1299 msgid "malformed requirements for option: {0} inverse must be boolean" msgstr "" -#: tiramisu/option.py:1257 +#: tiramisu/option.py:1303 msgid "malformed requirements for option: {0} transitive must be boolean" msgstr "" -#: tiramisu/option.py:1261 +#: tiramisu/option.py:1307 msgid "malformed requirements for option: {0} same_action must be boolean" msgstr "" -#: tiramisu/option.py:1265 +#: tiramisu/option.py:1311 msgid "malformed requirements must be an option in option {0}" msgstr "" -#: tiramisu/option.py:1268 +#: tiramisu/option.py:1314 msgid "malformed requirements option {0} should not be a multi" msgstr "" -#: tiramisu/option.py:1274 +#: tiramisu/option.py:1320 msgid "malformed requirements second argument must be valid for option {0}: {1}" msgstr "" -#: tiramisu/option.py:1279 +#: tiramisu/option.py:1325 msgid "inconsistency in action types for option: {0} action: {1}" msgstr "" -#: tiramisu/option.py:1304 +#: tiramisu/option.py:1350 msgid "{0} should be a function" msgstr "" -#: tiramisu/option.py:1307 +#: tiramisu/option.py:1353 msgid "{0}_params should be a dict" msgstr "" -#: tiramisu/option.py:1310 +#: tiramisu/option.py:1356 msgid "{0}_params with key {1} should not have length different to 1" msgstr "" -#: tiramisu/option.py:1314 +#: tiramisu/option.py:1360 msgid "{0}_params should be tuple for key \"{1}\"" msgstr "" -#: tiramisu/option.py:1320 +#: tiramisu/option.py:1366 msgid "validator not support tuple" msgstr "" -#: tiramisu/option.py:1323 +#: tiramisu/option.py:1369 msgid "{0}_params should have an option not a {0} for first argument" msgstr "" -#: tiramisu/option.py:1327 +#: tiramisu/option.py:1373 msgid "{0}_params should have a boolean not a {0} for second argument" msgstr "" @@ -351,7 +359,7 @@ msgstr "" msgid "permissive must be a tuple" msgstr "" -#: tiramisu/setting.py:471 tiramisu/value.py:300 +#: tiramisu/setting.py:471 tiramisu/value.py:301 msgid "invalid generic owner {0}" msgstr "" @@ -383,43 +391,43 @@ msgstr "" msgid "a dictionary cannot be persistent" msgstr "" -#: tiramisu/value.py:307 +#: tiramisu/value.py:308 msgid "no value for {0} cannot change owner to {1}" msgstr "" -#: tiramisu/value.py:414 +#: tiramisu/value.py:416 msgid "invalid len for the slave: {0} which has {1} as master" msgstr "" -#: tiramisu/value.py:438 +#: tiramisu/value.py:440 msgid "invalid len for the master: {0} which has {1} as slave with greater len" msgstr "" -#: tiramisu/value.py:468 +#: tiramisu/value.py:470 msgid "cannot append a value on a multi option {0} which is a slave" msgstr "" -#: tiramisu/value.py:503 +#: tiramisu/value.py:507 msgid "cannot sort multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:507 +#: tiramisu/value.py:511 msgid "cmp is not permitted in python v3 or greater" msgstr "" -#: tiramisu/value.py:516 +#: tiramisu/value.py:520 msgid "cannot reverse multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:524 +#: tiramisu/value.py:528 msgid "cannot insert multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:532 +#: tiramisu/value.py:536 msgid "cannot extend multi option {0} if master or slave" msgstr "" -#: tiramisu/value.py:559 +#: tiramisu/value.py:564 msgid "cannot pop a value on a multi option {0} which is a slave" msgstr "" From 3073940ca46e91e54daad91ec371580d990c39c9 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Thu, 26 Sep 2013 22:35:12 +0200 Subject: [PATCH 14/18] can't assign to an OptionDescription --- tiramisu/config.py | 4 +++- translations/fr/tiramisu.po | 16 ++++++++++------ translations/tiramisu.pot | 16 ++++++++++------ 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/tiramisu/config.py b/tiramisu/config.py index 3687349..25dbaf9 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -185,7 +185,9 @@ class SubConfig(object): homeconfig, name = self.cfgimpl_get_home_by_path(name) return homeconfig.__setattr__(name, value) child = getattr(self.cfgimpl_get_description(), name) - if not isinstance(child, SymLinkOption): + if isinstance(child, OptionDescription): + raise SyntaxError(_("can't assign to an OptionDescription")) + elif not isinstance(child, SymLinkOption): if self._impl_path is None: path = name else: diff --git a/translations/fr/tiramisu.po b/translations/fr/tiramisu.po index a81e14e..0075059 100644 --- a/translations/fr/tiramisu.po +++ b/translations/fr/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-09-26 22:24+CEST\n" +"POT-Creation-Date: 2013-09-26 22:33+CEST\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: LANGUAGE \n" @@ -41,23 +41,27 @@ msgstr "" "pas d'option description trouvé pour cette config (peut être une metaconfig " "sans meta)" -#: tiramisu/config.py:318 +#: tiramisu/config.py:189 +msgid "can't assign to an OptionDescription" +msgstr "ne peut pas attribuer une valeur à une OptionDescription" + +#: tiramisu/config.py:320 msgid "unknown type_ type {0}for _find" msgstr "type_ type {0} pour _find inconnu" -#: tiramisu/config.py:357 +#: tiramisu/config.py:359 msgid "no option found in config with these criteria" msgstr "aucune option trouvée dans la config avec ces critères" -#: tiramisu/config.py:407 +#: tiramisu/config.py:409 msgid "make_dict can't filtering with value without option" msgstr "make_dict ne peut filtrer sur une valeur mais sans option" -#: tiramisu/config.py:428 +#: tiramisu/config.py:430 msgid "unexpected path {0}, should start with {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}" -#: tiramisu/config.py:488 +#: tiramisu/config.py:490 msgid "opt in getowner must be an option not {0}" msgstr "opt dans getowner doit être une option pas {0}" diff --git a/translations/tiramisu.pot b/translations/tiramisu.pot index 7304c9f..dfd4b86 100644 --- a/translations/tiramisu.pot +++ b/translations/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2013-09-26 22:24+CEST\n" +"POT-Creation-Date: 2013-09-26 22:33+CEST\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -35,23 +35,27 @@ msgstr "" msgid "no option description found for this config (may be metaconfig without meta)" msgstr "" -#: tiramisu/config.py:318 +#: tiramisu/config.py:189 +msgid "can't assign to an OptionDescription" +msgstr "" + +#: tiramisu/config.py:320 msgid "unknown type_ type {0}for _find" msgstr "" -#: tiramisu/config.py:357 +#: tiramisu/config.py:359 msgid "no option found in config with these criteria" msgstr "" -#: tiramisu/config.py:407 +#: tiramisu/config.py:409 msgid "make_dict can't filtering with value without option" msgstr "" -#: tiramisu/config.py:428 +#: tiramisu/config.py:430 msgid "unexpected path {0}, should start with {1}" msgstr "" -#: tiramisu/config.py:488 +#: tiramisu/config.py:490 msgid "opt in getowner must be an option not {0}" msgstr "" From 162ae02df8fd05acbf9548b137baad73063a3f52 Mon Sep 17 00:00:00 2001 From: gwen Date: Fri, 27 Sep 2013 09:52:18 +0200 Subject: [PATCH 15/18] refactor (warnings_only) --- test/test_config.py | 6 ++++++ test/test_option_validator.py | 10 ++++----- tiramisu/config.py | 2 +- tiramisu/error.py | 2 +- tiramisu/option.py | 38 ++++++++++++++++------------------- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/test/test_config.py b/test/test_config.py index 17e863a..e091294 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -230,3 +230,9 @@ def test_duplicated_option(): root = OptionDescription('root', '', [d1, d2]) #in different OptionDescription raises(ConflictError, "config = Config(root)") + +def test_cannot_assign_value_to_option_description(): + descr = make_description() + cfg = Config(descr) + raises(TypeError, "cfg.gc = 3") + diff --git a/test/test_option_validator.py b/test/test_option_validator.py index 76078f1..001f9f7 100644 --- a/test/test_option_validator.py +++ b/test/test_option_validator.py @@ -72,9 +72,9 @@ def test_validator_multi(): def test_validator_warning(): - opt1 = StrOption('opt1', '', validator=return_true, default='val', only_warning=True) - opt2 = StrOption('opt2', '', validator=return_false, only_warning=True) - opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, only_warning=True) + opt1 = StrOption('opt1', '', validator=return_true, default='val', warnings_only=True) + opt2 = StrOption('opt2', '', validator=return_false, warnings_only=True) + opt3 = StrOption('opt3', '', validator=return_if_val, multi=True, warnings_only=True) root = OptionDescription('root', '', [opt1, opt2, opt3]) cfg = Config(root) assert cfg.opt1 == 'val' @@ -111,8 +111,8 @@ def test_validator_warning(): def test_validator_warning_master_slave(): - ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=return_false, only_warning=True) - netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=return_if_val, only_warning=True) + ip_admin_eth0 = StrOption('ip_admin_eth0', "ip reseau autorise", multi=True, validator=return_false, warnings_only=True) + netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-reseau", multi=True, validator=return_if_val, warnings_only=True) interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0]) interface1.impl_set_group_type(groups.master) assert interface1.impl_get_group_type() == groups.master diff --git a/tiramisu/config.py b/tiramisu/config.py index 25dbaf9..1493e22 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -186,7 +186,7 @@ class SubConfig(object): return homeconfig.__setattr__(name, value) child = getattr(self.cfgimpl_get_description(), name) if isinstance(child, OptionDescription): - raise SyntaxError(_("can't assign to an OptionDescription")) + raise TypeError(_("can't assign to an OptionDescription")) elif not isinstance(child, SymLinkOption): if self._impl_path is None: path = name diff --git a/tiramisu/error.py b/tiramisu/error.py index a4b3f41..5694e4d 100644 --- a/tiramisu/error.py +++ b/tiramisu/error.py @@ -77,7 +77,7 @@ class ValueWarning(UserWarning): >>> def a(val): ... raise ValueError('pouet') ... - >>> s=StrOption('s', '', validator=a, only_warning=True) + >>> s=StrOption('s', '', validator=a, warnings_only=True) >>> o=OptionDescription('o', '', [s]) >>> c=Config(o) >>> c.s = 'val' diff --git a/tiramisu/option.py b/tiramisu/option.py index cf35508..df4839a 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -328,13 +328,13 @@ class Option(BaseOption): """ __slots__ = ('_multi', '_validator', '_default_multi', '_default', '_state_callback', '_callback', '_multitype', - '_only_warning', '_master_slaves', '__weakref__') + '_warnings_only', '_master_slaves', '__weakref__') _empty = '' def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None, only_warning=False): + properties=None, warnings_only=False): """ :param name: the option's name :param doc: the option's description @@ -352,7 +352,7 @@ class Option(BaseOption): validation of the value :param validator_params: the validator's parameters :param properties: tuple of default properties - :param only_warning: _validator and _consistencies don't raise if True + :param warnings_only: _validator and _consistencies don't raise if True Values()._warning contain message """ @@ -391,7 +391,7 @@ class Option(BaseOption): default = [] self._multitype = multitypes.default self._default_multi = default_multi - self._only_warning = only_warning + self._warnings_only = warnings_only self.impl_validate(default) self._default = default @@ -475,6 +475,8 @@ class Option(BaseOption): def do_validation(_value, _index=None): if _value is None: return + # option validation + self._validate(_value) try: # valid with self._validator val_validator(_value) @@ -485,32 +487,26 @@ class Option(BaseOption): except ValueError as err: msg = _("invalid value {0} for option {1}: {2}").format( _value, self._name, err) - if self._only_warning: + if self._warnings_only: warnings.warn_explicit(ValueWarning(msg, self), ValueWarning, self.__class__.__name__, 0) else: raise ValueError(msg) - # option validation - self._validate(_value) # generic calculation if context is not None: descr = context.cfgimpl_get_description() - ret = None if not self._multi or force_no_multi: - ret = do_validation(value) + do_validation(value) else: if not isinstance(value, list): raise ValueError(_("invalid value {0} for option {1} " "which must be a list").format(value, self._name)) for index, val in enumerate(value): - ret_ = do_validation(val, index) - if ret_ is not None: - ret = ret_ - return ret + do_validation(val, index) def impl_getdefault(self, default_multi=False): "accessing the default value" @@ -628,7 +624,7 @@ class ChoiceOption(Option): def __init__(self, name, doc, values, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, open_values=False, validator=None, - validator_params=None, properties=None, only_warning=False): + validator_params=None, properties=None, warnings_only=False): """ :param values: is a list of values the option can possibly take """ @@ -648,7 +644,7 @@ class ChoiceOption(Option): validator=validator, validator_params=validator_params, properties=properties, - only_warning=only_warning) + warnings_only=warnings_only) def impl_get_values(self): return self._values @@ -767,7 +763,7 @@ class IPOption(Option): requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, properties=None, only_private=False, allow_reserved=False, - only_warning=False): + warnings_only=False): self._only_private = only_private self._allow_reserved = allow_reserved super(IPOption, self).__init__(name, doc, default=default, @@ -779,7 +775,7 @@ class IPOption(Option): validator=validator, validator_params=validator_params, properties=properties, - only_warning=only_warning) + warnings_only=warnings_only) def _validate(self, value): try: @@ -813,7 +809,7 @@ class PortOption(Option): callback_params=None, validator=None, validator_params=None, properties=None, allow_range=False, allow_zero=False, allow_wellknown=True, allow_registred=True, - allow_private=False, only_warning=False): + allow_private=False, warnings_only=False): self._allow_range = allow_range self._min_value = None self._max_value = None @@ -846,7 +842,7 @@ class PortOption(Option): validator=validator, validator_params=validator_params, properties=properties, - only_warning=only_warning) + warnings_only=warnings_only) def _validate(self, value): if self._allow_range and ":" in str(value): @@ -948,7 +944,7 @@ class DomainnameOption(Option): requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, properties=None, allow_ip=False, type_='domainname', - only_warning=False): + warnings_only=False): #netbios: for MS domain #hostname: to identify the device #domainname: @@ -968,7 +964,7 @@ class DomainnameOption(Option): validator=validator, validator_params=validator_params, properties=properties, - only_warning=only_warning) + warnings_only=warnings_only) def _validate(self, value): if self._allow_ip is True: From 2490d009353e5c860b9c6858175d81ea4d9ae002 Mon Sep 17 00:00:00 2001 From: gwen Date: Fri, 27 Sep 2013 11:28:23 +0200 Subject: [PATCH 16/18] refactor name only_private --- test/test_config_ip.py | 2 +- tiramisu/option.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test_config_ip.py b/test/test_config_ip.py index 2bdba53..b7d3010 100644 --- a/test/test_config_ip.py +++ b/test/test_config_ip.py @@ -7,7 +7,7 @@ from tiramisu.option import IPOption, NetworkOption, NetmaskOption, \ def test_ip(): a = IPOption('a', '') - b = IPOption('b', '', only_private=True) + b = IPOption('b', '', private_only=True) od = OptionDescription('od', '', [a, b]) c = Config(od) c.a = '192.168.1.1' diff --git a/tiramisu/option.py b/tiramisu/option.py index df4839a..f9a3541 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -756,15 +756,15 @@ class SymLinkOption(BaseOption): class IPOption(Option): "represents the choice of an ip" - __slots__ = ('_only_private', '_allow_reserved') + __slots__ = ('_private_only', '_allow_reserved') _opt_type = 'ip' def __init__(self, name, doc, default=None, default_multi=None, requires=None, multi=False, callback=None, callback_params=None, validator=None, validator_params=None, - properties=None, only_private=False, allow_reserved=False, + properties=None, private_only=False, allow_reserved=False, warnings_only=False): - self._only_private = only_private + self._private_only = private_only self._allow_reserved = allow_reserved super(IPOption, self).__init__(name, doc, default=default, default_multi=default_multi, @@ -787,7 +787,7 @@ class IPOption(Option): ip = IP('{0}/32'.format(value)) if not self._allow_reserved and ip.iptype() == 'RESERVED': raise ValueError(_("IP mustn't not be in reserved class")) - if self._only_private and not ip.iptype() == 'PRIVATE': + if self._private_only and not ip.iptype() == 'PRIVATE': raise ValueError(_("IP must be in private class")) From 482dfec7f2811d872747bd33e1b0db9df4298ba0 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Fri, 27 Sep 2013 23:26:10 +0200 Subject: [PATCH 17/18] consistancies can have more than one option add _cons_broadcast --- test/test_option_consistency.py | 21 +++- test/test_state.py | 3 +- tiramisu/option.py | 191 ++++++++++++++++++-------------- 3 files changed, 130 insertions(+), 85 deletions(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 5cf53cd..1c23dac 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -4,7 +4,7 @@ from py.test import raises from tiramisu.setting import owners, groups from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ - SymLinkOption, OptionDescription + BroadcastOption, SymLinkOption, OptionDescription def test_consistency_not_equal(): @@ -159,3 +159,22 @@ def test_consistency_network_netmask_multi_master(): c.a = ['192.168.1.0'] c.b = ['255.255.255.0'] raises(ValueError, "c.a = ['192.168.1.1']") + + +def test_consistency_broadcast(): + a = NetworkOption('a', '', multi=True) + b = NetmaskOption('b', '', multi=True) + c = BroadcastOption('c', '', multi=True) + od = OptionDescription('a', '', [a, b, c]) + od.impl_set_group_type(groups.master) + b.impl_add_consistency('network_netmask', a) + c.impl_add_consistency('broadcast', a, b) + c = Config(od) + c.a = ['192.168.1.0'] + c.b = ['255.255.255.0'] + c.c = ['192.168.1.255'] + raises(ValueError, "c.a = ['192.168.1.1']") + c.a = ['192.168.1.0', '192.168.2.128'] + c.b = ['255.255.255.0', '255.255.255.128'] + c.c = ['192.168.1.255', '192.168.2.255'] + raises(ValueError, "c.c[1] = '192.168.2.128'") diff --git a/test/test_state.py b/test/test_state.py index 8587b4a..4430bd9 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -72,7 +72,8 @@ def _diff_opt(opt1, opt2): if isinstance(val1, list): for index, consistency in enumerate(val1): assert consistency[0] == val2[index][0] - assert consistency[1]._name == val2[index][1]._name + for idx, opt in enumerate(consistency[1]): + assert opt._name == val2[index][1][idx]._name elif attr == '_callback': assert val1[0] == val2[0] if val1[1] is not None: diff --git a/tiramisu/option.py b/tiramisu/option.py index f9a3541..d3c4bf9 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -27,7 +27,7 @@ from types import FunctionType from IPy import IP import warnings -from tiramisu.error import ConflictError, ValueWarning +from tiramisu.error import ConfigError, ConflictError, ValueWarning from tiramisu.setting import groups, multitypes from tiramisu.i18n import _ from tiramisu.autolib import carry_out_calculation @@ -172,14 +172,13 @@ class BaseOption(object): if isinstance(consistencies, list): new_value = [] for consistency in consistencies: - if load: - new_value.append((consistency[0], - descr.impl_get_opt_by_path( - consistency[1]))) - else: - new_value.append((consistency[0], - descr.impl_get_path_by_opt( - consistency[1]))) + values = [] + for obj in consistency[1]: + if load: + values.append(descr.impl_get_opt_by_path(obj)) + else: + values.append(descr.impl_get_path_by_opt(obj)) + new_value.append((consistency[0], tuple(values))) else: new_value = {} @@ -395,50 +394,62 @@ class Option(BaseOption): self.impl_validate(default) self._default = default - def _launch_consistency(self, func, opt, vals, context, index, opt_): + def _launch_consistency(self, func, right_opt, right_val, context, index, + left_opts): if context is not None: descr = context.cfgimpl_get_description() - if opt is self: - #values are for self, search opt_ values - values = vals - if context is not None: - path = descr.impl_get_path_by_opt(opt_) - values_ = context._getattr(path, validate=False) + #right_opt is also in left_opts + if right_opt not in left_opts: + raise ConfigError(_('right_opt not in left_opts')) + + left_vals = [] + for opt in left_opts: + if right_opt == opt: + value = right_val + else: + if context is not None: + path = descr.impl_get_path_by_opt(opt) + value = context._getattr(path, validate=False) + else: + value = opt.impl_getdefault() + if index is None: + #could be multi or not + left_vals.append(value) else: - values_ = opt_.impl_getdefault() - if index is not None: #value is not already set, could be higher try: - values_ = values_[index] + if right_opt == opt: + val = value + else: + val = value[index] + if val is None: + #no value so no consistencies + return + left_vals.append(val) except IndexError: - values_ = None - else: - #values are for opt_, search self values - values_ = vals - if context is not None: - path = descr.impl_get_path_by_opt(self) - values = context._getattr(path, validate=False) + #so return if no value + return + + if self.impl_is_multi(): + if index is None: + for idx, right_v in enumerate(right_val): + try: + left_v = [] + for left_val in left_vals: + left_v.append(left_val[idx]) + if None in left_v: + continue + except IndexError: + continue + getattr(self, func)(left_opts, left_v) else: - values = self.impl_getdefault() - if index is not None: - #value is not already set, could be higher - try: - values = values[index] - except IndexError: - values = None - if index is None and self.impl_is_multi(): - for index in range(0, len(values)): - try: - value = values[index] - value_ = values_[index] - except IndexError: - value = None - value_ = None - if None not in (value, value_): - getattr(self, func)(opt_._name, value, value_) + if None in left_vals: + return + getattr(self, func)(left_opts, left_vals) else: - if None not in (values, values_): - getattr(self, func)(opt_._name, values, values_) + if None in left_vals: + return + getattr(self, func)(left_opts, left_vals) def impl_validate(self, value, context=None, validate=True, force_no_multi=False): @@ -550,29 +561,31 @@ class Option(BaseOption): def impl_is_multi(self): return self._multi - def impl_add_consistency(self, func, opt): + def impl_add_consistency(self, func, *left_opts): if self._consistencies is None: self._consistencies = [] - if not isinstance(opt, Option): - raise ValueError('consistency must be set with an option') - if self is opt: - raise ValueError('cannot add consistency with itself') - if self.impl_is_multi() != opt.impl_is_multi(): - raise ValueError('options in consistency' - ' should be multi in two sides') + for opt in left_opts: + if not isinstance(opt, Option): + raise ValueError(_('consistency should be set with an option')) + if self is opt: + raise ValueError(_('cannot add consistency with itself')) + if self.impl_is_multi() != opt.impl_is_multi(): + raise ValueError(_('options in consistency should be multi in ' + 'two sides')) func = '_cons_{0}'.format(func) - self._launch_consistency(func, - self, - self.impl_getdefault(), - None, None, opt) - self._consistencies.append((func, opt)) + opts = tuple([self] + list(left_opts)) + self._launch_consistency(func, self, self.impl_getdefault(), None, + None, opts) + self._consistencies.append((func, opts)) self.impl_validate(self.impl_getdefault()) - def _cons_not_equal(self, optname, value, value_): - if value == value_: + def _cons_not_equal(self, opts, vals): + if len(opts) != 2: + raise ConfigError(_('invalid len for opts')) + if vals[0] == vals[1]: raise ValueError(_("invalid value {0} for option {1} " "must be different as {2} option" - "").format(value, self._name, optname)) + "").format(vals[0], self._name, opts[1]._name)) def _impl_convert_callbacks(self, descr, load=False): if not load and self._callback is None: @@ -889,15 +902,17 @@ class NetmaskOption(Option): except ValueError: raise ValueError(_('invalid netmask address {0}').format(self._name)) - def _cons_network_netmask(self, optname, value, value_): + def _cons_network_netmask(self, opts, vals): #opts must be (netmask, network) options - self.__cons_netmask(optname, value, value_, False) + self.__cons_netmask(opts, vals[0], vals[1], False) - def _cons_ip_netmask(self, optname, value, value_): + def _cons_ip_netmask(self, opts, vals): #opts must be (netmask, ip) options - self.__cons_netmask(optname, value, value_, True) + self.__cons_netmask(opts, vals[0], vals[1], True) - def __cons_netmask(self, optname, val_netmask, val_ipnetwork, make_net): + def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net): + if len(opts) != 2: + raise ConfigError(_('invalid len for opts')) msg = None try: ip = IP('{0}/{1}'.format(val_ipnetwork, val_netmask), @@ -923,17 +938,30 @@ class NetmaskOption(Option): else: msg = _("invalid network {0} ({1}) with netmask {2} ({3})") if msg is not None: - raise ValueError(msg.format(val_ipnetwork, optname, + raise ValueError(msg.format(val_ipnetwork, opts[1]._name, val_netmask, self._name)) class BroadcastOption(Option): + __slots__ = tuple() + _opt_type = 'broadcast' + def _validate(self, value): try: IP('{0}/32'.format(value)) except ValueError: raise ValueError(_('invalid broadcast address {0}').format(self._name)) + def _cons_broadcast(self, opts, vals): + if len(vals) != 3: + raise ConfigError(_('invalid len for vals')) + broadcast, network, netmask = vals + if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast): + raise ValueError(_('invalid broadcast {0} ({1}) with network {2} ' + '({3}) and netmask {4} ({5})').format( + broadcast, opts[0]._name, network, + opts[1]._name, netmask, opts[2]._name)) + class DomainnameOption(Option): "represents the choice of a domain name" @@ -1098,12 +1126,11 @@ class OptionDescription(BaseOption): if not force_no_consistencies and \ option._consistencies is not None: for consistency in option._consistencies: - func, opt = consistency - opts = (option, opt) - _consistencies.setdefault(opt, - []).append((func, opts)) - _consistencies.setdefault(option, - []).append((func, opts)) + func, left_opts = consistency + for opt in left_opts: + _consistencies.setdefault(opt, + []).append((func, + left_opts)) else: _currpath.append(attr) option.impl_build_cache(cache_path, @@ -1186,17 +1213,15 @@ class OptionDescription(BaseOption): def impl_get_group_type(self): return self._group_type - def _valid_consistency(self, opt, value, context=None, index=None): - consistencies = self._consistencies.get(opt) + def _valid_consistency(self, right_opt, right_val, context=None, index=None): + #[('_cons_not_equal', (opt1, opt2))] + consistencies = self._consistencies.get(right_opt) if consistencies is not None: - for consistency in consistencies: - opt_ = consistency[1] - ret = opt_[0]._launch_consistency(consistency[0], - opt, - value, - context, - index, - opt_[1]) + for func, opts in consistencies: + #opts[0] is the option where func is set + #opts is left_opts + ret = opts[0]._launch_consistency(func, right_opt, right_val, + context, index, opts) if ret is False: return False return True From 70f684e70c5e4964a9e1acb810622b482ba92551 Mon Sep 17 00:00:00 2001 From: Emmanuel Garette Date: Sat, 28 Sep 2013 17:05:01 +0200 Subject: [PATCH 18/18] tiramisu/option.py: separate _consistencies (for Option) and _cache_consistencies (for OptionDescription) _launch_consistency need index for multi's option _cons_not_equal support multi options tiramisu/value.py: Multi._validate support consistency --- test/test_option_consistency.py | 98 +++++++++- test/test_state.py | 2 +- tiramisu/option.py | 337 ++++++++++++++++---------------- tiramisu/value.py | 14 +- 4 files changed, 277 insertions(+), 174 deletions(-) diff --git a/test/test_option_consistency.py b/test/test_option_consistency.py index 1c23dac..d5226db 100644 --- a/test/test_option_consistency.py +++ b/test/test_option_consistency.py @@ -5,6 +5,7 @@ from tiramisu.setting import owners, groups from tiramisu.config import Config from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ BroadcastOption, SymLinkOption, OptionDescription +from tiramisu.error import ConfigError def test_consistency_not_equal(): @@ -22,6 +23,60 @@ def test_consistency_not_equal(): c.b = 2 +def test_consistency_not_equal_many_opts(): + a = IntOption('a', '') + b = IntOption('b', '') + c = IntOption('c', '') + d = IntOption('d', '') + e = IntOption('e', '') + f = IntOption('f', '') + od = OptionDescription('od', '', [a, b, c, d, e, f]) + a.impl_add_consistency('not_equal', b, c, d, e, f) + c = Config(od) + assert c.a is None + assert c.b is None + # + c.a = 1 + del(c.a) + # + c.a = 1 + raises(ValueError, "c.b = 1") + # + c.b = 2 + raises(ValueError, "c.f = 2") + raises(ValueError, "c.f = 1") + # + c.d = 3 + raises(ValueError, "c.f = 3") + raises(ValueError, "c.a = 3") + raises(ValueError, "c.c = 3") + raises(ValueError, "c.e = 3") + + +def test_consistency_not_in_config(): + a = IntOption('a', '') + b = IntOption('b', '') + a.impl_add_consistency('not_equal', b) + od1 = OptionDescription('od1', '', [a]) + od2 = OptionDescription('od2', '', [b]) + od = OptionDescription('root', '', [od1]) + raises(ConfigError, "Config(od)") + od = OptionDescription('root', '', [od1, od2]) + Config(od) + #with subconfig + raises(ConfigError, "Config(od.od1)") + + +def test_consistency_afer_config(): + a = IntOption('a', '') + b = IntOption('b', '') + od1 = OptionDescription('od1', '', [a]) + od2 = OptionDescription('od2', '', [b]) + od = OptionDescription('root', '', [od1, od2]) + Config(od) + raises(AttributeError, "a.impl_add_consistency('not_equal', b)") + + def test_consistency_not_equal_symlink(): a = IntOption('a', '') b = IntOption('b', '') @@ -29,7 +84,7 @@ def test_consistency_not_equal_symlink(): od = OptionDescription('od', '', [a, b, c]) a.impl_add_consistency('not_equal', b) c = Config(od) - assert set(od._consistencies.keys()) == set([a, b]) + assert set(od._cache_consistencies.keys()) == set([a, b]) def test_consistency_not_equal_multi(): @@ -53,6 +108,14 @@ def test_consistency_default(): raises(ValueError, "a.impl_add_consistency('not_equal', b)") +def test_consistency_default_multi(): + a = IntOption('a', '', [2, 1], multi=True) + b = IntOption('b', '', [1, 1], multi=True) + c = IntOption('c', '', [1, 2], multi=True) + raises(ValueError, "a.impl_add_consistency('not_equal', b)") + a.impl_add_consistency('not_equal', c) + + def test_consistency_default_diff(): a = IntOption('a', '', 3) b = IntOption('b', '', 1) @@ -99,7 +162,7 @@ def test_consistency_ip_netmask_error_multi(): a = IPOption('a', '', multi=True) b = NetmaskOption('b', '') od = OptionDescription('od', '', [a, b]) - raises(ValueError, "b.impl_add_consistency('ip_netmask', a)") + raises(ConfigError, "b.impl_add_consistency('ip_netmask', a)") def test_consistency_ip_netmask_multi(): @@ -170,11 +233,42 @@ def test_consistency_broadcast(): b.impl_add_consistency('network_netmask', a) c.impl_add_consistency('broadcast', a, b) c = Config(od) + #first, test network_netmask + c.a = ['192.168.1.128'] + raises(ValueError, "c.b = ['255.255.255.0']") + # c.a = ['192.168.1.0'] c.b = ['255.255.255.0'] c.c = ['192.168.1.255'] raises(ValueError, "c.a = ['192.168.1.1']") + # c.a = ['192.168.1.0', '192.168.2.128'] c.b = ['255.255.255.0', '255.255.255.128'] c.c = ['192.168.1.255', '192.168.2.255'] raises(ValueError, "c.c[1] = '192.168.2.128'") + c.c[1] = '192.168.2.255' + + +def test_consistency_broadcast_default(): + a = NetworkOption('a', '', '192.168.1.0') + b = NetmaskOption('b', '', '255.255.255.128') + c = BroadcastOption('c', '', '192.168.2.127') + d = BroadcastOption('d', '', '192.168.1.127') + od = OptionDescription('a', '', [a, b, c]) + raises(ValueError, "c.impl_add_consistency('broadcast', a, b)") + od2 = OptionDescription('a', '', [a, b, d]) + d.impl_add_consistency('broadcast', a, b) + + +def test_consistency_not_all(): + #_cache_consistencies is not None by not options has consistencies + a = NetworkOption('a', '', multi=True) + b = NetmaskOption('b', '', multi=True) + c = BroadcastOption('c', '', multi=True) + od = OptionDescription('a', '', [a, b, c]) + od.impl_set_group_type(groups.master) + b.impl_add_consistency('network_netmask', a) + c = Config(od) + c.a = ['192.168.1.0'] + c.b = ['255.255.255.0'] + c.c = ['192.168.1.255'] diff --git a/test/test_state.py b/test/test_state.py index 4430bd9..ef46ce2 100644 --- a/test/test_state.py +++ b/test/test_state.py @@ -40,7 +40,7 @@ def _diff_opt(opt1, opt2): if diff2 != set(): raise Exception('more attribute in opt2 {0}'.format(list(diff2))) for attr in attr1: - if attr in ['_cache_paths']: + if attr in ['_cache_paths', '_cache_consistencies']: continue err1 = False err2 = False diff --git a/tiramisu/option.py b/tiramisu/option.py index d3c4bf9..c7a28c2 100644 --- a/tiramisu/option.py +++ b/tiramisu/option.py @@ -61,9 +61,8 @@ class BaseOption(object): __setattr__ method """ __slots__ = ('_name', '_requires', '_properties', '_readonly', - '_consistencies', '_calc_properties', '_impl_informations', - '_state_consistencies', '_state_readonly', '_state_requires', - '_stated') + '_calc_properties', '_impl_informations', + '_state_readonly', '_state_requires', '_stated') def __init__(self, name, doc, requires, properties): if not valid_name(name): @@ -73,7 +72,6 @@ class BaseOption(object): self.impl_set_information('doc', doc) self._calc_properties, self._requires = validate_requires_arg( requires, self._name) - self._consistencies = None if properties is None: properties = tuple() if not isinstance(properties, tuple): @@ -98,8 +96,7 @@ class BaseOption(object): "frozen" (which has noting to do with the high level "freeze" propertie or "read_only" property) """ - if not name.startswith('_state') and name not in ('_cache_paths', - '_consistencies'): + if not name.startswith('_state') and not name.startswith('_cache'): is_readonly = False # never change _name if name == '_name': @@ -109,15 +106,12 @@ class BaseOption(object): is_readonly = True except: pass - try: - if self._readonly is True: - if value is True: - # already readonly and try to re set readonly - # don't raise, just exit - return - is_readonly = True - except AttributeError: - pass + elif name != '_readonly': + try: + if self._readonly is True: + is_readonly = True + except AttributeError: + self._readonly = False if is_readonly: raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is" " read-only").format( @@ -149,57 +143,6 @@ class BaseOption(object): raise ValueError(_("information's item not found: {0}").format( key)) - # serialize/unserialize - def _impl_convert_consistencies(self, descr, load=False): - """during serialization process, many things have to be done. - one of them is the localisation of the options. - The paths are set once for all. - - :type descr: :class:`tiramisu.option.OptionDescription` - :param load: `True` if we are at the init of the option description - :type load: bool - """ - if not load and self._consistencies is None: - self._state_consistencies = None - elif load and self._state_consistencies is None: - self._consistencies = None - del(self._state_consistencies) - else: - if load: - consistencies = self._state_consistencies - else: - consistencies = self._consistencies - if isinstance(consistencies, list): - new_value = [] - for consistency in consistencies: - values = [] - for obj in consistency[1]: - if load: - values.append(descr.impl_get_opt_by_path(obj)) - else: - values.append(descr.impl_get_path_by_opt(obj)) - new_value.append((consistency[0], tuple(values))) - - else: - new_value = {} - for key, _consistencies in consistencies.items(): - new_value[key] = [] - for key_cons, _cons in _consistencies: - _list_cons = [] - for _con in _cons: - if load: - _list_cons.append( - descr.impl_get_opt_by_path(_con)) - else: - _list_cons.append( - descr.impl_get_path_by_opt(_con)) - new_value[key].append((key_cons, tuple(_list_cons))) - if load: - del(self._state_consistencies) - self._consistencies = new_value - else: - self._state_consistencies = new_value - def _impl_convert_requires(self, descr, load=False): """export of the requires during the serialization process @@ -245,10 +188,7 @@ class BaseOption(object): for func in dir(self): if func.startswith('_impl_convert_'): getattr(self, func)(descr) - try: - self._state_readonly = self._readonly - except AttributeError: - pass + self._state_readonly = self._readonly def __getstate__(self, stated=True): """special method to enable the serialization with pickle @@ -268,7 +208,8 @@ class BaseOption(object): for subclass in self.__class__.__mro__: if subclass is not object: slots.update(subclass.__slots__) - slots -= frozenset(['_cache_paths', '__weakref__']) + slots -= frozenset(['_cache_paths', '_cache_consistencies', + '__weakref__']) states = {} for slot in slots: # remove variable if save variable converted @@ -327,7 +268,8 @@ class Option(BaseOption): """ __slots__ = ('_multi', '_validator', '_default_multi', '_default', '_state_callback', '_callback', '_multitype', - '_warnings_only', '_master_slaves', '__weakref__') + '_consistencies', '_warnings_only', '_master_slaves', + '_state_consistencies', '__weakref__') _empty = '' def __init__(self, name, doc, default=None, default_multi=None, @@ -393,66 +335,58 @@ class Option(BaseOption): self._warnings_only = warnings_only self.impl_validate(default) self._default = default + self._consistencies = None - def _launch_consistency(self, func, right_opt, right_val, context, index, - left_opts): + def _launch_consistency(self, func, option, value, context, index, + all_cons_opts): + """Launch consistency now + + :param func: function name, this name should start with _cons_ + :type func: `str` + :param option: option that value is changing + :type option: `tiramisu.option.Option` + :param value: new value of this option + :param context: Config's context, if None, check default value instead + :type context: `tiramisu.config.Config` + :param index: only for multi option, consistency should be launch for + specified index + :type index: `int` + :param all_cons_opts: all options concerne by this consistency + :type all_cons_opts: `list` of `tiramisu.option.Option` + """ if context is not None: descr = context.cfgimpl_get_description() - #right_opt is also in left_opts - if right_opt not in left_opts: - raise ConfigError(_('right_opt not in left_opts')) + #option is also in all_cons_opts + if option not in all_cons_opts: + raise ConfigError(_('option not in all_cons_opts')) - left_vals = [] - for opt in left_opts: - if right_opt == opt: - value = right_val + all_cons_vals = [] + for opt in all_cons_opts: + #get value + if option == opt: + opt_value = value else: + #if context, calculate value, otherwise get default value if context is not None: - path = descr.impl_get_path_by_opt(opt) - value = context._getattr(path, validate=False) + opt_value = context._getattr( + descr.impl_get_path_by_opt(opt), validate=False) else: - value = opt.impl_getdefault() - if index is None: - #could be multi or not - left_vals.append(value) + opt_value = opt.impl_getdefault() + + #append value + if not self.impl_is_multi() or option == opt: + all_cons_vals.append(opt_value) else: - #value is not already set, could be higher + #value is not already set, could be higher index try: - if right_opt == opt: - val = value - else: - val = value[index] - if val is None: - #no value so no consistencies - return - left_vals.append(val) + all_cons_vals.append(opt_value[index]) except IndexError: #so return if no value return - - if self.impl_is_multi(): - if index is None: - for idx, right_v in enumerate(right_val): - try: - left_v = [] - for left_val in left_vals: - left_v.append(left_val[idx]) - if None in left_v: - continue - except IndexError: - continue - getattr(self, func)(left_opts, left_v) - else: - if None in left_vals: - return - getattr(self, func)(left_opts, left_vals) - else: - if None in left_vals: - return - getattr(self, func)(left_opts, left_vals) + getattr(self, func)(all_cons_opts, all_cons_vals) def impl_validate(self, value, context=None, validate=True, - force_no_multi=False): + force_index=None): """ :param value: the option's value :param context: Config's context @@ -509,12 +443,11 @@ class Option(BaseOption): if context is not None: descr = context.cfgimpl_get_description() - if not self._multi or force_no_multi: - do_validation(value) + if not self._multi or force_index is not None: + do_validation(value, force_index) else: if not isinstance(value, list): - raise ValueError(_("invalid value {0} for option {1} " - "which must be a list").format(value, + raise ValueError(_("which must be a list").format(value, self._name)) for index, val in enumerate(value): do_validation(val, index) @@ -561,31 +494,45 @@ class Option(BaseOption): def impl_is_multi(self): return self._multi - def impl_add_consistency(self, func, *left_opts): + def impl_add_consistency(self, func, *other_opts): + """Add consistency means that value will be validate with other_opts + option's values. + + :param func: function's name + :type func: `str` + :param other_opts: options used to validate value + :type other_opts: `list` of `tiramisu.option.Option` + """ if self._consistencies is None: self._consistencies = [] - for opt in left_opts: + for opt in other_opts: if not isinstance(opt, Option): - raise ValueError(_('consistency should be set with an option')) + raise ConfigError(_('consistency should be set with an option')) if self is opt: - raise ValueError(_('cannot add consistency with itself')) + raise ConfigError(_('cannot add consistency with itself')) if self.impl_is_multi() != opt.impl_is_multi(): - raise ValueError(_('options in consistency should be multi in ' - 'two sides')) + raise ConfigError(_('every options in consistency should be ' + 'multi or none')) func = '_cons_{0}'.format(func) - opts = tuple([self] + list(left_opts)) - self._launch_consistency(func, self, self.impl_getdefault(), None, - None, opts) - self._consistencies.append((func, opts)) + all_cons_opts = tuple([self] + list(other_opts)) + value = self.impl_getdefault() + if value is not None: + if self.impl_is_multi(): + for idx, val in enumerate(value): + self._launch_consistency(func, self, val, None, + idx, all_cons_opts) + else: + self._launch_consistency(func, self, value, None, + None, all_cons_opts) + self._consistencies.append((func, all_cons_opts)) self.impl_validate(self.impl_getdefault()) def _cons_not_equal(self, opts, vals): - if len(opts) != 2: - raise ConfigError(_('invalid len for opts')) - if vals[0] == vals[1]: - raise ValueError(_("invalid value {0} for option {1} " - "must be different as {2} option" - "").format(vals[0], self._name, opts[1]._name)) + for idx_inf, val_inf in enumerate(vals): + for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]): + if val_inf == val_sup is not None: + raise ValueError(_("same value for {0} and {1}").format( + opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name)) def _impl_convert_callbacks(self, descr, load=False): if not load and self._callback is None: @@ -621,6 +568,57 @@ class Option(BaseOption): else: self._state_callback = (callback, cllbck_prms) + # serialize/unserialize + def _impl_convert_consistencies(self, descr, load=False): + """during serialization process, many things have to be done. + one of them is the localisation of the options. + The paths are set once for all. + + :type descr: :class:`tiramisu.option.OptionDescription` + :param load: `True` if we are at the init of the option description + :type load: bool + """ + if not load and self._consistencies is None: + self._state_consistencies = None + elif load and self._state_consistencies is None: + self._consistencies = None + del(self._state_consistencies) + else: + if load: + consistencies = self._state_consistencies + else: + consistencies = self._consistencies + if isinstance(consistencies, list): + new_value = [] + for consistency in consistencies: + values = [] + for obj in consistency[1]: + if load: + values.append(descr.impl_get_opt_by_path(obj)) + else: + values.append(descr.impl_get_path_by_opt(obj)) + new_value.append((consistency[0], tuple(values))) + + else: + new_value = {} + for key, _consistencies in consistencies.items(): + new_value[key] = [] + for key_cons, _cons in _consistencies: + _list_cons = [] + for _con in _cons: + if load: + _list_cons.append( + descr.impl_get_opt_by_path(_con)) + else: + _list_cons.append( + descr.impl_get_path_by_opt(_con)) + new_value[key].append((key_cons, tuple(_list_cons))) + if load: + del(self._state_consistencies) + self._consistencies = new_value + else: + self._state_consistencies = new_value + def _second_level_validation(self, value): pass @@ -734,7 +732,7 @@ class SymLinkOption(BaseOption): __slots__ = ('_name', '_opt', '_state_opt') _opt_type = 'symlink' #not return _opt consistencies - _consistencies = {} + _consistencies = None def __init__(self, name, opt): self._name = name @@ -760,12 +758,6 @@ class SymLinkOption(BaseOption): del(self._state_opt) super(SymLinkOption, self)._impl_setstate(descr) - def _impl_convert_consistencies(self, descr, load=False): - if load: - del(self._state_consistencies) - else: - self._state_consistencies = None - class IPOption(Option): "represents the choice of an ip" @@ -904,10 +896,14 @@ class NetmaskOption(Option): def _cons_network_netmask(self, opts, vals): #opts must be (netmask, network) options + if None in vals: + return self.__cons_netmask(opts, vals[0], vals[1], False) def _cons_ip_netmask(self, opts, vals): #opts must be (netmask, ip) options + if None in vals: + return self.__cons_netmask(opts, vals[0], vals[1], True) def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net): @@ -955,6 +951,8 @@ class BroadcastOption(Option): def _cons_broadcast(self, opts, vals): if len(vals) != 3: raise ConfigError(_('invalid len for vals')) + if None in vals: + return broadcast, network, netmask = vals if IP('{0}/{1}'.format(network, netmask)).broadcast() != IP(broadcast): raise ValueError(_('invalid broadcast {0} ({1}) with network {2} ' @@ -964,7 +962,12 @@ class BroadcastOption(Option): class DomainnameOption(Option): - "represents the choice of a domain name" + """represents the choice of a domain name + netbios: for MS domain + hostname: to identify the device + domainname: + fqdn: with tld, not supported yet + """ __slots__ = ('_type', '_allow_ip') _opt_type = 'domainname' @@ -973,10 +976,6 @@ class DomainnameOption(Option): callback_params=None, validator=None, validator_params=None, properties=None, allow_ip=False, type_='domainname', warnings_only=False): - #netbios: for MS domain - #hostname: to identify the device - #domainname: - #fqdn: with tld, not supported yet if type_ not in ['netbios', 'hostname', 'domainname']: raise ValueError(_('unknown type_ {0} for hostname').format(type_)) self._type = type_ @@ -1030,9 +1029,9 @@ class OptionDescription(BaseOption): """ __slots__ = ('_name', '_requires', '_cache_paths', '_group_type', '_state_group_type', '_properties', '_children', - '_consistencies', '_calc_properties', '__weakref__', + '_cache_consistencies', '_calc_properties', '__weakref__', '_readonly', '_impl_informations', '_state_requires', - '_state_consistencies', '_stated', '_state_readonly') + '_stated', '_state_readonly') _opt_type = 'optiondescription' def __init__(self, name, doc, children, requires=None, properties=None): @@ -1053,6 +1052,7 @@ class OptionDescription(BaseOption): old = child self._children = (tuple(child_names), tuple(children)) self._cache_paths = None + self._cache_consistencies = None # the group_type is useful for filtering OptionDescriptions in a config self._group_type = groups.default @@ -1126,11 +1126,11 @@ class OptionDescription(BaseOption): if not force_no_consistencies and \ option._consistencies is not None: for consistency in option._consistencies: - func, left_opts = consistency - for opt in left_opts: + func, all_cons_opts = consistency + for opt in all_cons_opts: _consistencies.setdefault(opt, []).append((func, - left_opts)) + all_cons_opts)) else: _currpath.append(attr) option.impl_build_cache(cache_path, @@ -1142,7 +1142,12 @@ class OptionDescription(BaseOption): if save: self._cache_paths = (tuple(cache_option), tuple(cache_path)) if not force_no_consistencies: - self._consistencies = _consistencies + if _consistencies != {}: + self._cache_consistencies = {} + for opt, cons in _consistencies.items(): + if opt not in cache_option: + raise ConfigError(_('consistency with option {0} which is not in Config').format(opt._name)) + self._cache_consistencies[opt] = tuple(cons) self._readonly = True def impl_get_opt_by_path(self, path): @@ -1213,15 +1218,18 @@ class OptionDescription(BaseOption): def impl_get_group_type(self): return self._group_type - def _valid_consistency(self, right_opt, right_val, context=None, index=None): - #[('_cons_not_equal', (opt1, opt2))] - consistencies = self._consistencies.get(right_opt) + def _valid_consistency(self, option, value, context, index): + if self._cache_consistencies is None: + return True + #consistencies is something like [('_cons_not_equal', (opt1, opt2))] + consistencies = self._cache_consistencies.get(option) if consistencies is not None: - for func, opts in consistencies: - #opts[0] is the option where func is set - #opts is left_opts - ret = opts[0]._launch_consistency(func, right_opt, right_val, - context, index, opts) + for func, all_cons_opts in consistencies: + #all_cons_opts[0] is the option where func is set + ret = all_cons_opts[0]._launch_consistency(func, option, + value, + context, index, + all_cons_opts) if ret is False: return False return True @@ -1261,6 +1269,7 @@ class OptionDescription(BaseOption): """ if descr is None: self._cache_paths = None + self._cache_consistencies = None self.impl_build_cache(force_no_consistencies=True) descr = self self._group_type = getattr(groups, self._state_group_type) diff --git a/tiramisu/value.py b/tiramisu/value.py index 6942453..4426742 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -455,10 +455,10 @@ class Multi(list): value_slave.append(slave.impl_getdefault_multi(), force=True) - def __setitem__(self, key, value): - self._validate(value) + def __setitem__(self, index, value): + self._validate(value, index) #assume not checking mandatory property - super(Multi, self).__setitem__(key, value) + super(Multi, self).__setitem__(index, value) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) def append(self, value, force=False): @@ -476,7 +476,8 @@ class Multi(list): #Force None il return a list if isinstance(value, list): value = None - self._validate(value) + index = self.__len__() + self._validate(value, index) super(Multi, self).append(value) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, @@ -486,7 +487,6 @@ class Multi(list): path = values._get_opt_path(slave) if not values._is_default_owner(path): if slave.impl_has_callback(): - index = self.__len__() - 1 dvalue = values._getcallback_value(slave, index=index) else: dvalue = slave.impl_getdefault_multi() @@ -538,11 +538,11 @@ class Multi(list): super(Multi, self).extend(iterable) self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) - def _validate(self, value): + def _validate(self, value, force_index): if value is not None: try: self.opt.impl_validate(value, context=self.context(), - force_no_multi=True) + force_index=force_index) except ValueError as err: raise ValueError(_("invalid value {0} " "for option {1}: {2}"