Compare commits

..

28 commits

Author SHA1 Message Date
c14a34f232 bump: version 5.2.0a7 → 5.2.0a8 2025-05-05 08:44:59 +02:00
db1b8dd48a bump: version 5.2.0a6 → 5.2.0a7 2025-05-05 08:42:04 +02:00
5ed902a372 fix: better error message 2025-05-05 08:39:43 +02:00
c54f7cb775 bump: version 5.2.0a5 → 5.2.0a6 2025-04-30 09:12:27 +02:00
3aaebfa34e fix: update translation 2025-04-29 23:02:13 +02:00
1a8ad3d06e fix: better error message 2025-04-29 22:56:34 +02:00
c24b0642a7 feat: ability to redefine ConfigError message 2025-04-27 14:46:22 +02:00
7ecec88881 fix: do not modify the default_multi attribute 2025-04-21 19:45:14 +02:00
3d8eb2c80d bump: version 5.2.0a4 → 5.2.0a5 2025-04-09 21:16:49 +02:00
3fadb1f6c3 fix: version 2025-04-09 21:16:39 +02:00
65b156c47d bump: version 5.2.0a3 → 5.2.0a4 2025-04-09 08:28:06 +02:00
db82dd6d41 fix: better error message 2025-04-09 08:27:38 +02:00
888ba21551 bump: version 5.2.0a2 → 5.2.0a3 2025-03-19 09:58:14 +01:00
b8899c98b1 fix: better errors message 2025-03-19 09:57:03 +01:00
1b640689b8 bump: version 5.2.0a1 → 5.2.0a2 2025-02-13 22:11:53 +01:00
adf94e6b15 feat: an option could be not validate 2025-02-13 22:11:26 +01:00
2f0e1fcb0c bump: version 5.2.0a0 → 5.2.0a1 2025-02-10 09:11:13 +01:00
6e4b22aea2 feat: can get params value easily 2025-02-10 08:45:21 +01:00
90947b5578 feat: ability to resolve domainname 2025-02-07 08:01:59 +01:00
f33713231e feat: better error messages 2025-02-07 07:46:53 +01:00
b443771c30 bump: version 5.1.1a1 → 5.2.0a0 2025-01-04 17:39:41 +01:00
6181b728ad fix: message error improvment 2025-01-04 17:39:25 +01:00
4cba819e0a feat: return index in error message 2025-01-04 17:39:11 +01:00
42471d42b7 feat: option.type can return the symlink type 2025-01-04 17:36:44 +01:00
0c993eddb0 bump: version 5.1.1a0 → 5.1.1a1 2024-12-11 21:58:17 +01:00
22fdabb6c0 fix: option.get now accept allow_dynoption 2024-12-11 21:57:46 +01:00
46a27e3a5c bump: version 5.1.0 → 5.1.1a0 2024-11-25 09:02:41 +01:00
baf1245c7a fix: dynoption could be optional if identifier is unknown 2024-11-25 09:00:00 +01:00
36 changed files with 952 additions and 606 deletions

View file

