Compare commits

...

14 commits

29 changed files with 650 additions and 351 deletions

View file

@ -2,7 +2,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Tiramisu\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-05 08:49+0100\n"
"POT-Creation-Date: 2025-02-07 07:55+0100\n"
"PO-Revision-Date: \n"
"Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n"
"Language-Team: Tiramisu's team <egarette@cadoles.com>\n"
@ -34,23 +34,23 @@ msgstr "Ne peut avoir de warnings durant une validation"
msgid "Commands:"
msgstr "Commandes :"
#: tiramisu/api.py:111 tiramisu/api.py:1840
#: tiramisu/api.py:111 tiramisu/api.py:1843
msgid "please specify a valid sub function ({0}.{1})"
msgstr "veuillez spécifier une sous fonction valide ({0}.{1})"
#: tiramisu/api.py:194
#: tiramisu/api.py:196
msgid "please do not specify index ({0}.{1})"
msgstr "veuillez ne pas spécifier d'index ({0}.{1})"
#: tiramisu/api.py:199 tiramisu/api.py:844
#: tiramisu/api.py:201 tiramisu/api.py:847
msgid "please specify index with a follower option ({0}.{1})"
msgstr "veuillez spécifier un index avec une option suiveuse ({0}.{1})"
#: tiramisu/api.py:220
#: tiramisu/api.py:222
msgid "please specify a valid sub function ({0}.{1}): {2}"
msgstr "veuillez spécifier une sous fonction valide ({0}.{1}): {2}"
#: tiramisu/api.py:431
#: tiramisu/api.py:434
msgid ""
"the option {0} is not a dynamic option, cannot get identifiers with "
"only_self parameter to True"
@ -58,15 +58,15 @@ msgstr ""
"l'option {0} n'est pas une option dynamique, ne peut retrouver l'identifiant "
"avec le paramètre only_self à True"
#: tiramisu/api.py:517
#: tiramisu/api.py:520
msgid "cannot get option from a follower symlink without index"
msgstr "ne peut avoir d'option pour une symlink suiveuse sans index"
#: tiramisu/api.py:592
#: tiramisu/api.py:595
msgid "cannot add this property: \"{0}\""
msgstr "ne peut pas ajouter cette propriété : \"{0}\""
#: tiramisu/api.py:619
#: tiramisu/api.py:622
msgid ""
"cannot remove option's property \"{0}\", use permissive instead in option "
"\"{1}\""
@ -74,11 +74,11 @@ msgstr ""
"ne supprimer la propriété \"{0}\" de l'option, utiliser plutôt permissive "
"dans l'option \"{1}\""
#: tiramisu/api.py:623
#: tiramisu/api.py:626
msgid "cannot find \"{0}\" in option \"{1}\""
msgstr "ne peut trouver \"{0}\" dans l'option \"{1}\""
#: tiramisu/api.py:628
#: tiramisu/api.py:631
msgid ""
"cannot remove option's property \"{0}\", use permissive instead in option "
"\"{1}\" at index \"{2}\""
@ -86,39 +86,39 @@ msgstr ""
"ne peut supprimer la propriété \"{0}\" de l'option, utilisez plutôt "
"permissive dans l'option \"{1}\" a l'index \"{2}\""
#: tiramisu/api.py:632
#: tiramisu/api.py:635
msgid "cannot find \"{0}\" in option \"{1}\" at index \"{2}\""
msgstr "ne peut trouver \"{0}\" dans l'option \"{1}\" à l'index \"{2}\""
#: tiramisu/api.py:676
#: tiramisu/api.py:679
msgid "cannot find \"{0}\""
msgstr "ne peut trouver \"{0}\""
#: tiramisu/api.py:808
#: tiramisu/api.py:811
msgid "cannot reduce length of the leader {}"
msgstr "il est impossible de réduire la longueur du leader \"{}\""
#: tiramisu/api.py:861
#: tiramisu/api.py:864
msgid "only multi value has defaultmulti"
msgstr "seule les valeurs multiple a l'attribut defaultmulti"
#: tiramisu/api.py:1020
#: tiramisu/api.py:1023
msgid "please specify a valid sub function ({0}.{1}) for {2}"
msgstr "veuillez spécifier une sous fonction valide ({0}.{1}) pour {2}"
#: tiramisu/api.py:1407
#: tiramisu/api.py:1410
msgid "properties must be a frozenset"
msgstr "une propriété doit être de type frozenset"
#: tiramisu/api.py:1411 tiramisu/api.py:1438
#: tiramisu/api.py:1414 tiramisu/api.py:1441
msgid "unknown when {} (must be in append or remove)"
msgstr "value {} inconsistent (doit être append ou remove)"
#: tiramisu/api.py:1424 tiramisu/api.py:1448 tiramisu/config.py:1680
#: tiramisu/api.py:1427 tiramisu/api.py:1451 tiramisu/config.py:1681
msgid "unknown type {}"
msgstr "type inconnu {}"
#: tiramisu/api.py:1812
#: tiramisu/api.py:1815
msgid "do not use unrestraint, nowarnings or forcepermissive together"
msgstr ""
"il ne faut pas utiliser unrestraint, nowarnings ou forcepermissive ensemble"
@ -147,6 +147,26 @@ msgstr "param doit avoir un booléan pas un {} pour notraisepropertyerror"
msgid "param must have a boolean not a {} for raisepropertyerror"
msgstr "param doit avoir un booléan pas un {} pour raisepropertyerror"
#: tiramisu/autolib.py:151
msgid "identifiers in ParamDynOption must be a list, not {0}"
msgstr ""
"les identifiants d'un ParamDynOption doivent être une liste, donc pas {0}"
#: tiramisu/autolib.py:155
msgid "optional in ParamDynOption must be a boolean, not {0}"
msgstr ""
"le paramètre optional dans ParamDynOption doit être un booléen, pas {0}"
#: tiramisu/autolib.py:206
msgid "cannot add option in information after creating config"
msgstr ""
"ne peut ajouter une option dans une information après la création de la "
"configuration"
#: tiramisu/autolib.py:208
msgid "cannot redefine option in information"
msgstr "on ne peu pas redéfinir une option dans une information"
#: tiramisu/autolib.py:212
msgid "option in ParamInformation cannot be a symlinkoption"
msgstr "l'option dans ParamInformation ne peut pas être un symlinkoption"
@ -185,7 +205,16 @@ msgstr ""
"l'option {0} n'est pas une dynoptiondescription ou n'est pas dans une "
"dynoptiondescription"
#: tiramisu/autolib.py:848
#: tiramisu/autolib.py:687
msgid ""
"cannot calculate arguments for \"{0}\", cannot find dynamic variable \"{1}\" "
"with identifier \"{2}\", list of valid identifiers: {3}"
msgstr ""
"impossible de calculer les arguments de \"{0}\", ne peut trouver la variable "
"dynamique \"{1}\" avec l'identifiant \"{2}\", voici la liste des "
"identifiants valables : {3}"
#: tiramisu/autolib.py:866
msgid ""
"the \"{}\" function with positional arguments \"{}\" and keyword arguments "
"\"{}\" must not return a list (\"{}\") for the follower option {}"
@ -194,7 +223,7 @@ msgstr ""
"nommés \"{}\" ne doit pas retourner une liste (\"{}\") pour l'option "
"suiveuse {}"
#: tiramisu/autolib.py:863
#: tiramisu/autolib.py:881
msgid ""
"the \"{}\" function must not return a list (\"{}\") for the follower option "
"{}"
@ -202,7 +231,7 @@ msgstr ""
"la fonction \"{}\" ne doit pas retourner une liste (\"{}\") pour l'option "
"suiveuse {}"
#: tiramisu/autolib.py:904
#: tiramisu/autolib.py:922
msgid ""
"unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and "
"\"{4}\" for option {2}"
@ -210,64 +239,64 @@ msgstr ""
"erreur inattendue \"{0}\" dans la fonction \"{1}\" avec les arguments "
"\"{3}\" et \"{4}\" pour l'option {2}"
#: tiramisu/autolib.py:915
#: tiramisu/autolib.py:933
msgid "unexpected error \"{0}\" in function \"{1}\" for option {2}"
msgstr "erreur inattendue \"{0}\" dans la fonction \"{1}\" pour l'option {2}"
#: tiramisu/config.py:419
#: tiramisu/config.py:420
msgid ""
"index \"{0}\" is greater than the leadership length \"{1}\" for option {2}"
msgstr ""
"l'index \"{0}\" est supérieur à la longueur de la leadership \"{1}\" pour "
"l'option {2}"
#: tiramisu/config.py:579
#: tiramisu/config.py:580
msgid "there is no option description for this config (may be GroupConfig)"
msgstr ""
"il n'y a pas d'option description trouvé pour cette config (peut être un "
"GroupConfig)"
#: tiramisu/config.py:668
#: tiramisu/config.py:669
msgid "no option found in config with these criteria"
msgstr "aucune option trouvée dans la config avec ces critères"
#: tiramisu/config.py:871
#: tiramisu/config.py:872
msgid ""
"the follower option {0} has greater length ({1}) than the leader length ({2})"
msgstr ""
"l'option suiveuse {0} a une longueur supérieur ({1}) à la longueur de "
"l'option leader ({2})"
#: tiramisu/config.py:982 tiramisu/option/optiondescription.py:74
#: tiramisu/config.py:983 tiramisu/option/optiondescription.py:74
msgid "option description seems to be part of an other config"
msgstr "l'option description semble faire parti d'une autre config"
#: tiramisu/config.py:1144
#: tiramisu/config.py:1145
msgid "parent of {0} not already exists"
msgstr "le parent de {0} n'existe plus"
#: tiramisu/config.py:1191
#: tiramisu/config.py:1192
msgid "cannot set leadership object has root optiondescription"
msgstr "ne peut assigner un objet leadership comme optiondescription racine"
#: tiramisu/config.py:1194
#: tiramisu/config.py:1195
msgid "cannot set dynoptiondescription object has root optiondescription"
msgstr ""
"ne peut assigner un objet dynoptiondescription comme optiondescription racine"
#: tiramisu/config.py:1246
#: tiramisu/config.py:1247
msgid "config name must be uniq in groupconfig for \"{0}\""
msgstr "le nom d'un config doit être unique dans un groupconfig pour \"{0}\""
#: tiramisu/config.py:1457
#: tiramisu/config.py:1458
msgid "unknown config \"{}\""
msgstr "config \"{}\" inconnue"
#: tiramisu/config.py:1482
#: tiramisu/config.py:1483
msgid "child must be a Config, MixConfig or MetaConfig"
msgstr "l'enfant doit être une Config, MixConfig ou MetaConfig"
#: tiramisu/config.py:1517
#: tiramisu/config.py:1518
msgid ""
"force_default, force_default_if_same or force_dont_change_value cannot be "
"set with only_config"
@ -275,43 +304,43 @@ msgstr ""
"force_default, force_default_if_same ou force_dont_change_value ne peuvent "
"pas être spécifié avec only_config"
#: tiramisu/config.py:1527
#: tiramisu/config.py:1528
msgid "force_default and force_dont_change_value cannot be set together"
msgstr ""
"force_default et force_dont_change_value ne peuvent pas être mis ensemble"
#: tiramisu/config.py:1676
#: tiramisu/config.py:1677
msgid "config name must be uniq in groupconfig for {0}"
msgstr "le nom de la config doit être unique dans un groupconfig pour {0}"
#: tiramisu/config.py:1721
#: tiramisu/config.py:1722
msgid "config added has no name, the name is mandatory"
msgstr "la config ajoutée n'a pas de nom, le nom est obligatoire"
#: tiramisu/config.py:1726
#: tiramisu/config.py:1727
msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\""
msgstr ""
"le nom d'un config \"{0}\" n'est pas unique dans le groupconfig \"{1}\""
#: tiramisu/config.py:1744 tiramisu/config.py:1750
#: tiramisu/config.py:1745 tiramisu/config.py:1751
msgid "cannot find the config {0}"
msgstr "ne peut pas trouver la config {0}"
#: tiramisu/config.py:1776
#: tiramisu/config.py:1777
msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr ""
"MetaConfig avec une optiondescription doit avoir un nom comme enfant, pas {}"
#: tiramisu/config.py:1788
#: tiramisu/config.py:1789
msgid "child must be a Config or MetaConfig"
msgstr "enfant doit être une une Config ou une MetaConfig"
#: tiramisu/config.py:1793
#: tiramisu/config.py:1794
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
"toutes les configs d'une metaconfig doivent avoir la même optiondescription"
#: tiramisu/config.py:1810
#: tiramisu/config.py:1811
msgid "metaconfig must have the same optiondescription"
msgstr "metaconfig doivent avoir la même optiondescription"
@ -355,14 +384,23 @@ msgstr "ne peut accéder à l'{0} {1} à cause {2} {3}"
msgid "invalid value"
msgstr "valeur invalide"
#: tiramisu/error.py:201
#: tiramisu/error.py:202
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\""
msgstr "attention, \"{0}\" peut être un {1} invalide pour \"{2}\""
#: tiramisu/error.py:219 tiramisu/error.py:228
#: tiramisu/error.py:204
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\" at index \"{3}\""
msgstr ""
"attention, \"{0}\" peut être un {1} invalide pour \"{2}\" à l'index \"{3}\""
#: tiramisu/error.py:223 tiramisu/error.py:234
msgid "\"{0}\" is an invalid {1} for \"{2}\""
msgstr "\"{0}\" est une valeur invalide pour l'option \"{2}\" de type {1}"
#: tiramisu/error.py:225
msgid "\"{0}\" is an invalid {1} for \"{2}\" at index \"{3}\""
msgstr "\"{0}\" est un {1} invalide pour \"{2}\" à l'index \"{3}\""
#: tiramisu/function.py:65
msgid "network \"{0}\" ({1}) does not match with this netmask"
msgstr "le réseau \"{0}\" (\"{1}\") ne correspond pas avec le masque de réseau"
@ -484,27 +522,27 @@ msgstr "seul \"{0}\" est autorisé"
msgid "only {0} are allowed"
msgstr "seul {0} sont autorisées"
#: tiramisu/option/domainnameoption.py:60
#: tiramisu/option/domainnameoption.py:62
msgid "unknown type {0} for hostname"
msgstr "type_ inconnu {0} pour le nom d'hôte"
#: tiramisu/option/domainnameoption.py:63
#: tiramisu/option/domainnameoption.py:65
msgid "allow_ip must be a boolean"
msgstr "allow_ip doit être un booléen"
#: tiramisu/option/domainnameoption.py:65
#: tiramisu/option/domainnameoption.py:67
msgid "allow_cidr_network must be a boolean"
msgstr "allow_cidr_network doit être un booléen"
#: tiramisu/option/domainnameoption.py:67
#: tiramisu/option/domainnameoption.py:69
msgid "allow_without_dot must be a boolean"
msgstr "allow_without_dot doit être un booléen"
#: tiramisu/option/domainnameoption.py:69
#: tiramisu/option/domainnameoption.py:71
msgid "allow_startswith_dot must be a boolean"
msgstr "allow_startswith_dot doit être un booléen"
#: tiramisu/option/domainnameoption.py:81
#: tiramisu/option/domainnameoption.py:82
msgid ""
"must start with lowercase characters followed by lowercase characters, "
"number, \"-\" and \".\" characters are allowed"
@ -512,7 +550,7 @@ msgstr ""
"doit démarrer par un caractère en minuscule suivi par des caractères en "
"minuscule, des nombres, \"-\" et \".\" sont autorisés"
#: tiramisu/option/domainnameoption.py:84
#: tiramisu/option/domainnameoption.py:85
msgid ""
"must start with lowercase characters followed by lowercase characters, "
"number, \"-\" and \".\" characters are recommanded"
@ -520,32 +558,56 @@ msgstr ""
"doit démarrer par un caractère en minuscule suivi par des caractères en "
"minuscule, des nombres, \"-\" et \".\" sont recommandés"
#: tiramisu/option/domainnameoption.py:88
#: tiramisu/option/domainnameoption.py:89
#: tiramisu/option/domainnameoption.py:90
msgid ""
"must start with lowercase characters followed by lowercase characters, "
"number and \"-\" characters are allowed"
msgstr ""
"doit démarrer par un caractère en minuscule suivi par des caractères en "
"minuscule, des nombres et \"-\" sont autorisés"
#: tiramisu/option/domainnameoption.py:93
msgid ""
"must start with lowercase characters followed by lowercase characters, "
"number and \"-\" characters are recommanded"
msgstr ""
"doit démarrer par un caractère en minuscule suivi par des caractères en "
"minuscule, des nombres et\"-\" sont recommandés"
#: tiramisu/option/domainnameoption.py:97
#: tiramisu/option/domainnameoption.py:98
msgid "could be a IP, otherwise {}"
msgstr "peut être une IP, autrement {}"
#: tiramisu/option/domainnameoption.py:134
#: tiramisu/option/domainnameoption.py:143
msgid "invalid length (min 1)"
msgstr "longueur invalide (min 1)"
#: tiramisu/option/domainnameoption.py:137
#: tiramisu/option/domainnameoption.py:146
msgid "invalid length (max {0})"
msgstr "longueur invalide (max {0})"
#: tiramisu/option/domainnameoption.py:143
#: tiramisu/option/domainnameoption.py:152
msgid "must have dot"
msgstr "doit avoir un point"
#: tiramisu/option/domainnameoption.py:145
#: tiramisu/option/domainnameoption.py:154
msgid "invalid length (max 255)"
msgstr "longueur invalide (max 255)"
#: tiramisu/option/domainnameoption.py:163
#: tiramisu/option/domainnameoption.py:175
msgid "DNS resolution failed"
msgstr "la résolution DNS a échoué"
#: tiramisu/option/domainnameoption.py:179
msgid "error resolving DNS: {1}"
msgstr "erreur de résolution DNS : {1}"
#: tiramisu/option/domainnameoption.py:186
msgid "must not be an IP"
msgstr "ne doit pas être une IP"
#: tiramisu/option/domainnameoption.py:186
#: tiramisu/option/domainnameoption.py:209
msgid "some characters are uppercase"
msgstr "des caractères sont en majuscule"
@ -576,9 +638,17 @@ msgstr ""
msgid "must starts with \"/\""
msgstr "doit débuter par \"/\""
#: tiramisu/option/filenameoption.py:78
msgid "cannot find {0} \"{1}\""
msgstr "ne peut pas trouver {0} \"{1}\""
#: tiramisu/option/filenameoption.py:77
msgid "directory"
msgstr "répertoire"
#: tiramisu/option/filenameoption.py:77
msgid "file"
msgstr "fichier"
#: tiramisu/option/filenameoption.py:79
msgid "cannot find this {0}"
msgstr "ce {0} est introuvable"
#: tiramisu/option/intoption.py:52
msgid "value should be equal or greater than \"{0}\""
@ -688,15 +758,15 @@ msgstr "validators doit être une liste de Calculation pour \"{0}\""
msgid "validators must be a Calculation for \"{0}\""
msgstr "validators doit être un Calculation pour \"{0}\""
#: tiramisu/option/option.py:146
#: tiramisu/option/option.py:141
msgid "invalid default_multi value \"{0}\" for option {1}"
msgstr "la valeur default_multi est invalide {0} pour l'option {1}"
#: tiramisu/option/option.py:154
#: tiramisu/option/option.py:149
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/option.py:167
#: tiramisu/option/option.py:162
msgid ""
"invalid default_multi value \"{0}\" for option {1}, must be a list for a "
"submulti"
@ -704,19 +774,19 @@ msgstr ""
"valeur invalide pour default_multi \"{0}\" pour l'option {1}, doit être une "
"liste pour une submulti"
#: tiramisu/option/option.py:290
#: tiramisu/option/option.py:285
msgid "the value \"{}\" is not unique"
msgstr "la valeur de \"{}\" n'est pas unique"
#: tiramisu/option/option.py:352
#: tiramisu/option/option.py:347
msgid "which must not be a list"
msgstr "qui ne doit pas être une liste"
#: tiramisu/option/option.py:404 tiramisu/option/option.py:430
#: tiramisu/option/option.py:399 tiramisu/option/option.py:425
msgid "which must be a list"
msgstr "qui doit être une liste"
#: tiramisu/option/option.py:424
#: tiramisu/option/option.py:419
msgid "which \"{}\" must be a list of list"
msgstr "lequel \"{}\" doit être une liste de liste"
@ -823,6 +893,10 @@ msgstr "devrait être une nombre entre {0} et {1}"
msgid "must be between {0} and {1}"
msgstr "doit être une nombre entre {0} et {1}"
#: tiramisu/option/stroption.py:41
msgid "which is not a string"
msgstr "qui n'est pas une chaîne de caractères"
#: tiramisu/option/symlinkoption.py:51
msgid ""
"malformed symlink second parameters must be an option for \"{0}\", not {1}"
@ -834,7 +908,15 @@ msgstr ""
msgid "must start with http:// or https://"
msgstr "doit débuter par http:// ou https://"
#: tiramisu/option/urloption.py:119
#: tiramisu/option/urloption.py:116
msgid "the port \"{0}\" is invalid: {1}"
msgstr "le port \"{0}\" est invalide : {1}"
#: tiramisu/option/urloption.py:123
msgid "the domain \"{0}\" is invalid: {1}"
msgstr "le domaine \"{0}\" est invalide : {1}"
#: tiramisu/option/urloption.py:127
msgid "must ends with a valid resource name"
msgstr "doit finir par un nom de ressource valide"
@ -934,6 +1016,9 @@ msgstr ""
msgid "information's item not found \"{}\""
msgstr "l'information de l'objet ne sont pas trouvé \"{}\""
#~ msgid "cannot find {0} \"{1}\""
#~ msgstr "ne peut pas trouver {0} \"{1}\""
#~ msgid ""
#~ "IP \"{ip[\"value\"]}\" ({ip[\"name\"]}) with this netmask is in fact a "
#~ "broacast address"
@ -1107,18 +1192,12 @@ msgstr "l'information de l'objet ne sont pas trouvé \"{}\""
#~ msgstr ""
#~ "seuls les caractères en minuscule, les nombres et \"-\" sont recommandés"
#~ msgid "cannot set symlinkoption in a dynoptiondescription"
#~ msgstr "ne peut mettre une symlinkoption dans une dynoptiondescription"
#~ msgid "callback is mandatory for the dynoptiondescription \"{}\""
#~ msgstr "un callback est obligatoire pour le dynoptiondescription \"{}\""
#~ msgid "email address"
#~ msgstr "adresse mail"
#~ msgid "file name"
#~ msgstr "nom de fichier"
#~ msgid "float"
#~ msgstr "nombre flottant"
@ -1191,9 +1270,6 @@ msgstr "l'information de l'objet ne sont pas trouvé \"{}\""
#~ msgid "IP \"{0}\" (\"{1}\") is the broadcast"
#~ msgstr "IP \"{0}\" (\"{1}\") est l'adresse de broadcast"
#~ msgid "unique must be a boolean, not \"{}\""
#~ msgstr "unique doit être un booléan, pas \"{}\""
#~ msgid "unique must be set only with multi value"
#~ msgstr "unique doit être activé uniquement avec une valeur multiple"

