diff --git a/locale/fr/LC_MESSAGES/tiramisu.po b/locale/fr/LC_MESSAGES/tiramisu.po index e690849..4ab1976 100644 --- a/locale/fr/LC_MESSAGES/tiramisu.po +++ b/locale/fr/LC_MESSAGES/tiramisu.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: Tiramisu\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-05-12 09:04+0200\n" +"POT-Creation-Date: 2025-09-19 22:03+0200\n" "PO-Revision-Date: \n" "Last-Translator: Emmanuel Garette \n" "Language-Team: Tiramisu's team \n" @@ -11,46 +11,46 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -"X-Generator: Poedit 3.5\n" +"X-Generator: Poedit 3.7\n" "X-Poedit-SourceCharset: UTF-8\n" -#: tiramisu/api.py:79 +#: tiramisu/api.py:86 msgid "Settings:" msgstr "Paramètres :" -#: tiramisu/api.py:83 +#: tiramisu/api.py:90 msgid "Access to option without verifying permissive properties" msgstr "Accès à une option sans vérifié les propriétés permises" -#: tiramisu/api.py:88 +#: tiramisu/api.py:95 msgid "Access to option without property restriction" msgstr "Accès à une option sans restriction de propriété" -#: tiramisu/api.py:93 +#: tiramisu/api.py:100 msgid "Do not warnings during validation" msgstr "Ne peut avoir de warnings durant une validation" -#: tiramisu/api.py:97 +#: tiramisu/api.py:104 msgid "Commands:" msgstr "Commandes :" -#: tiramisu/api.py:111 tiramisu/api.py:1857 +#: tiramisu/api.py:118 tiramisu/api.py:1955 msgid "please specify a valid sub function ({0}.{1})" msgstr "veuillez spécifier une sous fonction valide ({0}.{1})" -#: tiramisu/api.py:196 +#: tiramisu/api.py:206 msgid "please do not specify index ({0}.{1})" msgstr "veuillez ne pas spécifier d'index ({0}.{1})" -#: tiramisu/api.py:201 tiramisu/api.py:856 +#: tiramisu/api.py:211 tiramisu/api.py:940 msgid "please specify index with a follower option ({0}.{1})" msgstr "veuillez spécifier un index avec une option suiveuse ({0}.{1})" -#: tiramisu/api.py:222 +#: tiramisu/api.py:234 msgid "please specify a valid sub function ({0}.{1}): {2}" msgstr "veuillez spécifier une sous fonction valide ({0}.{1}): {2}" -#: tiramisu/api.py:446 +#: tiramisu/api.py:525 msgid "" "the option {0} is not a dynamic option, cannot get identifiers with " "only_self parameter to True" @@ -58,15 +58,15 @@ msgstr "" "l'option {0} n'est pas une option dynamique, ne peut retrouver l'identifiant " "avec le paramètre only_self à True" -#: tiramisu/api.py:532 +#: tiramisu/api.py:613 msgid "cannot get option from a follower symlink without index" msgstr "ne peut avoir d'option pour une symlink suiveuse sans index" -#: tiramisu/api.py:607 +#: tiramisu/api.py:691 msgid "cannot add this property: \"{0}\"" msgstr "ne peut pas ajouter cette propriété : \"{0}\"" -#: tiramisu/api.py:634 +#: tiramisu/api.py:718 msgid "" "cannot remove option's property \"{0}\", use permissive instead in option " "\"{1}\"" @@ -74,11 +74,11 @@ msgstr "" "ne supprimer la propriété \"{0}\" de l'option, utiliser plutôt permissive " "dans l'option \"{1}\"" -#: tiramisu/api.py:638 +#: tiramisu/api.py:722 msgid "cannot find \"{0}\" in option \"{1}\"" msgstr "ne peut trouver \"{0}\" dans l'option \"{1}\"" -#: tiramisu/api.py:643 +#: tiramisu/api.py:727 msgid "" "cannot remove option's property \"{0}\", use permissive instead in option " "\"{1}\" at index \"{2}\"" @@ -86,35 +86,35 @@ msgstr "" "ne peut supprimer la propriété \"{0}\" de l'option, utilisez plutôt " "permissive dans l'option \"{1}\" a l'index \"{2}\"" -#: tiramisu/api.py:647 +#: tiramisu/api.py:731 msgid "cannot find \"{0}\" in option \"{1}\" at index \"{2}\"" msgstr "ne peut trouver \"{0}\" dans l'option \"{1}\" à l'index \"{2}\"" -#: tiramisu/api.py:691 +#: tiramisu/api.py:775 msgid "cannot find \"{0}\"" msgstr "ne peut trouver \"{0}\"" -#: tiramisu/api.py:873 +#: tiramisu/api.py:957 msgid "only multi value has defaultmulti" msgstr "seule les valeurs multiple a l'attribut defaultmulti" -#: tiramisu/api.py:1037 +#: tiramisu/api.py:1121 msgid "please specify a valid sub function ({0}.{1}) for {2}" msgstr "veuillez spécifier une sous fonction valide ({0}.{1}) pour {2}" -#: tiramisu/api.py:1424 +#: tiramisu/api.py:1520 msgid "properties must be a frozenset" msgstr "une propriété doit être de type frozenset" -#: tiramisu/api.py:1428 tiramisu/api.py:1455 +#: tiramisu/api.py:1524 tiramisu/api.py:1551 msgid "unknown when {} (must be in append or remove)" msgstr "value {} inconsistent (doit être append ou remove)" -#: tiramisu/api.py:1441 tiramisu/api.py:1465 tiramisu/config.py:1676 +#: tiramisu/api.py:1537 tiramisu/api.py:1561 tiramisu/config.py:1691 msgid "unknown type {}" msgstr "type inconnu {}" -#: tiramisu/api.py:1829 +#: tiramisu/api.py:1927 msgid "do not use unrestraint, nowarnings or forcepermissive together" msgstr "" "il ne faut pas utiliser unrestraint, nowarnings ou forcepermissive ensemble" @@ -183,29 +183,29 @@ msgstr "le premier argument ({0}) doit être une fonction" msgid "help_function ({0}) must be a function" msgstr "help_function ({0}) doit être une fonction" -#: tiramisu/autolib.py:469 tiramisu/autolib.py:524 +#: tiramisu/autolib.py:469 tiramisu/autolib.py:525 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})" -#: tiramisu/autolib.py:482 tiramisu/autolib.py:538 tiramisu/autolib.py:588 +#: tiramisu/autolib.py:482 tiramisu/autolib.py:539 tiramisu/autolib.py:589 msgid "unable to get value for calculating {0}, {1}" msgstr "impossible de trouver la valeur pour calculer {0}, {1}" -#: tiramisu/autolib.py:517 +#: tiramisu/autolib.py:518 msgid "unable to carry out a calculation for {0}, {1}" msgstr "impossible d'effectuer le calcul pour {0}, {1}" -#: tiramisu/autolib.py:563 +#: tiramisu/autolib.py:564 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:603 +#: tiramisu/autolib.py:604 msgid "option {0} is not a dynoptiondescription or in a dynoptiondescription" msgstr "" "l'option {0} n'est pas une dynoptiondescription ou n'est pas dans une " "dynoptiondescription" -#: tiramisu/autolib.py:696 +#: tiramisu/autolib.py:697 msgid "" "cannot calculate arguments for {0}, {1} with identifier \"{2}\", there is no " "identifiers" @@ -213,7 +213,7 @@ msgstr "" "impossible de calculer les arguments de {0}, {1} avec l'identifiant \"{2}\", " "il n'y a pas d'identifiants" -#: tiramisu/autolib.py:708 +#: tiramisu/autolib.py:709 msgid "" "cannot calculate arguments for {0}, {1} with identifier \"{2}\", list of " "valid identifiers: {3}" @@ -221,12 +221,12 @@ msgstr "" "impossible de calculer les arguments de {0}, {1} avec l'identifiant \"{2}\", " "voici la liste des identifiants valables : {3}" -#: tiramisu/autolib.py:819 +#: tiramisu/autolib.py:820 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:931 +#: tiramisu/autolib.py:932 msgid "" "unexpected error \"{1}\" in function \"{2}\" with arguments \"{3}\" and " "\"{4}\" for option {0}" @@ -234,50 +234,54 @@ msgstr "" "erreur inattendue \"{1}\" dans la fonction \"{2}\" avec les arguments " "\"{3}\" et \"{4}\" pour l'option {0}" -#: tiramisu/autolib.py:941 +#: tiramisu/autolib.py:942 msgid "unexpected error \"{1}\" in function \"{2}\" for option {0}" msgstr "erreur inattendue \"{1}\" dans la fonction \"{2}\" pour l'option {0}" -#: tiramisu/config.py:574 +#: tiramisu/config.py:391 tiramisu/config.py:413 +msgid "option name {0} is not unique in {1}" +msgstr "le nom de l'option {0} n'est pas unique dans {1}" + +#: tiramisu/config.py:638 msgid "there is no option description for this config (may be GroupConfig)" msgstr "" "il n'y a pas d'option description trouvé pour cette config (peut être un " "GroupConfig)" -#: tiramisu/config.py:663 -msgid "no option found in config with these criteria" -msgstr "aucune option trouvée dans la config avec ces critères" - -#: tiramisu/config.py:978 tiramisu/option/optiondescription.py:74 +#: tiramisu/config.py:989 tiramisu/option/optiondescription.py:74 msgid "option description seems to be part of an other config" msgstr "l'option description semble faire parti d'une autre config" -#: tiramisu/config.py:1140 +#: tiramisu/config.py:1151 msgid "parent of {0} not already exists" msgstr "le parent de {0} n'existe plus" -#: tiramisu/config.py:1187 +#: tiramisu/config.py:1198 msgid "cannot set leadership object has root optiondescription" msgstr "ne peut assigner un objet leadership comme optiondescription racine" -#: tiramisu/config.py:1190 +#: tiramisu/config.py:1201 msgid "cannot set dynoptiondescription object has root optiondescription" msgstr "" "ne peut assigner un objet dynoptiondescription comme optiondescription racine" -#: tiramisu/config.py:1242 +#: tiramisu/config.py:1248 +msgid "child must be a Config, GroupConfig, MixConfig or MetaConfig" +msgstr "l'enfant doit être une Config, GroupConfig, MixConfig ou MetaConfig" + +#: tiramisu/config.py:1257 msgid "config name must be uniq in groupconfig for \"{0}\"" msgstr "le nom d'un config doit être unique dans un groupconfig pour \"{0}\"" -#: tiramisu/config.py:1453 +#: tiramisu/config.py:1466 msgid "unknown config \"{}\"" msgstr "config \"{}\" inconnue" -#: tiramisu/config.py:1478 +#: tiramisu/config.py:1491 msgid "child must be a Config, MixConfig or MetaConfig" msgstr "l'enfant doit être une Config, MixConfig ou MetaConfig" -#: tiramisu/config.py:1513 +#: tiramisu/config.py:1526 msgid "" "force_default, force_default_if_same or force_dont_change_value cannot be " "set with only_config" @@ -285,43 +289,43 @@ msgstr "" "force_default, force_default_if_same ou force_dont_change_value ne peuvent " "pas être spécifié avec only_config" -#: tiramisu/config.py:1523 +#: tiramisu/config.py:1536 msgid "force_default and force_dont_change_value cannot be set together" msgstr "" "force_default et force_dont_change_value ne peuvent pas être mis ensemble" -#: tiramisu/config.py:1672 +#: tiramisu/config.py:1687 msgid "config name must be uniq in groupconfig for {0}" msgstr "le nom de la config doit être unique dans un groupconfig pour {0}" -#: tiramisu/config.py:1717 +#: tiramisu/config.py:1732 msgid "config added has no name, the name is mandatory" msgstr "la config ajoutée n'a pas de nom, le nom est obligatoire" -#: tiramisu/config.py:1722 +#: tiramisu/config.py:1737 msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\"" msgstr "" "le nom d'un config \"{0}\" n'est pas unique dans le groupconfig \"{1}\"" -#: tiramisu/config.py:1740 tiramisu/config.py:1746 +#: tiramisu/config.py:1755 tiramisu/config.py:1761 msgid "cannot find the config {0}" msgstr "ne peut pas trouver la config {0}" -#: tiramisu/config.py:1772 +#: tiramisu/config.py:1787 msgid "MetaConfig with optiondescription must have string has child, not {}" msgstr "" "MetaConfig avec une optiondescription doit avoir un nom comme enfant, pas {}" -#: tiramisu/config.py:1784 +#: tiramisu/config.py:1799 msgid "child must be a Config or MetaConfig" msgstr "enfant doit être une une Config ou une MetaConfig" -#: tiramisu/config.py:1789 +#: tiramisu/config.py:1804 msgid "all config in metaconfig must have the same optiondescription" msgstr "" "toutes les configs d'une metaconfig doivent avoir la même optiondescription" -#: tiramisu/config.py:1806 +#: tiramisu/config.py:1821 msgid "metaconfig must have the same optiondescription" msgstr "metaconfig doivent avoir la même optiondescription" @@ -354,8 +358,6 @@ msgid "cannot modify the {0} {1} because is frozen" msgstr "ne peut modifier {0} {1} parce qu'est frozen" #: tiramisu/error.py:160 -#, fuzzy -#| msgid "cannot access to {0} {1} at index \"{2}\" because {2} hasn't value" msgid "cannot access to {0} {1} at index \"{2}\" because {3} hasn't value" msgstr "" "ne peut accéder à {0} {1} à l'index \"{2}\" parce que {3} n'a pas de valeur" @@ -455,24 +457,24 @@ msgstr "" "la fonction \"{0}\" ne doit pas retourner une liste (\"{1}\") pour l'option " "suiveuse {2}" -#: tiramisu/error.py:331 +#: tiramisu/error.py:333 msgid "invalid value" msgstr "valeur invalide" -#: tiramisu/error.py:341 +#: tiramisu/error.py:343 msgid "attention, \"{0}\" could be an invalid {1} for {2}" msgstr "attention, \"{0}\" peut être un {1} invalide pour {2}" -#: tiramisu/error.py:345 +#: tiramisu/error.py:347 msgid "attention, \"{0}\" could be an invalid {1} for {2} at index \"{3}\"" msgstr "" "attention, \"{0}\" peut être un {1} invalide pour {2} à l'index \"{3}\"" -#: tiramisu/error.py:366 tiramisu/error.py:377 +#: tiramisu/error.py:368 tiramisu/error.py:379 msgid "\"{0}\" is an invalid {1} for {2}" msgstr "\"{0}\" est une valeur invalide pour l'option {2} de type {1}" -#: tiramisu/error.py:368 +#: tiramisu/error.py:370 msgid "\"{0}\" is an invalid {1} for {2} at index \"{3}\"" msgstr "\"{0}\" est un {1} invalide pour {2} à l'index \"{3}\"" @@ -547,33 +549,33 @@ msgstr "la valeur de \"{0}\" est {1}" msgid "the value of \"{0}\" is not {1}" msgstr "la valeur de \"{0}\" n'est pas {1}" -#: tiramisu/option/baseoption.py:75 tiramisu/option/symlinkoption.py:44 +#: tiramisu/option/baseoption.py:76 tiramisu/option/symlinkoption.py:44 msgid "\"{0}\" is an invalid name for an option" msgstr "\"{0}\" est un nom invalide pour une option" -#: tiramisu/option/baseoption.py:88 +#: tiramisu/option/baseoption.py:89 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" -#: tiramisu/option/baseoption.py:100 +#: tiramisu/option/baseoption.py:102 msgid "invalid property type {0} for {1}, must be a string or a Calculation" msgstr "" "type {0} invalide pour des propriétés pour {1}, doit être des caractères ou " "un objet Calculation" -#: tiramisu/option/baseoption.py:251 +#: tiramisu/option/baseoption.py:253 msgid "information's item for {0} not found: \"{1}\"" msgstr "item pour {0} dans les informations non trouvée: \"{1}\"" -#: tiramisu/option/baseoption.py:269 +#: tiramisu/option/baseoption.py:271 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule" -#: tiramisu/option/baseoption.py:310 +#: tiramisu/option/baseoption.py:312 msgid "\"{}\" ({}) object attribute \"{}\" is read-only" msgstr "\"{}\" ({}) l'attribut de l'objet \"{}\" est en lecture seule" -#: tiramisu/option/baseoption.py:322 +#: tiramisu/option/baseoption.py:324 msgid "{0} not part of any Config" msgstr "{0} ne fait pas parti d'une Config" @@ -581,19 +583,19 @@ msgstr "{0} ne fait pas parti d'une Config" msgid "invalid string" msgstr "invalide caractère" -#: tiramisu/option/choiceoption.py:47 +#: tiramisu/option/choiceoption.py:52 msgid "values must be a tuple or a calculation for {0}" msgstr "les valeurs doivent être un tuple ou une fonction pour {0}" -#: tiramisu/option/choiceoption.py:70 +#: tiramisu/option/choiceoption.py:75 msgid "the calculated values \"{0}\" for \"{1}\" is not a list" msgstr "les valeurs calculées \"{0}\" pour \"{1}\" n'est pas une liste" -#: tiramisu/option/choiceoption.py:101 +#: tiramisu/option/choiceoption.py:106 msgid "only \"{0}\" is allowed" msgstr "seul \"{0}\" est autorisé" -#: tiramisu/option/choiceoption.py:103 +#: tiramisu/option/choiceoption.py:108 msgid "only {0} are allowed" msgstr "seul {0} sont autorisées" @@ -696,7 +698,7 @@ msgstr "" msgid "invalid identifier \"{}\" for option {}" msgstr "identifiant \"{}\" invalide pour l'option \"{}\"" -#: tiramisu/option/dynoptiondescription.py:150 +#: tiramisu/option/dynoptiondescription.py:154 msgid "" "DynOptionDescription \"{0}\" identifiers return a list with same values " "\"{1}\"" @@ -849,42 +851,45 @@ msgstr "" msgid "the value \"{}\" is not unique" msgstr "la valeur \"{}\" n'est pas unique" -#: tiramisu/option/option.py:356 +#: tiramisu/option/option.py:357 msgid "which must not be a list" msgstr "qui ne doit pas être une liste" -#: tiramisu/option/option.py:408 tiramisu/option/option.py:434 +#: tiramisu/option/option.py:412 tiramisu/option/option.py:450 msgid "which must be a list" msgstr "qui doit être une liste" -#: tiramisu/option/option.py:428 +#: tiramisu/option/option.py:440 msgid "which \"{}\" must be a list of list" msgstr "lequel \"{}\" doit être une liste de liste" -#: tiramisu/option/optiondescription.py:109 -msgid "duplicate option: {0}" -msgstr "option dupliquée : {0}" +#: tiramisu/option/optiondescription.py:111 +#: tiramisu/option/optiondescription.py:117 +msgid "\"{0}\" option description" +msgstr "l'option description \"{0}\"" -#: tiramisu/option/optiondescription.py:336 +#: tiramisu/option/optiondescription.py:115 +#: tiramisu/option/optiondescription.py:121 +msgid "root option description" +msgstr "l'option description racine" + +#: tiramisu/option/optiondescription.py:123 +msgid "option \"{0}\" is include in {1} but is also in {2}" +msgstr "l'option \"{0}\" est incluse dans {1} mais est aussi dans {2}" + +#: tiramisu/option/optiondescription.py:328 msgid "children in optiondescription \"{}\" must be a list" msgstr "les enfants d'une optiondescription \"{}\" doivent être une liste" -#: tiramisu/option/optiondescription.py:364 -msgid "duplicate option name: \"{0}\"" -msgstr "nom de l'option dupliqué : \"{0}\"" +#: tiramisu/option/optiondescription.py:357 +msgid "the option name \"{0}\" is duplicate in \"{1}\"" +msgstr "le nom de l'option \"{0}\" est dupliqué dans \"{1}\"" -#: tiramisu/option/optiondescription.py:370 -msgid "" -"the option's name \"{0}\" start as the dynoptiondescription's name \"{1}\"" -msgstr "" -"le nom de l'option \"{0}\" commence comme le nom du dynoptiondescription " -"\"{1}\"" - -#: tiramisu/option/optiondescription.py:413 +#: tiramisu/option/optiondescription.py:411 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})" -#: tiramisu/option/optiondescription.py:418 +#: tiramisu/option/optiondescription.py:416 msgid "group_type: {0} not allowed" msgstr "group_type : {0} non autorisé" @@ -925,27 +930,27 @@ msgstr "{0} a plus de droit que {1}" msgid "too weak" msgstr "trop simple" -#: tiramisu/option/portoption.py:80 +#: tiramisu/option/portoption.py:77 msgid "inconsistency in allowed range" msgstr "inconsistence dans la plage autorisée" -#: tiramisu/option/portoption.py:85 +#: tiramisu/option/portoption.py:82 msgid "max value is empty" msgstr "la valeur maximum est vide" -#: tiramisu/option/portoption.py:98 +#: tiramisu/option/portoption.py:95 msgid "range must have two values only" msgstr "un rang doit avoir deux valeurs seulement" -#: tiramisu/option/portoption.py:101 +#: tiramisu/option/portoption.py:98 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" -#: tiramisu/option/portoption.py:127 +#: tiramisu/option/portoption.py:124 msgid "should be between {0} and {1}" msgstr "devrait être une nombre entre {0} et {1}" -#: tiramisu/option/portoption.py:129 +#: tiramisu/option/portoption.py:126 msgid "must be between {0} and {1}" msgstr "doit être une nombre entre {0} et {1}" @@ -989,8 +994,8 @@ msgid "" "invalid property type {type(new_prop)} for {subconfig.option.impl_getname()} " "with {prop.function.__name__} function" msgstr "" -"type {type(new_prop)} de la propriété invalide pour la fonction {subconfig." -"option.impl_getname()} with {prop.function.__name__}" +"type {type(new_prop)} de la propriété invalide pour la fonction " +"{subconfig.option.impl_getname()} with {prop.function.__name__}" #: tiramisu/setting.py:606 msgid "permissive must be a frozenset" @@ -1034,28 +1039,42 @@ msgstr "" msgid "unknown action {}" msgstr "action inconnue {}" -#: tiramisu/value.py:570 tiramisu/value.py:872 +#: tiramisu/value.py:557 tiramisu/value.py:859 msgid "set owner \"{0}\" is forbidden" msgstr "assigner l'utilisateur \"{0}\" est interdit" -#: tiramisu/value.py:577 +#: tiramisu/value.py:564 msgid "\"{0}\" is a default value, so we cannot change owner to \"{1}\"" msgstr "" "\"{0}\" est une valeur par défaut, donc ne peut changer d'utilisateur à " "\"{1}\"" -#: tiramisu/value.py:751 -msgid "" -"index {index} is greater than the length {length} for option {subconfig." -"option.impl_get_display_name(with_quote=True)}" -msgstr "" -"l'index {index} est supérieur à la longueur \"{length}\" pour l'option " -"\"{subconfig.option.impl_get_display_name(with_quote=True)}\"" - -#: tiramisu/value.py:858 +#: tiramisu/value.py:845 msgid "information's item not found \"{}\"" msgstr "l'information de l'objet ne sont pas trouvé \"{}\"" +#~ msgid "no option found in config with these criteria" +#~ msgstr "aucune option trouvée dans la config avec ces critères" + +#~ msgid "" +#~ "the option's name \"{0}\" start as the dynoptiondescription's name \"{1}\"" +#~ msgstr "" +#~ "le nom de l'option \"{0}\" commence comme le nom du dynoptiondescription " +#~ "\"{1}\"" + +#~ msgid "duplicate option name: \"{0}\"" +#~ msgstr "nom de l'option dupliqué : \"{0}\"" + +#~ msgid "duplicate option: {0}" +#~ msgstr "option dupliquée : {0}" + +#~ msgid "" +#~ "index {index} is greater than the length {length} for option " +#~ "{subconfig.option.impl_get_display_name(with_quote=True)}" +#~ msgstr "" +#~ "l'index {index} est supérieur à la longueur \"{length}\" pour l'option " +#~ "\"{subconfig.option.impl_get_display_name(with_quote=True)}\"" + #~ msgid "cannot find \"{0}\" in \"{1}\"" #~ msgstr "ne peut pas trouver \"{0}\" dans \"{1}\"" @@ -1077,9 +1096,6 @@ msgstr "l'information de l'objet ne sont pas trouvé \"{}\"" #~ 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 "" #~ "IP \"{ip[\"value\"]}\" ({ip[\"name\"]}) with this netmask is in fact a " #~ "broacast address" @@ -1090,9 +1106,6 @@ msgstr "l'information de l'objet ne sont pas trouvé \"{}\"" #~ msgid "Call: {}" #~ msgstr "Appel : {}" -#~ msgid "option must not be an optiondescription" -#~ msgstr "option ne doit pas être une optiondescription" - #~ msgid "index must be set with the follower option \"{}\"" #~ msgstr "l'index est obligatoire pour l'option suiveuse \"{}\"" @@ -1114,9 +1127,6 @@ msgstr "l'information de l'objet ne sont pas trouvé \"{}\"" #~ msgid "cannot set session_id and config together" #~ msgstr "session_id et config ne peut être mis ensemble" -#~ msgid "\"{0}\" must be an optiondescription, not an {1}" -#~ msgstr "\"{0}\" doit être une optiondescription, et non {1}" - #~ msgid "unknown option {}" #~ msgstr "option {} inconnue" diff --git a/locale/tiramisu.pot b/locale/tiramisu.pot index b9b4dc1..1fef334 100644 --- a/locale/tiramisu.pot +++ b/locale/tiramisu.pot @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" -"POT-Creation-Date: 2025-06-28 10:15+0300\n" +"POT-Creation-Date: 2025-09-19 22:05+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -35,7 +35,7 @@ msgstr "" msgid "Commands:" msgstr "" -#: tiramisu/api.py:118 tiramisu/api.py:1920 +#: tiramisu/api.py:118 tiramisu/api.py:1955 msgid "please specify a valid sub function ({0}.{1})" msgstr "" @@ -43,67 +43,67 @@ msgstr "" msgid "please do not specify index ({0}.{1})" msgstr "" -#: tiramisu/api.py:211 tiramisu/api.py:906 +#: tiramisu/api.py:211 tiramisu/api.py:940 msgid "please specify index with a follower option ({0}.{1})" msgstr "" -#: tiramisu/api.py:232 +#: tiramisu/api.py:234 msgid "please specify a valid sub function ({0}.{1}): {2}" msgstr "" -#: tiramisu/api.py:493 +#: tiramisu/api.py:525 msgid "the option {0} is not a dynamic option, cannot get identifiers with only_self parameter to True" msgstr "" -#: tiramisu/api.py:581 +#: tiramisu/api.py:613 msgid "cannot get option from a follower symlink without index" msgstr "" -#: tiramisu/api.py:657 +#: tiramisu/api.py:691 msgid "cannot add this property: \"{0}\"" msgstr "" -#: tiramisu/api.py:684 +#: tiramisu/api.py:718 msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\"" msgstr "" -#: tiramisu/api.py:688 +#: tiramisu/api.py:722 msgid "cannot find \"{0}\" in option \"{1}\"" msgstr "" -#: tiramisu/api.py:693 +#: tiramisu/api.py:727 msgid "cannot remove option's property \"{0}\", use permissive instead in option \"{1}\" at index \"{2}\"" msgstr "" -#: tiramisu/api.py:697 +#: tiramisu/api.py:731 msgid "cannot find \"{0}\" in option \"{1}\" at index \"{2}\"" msgstr "" -#: tiramisu/api.py:741 +#: tiramisu/api.py:775 msgid "cannot find \"{0}\"" msgstr "" -#: tiramisu/api.py:923 +#: tiramisu/api.py:957 msgid "only multi value has defaultmulti" msgstr "" -#: tiramisu/api.py:1087 +#: tiramisu/api.py:1121 msgid "please specify a valid sub function ({0}.{1}) for {2}" msgstr "" -#: tiramisu/api.py:1485 +#: tiramisu/api.py:1520 msgid "properties must be a frozenset" msgstr "" -#: tiramisu/api.py:1489 tiramisu/api.py:1516 +#: tiramisu/api.py:1524 tiramisu/api.py:1551 msgid "unknown when {} (must be in append or remove)" msgstr "" -#: tiramisu/api.py:1502 tiramisu/api.py:1526 tiramisu/config.py:1722 +#: tiramisu/api.py:1537 tiramisu/api.py:1561 tiramisu/config.py:1691 msgid "unknown type {}" msgstr "" -#: tiramisu/api.py:1892 +#: tiramisu/api.py:1927 msgid "do not use unrestraint, nowarnings or forcepermissive together" msgstr "" @@ -167,123 +167,123 @@ msgstr "" msgid "help_function ({0}) must be a function" msgstr "" -#: tiramisu/autolib.py:469 tiramisu/autolib.py:524 +#: tiramisu/autolib.py:469 tiramisu/autolib.py:525 msgid "the option {0} is used in a calculation but is invalid ({1})" msgstr "" -#: tiramisu/autolib.py:482 tiramisu/autolib.py:538 tiramisu/autolib.py:588 +#: tiramisu/autolib.py:482 tiramisu/autolib.py:539 tiramisu/autolib.py:589 msgid "unable to get value for calculating {0}, {1}" msgstr "" -#: tiramisu/autolib.py:517 +#: tiramisu/autolib.py:518 msgid "unable to carry out a calculation for {0}, {1}" msgstr "" -#: tiramisu/autolib.py:563 +#: tiramisu/autolib.py:564 msgid "cannot find information for {0}, {1} is a dynamic option" msgstr "" -#: tiramisu/autolib.py:603 +#: tiramisu/autolib.py:604 msgid "option {0} is not a dynoptiondescription or in a dynoptiondescription" msgstr "" -#: tiramisu/autolib.py:696 +#: tiramisu/autolib.py:697 msgid "cannot calculate arguments for {0}, {1} with identifier \"{2}\", there is no identifiers" msgstr "" -#: tiramisu/autolib.py:708 +#: tiramisu/autolib.py:709 msgid "cannot calculate arguments for {0}, {1} with identifier \"{2}\", list of valid identifiers: {3}" msgstr "" -#: tiramisu/autolib.py:819 +#: tiramisu/autolib.py:820 msgid "the follower {0} must have index in carry_out_calculation!" msgstr "" -#: tiramisu/autolib.py:931 +#: tiramisu/autolib.py:932 msgid "unexpected error \"{1}\" in function \"{2}\" with arguments \"{3}\" and \"{4}\" for option {0}" msgstr "" -#: tiramisu/autolib.py:941 +#: tiramisu/autolib.py:942 msgid "unexpected error \"{1}\" in function \"{2}\" for option {0}" msgstr "" -#: tiramisu/config.py:609 +#: tiramisu/config.py:391 tiramisu/config.py:413 +msgid "option name {0} is not unique in {1}" +msgstr "" + +#: tiramisu/config.py:638 msgid "there is no option description for this config (may be GroupConfig)" msgstr "" -#: tiramisu/config.py:698 -msgid "no option found in config with these criteria" -msgstr "" - -#: tiramisu/config.py:1024 tiramisu/option/optiondescription.py:74 +#: tiramisu/config.py:989 tiramisu/option/optiondescription.py:74 msgid "option description seems to be part of an other config" msgstr "" -#: tiramisu/config.py:1186 +#: tiramisu/config.py:1151 msgid "parent of {0} not already exists" msgstr "" -#: tiramisu/config.py:1233 +#: tiramisu/config.py:1198 msgid "cannot set leadership object has root optiondescription" msgstr "" -#: tiramisu/config.py:1236 +#: tiramisu/config.py:1201 msgid "cannot set dynoptiondescription object has root optiondescription" msgstr "" -#: tiramisu/config.py:1282 +#: tiramisu/config.py:1248 msgid "child must be a Config, GroupConfig, MixConfig or MetaConfig" msgstr "" -#: tiramisu/config.py:1290 +#: tiramisu/config.py:1257 msgid "config name must be uniq in groupconfig for \"{0}\"" msgstr "" -#: tiramisu/config.py:1499 +#: tiramisu/config.py:1466 msgid "unknown config \"{}\"" msgstr "" -#: tiramisu/config.py:1524 +#: tiramisu/config.py:1491 msgid "child must be a Config, MixConfig or MetaConfig" msgstr "" -#: tiramisu/config.py:1559 +#: tiramisu/config.py:1526 msgid "force_default, force_default_if_same or force_dont_change_value cannot be set with only_config" msgstr "" -#: tiramisu/config.py:1569 +#: tiramisu/config.py:1536 msgid "force_default and force_dont_change_value cannot be set together" msgstr "" -#: tiramisu/config.py:1718 +#: tiramisu/config.py:1687 msgid "config name must be uniq in groupconfig for {0}" msgstr "" -#: tiramisu/config.py:1763 +#: tiramisu/config.py:1732 msgid "config added has no name, the name is mandatory" msgstr "" -#: tiramisu/config.py:1768 +#: tiramisu/config.py:1737 msgid "config name \"{0}\" is not uniq in groupconfig \"{1}\"" msgstr "" -#: tiramisu/config.py:1786 tiramisu/config.py:1792 +#: tiramisu/config.py:1755 tiramisu/config.py:1761 msgid "cannot find the config {0}" msgstr "" -#: tiramisu/config.py:1818 +#: tiramisu/config.py:1787 msgid "MetaConfig with optiondescription must have string has child, not {}" msgstr "" -#: tiramisu/config.py:1830 +#: tiramisu/config.py:1799 msgid "child must be a Config or MetaConfig" msgstr "" -#: tiramisu/config.py:1835 +#: tiramisu/config.py:1804 msgid "all config in metaconfig must have the same optiondescription" msgstr "" -#: tiramisu/config.py:1852 +#: tiramisu/config.py:1821 msgid "metaconfig must have the same optiondescription" msgstr "" @@ -471,31 +471,31 @@ msgstr "" msgid "the value of \"{0}\" is not {1}" msgstr "" -#: tiramisu/option/baseoption.py:75 tiramisu/option/symlinkoption.py:44 +#: tiramisu/option/baseoption.py:76 tiramisu/option/symlinkoption.py:44 msgid "\"{0}\" is an invalid name for an option" msgstr "" -#: tiramisu/option/baseoption.py:88 +#: tiramisu/option/baseoption.py:89 msgid "invalid properties type {0} for {1}, must be a frozenset" msgstr "" -#: tiramisu/option/baseoption.py:100 +#: tiramisu/option/baseoption.py:102 msgid "invalid property type {0} for {1}, must be a string or a Calculation" msgstr "" -#: tiramisu/option/baseoption.py:251 +#: tiramisu/option/baseoption.py:253 msgid "information's item for {0} not found: \"{1}\"" msgstr "" -#: tiramisu/option/baseoption.py:269 +#: tiramisu/option/baseoption.py:271 msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgstr "" -#: tiramisu/option/baseoption.py:310 +#: tiramisu/option/baseoption.py:312 msgid "\"{}\" ({}) object attribute \"{}\" is read-only" msgstr "" -#: tiramisu/option/baseoption.py:322 +#: tiramisu/option/baseoption.py:324 msgid "{0} not part of any Config" msgstr "" @@ -600,7 +600,7 @@ msgstr "" msgid "invalid identifier \"{}\" for option {}" msgstr "" -#: tiramisu/option/dynoptiondescription.py:150 +#: tiramisu/option/dynoptiondescription.py:154 msgid "DynOptionDescription \"{0}\" identifiers return a list with same values \"{1}\"" msgstr "" @@ -732,43 +732,49 @@ msgstr "" msgid "invalid default_multi value \"{0}\" for option {1}, must be a list for a submulti" msgstr "" -#: tiramisu/option/option.py:291 +#: tiramisu/option/option.py:294 msgid "the value \"{}\" is not unique" msgstr "" -#: tiramisu/option/option.py:353 +#: tiramisu/option/option.py:357 msgid "which must not be a list" msgstr "" -#: tiramisu/option/option.py:408 tiramisu/option/option.py:446 +#: tiramisu/option/option.py:412 tiramisu/option/option.py:450 msgid "which must be a list" msgstr "" -#: tiramisu/option/option.py:436 +#: tiramisu/option/option.py:440 msgid "which \"{}\" must be a list of list" msgstr "" -#: tiramisu/option/optiondescription.py:109 -msgid "duplicate option: {0}" +#: tiramisu/option/optiondescription.py:111 +#: tiramisu/option/optiondescription.py:117 +msgid "\"{0}\" option description" msgstr "" -#: tiramisu/option/optiondescription.py:336 +#: tiramisu/option/optiondescription.py:115 +#: tiramisu/option/optiondescription.py:121 +msgid "root option description" +msgstr "" + +#: tiramisu/option/optiondescription.py:123 +msgid "option \"{0}\" is include in {1} but is also in {2}" +msgstr "" + +#: tiramisu/option/optiondescription.py:328 msgid "children in optiondescription \"{}\" must be a list" msgstr "" -#: tiramisu/option/optiondescription.py:364 -msgid "duplicate option name: \"{0}\"" +#: tiramisu/option/optiondescription.py:357 +msgid "the option name \"{0}\" is duplicate in \"{1}\"" msgstr "" -#: tiramisu/option/optiondescription.py:370 -msgid "the option's name \"{0}\" start as the dynoptiondescription's name \"{1}\"" -msgstr "" - -#: tiramisu/option/optiondescription.py:413 +#: tiramisu/option/optiondescription.py:411 msgid "cannot change group_type if already set (old {0}, new {1})" msgstr "" -#: tiramisu/option/optiondescription.py:418 +#: tiramisu/option/optiondescription.py:416 msgid "group_type: {0} not allowed" msgstr "" diff --git a/tests/test_config.py b/tests/test_config.py index 56f1ee5..361b9bd 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -332,7 +332,7 @@ def test_duplicated_option(): g1 #in same OptionDescription with pytest.raises(ConflictError): - d1 = OptionDescription('od', '', [g1, g1]) + OptionDescription('od', '', [g1, g1]) # assert not list_sessions() @@ -346,6 +346,28 @@ def test_duplicated_option_diff_od(): Config(d2) +def test_duplicated_option_diff_od_2(): + g1 = IntOption('g1', '', 1) + d1 = OptionDescription('od1', '', [g1]) + #in different OptionDescription + d2 = OptionDescription('od2', '', [d1, g1]) + d2 + with pytest.raises(ConflictError): + Config(d2) + + +def test_duplicated_option_diff_od_3(): + g1 = IntOption('g1', '', 1) + d1 = OptionDescription('od1', '', [g1]) + d3 = OptionDescription('od3', '', [g1]) + #in different OptionDescription + d2 = OptionDescription('od2', '', [d1, d3]) + d4 = OptionDescription('od4', '', [d2]) + d4 + with pytest.raises(ConflictError): + Config(d4) + + def test_cannot_assign_value_to_option_description(): od1 = make_description() cfg = Config(od1) diff --git a/tests/test_dyn_optiondescription.py b/tests/test_dyn_optiondescription.py index bbb645e..8ac04fe 100644 --- a/tests/test_dyn_optiondescription.py +++ b/tests/test_dyn_optiondescription.py @@ -1720,16 +1720,6 @@ def test_leadership_callback_samegroup_dyndescription(): # assert not list_sessions() -def test_invalid_conflict_dyndescription(): - st = StrOption('st', '') - dod = DynOptionDescription('dod', '', [st], identifiers=Calculation(return_list)) - dodinvalid = StrOption('dodinvalid', '') - dod, dodinvalid - with pytest.raises(ConflictError): - OptionDescription('od', '', [dod, dodinvalid]) -# assert not list_sessions() - - def test_leadership_default_multi_dyndescription4(): st1 = StrOption('st1', "", multi=True) st2 = StrOption('st2', "", multi=True, default_multi='no') @@ -2883,3 +2873,86 @@ def test_callback_list_dyndescription_information_not_list(): cfg.information.set('identifier', 'ival3') assert cfg.option('od.dodival3.st').value.get() == ['ival3', 'val2'] # assert not list_sessions() + + +def test_dynoption_not_duplicate(): + st = StrOption('st', '') + st2 = StrOption('st', '') + dod = DynOptionDescription('od_', '', [st], identifiers=["val1"]) + dod2 = DynOptionDescription('od_', '', [st2], identifiers=["val2"]) + od = OptionDescription('od', '', [dod, dod2]) + od2 = OptionDescription('od', '', [od]) + cfg = Config(od2) + assert parse_od_get(cfg.value.get()) == { + 'od.od_val1.st': None, + 'od.od_val2.st': None, + } + + +def test_dynoption_duplicate_1(): + st = StrOption('st', '', "val1") + od = OptionDescription('od_val', '', [st]) + st2 = StrOption('st', '', "val2") + dod = DynOptionDescription('od_', '', [st2], identifiers=["val"]) + od1 = OptionDescription('od', '', [od, dod]) + od2 = OptionDescription('od', '', [od1]) + cfg = Config(od2) + with pytest.raises(ConflictError): + cfg.value.get() + with pytest.raises(ConflictError): + cfg.option('od.od_val').value.get() + + +def test_dynoption_duplicate_2(): + st2 = StrOption('st', '', "val2") + dod = DynOptionDescription('od_', '', [st2], identifiers=["val"]) + st = StrOption('st', '', "val1") + od = OptionDescription('od_val', '', [st]) + od1 = OptionDescription('od', '', [dod, od]) + od2 = OptionDescription('od', '', [od1]) + cfg = Config(od2) + with pytest.raises(ConflictError): + cfg.value.get() + with pytest.raises(ConflictError): + cfg.option('od.od_val').value.get() + + +def test_dynoption_duplicate_3(): + st = StrOption('od_val', '') + st2 = StrOption('st', '', "val2") + dod = DynOptionDescription('od_', '', [st2], identifiers=["val"]) + od1 = OptionDescription('od', '', [dod, st]) + od2 = OptionDescription('od', '', [od1]) + cfg = Config(od2) + with pytest.raises(ConflictError): + cfg.value.get() + with pytest.raises(ConflictError): + cfg.option('od.od_val').value.get() + + +def test_dynoption_duplicate_4(): + st = StrOption('st', '') + st2 = StrOption('st', '') + dod = DynOptionDescription('od_', '', [st], identifiers=["val"]) + dod2 = DynOptionDescription('od_', '', [st2], identifiers=["val"]) + od = OptionDescription('od', '', [dod, dod2]) + od2 = OptionDescription('od', '', [od]) + cfg = Config(od2) + with pytest.raises(ConflictError): + cfg.value.get() + with pytest.raises(ConflictError): + cfg.option('od.od_val').value.get() + + +def test_dynoption_duplicate_5(): + st = StrOption('st', '') + st2 = StrOption('st', '') + dod = DynOptionDescription('od_', '', [st], identifiers=["val"]) + dod2 = DynOptionDescription('od', '', [st2], identifiers=["_val"]) + od = OptionDescription('od', '', [dod, dod2]) + od2 = OptionDescription('od', '', [od]) + cfg = Config(od2) + with pytest.raises(ConflictError): + cfg.value.get() + with pytest.raises(ConflictError): + cfg.option('od.od_val').value.get() diff --git a/tiramisu/api.py b/tiramisu/api.py index a2274ab..2205b00 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -212,7 +212,9 @@ def option_type(typ): "please specify index with a follower option ({0}.{1})" ).format(self.__class__.__name__, func.__name__) raise ConfigError(msg) - if 'validate_properties' in types or (self._validate_properties and "dont_validate_property" not in types): + if "validate_properties" in types or ( + self._validate_properties and "dont_validate_property" not in types + ): settings = self._config_bag.context.get_settings() parent = self._subconfig.parent if parent and parent.transitive_properties: @@ -267,7 +269,9 @@ class _TiramisuOptionWalk: ): options = [] for sub_subconfig in subconfig.get_children( - validate_properties, uncalculated=uncalculated, with_index=with_index, + validate_properties, + uncalculated=uncalculated, + with_index=with_index, ): options.append( TiramisuOption( @@ -298,7 +302,15 @@ class _TiramisuOptionOptionDescription: """Get Tiramisu option""" return self._subconfig.option - @option_type(["optiondescription", "option", "with_or_without_index", "symlink", "allow_dynoption"]) + @option_type( + [ + "optiondescription", + "option", + "with_or_without_index", + "symlink", + "allow_dynoption", + ] + ) def isoptiondescription(self): """Test if option is an optiondescription""" return self._subconfig.option.impl_is_optiondescription() @@ -376,10 +388,14 @@ class _TiramisuOptionOptionDescription: index = self._index parent = self._subconfig.parent parent_option = parent.option - for woption in self._subconfig.option.get_dependencies(self._config_bag.context): + for woption in self._subconfig.option.get_dependencies( + self._config_bag.context + ): option = woption() if not uncalculated and option.issubdyn(): - for subconfig in context.get_dynamic_from_dyn_option(self._subconfig, option): + for subconfig in context.get_dynamic_from_dyn_option( + self._subconfig, option + ): options.append( TiramisuOption( subconfig.path, @@ -388,7 +404,9 @@ class _TiramisuOptionOptionDescription: ) ) elif not uncalculated and option.impl_is_dynoptiondescription(): - for subconfig in context.get_dynamic_from_dyn_option(self._subconfig, option): + for subconfig in context.get_dynamic_from_dyn_option( + self._subconfig, option + ): options.append( TiramisuOption( subconfig.path, @@ -397,7 +415,12 @@ class _TiramisuOptionOptionDescription: ) ) else: - if not option.impl_is_optiondescription() and option.impl_is_follower() and parent_option.impl_is_leadership() and parent_option.in_same_leadership(option): + if ( + not option.impl_is_optiondescription() + and option.impl_is_follower() + and parent_option.impl_is_leadership() + and parent_option.in_same_leadership(option) + ): if index is not None: current_indexes = [index] else: @@ -606,7 +629,9 @@ class TiramisuOptionOwner(CommonTiramisuOption): @option_type(["symlink", "option", "with_index"]) def get(self, only_self=False): """Get owner for a specified option""" - return self._config_bag.context.get_owner(self._subconfig, validate_meta=not only_self) + return self._config_bag.context.get_owner( + self._subconfig, validate_meta=not only_self + ) @option_type(["symlink", "option", "with_index"]) def isdefault(self): @@ -1251,7 +1276,7 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet): path: str, value: Any, *, - index: Optional[int] = None, + index: Optional[int] = None, only_config=undefined, force_default=undefined, force_default_if_same=undefined, @@ -1269,12 +1294,13 @@ class TiramisuContextValue(TiramisuConfig, _TiramisuODGet): kwargs["force_dont_change_value"] = force_dont_change_value context = self._config_bag.context if isinstance(context, KernelGroupConfig): - subconfig = Fake_SubConfig(self._config_bag, - path, - index, - ) + subconfig = Fake_SubConfig( + self._config_bag, + path, + index, + ) else: - subconfig = context.get_sub_config( + subconfig = context.get_sub_config( self._config_bag, path, index, diff --git a/tiramisu/autolib.py b/tiramisu/autolib.py index f31b5c5..82f279c 100644 --- a/tiramisu/autolib.py +++ b/tiramisu/autolib.py @@ -507,6 +507,7 @@ def manager_callback( index_, validate_properties=not self_calc, properties=properties, + valid_conflict=False, ) except PropertiesOptionError as err: # raise PropertiesOptionError (which is catched) because must not add value None in carry_out_calculation diff --git a/tiramisu/config.py b/tiramisu/config.py index a0a2313..d8fec1f 100644 --- a/tiramisu/config.py +++ b/tiramisu/config.py @@ -166,7 +166,7 @@ class CCache: def get_dynamic_from_dyn_option(self, subconfig, option): config_bag = subconfig.config_bag sub_paths = option.impl_getpath() - current_paths = subconfig.path.split('.') + current_paths = subconfig.path.split(".") current_paths_max_index = len(current_paths) - 1 current_subconfigs = [] parent = subconfig @@ -186,7 +186,10 @@ class CCache: allow_dynoption=True, ) if sub_option.impl_is_dynoptiondescription(): - if idx <= current_paths_max_index and sub_option == current_subconfigs[idx].option: + if ( + idx <= current_paths_max_index + and sub_option == current_subconfigs[idx].option + ): new_currents.append(current_subconfigs[idx]) else: new_currents.extend( @@ -374,12 +377,26 @@ class SubConfig: if self.option.impl_is_leadership() and not uncalculated and with_index: yield from self.get_leadership_children(validate_properties) else: + children_name = [] for child in self.option.get_children(): if child.impl_is_dynoptiondescription() and not uncalculated: - yield from self.dyn_to_subconfig( + for dyn_child in self.dyn_to_subconfig( child, validate_properties, - ) + ): + yield dyn_child + if child.could_conflict: + name = dyn_child.path + if name in children_name: + raise ConflictError( + _("option name \"{0}\" is not unique in {1}").format( + name, + self.option.impl_get_display_name( + self, with_quote=True + ), + ) + ) + children_name.append(name) else: try: yield self.get_child( @@ -390,6 +407,18 @@ class SubConfig: except PropertiesOptionError as err: if err.proptype in (["mandatory"], ["empty"]): raise err + if child.could_conflict: + name = child.impl_getpath() + if name in children_name: + raise ConflictError( + _("option name \"{0}\" is not unique in {1}").format( + name, + self.option.impl_get_display_name( + self, with_quote=True + ), + ) + ) + children_name.append(name) def get_child( self, @@ -561,15 +590,16 @@ class SubConfig: def change_context(self, context) -> "SubConfig": config_bag = self.config_bag.copy() config_bag.context = context - return SubConfig(self.option, - self.index, - self.path, - config_bag, - self.parent, - self.identifiers, - true_path=self.true_path, - validate_properties=False, - ) + return SubConfig( + self.option, + self.index, + self.path, + config_bag, + self.parent, + self.identifiers, + true_path=self.true_path, + validate_properties=False, + ) class _Config(CCache): @@ -623,80 +653,8 @@ class _Config(CCache): """get cache for values""" return self._impl_values_cache # pylint: disable=no-member - # ============================================================================= - # WALK - def find( - self, - option_bag, - bytype, - byname, - byvalue, - raise_if_not_found=True, - only_path=undefined, - only_option=undefined, - with_option=False, - ): - """ - convenience method for finding an option that lives only in the subtree - - :param first: return only one option if True, a list otherwise - :return: find list or an exception if nothing has been found - """ - - # pylint: disable=too-many-arguments,too-many-locals - def _filter_by_value(soption_bag): - value = self.get_value(soption_bag) - if isinstance(value, list): - return byvalue in value - return value == byvalue - - found = False - if only_path is not undefined: - - def _fake_iter(): - yield only_option - - options = _fake_iter() - else: - options = option_bag.option.get_children_recursively( - bytype, - byname, - option_bag.config_bag, - ) - for option in options: - path = option.impl_getpath() - soption_bag = OptionBag( - option, - None, - option_bag.config_bag, - ) - if byvalue is not undefined and not _filter_by_value(soption_bag): - continue - if option_bag.config_bag.properties: - # remove option with propertyerror, ... - try: - self.get_sub_config( - option_bag.config_bag, # pylint: disable=no-member - path, - None, - validate_properties=True, - ) - except PropertiesOptionError: - continue - found = True - if not with_option: - yield path - else: - yield path, option - self._find_return_results( - found, - raise_if_not_found, - ) - - def _find_return_results(self, found, raise_if_not_found): - if not found and raise_if_not_found: - raise AttributeError(_("no option found in config" " with these criteria")) - + # # ============================================================================= + # # WALK def walk_valid_value( self, subconfig, @@ -741,6 +699,7 @@ class _Config(CCache): properties=undefined, true_path: Optional[str] = None, allow_dynoption: bool = False, + valid_conflict: bool = True, ): subconfig = self.get_root(config_bag) if path is None: @@ -773,6 +732,32 @@ class _Config(CCache): identifier, option = option else: identifier = None + if valid_conflict and option.could_conflict: + for ref in option.could_conflict: + child = ref() + if child.impl_is_dynoptiondescription(): + for dyn_child in subconfig.dyn_to_subconfig( + child, + validate_properties, + ): + if path == dyn_child.path: + raise ConflictError( + _("option name \"{0}\" is not unique in {1}").format( + name, + option.impl_get_display_name( + subconfig, with_quote=True + ), + ) + ) + elif child.impl_getname() == name: + raise ConflictError( + _("option name \"{0}\" is not unique in {1}").format( + name, + self.option.impl_get_display_name( + self, with_quote=True + ), + ) + ) subconfig = subconfig.get_child( option, index_, @@ -851,16 +836,14 @@ class _Config(CCache): # ============================================================================= # Manage value - def set_value(self, - subconfig, - value: Any, - ) -> Any: - """set value - """ + def set_value( + self, + subconfig, + value: Any, + ) -> Any: + """set value""" self.get_settings().validate_properties(subconfig) - return self.get_values().set_value(subconfig, - value - ) + return self.get_values().set_value(subconfig, value) def get_value( self, @@ -1282,7 +1265,9 @@ class KernelGroupConfig(_CommonConfig): names = [] for child in children: if not isinstance(child, (KernelConfig, KernelGroupConfig)): - raise TypeError(_("child must be a Config, GroupConfig, MixConfig or MetaConfig")) + raise TypeError( + _("child must be a Config, GroupConfig, MixConfig or MetaConfig") + ) name_ = child._impl_name names.append(name_) if len(names) != len(set(names)): @@ -1597,7 +1582,9 @@ class KernelMixConfig(KernelGroupConfig): validate_properties=validate_properties, ) if force_default_if_same: - if not child.get_values().hasvalue(subconfig.path, index=subconfig.index): + if not child.get_values().hasvalue( + subconfig.path, index=subconfig.index + ): child_value = undefined else: child_value = child.get_value(moption_bag) diff --git a/tiramisu/locale/fr/LC_MESSAGES/tiramisu.mo b/tiramisu/locale/fr/LC_MESSAGES/tiramisu.mo index dae84d5..cb345cc 100644 Binary files a/tiramisu/locale/fr/LC_MESSAGES/tiramisu.mo and b/tiramisu/locale/fr/LC_MESSAGES/tiramisu.mo differ diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index cc82a95..2e12d7d 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -59,6 +59,7 @@ class Base: "_dependencies", "_dependencies_information", "_identifiers_dependencies", + "could_conflict", "__weakref__", ) @@ -93,6 +94,7 @@ class Base: _setattr = object.__setattr__ _setattr(self, "_name", name) _setattr(self, "_informations", {"doc": doc}) + _setattr(self, "could_conflict", []) for prop in properties: if not isinstance(prop, str): if not isinstance(prop, Calculation): @@ -188,6 +190,7 @@ class Base: else: dico = tuple([keys, tuple(dico.values())]) _setattr(self, "_informations", dico) + _setattr(self, "could_conflict", tuple(self.could_conflict)) extra = getattr(self, "_extra", None) if extra is not None: _setattr( diff --git a/tiramisu/option/dynoptiondescription.py b/tiramisu/option/dynoptiondescription.py index 734cd69..7ac13e2 100644 --- a/tiramisu/option/dynoptiondescription.py +++ b/tiramisu/option/dynoptiondescription.py @@ -82,6 +82,9 @@ class DynOptionDescription(OptionDescription): identifier = identifier.replace(".", "_") return identifier + def name_could_conflict(self, dynchild, child): + return child.impl_getname().startswith(dynchild.impl_getname()) + def impl_is_dynoptiondescription(self) -> bool: return True @@ -145,7 +148,11 @@ class DynOptionDescription(OptionDescription): ) else: values_.append(val) - if __debug__ and "demoting_error_warning" not in subconfig.config_bag.properties and len(values_) > len(set(values_)): + if ( + __debug__ + and "demoting_error_warning" not in subconfig.config_bag.properties + and len(values_) > len(set(values_)) + ): raise ValueError( _( 'DynOptionDescription "{0}" identifiers return a list with same values "{1}"' diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 7c574d6..2149d0c 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -286,9 +286,13 @@ class Option(BaseOption): return if not subconfig or not check_error or "unique" not in subconfig.properties: return - indexes = [index for index, value in enumerate(values) if value == current_value] + indexes = [ + index for index, value in enumerate(values) if value == current_value + ] if len(indexes) > 1: - raise ValueError(_('the value "{}" is not unique' "").format(current_value)) + raise ValueError( + _('the value "{}" is not unique' "").format(current_value) + ) def calculation_validator( val, @@ -456,7 +460,7 @@ class Option(BaseOption): except ValueError as err: self.validate_parse_error(val, err_index, err, subconfig) ret = False -# return False + # return False return ret def validate_parse_error(self, val, index, err, subconfig): diff --git a/tiramisu/option/optiondescription.py b/tiramisu/option/optiondescription.py index b67c5e2..9fdd0df 100644 --- a/tiramisu/option/optiondescription.py +++ b/tiramisu/option/optiondescription.py @@ -106,7 +106,24 @@ class CacheOptionDescription(BaseOption): if "force_store_value" in properties: force_store_values.append(option) if option.impl_is_readonly(): - raise ConflictError(_("duplicate option: {0}").format(option)) + previous_path = option.impl_getpath() + if "." in previous_path: + previous_path = _('"{0}" option description').format( + previous_path.rsplit(".", 1)[0] + ) + else: + previous_path = _("root option description") + if currpath: + current_path = _('"{0}" option description').format( + ".".join(currpath) + ) + else: + current_path = _("root option description") + raise ConflictError( + _('option "{0}" is include in {1} but is also in {2}').format( + option.impl_get_display_name(None), current_path, previous_path + ) + ) if not self.impl_is_readonly() and display_name: option._display_name_function = ( display_name # pylint: disable=protected-access @@ -286,31 +303,6 @@ class OptionDescriptionWalk(CacheOptionDescription): """get children""" return self._children[1] - def get_children_recursively( - self, - bytype: Optional[BaseOption], - byname: Optional[str], - config_bag: ConfigBag, - self_opt: BaseOption = None, - *, - option_identifiers: Optional[list] = None, - ) -> Iterator[Union[BaseOption]]: - """get children recursively""" - if self_opt is None: - self_opt = self - for option in self_opt.get_children(): - if option.impl_is_optiondescription(): - for subopt in option.get_children_recursively( - bytype, - byname, - config_bag, - ): - yield subopt - elif (byname is None or option.impl_getname() == byname) and ( - bytype is None or isinstance(option, bytype) - ): - yield option - class OptionDescription(OptionDescriptionWalk): """Config's schema (organisation, group) and container of Options @@ -343,35 +335,38 @@ class OptionDescription(OptionDescriptionWalk): properties=properties, ) child_names = [] - if __debug__: - dynopt_names = [] + fix_child_names = [] + dynopts = [] for child in children: name = child.impl_getname() child_names.append(name) - if __debug__ and child.impl_is_dynoptiondescription(): - dynopt_names.append(name) + if child.impl_is_dynoptiondescription(): + dynopts.append(child) + else: + fix_child_names.append(name) # before sorting children_ = (tuple(child_names), tuple(children)) - if __debug__: - # better performance like this - child_names.sort() - old = None - for child in child_names: - if child == old: - raise ConflictError( - _("duplicate option name: " '"{0}"').format(child) + # better performance like this + fix_child_names.sort() + old = None + for child_name in fix_child_names: + if child_name == old: + raise ConflictError( + _('the option name "{0}" is duplicate in "{1}"').format( + child_name, self.impl_get_display_name(None) ) - if dynopt_names: - for dynopt in dynopt_names: - if child != dynopt and child.startswith(dynopt): - raise ConflictError( - _( - 'the option\'s name "{0}" start as the dynoptiondescription\'s name "{1}"' - ).format(child, dynopt) - ) - old = child + ) + old = child_name + for dynopt in dynopts: + if dynopt.could_conflict: + continue + for child in children: + if child != dynopt and dynopt.name_could_conflict(dynopt, child): + dynopt.could_conflict.append(weakref.ref(child)) + child.could_conflict.append(weakref.ref(dynopt)) + break self._children = children_ # the group_type is useful for filtering OptionDescriptions in a config self._group_type = None