@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Tiramisu\n" "Project-Id-Version: Tiramisu\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-05 08:49+0100\n" "POT-Creation-Date: 2025-04-29 23:01+0200\n"
"PO-Revision-Date: \n" "PO-Revision-Date: \n"
"Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n" "Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n"
"Language-Team: Tiramisu's team <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:" msgid "Commands:"
msgstr "Commandes :" msgstr "Commandes :"
#: tiramisu/api.py:111 tiramisu/api.py:1840 #: tiramisu/api.py:111 tiramisu/api.py:1849
msgid "please specify a valid sub function ({0}.{1})" msgid "please specify a valid sub function ({0}.{1})"
msgstr "veuillez spécifier une sous fonction valide ({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})" msgid "please do not specify index ({0}.{1})"
msgstr "veuillez ne pas spécifier d'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:848
msgid "please specify index with a follower option ({0}.{1})" msgid "please specify index with a follower option ({0}.{1})"
msgstr "veuillez spécifier un index avec une option suiveuse ({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}" msgid "please specify a valid sub function ({0}.{1}): {2}"
msgstr "veuillez spécifier une sous fonction valide ({0}.{1}): {2}" msgstr "veuillez spécifier une sous fonction valide ({0}.{1}): {2}"
#: tiramisu/api.py:431 #: tiramisu/api.py:434
msgid "" msgid ""
"the option {0} is not a dynamic option, cannot get identifiers with " "the option {0} is not a dynamic option, cannot get identifiers with "
"only_self parameter to True" "only_self parameter to True"
@ -58,15 +58,15 @@ msgstr ""
"l'option {0} n'est pas une option dynamique, ne peut retrouver l'identifiant " "l'option {0} n'est pas une option dynamique, ne peut retrouver l'identifiant "
"avec le paramètre only_self à True" "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" msgid "cannot get option from a follower symlink without index"
msgstr "ne peut avoir d'option pour une symlink suiveuse sans 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}\"" msgid "cannot add this property: \"{0}\""
msgstr "ne peut pas ajouter cette propriété : \"{0}\"" msgstr "ne peut pas ajouter cette propriété : \"{0}\""
#: tiramisu/api.py:619 #: tiramisu/api.py:622
msgid "" msgid ""
"cannot remove option's property \"{0}\", use permissive instead in option " "cannot remove option's property \"{0}\", use permissive instead in option "
"\"{1}\"" "\"{1}\""
@ -74,11 +74,11 @@ msgstr ""
"ne supprimer la propriété \"{0}\" de l'option, utiliser plutôt permissive " "ne supprimer la propriété \"{0}\" de l'option, utiliser plutôt permissive "
"dans l'option \"{1}\"" "dans l'option \"{1}\""
#: tiramisu/api.py:623 #: tiramisu/api.py:626
msgid "cannot find \"{0}\" in option \"{1}\"" msgid "cannot find \"{0}\" in option \"{1}\""
msgstr "ne peut trouver \"{0}\" dans l'option \"{1}\"" msgstr "ne peut trouver \"{0}\" dans l'option \"{1}\""
#: tiramisu/api.py:628 #: tiramisu/api.py:631
msgid "" msgid ""
"cannot remove option's property \"{0}\", use permissive instead in option " "cannot remove option's property \"{0}\", use permissive instead in option "
"\"{1}\" at index \"{2}\"" "\"{1}\" at index \"{2}\""
@ -86,106 +86,144 @@ msgstr ""
"ne peut supprimer la propriété \"{0}\" de l'option, utilisez plutôt " "ne peut supprimer la propriété \"{0}\" de l'option, utilisez plutôt "
"permissive dans l'option \"{1}\" a l'index \"{2}\"" "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}\"" msgid "cannot find \"{0}\" in option \"{1}\" at index \"{2}\""
msgstr "ne peut trouver \"{0}\" dans l'option \"{1}\" à l'index \"{2}\"" msgstr "ne peut trouver \"{0}\" dans l'option \"{1}\" à l'index \"{2}\""
#: tiramisu/api.py:676 #: tiramisu/api.py:679 tiramisu/option/optiondescription.py:283
msgid "cannot find \"{0}\"" msgid "cannot find \"{0}\""
msgstr "ne peut trouver \"{0}\"" msgstr "ne peut trouver \"{0}\""
#: tiramisu/api.py:808 #: tiramisu/api.py:812
msgid "cannot reduce length of the leader {}" msgid "cannot reduce length of the leader {}"
msgstr "il est impossible de réduire la longueur du leader \"{}\"" msgstr "il est impossible de réduire la longueur du leader \"{}\""
#: tiramisu/api.py:861 #: tiramisu/api.py:865
msgid "only multi value has defaultmulti" msgid "only multi value has defaultmulti"
msgstr "seule les valeurs multiple a l'attribut defaultmulti" msgstr "seule les valeurs multiple a l'attribut defaultmulti"
#: tiramisu/api.py:1020 #: tiramisu/api.py:1029
msgid "please specify a valid sub function ({0}.{1}) for {2}" msgid "please specify a valid sub function ({0}.{1}) for {2}"
msgstr "veuillez spécifier une sous fonction valide ({0}.{1}) pour {2}" msgstr "veuillez spécifier une sous fonction valide ({0}.{1}) pour {2}"
#: tiramisu/api.py:1407 #: tiramisu/api.py:1416
msgid "properties must be a frozenset" msgid "properties must be a frozenset"
msgstr "une propriété doit être de type frozenset" msgstr "une propriété doit être de type frozenset"
#: tiramisu/api.py:1411 tiramisu/api.py:1438 #: tiramisu/api.py:1420 tiramisu/api.py:1447
msgid "unknown when {} (must be in append or remove)" msgid "unknown when {} (must be in append or remove)"
msgstr "value {} inconsistent (doit être append ou remove)" msgstr "value {} inconsistent (doit être append ou remove)"
#: tiramisu/api.py:1424 tiramisu/api.py:1448 tiramisu/config.py:1680 #: tiramisu/api.py:1433 tiramisu/api.py:1457 tiramisu/config.py:1681
msgid "unknown type {}" msgid "unknown type {}"
msgstr "type inconnu {}" msgstr "type inconnu {}"
#: tiramisu/api.py:1812 #: tiramisu/api.py:1821
msgid "do not use unrestraint, nowarnings or forcepermissive together" msgid "do not use unrestraint, nowarnings or forcepermissive together"
msgstr "" msgstr ""
"il ne faut pas utiliser unrestraint, nowarnings ou forcepermissive ensemble" "il ne faut pas utiliser unrestraint, nowarnings ou forcepermissive ensemble"
#: tiramisu/autolib.py:80 #: tiramisu/autolib.py:81
msgid "args in params must be a tuple" msgid "args in params must be a tuple"
msgstr "args dans params doit être un tuple" msgstr "args dans params doit être un tuple"
#: tiramisu/autolib.py:83 tiramisu/autolib.py:88 #: tiramisu/autolib.py:84 tiramisu/autolib.py:89
msgid "arg in params must be a Param" msgid "arg in params must be a Param"
msgstr "arg dans params doit être un Param" msgstr "arg dans params doit être un Param"
#: tiramisu/autolib.py:85 #: tiramisu/autolib.py:86
msgid "kwargs in params must be a dict" msgid "kwargs in params must be a dict"
msgstr "kwargs dans params doit être un dict" msgstr "kwargs dans params doit être un dict"
#: tiramisu/autolib.py:113 #: tiramisu/autolib.py:114
msgid "paramoption needs an option not {}" msgid "paramoption needs an option not {}"
msgstr "paramoption doit être une option pas {0}" msgstr "paramoption doit être une option pas {0}"
#: tiramisu/autolib.py:119 #: tiramisu/autolib.py:120
msgid "param must have a boolean not a {} for notraisepropertyerror" msgid "param must have a boolean not a {} for notraisepropertyerror"
msgstr "param doit avoir un booléan pas un {} pour notraisepropertyerror" msgstr "param doit avoir un booléan pas un {} pour notraisepropertyerror"
#: tiramisu/autolib.py:122 #: tiramisu/autolib.py:123
msgid "param must have a boolean not a {} for raisepropertyerror" msgid "param must have a boolean not a {} for raisepropertyerror"
msgstr "param doit avoir un booléan pas un {} pour raisepropertyerror" msgstr "param doit avoir un booléan pas un {} pour raisepropertyerror"
#: tiramisu/autolib.py:212 #: tiramisu/autolib.py:152
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:156
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:207
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:209
msgid "cannot redefine option in information"
msgstr "on ne peu pas redéfinir une option dans une information"
#: tiramisu/autolib.py:213
msgid "option in ParamInformation cannot be a symlinkoption" msgid "option in ParamInformation cannot be a symlinkoption"
msgstr "l'option dans ParamInformation ne peut pas être un symlinkoption" msgstr "l'option dans ParamInformation ne peut pas être un symlinkoption"
#: tiramisu/autolib.py:215 #: tiramisu/autolib.py:216
msgid "option in ParamInformation cannot be a follower" msgid "option in ParamInformation cannot be a follower"
msgstr "l'option dans ParamInformation ne peut pas être suiveuse" msgstr "l'option dans ParamInformation ne peut pas être suiveuse"
#: tiramisu/autolib.py:218 #: tiramisu/autolib.py:219
msgid "option in ParamInformation cannot be a dynamic option" msgid "option in ParamInformation cannot be a dynamic option"
msgstr "l'option dans ParamInformation ne peut pas être une option dynamique" msgstr "l'option dans ParamInformation ne peut pas être une option dynamique"
#: tiramisu/autolib.py:279 #: tiramisu/autolib.py:280
msgid "first argument ({0}) must be a function" msgid "first argument ({0}) must be a function"
msgstr "le premier argument ({0}à doit être une fonction" msgstr "le premier argument ({0}) doit être une fonction"
#: tiramisu/autolib.py:283 #: tiramisu/autolib.py:284
msgid "help_function ({0}) must be a function" msgid "help_function ({0}) must be a function"
msgstr "help_function ({0}) doit être une fonction" msgstr "help_function ({0}) doit être une fonction"
#: tiramisu/autolib.py:452 tiramisu/autolib.py:514 #: tiramisu/autolib.py:455 tiramisu/autolib.py:503
msgid "unable to carry out a calculation for {}, {}"
msgstr "impossible d'effectuer le calcul pour {}, {}"
#: tiramisu/autolib.py:461 tiramisu/autolib.py:521
msgid "the option {0} is used in a calculation but is invalid ({1})" msgid "the option {0} is used in a calculation but is invalid ({1})"
msgstr "l'option {0} est utilisé dans un calcul mais est invalide ({1})" msgstr "l'option {0} est utilisé dans un calcul mais est invalide ({1})"
#: tiramisu/autolib.py:477 tiramisu/autolib.py:535 tiramisu/autolib.py:584 #: tiramisu/autolib.py:467 tiramisu/autolib.py:515 tiramisu/autolib.py:554
msgid "unable to get value for calculating {0}, {1}" msgid "unable to get value for calculating {0}, {1}"
msgstr "impossible de trouver la valeur pour calculer {0}, {1}" msgstr "impossible de trouver la valeur pour calculer {0}, {1}"
#: tiramisu/autolib.py:601 #: tiramisu/autolib.py:499
msgid "unable to carry out a calculation for {0}, {1}"
msgstr "impossible d'effectuer le calcul pour {0}, {1}"
#: tiramisu/autolib.py:535
msgid "cannot find information for {0}, {1} is a dynamic option"
msgstr "ne peut trouver l'information pour {0}, {1} est une option dynamique"
#: tiramisu/autolib.py:564
msgid "option {0} is not a dynoptiondescription or in a dynoptiondescription" msgid "option {0} is not a dynoptiondescription or in a dynoptiondescription"
msgstr "" msgstr ""
"l'option {0} n'est pas une dynoptiondescription ou n'est pas dans une " "l'option {0} n'est pas une dynoptiondescription ou n'est pas dans une "
"dynoptiondescription" "dynoptiondescription"
#: tiramisu/autolib.py:848 #: tiramisu/autolib.py:650
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:754
msgid "the follower {0} must have index in carry_out_calculation!"
msgstr ""
"la variable suiveuse {0} doit avoir un index dans carry_out_calculation!"
#: tiramisu/autolib.py:826
msgid "" msgid ""
"the \"{}\" function with positional arguments \"{}\" and keyword arguments " "the \"{}\" function with positional arguments \"{}\" and keyword arguments "
"\"{}\" must not return a list (\"{}\") for the follower option {}" "\"{}\" must not return a list (\"{}\") for the follower option {}"
@ -194,7 +232,7 @@ msgstr ""
"nommés \"{}\" ne doit pas retourner une liste (\"{}\") pour l'option " "nommés \"{}\" ne doit pas retourner une liste (\"{}\") pour l'option "
"suiveuse {}" "suiveuse {}"
#: tiramisu/autolib.py:863 #: tiramisu/autolib.py:841
msgid "" msgid ""
"the \"{}\" function must not return a list (\"{}\") for the follower option " "the \"{}\" function must not return a list (\"{}\") for the follower option "
"{}" "{}"
@ -202,72 +240,72 @@ msgstr ""
"la fonction \"{}\" ne doit pas retourner une liste (\"{}\") pour l'option " "la fonction \"{}\" ne doit pas retourner une liste (\"{}\") pour l'option "
"suiveuse {}" "suiveuse {}"
#: tiramisu/autolib.py:904 #: tiramisu/autolib.py:882
msgid "" msgid ""
"unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and " "unexpected error \"{1}\" in function \"{2}\" with arguments \"{3}\" and "
"\"{4}\" for option {2}" "\"{4}\" for option {0}"
msgstr "" msgstr ""
"erreur inattendue \"{0}\" dans la fonction \"{1}\" avec les arguments " "erreur inattendue \"{1}\" dans la fonction \"{2}\" avec les arguments "
"\"{3}\" et \"{4}\" pour l'option {2}" "\"{3}\" et \"{4}\" pour l'option {0}"
#: tiramisu/autolib.py:915 #: tiramisu/autolib.py:891
msgid "unexpected error \"{0}\" in function \"{1}\" for option {2}" msgid "unexpected error \"{1}\" in function \"{2}\" for option {0}"
msgstr "erreur inattendue \"{0}\" dans la fonction \"{1}\" pour l'option {2}" msgstr "erreur inattendue \"{1}\" dans la fonction \"{2}\" pour l'option {0}"
#: tiramisu/config.py:419 #: tiramisu/config.py:420
msgid "" msgid ""
"index \"{0}\" is greater than the leadership length \"{1}\" for option {2}" "index \"{0}\" is greater than the leadership length \"{1}\" for option {2}"
msgstr "" msgstr ""
"l'index \"{0}\" est supérieur à la longueur de la leadership \"{1}\" pour " "l'index \"{0}\" est supérieur à la longueur de la leadership \"{1}\" pour "
"l'option {2}" "l'option {2}"
#: tiramisu/config.py:579 #: tiramisu/config.py:580
msgid "there is no option description for this config (may be GroupConfig)" msgid "there is no option description for this config (may be GroupConfig)"
msgstr "" msgstr ""
"il n'y a pas d'option description trouvé pour cette config (peut être un " "il n'y a pas d'option description trouvé pour cette config (peut être un "
"GroupConfig)" "GroupConfig)"
#: tiramisu/config.py:668 #: tiramisu/config.py:669
msgid "no option found in config with these criteria" msgid "no option found in config with these criteria"
msgstr "aucune option trouvée dans la config avec ces critères" msgstr "aucune option trouvée dans la config avec ces critères"
#: tiramisu/config.py:871 #: tiramisu/config.py:872
msgid "" msgid ""
"the follower option {0} has greater length ({1}) than the leader length ({2})" "the follower option {0} has greater length ({1}) than the leader length ({2})"
msgstr "" msgstr ""
"l'option suiveuse {0} a une longueur supérieur ({1}) à la longueur de " "l'option suiveuse {0} a une longueur supérieur ({1}) à la longueur de "
"l'option leader ({2})" "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" msgid "option description seems to be part of an other config"
msgstr "l'option description semble faire parti d'une autre 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" msgid "parent of {0} not already exists"
msgstr "le parent de {0} n'existe plus" msgstr "le parent de {0} n'existe plus"
#: tiramisu/config.py:1191 #: tiramisu/config.py:1192
msgid "cannot set leadership object has root optiondescription" msgid "cannot set leadership object has root optiondescription"
msgstr "ne peut assigner un objet leadership comme optiondescription racine" 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" msgid "cannot set dynoptiondescription object has root optiondescription"
msgstr "" msgstr ""
"ne peut assigner un objet dynoptiondescription comme optiondescription racine" "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}\"" msgid "config name must be uniq in groupconfig for \"{0}\""
msgstr "le nom d'un config doit être unique dans un groupconfig pour \"{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 \"{}\"" msgid "unknown config \"{}\""
msgstr "config \"{}\" inconnue" msgstr "config \"{}\" inconnue"
#: tiramisu/config.py:1482 #: tiramisu/config.py:1483
msgid "child must be a Config, MixConfig or MetaConfig" msgid "child must be a Config, MixConfig or MetaConfig"
msgstr "l'enfant doit être une Config, MixConfig ou MetaConfig" msgstr "l'enfant doit être une Config, MixConfig ou MetaConfig"
#: tiramisu/config.py:1517 #: tiramisu/config.py:1518
msgid "" msgid ""
"force_default, force_default_if_same or force_dont_change_value cannot be " "force_default, force_default_if_same or force_dont_change_value cannot be "
"set with only_config" "set with only_config"
@ -275,43 +313,43 @@ msgstr ""
"force_default, force_default_if_same ou force_dont_change_value ne peuvent " "force_default, force_default_if_same ou force_dont_change_value ne peuvent "
"pas être spécifié avec only_config" "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" msgid "force_default and force_dont_change_value cannot be set together"
msgstr "" msgstr ""
"force_default et force_dont_change_value ne peuvent pas être mis ensemble" "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}" msgid "config name must be uniq in groupconfig for {0}"
msgstr "le nom de la config doit être unique dans un groupconfig pour {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" msgid "config added has no name, the name is mandatory"
msgstr "la config ajoutée n'a pas de nom, le nom est obligatoire" 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}\"" msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\""
msgstr "" msgstr ""
"le nom d'un config \"{0}\" n'est pas unique dans le groupconfig \"{1}\"" "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}" msgid "cannot find the config {0}"
msgstr "ne peut pas trouver la 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 {}" msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr "" msgstr ""
"MetaConfig avec une optiondescription doit avoir un nom comme enfant, pas {}" "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" msgid "child must be a Config or MetaConfig"
msgstr "enfant doit être une une Config ou une 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" msgid "all config in metaconfig must have the same optiondescription"
msgstr "" msgstr ""
"toutes les configs d'une metaconfig doivent avoir la même optiondescription" "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" msgid "metaconfig must have the same optiondescription"
msgstr "metaconfig doivent avoir la même optiondescription" msgstr "metaconfig doivent avoir la même optiondescription"
@ -344,24 +382,41 @@ msgid "cannot modify the {0} {1} because has {2} {3}"
msgstr "ne peut modifier {0} \"{1}\" à cause {2} {3}" msgstr "ne peut modifier {0} \"{1}\" à cause {2} {3}"
#: tiramisu/error.py:118 #: tiramisu/error.py:118
msgid "cannot access to {0} {1} because \"{2}\" hasn't value"
msgstr "ne peut accéder à {0} {1} parce que \"{2}\" n'a pas de valeur"
#: tiramisu/error.py:120
msgid "{0} {1} is mandatory but hasn't value"
msgstr "{0} {1} est obligatoire mais n'a pas de valeur"
#: tiramisu/error.py:123
msgid "cannot access to {0} {1} because \"{2}\" has {3} {4}" msgid "cannot access to {0} {1} because \"{2}\" has {3} {4}"
msgstr "ne peut accéder à {0} {1} parce que \"{2}\" a {3} {4}" msgstr "ne peut accéder à {0} {1} parce que \"{2}\" a {3} {4}"
#: tiramisu/error.py:120 #: tiramisu/error.py:125
msgid "cannot access to {0} {1} because has {2} {3}" msgid "cannot access to {0} {1} because has {2} {3}"
msgstr "ne peut accéder à l'{0} {1} à cause {2} {3}" msgstr "ne peut accéder à l'{0} {1} à cause {2} {3}"
#: tiramisu/error.py:192 #: tiramisu/error.py:197
msgid "invalid value" msgid "invalid value"
msgstr "valeur invalide" msgstr "valeur invalide"
#: tiramisu/error.py:201 #: tiramisu/error.py:207
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\"" msgid "attention, \"{0}\" could be an invalid {1} for {2}"
msgstr "attention, \"{0}\" peut être un {1} invalide pour \"{2}\"" msgstr "attention, \"{0}\" peut être un {1} invalide pour {2}"
#: tiramisu/error.py:219 tiramisu/error.py:228 #: tiramisu/error.py:209
msgid "\"{0}\" is an invalid {1} for \"{2}\"" msgid "attention, \"{0}\" could be an invalid {1} for {2} at index \"{3}\""
msgstr "\"{0}\" est une valeur invalide pour l'option \"{2}\" de type {1}" msgstr ""
"attention, \"{0}\" peut être un {1} invalide pour {2} à l'index \"{3}\""
#: tiramisu/error.py:228 tiramisu/error.py:239
msgid "\"{0}\" is an invalid {1} for {2}"
msgstr "\"{0}\" est une valeur invalide pour l'option {2} de type {1}"
#: tiramisu/error.py:230
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 #: tiramisu/function.py:65
msgid "network \"{0}\" ({1}) does not match with this netmask" msgid "network \"{0}\" ({1}) does not match with this netmask"
@ -442,25 +497,25 @@ msgstr "\"{0}\" est un nom invalide pour une option"
msgid "invalid properties type {0} for {1}, must be a frozenset" msgid "invalid properties type {0} for {1}, must be a frozenset"
msgstr "type {0} invalide pour des propriétés pour {1}, doit être un frozenset" msgstr "type {0} invalide pour des propriétés pour {1}, doit être un frozenset"
#: tiramisu/option/baseoption.py:98 #: tiramisu/option/baseoption.py:100
msgid "invalid property type {0} for {1}, must be a string or a Calculation" msgid "invalid property type {0} for {1}, must be a string or a Calculation"
msgstr "" msgstr ""
"type {0} invalide pour des propriétés pour {1}, doit être des caractères ou " "type {0} invalide pour des propriétés pour {1}, doit être des caractères ou "
"un objet Calculation" "un objet Calculation"
#: tiramisu/option/baseoption.py:249 #: tiramisu/option/baseoption.py:251
msgid "information's item for {0} not found: \"{1}\"" msgid "information's item for {0} not found: \"{1}\""
msgstr "item pour {0} dans les informations non trouvée: \"{1}\"" msgstr "item pour {0} dans les informations non trouvée: \"{1}\""
#: tiramisu/option/baseoption.py:267 #: tiramisu/option/baseoption.py:269
msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule" msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule"
#: tiramisu/option/baseoption.py:308 #: tiramisu/option/baseoption.py:310
msgid "\"{}\" ({}) object attribute \"{}\" is read-only" msgid "\"{}\" ({}) object attribute \"{}\" is read-only"
msgstr "\"{}\" ({}) l'attribut de l'objet \"{}\" est en lecture seule" msgstr "\"{}\" ({}) l'attribut de l'objet \"{}\" est en lecture seule"
#: tiramisu/option/baseoption.py:320 #: tiramisu/option/baseoption.py:322
msgid "{0} not part of any Config" msgid "{0} not part of any Config"
msgstr "{0} ne fait pas parti d'une Config" msgstr "{0} ne fait pas parti d'une Config"
@ -484,27 +539,27 @@ msgstr "seul \"{0}\" est autorisé"
msgid "only {0} are allowed" msgid "only {0} are allowed"
msgstr "seul {0} sont autorisées" msgstr "seul {0} sont autorisées"
#: tiramisu/option/domainnameoption.py:60 #: tiramisu/option/domainnameoption.py:67
msgid "unknown type {0} for hostname" msgid "unknown type {0} for hostname"
msgstr "type_ inconnu {0} pour le nom d'hôte" msgstr "type_ inconnu {0} pour le nom d'hôte"
#: tiramisu/option/domainnameoption.py:63 #: tiramisu/option/domainnameoption.py:70
msgid "allow_ip must be a boolean" msgid "allow_ip must be a boolean"
msgstr "allow_ip doit être un booléen" msgstr "allow_ip doit être un booléen"
#: tiramisu/option/domainnameoption.py:65 #: tiramisu/option/domainnameoption.py:72
msgid "allow_cidr_network must be a boolean" msgid "allow_cidr_network must be a boolean"
msgstr "allow_cidr_network doit être un booléen" msgstr "allow_cidr_network doit être un booléen"
#: tiramisu/option/domainnameoption.py:67 #: tiramisu/option/domainnameoption.py:74
msgid "allow_without_dot must be a boolean" msgid "allow_without_dot must be a boolean"
msgstr "allow_without_dot doit être un booléen" msgstr "allow_without_dot doit être un booléen"
#: tiramisu/option/domainnameoption.py:69 #: tiramisu/option/domainnameoption.py:76
msgid "allow_startswith_dot must be a boolean" msgid "allow_startswith_dot must be a boolean"
msgstr "allow_startswith_dot doit être un booléen" msgstr "allow_startswith_dot doit être un booléen"
#: tiramisu/option/domainnameoption.py:81 #: tiramisu/option/domainnameoption.py:87
msgid "" msgid ""
"must start with lowercase characters followed by lowercase characters, " "must start with lowercase characters followed by lowercase characters, "
"number, \"-\" and \".\" characters are allowed" "number, \"-\" and \".\" characters are allowed"
@ -512,7 +567,7 @@ msgstr ""
"doit démarrer par un caractère en minuscule suivi par des caractères en " "doit démarrer par un caractère en minuscule suivi par des caractères en "
"minuscule, des nombres, \"-\" et \".\" sont autorisés" "minuscule, des nombres, \"-\" et \".\" sont autorisés"
#: tiramisu/option/domainnameoption.py:84 #: tiramisu/option/domainnameoption.py:90
msgid "" msgid ""
"must start with lowercase characters followed by lowercase characters, " "must start with lowercase characters followed by lowercase characters, "
"number, \"-\" and \".\" characters are recommanded" "number, \"-\" and \".\" characters are recommanded"
@ -520,36 +575,60 @@ msgstr ""
"doit démarrer par un caractère en minuscule suivi par des caractères en " "doit démarrer par un caractère en minuscule suivi par des caractères en "
"minuscule, des nombres, \"-\" et \".\" sont recommandés" "minuscule, des nombres, \"-\" et \".\" sont recommandés"
#: tiramisu/option/domainnameoption.py:88 #: tiramisu/option/domainnameoption.py:95
#: tiramisu/option/domainnameoption.py:89 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:98
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:102
#: tiramisu/option/domainnameoption.py:103
msgid "could be a IP, otherwise {}" msgid "could be a IP, otherwise {}"
msgstr "peut être une IP, autrement {}" msgstr "peut être une IP, autrement {}"
#: tiramisu/option/domainnameoption.py:134 #: tiramisu/option/domainnameoption.py:148
msgid "invalid length (min 1)" msgid "invalid length (min 1)"
msgstr "longueur invalide (min 1)" msgstr "longueur invalide (min 1)"
#: tiramisu/option/domainnameoption.py:137 #: tiramisu/option/domainnameoption.py:151
msgid "invalid length (max {0})" msgid "invalid length (max {0})"
msgstr "longueur invalide (max {0})" msgstr "longueur invalide (max {0})"
#: tiramisu/option/domainnameoption.py:143 #: tiramisu/option/domainnameoption.py:157
msgid "must have dot" msgid "must have dot"
msgstr "doit avoir un point" msgstr "doit avoir un point"
#: tiramisu/option/domainnameoption.py:145 #: tiramisu/option/domainnameoption.py:159
msgid "invalid length (max 255)" msgid "invalid length (max 255)"
msgstr "longueur invalide (max 255)" msgstr "longueur invalide (max 255)"
#: tiramisu/option/domainnameoption.py:163 #: tiramisu/option/domainnameoption.py:180
msgid "DNS resolution failed"
msgstr "la résolution DNS a échoué"
#: tiramisu/option/domainnameoption.py:184
msgid "error resolving DNS: {1}"
msgstr "erreur de résolution DNS : {1}"
#: tiramisu/option/domainnameoption.py:191
msgid "must not be an IP" msgid "must not be an IP"
msgstr "ne doit pas être une IP" msgstr "ne doit pas être une IP"
#: tiramisu/option/domainnameoption.py:186 #: tiramisu/option/domainnameoption.py:214
msgid "some characters are uppercase" msgid "some characters are uppercase"
msgstr "des caractères sont en majuscule" msgstr "des caractères sont en majuscule"
#: tiramisu/option/dynoptiondescription.py:131 #: tiramisu/option/dynoptiondescription.py:130
msgid "DynOptionDescription identifiers for option {0}, is not a list ({1})" msgid "DynOptionDescription identifiers for option {0}, is not a list ({1})"
msgstr "" msgstr ""
"les identifiants de la DynOptionDescription pour l'option {0}, n'est pas une " "les identifiants de la DynOptionDescription pour l'option {0}, n'est pas une "
@ -576,9 +655,21 @@ msgstr ""
msgid "must starts with \"/\"" msgid "must starts with \"/\""
msgstr "doit débuter par \"/\"" msgstr "doit débuter par \"/\""
#: tiramisu/option/filenameoption.py:78 #: tiramisu/option/filenameoption.py:77
msgid "cannot find {0} \"{1}\"" msgid "directory"
msgstr "ne peut pas trouver {0} \"{1}\"" 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:46
msgid "which is not an integer"
msgstr "qui n'est pas un chiffre"
#: tiramisu/option/intoption.py:52 #: tiramisu/option/intoption.py:52
msgid "value should be equal or greater than \"{0}\"" msgid "value should be equal or greater than \"{0}\""
@ -648,10 +739,10 @@ msgstr "une option leadership \"{0}\" ne devrait pas avoir de sous-groupe"
#: tiramisu/option/leadership.py:114 #: tiramisu/option/leadership.py:114
msgid "" msgid ""
"only multi option allowed in leadership {0} but option {1} is not a multi" "only multi option are allowed in leadership {0} but option {1} is not a multi"
msgstr "" msgstr ""
"seules des options multiples sont autorisées dans l'option leadership " "seules des options multiples sont autorisées dans l'option leadership {0} "
"\"{0}\" alors que l'option \"{1}\" n'est pas une option multiple" "alors que l'option {1} n'est pas une option multiple"
#: tiramisu/option/leadership.py:141 #: tiramisu/option/leadership.py:141
msgid "not allowed default value for follower option {0} in leadership {1}" msgid "not allowed default value for follower option {0} in leadership {1}"
@ -688,15 +779,15 @@ msgstr "validators doit être une liste de Calculation pour \"{0}\""
msgid "validators must be a Calculation for \"{0}\"" msgid "validators must be a Calculation for \"{0}\""
msgstr "validators doit être un Calculation pour \"{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}" msgid "invalid default_multi value \"{0}\" for option {1}"
msgstr "la valeur default_multi est invalide {0} pour l'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}" msgid "invalid default_multi value \"{0}\" for option {1}, {2}"
msgstr "la valeur default_multi est invalide \"{0}\" pour l'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 "" msgid ""
"invalid default_multi value \"{0}\" for option {1}, must be a list for a " "invalid default_multi value \"{0}\" for option {1}, must be a list for a "
"submulti" "submulti"
@ -704,19 +795,19 @@ msgstr ""
"valeur invalide pour default_multi \"{0}\" pour l'option {1}, doit être une " "valeur invalide pour default_multi \"{0}\" pour l'option {1}, doit être une "
"liste pour une submulti" "liste pour une submulti"
#: tiramisu/option/option.py:290 #: tiramisu/option/option.py:291
msgid "the value \"{}\" is not unique" msgid "the value \"{}\" is not unique"
msgstr "la valeur de \"{}\" n'est pas unique" msgstr "la valeur de \"{}\" n'est pas unique"
#: tiramisu/option/option.py:352 #: tiramisu/option/option.py:353
msgid "which must not be a list" msgid "which must not be a list"
msgstr "qui ne doit pas être une liste" msgstr "qui ne doit pas être une liste"
#: tiramisu/option/option.py:404 tiramisu/option/option.py:430 #: tiramisu/option/option.py:405 tiramisu/option/option.py:431
msgid "which must be a list" msgid "which must be a list"
msgstr "qui doit être une liste" msgstr "qui doit être une liste"
#: tiramisu/option/option.py:424 #: tiramisu/option/option.py:425
msgid "which \"{}\" must be a list of list" msgid "which \"{}\" must be a list of list"
msgstr "lequel \"{}\" doit être une liste de liste" msgstr "lequel \"{}\" doit être une liste de liste"
@ -724,41 +815,34 @@ msgstr "lequel \"{}\" doit être une liste de liste"
msgid "duplicate option: {0}" msgid "duplicate option: {0}"
msgstr "option dupliquée : {0}" msgstr "option dupliquée : {0}"
#: tiramisu/option/optiondescription.py:244 #: tiramisu/option/optiondescription.py:249
msgid "" msgid "cannot access to \"{0}\" it's a dynamic option"
"unknown option \"{0}\" in root optiondescription (it's a dynamic option)" msgstr "ne peut accéder à \"{0}\" c'est une option dynamique"
msgstr ""
"option \"{0}\" inconnue dans l'optiondescription racine (c'est une option "
"dynamique)"
#: tiramisu/option/optiondescription.py:279 #: tiramisu/option/optiondescription.py:286
msgid "unknown option \"{0}\" in root optiondescription" msgid "cannot find \"{0}\" in \"{1}\""
msgstr "option \"{0}\" inconnue dans l'optiondescription racine" msgstr "ne peut pas trouver \"{0}\" dans \"{1}\""
#: tiramisu/option/optiondescription.py:282 #: tiramisu/option/optiondescription.py:342
msgid "unknown option \"{0}\" in optiondescription {1}"
msgstr "option \"{0}\" inconnue dans l'optiondescription {1}"
#: tiramisu/option/optiondescription.py:338
msgid "children in optiondescription \"{}\" must be a list" msgid "children in optiondescription \"{}\" must be a list"
msgstr "les enfants d'une optiondescription \"{}\" doivent être une liste" msgstr "les enfants d'une optiondescription \"{}\" doivent être une liste"
#: tiramisu/option/optiondescription.py:366 #: tiramisu/option/optiondescription.py:370
msgid "duplicate option name: \"{0}\"" msgid "duplicate option name: \"{0}\""
msgstr "nom de l'option dupliqué : \"{0}\"" msgstr "nom de l'option dupliqué : \"{0}\""
#: tiramisu/option/optiondescription.py:372 #: tiramisu/option/optiondescription.py:376
msgid "" msgid ""
"the option's name \"{0}\" start as the dynoptiondescription's name \"{1}\"" "the option's name \"{0}\" start as the dynoptiondescription's name \"{1}\""
msgstr "" msgstr ""
"le nom de l'option \"{0}\" commence comme le nom du dynoptiondescription " "le nom de l'option \"{0}\" commence comme le nom du dynoptiondescription "
"\"{1}\"" "\"{1}\""
#: tiramisu/option/optiondescription.py:415 #: tiramisu/option/optiondescription.py:419
msgid "cannot change group_type if already set (old {0}, new {1})" 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})" msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})"
#: tiramisu/option/optiondescription.py:420 #: tiramisu/option/optiondescription.py:424
msgid "group_type: {0} not allowed" msgid "group_type: {0} not allowed"
msgstr "group_type : {0} non autorisé" msgstr "group_type : {0} non autorisé"
@ -799,30 +883,34 @@ msgstr "{0} a plus de droit que {1}"
msgid "too weak" msgid "too weak"
msgstr "trop simple" msgstr "trop simple"
#: tiramisu/option/portoption.py:74 #: tiramisu/option/portoption.py:80
msgid "inconsistency in allowed range" msgid "inconsistency in allowed range"
msgstr "inconsistence dans la plage autorisée" msgstr "inconsistence dans la plage autorisée"
#: tiramisu/option/portoption.py:79 #: tiramisu/option/portoption.py:85
msgid "max value is empty" msgid "max value is empty"
msgstr "la valeur maximum est vide" msgstr "la valeur maximum est vide"
#: tiramisu/option/portoption.py:92 #: tiramisu/option/portoption.py:98
msgid "range must have two values only" msgid "range must have two values only"
msgstr "un rang doit avoir deux valeurs seulement" msgstr "un rang doit avoir deux valeurs seulement"
#: tiramisu/option/portoption.py:95 #: tiramisu/option/portoption.py:101
msgid "first port in range must be smaller than the second one" msgid "first port in range must be smaller than the second one"
msgstr "le premier port d'un rang doit être plus petit que le second" msgstr "le premier port d'un rang doit être plus petit que le second"
#: tiramisu/option/portoption.py:121 #: tiramisu/option/portoption.py:127
msgid "should be between {0} and {1}" msgid "should be between {0} and {1}"
msgstr "devrait être une nombre entre {0} et {1}" msgstr "devrait être une nombre entre {0} et {1}"
#: tiramisu/option/portoption.py:123 #: tiramisu/option/portoption.py:129
msgid "must be between {0} and {1}" msgid "must be between {0} and {1}"
msgstr "doit être une nombre entre {0} et {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 #: tiramisu/option/symlinkoption.py:51
msgid "" msgid ""
"malformed symlink second parameters must be an option for \"{0}\", not {1}" "malformed symlink second parameters must be an option for \"{0}\", not {1}"
@ -830,23 +918,31 @@ msgstr ""
"symlink mal formé, le second paramètre doit être une option pour \"{0}\", " "symlink mal formé, le second paramètre doit être une option pour \"{0}\", "
"not {1}" "not {1}"
#: tiramisu/option/urloption.py:91 #: tiramisu/option/urloption.py:92
msgid "must start with http:// or https://" msgid "must start with http:// or https://"
msgstr "doit débuter par http:// ou https://" msgstr "doit débuter par http:// ou https://"
#: tiramisu/option/urloption.py:119 #: tiramisu/option/urloption.py:117
msgid "the port \"{0}\" is invalid: {1}"
msgstr "le port \"{0}\" est invalide : {1}"
#: tiramisu/option/urloption.py:124
msgid "the domain \"{0}\" is invalid: {1}"
msgstr "le domaine \"{0}\" est invalide : {1}"
#: tiramisu/option/urloption.py:128
msgid "must ends with a valid resource name" msgid "must ends with a valid resource name"
msgstr "doit finir par un nom de ressource valide" msgstr "doit finir par un nom de ressource valide"
#: tiramisu/setting.py:255 #: tiramisu/setting.py:258
msgid "can't rebind {0}" msgid "can't rebind {0}"
msgstr "ne peut redéfinir ({0})" msgstr "ne peut redéfinir ({0})"
#: tiramisu/setting.py:262 #: tiramisu/setting.py:265
msgid "can't unbind {0}" msgid "can't unbind {0}"
msgstr "ne peut supprimer ({0})" msgstr "ne peut supprimer ({0})"
#: tiramisu/setting.py:464 #: tiramisu/setting.py:467
msgid "" msgid ""
"invalid property type {type(new_prop)} for {subconfig.option.impl_getname()} " "invalid property type {type(new_prop)} for {subconfig.option.impl_getname()} "
"with {prop.function.__name__} function" "with {prop.function.__name__} function"
@ -854,15 +950,15 @@ msgstr ""
"type {type(new_prop)} de la propriété invalide pour la fonction {subconfig." "type {type(new_prop)} de la propriété invalide pour la fonction {subconfig."
"option.impl_getname()} with {prop.function.__name__}" "option.impl_getname()} with {prop.function.__name__}"
#: tiramisu/setting.py:476 #: tiramisu/setting.py:479
msgid "leader cannot have \"{new_prop}\" property" msgid "leader cannot have \"{new_prop}\" property"
msgstr "une option leader ne peu avoir la propriété \"{new_prop}\"" msgstr "une option leader ne peu avoir la propriété \"{new_prop}\""
#: tiramisu/setting.py:564 #: tiramisu/setting.py:567
msgid "leader cannot have \"{0}\" property" msgid "leader cannot have \"{0}\" property"
msgstr "leader ne peut avoir la propriété \"{0}\"" msgstr "leader ne peut avoir la propriété \"{0}\""
#: tiramisu/setting.py:573 #: tiramisu/setting.py:576
msgid "" msgid ""
"a leader ({0}) cannot have \"force_default_on_freeze\" or " "a leader ({0}) cannot have \"force_default_on_freeze\" or "
"\"force_metaconfig_on_freeze\" property without \"frozen\"" "\"force_metaconfig_on_freeze\" property without \"frozen\""
@ -870,19 +966,19 @@ msgstr ""
"une option leader ({0}) ne peut avoir de propriété " "une option leader ({0}) ne peut avoir de propriété "
"\"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" sans \"frozen\"" "\"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" sans \"frozen\""
#: tiramisu/setting.py:607 #: tiramisu/setting.py:610
msgid "permissive must be a frozenset" msgid "permissive must be a frozenset"
msgstr "une permissive doit être de type frozenset" msgstr "une permissive doit être de type frozenset"
#: tiramisu/setting.py:617 #: tiramisu/setting.py:620
msgid "cannot add those permissives: {0}" msgid "cannot add those permissives: {0}"
msgstr "ne peut ajouter ces permissives : {0}" msgstr "ne peut ajouter ces permissives : {0}"
#: tiramisu/setting.py:654 #: tiramisu/setting.py:657
msgid "can't reset properties to the symlinkoption \"{}\"" msgid "can't reset properties to the symlinkoption \"{}\""
msgstr "ne peut réinitialiser les propriétés de la symlinkoption \"{}\"" msgstr "ne peut réinitialiser les propriétés de la symlinkoption \"{}\""
#: tiramisu/setting.py:667 #: tiramisu/setting.py:670
msgid "can't reset permissives to the symlinkoption \"{}\"" msgid "can't reset permissives to the symlinkoption \"{}\""
msgstr "ne peut réinitialiser les permissive de la symlinkoption \"{}\"" msgstr "ne peut réinitialiser les permissive de la symlinkoption \"{}\""
@ -912,17 +1008,17 @@ msgstr ""
msgid "unknown action {}" msgid "unknown action {}"
msgstr "action inconnue {}" msgstr "action inconnue {}"
#: tiramisu/value.py:564 tiramisu/value.py:861 #: tiramisu/value.py:566 tiramisu/value.py:863
msgid "set owner \"{0}\" is forbidden" msgid "set owner \"{0}\" is forbidden"
msgstr "assigner l'utilisateur \"{0}\" est interdit" msgstr "assigner l'utilisateur \"{0}\" est interdit"
#: tiramisu/value.py:571 #: tiramisu/value.py:573
msgid "\"{0}\" is a default value, so we cannot change owner to \"{1}\"" msgid "\"{0}\" is a default value, so we cannot change owner to \"{1}\""
msgstr "" msgstr ""
"\"{0}\" est une valeur par défaut, donc ne peut changer d'utilisateur à " "\"{0}\" est une valeur par défaut, donc ne peut changer d'utilisateur à "
"\"{1}\"" "\"{1}\""
#: tiramisu/value.py:740 #: tiramisu/value.py:742
msgid "" msgid ""
"index {index} is greater than the length {length} for option {subconfig." "index {index} is greater than the length {length} for option {subconfig."
"option.impl_get_display_name(with_quote=True)}" "option.impl_get_display_name(with_quote=True)}"
@ -930,10 +1026,25 @@ msgstr ""
"l'index {index} est supérieur à la longueur \"{length}\" pour l'option " "l'index {index} est supérieur à la longueur \"{length}\" pour l'option "
"\"{subconfig.option.impl_get_display_name(with_quote=True)}\"" "\"{subconfig.option.impl_get_display_name(with_quote=True)}\""
#: tiramisu/value.py:847 #: tiramisu/value.py:849
msgid "information's item not found \"{}\"" msgid "information's item not found \"{}\""
msgstr "l'information de l'objet ne sont pas trouvé \"{}\"" msgstr "l'information de l'objet ne sont pas trouvé \"{}\""
#~ msgid "unable to unable to get value for calculating {0}, {1}"
#~ msgstr "impossible de trouver la valeur pour calculer {0}, {1}"
#~ msgid ""
#~ "unknown option \"{0}\" in root optiondescription (it's a dynamic option)"
#~ msgstr ""
#~ "option \"{0}\" inconnue dans l'optiondescription racine (c'est une option "
#~ "dynamique)"
#~ msgid "unknown option \"{0}\" in root optiondescription"
#~ msgstr "option \"{0}\" inconnue dans l'optiondescription racine"
#~ msgid "unknown option \"{0}\" in optiondescription {1}"
#~ msgstr "option \"{0}\" inconnue dans l'optiondescription {1}"
#~ msgid "" #~ msgid ""
#~ "IP \"{ip[\"value\"]}\" ({ip[\"name\"]}) with this netmask is in fact a " #~ "IP \"{ip[\"value\"]}\" ({ip[\"name\"]}) with this netmask is in fact a "
#~ "broacast address" #~ "broacast address"
@ -1107,18 +1218,12 @@ msgstr "l'information de l'objet ne sont pas trouvé \"{}\""
#~ msgstr "" #~ msgstr ""
#~ "seuls les caractères en minuscule, les nombres et \"-\" sont recommandés" #~ "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 \"{}\"" #~ msgid "callback is mandatory for the dynoptiondescription \"{}\""
#~ msgstr "un callback est obligatoire pour le dynoptiondescription \"{}\"" #~ msgstr "un callback est obligatoire pour le dynoptiondescription \"{}\""
#~ msgid "email address" #~ msgid "email address"
#~ msgstr "adresse mail" #~ msgstr "adresse mail"
#~ msgid "file name"
#~ msgstr "nom de fichier"
#~ msgid "float" #~ msgid "float"
#~ msgstr "nombre flottant" #~ msgstr "nombre flottant"
@ -1191,9 +1296,6 @@ msgstr "l'information de l'objet ne sont pas trouvé \"{}\""
#~ msgid "IP \"{0}\" (\"{1}\") is the broadcast" #~ msgid "IP \"{0}\" (\"{1}\") is the broadcast"
#~ msgstr "IP \"{0}\" (\"{1}\") est l'adresse de 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" #~ msgid "unique must be set only with multi value"
#~ msgstr "unique doit être activé uniquement avec une valeur multiple" #~ msgstr "unique doit être activé uniquement avec une valeur multiple"