View file

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-11-05 08:52+0100\n"
"POT-Creation-Date: 2025-02-07 08:01+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -35,79 +35,79 @@ msgstr ""
msgid "Commands:"
msgstr ""
#: tiramisu/api.py:111 tiramisu/api.py:1840
#: tiramisu/api.py:111 tiramisu/api.py:1843
msgid "please specify a valid sub function ({0}.{1})"
msgstr ""
#: tiramisu/api.py:194
#: tiramisu/api.py:196
msgid "please do not specify index ({0}.{1})"
msgstr ""
#: tiramisu/api.py:199 tiramisu/api.py:844
#: tiramisu/api.py:201 tiramisu/api.py:847
msgid "please specify index with a follower option ({0}.{1})"
msgstr ""
#: tiramisu/api.py:220
#: tiramisu/api.py:222
msgid "please specify a valid sub function ({0}.{1}): {2}"
msgstr ""
#: tiramisu/api.py:431
#: tiramisu/api.py:434
msgid "the option {0} is not a dynamic option, cannot get identifiers with only_self parameter to True"
msgstr ""
#: tiramisu/api.py:517
#: tiramisu/api.py:520
msgid "cannot get option from a follower symlink without index"
msgstr ""
#: tiramisu/api.py:592
#: tiramisu/api.py:595
msgid "cannot add this property: \"{0}\""
msgstr ""
#: tiramisu/api.py:619
#: tiramisu/api.py:622
msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\""
msgstr ""
#: tiramisu/api.py:623
#: tiramisu/api.py:626
msgid "cannot find \"{0}\" in option \"{1}\""
msgstr ""
#: tiramisu/api.py:628
#: tiramisu/api.py:631
msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\" at index \"{2}\""
msgstr ""
#: tiramisu/api.py:632
#: tiramisu/api.py:635
msgid "cannot find \"{0}\" in option \"{1}\" at index \"{2}\""
msgstr ""
#: tiramisu/api.py:676
#: tiramisu/api.py:679
msgid "cannot find \"{0}\""
msgstr ""
#: tiramisu/api.py:808
#: tiramisu/api.py:811
msgid "cannot reduce length of the leader {}"
msgstr ""
#: tiramisu/api.py:861
#: tiramisu/api.py:864
msgid "only multi value has defaultmulti"
msgstr ""
#: tiramisu/api.py:1020
#: tiramisu/api.py:1023
msgid "please specify a valid sub function ({0}.{1}) for {2}"
msgstr ""
#: tiramisu/api.py:1407
#: tiramisu/api.py:1410
msgid "properties must be a frozenset"
msgstr ""
#: tiramisu/api.py:1411 tiramisu/api.py:1438
#: tiramisu/api.py:1414 tiramisu/api.py:1441
msgid "unknown when {} (must be in append or remove)"
msgstr ""
#: tiramisu/api.py:1424 tiramisu/api.py:1448 tiramisu/config.py:1680
#: tiramisu/api.py:1427 tiramisu/api.py:1451 tiramisu/config.py:1681
msgid "unknown type {}"
msgstr ""
#: tiramisu/api.py:1812
#: tiramisu/api.py:1815
msgid "do not use unrestraint, nowarnings or forcepermissive together"
msgstr ""
@ -135,6 +135,22 @@ msgstr ""
msgid "param must have a boolean not a {} for raisepropertyerror"
msgstr ""
#: tiramisu/autolib.py:151
msgid "identifiers in ParamDynOption must be a list, not {0}"
msgstr ""
#: tiramisu/autolib.py:155
msgid "optional in ParamDynOption must be a boolean, not {0}"
msgstr ""
#: tiramisu/autolib.py:206
msgid "cannot add option in information after creating config"
msgstr ""
#: tiramisu/autolib.py:208
msgid "cannot redefine option in information"
msgstr ""
#: tiramisu/autolib.py:212
msgid "option in ParamInformation cannot be a symlinkoption"
msgstr ""
@ -171,103 +187,107 @@ msgstr ""
msgid "option {0} is not a dynoptiondescription or in a dynoptiondescription"
msgstr ""
#: tiramisu/autolib.py:848
#: tiramisu/autolib.py:687
msgid "cannot calculate arguments for \"{0}\", cannot find dynamic variable \"{1}\" with identifier \"{2}\", list of valid identifiers: {3}"
msgstr ""
#: tiramisu/autolib.py:866
msgid "the \"{}\" function with positional arguments \"{}\" and keyword arguments \"{}\" must not return a list (\"{}\") for the follower option {}"
msgstr ""
#: tiramisu/autolib.py:863
#: tiramisu/autolib.py:881
msgid "the \"{}\" function must not return a list (\"{}\") for the follower option {}"
msgstr ""
#: tiramisu/autolib.py:904
#: tiramisu/autolib.py:922
msgid "unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and \"{4}\" for option {2}"
msgstr ""
#: tiramisu/autolib.py:915
#: tiramisu/autolib.py:933
msgid "unexpected error \"{0}\" in function \"{1}\" for option {2}"
msgstr ""
#: tiramisu/config.py:419
#: tiramisu/config.py:420
msgid "index \"{0}\" is greater than the leadership length \"{1}\" for option {2}"
msgstr ""
#: tiramisu/config.py:579
#: tiramisu/config.py:580
msgid "there is no option description for this config (may be GroupConfig)"
msgstr ""
#: tiramisu/config.py:668
#: tiramisu/config.py:669
msgid "no option found in config with these criteria"
msgstr ""
#: tiramisu/config.py:871
#: tiramisu/config.py:872
msgid "the follower option {0} has greater length ({1}) than the leader length ({2})"
msgstr ""
#: tiramisu/config.py:982 tiramisu/option/optiondescription.py:74
#: tiramisu/config.py:983 tiramisu/option/optiondescription.py:74
msgid "option description seems to be part of an other config"
msgstr ""
#: tiramisu/config.py:1144
#: tiramisu/config.py:1145
msgid "parent of {0} not already exists"
msgstr ""
#: tiramisu/config.py:1191
#: tiramisu/config.py:1192
msgid "cannot set leadership object has root optiondescription"
msgstr ""
#: tiramisu/config.py:1194
#: tiramisu/config.py:1195
msgid "cannot set dynoptiondescription object has root optiondescription"
msgstr ""
#: tiramisu/config.py:1246
#: tiramisu/config.py:1247
msgid "config name must be uniq in groupconfig for \"{0}\""
msgstr ""
#: tiramisu/config.py:1457
#: tiramisu/config.py:1458
msgid "unknown config \"{}\""
msgstr ""
#: tiramisu/config.py:1482
#: tiramisu/config.py:1483
msgid "child must be a Config, MixConfig or MetaConfig"
msgstr ""
#: tiramisu/config.py:1517
#: tiramisu/config.py:1518
msgid "force_default, force_default_if_same or force_dont_change_value cannot be set with only_config"
msgstr ""
#: tiramisu/config.py:1527
#: tiramisu/config.py:1528
msgid "force_default and force_dont_change_value cannot be set together"
msgstr ""
#: tiramisu/config.py:1676
#: tiramisu/config.py:1677
msgid "config name must be uniq in groupconfig for {0}"
msgstr ""
#: tiramisu/config.py:1721
#: tiramisu/config.py:1722
msgid "config added has no name, the name is mandatory"
msgstr ""
#: tiramisu/config.py:1726
#: tiramisu/config.py:1727
msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\""
msgstr ""
#: tiramisu/config.py:1744 tiramisu/config.py:1750
#: tiramisu/config.py:1745 tiramisu/config.py:1751
msgid "cannot find the config {0}"
msgstr ""
#: tiramisu/config.py:1776
#: tiramisu/config.py:1777
msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr ""
#: tiramisu/config.py:1788
#: tiramisu/config.py:1789
msgid "child must be a Config or MetaConfig"
msgstr ""
#: tiramisu/config.py:1793
#: tiramisu/config.py:1794
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/config.py:1810
#: tiramisu/config.py:1811
msgid "metaconfig must have the same optiondescription"
msgstr ""
@ -311,14 +331,22 @@ msgstr ""
msgid "invalid value"
msgstr ""
#: tiramisu/error.py:201
#: tiramisu/error.py:202
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\""
msgstr ""
#: tiramisu/error.py:219 tiramisu/error.py:228
#: tiramisu/error.py:204
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\" at index \"{3}\""
msgstr ""
#: tiramisu/error.py:223 tiramisu/error.py:234
msgid "\"{0}\" is an invalid {1} for \"{2}\""
msgstr ""
#: tiramisu/error.py:225
msgid "\"{0}\" is an invalid {1} for \"{2}\" at index \"{3}\""
msgstr ""
#: tiramisu/function.py:65
msgid "network \"{0}\" ({1}) does not match with this netmask"
msgstr ""
@ -423,60 +451,76 @@ msgstr ""
msgid "only {0} are allowed"
msgstr ""
#: tiramisu/option/domainnameoption.py:60
#: tiramisu/option/domainnameoption.py:62
msgid "unknown type {0} for hostname"
msgstr ""
#: tiramisu/option/domainnameoption.py:63
#: tiramisu/option/domainnameoption.py:65
msgid "allow_ip must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:65
#: tiramisu/option/domainnameoption.py:67
msgid "allow_cidr_network must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:67
#: tiramisu/option/domainnameoption.py:69
msgid "allow_without_dot must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:69
#: tiramisu/option/domainnameoption.py:71
msgid "allow_startswith_dot must be a boolean"
msgstr ""
#: tiramisu/option/domainnameoption.py:81
#: tiramisu/option/domainnameoption.py:82
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are allowed"
msgstr ""
#: tiramisu/option/domainnameoption.py:84
#: tiramisu/option/domainnameoption.py:85
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are recommanded"
msgstr ""
#: tiramisu/option/domainnameoption.py:88
#: tiramisu/option/domainnameoption.py:89
#: tiramisu/option/domainnameoption.py:90
msgid "must start with lowercase characters followed by lowercase characters, number and \"-\" characters are allowed"
msgstr ""
#: tiramisu/option/domainnameoption.py:93
msgid "must start with lowercase characters followed by lowercase characters, number and \"-\" characters are recommanded"
msgstr ""
#: tiramisu/option/domainnameoption.py:97
#: tiramisu/option/domainnameoption.py:98
msgid "could be a IP, otherwise {}"
msgstr ""
#: tiramisu/option/domainnameoption.py:134
#: tiramisu/option/domainnameoption.py:143
msgid "invalid length (min 1)"
msgstr ""
#: tiramisu/option/domainnameoption.py:137
#: tiramisu/option/domainnameoption.py:146
msgid "invalid length (max {0})"
msgstr ""
#: tiramisu/option/domainnameoption.py:143
#: tiramisu/option/domainnameoption.py:152
msgid "must have dot"
msgstr ""
#: tiramisu/option/domainnameoption.py:145
#: tiramisu/option/domainnameoption.py:154
msgid "invalid length (max 255)"
msgstr ""
#: tiramisu/option/domainnameoption.py:163
msgid "must not be an IP"
#: tiramisu/option/domainnameoption.py:175
msgid "DNS resolution failed"
msgstr ""
#: tiramisu/option/domainnameoption.py:179
msgid "error resolving DNS: {1}"
msgstr ""
#: tiramisu/option/domainnameoption.py:186
msgid "must not be an IP"
msgstr ""
#: tiramisu/option/domainnameoption.py:209
msgid "some characters are uppercase"
msgstr ""
@ -500,8 +544,16 @@ msgstr ""
msgid "must starts with \"/\""
msgstr ""
#: tiramisu/option/filenameoption.py:78
msgid "cannot find {0} \"{1}\""
#: tiramisu/option/filenameoption.py:77
msgid "directory"
msgstr ""
#: tiramisu/option/filenameoption.py:77
msgid "file"
msgstr ""
#: tiramisu/option/filenameoption.py:79
msgid "cannot find this {0}"
msgstr ""
#: tiramisu/option/intoption.py:52
@ -604,31 +656,31 @@ msgstr ""
msgid "validators must be a Calculation for \"{0}\""
msgstr ""
#: tiramisu/option/option.py:146
#: tiramisu/option/option.py:141
msgid "invalid default_multi value \"{0}\" for option {1}"
msgstr ""
#: tiramisu/option/option.py:154
#: tiramisu/option/option.py:149
msgid "invalid default_multi value \"{0}\" for option {1}, {2}"
msgstr ""
#: tiramisu/option/option.py:167
#: tiramisu/option/option.py:162
msgid "invalid default_multi value \"{0}\" for option {1}, must be a list for a submulti"
msgstr ""
#: tiramisu/option/option.py:290
#: tiramisu/option/option.py:285
msgid "the value \"{}\" is not unique"
msgstr ""
#: tiramisu/option/option.py:352
#: tiramisu/option/option.py:347
msgid "which must not be a list"
msgstr ""
#: tiramisu/option/option.py:404 tiramisu/option/option.py:430
#: tiramisu/option/option.py:399 tiramisu/option/option.py:425
msgid "which must be a list"
msgstr ""
#: tiramisu/option/option.py:424
#: tiramisu/option/option.py:419
msgid "which \"{}\" must be a list of list"
msgstr ""
@ -729,6 +781,10 @@ msgstr ""
msgid "must be between {0} and {1}"
msgstr ""
#: tiramisu/option/stroption.py:41
msgid "which is not a string"
msgstr ""
#: tiramisu/option/symlinkoption.py:51
msgid "malformed symlink second parameters must be an option for \"{0}\", not {1}"
msgstr ""
@ -737,7 +793,15 @@ msgstr ""
msgid "must start with http:// or https://"
msgstr ""
#: tiramisu/option/urloption.py:119
#: tiramisu/option/urloption.py:116
msgid "the port \"{0}\" is invalid: {1}"
msgstr ""
#: tiramisu/option/urloption.py:123
msgid "the domain \"{0}\" is invalid: {1}"
msgstr ""
#: tiramisu/option/urloption.py:127
msgid "must ends with a valid resource name"
msgstr ""