View file

@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-11-05 08:52+0100\n" "POT-Creation-Date: 2025-04-29 23:01+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -35,239 +35,267 @@ msgstr ""
msgid "Commands:" msgid "Commands:"
msgstr "" msgstr ""
#: tiramisu/api.py:111 tiramisu/api.py:1840 #: tiramisu/api.py:111 tiramisu/api.py:1849
msgid "please specify a valid sub function ({0}.{1})" msgid "please specify a valid sub function ({0}.{1})"
msgstr "" msgstr ""
#: tiramisu/api.py:194 #: tiramisu/api.py:196
msgid "please do not specify index ({0}.{1})" msgid "please do not specify index ({0}.{1})"
msgstr "" msgstr ""
#: tiramisu/api.py:199 tiramisu/api.py:844 #: tiramisu/api.py:201 tiramisu/api.py:848
msgid "please specify index with a follower option ({0}.{1})" msgid "please specify index with a follower option ({0}.{1})"
msgstr "" msgstr ""
#: tiramisu/api.py:220 #: tiramisu/api.py:222
msgid "please specify a valid sub function ({0}.{1}): {2}" msgid "please specify a valid sub function ({0}.{1}): {2}"
msgstr "" 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" msgid "the option {0} is not a dynamic option, cannot get identifiers with only_self parameter to True"
msgstr "" msgstr ""
#: tiramisu/api.py:517 #: tiramisu/api.py:520
msgid "cannot get option from a follower symlink without index" msgid "cannot get option from a follower symlink without index"
msgstr "" msgstr ""
#: tiramisu/api.py:592 #: tiramisu/api.py:595
msgid "cannot add this property: \"{0}\"" msgid "cannot add this property: \"{0}\""
msgstr "" msgstr ""
#: tiramisu/api.py:619 #: tiramisu/api.py:622
msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\"" msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\""
msgstr "" msgstr ""
#: tiramisu/api.py:623 #: tiramisu/api.py:626
msgid "cannot find \"{0}\" in option \"{1}\"" msgid "cannot find \"{0}\" in option \"{1}\""
msgstr "" 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}\"" msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\" at index \"{2}\""
msgstr "" msgstr ""
#: tiramisu/api.py:632 #: tiramisu/api.py:635
msgid "cannot find \"{0}\" in option \"{1}\" at index \"{2}\"" msgid "cannot find \"{0}\" in option \"{1}\" at index \"{2}\""
msgstr "" msgstr ""
#: tiramisu/api.py:676 #: tiramisu/api.py:679 tiramisu/option/optiondescription.py:283
msgid "cannot find \"{0}\"" msgid "cannot find \"{0}\""
msgstr "" msgstr ""
#: tiramisu/api.py:808 #: tiramisu/api.py:812
msgid "cannot reduce length of the leader {}" msgid "cannot reduce length of the leader {}"
msgstr "" msgstr ""
#: tiramisu/api.py:861 #: tiramisu/api.py:865
msgid "only multi value has defaultmulti" msgid "only multi value has defaultmulti"
msgstr "" msgstr ""
#: tiramisu/api.py:1020 #: tiramisu/api.py:1029
msgid "please specify a valid sub function ({0}.{1}) for {2}" msgid "please specify a valid sub function ({0}.{1}) for {2}"
msgstr "" msgstr ""
#: tiramisu/api.py:1407 #: tiramisu/api.py:1416
msgid "properties must be a frozenset" msgid "properties must be a frozenset"
msgstr "" msgstr ""
#: tiramisu/api.py:1411 tiramisu/api.py:1438 #: tiramisu/api.py:1420 tiramisu/api.py:1447
msgid "unknown when {} (must be in append or remove)" msgid "unknown when {} (must be in append or remove)"
msgstr "" msgstr ""
#: tiramisu/api.py:1424 tiramisu/api.py:1448 tiramisu/config.py:1680 #: tiramisu/api.py:1433 tiramisu/api.py:1457 tiramisu/config.py:1681
msgid "unknown type {}" msgid "unknown type {}"
msgstr "" msgstr ""
#: tiramisu/api.py:1812 #: tiramisu/api.py:1821
msgid "do not use unrestraint, nowarnings or forcepermissive together" msgid "do not use unrestraint, nowarnings or forcepermissive together"
msgstr "" msgstr ""
#: tiramisu/autolib.py:80 #: tiramisu/autolib.py:81
msgid "args in params must be a tuple" msgid "args in params must be a tuple"
msgstr "" msgstr ""
#: tiramisu/autolib.py:83 tiramisu/autolib.py:88 #: tiramisu/autolib.py:84 tiramisu/autolib.py:89
msgid "arg in params must be a Param" msgid "arg in params must be a Param"
msgstr "" msgstr ""
#: tiramisu/autolib.py:85 #: tiramisu/autolib.py:86
msgid "kwargs in params must be a dict" msgid "kwargs in params must be a dict"
msgstr "" msgstr ""
#: tiramisu/autolib.py:113 #: tiramisu/autolib.py:114
msgid "paramoption needs an option not {}" msgid "paramoption needs an option not {}"
msgstr "" msgstr ""
#: tiramisu/autolib.py:119 #: tiramisu/autolib.py:120
msgid "param must have a boolean not a {} for notraisepropertyerror" msgid "param must have a boolean not a {} for notraisepropertyerror"
msgstr "" msgstr ""
#: tiramisu/autolib.py:122 #: tiramisu/autolib.py:123
msgid "param must have a boolean not a {} for raisepropertyerror" msgid "param must have a boolean not a {} for raisepropertyerror"
msgstr "" msgstr ""
#: tiramisu/autolib.py:212 #: tiramisu/autolib.py:152
msgid "identifiers in ParamDynOption must be a list, not {0}"
msgstr ""
#: tiramisu/autolib.py:156
msgid "optional in ParamDynOption must be a boolean, not {0}"
msgstr ""
#: tiramisu/autolib.py:207
msgid "cannot add option in information after creating config"
msgstr ""
#: tiramisu/autolib.py:209
msgid "cannot redefine option in information"
msgstr ""
#: tiramisu/autolib.py:213
msgid "option in ParamInformation cannot be a symlinkoption" msgid "option in ParamInformation cannot be a symlinkoption"
msgstr "" msgstr ""
#: tiramisu/autolib.py:215 #: tiramisu/autolib.py:216
msgid "option in ParamInformation cannot be a follower" msgid "option in ParamInformation cannot be a follower"
msgstr "" msgstr ""
#: tiramisu/autolib.py:218 #: tiramisu/autolib.py:219
msgid "option in ParamInformation cannot be a dynamic option" msgid "option in ParamInformation cannot be a dynamic option"
msgstr "" msgstr ""
#: tiramisu/autolib.py:279 #: tiramisu/autolib.py:280
msgid "first argument ({0}) must be a function" msgid "first argument ({0}) must be a function"
msgstr "" msgstr ""
#: tiramisu/autolib.py:283 #: tiramisu/autolib.py:284
msgid "help_function ({0}) must be a function" msgid "help_function ({0}) must be a function"
msgstr "" msgstr ""
#: tiramisu/autolib.py:452 tiramisu/autolib.py:514 #: tiramisu/autolib.py:455 tiramisu/autolib.py:503
msgid "unable to carry out a calculation for {}, {}"
msgstr ""
#: tiramisu/autolib.py:461 tiramisu/autolib.py:521
msgid "the option {0} is used in a calculation but is invalid ({1})" msgid "the option {0} is used in a calculation but is invalid ({1})"
msgstr "" msgstr ""
#: tiramisu/autolib.py:477 tiramisu/autolib.py:535 tiramisu/autolib.py:584 #: tiramisu/autolib.py:467 tiramisu/autolib.py:515 tiramisu/autolib.py:554
msgid "unable to get value for calculating {0}, {1}" msgid "unable to get value for calculating {0}, {1}"
msgstr "" msgstr ""
#: tiramisu/autolib.py:601 #: tiramisu/autolib.py:499
msgid "unable to carry out a calculation for {0}, {1}"
msgstr ""
#: tiramisu/autolib.py:535
msgid "cannot find information for {0}, {1} is a dynamic option"
msgstr ""
#: tiramisu/autolib.py:564
msgid "option {0} is not a dynoptiondescription or in a dynoptiondescription" msgid "option {0} is not a dynoptiondescription or in a dynoptiondescription"
msgstr "" msgstr ""
#: tiramisu/autolib.py:848 #: tiramisu/autolib.py:650
msgid "cannot calculate arguments for \"{0}\", cannot find dynamic variable \"{1}\" with identifier \"{2}\", list of valid identifiers: {3}"
msgstr ""
#: tiramisu/autolib.py:754
msgid "the follower {0} must have index in carry_out_calculation!"
msgstr ""
#: tiramisu/autolib.py:826
msgid "the \"{}\" function with positional arguments \"{}\" and keyword arguments \"{}\" must not return a list (\"{}\") for the follower option {}" msgid "the \"{}\" function with positional arguments \"{}\" and keyword arguments \"{}\" must not return a list (\"{}\") for the follower option {}"
msgstr "" msgstr ""
#: tiramisu/autolib.py:863 #: tiramisu/autolib.py:841
msgid "the \"{}\" function must not return a list (\"{}\") for the follower option {}" msgid "the \"{}\" function must not return a list (\"{}\") for the follower option {}"
msgstr "" msgstr ""
#: tiramisu/autolib.py:904 #: tiramisu/autolib.py:882
msgid "unexpected error \"{0}\" in function \"{1}\" with arguments \"{3}\" and \"{4}\" for option {2}" msgid "unexpected error \"{1}\" in function \"{2}\" with arguments \"{3}\" and \"{4}\" for option {0}"
msgstr "" msgstr ""
#: tiramisu/autolib.py:915 #: tiramisu/autolib.py:891
msgid "unexpected error \"{0}\" in function \"{1}\" for option {2}" msgid "unexpected error \"{1}\" in function \"{2}\" for option {0}"
msgstr "" msgstr ""
#: tiramisu/config.py:419 #: tiramisu/config.py:420
msgid "index \"{0}\" is greater than the leadership length \"{1}\" for option {2}" msgid "index \"{0}\" is greater than the leadership length \"{1}\" for option {2}"
msgstr "" msgstr ""
#: tiramisu/config.py:579 #: tiramisu/config.py:580
msgid "there is no option description for this config (may be GroupConfig)" msgid "there is no option description for this config (may be GroupConfig)"
msgstr "" msgstr ""
#: tiramisu/config.py:668 #: tiramisu/config.py:669
msgid "no option found in config with these criteria" msgid "no option found in config with these criteria"
msgstr "" msgstr ""
#: tiramisu/config.py:871 #: tiramisu/config.py:872
msgid "the follower option {0} has greater length ({1}) than the leader length ({2})" msgid "the follower option {0} has greater length ({1}) than the leader length ({2})"
msgstr "" 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" msgid "option description seems to be part of an other config"
msgstr "" msgstr ""
#: tiramisu/config.py:1144 #: tiramisu/config.py:1145
msgid "parent of {0} not already exists" msgid "parent of {0} not already exists"
msgstr "" msgstr ""
#: tiramisu/config.py:1191 #: tiramisu/config.py:1192
msgid "cannot set leadership object has root optiondescription" msgid "cannot set leadership object has root optiondescription"
msgstr "" msgstr ""
#: tiramisu/config.py:1194 #: tiramisu/config.py:1195
msgid "cannot set dynoptiondescription object has root optiondescription" msgid "cannot set dynoptiondescription object has root optiondescription"
msgstr "" msgstr ""
#: tiramisu/config.py:1246 #: tiramisu/config.py:1247
msgid "config name must be uniq in groupconfig for \"{0}\"" msgid "config name must be uniq in groupconfig for \"{0}\""
msgstr "" msgstr ""
#: tiramisu/config.py:1457 #: tiramisu/config.py:1458
msgid "unknown config \"{}\"" msgid "unknown config \"{}\""
msgstr "" msgstr ""
#: tiramisu/config.py:1482 #: tiramisu/config.py:1483
msgid "child must be a Config, MixConfig or MetaConfig" msgid "child must be a Config, MixConfig or MetaConfig"
msgstr "" 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" msgid "force_default, force_default_if_same or force_dont_change_value cannot be set with only_config"
msgstr "" msgstr ""
#: tiramisu/config.py:1527 #: tiramisu/config.py:1528
msgid "force_default and force_dont_change_value cannot be set together" msgid "force_default and force_dont_change_value cannot be set together"
msgstr "" msgstr ""
#: tiramisu/config.py:1676 #: tiramisu/config.py:1677
msgid "config name must be uniq in groupconfig for {0}" msgid "config name must be uniq in groupconfig for {0}"
msgstr "" msgstr ""
#: tiramisu/config.py:1721 #: tiramisu/config.py:1722
msgid "config added has no name, the name is mandatory" msgid "config added has no name, the name is mandatory"
msgstr "" msgstr ""
#: tiramisu/config.py:1726 #: tiramisu/config.py:1727
msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\"" msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\""
msgstr "" msgstr ""
#: tiramisu/config.py:1744 tiramisu/config.py:1750 #: tiramisu/config.py:1745 tiramisu/config.py:1751
msgid "cannot find the config {0}" msgid "cannot find the config {0}"
msgstr "" msgstr ""
#: tiramisu/config.py:1776 #: tiramisu/config.py:1777
msgid "MetaConfig with optiondescription must have string has child, not {}" msgid "MetaConfig with optiondescription must have string has child, not {}"
msgstr "" msgstr ""
#: tiramisu/config.py:1788 #: tiramisu/config.py:1789
msgid "child must be a Config or MetaConfig" msgid "child must be a Config or MetaConfig"
msgstr "" msgstr ""
#: tiramisu/config.py:1793 #: tiramisu/config.py:1794
msgid "all config in metaconfig must have the same optiondescription" msgid "all config in metaconfig must have the same optiondescription"
msgstr "" msgstr ""
#: tiramisu/config.py:1810 #: tiramisu/config.py:1811
msgid "metaconfig must have the same optiondescription" msgid "metaconfig must have the same optiondescription"
msgstr "" msgstr ""
@ -300,23 +328,39 @@ msgid "cannot modify the {0} {1} because has {2} {3}"
msgstr "" msgstr ""
#: tiramisu/error.py:118 #: tiramisu/error.py:118
msgid "cannot access to {0} {1} because \"{2}\" has {3} {4}" msgid "cannot access to {0} {1} because \"{2}\" hasn't value"
msgstr "" msgstr ""
#: tiramisu/error.py:120 #: tiramisu/error.py:120
msgid "{0} {1} is mandatory but hasn't value"
msgstr ""
#: tiramisu/error.py:123
msgid "cannot access to {0} {1} because \"{2}\" has {3} {4}"
msgstr ""
#: tiramisu/error.py:125
msgid "cannot access to {0} {1} because has {2} {3}" msgid "cannot access to {0} {1} because has {2} {3}"
msgstr "" msgstr ""
#: tiramisu/error.py:192 #: tiramisu/error.py:197
msgid "invalid value" msgid "invalid value"
msgstr "" msgstr ""
#: tiramisu/error.py:201 #: tiramisu/error.py:207
msgid "attention, \"{0}\" could be an invalid {1} for \"{2}\"" msgid "attention, \"{0}\" could be an invalid {1} for {2}"
msgstr "" msgstr ""
#: tiramisu/error.py:219 tiramisu/error.py:228 #: tiramisu/error.py:209
msgid "\"{0}\" is an invalid {1} for \"{2}\"" msgid "attention, \"{0}\" could be an invalid {1} for {2} at index \"{3}\""
msgstr ""
#: tiramisu/error.py:228 tiramisu/error.py:239
msgid "\"{0}\" is an invalid {1} for {2}"
msgstr ""
#: tiramisu/error.py:230
msgid "\"{0}\" is an invalid {1} for {2} at index \"{3}\""
msgstr "" msgstr ""
#: tiramisu/function.py:65 #: tiramisu/function.py:65
@ -383,23 +427,23 @@ msgstr ""
msgid "invalid properties type {0} for {1}, must be a frozenset" msgid "invalid properties type {0} for {1}, must be a frozenset"
msgstr "" msgstr ""
#: tiramisu/option/baseoption.py:98 #: tiramisu/option/baseoption.py:100
msgid "invalid property type {0} for {1}, must be a string or a Calculation" msgid "invalid property type {0} for {1}, must be a string or a Calculation"
msgstr "" msgstr ""
#: tiramisu/option/baseoption.py:249 #: tiramisu/option/baseoption.py:251
msgid "information's item for {0} not found: \"{1}\"" msgid "information's item for {0} not found: \"{1}\""
msgstr "" msgstr ""
#: tiramisu/option/baseoption.py:267 #: tiramisu/option/baseoption.py:269
msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr "" msgstr ""
#: tiramisu/option/baseoption.py:308 #: tiramisu/option/baseoption.py:310
msgid "\"{}\" ({}) object attribute \"{}\" is read-only" msgid "\"{}\" ({}) object attribute \"{}\" is read-only"
msgstr "" msgstr ""
#: tiramisu/option/baseoption.py:320 #: tiramisu/option/baseoption.py:322
msgid "{0} not part of any Config" msgid "{0} not part of any Config"
msgstr "" msgstr ""
@ -423,64 +467,80 @@ msgstr ""
msgid "only {0} are allowed" msgid "only {0} are allowed"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:60 #: tiramisu/option/domainnameoption.py:67
msgid "unknown type {0} for hostname" msgid "unknown type {0} for hostname"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:63 #: tiramisu/option/domainnameoption.py:70
msgid "allow_ip must be a boolean" msgid "allow_ip must be a boolean"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:65 #: tiramisu/option/domainnameoption.py:72
msgid "allow_cidr_network must be a boolean" msgid "allow_cidr_network must be a boolean"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:67 #: tiramisu/option/domainnameoption.py:74
msgid "allow_without_dot must be a boolean" msgid "allow_without_dot must be a boolean"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:69 #: tiramisu/option/domainnameoption.py:76
msgid "allow_startswith_dot must be a boolean" msgid "allow_startswith_dot must be a boolean"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:81 #: tiramisu/option/domainnameoption.py:87
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are allowed" msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are allowed"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:84 #: tiramisu/option/domainnameoption.py:90
msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are recommanded" msgid "must start with lowercase characters followed by lowercase characters, number, \"-\" and \".\" characters are recommanded"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:88 #: tiramisu/option/domainnameoption.py:95
#: tiramisu/option/domainnameoption.py:89 msgid "must start with lowercase characters followed by lowercase characters, number and \"-\" characters are allowed"
msgstr ""
#: tiramisu/option/domainnameoption.py:98
msgid "must start with lowercase characters followed by lowercase characters, number and \"-\" characters are recommanded"
msgstr ""
#: tiramisu/option/domainnameoption.py:102
#: tiramisu/option/domainnameoption.py:103
msgid "could be a IP, otherwise {}" msgid "could be a IP, otherwise {}"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:134 #: tiramisu/option/domainnameoption.py:148
msgid "invalid length (min 1)" msgid "invalid length (min 1)"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:137 #: tiramisu/option/domainnameoption.py:151
msgid "invalid length (max {0})" msgid "invalid length (max {0})"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:143 #: tiramisu/option/domainnameoption.py:157
msgid "must have dot" msgid "must have dot"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:145 #: tiramisu/option/domainnameoption.py:159
msgid "invalid length (max 255)" msgid "invalid length (max 255)"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:163 #: tiramisu/option/domainnameoption.py:180
msgid "DNS resolution failed"
msgstr ""
#: tiramisu/option/domainnameoption.py:184
msgid "error resolving DNS: {1}"
msgstr ""
#: tiramisu/option/domainnameoption.py:191
msgid "must not be an IP" msgid "must not be an IP"
msgstr "" msgstr ""
#: tiramisu/option/domainnameoption.py:186 #: tiramisu/option/domainnameoption.py:214
msgid "some characters are uppercase" msgid "some characters are uppercase"
msgstr "" msgstr ""
#: tiramisu/option/dynoptiondescription.py:131 #: tiramisu/option/dynoptiondescription.py:130
msgid "DynOptionDescription identifiers for option {0}, is not a list ({1})" msgid "DynOptionDescription identifiers for option {0}, is not a list ({1})"
msgstr "" msgstr ""
@ -500,8 +560,20 @@ msgstr ""
msgid "must starts with \"/\"" msgid "must starts with \"/\""
msgstr "" msgstr ""
#: tiramisu/option/filenameoption.py:78 #: tiramisu/option/filenameoption.py:77
msgid "cannot find {0} \"{1}\"" 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:46
msgid "which is not an integer"
msgstr "" msgstr ""
#: tiramisu/option/intoption.py:52 #: tiramisu/option/intoption.py:52
@ -569,7 +641,7 @@ msgid "leadership {0} shall not have a subgroup"
msgstr "" msgstr ""
#: tiramisu/option/leadership.py:114 #: tiramisu/option/leadership.py:114
msgid "only multi option allowed in leadership {0} but option {1} is not a multi" msgid "only multi option are allowed in leadership {0} but option {1} is not a multi"
msgstr "" msgstr ""
#: tiramisu/option/leadership.py:141 #: tiramisu/option/leadership.py:141
@ -604,31 +676,31 @@ msgstr ""
msgid "validators must be a Calculation for \"{0}\"" msgid "validators must be a Calculation for \"{0}\""
msgstr "" msgstr ""
#: tiramisu/option/option.py:146 #: tiramisu/option/option.py:141
msgid "invalid default_multi value \"{0}\" for option {1}" msgid "invalid default_multi value \"{0}\" for option {1}"
msgstr "" msgstr ""
#: tiramisu/option/option.py:154 #: tiramisu/option/option.py:149
msgid "invalid default_multi value \"{0}\" for option {1}, {2}" msgid "invalid default_multi value \"{0}\" for option {1}, {2}"
msgstr "" 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" msgid "invalid default_multi value \"{0}\" for option {1}, must be a list for a submulti"
msgstr "" msgstr ""
#: tiramisu/option/option.py:290 #: tiramisu/option/option.py:291
msgid "the value \"{}\" is not unique" msgid "the value \"{}\" is not unique"
msgstr "" msgstr ""
#: tiramisu/option/option.py:352 #: tiramisu/option/option.py:353
msgid "which must not be a list" msgid "which must not be a list"
msgstr "" msgstr ""
#: tiramisu/option/option.py:404 tiramisu/option/option.py:430 #: tiramisu/option/option.py:405 tiramisu/option/option.py:431
msgid "which must be a list" msgid "which must be a list"
msgstr "" msgstr ""
#: tiramisu/option/option.py:424 #: tiramisu/option/option.py:425
msgid "which \"{}\" must be a list of list" msgid "which \"{}\" must be a list of list"
msgstr "" msgstr ""
@ -636,35 +708,31 @@ msgstr ""
msgid "duplicate option: {0}" msgid "duplicate option: {0}"
msgstr "" msgstr ""
#: tiramisu/option/optiondescription.py:244 #: tiramisu/option/optiondescription.py:249
msgid "unknown option \"{0}\" in root optiondescription (it's a dynamic option)" msgid "cannot access to \"{0}\" it's a dynamic option"
msgstr "" msgstr ""
#: tiramisu/option/optiondescription.py:279 #: tiramisu/option/optiondescription.py:286
msgid "unknown option \"{0}\" in root optiondescription" msgid "cannot find \"{0}\" in \"{1}\""
msgstr "" msgstr ""
#: tiramisu/option/optiondescription.py:282 #: tiramisu/option/optiondescription.py:342
msgid "unknown option \"{0}\" in optiondescription {1}"
msgstr ""
#: tiramisu/option/optiondescription.py:338
msgid "children in optiondescription \"{}\" must be a list" msgid "children in optiondescription \"{}\" must be a list"
msgstr "" msgstr ""
#: tiramisu/option/optiondescription.py:366 #: tiramisu/option/optiondescription.py:370
msgid "duplicate option name: \"{0}\"" msgid "duplicate option name: \"{0}\""
msgstr "" msgstr ""
#: tiramisu/option/optiondescription.py:372 #: tiramisu/option/optiondescription.py:376
msgid "the option's name \"{0}\" start as the dynoptiondescription's name \"{1}\"" msgid "the option's name \"{0}\" start as the dynoptiondescription's name \"{1}\""
msgstr "" msgstr ""
#: tiramisu/option/optiondescription.py:415 #: tiramisu/option/optiondescription.py:419
msgid "cannot change group_type if already set (old {0}, new {1})" msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr "" msgstr ""
#: tiramisu/option/optiondescription.py:420 #: tiramisu/option/optiondescription.py:424
msgid "group_type: {0} not allowed" msgid "group_type: {0} not allowed"
msgstr "" msgstr ""
@ -705,79 +773,91 @@ msgstr ""
msgid "too weak" msgid "too weak"
msgstr "" msgstr ""
#: tiramisu/option/portoption.py:74 #: tiramisu/option/portoption.py:80
msgid "inconsistency in allowed range" msgid "inconsistency in allowed range"
msgstr "" msgstr ""
#: tiramisu/option/portoption.py:79 #: tiramisu/option/portoption.py:85
msgid "max value is empty" msgid "max value is empty"
msgstr "" msgstr ""
#: tiramisu/option/portoption.py:92 #: tiramisu/option/portoption.py:98
msgid "range must have two values only" msgid "range must have two values only"
msgstr "" msgstr ""
#: tiramisu/option/portoption.py:95 #: tiramisu/option/portoption.py:101
msgid "first port in range must be smaller than the second one" msgid "first port in range must be smaller than the second one"
msgstr "" msgstr ""
#: tiramisu/option/portoption.py:121 #: tiramisu/option/portoption.py:127
msgid "should be between {0} and {1}" msgid "should be between {0} and {1}"
msgstr "" msgstr ""
#: tiramisu/option/portoption.py:123 #: tiramisu/option/portoption.py:129
msgid "must be between {0} and {1}" msgid "must be between {0} and {1}"
msgstr "" msgstr ""
#: tiramisu/option/stroption.py:41
msgid "which is not a string"
msgstr ""
#: tiramisu/option/symlinkoption.py:51 #: tiramisu/option/symlinkoption.py:51
msgid "malformed symlink second parameters must be an option for \"{0}\", not {1}" msgid "malformed symlink second parameters must be an option for \"{0}\", not {1}"
msgstr "" msgstr ""
#: tiramisu/option/urloption.py:91 #: tiramisu/option/urloption.py:92
msgid "must start with http:// or https://" msgid "must start with http:// or https://"
msgstr "" msgstr ""
#: tiramisu/option/urloption.py:119 #: tiramisu/option/urloption.py:117
msgid "the port \"{0}\" is invalid: {1}"
msgstr ""
#: tiramisu/option/urloption.py:124
msgid "the domain \"{0}\" is invalid: {1}"
msgstr ""
#: tiramisu/option/urloption.py:128
msgid "must ends with a valid resource name" msgid "must ends with a valid resource name"
msgstr "" msgstr ""
#: tiramisu/setting.py:255 #: tiramisu/setting.py:258
msgid "can't rebind {0}" msgid "can't rebind {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:262 #: tiramisu/setting.py:265
msgid "can't unbind {0}" msgid "can't unbind {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:464 #: tiramisu/setting.py:467
msgid "invalid property type {type(new_prop)} for {subconfig.option.impl_getname()} with {prop.function.__name__} function" msgid "invalid property type {type(new_prop)} for {subconfig.option.impl_getname()} with {prop.function.__name__} function"
msgstr "" msgstr ""
#: tiramisu/setting.py:476 #: tiramisu/setting.py:479
msgid "leader cannot have \"{new_prop}\" property" msgid "leader cannot have \"{new_prop}\" property"
msgstr "" msgstr ""
#: tiramisu/setting.py:564 #: tiramisu/setting.py:567
msgid "leader cannot have \"{0}\" property" msgid "leader cannot have \"{0}\" property"
msgstr "" msgstr ""
#: tiramisu/setting.py:573 #: tiramisu/setting.py:576
msgid "a leader ({0}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\"" msgid "a leader ({0}) cannot have \"force_default_on_freeze\" or \"force_metaconfig_on_freeze\" property without \"frozen\""
msgstr "" msgstr ""
#: tiramisu/setting.py:607 #: tiramisu/setting.py:610
msgid "permissive must be a frozenset" msgid "permissive must be a frozenset"
msgstr "" msgstr ""
#: tiramisu/setting.py:617 #: tiramisu/setting.py:620
msgid "cannot add those permissives: {0}" msgid "cannot add those permissives: {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:654 #: tiramisu/setting.py:657
msgid "can't reset properties to the symlinkoption \"{}\"" msgid "can't reset properties to the symlinkoption \"{}\""
msgstr "" msgstr ""
#: tiramisu/setting.py:667 #: tiramisu/setting.py:670
msgid "can't reset permissives to the symlinkoption \"{}\"" msgid "can't reset permissives to the symlinkoption \"{}\""
msgstr "" msgstr ""
@ -805,19 +885,19 @@ msgstr ""
msgid "unknown action {}" msgid "unknown action {}"
msgstr "" msgstr ""
#: tiramisu/value.py:564 tiramisu/value.py:861 #: tiramisu/value.py:566 tiramisu/value.py:863
msgid "set owner \"{0}\" is forbidden" msgid "set owner \"{0}\" is forbidden"
msgstr "" msgstr ""
#: tiramisu/value.py:571 #: tiramisu/value.py:573
msgid "\"{0}\" is a default value, so we cannot change owner to \"{1}\"" msgid "\"{0}\" is a default value, so we cannot change owner to \"{1}\""
msgstr "" msgstr ""
#: tiramisu/value.py:740 #: tiramisu/value.py:742
msgid "index {index} is greater than the length {length} for option {subconfig.option.impl_get_display_name(with_quote=True)}" msgid "index {index} is greater than the length {length} for option {subconfig.option.impl_get_display_name(with_quote=True)}"
msgstr "" msgstr ""
#: tiramisu/value.py:847 #: tiramisu/value.py:849
msgid "information's item not found \"{}\"" msgid "information's item not found \"{}\""
msgstr "" msgstr ""