View file

@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
[project]
name = "tiramisu"
version = "5.1.0"
version = "5.2.0a2"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md"
description = "an options controller tool"

View file

@ -69,11 +69,11 @@ def test_cache_importation_property():
cfg = Config(od1)
cfg.option('u2').property.add('prop')
export = cfg.property.exportation()
assert cfg.option('u2').property.get() == {'prop'}
assert cfg.option('u2').property.get() == {'validator', 'prop'}
cfg.option('u2').property.add('prop2')
assert cfg.option('u2').property.get() == {'prop', 'prop2'}
assert cfg.option('u2').property.get() == {'validator', 'prop', 'prop2'}
cfg.property.importation(export)
assert cfg.option('u2').property.get() == {'prop'}
assert cfg.option('u2').property.get() == {'validator', 'prop'}
cfg = Config(od1)
# assert not list_sessions()
@ -366,8 +366,8 @@ def test_cache_leader_and_followers():
cfg.value.get()
global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']
val1_props = []
val1_val1_props = ['empty', 'unique']
val1_val2_props = []
val1_val1_props = ['empty', 'unique', 'validator']
val1_val2_props = ['validator']
global_props = frozenset(global_props)
val1_props = frozenset(val1_props)
val1_val1_props = frozenset(val1_val1_props)
@ -384,7 +384,7 @@ def test_cache_leader_and_followers():
#
cfg.option('val1.val1').value.set([None])
val_val2_props = {idx_val2: (val1_val2_props, None), None: (set(), None)}
compare(settings.get_cached(), {'val1.val1': {None: ({'empty', 'unique'}, None, True)}})
compare(settings.get_cached(), {'val1.val1': {None: ({'validator', 'empty', 'unique'}, None, True)}})
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
cfg.value.get()
#has value
@ -416,8 +416,8 @@ def test_cache_leader_callback():
cfg.value.get()
global_props = ['cache', 'disabled', 'frozen', 'hidden', 'validator', 'warnings', 'force_store_value']
val1_props = []
val1_val1_props = ['empty', 'unique']
val1_val2_props = []
val1_val1_props = ['empty', 'unique', 'validator']
val1_val2_props = ['validator']
global_props = frozenset(global_props)
val1_props = frozenset(val1_props)
val1_val1_props = frozenset(val1_val1_props)
@ -429,7 +429,7 @@ def test_cache_leader_callback():
})
compare(values.get_cached(), {'val1.val1': {None: ([], None)}})
cfg.option('val1.val1').value.set([None])
compare(settings.get_cached(), {'val1.val1': {None: ({'unique', 'empty'}, None, True)}})
compare(settings.get_cached(), {'val1.val1': {None: ({'unique', 'empty', 'validator'}, None, True)}})
compare(values.get_cached(), {'val1.val1': {None: ([None], None, True)}})
cfg.value.get()
@ -451,24 +451,24 @@ def test_cache_requires():
settings = cfg._config_bag.context.properties_cache
assert values.get_cached() == {}
assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(settings.get_cached(), {'activate_service': {None: ({'validator'}, None)},
'ip_address_service': {None: ({"validator"}, None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}})
cfg.value.get()
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
'ip_address_service': {None: ({"validator"}, None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}})
cfg.option('ip_address_service').value.set('1.1.1.1')
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)}})
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)}})
compare(values.get_cached(), {'activate_service': {None: (True, None)}, 'ip_address_service': {None: ('1.1.1.1', None, True)}})
cfg.value.get()
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
'ip_address_service': {None: ({"validator"}, None)}})
compare(values.get_cached(), {'ip_address_service': {None: ('1.1.1.1', None)},
'activate_service': {None: (True, None)}})
@ -477,8 +477,8 @@ def test_cache_requires():
compare(values.get_cached(), {'activate_service': {None: (False, None)}})
cfg.value.get()
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set(['disabled']), None)}})
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
'ip_address_service': {None: ({'disabled', "validator"}, None)}})
compare(values.get_cached(), {'activate_service': {None: (False, None)}})
# assert not list_sessions()
@ -499,19 +499,19 @@ def test_cache_global_properties():
settings = cfg._config_bag.context.properties_cache
assert values.get_cached() == {}
assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
'ip_address_service': {None: ({"validator"}, None)}})
compare(values.get_cached(), {'ip_address_service': {None: (None, None)},
'activate_service': {None: (True, None)}})
cfg.property.remove('disabled')
assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
'ip_address_service': {None: ({"validator"}, None)}})
cfg.property.add('test')
assert cfg.option('ip_address_service').value.get() == None
compare(settings.get_cached(), {'activate_service': {None: (set([]), None)},
'ip_address_service': {None: (set([]), None)}})
compare(settings.get_cached(), {'activate_service': {None: ({"validator"}, None)},
'ip_address_service': {None: ({"validator"}, None)}})
# assert not list_sessions()

View file

@ -423,7 +423,7 @@ def test_config_reset():
cfg.owner.set('test')
assert cfg.owner.get() == 'test'
assert not cfg.option('gc.gc2.bool').value.get()
assert not cfg.option('boolop').property.get()
assert cfg.option('boolop').property.get() == frozenset(["validator"])
assert not cfg.option('boolop').permissive.get()
assert not cfg.option('wantref').information.get('info', None)
#
@ -440,7 +440,7 @@ def test_config_reset():
cfg.config.reset()
assert cfg.owner.get() == 'test'
assert not cfg.option('gc.gc2.bool').value.get()
assert not cfg.option('boolop').property.get()
assert cfg.option('boolop').property.get() == {"validator"}
assert not cfg.option('float').permissive.get()
assert not cfg.option('wantref').information.get('info', None)
# assert not list_sessions()

View file

@ -275,3 +275,9 @@ def test_url(config_type):
with pytest.raises(ValueError):
cfg.option('u').value.set('https://FOO.COM:8443')
# assert not list_sessions()
def test_domainname_existence():
DomainnameOption('d', '', 'google.fr', test_existence=True)
with pytest.raises(ValueError):
DomainnameOption('d', '', 'ljijouuuehyfr.com', test_existence=True)

View file

@ -300,20 +300,20 @@ def test_prop_dyndescription():
od = OptionDescription('od', '', [dod])
od2 = OptionDescription('od', '', [od])
cfg = Config(od2)
assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval1.st').property.get()) == {'test', "validator"}
assert set(cfg.option('od.dodval2.st').property.get()) == {'test', "validator"}
cfg.option('od.dodval2.st').property.add('test2')
assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
assert set(cfg.option('od.dodval1.st').property.get()) == {'test', "validator"}
assert set(cfg.option('od.dodval2.st').property.get()) == {'test', 'test2', "validator"}
#
assert set(cfg.option('od.dodval1').property.get()) == set([])
assert set(cfg.option('od.dodval2').property.get()) == set([])
assert set(cfg.option('od.dodval1').property.get()) == set()
assert set(cfg.option('od.dodval2').property.get()) == set()
cfg.option('od.dodval1').property.add('test1')
assert set(cfg.option('od.dodval1').property.get()) == set(['test1'])
assert set(cfg.option('od.dodval2').property.get()) == set([])
assert set(cfg.option('od.dodval1').property.get()) == {'test1'}
assert set(cfg.option('od.dodval2').property.get()) == set()
cfg.option('od.dodval1').property.remove('test1')
assert set(cfg.option('od.dodval1').property.get()) == set([])
assert set(cfg.option('od.dodval2').property.get()) == set([])
assert set(cfg.option('od.dodval1').property.get()) == set()
assert set(cfg.option('od.dodval2').property.get()) == set()
# assert not list_sessions()
@ -432,6 +432,20 @@ def test_callback_dyndescription_outside3():
assert parse_od_get(cfg.value.get()) == {'od.out': 'val1', 'lst': ['val1', 'val2']}
def test_callback_dyndescription_outside_optional():
lst = StrOption('lst', '', ['val'], multi=True)
st = StrOption('st', '', 'val')
dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list, Params(ParamOption(lst))))
out = StrOption('out', '', Calculation(calc_value, Params(ParamDynOption(st, ['unknown_val'], optional=True))))
od = OptionDescription('od', '', [dod, out])
od2 = OptionDescription('od', '', [od, lst])
cfg = Config(od2)
assert parse_od_get(cfg.value.get()) == {'od.dodval.st': 'val', 'od.out': None, 'lst': ['val']}
cfg.option('lst').value.set(['val', 'unknown_val'])
assert parse_od_get(cfg.value.get()) == {'od.dodval.st': 'val', 'od.dodunknown_val.st': 'val', 'od.out': 'val', 'lst': ['val', 'unknown_val']}
# assert not list_sessions()
def test_callback_dyndescription_subdyn():
lst = StrOption('lst', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '', 'val1')
@ -602,14 +616,14 @@ def test_prop_dyndescription_context():
od = OptionDescription('od', '', [dod, val1])
od2 = OptionDescription('od', '', [od])
cfg = Config(od2)
assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator", 'test'}
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test'}
cfg.option('od.dodval2.st').property.add('test2')
assert set(cfg.option('od.dodval1.st').property.get()) == set(['test'])
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator", 'test'}
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test', 'test2'}
cfg.option('od.dodval1.st').permissive.add('test')
assert set(cfg.option('od.dodval1.st').property.get()) == set([])
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2'])
assert set(cfg.option('od.dodval1.st').property.get()) == {"validator"}
assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test', 'test2'}
# assert not list_sessions()