View file

@ -4,7 +4,7 @@ requires = ["flit_core >=3.8.0,<4"]
[project] [project]
name = "tiramisu" name = "tiramisu"
version = "5.1.0" version = "5.2.0a8"
authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}] authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md" readme = "README.md"
description = "an options controller tool" description = "an options controller tool"
@ -18,6 +18,8 @@ classifiers = [
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Operating System :: OS Independent", "Operating System :: OS Independent",
"Natural Language :: English", "Natural Language :: English",
@ -33,5 +35,9 @@ name = "cz_conventional_commits"
tag_format = "$version" tag_format = "$version"
version_scheme = "pep440" version_scheme = "pep440"
version_provider = "pep621" version_provider = "pep621"
version_files = [
"tiramisu/__version__.py",
"pyproject.toml:version"
]
#update_changelog_on_bump = true #update_changelog_on_bump = true
changelog_merge_prerelease = true changelog_merge_prerelease = true

View file

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

View file

@ -380,12 +380,12 @@ def test_prefix_error():
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
assert str(err) == _('"{0}" is an invalid {1} for "{2}"').format('yes', _('integer'), 'test1') assert str(err) == _('"{0}" is an invalid {1} for "{2}", which is not an integer').format('yes', _('integer'), 'test1')
try: try:
cfg.option('test1').value.set('yes') cfg.option('test1').value.set('yes')
except Exception as err: except Exception as err:
err.prefix = '' err.prefix = ''
assert str(err) == _('invalid value') assert str(err) == _('which is not an integer')
# assert not list_sessions() # assert not list_sessions()

View file

@ -423,7 +423,7 @@ def test_config_reset():
cfg.owner.set('test') cfg.owner.set('test')
assert cfg.owner.get() == 'test' assert cfg.owner.get() == 'test'
assert not cfg.option('gc.gc2.bool').value.get() 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('boolop').permissive.get()
assert not cfg.option('wantref').information.get('info', None) assert not cfg.option('wantref').information.get('info', None)
# #
@ -440,7 +440,7 @@ def test_config_reset():
cfg.config.reset() cfg.config.reset()
assert cfg.owner.get() == 'test' assert cfg.owner.get() == 'test'
assert not cfg.option('gc.gc2.bool').value.get() 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('float').permissive.get()
assert not cfg.option('wantref').information.get('info', None) assert not cfg.option('wantref').information.get('info', None)
# assert not list_sessions() # assert not list_sessions()

View file

@ -275,3 +275,9 @@ def test_url(config_type):
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('u').value.set('https://FOO.COM:8443') cfg.option('u').value.set('https://FOO.COM:8443')
# assert not list_sessions() # 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]) od = OptionDescription('od', '', [dod])
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
cfg = Config(od2) cfg = Config(od2)
assert set(cfg.option('od.dodval1.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()) == set(['test']) assert set(cfg.option('od.dodval2.st').property.get()) == {'test', "validator"}
cfg.option('od.dodval2.st').property.add('test2') cfg.option('od.dodval2.st').property.add('test2')
assert set(cfg.option('od.dodval1.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()) == set(['test', 'test2']) 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.dodval1').property.get()) == set()
assert set(cfg.option('od.dodval2').property.get()) == set([]) assert set(cfg.option('od.dodval2').property.get()) == set()
cfg.option('od.dodval1').property.add('test1') cfg.option('od.dodval1').property.add('test1')
assert set(cfg.option('od.dodval1').property.get()) == set(['test1']) assert set(cfg.option('od.dodval1').property.get()) == {'test1'}
assert set(cfg.option('od.dodval2').property.get()) == set([]) assert set(cfg.option('od.dodval2').property.get()) == set()
cfg.option('od.dodval1').property.remove('test1') cfg.option('od.dodval1').property.remove('test1')
assert set(cfg.option('od.dodval1').property.get()) == set([]) assert set(cfg.option('od.dodval1').property.get()) == set()
assert set(cfg.option('od.dodval2').property.get()) == set([]) assert set(cfg.option('od.dodval2').property.get()) == set()
# assert not list_sessions() # 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']} 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(): def test_callback_dyndescription_subdyn():
lst = StrOption('lst', '', ['val1', 'val2'], multi=True) lst = StrOption('lst', '', ['val1', 'val2'], multi=True)
st = StrOption('st', '', 'val1') st = StrOption('st', '', 'val1')
@ -602,14 +616,14 @@ def test_prop_dyndescription_context():
od = OptionDescription('od', '', [dod, val1]) od = OptionDescription('od', '', [dod, val1])
od2 = OptionDescription('od', '', [od]) od2 = OptionDescription('od', '', [od])
cfg = Config(od2) cfg = Config(od2)
assert set(cfg.option('od.dodval1.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()) == set(['test']) assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test'}
cfg.option('od.dodval2.st').property.add('test2') cfg.option('od.dodval2.st').property.add('test2')
assert set(cfg.option('od.dodval1.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()) == set(['test', 'test2']) assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test', 'test2'}
cfg.option('od.dodval1.st').permissive.add('test') cfg.option('od.dodval1.st').permissive.add('test')
assert set(cfg.option('od.dodval1.st').property.get()) == set([]) assert set(cfg.option('od.dodval1.st').property.get()) == {"validator"}
assert set(cfg.option('od.dodval2.st').property.get()) == set(['test', 'test2']) assert set(cfg.option('od.dodval2.st').property.get()) == {"validator", 'test', 'test2'}
# assert not list_sessions() # assert not list_sessions()

View file

@ -6,7 +6,7 @@ import pytest
from tiramisu.setting import groups, owners from tiramisu.setting import groups, owners
from tiramisu import ChoiceOption, BoolOption, IntOption, IPOption, NetworkOption, NetmaskOption, \ from tiramisu import ChoiceOption, BoolOption, IntOption, IPOption, NetworkOption, NetmaskOption, \
StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, calc_value, Params StrOption, OptionDescription, Leadership, Config, Calculation, ParamValue, ParamOption, calc_value, Params, submulti
from tiramisu.error import LeadershipError, PropertiesOptionError, ConfigError from tiramisu.error import LeadershipError, PropertiesOptionError, ConfigError
@ -796,7 +796,7 @@ def test_follower_unique():
cfg = Config(od1) cfg = Config(od1)
cfg.option('ip_admin_eth0.ip_admin_eth0').value.set(["192.168.230.145", "192.168.230.146"]) 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 # 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() # assert not list_sessions()
@ -1093,3 +1093,25 @@ def test_leader_forbidden_properties_callback(config_type):
cfg = Config(od1) cfg = Config(od1)
with pytest.raises(LeadershipError): with pytest.raises(LeadershipError):
cfg.option('ip_admin_eth0.ip_admin_eth0').value.get() 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)
def test_default_calc():
var1 = StrOption('var1', "", multi=True, default=['leader1', 'leader2'], properties=frozenset({"mandatory",}))
var2 = StrOption('var2', "", default_multi=[Calculation(calc_value, Params((ParamOption(var1))))], multi=submulti, properties=frozenset({"mandatory",}))
leader = Leadership('interface0', '', [var1, var2])
od1 = OptionDescription('od', '', [leader])
od2 = OptionDescription('root', '', [od1])
cfg = Config(od2)
assert parse_od_get(cfg.value.get()) == {'od.interface0.var1': [{'od.interface0.var1': 'leader1', 'od.interface0.var2': ['leader1']}, {'od.interface0.var1': 'leader2', 'od.interface0.var2': ['leader2']}]}

View file

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

View file

@ -121,6 +121,34 @@ def test_validator(config_type):
# assert not list_sessions() # 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): def test_validator_not_valid(config_type):
with pytest.raises(ValueError): with pytest.raises(ValueError):
StrOption('not_a_list', '', validators=Calculation(return_true, Params(ParamSelfOption())), default='val') 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': if config_type == 'tiramisu-api':
cfg.send() 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'} # 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() # assert not list_sessions()

View file

@ -28,6 +28,8 @@ def test_symlink_option(config_type):
assert cfg.option('c').issymlinkoption() assert cfg.option('c').issymlinkoption()
assert cfg.option('s1.b').type() == 'boolean' assert cfg.option('s1.b').type() == 'boolean'
assert cfg.option('c').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 assert cfg.option('s1.b').value.get() is False
cfg.option("s1.b").value.set(True) cfg.option("s1.b").value.set(True)
cfg.option("s1.b").value.set(False) cfg.option("s1.b").value.set(False)
@ -157,7 +159,7 @@ def test_symlink_getproperties():
od1 = OptionDescription('opt', '', [boolopt, linkopt]) od1 = OptionDescription('opt', '', [boolopt, linkopt])
cfg = Config(od1) cfg = Config(od1)
cfg.property.read_write() 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 boolopt.impl_has_callback() == linkopt.impl_has_callback() == False
# assert not list_sessions() # assert not list_sessions()

View file

@ -42,6 +42,7 @@ from .error import ConfigError
from .api import Config, MetaConfig, GroupConfig, MixConfig from .api import Config, MetaConfig, GroupConfig, MixConfig
from .option import __all__ as all_options from .option import __all__ as all_options
from .setting import owners, groups, undefined from .setting import owners, groups, undefined
from .__version__ import __version__
allfuncs = [ allfuncs = [
@ -76,4 +77,3 @@ allfuncs.extend(all_options)
del all_options del all_options
__all__ = tuple(allfuncs) __all__ = tuple(allfuncs)
del allfuncs del allfuncs
__version__ = "4.1.0"

1
tiramisu/__version__.py Normal file
View file

@ -0,0 +1 @@
__version__ = "5.2.0a8"

View file

@ -139,6 +139,8 @@ def option_type(typ):
@wraps(func) @wraps(func)
def wrapped(*args, **kwargs): def wrapped(*args, **kwargs):
self = args[0] self = args[0]
if isinstance(typ, list) and "allow_dynoption" in typ:
self._allow_dynoption = True
config_bag = self._config_bag config_bag = self._config_bag
if self._config_bag.context.impl_type == "group" and "group" in types: if self._config_bag.context.impl_type == "group" and "group" in types:
options_bag = [ options_bag = [
@ -269,10 +271,9 @@ class _TiramisuOptionWalk:
class _TiramisuOptionOptionDescription: class _TiramisuOptionOptionDescription:
"""Manage option""" """Manage option"""
_validate_properties = False _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): def get(self):
"""Get Tiramisu option""" """Get Tiramisu option"""
return self._subconfig.option return self._subconfig.option
@ -347,11 +348,13 @@ class _TiramisuOptionOptionDescription:
return options return options
@option_type(["option", "optiondescription", "symlink", "with_or_without_index"]) @option_type(["option", "optiondescription", "symlink", "with_or_without_index"])
def type(self): def type(self, only_self=False):
"""Get de option type""" """Get de option type"""
option = self._subconfig.option option = self._subconfig.option
if option.impl_is_optiondescription(): if option.impl_is_optiondescription():
return "optiondescription" return "optiondescription"
if only_self and option.impl_is_symlinkoption():
return 'symlink'
return option.get_type() return option.get_type()
@option_type(["option", "symlink", "with_or_without_index"]) @option_type(["option", "symlink", "with_or_without_index"])
@ -801,6 +804,7 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
option = self._subconfig.option option = self._subconfig.option
if ( if (
not isinstance(value, Calculation) not isinstance(value, Calculation)
and isinstance(value, list)
and option.impl_is_leader() and option.impl_is_leader()
and len(value) < self._subconfig.parent.get_length_leadership() and len(value) < self._subconfig.parent.get_length_leadership()
): ):
@ -906,34 +910,39 @@ class TiramisuOptionValue(CommonTiramisuOption, _TiramisuODGet):
def mandatory(self): def mandatory(self):
"""Return path of options with mandatory property without any value""" """Return path of options with mandatory property without any value"""
subconfig = self._subconfig subconfig = self._subconfig
if subconfig.option.impl_is_optiondescription(): ori_config_bag = self._subconfig.config_bag
ori_config_bag = self._subconfig.config_bag config_bag = ori_config_bag.copy()
config_bag = ori_config_bag.copy() config_bag.properties -= {"mandatory", "empty", "warnings"}
config_bag.properties -= {"mandatory", "empty", "warnings"} config_bag.set_permissive()
config_bag.set_permissive() self._subconfig.config_bag = config_bag
self._subconfig.config_bag = config_bag
options = []
for subconfig in self._config_bag.context.walk(
self._subconfig,
only_mandatory=True,
):
options.append(
TiramisuOption(
subconfig.path,
subconfig.index,
ori_config_bag,
subconfig=subconfig,
)
)
self._subconfig.config_bag = ori_config_bag
return options
try: try:
self._config_bag.context.walk_valid_value( if subconfig.option.impl_is_optiondescription():
self._subconfig, only_mandatory=True options = []
) for subconfig in config_bag.context.walk(
except PropertiesOptionError as err: self._subconfig,
return err.proptype == ["mandatory"] or err.proptype == ["empty"] only_mandatory=True,
return False ):
options.append(
TiramisuOption(
subconfig.path,
subconfig.index,
ori_config_bag,
subconfig=subconfig,
)
)
self._subconfig.config_bag = ori_config_bag
return options
try:
self._config_bag.context.walk_valid_value(
self._subconfig, only_mandatory=True
)
except PropertiesOptionError as err:
return err.proptype == ["mandatory"] or err.proptype == ["empty"]
self._subconfig.config_bag = ori_config_bag
return False
except Exception as err:
self._subconfig.config_bag = ori_config_bag
raise err from err
def _registers( def _registers(
@ -1527,7 +1536,7 @@ class TiramisuContextOption(TiramisuConfig, _TiramisuOptionWalk):
def get(self): def get(self):
"""Get Tiramisu option""" """Get Tiramisu option"""
return None return self._config_bag.context.get_description()
def isleadership(self): def isleadership(self):
"""Test if option is a leader or a follower""" """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 from itertools import chain
import weakref import weakref
from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWarning from .error import PropertiesOptionError, ConfigError, LeadershipError, ValueWarning, CancelParam, display_list, errors
from .i18n import _ from .i18n import _
from .setting import undefined, ConfigBag from .setting import undefined, ConfigBag
from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR from .function import FUNCTION_WAITING_FOR_DICT, FUNCTION_WAITING_FOR_ERROR
@ -49,6 +49,7 @@ def get_calculated_value(
has_calculation = True has_calculation = True
elif isinstance(value, list): elif isinstance(value, list):
# if value is a list, do subcalculation # if value is a list, do subcalculation
value = value.copy()
for idx, val in enumerate(value): for idx, val in enumerate(value):
value[idx], _has_calculation = get_calculated_value( value[idx], _has_calculation = get_calculated_value(
subconfig, subconfig,
@ -148,11 +149,11 @@ class ParamDynOption(ParamOption):
) )
if not isinstance(identifiers, list): if not isinstance(identifiers, list):
raise Exception( 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): if not isinstance(optional, bool):
raise Exception( 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.identifiers = identifiers
self.optional = optional self.optional = optional
@ -203,9 +204,9 @@ class ParamInformation(Param):
def set_option(self, option: "Option" = None) -> None: def set_option(self, option: "Option" = None) -> None:
if not hasattr(self, "self_option"): 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: if self.option:
raise ConfigError("cannot redefine option in information") raise ConfigError(_("cannot redefine option in information"))
if not option.impl_is_optiondescription(): if not option.impl_is_optiondescription():
if option.impl_is_symlinkoption(): if option.impl_is_symlinkoption():
raise ValueError( raise ValueError(
@ -445,14 +446,7 @@ def manager_callback(
or param.raisepropertyerror or param.raisepropertyerror
): ):
raise err from err raise err from err
display_name = subconfig.option.impl_get_display_name( raise ConfigError(err)
subconfig, with_quote=True
)
raise ConfigError(
_("unable to carry out a calculation for {}, {}").format(
display_name, err
)
) from err
except ValueError as err: except ValueError as err:
display_name = subconfig.option.impl_get_display_name( display_name = subconfig.option.impl_get_display_name(
subconfig, with_quote=True subconfig, with_quote=True
@ -464,20 +458,13 @@ def manager_callback(
) from err ) from err
except AttributeError as err: except AttributeError as err:
if isinstance(param, ParamDynOption) and param.optional: if isinstance(param, ParamDynOption) and param.optional:
# cannot acces, simulate a propertyerror # cannot access, simulate a propertyerror
raise PropertiesOptionError( raise PropertiesOptionError(
subconfig, subconfig,
["configerror"], ["configerror"],
config_bag.context.get_settings(), config_bag.context.get_settings(),
) )
display_name = subconfig.option.impl_get_display_name( errors.raise_carry_out_calculation_error(subconfig, _("unable to get value for calculating {0}, {1}"), err)
subconfig, with_quote=True
)
raise ConfigError(
_("unable to get value for calculating {0}, {1}").format(
display_name, err
)
) from err
return value return value
def get_option_bag( def get_option_bag(
@ -509,12 +496,7 @@ def manager_callback(
# raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation # raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation
if param.notraisepropertyerror or param.raisepropertyerror: if param.notraisepropertyerror or param.raisepropertyerror:
raise err from err raise err from err
display_name = option.impl_get_display_name(subconfig, with_quote=True) errors.raise_carry_out_calculation_error(subconfig, _("unable to carry out a calculation for {0}, {1}"), err, option=option)
raise ConfigError(
_("unable to carry out a calculation for {}, {}").format(
display_name, err
)
) from err
except ValueError as err: except ValueError as err:
display_name = option.impl_get_display_name(subconfig, with_quote=True) display_name = option.impl_get_display_name(subconfig, with_quote=True)
raise ValueError( raise ValueError(
@ -524,18 +506,13 @@ def manager_callback(
) from err ) from err
except AttributeError as err: except AttributeError as err:
if isinstance(param, ParamDynOption) and param.optional: if isinstance(param, ParamDynOption) and param.optional:
# cannot acces, simulate a propertyerror # cannot access, simulate a propertyerror
raise PropertiesOptionError( raise PropertiesOptionError(
param, param,
["configerror"], ["configerror"],
config_bag.context.get_settings(), config_bag.context.get_settings(),
) )
display_name = option.impl_get_display_name(subconfig, with_quote=True) errors.raise_carry_out_calculation_error(subconfig, _("unable to get value for calculating {0}, {1}"), err, option=option)
raise ConfigError(
_("unable to get value for calculating {0}, {1}").format(
display_name, err
)
) from err
return subsubconfig return subsubconfig
if isinstance(param, ParamValue): if isinstance(param, ParamValue):
@ -552,15 +529,10 @@ def manager_callback(
true_path=subconfig.path, true_path=subconfig.path,
) )
if isinstance(isubconfig, list): if isinstance(isubconfig, list):
display_name = option.impl_get_display_name(
subconfig, with_quote=True
)
search_name = search_option.impl_get_display_name( search_name = search_option.impl_get_display_name(
None, with_quote=True None, with_quote=True
) )
raise ConfigError( errors.raise_carry_out_calculation_error(subconfig, _("cannot find information for {0}, {1} is a dynamic option"), None, option=option, extra_keys=[search_name])
f"cannot find information for {display_name}, {search_name} is a dynamic option"
)
else: else:
isubconfig = get_option_bag( isubconfig = get_option_bag(
config_bag, config_bag,
@ -579,12 +551,7 @@ def manager_callback(
param.default_value, param.default_value,
) )
except ValueError as err: except ValueError as err:
display_name = option.impl_get_display_name(subconfig, with_quote=True) errors.raise_carry_out_calculation_error(subconfig, _("unable to get value for calculating {0}, {1}"), err, option=option)
raise ConfigError(
_("unable to get value for calculating {0}, {1}").format(
display_name, err
)
) from err
if isinstance(param, ParamIndex): if isinstance(param, ParamIndex):
return index return index
@ -594,14 +561,10 @@ def manager_callback(
not option.impl_is_optiondescription() not option.impl_is_optiondescription()
or not option.impl_is_dynoptiondescription() or not option.impl_is_dynoptiondescription()
): ):
display_name = subconfig.option.impl_get_display_name( errors.raise_carry_out_calculation_error(subconfig, _("option {0} is not a dynoptiondescription or in a dynoptiondescription"), None, option=option)
subconfig, with_quote=True if subconfig.identifiers is None:
) # if uncalculated
raise ConfigError( return
_(
"option {0} is not a dynoptiondescription or in a dynoptiondescription"
).format(display_name)
)
return subconfig.identifiers[param.identifier_index] return subconfig.identifiers[param.identifier_index]
if isinstance(param, ParamSelfOption): if isinstance(param, ParamSelfOption):
@ -673,7 +636,19 @@ def manager_callback(
parent, parent,
) )
except AttributeError as err: 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 = doption.get_identifiers(parent)
if not identifiers:
errors.raise_carry_out_calculation_error(subconfig, _('cannot calculate arguments for {0}, {1} with identifier "{2}", there is no identifiers'), err, extra_keys=[identifier])
else:
identifiers_list = display_list(identifiers, add_quote=True)
errors.raise_carry_out_calculation_error(subconfig, _('cannot calculate arguments for {0}, {1} with identifier "{2}", list of valid identifiers: {3}'), err, extra_keys=[identifier, identifiers_list])
new_parents.append( new_parents.append(
parent.get_child( parent.get_child(
doption, doption,
@ -777,9 +752,7 @@ def carry_out_calculation(
and option.impl_is_follower() and option.impl_is_follower()
and index is None and index is None
): ):
raise ConfigError( errors.raise_carry_out_calculation_error(subconfig, _("the follower {0} must have index in carry_out_calculation!"), None, option=option)
f"the follower {option.impl_get_display_name(subconfig, with_quote=True)} must have index in carry_out_calculation!"
)
def fake_items(iterator): def fake_items(iterator):
return ((None, i) for i in iterator) return ((None, i) for i in iterator)
@ -828,6 +801,12 @@ def carry_out_calculation(
args.append(err) args.append(err)
else: else:
kwargs[key] = err 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( ret = calculate(
subconfig, subconfig,
callback, callback,
@ -893,7 +872,7 @@ def calculate(
except (ValueError, ValueWarning) as err: except (ValueError, ValueWarning) as err:
if allow_value_error: if allow_value_error:
if force_value_warning: if force_value_warning:
raise ValueWarning(str(err)) raise ValueWarning(msg=str(err))
raise err from err raise err from err
error = err error = err
except ConfigError as err: except ConfigError as err:
@ -902,19 +881,14 @@ def calculate(
error = err error = err
if args or kwargs: if args or kwargs:
msg = _( msg = _(
'unexpected error "{0}" in function "{1}" with arguments "{3}" and "{4}" ' 'unexpected error "{1}" in function "{2}" with arguments "{3}" and "{4}" '
"for option {2}" "for option {0}"
).format(
str(error),
callback.__name__,
subconfig.option.impl_get_display_name(subconfig, with_quote=True),
args,
kwargs,
) )
extra_keys = [callback.__name__,
args,
kwargs,
]
else: else:
msg = _('unexpected error "{0}" in function "{1}" for option {2}' "").format( msg = _('unexpected error "{1}" in function "{2}" for option {0}')
str(error), extra_keys = [callback.__name__]
callback.__name__, errors.raise_carry_out_calculation_error(subconfig, msg, error, extra_keys=extra_keys)
subconfig.option.impl_get_display_name(subconfig, with_quote=True),
)
raise ConfigError(msg) from error

View file

@ -41,7 +41,7 @@ def get_common_path(path1, path2):
return common_path return common_path
if common_path.endswith("."): if common_path.endswith("."):
return common_path[:-1] return common_path[:-1]
if "." in common_path: elif "." in common_path:
return common_path.rsplit(".", 1)[0] return common_path.rsplit(".", 1)[0]
return None return None
@ -227,7 +227,8 @@ class SubConfig:
identifiers: Optional[list[str]], identifiers: Optional[list[str]],
*, *,
true_path: Optional[str] = None, 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, validate_properties: bool = True,
) -> None: ) -> None:
self.index = index self.index = index

View file

@ -113,6 +113,11 @@ class PropertiesOptionError(AttributeError):
msg = _('cannot modify the {0} {1} because "{2}" has {3} {4}') msg = _('cannot modify the {0} {1} because "{2}" has {3} {4}')
else: else:
msg = _("cannot modify the {0} {1} because has {2} {3}") msg = _("cannot modify the {0} {1} because has {2} {3}")
elif properties == ["mandatory"]:
if self._orig_opt:
msg = _("cannot access to {0} {1} because \"{2}\" hasn't value")
else:
msg = _("{0} {1} is mandatory but hasn't value")
else: else:
if self._orig_opt: if self._orig_opt:
msg = _('cannot access to {0} {1} because "{2}" has {3} {4}') msg = _('cannot access to {0} {1} because "{2}" has {3} {4}')
@ -172,7 +177,7 @@ class _CommonError:
self.val = val self.val = val
self.display_type = display_type self.display_type = display_type
self.opt = weakref.ref(opt) self.opt = weakref.ref(opt)
self.name = opt.impl_get_display_name(subconfig) self.name = opt.impl_get_display_name(subconfig, with_quote=True)
self.err_msg = err_msg self.err_msg = err_msg
self.index = index self.index = index
super().__init__(self.err_msg) super().__init__(self.err_msg)
@ -181,7 +186,7 @@ class _CommonError:
try: try:
msg = self.prefix msg = self.prefix
except AttributeError: 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 msg = self.prefix
if self.err_msg: if self.err_msg:
if msg: if msg:
@ -196,13 +201,16 @@ class _CommonError:
class ValueWarning(_CommonError, UserWarning): class ValueWarning(_CommonError, UserWarning):
tmpl = None tmpl = None
def __init__(self, *args, **kwargs): def __init__(self, **kwargs):
if ValueWarning.tmpl is None: if ValueWarning.tmpl is None:
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for "{2}"') if kwargs.get('index') is None:
if len(args) == 1 and not kwargs: ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for {2}')
self.msg = args[0] else:
ValueWarning.tmpl = _('attention, "{0}" could be an invalid {1} for {2} at index "{3}"')
if list(kwargs) == ['msg']:
self.msg = kwargs['msg']
else: else:
super().__init__(*args, **kwargs) super().__init__(**kwargs)
self.msg = None self.msg = None
def __str__(self): def __str__(self):
@ -214,10 +222,13 @@ class ValueWarning(_CommonError, UserWarning):
class ValueOptionError(_CommonError, ValueError): class ValueOptionError(_CommonError, ValueError):
tmpl = None tmpl = None
def __init__(self, *args, **kwargs): def __init__(self, **kwargs):
if ValueOptionError.tmpl is None: if ValueOptionError.tmpl is None:
ValueOptionError.tmpl = _('"{0}" is an invalid {1} for "{2}"') if kwargs.get('index') is None:
super().__init__(*args, **kwargs) 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): class ValueErrorWarning(ValueWarning):
@ -225,5 +236,39 @@ class ValueErrorWarning(ValueWarning):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if ValueErrorWarning.tmpl is None: if ValueErrorWarning.tmpl is None:
ValueErrorWarning.tmpl = _('"{0}" is an invalid {1} for "{2}"') ValueErrorWarning.tmpl = _('"{0}" is an invalid {1} for {2}')
super().__init__(*args, **kwargs) 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
class Errors:
@staticmethod
def raise_carry_out_calculation_error(subconfig, message, original_error, option=None, extra_keys=[]):
if option is None:
option = subconfig.option
display_name = option.impl_get_display_name(
subconfig, with_quote=True
)
if original_error:
raise ConfigError(
message.format(display_name, original_error, *extra_keys)
) from original_error
raise ConfigError(message.format(display_name, extra_keys))
errors = Errors()

View file

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

View file

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

View file

@ -124,7 +124,6 @@ class DynOptionDescription(OptionDescription):
)[0] )[0]
if values is None: if values is None:
values = [] values = []
values_ = []
if __debug__: if __debug__:
if not isinstance(values, list): if not isinstance(values, list):
raise ValueError( raise ValueError(
@ -134,6 +133,7 @@ class DynOptionDescription(OptionDescription):
self.impl_get_display_name(subconfig, with_quote=True), values self.impl_get_display_name(subconfig, with_quote=True), values
) )
) )
values_ = []
for val in values: for val in values:
cval = self.convert_identifier_to_path(val) cval = self.convert_identifier_to_path(val)
if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None: if not isinstance(cval, str) or re.match(NAME_REGEXP, cval) is None:

View file

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

View file

@ -43,7 +43,7 @@ class IntOption(Option):
value: int, value: int,
) -> None: ) -> None:
if not isinstance(value, int): if not isinstance(value, int):
raise ValueError() raise ValueError(_('which is not an integer'))
def second_level_validation(self, value, warnings_only): def second_level_validation(self, value, warnings_only):
min_number = self.impl_get_extra("min_number") min_number = self.impl_get_extra("min_number")

View file

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

View file

@ -112,7 +112,7 @@ class Leadership(OptionDescription):
if not child.impl_is_multi(): if not child.impl_is_multi():
raise ValueError( raise ValueError(
_( _(
"only multi option allowed in leadership {0} but option " "only multi option are allowed in leadership {0} but option "
"{1} is not a multi" "{1} is not a multi"
"" ""
).format( ).format(

View file

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

View file

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

View file

@ -232,18 +232,21 @@ class OptionDescriptionWalk(CacheOptionDescription):
def get_child_not_dynamic( def get_child_not_dynamic(
self, self,
name, name: str,
allow_dynoption, allow_dynoption: bool,
parent: "SubConfig",
): ):
if name in self._children[0]: # pylint: disable=no-member if name in self._children[0]: # pylint: disable=no-member
option = self._children[1][ option = self._children[1][
self._children[0].index(name) self._children[0].index(name)
] # pylint: disable=no-member ] # pylint: disable=no-member
if option.impl_is_dynoptiondescription() and not allow_dynoption: if option.impl_is_dynoptiondescription() and not allow_dynoption:
if parent.path:
path = parent.path + '.' + name
else:
path = name
raise AttributeError( raise AttributeError(
_( _('cannot access to "{0}" it\'s a dynamic option').format(path)
'unknown option "{0}" in root optiondescription (it\'s a dynamic option)'
).format(name)
) )
return option return option
@ -261,6 +264,7 @@ class OptionDescriptionWalk(CacheOptionDescription):
option = self.get_child_not_dynamic( option = self.get_child_not_dynamic(
name, name,
allow_dynoption, allow_dynoption,
parent,
) )
if option: if option:
return option return option
@ -274,13 +278,13 @@ class OptionDescriptionWalk(CacheOptionDescription):
if not with_identifier: if not with_identifier:
return child return child
return identifier, child return identifier, child
if self.impl_get_group_type() == groups.root: # pylint: disable=no-member if parent.path is None:
raise AttributeError( raise AttributeError(
_('unknown option "{0}" in root optiondescription').format(name) _('cannot find "{0}"').format(name)
) )
raise AttributeError( raise AttributeError(
_('unknown option "{0}" in optiondescription {1}').format( _('cannot find "{0}" in "{1}"').format(
name, self.impl_get_display_name(parent, with_quote=True) name, parent.path
) )
) )

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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