View file

@ -796,7 +796,7 @@ def test_follower_unique():
cfg = Config(od1)
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(["192.168.230.145", "192.168.230.146"])
# unique property is removed for a follower
assert not cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get()
assert cfg.option('ip_admin_eth0.netmask_admin_eth0', 1).property.get() == {"validator"}
# assert not list_sessions()
@ -1093,3 +1093,15 @@ def test_leader_forbidden_properties_callback(config_type):
cfg = Config(od1)
with pytest.raises(LeadershipError):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get()
def test_follower_value_not_list():
ip_admin_eth0 = IPOption('ip_admin_eth0', "ip réseau autorisé", multi=True, default=['1.1.1.1'])
netmask_admin_eth0 = NetmaskOption('netmask_admin_eth0', "masque du sous-réseau", default_multi='255.255.255.0', multi=True, properties=('force_store_value',))
interface0 = Leadership('interface0', '', [ip_admin_eth0, netmask_admin_eth0])
od1 = OptionDescription('od', '', [interface0])
od2 = OptionDescription('root', '', [od1])
cfg = Config(od2)
cfg.property.read_write()
with pytest.raises(ValueError):
cfg.option('od.interface0.ip_admin_eth0').value.set(None)

View file

@ -202,10 +202,10 @@ def test_property_get_unique_empty():
od1 = OptionDescription("options", "", [s, s2, s3, s4])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.option('string').property.get() == {'empty', 'unique'}
assert cfg.option('string2').property.get() == {'empty', 'notunique'}
assert cfg.option('string3').property.get() == {'unique', 'notempty'}
assert cfg.option('string4').property.get() == {'notunique', 'notempty'}
assert cfg.option('string').property.get() == {"validator", 'empty', 'unique'}
assert cfg.option('string2').property.get() == {"validator", 'empty', 'notunique'}
assert cfg.option('string3').property.get() == {"validator", 'unique', 'notempty'}
assert cfg.option('string4').property.get() == {"validator", 'notunique', 'notempty'}
# assert not list_sessions()
@ -220,7 +220,7 @@ def test_property_only_raises():
od1 = OptionDescription("options", "", [s, intoption, stroption])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.option('str').property.get() == {'empty', 'unique'}
assert cfg.option('str').property.get() == {'empty', 'unique', 'validator'}
assert cfg.option('str').property.get(only_raises=True) == set()
# assert not list_sessions()
@ -569,23 +569,23 @@ def test_access_by_get_whith_hide():
def test_append_properties():
od1 = make_description()
cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == set()
assert cfg.option('gc.dummy').property.get() == {"validator"}
cfg.option('gc.dummy').property.add('test')
assert cfg.option('gc.dummy').property.get() == {'test'}
assert cfg.option('gc.dummy').property.get() == {'test', "validator"}
with pytest.raises(ConfigError):
cfg.option('gc.dummy').property.add('force_store_value')
assert cfg.option('gc.dummy').property.get() == {'test'}
assert cfg.option('gc.dummy').property.get() == {'test', "validator"}
# assert not list_sessions()
def test_reset_properties():
od1 = make_description()
cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == set()
assert cfg.option('gc.dummy').property.get() == {"validator"}
cfg.option('gc.dummy').property.add('frozen')
assert cfg.option('gc.dummy').property.get() == {'frozen'}
assert cfg.option('gc.dummy').property.get() == {"validator", 'frozen'}
cfg.option('gc.dummy').property.reset()
assert cfg.option('gc.dummy').property.get() == set()
assert cfg.option('gc.dummy').property.get() == {"validator"}
# assert not list_sessions()
@ -594,7 +594,7 @@ def test_properties_cached():
od1 = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])])
cfg = Config(od1)
cfg.property.read_write()
assert cfg.option('sub.b1').property.get() == {'test'}
assert cfg.option('sub.b1').property.get() == {'test', "validator"}
# assert not list_sessions()
@ -603,9 +603,9 @@ def test_append_properties_force_store_value():
gcgroup = OptionDescription('gc', '', [gcdummy])
od1 = OptionDescription('tiramisu', '', [gcgroup])
cfg = Config(od1)
assert cfg.option('gc.dummy').property.get() == {'force_store_value'}
assert cfg.option('gc.dummy').property.get() == {'force_store_value', "validator"}
cfg.option('gc.dummy').property.add('test')
assert cfg.option('gc.dummy').property.get() == {'force_store_value', 'test'}
assert cfg.option('gc.dummy').property.get() == {'force_store_value', 'test', "validator"}
# assert not list_sessions()

View file

@ -121,6 +121,34 @@ def test_validator(config_type):
# assert not list_sessions()
def test_validator_no_validation(config_type):
opt1 = StrOption('opt1', '', validators=[Calculation(return_true, Params(ParamSelfOption()))], default='val', properties=frozenset(['novalidator']))
opt2 = StrOption('opt2', '', validators=[Calculation(return_false, Params(ParamSelfOption()))], properties=frozenset(['novalidator']))
od1 = OptionDescription('root', '', [opt1, opt2])
cfg_ori = Config(od1)
cfg = get_config(cfg_ori, config_type)
assert cfg.option('opt1').value.get() == 'val'
assert cfg.option('opt2').value.valid() is True
cfg.option('opt2').value.set('val')
def test_validator_no_validation2(config_type):
opt1 = StrOption('opt1', '', properties=frozenset(['novalidator']))
od1 = OptionDescription('root', '', [opt1])
cfg_ori = Config(od1)
cfg = get_config(cfg_ori, config_type)
cfg.option('opt1').value.set(1)
assert cfg.option('opt1').value.get() == 1
def test_validator_no_validation3(config_type):
opt1 = StrOption('opt1', '', 1, properties=frozenset(['novalidator']))
od1 = OptionDescription('root', '', [opt1])
cfg_ori = Config(od1)
cfg = get_config(cfg_ori, config_type)
assert cfg.option('opt1').value.get() == 1
def test_validator_not_valid(config_type):
with pytest.raises(ValueError):
StrOption('not_a_list', '', validators=Calculation(return_true, Params(ParamSelfOption())), default='val')

View file

@ -426,9 +426,9 @@ def test_requires_transitive_unrestraint(config_type):
#
if config_type == 'tiramisu-api':
cfg.send()
assert cfg_ori.option('activate_service_web').property.get() == {'disabled'}
assert cfg_ori.option('activate_service_web').property.get() == {'disabled', "validator"}
# FIXME assert cfg_ori.unrestraint.option('ip_address_service_web').property.get() == {'disabled'}
assert cfg_ori.option('ip_address_service_web').property.get() == {'disabled'}
assert cfg_ori.option('ip_address_service_web').property.get() == {'disabled', "validator"}
# assert not list_sessions()

View file

@ -28,6 +28,8 @@ def test_symlink_option(config_type):
assert cfg.option('c').issymlinkoption()
assert cfg.option('s1.b').type() == 'boolean'
assert cfg.option('c').type() == 'boolean'
assert cfg.option('s1.b').type(only_self=True) == 'boolean'
assert cfg.option('c').type(only_self=True) == 'symlink'
assert cfg.option('s1.b').value.get() is False
cfg.option("s1.b").value.set(True)
cfg.option("s1.b").value.set(False)
@ -157,7 +159,7 @@ def test_symlink_getproperties():
od1 = OptionDescription('opt', '', [boolopt, linkopt])
cfg = Config(od1)
cfg.property.read_write()
assert boolopt.impl_getproperties() == linkopt.impl_getproperties() == {'test'}
assert boolopt.impl_getproperties() == linkopt.impl_getproperties() == {'test', "validator"}
# assert boolopt.impl_has_callback() == linkopt.impl_has_callback() == False
# assert not list_sessions()

View file

@ -139,6 +139,8 @@ def option_type(typ):
@wraps(func)
def wrapped(*args, **kwargs):
self = args[0]
if isinstance(typ, list) and "allow_dynoption" in typ:
self._allow_dynoption = True
config_bag = self._config_bag
if self._config_bag.context.impl_type == "group" and "group" in types:
options_bag = [
@ -269,10 +271,9 @@ class _TiramisuOptionWalk:
class _TiramisuOptionOptionDescription:
"""Manage option"""
_validate_properties = False
@option_type(["optiondescription", "option", "with_or_without_index", "symlink"])
@option_type(["optiondescription", "option", "with_or_without_index", "symlink", "allow_dynoption"])
def get(self):
"""Get Tiramisu option"""
return self._subconfig.option
@ -347,11 +348,13 @@ class _TiramisuOptionOptionDescription:
return options
@option_type(["option", "optiondescription", "symlink", "with_or_without_index"])
def type(self):
def type(self, only_self=False):
"""Get de option type"""
option = self._subconfig.option
if option.impl_is_optiondescription():
return "optiondescription"
if only_self and option.impl_is_symlinkoption():
return 'symlink'
return option.get_type()
@option_type(["option", "symlink", "with_or_without_index"])
@ -801,6 +804,7 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
option = self._subconfig.option
if (
not isinstance(value, Calculation)
and isinstance(value, list)
and option.impl_is_leader()
and len(value) < self._subconfig.parent.get_length_leadership()
):
@ -1527,7 +1531,7 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
def get(self):
"""Get Tiramisu option"""
return None
return self._config_bag.context.get_description()
def isleadership(self):
"""Test if option is a leader or a follower"""

View file

@ -22,7 +22,7 @@ from typing import Any, Optional, Union, Callable, Dict, List
from itertools import chain
import weakref
from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWarning
from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWarning, CancelParam, display_list
from .i18n import _
from .setting import undefined, ConfigBag
from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR
@ -148,11 +148,11 @@ class ParamDynOption(ParamOption):
)
if not isinstance(identifiers, list):
raise Exception(
f"identifiers in ParamDynOption must be a list, not {identifiers}"
_("identifiers in ParamDynOption must be a list, not {0}").format(identifiers)
)
if not isinstance(optional, bool):
raise Exception(
f"optional in ParamDynOption must be a boolean, not {optional}"
_("optional in ParamDynOption must be a boolean, not {0}").format(optional)
)
self.identifiers = identifiers
self.optional = optional
@ -203,9 +203,9 @@ class ParamInformation(Param):
def set_option(self, option: "Option" = None) -> None:
if not hasattr(self, "self_option"):
raise ConfigError("cannot add option in information after creating config")
raise ConfigError(_("cannot add option in information after creating config"))
if self.option:
raise ConfigError("cannot redefine option in information")
raise ConfigError(_("cannot redefine option in information"))
if not option.impl_is_optiondescription():
if option.impl_is_symlinkoption():
raise ValueError(
@ -464,7 +464,7 @@ def manager_callback(
) from err
except AttributeError as err:
if isinstance(param, ParamDynOption) and param.optional:
# cannot acces, simulate a propertyerror
# cannot access, simulate a propertyerror
raise PropertiesOptionError(
subconfig,
["configerror"],
@ -524,7 +524,7 @@ def manager_callback(
) from err
except AttributeError as err:
if isinstance(param, ParamDynOption) and param.optional:
# cannot acces, simulate a propertyerror
# cannot access, simulate a propertyerror
raise PropertiesOptionError(
param,
["configerror"],
@ -602,6 +602,9 @@ def manager_callback(
"option {0} is not a dynoptiondescription or in a dynoptiondescription"
).format(display_name)
)
if subconfig.identifiers is None:
# if uncalculated
return
return subconfig.identifiers[param.identifier_index]
if isinstance(param, ParamSelfOption):
@ -673,7 +676,16 @@ def manager_callback(
parent,
)
except AttributeError as err:
raise ConfigError(err) from err
if parent.path:
child_path = parent.path + '.' + name
else:
child_path = name
if param.optional:
raise CancelParam(callbk_option.impl_getpath(), child_path)
identifiers = display_list(doption.get_identifiers(parent), add_quote=True)
msg = _('cannot calculate arguments for "{0}", cannot find dynamic variable "{1}" with identifier "{2}", list of valid identifiers: {3}').format(subconfig.path, doption.impl_getpath(), identifier, identifiers)
raise ConfigError(msg) from err
new_parents.append(
parent.get_child(
doption,
@ -828,6 +840,12 @@ def carry_out_calculation(
args.append(err)
else:
kwargs[key] = err
except CancelParam as err:
if callback.__name__ in FUNCTION_WAITING_FOR_ERROR:
if key is None:
args.append(err)
else:
kwargs[key] = err
ret = calculate(
subconfig,
callback,
@ -893,7 +911,7 @@ def calculate(
except (ValueError, ValueWarning) as err:
if allow_value_error:
if force_value_warning:
raise ValueWarning(str(err))
raise ValueWarning(msg=str(err))
raise err from err
error = err
except ConfigError as err:

View file

@ -227,7 +227,8 @@ class SubConfig:
identifiers: Optional[list[str]],
*,
true_path: Optional[str] = None,
properties: Union[list[str], undefined] = undefined,
# for python 3.9 properties: Union[list[str], undefined] = undefined,
properties = undefined,
validate_properties: bool = True,
) -> None:
self.index = index

View file

@ -181,7 +181,7 @@ class _CommonError:
try:
msg = self.prefix
except AttributeError:
self.prefix = self.tmpl.format(self.val, _(self.display_type), self.name)
self.prefix = self.tmpl.format(self.val, _(self.display_type), self.name, self.index)
msg = self.prefix
if self.err_msg:
if msg:
@ -196,13 +196,16 @@ class _CommonError:
class ValueWarning(_CommonError, UserWarning):
tmpl = None
def __init__(self, *args, **kwargs):
def __init__(self, **kwargs):
if ValueWarning.tmpl is None:
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for "{2}"')
if len(args) == 1 and not kwargs:
self.msg = args[0]
if kwargs.get('index') is None:
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for "{2}"')
else:
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for "{2}" at index "{3}"')
if list(kwargs) == ['msg']:
self.msg = kwargs['msg']
else:
super().__init__(*args, **kwargs)
super().__init__(**kwargs)
self.msg = None
def __str__(self):
@ -214,10 +217,13 @@ class ValueWarning(_CommonError, UserWarning):
class ValueOptionError(_CommonError, ValueError):
tmpl = None
def __init__(self, *args, **kwargs):
def __init__(self, **kwargs):
if ValueOptionError.tmpl is None:
ValueOptionError.tmpl = _('"{0}" is an invalid {1} for "{2}"')
super().__init__(*args, **kwargs)
if kwargs.get('index') is None:
self.tmpl = _('"{0}" is an invalid {1} for "{2}"')
else:
self.tmpl = _('"{0}" is an invalid {1} for "{2}" at index "{3}"')
super().__init__(**kwargs)
class ValueErrorWarning(ValueWarning):
@ -227,3 +233,19 @@ class ValueErrorWarning(ValueWarning):
if ValueErrorWarning.tmpl is None:
ValueErrorWarning.tmpl = _('"{0}" is an invalid {1} for "{2}"')
super().__init__(*args, **kwargs)
class CancelParam(Exception):
def __init__(self, origin_path, current_path):
super().__init__()
self.origin_path = origin_path
self.current_path = current_path
def __ne__(self, value):
return value is None or value == ""
def __eq__(self, value):
return value is None or value == ""
def __bool__(self):
return False

View file

@ -88,6 +88,8 @@ class Base:
assert isinstance(properties, frozenset), _(
"invalid properties type {0} for {1}," " must be a frozenset"
).format(type(properties), name)
if not self.impl_is_optiondescription() and "novalidator" not in properties:
properties = properties | {"validator"}
_setattr = object.__setattr__
_setattr(self, "_name", name)
_setattr(self, "_informations", {"doc": doc})

View file

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2024 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2025 Team tiramisu (see AUTHORS for all contributors)
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
@ -21,6 +21,7 @@
"""DomainnameOption
"""
import re
import socket
from ipaddress import ip_interface
from typing import Any, Optional, List
@ -53,12 +54,18 @@ class DomainnameOption(StrOption):
type: str = "domainname",
allow_without_dot: bool = False,
allow_startswith_dot: bool = False,
test_existence: bool = False,
_extra: dict = None,
**kwargs,
) -> None:
# pylint: disable=too-many-branches,too-many-locals,too-many-arguments
if _extra is None:
extra = {}
else:
extra = _extra
if type not in ["netbios", "hostname", "domainname"]:
raise ValueError(_("unknown type {0} for hostname").format(type))
extra = {"_dom_type": type}
extra["type"] = type
if not isinstance(allow_ip, bool):
raise ValueError(_("allow_ip must be a boolean"))
if not isinstance(allow_cidr_network, bool):
@ -67,7 +74,8 @@ class DomainnameOption(StrOption):
raise ValueError(_("allow_without_dot must be a boolean"))
if not isinstance(allow_startswith_dot, bool):
raise ValueError(_("allow_startswith_dot must be a boolean"))
extra["_allow_without_dot"] = allow_without_dot
extra["allow_without_dot"] = allow_without_dot
extra["test_existence"] = test_existence
if type == "domainname":
if allow_without_dot:
min_time = 0
@ -76,14 +84,20 @@ class DomainnameOption(StrOption):
regexp = r"((?!-)[a-z0-9-]{{{1},{0}}}\.){{{1},}}[a-z0-9-]{{1,{0}}}".format(
self._get_len(type), min_time
)
msg = _(
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed'
)
msg_warning = _(
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded'
)
else:
regexp = r"((?!-)[a-z0-9-]{{1,{0}}})".format(self._get_len(type))
msg = _(
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are allowed'
)
msg_warning = _(
'must start with lowercase characters followed by lowercase characters, number, "-" and "." characters are recommanded'
)
msg = _(
'must start with lowercase characters followed by lowercase characters, number and "-" characters are allowed'
)
msg_warning = _(
'must start with lowercase characters followed by lowercase characters, number and "-" characters are recommanded'
)
if allow_ip:
msg = _("could be a IP, otherwise {}").format(msg)
msg_warning = _("could be a IP, otherwise {}").format(msg_warning)
@ -105,15 +119,15 @@ class DomainnameOption(StrOption):
name,
doc,
)
extra["_allow_ip"] = allow_ip
extra["allow_ip"] = allow_ip
if allow_cidr_network:
extra["_network"] = NetworkOption(
name,
doc,
cidr=True,
)
extra["_allow_cidr_network"] = allow_cidr_network
extra["_allow_startswith_dot"] = allow_startswith_dot
extra["allow_cidr_network"] = allow_cidr_network
extra["allow_startswith_dot"] = allow_startswith_dot
super().__init__(
name,
@ -137,13 +151,13 @@ class DomainnameOption(StrOption):
_("invalid length (max {0})" "").format(part_name_length)
)
part_name_length = self._get_len(self.impl_get_extra("_dom_type"))
if self.impl_get_extra("_dom_type") == "domainname":
if not self.impl_get_extra("_allow_without_dot") and not "." in value:
part_name_length = self._get_len(self.impl_get_extra("type"))
if self.impl_get_extra("type") == "domainname":
if not self.impl_get_extra("allow_without_dot") and not "." in value:
raise ValueError(_("must have dot"))
if len(value) > 255:
raise ValueError(_("invalid length (max 255)"))
if self.impl_get_extra("_allow_startswith_dot") and value.startswith("."):
if self.impl_get_extra("allow_startswith_dot") and value.startswith("."):
val = value[1:]
else:
val = value
@ -155,10 +169,24 @@ class DomainnameOption(StrOption):
_valid_length(dom)
else:
_valid_length(value)
self._validate_domain_resolution(value)
def _validate_domain_resolution(self, value: str) -> None:
if not value.startswith(".") and self.impl_get_extra("test_existence") is True:
try:
socket.gethostbyname(value)
except socket.gaierror as err:
raise ValueError(
_("DNS resolution failed").format(value)
) from err
except Exception as err:
raise ValueError(
_("error resolving DNS: {1}").format(value, err)
) from err
def _validate_ip_network(self, value: str) -> None:
allow_ip = self.impl_get_extra("_allow_ip")
allow_cidr_network = self.impl_get_extra("_allow_cidr_network")
allow_ip = self.impl_get_extra("allow_ip")
allow_cidr_network = self.impl_get_extra("allow_cidr_network")
if allow_ip is False and allow_cidr_network is False:
raise ValueError(_("must not be an IP"))
if allow_ip is True:
@ -184,7 +212,7 @@ class DomainnameOption(StrOption):
def _second_level_validation_domain(self, value: str, warnings_only: bool) -> None:
if self.impl_get_extra("_has_upper").search(value):
raise ValueError(_("some characters are uppercase"))
if self.impl_get_extra("_allow_startswith_dot") and value.startswith("."):
if self.impl_get_extra("allow_startswith_dot") and value.startswith("."):
val = value[1:]
else:
val = value
@ -200,8 +228,8 @@ class DomainnameOption(StrOption):
def _second_level_validation_ip_network(
self, value: str, warnings_only: bool
) -> None:
allow_ip = self.impl_get_extra("_allow_ip")
allow_cidr_network = self.impl_get_extra("_allow_cidr_network")
allow_ip = self.impl_get_extra("allow_ip")
allow_cidr_network = self.impl_get_extra("allow_cidr_network")
# it's an IP so validate with IPOption
if allow_ip is True and allow_cidr_network is False:
try:

View file

@ -52,9 +52,9 @@ class FilenameOption(StrOption):
if typ not in ["file", "directory"]:
raise ValueError(f'unknown type "{typ}" for "{name}"')
extra = {
"_allow_relative": allow_relative,
"_test_existence": test_existence,
"_types": types,
"allow_relative": allow_relative,
"test_existence": test_existence,
"types": types,
}
super().__init__(name, *args, extra=extra, **kwargs)
@ -63,10 +63,10 @@ class FilenameOption(StrOption):
value: str,
) -> None:
super().validate(value)
if not self.impl_get_extra("_allow_relative") and not value.startswith("/"):
if not self.impl_get_extra("allow_relative") and not value.startswith("/"):
raise ValueError(_('must starts with "/"'))
if value is not None and self.impl_get_extra("_test_existence"):
types = self.impl_get_extra("_types")
if value is not None and self.impl_get_extra("test_existence"):
types = self.impl_get_extra("types")
file = Path(value)
found = False
if "file" in types and file.is_file():
@ -74,8 +74,9 @@ class FilenameOption(StrOption):
if not found and "directory" in types and file.is_dir():
found = True
if not found:
translated_types = [{"file": _("file"), "directory": _("directory")}.get(typ) for typ in types]
raise ValueError(
_('cannot find {0} "{1}"').format(
display_list(types, separator="or"), value
_('cannot find this {0}').format(
display_list(translated_types, separator="or"), value
)
)

View file

@ -43,9 +43,9 @@ class IPOption(StrOption):
):
if extra is None:
extra = {}
extra["_private_only"] = private_only
extra["_allow_reserved"] = allow_reserved
extra["_cidr"] = cidr
extra["private_only"] = private_only
extra["allow_reserved"] = allow_reserved
extra["cidr"] = cidr
super().__init__(*args, extra=extra, **kwargs)
def _validate_cidr(self, value):
@ -66,7 +66,7 @@ class IPOption(StrOption):
def validate(self, value: str) -> None:
super().validate(value)
if self.impl_get_extra("_cidr"):
if self.impl_get_extra("cidr"):
if "/" not in value:
raise ValueError(_('CIDR address must have a "/"'))
self._validate_cidr(value)
@ -75,13 +75,13 @@ class IPOption(StrOption):
def second_level_validation(self, value: str, warnings_only: bool) -> None:
ip_obj = ip_interface(value)
if not self.impl_get_extra("_allow_reserved") and ip_obj.is_reserved:
if not self.impl_get_extra("allow_reserved") and ip_obj.is_reserved:
if warnings_only:
msg = _("shouldn't be reserved IP")
else:
msg = _("mustn't be reserved IP")
raise ValueError(msg)
if self.impl_get_extra("_private_only") and not ip_obj.is_private:
if self.impl_get_extra("private_only") and not ip_obj.is_private:
if warnings_only:
msg = _("should be private IP")
else:

View file

@ -32,14 +32,14 @@ class NetworkOption(StrOption):
_type = "network address"
def __init__(self, *args, cidr=False, **kwargs):
extra = {"_cidr": cidr}
extra = {"cidr": cidr}
super().__init__(*args, extra=extra, **kwargs)
def validate(self, value: str) -> None:
super().validate(value)
if value.count(".") != 3:
raise ValueError()
cidr = self.impl_get_extra("_cidr")
cidr = self.impl_get_extra("cidr")
if cidr:
if "/" not in value:
raise ValueError(_("must use CIDR notation"))

View file

@ -127,11 +127,6 @@ class Option(BaseOption):
def test_multi_value(value):
if isinstance(value, Calculation):
return
# option_bag = OptionBag(self,
# None,
# undefined,
# properties=None,
# )
try:
self.validate(value)
self.validate_with_option(
@ -183,16 +178,19 @@ class Option(BaseOption):
# undefined,
# properties=None,
# )
self_properties = getattr(self, "_properties", {})
self.impl_validate(
None,
default,
loaded=True,
self_properties=self_properties,
)
self.impl_validate(
None,
default,
check_error=False,
loaded=True,
self_properties=self_properties,
)
self.value_dependencies(default)
if (is_multi and default != []) or (not is_multi and default is not None):
@ -261,16 +259,19 @@ class Option(BaseOption):
*,
check_error: bool = True,
loaded: bool = False,
self_properties: frozenset=frozenset(),
) -> bool:
"""Return True if value is really valid
If not validate or invalid return it returns False
"""
if (
check_error
and subconfig
and not "validator" in subconfig.config_bag.properties
):
return False
if check_error:
if subconfig:
config_properties = subconfig.config_bag.properties
self_properties = subconfig.properties
else:
config_properties = {'validator'}
if "validator" not in config_properties or "validator" not in self_properties:
return False
if subconfig:
force_index = subconfig.index
else:
@ -329,12 +330,12 @@ class Option(BaseOption):
except ValueWarning as warn:
warnings.warn_explicit(
ValueWarning(
subconfig,
val,
_(self.get_type()),
self,
str(warn),
_index,
subconfig=subconfig,
val=val,
display_type=_(self.get_type()),
opt=self,
err_msg=str(warn),
index=_index,
),
ValueWarning,
self.__class__.__name__,
@ -370,12 +371,12 @@ class Option(BaseOption):
if is_warnings_only:
warnings.warn_explicit(
ValueWarning(
subconfig,
_value,
_(self.get_type()),
self,
str(err),
_index,
subconfig=subconfig,
val=_value,
display_type=_(self.get_type()),
opt=self,
err_msg=str(err),
index=_index,
),
ValueWarning,
self.__class__.__name__,
@ -442,11 +443,11 @@ class Option(BaseOption):
or "demoting_error_warning" not in subconfig.config_bag.properties
):
raise ValueOptionError(
subconfig, val, _(self.get_type()), self, str(err), err_index
subconfig=subconfig, val=val, display_type=_(self.get_type()), opt=self, err_msg=str(err), index=err_index
) from err
warnings.warn_explicit(
ValueErrorWarning(
subconfig, val, _(self.get_type()), self, str(err), err_index
subconfig=subconfig, val=val, display_type=_(self.get_type()), opt=self, err_msg=str(err), index=err_index
),
ValueErrorWarning,
self.__class__.__name__,

View file

@ -50,15 +50,21 @@ class PortOption(StrOption):
allow_registred: bool = True,
allow_protocol: bool = False,
allow_private: bool = False,
_extra: dict = None,
**kwargs,
) -> None:
extra = {
"_allow_range": allow_range,
"_allow_protocol": allow_protocol,
"_min_value": None,
"_max_value": None,
}
if _extra is None:
extra = {}
else:
extra = _extra
extra["allow_range"] = allow_range
extra["allow_protocol"] = allow_protocol
extra["allow_zero"] = allow_zero
extra["allow_wellknown"] = allow_wellknown
extra["allow_registred"] = allow_registred
extra["allow_private"] = allow_private
extra["_min_value"] = None
extra["_max_value"] = None
ports_min = [0, 1, 1024, 49152]
ports_max = [0, 1023, 49151, 65535]
is_finally = False
@ -82,11 +88,11 @@ class PortOption(StrOption):
def validate(self, value: str) -> None:
super().validate(value)
if self.impl_get_extra("_allow_protocol") and (
if self.impl_get_extra("allow_protocol") and (
value.startswith("tcp:") or value.startswith("udp:")
):
value = [value[4:]]
elif self.impl_get_extra("_allow_range") and ":" in str(value):
elif self.impl_get_extra("allow_range") and ":" in str(value):
value = value.split(":")
if len(value) != 2:
raise ValueError(_("range must have two values only"))
@ -102,7 +108,7 @@ class PortOption(StrOption):
raise ValueError()
def second_level_validation(self, value: str, warnings_only: bool) -> None:
if self.impl_get_extra("_allow_protocol") and (
if self.impl_get_extra("allow_protocol") and (
value.startswith("tcp:") or value.startswith("udp:")
):
value = [value[4:]]

View file

@ -38,7 +38,7 @@ class StrOption(Option):
) -> None:
"""validation"""
if not isinstance(value, str):
raise ValueError()
raise ValueError(_('which is not a string'))
class RegexpOption(StrOption):

View file

@ -54,15 +54,16 @@ class URLOption(StrOption):
**kwargs,
) -> None:
# pylint: disable=too-many-arguments,too-many-locals,redefined-builtin
extra = {
"_domainname": DomainnameOption(
extra = {}
extra["_domainname"] = DomainnameOption(
name,
doc,
allow_ip=allow_ip,
type=type,
allow_without_dot=allow_without_dot,
),
"_port": PortOption(
_extra=extra,
)
extra["_port"] = PortOption(
name,
doc,
allow_range=allow_range,
@ -70,8 +71,8 @@ class URLOption(StrOption):
allow_wellknown=allow_wellknown,
allow_registred=allow_registred,
allow_private=allow_private,
),
}
_extra=extra,
)
super().__init__(
name,
doc,
@ -110,10 +111,18 @@ class URLOption(StrOption):
domain, port, files = self._get_domain_port_files(value)
# validate port
portoption = self.impl_get_extra("_port")
portoption.validate(port)
try:
portoption.validate(port)
except ValueError as err:
msg = _('the port "{0}" is invalid: {1}').format(domain, err)
raise ValueError(msg) from err
# validate domainname
domainnameoption = self.impl_get_extra("_domainname")
domainnameoption.validate(domain)
try:
domainnameoption.validate(domain)
except ValueError as err:
msg = _('the domain "{0}" is invalid: {1}').format(domain, err)
raise ValueError(msg) from err
# validate files
if files is not None and files != "" and not self.path_re.search(files):
raise ValueError(_("must ends with a valid resource name"))

View file

@ -94,7 +94,7 @@ EXPIRATION_TIME = 5
# demoting_error_warning
# all value errors are convert to warning (ValueErrorWarning)
DEFAULT_PROPERTIES = frozenset(["cache", "validator", "warnings"])
SPECIAL_PROPERTIES = {"frozen", "mandatory", "empty", "force_store_value"}
SPECIAL_PROPERTIES = {"frozen", "mandatory", "empty", "force_store_value", "validator"}
# Config can be in two defaut mode:
#
@ -149,6 +149,7 @@ FORBIDDEN_SET_PERMISSIVES = frozenset(
"force_default_on_freeze",
"force_metaconfig_on_freeze",
"force_store_value",
"validator",
]
)
ALLOWED_LEADER_PROPERTIES = {
@ -158,6 +159,8 @@ ALLOWED_LEADER_PROPERTIES = {
"unique",
"force_store_value",
"mandatory",
"validator",
"novalidator",
"force_default_on_freeze",
"force_metaconfig_on_freeze",
"frozen",

View file

@ -646,9 +646,9 @@ class TiramisuDict:
if self.remotable == "all" or childapi.has_dependency():
obj_form["remote"] = True
if childtype == "IPOption" and (
child.impl_get_extra("_private_only")
or not child.impl_get_extra("_allow_reserved")
or child.impl_get_extra("_cidr")
child.impl_get_extra("private_only")
or not child.impl_get_extra("allow_reserved")
or child.impl_get_extra("cidr")
):
obj_form["remote"] = True
if childtype == "DateOption":

View file

@ -265,9 +265,10 @@ class Values:
) -> None:
"""set value to option"""
owner = self.get_context_owner()
self_properties = subconfig.properties
setting_properties = subconfig.config_bag.properties
ori_value = value
if "validator" in setting_properties:
if "validator" in setting_properties and "validator" in self_properties:
value, has_calculation = self.setvalue_validation(
subconfig,
value,
@ -294,8 +295,9 @@ class Values:
owners.forced,
)
validator = (
"validator" in setting_properties
and "demoting_error_warning" not in setting_properties
"validator" in setting_properties and
"validator" in self_properties and
"demoting_error_warning" not in setting_properties
)
if validator and not has_calculation:
cache = subconfig.config_bag.context.get_values_cache()
@ -304,7 +306,7 @@ class Values:
value,
validated=validator,
)
elif "validator" in setting_properties and has_calculation:
elif "validator" in setting_properties and "validator" in self_properties and has_calculation:
cache = subconfig.config_bag.context.get_values_cache()
cache.delcache(subconfig.path)
@ -587,35 +589,34 @@ class Values:
"""reset value for an option"""
config_bag = subconfig.config_bag
hasvalue = self.hasvalue(subconfig.path)
self_properties = subconfig.properties
context = config_bag.context
setting_properties = config_bag.properties
if validate:
if hasvalue and "validator" in setting_properties:
fake_context = context.gen_fake_context()
fake_config_bag = config_bag.copy()
fake_config_bag.remove_validation()
fake_config_bag.context = fake_context
fake_subconfig = fake_context.get_sub_config(
fake_config_bag,
subconfig.path,
subconfig.index,
validate_properties=False,
)
fake_values = fake_context.get_values()
fake_values.reset(fake_subconfig)
fake_subconfig.config_bag.properties = setting_properties
value = fake_values.get_default_value(fake_subconfig)
fake_values.setvalue_validation(
fake_subconfig,
value,
)
# if hasvalue:
if validate and hasvalue and "validator" in setting_properties and "validator" in self_properties:
fake_context = context.gen_fake_context()
fake_config_bag = config_bag.copy()
fake_config_bag.remove_validation()
fake_config_bag.context = fake_context
fake_subconfig = fake_context.get_sub_config(
fake_config_bag,
subconfig.path,
subconfig.index,
validate_properties=False,
)
fake_values = fake_context.get_values()
fake_values.reset(fake_subconfig)
fake_subconfig.config_bag.properties = setting_properties
value = fake_values.get_default_value(fake_subconfig)
fake_values.setvalue_validation(
fake_subconfig,
value,
)
opt = subconfig.option
if opt.impl_is_leader():
opt.impl_get_leadership().reset(subconfig.parent)
if (
"force_store_value" in setting_properties
and "force_store_value" in subconfig.properties
and "force_store_value" in self_properties
):
value = self.get_default_value(subconfig)
@ -662,10 +663,11 @@ class Values:
index=subconfig.index,
):
return
self_properties = subconfig.properties
config_bag = subconfig.config_bag
context = config_bag.context
setting_properties = config_bag.properties
if "validator" in setting_properties:
if "validator" in setting_properties and "validator" in self_properties:
fake_context = context.gen_fake_context()
fake_config_bag = config_bag.copy()
fake_config_bag.remove_validation()
@ -686,7 +688,7 @@ class Values:
)
if (
"force_store_value" in setting_properties
and "force_store_value" in subconfig.properties
and "force_store_value" in self_properties
):
value = self.get_default_value(
subconfig,